subscript 9.1.0 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -171
- package/feature/access.js +67 -7
- package/feature/accessor.js +49 -0
- package/feature/asi.js +15 -0
- package/feature/async.js +45 -0
- package/feature/block.js +41 -0
- package/feature/class.js +69 -0
- package/feature/collection.js +40 -0
- package/feature/comment.js +25 -6
- package/feature/destruct.js +33 -0
- package/feature/function.js +44 -0
- package/feature/group.js +41 -12
- package/feature/if.js +28 -0
- package/feature/literal.js +13 -0
- package/feature/loop.js +123 -0
- package/feature/module.js +42 -0
- package/feature/number.js +45 -10
- package/feature/op/arithmetic.js +29 -0
- package/feature/op/arrow.js +33 -0
- package/feature/op/assign-logical.js +33 -0
- package/feature/op/assignment.js +47 -0
- package/feature/op/bitwise-unsigned.js +17 -0
- package/feature/op/bitwise.js +29 -0
- package/feature/op/comparison.js +19 -0
- package/feature/op/defer.js +15 -0
- package/feature/op/equality.js +16 -0
- package/feature/op/identity.js +15 -0
- package/feature/op/increment.js +23 -0
- package/feature/op/logical.js +21 -0
- package/feature/op/membership.js +17 -0
- package/feature/op/nullish.js +13 -0
- package/feature/op/optional.js +61 -0
- package/feature/op/pow.js +19 -0
- package/feature/op/range.js +26 -0
- package/feature/op/spread.js +15 -0
- package/feature/op/ternary.js +15 -0
- package/feature/op/type.js +18 -0
- package/feature/op/unary.js +41 -0
- package/feature/prop.js +34 -0
- package/feature/regex.js +31 -0
- package/feature/seq.js +21 -0
- package/feature/string.js +24 -16
- package/feature/switch.js +48 -0
- package/feature/template.js +39 -0
- package/feature/try.js +57 -0
- package/feature/unit.js +35 -0
- package/feature/var.js +59 -0
- package/jessie.js +31 -0
- package/jessie.min.js +8 -0
- package/justin.js +39 -48
- package/justin.min.js +8 -1
- package/package.json +18 -23
- package/parse.js +153 -0
- package/subscript.d.ts +45 -5
- package/subscript.js +62 -22
- package/subscript.min.js +5 -1
- package/util/bundle.js +507 -0
- package/util/stringify.js +172 -0
- package/feature/add.js +0 -22
- package/feature/array.js +0 -11
- package/feature/arrow.js +0 -23
- package/feature/assign.js +0 -11
- package/feature/bitwise.js +0 -11
- package/feature/bool.js +0 -5
- package/feature/call.js +0 -15
- package/feature/compare.js +0 -11
- package/feature/increment.js +0 -11
- package/feature/logic.js +0 -11
- package/feature/mult.js +0 -25
- package/feature/object.js +0 -17
- package/feature/optional.js +0 -30
- package/feature/pow.js +0 -5
- package/feature/shift.js +0 -12
- package/feature/spread.js +0 -6
- package/feature/ternary.js +0 -10
- package/src/compile.d.ts +0 -17
- package/src/compile.js +0 -28
- package/src/const.js +0 -42
- package/src/parse.d.ts +0 -22
- package/src/parse.js +0 -114
- package/src/stringify.js +0 -31
- /package/{LICENSE → license} +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection literals: arrays and objects (Justin feature)
|
|
3
|
+
*
|
|
4
|
+
* [a, b, c]
|
|
5
|
+
* {a: 1, b: 2}
|
|
6
|
+
* {a, b} (shorthand)
|
|
7
|
+
*/
|
|
8
|
+
import { group, binary, operator, compile } from '../parse.js';
|
|
9
|
+
import { ACC } from './accessor.js';
|
|
10
|
+
|
|
11
|
+
const ASSIGN = 20, TOKEN = 200;
|
|
12
|
+
|
|
13
|
+
// [a,b,c]
|
|
14
|
+
group('[]', TOKEN);
|
|
15
|
+
|
|
16
|
+
// {a:1, b:2, c:3}
|
|
17
|
+
group('{}', TOKEN);
|
|
18
|
+
|
|
19
|
+
// a: b (colon operator for object properties)
|
|
20
|
+
binary(':', ASSIGN - 1, true);
|
|
21
|
+
|
|
22
|
+
// Compile
|
|
23
|
+
operator('{}', (a, b) => {
|
|
24
|
+
if (b !== undefined) return;
|
|
25
|
+
a = !a ? [] : a[0] !== ',' ? [a] : a.slice(1);
|
|
26
|
+
const props = a.map(p => compile(typeof p === 'string' ? [':', p, p] : p));
|
|
27
|
+
return ctx => {
|
|
28
|
+
const obj = {}, acc = {};
|
|
29
|
+
for (const e of props.flatMap(f => f(ctx))) {
|
|
30
|
+
if (e[0] === ACC) {
|
|
31
|
+
const [, n, desc] = e;
|
|
32
|
+
acc[n] = { ...acc[n], ...desc, configurable: true, enumerable: true };
|
|
33
|
+
} else obj[e[0]] = e[1];
|
|
34
|
+
}
|
|
35
|
+
for (const n in acc) Object.defineProperty(obj, n, acc[n]);
|
|
36
|
+
return obj;
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
operator(':', (a, b) => (b = compile(b), Array.isArray(a) ?
|
|
40
|
+
(a = compile(a), ctx => [[a(ctx), b(ctx)]]) : ctx => [[a, b(ctx)]]));
|
package/feature/comment.js
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
/** Configurable comments via parse.comment = { start: end } */
|
|
2
|
+
import { parse, cur, idx, seek } from '../parse.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const SPACE = 32, space = parse.space;
|
|
5
|
+
|
|
6
|
+
// Default C-style comments
|
|
7
|
+
parse.comment ??= { '//': '\n', '/*': '*/' };
|
|
8
|
+
|
|
9
|
+
// Cached array: [[start, end, firstCharCode], ...]
|
|
10
|
+
let comments;
|
|
11
|
+
|
|
12
|
+
parse.space = () => {
|
|
13
|
+
if (!comments) comments = Object.entries(parse.comment).map(([s, e]) => [s, e, s.charCodeAt(0)]);
|
|
14
|
+
for (var cc; (cc = space()); ) {
|
|
15
|
+
for (var j = 0, c; c = comments[j++]; ) {
|
|
16
|
+
if (cc === c[2] && cur.substr(idx, c[0].length) === c[0]) {
|
|
17
|
+
var i = idx + c[0].length;
|
|
18
|
+
if (c[1] === '\n') while (cur.charCodeAt(i) >= SPACE) i++;
|
|
19
|
+
else { while (cur[i] && cur.substr(i, c[1].length) !== c[1]) i++; if (cur[i]) i += c[1].length; }
|
|
20
|
+
seek(i); cc = 0; break;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (cc) return cc;
|
|
24
|
+
}
|
|
25
|
+
return cc;
|
|
26
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Destructuring patterns and binding
|
|
3
|
+
*
|
|
4
|
+
* Handles: [a, b] = arr, {x, y} = obj, [a, ...rest] = arr, {x = default} = obj
|
|
5
|
+
*/
|
|
6
|
+
import { compile } from '../parse.js';
|
|
7
|
+
|
|
8
|
+
// Destructure value into context
|
|
9
|
+
export const destructure = (pattern, value, ctx) => {
|
|
10
|
+
if (typeof pattern === 'string') { ctx[pattern] = value; return; }
|
|
11
|
+
const [op, ...items] = pattern;
|
|
12
|
+
if (op === '{}') {
|
|
13
|
+
for (const item of items) {
|
|
14
|
+
let key, binding, def;
|
|
15
|
+
if (item[0] === '=') [, [, key, binding], def] = item;
|
|
16
|
+
else [, key, binding] = item;
|
|
17
|
+
let val = value[key];
|
|
18
|
+
if (val === undefined && def) val = compile(def)(ctx);
|
|
19
|
+
destructure(binding, val, ctx);
|
|
20
|
+
}
|
|
21
|
+
} else if (op === '[]') {
|
|
22
|
+
let i = 0;
|
|
23
|
+
for (const item of items) {
|
|
24
|
+
if (item === null) { i++; continue; }
|
|
25
|
+
if (Array.isArray(item) && item[0] === '...') { ctx[item[1]] = value.slice(i); break; }
|
|
26
|
+
let binding = item, def;
|
|
27
|
+
if (Array.isArray(item) && item[0] === '=') [, binding, def] = item;
|
|
28
|
+
let val = value[i++];
|
|
29
|
+
if (val === undefined && def) val = compile(def)(ctx);
|
|
30
|
+
destructure(binding, val, ctx);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Function declarations and expressions
|
|
2
|
+
import { space, next, parse, parens, expr, operator, compile } from '../parse.js';
|
|
3
|
+
import { RETURN } from './loop.js';
|
|
4
|
+
import { keyword, block } from './block.js';
|
|
5
|
+
|
|
6
|
+
const TOKEN = 200;
|
|
7
|
+
|
|
8
|
+
keyword('function', TOKEN, () => {
|
|
9
|
+
space();
|
|
10
|
+
const name = next(parse.id);
|
|
11
|
+
name && space();
|
|
12
|
+
return ['function', name, parens() || null, block()];
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Compile
|
|
16
|
+
operator('function', (name, params, body) => {
|
|
17
|
+
body = body ? compile(body) : () => undefined;
|
|
18
|
+
// Normalize params: null → [], 'x' → ['x'], [',', 'a', 'b'] → ['a', 'b']
|
|
19
|
+
const ps = !params ? [] : params[0] === ',' ? params.slice(1) : [params];
|
|
20
|
+
// Check for rest param
|
|
21
|
+
let restName = null, restIdx = -1;
|
|
22
|
+
const last = ps[ps.length - 1];
|
|
23
|
+
if (Array.isArray(last) && last[0] === '...') {
|
|
24
|
+
restIdx = ps.length - 1;
|
|
25
|
+
restName = last[1];
|
|
26
|
+
ps.length--;
|
|
27
|
+
}
|
|
28
|
+
return ctx => {
|
|
29
|
+
const fn = (...args) => {
|
|
30
|
+
const l = {};
|
|
31
|
+
ps.forEach((p, i) => l[p] = args[i]);
|
|
32
|
+
if (restName) l[restName] = args.slice(restIdx);
|
|
33
|
+
const fnCtx = new Proxy(l, {
|
|
34
|
+
get: (l, k) => k in l ? l[k] : ctx[k],
|
|
35
|
+
set: (l, k, v) => ((k in l ? l : ctx)[k] = v, true),
|
|
36
|
+
has: (l, k) => k in l || k in ctx
|
|
37
|
+
});
|
|
38
|
+
try { return body(fnCtx); }
|
|
39
|
+
catch (e) { if (e?.type === RETURN) return e.value; throw e; }
|
|
40
|
+
};
|
|
41
|
+
if (name) ctx[name] = fn;
|
|
42
|
+
return fn;
|
|
43
|
+
};
|
|
44
|
+
});
|
package/feature/group.js
CHANGED
|
@@ -1,12 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
nary(',',
|
|
12
|
-
nary(';',
|
|
1
|
+
import { nary, group, operator, compile } from '../parse.js';
|
|
2
|
+
import { BREAK, CONTINUE } from './loop.js';
|
|
3
|
+
import { prop } from './access.js';
|
|
4
|
+
|
|
5
|
+
const STATEMENT = 5, SEQ = 10, ACCESS = 170;
|
|
6
|
+
|
|
7
|
+
// (a,b,c), (a) — uses ACCESS to avoid conflict with ?.
|
|
8
|
+
group('()', ACCESS);
|
|
9
|
+
|
|
10
|
+
// Sequences
|
|
11
|
+
nary(',', SEQ);
|
|
12
|
+
nary(';', STATEMENT, true); // right-assoc to allow same-prec statements
|
|
13
|
+
|
|
14
|
+
// Compile
|
|
15
|
+
const err = msg => { throw Error(msg) };
|
|
16
|
+
operator('()', (a, b) => {
|
|
17
|
+
// Group: (expr) - no second argument means grouping, not call
|
|
18
|
+
if (b === undefined) return a == null ? err('Empty ()') : compile(a);
|
|
19
|
+
// Validate: no sparse arguments in calls
|
|
20
|
+
const hasSparse = n => n?.[0] === ',' && n.slice(1).some(a => a == null || hasSparse(a));
|
|
21
|
+
if (hasSparse(b)) err('Empty argument');
|
|
22
|
+
const args = !b ? () => [] :
|
|
23
|
+
b[0] === ',' ? (b = b.slice(1).map(compile), ctx => b.map(arg => arg(ctx))) :
|
|
24
|
+
(b = compile(b), ctx => [b(ctx)]);
|
|
25
|
+
return prop(a, (obj, path, ctx) => obj[path](...args(ctx)), true);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// sequence returns last evaluated value; catches BREAK/CONTINUE and attaches result
|
|
29
|
+
const seq = (...args) => (args = args.map(compile), ctx => {
|
|
30
|
+
let r;
|
|
31
|
+
for (const arg of args) {
|
|
32
|
+
try { r = arg(ctx); }
|
|
33
|
+
catch (e) {
|
|
34
|
+
if (e?.type === BREAK || e?.type === CONTINUE) { e.value = r; throw e; }
|
|
35
|
+
throw e;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return r;
|
|
39
|
+
});
|
|
40
|
+
operator(',', seq);
|
|
41
|
+
operator(';', seq);
|
package/feature/if.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// If/else statement - else consumed internally
|
|
2
|
+
import { space, skip, parens, word, idx, seek, operator, compile } from '../parse.js';
|
|
3
|
+
import { body, keyword } from './block.js';
|
|
4
|
+
|
|
5
|
+
const STATEMENT = 5, SEMI = 59;
|
|
6
|
+
|
|
7
|
+
// Check for `else` after optional semicolon
|
|
8
|
+
const checkElse = () => {
|
|
9
|
+
const from = idx;
|
|
10
|
+
if (space() === SEMI) skip();
|
|
11
|
+
space();
|
|
12
|
+
if (word('else')) return skip(4), true;
|
|
13
|
+
return seek(from), false;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// if (cond) body [else body] - self-contained
|
|
17
|
+
keyword('if', STATEMENT + 1, () => {
|
|
18
|
+
space();
|
|
19
|
+
const node = ['if', parens(), body()];
|
|
20
|
+
if (checkElse()) node.push(body());
|
|
21
|
+
return node;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Compile
|
|
25
|
+
operator('if', (cond, body, alt) => {
|
|
26
|
+
cond = compile(cond); body = compile(body); alt = alt !== undefined ? compile(alt) : null;
|
|
27
|
+
return ctx => cond(ctx) ? body(ctx) : alt?.(ctx);
|
|
28
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Literal values
|
|
3
|
+
*
|
|
4
|
+
* true, false, null, undefined, NaN, Infinity
|
|
5
|
+
*/
|
|
6
|
+
import { literal } from '../parse.js';
|
|
7
|
+
|
|
8
|
+
literal('true', true);
|
|
9
|
+
literal('false', false);
|
|
10
|
+
literal('null', null);
|
|
11
|
+
literal('undefined', undefined);
|
|
12
|
+
literal('NaN', NaN);
|
|
13
|
+
literal('Infinity', Infinity);
|
package/feature/loop.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// Loops: while, do-while, for, for await, break, continue, return
|
|
2
|
+
import { expr, skip, space, parse, word, parens, cur, idx, operator, compile } from '../parse.js';
|
|
3
|
+
import { body, keyword } from './block.js';
|
|
4
|
+
import { destructure } from './destruct.js';
|
|
5
|
+
|
|
6
|
+
// Control flow symbols
|
|
7
|
+
export const BREAK = Symbol('break'), CONTINUE = Symbol('continue'), RETURN = Symbol('return');
|
|
8
|
+
|
|
9
|
+
// Loop body executor - catches control flow and returns status
|
|
10
|
+
export const loop = (body, ctx) => {
|
|
11
|
+
try { return { v: body(ctx) }; }
|
|
12
|
+
catch (e) {
|
|
13
|
+
if (e?.type === BREAK) return { b: 1 };
|
|
14
|
+
if (e?.type === CONTINUE) return { c: 1 };
|
|
15
|
+
if (e?.type === RETURN) return { r: 1, v: e.value };
|
|
16
|
+
throw e;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const STATEMENT = 5, CBRACE = 125, SEMI = 59;
|
|
21
|
+
|
|
22
|
+
keyword('while', STATEMENT + 1, () => (space(), ['while', parens(), body()]));
|
|
23
|
+
keyword('do', STATEMENT + 1, () => (b => (space(), skip(5), space(), ['do', b, parens()]))(body()));
|
|
24
|
+
|
|
25
|
+
// for / for await
|
|
26
|
+
keyword('for', STATEMENT + 1, () => {
|
|
27
|
+
space();
|
|
28
|
+
// for await (x of y)
|
|
29
|
+
if (word('await')) {
|
|
30
|
+
skip(5);
|
|
31
|
+
space();
|
|
32
|
+
return ['for await', parens(), body()];
|
|
33
|
+
}
|
|
34
|
+
return ['for', parens(), body()];
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
keyword('break', STATEMENT + 1, () => ['break']);
|
|
38
|
+
keyword('continue', STATEMENT + 1, () => ['continue']);
|
|
39
|
+
keyword('return', STATEMENT + 1, () => {
|
|
40
|
+
parse.asi && (parse.newline = false);
|
|
41
|
+
space();
|
|
42
|
+
const c = cur.charCodeAt(idx);
|
|
43
|
+
return !c || c === CBRACE || c === SEMI || parse.newline ? ['return'] : ['return', expr(STATEMENT)];
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Compile
|
|
47
|
+
operator('while', (cond, body) => {
|
|
48
|
+
cond = compile(cond); body = compile(body);
|
|
49
|
+
return ctx => {
|
|
50
|
+
let r, res;
|
|
51
|
+
while (cond(ctx)) if ((r = loop(body, ctx)).b) break; else if (r.r) return r.v; else if (!r.c) res = r.v;
|
|
52
|
+
return res;
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
operator('do', (body, cond) => {
|
|
57
|
+
body = compile(body); cond = compile(cond);
|
|
58
|
+
return ctx => {
|
|
59
|
+
let r, res;
|
|
60
|
+
do { if ((r = loop(body, ctx)).b) break; else if (r.r) return r.v; else if (!r.c) res = r.v; } while (cond(ctx));
|
|
61
|
+
return res;
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
operator('for', (head, body) => {
|
|
66
|
+
// Normalize head: [';', init, cond, step] or single expr (for-in/of)
|
|
67
|
+
if (Array.isArray(head) && head[0] === ';') {
|
|
68
|
+
let [, init, cond, step] = head;
|
|
69
|
+
init = init ? compile(init) : null;
|
|
70
|
+
cond = cond ? compile(cond) : () => true;
|
|
71
|
+
step = step ? compile(step) : null;
|
|
72
|
+
body = compile(body);
|
|
73
|
+
return ctx => {
|
|
74
|
+
let r, res;
|
|
75
|
+
for (init?.(ctx); cond(ctx); step?.(ctx))
|
|
76
|
+
if ((r = loop(body, ctx)).b) break; else if (r.r) return r.v; else if (!r.c) res = r.v;
|
|
77
|
+
return res;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// For-in/of: head is ['in', lhs, rhs] or ['of', lhs, rhs]
|
|
81
|
+
if (Array.isArray(head) && (head[0] === 'in' || head[0] === 'of')) {
|
|
82
|
+
let [op, lhs, rhs] = head;
|
|
83
|
+
// Extract name from declaration: ['let', 'x'] → 'x'
|
|
84
|
+
if (Array.isArray(lhs) && (lhs[0] === 'let' || lhs[0] === 'const' || lhs[0] === 'var')) lhs = lhs[1];
|
|
85
|
+
if (op === 'in') return forIn(lhs, rhs, body);
|
|
86
|
+
if (op === 'of') return forOf(lhs, rhs, body);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const forOf = (name, iterable, body) => {
|
|
91
|
+
iterable = compile(iterable); body = compile(body);
|
|
92
|
+
const isPattern = Array.isArray(name);
|
|
93
|
+
return ctx => {
|
|
94
|
+
let r, res;
|
|
95
|
+
const prev = isPattern ? null : ctx[name];
|
|
96
|
+
for (const val of iterable(ctx)) {
|
|
97
|
+
if (isPattern) destructure(name, val, ctx); else ctx[name] = val;
|
|
98
|
+
if ((r = loop(body, ctx)).b) break; else if (r.r) return r.v; else if (!r.c) res = r.v;
|
|
99
|
+
}
|
|
100
|
+
if (!isPattern) ctx[name] = prev;
|
|
101
|
+
return res;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const forIn = (name, obj, body) => {
|
|
106
|
+
obj = compile(obj); body = compile(body);
|
|
107
|
+
const isPattern = Array.isArray(name);
|
|
108
|
+
return ctx => {
|
|
109
|
+
let r, res;
|
|
110
|
+
const prev = isPattern ? null : ctx[name];
|
|
111
|
+
for (const key in obj(ctx)) {
|
|
112
|
+
if (isPattern) destructure(name, key, ctx); else ctx[name] = key;
|
|
113
|
+
if ((r = loop(body, ctx)).b) break; else if (r.r) return r.v; else if (!r.c) res = r.v;
|
|
114
|
+
}
|
|
115
|
+
if (!isPattern) ctx[name] = prev;
|
|
116
|
+
return res;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
operator('break', () => () => { throw { type: BREAK }; });
|
|
121
|
+
operator('continue', () => () => { throw { type: CONTINUE }; });
|
|
122
|
+
operator('return', val => (val = val !== undefined ? compile(val) : null,
|
|
123
|
+
ctx => { throw { type: RETURN, value: val?.(ctx) }; }));
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import/Export with contextual 'from' operator
|
|
3
|
+
*
|
|
4
|
+
* AST:
|
|
5
|
+
* import './x.js' → ['import', path]
|
|
6
|
+
* import X from './x.js' → ['import', ['from', 'X', path]]
|
|
7
|
+
* import { a, b } from './x' → ['import', ['from', ['{}', ...], path]]
|
|
8
|
+
* import * as X from './x.js' → ['import', ['from', ['as', '*', X], path]]
|
|
9
|
+
* export { a } from './x' → ['export', ['from', ['{}', ...], path]]
|
|
10
|
+
* export const x = 1 → ['export', decl]
|
|
11
|
+
*/
|
|
12
|
+
import { token, expr, space, lookup, skip } from '../parse.js';
|
|
13
|
+
import { keyword } from './block.js';
|
|
14
|
+
|
|
15
|
+
const STATEMENT = 5, SEQ = 10, STAR = 42;
|
|
16
|
+
|
|
17
|
+
// * as prefix in import context (import * as X)
|
|
18
|
+
const prevStar = lookup[STAR];
|
|
19
|
+
lookup[STAR] = (a, prec) => !a ? (skip(), '*') : prevStar?.(a, prec);
|
|
20
|
+
|
|
21
|
+
// 'from' as contextual binary - only after import-like LHS (not = or ,), false in prefix for identifier fallback
|
|
22
|
+
token('from', SEQ + 1, a => !a ? false : a[0] !== '=' && a[0] !== ',' && (space(), ['from', a, expr(SEQ + 1)]));
|
|
23
|
+
|
|
24
|
+
// 'as' for aliasing: * as X, { a as b }. False in prefix for identifier fallback
|
|
25
|
+
token('as', SEQ + 2, a => !a ? false : (space(), ['as', a, expr(SEQ + 2)]));
|
|
26
|
+
|
|
27
|
+
// import: prefix that parses specifiers + from + path
|
|
28
|
+
keyword('import', STATEMENT, () => (space(), ['import', expr(SEQ)]));
|
|
29
|
+
|
|
30
|
+
// export: prefix for declarations or re-exports (use STATEMENT to capture const/let/function)
|
|
31
|
+
keyword('export', STATEMENT, () => (space(), ['export', expr(STATEMENT)]));
|
|
32
|
+
|
|
33
|
+
// default: prefix for export default
|
|
34
|
+
keyword('default', SEQ + 1, () => (space(), ['default', expr(SEQ)]));
|
|
35
|
+
|
|
36
|
+
// Compile stubs - import/export are parse-only (no runtime semantics)
|
|
37
|
+
import { operator, compile } from '../parse.js';
|
|
38
|
+
operator('import', () => () => undefined);
|
|
39
|
+
operator('export', () => () => undefined);
|
|
40
|
+
operator('from', (a, b) => () => undefined);
|
|
41
|
+
operator('as', (a, b) => () => undefined);
|
|
42
|
+
operator('default', (a) => compile(a));
|
package/feature/number.js
CHANGED
|
@@ -1,13 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Numbers with configurable prefix notation
|
|
3
|
+
*
|
|
4
|
+
* Configurable via parse.number: { '0x': 16, '0b': 2, '0o': 8 }
|
|
5
|
+
*/
|
|
6
|
+
import { parse, lookup, next, err, skip, idx, cur } from '../parse.js';
|
|
3
7
|
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
a = +next(c => (c === PERIOD) || (c >= _0 && c <= _9) || (c === _E || c === _e ? 2 : 0))
|
|
7
|
-
) != a ? err() : a]
|
|
8
|
+
const PERIOD = 46, _0 = 48, _9 = 57, _E = 69, _e = 101, PLUS = 43, MINUS = 45;
|
|
9
|
+
const _a = 97, _f = 102, _A = 65, _F = 70;
|
|
8
10
|
|
|
9
|
-
// .
|
|
10
|
-
|
|
11
|
+
// Decimal number - check for .. range operator (don't consume . if followed by .)
|
|
12
|
+
const num = a => [, (
|
|
13
|
+
a = +next(c =>
|
|
14
|
+
// . is decimal only if NOT followed by another . (range operator)
|
|
15
|
+
(c === PERIOD && cur.charCodeAt(idx + 1) !== PERIOD) ||
|
|
16
|
+
(c >= _0 && c <= _9) ||
|
|
17
|
+
((c === _E || c === _e) && ((c = cur.charCodeAt(idx + 1)) >= _0 && c <= _9 || c === PLUS || c === MINUS) ? 2 : 0)
|
|
18
|
+
)
|
|
19
|
+
) != a ? err() : a];
|
|
11
20
|
|
|
12
|
-
//
|
|
13
|
-
|
|
21
|
+
// Char test for prefix base
|
|
22
|
+
const charTest = {
|
|
23
|
+
2: c => c === 48 || c === 49,
|
|
24
|
+
8: c => c >= 48 && c <= 55,
|
|
25
|
+
16: c => (c >= _0 && c <= _9) || (c >= _a && c <= _f) || (c >= _A && c <= _F)
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Default: no prefixes
|
|
29
|
+
parse.number = null;
|
|
30
|
+
|
|
31
|
+
// .1 (but not .. range)
|
|
32
|
+
lookup[PERIOD] = a => !a && cur.charCodeAt(idx + 1) !== PERIOD && num();
|
|
33
|
+
|
|
34
|
+
// 0-9: check parse.number for prefix config
|
|
35
|
+
for (let i = _0; i <= _9; i++) lookup[i] = a => a ? void 0 : num();
|
|
36
|
+
lookup[_0] = a => {
|
|
37
|
+
if (a) return;
|
|
38
|
+
const cfg = parse.number;
|
|
39
|
+
if (cfg) {
|
|
40
|
+
for (const [pre, base] of Object.entries(cfg)) {
|
|
41
|
+
if (pre[0] === '0' && cur[idx + 1]?.toLowerCase() === pre[1]) {
|
|
42
|
+
skip(2);
|
|
43
|
+
return [, parseInt(next(charTest[base]), base)];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return num();
|
|
48
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arithmetic operators
|
|
3
|
+
*
|
|
4
|
+
* + - * / %
|
|
5
|
+
* Unary: + -
|
|
6
|
+
*/
|
|
7
|
+
import { binary, unary, operator, compile } from '../../parse.js';
|
|
8
|
+
|
|
9
|
+
const ADD = 110, MULT = 120, PREFIX = 140;
|
|
10
|
+
|
|
11
|
+
binary('+', ADD);
|
|
12
|
+
binary('-', ADD);
|
|
13
|
+
binary('*', MULT);
|
|
14
|
+
binary('/', MULT);
|
|
15
|
+
binary('%', MULT);
|
|
16
|
+
|
|
17
|
+
unary('+', PREFIX);
|
|
18
|
+
unary('-', PREFIX);
|
|
19
|
+
|
|
20
|
+
// Compile
|
|
21
|
+
operator('+', (a, b) => b !== undefined ?
|
|
22
|
+
(a = compile(a), b = compile(b), ctx => a(ctx) + b(ctx)) :
|
|
23
|
+
(a = compile(a), ctx => +a(ctx)));
|
|
24
|
+
operator('-', (a, b) => b !== undefined ?
|
|
25
|
+
(a = compile(a), b = compile(b), ctx => a(ctx) - b(ctx)) :
|
|
26
|
+
(a = compile(a), ctx => -a(ctx)));
|
|
27
|
+
operator('*', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) * b(ctx)));
|
|
28
|
+
operator('/', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) / b(ctx)));
|
|
29
|
+
operator('%', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) % b(ctx)));
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arrow function operator
|
|
3
|
+
*
|
|
4
|
+
* (a, b) => expr → arrow function
|
|
5
|
+
*
|
|
6
|
+
* Common in: JS, TS, Java, C#, Kotlin, Scala
|
|
7
|
+
*/
|
|
8
|
+
import { binary, operator, compile } from '../../parse.js';
|
|
9
|
+
|
|
10
|
+
const ASSIGN = 20;
|
|
11
|
+
|
|
12
|
+
binary('=>', ASSIGN, true);
|
|
13
|
+
|
|
14
|
+
// Compile
|
|
15
|
+
operator('=>', (a, b) => {
|
|
16
|
+
a = a[0] === '()' ? a[1] : a;
|
|
17
|
+
a = !a ? [] : a[0] === ',' ? a.slice(1) : [a];
|
|
18
|
+
let restIdx = -1, restName = null;
|
|
19
|
+
if (a.length && Array.isArray(a[a.length - 1]) && a[a.length - 1][0] === '...') {
|
|
20
|
+
restIdx = a.length - 1;
|
|
21
|
+
restName = a[restIdx][1];
|
|
22
|
+
a = a.slice(0, -1);
|
|
23
|
+
}
|
|
24
|
+
b = compile(b[0] === '{}' ? b[1] : b);
|
|
25
|
+
return (ctx = null) => {
|
|
26
|
+
ctx = Object.create(ctx);
|
|
27
|
+
return (...args) => {
|
|
28
|
+
a.forEach((p, i) => ctx[p] = args[i]);
|
|
29
|
+
if (restName) ctx[restName] = args.slice(restIdx);
|
|
30
|
+
return b(ctx);
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logical/nullish assignment operators + destructuring
|
|
3
|
+
*
|
|
4
|
+
* ||= &&= ??= + destructuring support for let/const/var
|
|
5
|
+
*/
|
|
6
|
+
import { binary, operator, compile } from '../../parse.js';
|
|
7
|
+
import { destructure } from '../destruct.js';
|
|
8
|
+
import { isLval, prop } from '../access.js';
|
|
9
|
+
|
|
10
|
+
const ASSIGN = 20;
|
|
11
|
+
const err = msg => { throw Error(msg) };
|
|
12
|
+
|
|
13
|
+
binary('||=', ASSIGN, true);
|
|
14
|
+
binary('&&=', ASSIGN, true);
|
|
15
|
+
binary('??=', ASSIGN, true);
|
|
16
|
+
|
|
17
|
+
// Override = to support destructuring
|
|
18
|
+
operator('=', (a, b) => {
|
|
19
|
+
// Handle let/const/var declarations: ['=', ['let', pattern], value]
|
|
20
|
+
if (Array.isArray(a) && (a[0] === 'let' || a[0] === 'const' || a[0] === 'var')) {
|
|
21
|
+
const pattern = a[1];
|
|
22
|
+
b = compile(b);
|
|
23
|
+
if (typeof pattern === 'string') return ctx => { ctx[pattern] = b(ctx); };
|
|
24
|
+
return ctx => destructure(pattern, b(ctx), ctx);
|
|
25
|
+
}
|
|
26
|
+
isLval(a) || err('Invalid assignment target');
|
|
27
|
+
return (b = compile(b), prop(a, (obj, path, ctx) => obj[path] = b(ctx)));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Compile
|
|
31
|
+
operator('||=', (a, b) => (isLval(a) || err('Invalid assignment target'), b = compile(b), prop(a, (o, k, ctx) => o[k] ||= b(ctx))));
|
|
32
|
+
operator('&&=', (a, b) => (isLval(a) || err('Invalid assignment target'), b = compile(b), prop(a, (o, k, ctx) => o[k] &&= b(ctx))));
|
|
33
|
+
operator('??=', (a, b) => (isLval(a) || err('Invalid assignment target'), b = compile(b), prop(a, (o, k, ctx) => o[k] ??= b(ctx))));
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assignment operators (C-family)
|
|
3
|
+
*
|
|
4
|
+
* = += -= *= /= %= |= &= ^= >>= <<=
|
|
5
|
+
* Note: **= is in pow.js, >>>= ||= &&= ??= are JS-specific (in assignment-js.js)
|
|
6
|
+
*/
|
|
7
|
+
import { binary, operator, compile } from '../../parse.js';
|
|
8
|
+
|
|
9
|
+
const ASSIGN = 20;
|
|
10
|
+
|
|
11
|
+
// Base assignment
|
|
12
|
+
binary('=', ASSIGN, true);
|
|
13
|
+
|
|
14
|
+
// Compound arithmetic
|
|
15
|
+
binary('+=', ASSIGN, true);
|
|
16
|
+
binary('-=', ASSIGN, true);
|
|
17
|
+
binary('*=', ASSIGN, true);
|
|
18
|
+
binary('/=', ASSIGN, true);
|
|
19
|
+
binary('%=', ASSIGN, true);
|
|
20
|
+
|
|
21
|
+
// Compound bitwise
|
|
22
|
+
binary('|=', ASSIGN, true);
|
|
23
|
+
binary('&=', ASSIGN, true);
|
|
24
|
+
binary('^=', ASSIGN, true);
|
|
25
|
+
binary('>>=', ASSIGN, true);
|
|
26
|
+
binary('<<=', ASSIGN, true);
|
|
27
|
+
|
|
28
|
+
// Simple assign helper for x, a.b, a[b], (x)
|
|
29
|
+
const assign = (a, fn, obj, key) =>
|
|
30
|
+
typeof a === 'string' ? ctx => fn(ctx, a, ctx) :
|
|
31
|
+
a[0] === '.' ? (obj = compile(a[1]), key = a[2], ctx => fn(obj(ctx), key, ctx)) :
|
|
32
|
+
a[0] === '[]' && a.length === 3 ? (obj = compile(a[1]), key = compile(a[2]), ctx => fn(obj(ctx), key(ctx), ctx)) :
|
|
33
|
+
a[0] === '()' && a.length === 2 ? assign(a[1], fn) : // unwrap parens: (x) = 1
|
|
34
|
+
(() => { throw Error('Invalid assignment target') })();
|
|
35
|
+
|
|
36
|
+
// Compile
|
|
37
|
+
operator('=', (a, b) => (b = compile(b), assign(a, (o, k, ctx) => o[k] = b(ctx))));
|
|
38
|
+
operator('+=', (a, b) => (b = compile(b), assign(a, (o, k, ctx) => o[k] += b(ctx))));
|
|
39
|
+
operator('-=', (a, b) => (b = compile(b), assign(a, (o, k, ctx) => o[k] -= b(ctx))));
|
|
40
|
+
operator('*=', (a, b) => (b = compile(b), assign(a, (o, k, ctx) => o[k] *= b(ctx))));
|
|
41
|
+
operator('/=', (a, b) => (b = compile(b), assign(a, (o, k, ctx) => o[k] /= b(ctx))));
|
|
42
|
+
operator('%=', (a, b) => (b = compile(b), assign(a, (o, k, ctx) => o[k] %= b(ctx))));
|
|
43
|
+
operator('|=', (a, b) => (b = compile(b), assign(a, (o, k, ctx) => o[k] |= b(ctx))));
|
|
44
|
+
operator('&=', (a, b) => (b = compile(b), assign(a, (o, k, ctx) => o[k] &= b(ctx))));
|
|
45
|
+
operator('^=', (a, b) => (b = compile(b), assign(a, (o, k, ctx) => o[k] ^= b(ctx))));
|
|
46
|
+
operator('>>=', (a, b) => (b = compile(b), assign(a, (o, k, ctx) => o[k] >>= b(ctx))));
|
|
47
|
+
operator('<<=', (a, b) => (b = compile(b), assign(a, (o, k, ctx) => o[k] <<= b(ctx))));
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unsigned right shift operators
|
|
3
|
+
*
|
|
4
|
+
* >>> >>>=
|
|
5
|
+
*/
|
|
6
|
+
import { binary, operator, compile } from '../../parse.js';
|
|
7
|
+
import { isLval, prop } from '../access.js';
|
|
8
|
+
|
|
9
|
+
const ASSIGN = 20, SHIFT = 100;
|
|
10
|
+
const err = msg => { throw Error(msg) };
|
|
11
|
+
|
|
12
|
+
binary('>>>', SHIFT);
|
|
13
|
+
binary('>>>=', ASSIGN, true);
|
|
14
|
+
|
|
15
|
+
// Compile
|
|
16
|
+
operator('>>>', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) >>> b(ctx)));
|
|
17
|
+
operator('>>>=', (a, b) => (isLval(a) || err('Invalid assignment target'), b = compile(b), prop(a, (o, k, ctx) => o[k] >>>= b(ctx))));
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bitwise operators (C-family)
|
|
3
|
+
*
|
|
4
|
+
* | & ^ ~ >> <<
|
|
5
|
+
* Note: >>> is JS-specific (in bitwise-js.js)
|
|
6
|
+
*/
|
|
7
|
+
import { binary, unary, operator, compile } from '../../parse.js';
|
|
8
|
+
|
|
9
|
+
const OR = 50, XOR = 60, AND = 70, SHIFT = 100, PREFIX = 140;
|
|
10
|
+
|
|
11
|
+
// Base operators first (tried last in chain)
|
|
12
|
+
binary('|', OR);
|
|
13
|
+
binary('&', AND);
|
|
14
|
+
binary('^', XOR);
|
|
15
|
+
|
|
16
|
+
// Shifts (after < >)
|
|
17
|
+
binary('>>', SHIFT);
|
|
18
|
+
binary('<<', SHIFT);
|
|
19
|
+
|
|
20
|
+
// Unary
|
|
21
|
+
unary('~', PREFIX);
|
|
22
|
+
|
|
23
|
+
// Compile
|
|
24
|
+
operator('~', a => (a = compile(a), ctx => ~a(ctx)));
|
|
25
|
+
operator('|', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) | b(ctx)));
|
|
26
|
+
operator('&', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) & b(ctx)));
|
|
27
|
+
operator('^', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) ^ b(ctx)));
|
|
28
|
+
operator('>>', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) >> b(ctx)));
|
|
29
|
+
operator('<<', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) << b(ctx)));
|