GPTScript / AiScript

A Minimal, Full-Stack, Tool-Assisted Language. Native to Browsers and Bun. Strictly & Strongly-Typed.
https://github.com/GPTScript/AiScript
Mozilla Public License 2.0
9 stars 1 forks source link

Language Draft #43

Open coolaj86 opened 1 year ago

coolaj86 commented 1 year ago

Table of Contents

Questions

Guiding Principles

Language Principles

Syntax Principles

Ai JitScript by Example

Type System

Duck-Typing is King! (everything is an interface)

Type Drafting

Types can be inferred from correct code, and drafted automatically.

Consider this code:

var Person = module.exports;
Person.create = function(opts) {
  var p = {};

  p.name = opts.name           // required
  p.age = opts.age || null     // optional
  p.ssn = opts.ssn             // required
  p.friend = opts.friend || p  // optional
  p.fullName = function () {
    return `${p.name} ${p.last}`
  };

  return p;
}
Person.fullName = function (p) {
  return `${p.name} ${p.last}`
};

Person.create({ name: "Jack", age: 42, friend: "Jeff" });

Since the code and its execution can be entirely understood, we can draft the type based on known information:

/***
 * A person interface
 * @Person Struct
 * @name String
 * @age Number?
 * @ssn UNKNOWN           // WARN: incomplete use / conflict
 * @friend String|Person
 *
 * $version draft ef2b31  // drafted, non-authoritative
 *                        // can be updated by tooling
 */

Note: draft means that we should not use the type comment to enforce what the code should be, but rather we re-gen on code update. Anytime the code is correct and the type definition is out-of-date we can change the $version to draft to allow it to be regenerated.

var Person = {}

/***
 * @Create                        // the constructor type
 * @opts Person{                  // sub-interface definition
 *         name,
 *         age?,
 *         ssn,
 *         friend?,
 *       }
 *                                // (properties have the same type as Person)
 * returns @Person
 */
Person.create = function(opts) {
  var p = {};

  p.name = opts.name              // required
  p.age = opts.age || 0           // optional
  p.ssn = opts.ssn                // WARN: ambiguous
  p.friend = opts.friend || p     // enum

  return p;
}

Person.create({ name: "Jack", age: 42, friend: "Jeff" });

We can tell by the usage what a Person object can and can't be.

Concrete types

/***
 * A person (or person-esque) interface
 * @Person Struct            // Object, sans prototype stuff
 * @name String              // common String
 * @age  NaturalNumber|null  // 1 to MAX_SAFE_INTEGER, or null
 * @bankBalance BigInt       // can be pos, neg, or 0! (probably in pennies)
 * @friends []Person         // friend list or null
 * @favoritePet Pet|null?    // pet or null or not defined
 *
 * $version v1.0.1           // whether the type has been locked
 */

/*** @Person */
var Person = {};
Person.name = "AJ";
Person.age = 0;

To create an ad-hoc new type:

function getInterestingType() {
    /*** @KnownType */
    var knownType = { foo: "bar" }

    var newProperties = { bar: "baz" };
    var newType = Object.assign(knownType, newProperties)
}

// this is now known to be KnowType-ish + `bar` and can be checked as such.
var interestingType = getInterestingType();

If a function can throw, that's part of its type information.

Functions

Special Functions

Variables

Expressions

Strings

Numbers

Some rough, not-well-thought-out ideas (Re: can division throw):

Macro System

Think of prettier - the macro parser and expander executes on save.

Syntax

Keywords

These words cannot be used for standalone variable or function names, but may be used as property names.

(30) arguments, async, await, break, case, catch, const, continue, debugger, default, delete, else, export, exports, false, for, function, if, import, in, let, null, return, require , switch, throw, true, try, typeof, undefined, var, void

Idea: can we remove more without increasing pain?

(24) arguments, async, await, break, catch, continue, delete, else, export, exports, false, for, function, if, import, in, null, return, require, throw, true, try, typeof, undefined, var, void

(unused: case, debugger, default, switch, import, export, var, const)

Special Words

(Unused) Reserved Words

These words are reserved for historical reasons.

do, class, enum, eval, extends, finally, function*, implements, instanceof, interface, let, new, package, private, protected, public, static, super, this, while, with, yield, yield*

Non-Reserved Words

These words might appear to be reserved, but they aren't.

abstract, boolean, byte, char, double, final, float, goto, int, long, native, short, synchronized, throws, transient, volatile

Syntax Characters

Alphanumeric and Word Characters:

Identifiers can also contain:

Accessors may also use:

Functions use:

Math Operations:

Strings:

Logical Operators:

Bitwise Operators

Other

Special syntax

No-Conflict

Cannot be in conflict with https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types or https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators

Nutty Things

void is among the Browser's best-kept secrets:

// ❌
var x = a + b(function () {
  // whatever
}())
// ✅
var x = a + b

void (function () {
  // whatever
}())
// ✅
var x = a + b;

(function () {
  // whatever
}());

// ❌ // b["apples,bananas,oranges"].forEach(...)
var x = a + b[
  "apples",
  "bananas",
  "oranges",
].forEach(function () {
  // ...
})
// ✅
var x = a + b

void [
  "apples",
  "bananas",
  "oranges",
].forEach(function () {
  // ...
})
// ✅
var x = a + b;

[
  "apples",
  "bananas",
  "oranges",
].forEach(function () {
  // ...
});