subscript 9.2.0 → 10.0.1
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 +101 -184
- package/feature/access.js +68 -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 -5
- package/feature/control.js +2 -142
- package/feature/destruct.js +33 -0
- package/feature/function.js +44 -0
- package/feature/group.js +13 -9
- package/feature/if.js +23 -38
- package/feature/literal.js +13 -0
- package/feature/loop.js +107 -106
- package/feature/module.js +42 -0
- package/feature/number.js +41 -38
- 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 +26 -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 -17
- 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 +51 -41
- package/jessie.js +31 -0
- package/jessie.min.js +8 -0
- package/justin.js +39 -48
- package/justin.min.js +8 -4
- package/package.json +15 -16
- package/parse.js +146 -0
- package/subscript.d.ts +45 -5
- package/subscript.js +62 -22
- package/subscript.min.js +5 -4
- 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 -23
- 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 -45
- package/src/parse.d.ts +0 -22
- package/src/parse.js +0 -113
- package/src/stringify.js +0 -27
- /package/{LICENSE → license} +0 -0
|
@@ -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,45 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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';
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const _a = 97, _f = 102, _A = 65, _F = 70
|
|
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
|
-
const num =
|
|
11
|
-
a = +next(c =>
|
|
12
|
-
|
|
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];
|
|
13
20
|
|
|
14
|
-
//
|
|
15
|
-
|
|
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
|
+
};
|
|
16
27
|
|
|
17
|
-
//
|
|
18
|
-
|
|
28
|
+
// Default: no prefixes
|
|
29
|
+
parse.number = null;
|
|
19
30
|
|
|
20
|
-
//
|
|
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();
|
|
21
36
|
lookup[_0] = a => {
|
|
22
|
-
if (a) return
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// Octal: 0o
|
|
32
|
-
if (nextChar === _o || nextChar === _O) {
|
|
33
|
-
skip(); skip() // consume '0o'
|
|
34
|
-
const s = next(c => c >= 48 && c <= 55) // 0-7
|
|
35
|
-
return [, parseInt(s, 8)]
|
|
36
|
-
}
|
|
37
|
-
// Hex: 0x
|
|
38
|
-
if (nextChar === _x || nextChar === _X) {
|
|
39
|
-
skip(); skip() // consume '0x'
|
|
40
|
-
const s = next(c => (c >= _0 && c <= _9) || (c >= _a && c <= _f) || (c >= _A && c <= _F))
|
|
41
|
-
return [, parseInt(s, 16)]
|
|
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
|
+
}
|
|
42
46
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
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,26 @@
|
|
|
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
|
+
// Register all assignment operators
|
|
12
|
+
'= += -= *= /= %= |= &= ^= >>= <<='.split(' ').map(op => binary(op, ASSIGN, true));
|
|
13
|
+
|
|
14
|
+
// Simple assign helper for x, a.b, a[b], (x)
|
|
15
|
+
const assign = (a, fn, obj, key) =>
|
|
16
|
+
typeof a === 'string' ? ctx => fn(ctx, a, ctx) :
|
|
17
|
+
a[0] === '.' ? (obj = compile(a[1]), key = a[2], ctx => fn(obj(ctx), key, ctx)) :
|
|
18
|
+
a[0] === '[]' && a.length === 3 ? (obj = compile(a[1]), key = compile(a[2]), ctx => fn(obj(ctx), key(ctx), ctx)) :
|
|
19
|
+
a[0] === '()' && a.length === 2 ? assign(a[1], fn) :
|
|
20
|
+
(() => { throw Error('Invalid assignment target') })();
|
|
21
|
+
|
|
22
|
+
// Compile - use Function to generate operator implementations
|
|
23
|
+
const ops = { '=': (o,k,v)=>o[k]=v, '+=': (o,k,v)=>o[k]+=v, '-=': (o,k,v)=>o[k]-=v, '*=': (o,k,v)=>o[k]*=v,
|
|
24
|
+
'/=': (o,k,v)=>o[k]/=v, '%=': (o,k,v)=>o[k]%=v, '|=': (o,k,v)=>o[k]|=v, '&=': (o,k,v)=>o[k]&=v,
|
|
25
|
+
'^=': (o,k,v)=>o[k]^=v, '>>=': (o,k,v)=>o[k]>>=v, '<<=': (o,k,v)=>o[k]<<=v };
|
|
26
|
+
for (const op in ops) operator(op, (a, b) => (b = compile(b), assign(a, (o, k, ctx) => ops[op](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)));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comparison operators
|
|
3
|
+
*
|
|
4
|
+
* < > <= >=
|
|
5
|
+
*/
|
|
6
|
+
import { binary, operator, compile } from '../../parse.js';
|
|
7
|
+
|
|
8
|
+
const COMP = 90;
|
|
9
|
+
|
|
10
|
+
binary('<', COMP);
|
|
11
|
+
binary('>', COMP);
|
|
12
|
+
binary('<=', COMP);
|
|
13
|
+
binary('>=', COMP);
|
|
14
|
+
|
|
15
|
+
// Compile
|
|
16
|
+
operator('>', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) > b(ctx)));
|
|
17
|
+
operator('<', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) < b(ctx)));
|
|
18
|
+
operator('>=', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) >= b(ctx)));
|
|
19
|
+
operator('<=', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) <= b(ctx)));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defer operator
|
|
3
|
+
*
|
|
4
|
+
* defer expr: registers cleanup to run at scope exit
|
|
5
|
+
*
|
|
6
|
+
* Common in: Go, Swift, Zig
|
|
7
|
+
*/
|
|
8
|
+
import { unary, operator, compile } from '../../parse.js';
|
|
9
|
+
|
|
10
|
+
const PREFIX = 140;
|
|
11
|
+
|
|
12
|
+
unary('defer', PREFIX);
|
|
13
|
+
|
|
14
|
+
// Compile
|
|
15
|
+
operator('defer', a => (a = compile(a), ctx => { ctx.__deferred__ = ctx.__deferred__ || []; ctx.__deferred__.push(a); }));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Equality operators (base)
|
|
3
|
+
*
|
|
4
|
+
* == !=
|
|
5
|
+
* For === !== see equality-strict.js
|
|
6
|
+
*/
|
|
7
|
+
import { binary, operator, compile } from '../../parse.js';
|
|
8
|
+
|
|
9
|
+
const EQ = 80;
|
|
10
|
+
|
|
11
|
+
binary('==', EQ);
|
|
12
|
+
binary('!=', EQ);
|
|
13
|
+
|
|
14
|
+
// Compile
|
|
15
|
+
operator('==', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) == b(ctx)));
|
|
16
|
+
operator('!=', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) != b(ctx)));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity operators
|
|
3
|
+
*
|
|
4
|
+
* === !==
|
|
5
|
+
*/
|
|
6
|
+
import { binary, operator, compile } from '../../parse.js';
|
|
7
|
+
|
|
8
|
+
const EQ = 80;
|
|
9
|
+
|
|
10
|
+
binary('===', EQ);
|
|
11
|
+
binary('!==', EQ);
|
|
12
|
+
|
|
13
|
+
// Compile
|
|
14
|
+
operator('===', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) === b(ctx)));
|
|
15
|
+
operator('!==', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) !== b(ctx)));
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Increment/decrement operators
|
|
3
|
+
*
|
|
4
|
+
* ++ -- (prefix and postfix)
|
|
5
|
+
*/
|
|
6
|
+
import { token, expr, operator, compile } from '../../parse.js';
|
|
7
|
+
|
|
8
|
+
const POSTFIX = 150;
|
|
9
|
+
|
|
10
|
+
token('++', POSTFIX, a => a ? ['++', a, null] : ['++', expr(POSTFIX - 1)]);
|
|
11
|
+
token('--', POSTFIX, a => a ? ['--', a, null] : ['--', expr(POSTFIX - 1)]);
|
|
12
|
+
|
|
13
|
+
// Compile (b=null means postfix, b=undefined means prefix)
|
|
14
|
+
// Simple prop helper for increment - handles x, a.b, a[b], (x)
|
|
15
|
+
const inc = (a, fn, obj, key) =>
|
|
16
|
+
typeof a === 'string' ? ctx => fn(ctx, a) :
|
|
17
|
+
a[0] === '.' ? (obj = compile(a[1]), key = a[2], ctx => fn(obj(ctx), key)) :
|
|
18
|
+
a[0] === '[]' && a.length === 3 ? (obj = compile(a[1]), key = compile(a[2]), ctx => fn(obj(ctx), key(ctx))) :
|
|
19
|
+
a[0] === '()' && a.length === 2 ? inc(a[1], fn) : // unwrap parens: (x)++
|
|
20
|
+
(() => { throw Error('Invalid increment target') })();
|
|
21
|
+
|
|
22
|
+
operator('++', (a, b) => inc(a, b === null ? (o, k) => o[k]++ : (o, k) => ++o[k]));
|
|
23
|
+
operator('--', (a, b) => inc(a, b === null ? (o, k) => o[k]-- : (o, k) => --o[k]));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logical operators (base)
|
|
3
|
+
*
|
|
4
|
+
* ! && ||
|
|
5
|
+
* For ?? see nullish.js
|
|
6
|
+
*/
|
|
7
|
+
import { binary, unary, operator, compile } from '../../parse.js';
|
|
8
|
+
|
|
9
|
+
const LOR = 30, LAND = 40, PREFIX = 140;
|
|
10
|
+
|
|
11
|
+
// ! must be registered before != and !==
|
|
12
|
+
binary('!', PREFIX);
|
|
13
|
+
unary('!', PREFIX);
|
|
14
|
+
|
|
15
|
+
binary('||', LOR);
|
|
16
|
+
binary('&&', LAND);
|
|
17
|
+
|
|
18
|
+
// Compile
|
|
19
|
+
operator('!', a => (a = compile(a), ctx => !a(ctx)));
|
|
20
|
+
operator('||', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) || b(ctx)));
|
|
21
|
+
operator('&&', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) && b(ctx)));
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Membership operator
|
|
3
|
+
*
|
|
4
|
+
* in: key in object
|
|
5
|
+
* of: for-of iteration (parsed as binary in for head)
|
|
6
|
+
*
|
|
7
|
+
* Note: instanceof is in class.js (jessie feature)
|
|
8
|
+
*/
|
|
9
|
+
import { binary, operator, compile } from '../../parse.js';
|
|
10
|
+
|
|
11
|
+
const COMP = 90;
|
|
12
|
+
|
|
13
|
+
binary('in', COMP);
|
|
14
|
+
binary('of', COMP);
|
|
15
|
+
|
|
16
|
+
// Compile
|
|
17
|
+
operator('in', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) in b(ctx)));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nullish coalescing operator (JS-specific)
|
|
3
|
+
*
|
|
4
|
+
* ??
|
|
5
|
+
*/
|
|
6
|
+
import { binary, operator, compile } from '../../parse.js';
|
|
7
|
+
|
|
8
|
+
const LOR = 30;
|
|
9
|
+
|
|
10
|
+
binary('??', LOR);
|
|
11
|
+
|
|
12
|
+
// Compile
|
|
13
|
+
operator('??', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) ?? b(ctx)));
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional chaining operators
|
|
3
|
+
*
|
|
4
|
+
* a?.b → optional member access
|
|
5
|
+
* a?.[x] → optional computed access
|
|
6
|
+
* a?.() → optional call
|
|
7
|
+
*
|
|
8
|
+
* Common in: JS, TS, Swift, Kotlin, C#
|
|
9
|
+
*/
|
|
10
|
+
import { token, expr, skip, space, operator, compile } from '../../parse.js';
|
|
11
|
+
import { unsafe } from '../access.js';
|
|
12
|
+
|
|
13
|
+
const ACCESS = 170;
|
|
14
|
+
|
|
15
|
+
token('?.', ACCESS, (a, b) => {
|
|
16
|
+
if (!a) return;
|
|
17
|
+
const cc = space();
|
|
18
|
+
// Optional call: a?.()
|
|
19
|
+
if (cc === 40) { skip(); return ['?.()', a, expr(0, 41) || null]; }
|
|
20
|
+
// Optional computed: a?.[x]
|
|
21
|
+
if (cc === 91) { skip(); return ['?.[]', a, expr(0, 93)]; }
|
|
22
|
+
// Optional member: a?.b
|
|
23
|
+
b = expr(ACCESS);
|
|
24
|
+
return b ? ['?.', a, b] : void 0;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Compile
|
|
28
|
+
operator('?.', (a, b) => (a = compile(a), unsafe(b) ? () => undefined : ctx => a(ctx)?.[b]));
|
|
29
|
+
operator('?.[]', (a, b) => (a = compile(a), b = compile(b), ctx => { const k = b(ctx); return unsafe(k) ? undefined : a(ctx)?.[k]; }));
|
|
30
|
+
operator('?.()', (a, b) => {
|
|
31
|
+
const args = !b ? () => [] :
|
|
32
|
+
b[0] === ',' ? (b = b.slice(1).map(compile), ctx => b.map(arg => arg(ctx))) :
|
|
33
|
+
(b = compile(b), ctx => [b(ctx)]);
|
|
34
|
+
|
|
35
|
+
// Handle nested optional chain: a?.method?.() or a?.["method"]?.()
|
|
36
|
+
if (a[0] === '?.') {
|
|
37
|
+
const container = compile(a[1]);
|
|
38
|
+
const prop = a[2];
|
|
39
|
+
return unsafe(prop) ? () => undefined :
|
|
40
|
+
ctx => { const c = container(ctx); return c?.[prop]?.(...args(ctx)); };
|
|
41
|
+
}
|
|
42
|
+
if (a[0] === '?.[]') {
|
|
43
|
+
const container = compile(a[1]);
|
|
44
|
+
const prop = compile(a[2]);
|
|
45
|
+
return ctx => { const c = container(ctx); const p = prop(ctx); return unsafe(p) ? undefined : c?.[p]?.(...args(ctx)); };
|
|
46
|
+
}
|
|
47
|
+
// Handle a?.() where a is a.method or a[method] - need to bind this
|
|
48
|
+
if (a[0] === '.') {
|
|
49
|
+
const obj = compile(a[1]);
|
|
50
|
+
const prop = a[2];
|
|
51
|
+
return unsafe(prop) ? () => undefined :
|
|
52
|
+
ctx => { const o = obj(ctx); return o?.[prop]?.(...args(ctx)); };
|
|
53
|
+
}
|
|
54
|
+
if (a[0] === '[]' && a.length === 3) {
|
|
55
|
+
const obj = compile(a[1]);
|
|
56
|
+
const prop = compile(a[2]);
|
|
57
|
+
return ctx => { const o = obj(ctx); const p = prop(ctx); return unsafe(p) ? undefined : o?.[p]?.(...args(ctx)); };
|
|
58
|
+
}
|
|
59
|
+
const fn = compile(a);
|
|
60
|
+
return ctx => fn(ctx)?.(...args(ctx));
|
|
61
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exponentiation operator
|
|
3
|
+
*
|
|
4
|
+
* ** **=
|
|
5
|
+
*
|
|
6
|
+
* ES2016+, not in classic JS/C
|
|
7
|
+
*/
|
|
8
|
+
import { binary, operator, compile } from '../../parse.js';
|
|
9
|
+
import { isLval, prop } from '../access.js';
|
|
10
|
+
|
|
11
|
+
const EXP = 130, ASSIGN = 20;
|
|
12
|
+
|
|
13
|
+
binary('**', EXP, true);
|
|
14
|
+
binary('**=', ASSIGN, true);
|
|
15
|
+
|
|
16
|
+
// Compile
|
|
17
|
+
operator('**', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) ** b(ctx)));
|
|
18
|
+
const err = msg => { throw Error(msg) };
|
|
19
|
+
operator('**=', (a, b) => (isLval(a) || err('Invalid assignment target'), b = compile(b), prop(a, (obj, path, ctx) => obj[path] **= b(ctx))));
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Range operators
|
|
3
|
+
*
|
|
4
|
+
* .. (inclusive range): 1..5 → [1,2,3,4,5]
|
|
5
|
+
* ..< (exclusive range): 1..<5 → [1,2,3,4]
|
|
6
|
+
*
|
|
7
|
+
* Common in: Swift, Kotlin, Rust, Ruby
|
|
8
|
+
*/
|
|
9
|
+
import { binary, operator, compile } from '../../parse.js';
|
|
10
|
+
|
|
11
|
+
const COMP = 90;
|
|
12
|
+
|
|
13
|
+
binary('..', COMP);
|
|
14
|
+
binary('..<', COMP);
|
|
15
|
+
|
|
16
|
+
// Compile
|
|
17
|
+
operator('..', (a, b) => (a = compile(a), b = compile(b), ctx => {
|
|
18
|
+
const start = a(ctx), end = b(ctx), arr = [];
|
|
19
|
+
for (let i = start; i <= end; i++) arr.push(i);
|
|
20
|
+
return arr;
|
|
21
|
+
}));
|
|
22
|
+
operator('..<', (a, b) => (a = compile(a), b = compile(b), ctx => {
|
|
23
|
+
const start = a(ctx), end = b(ctx), arr = [];
|
|
24
|
+
for (let i = start; i < end; i++) arr.push(i);
|
|
25
|
+
return arr;
|
|
26
|
+
}));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spread/rest operator
|
|
3
|
+
*
|
|
4
|
+
* ...x → spread in arrays/calls, rest in params
|
|
5
|
+
*
|
|
6
|
+
* Common in: JS, TS, Python (*args), Ruby (*splat)
|
|
7
|
+
*/
|
|
8
|
+
import { unary, operator, compile } from '../../parse.js';
|
|
9
|
+
|
|
10
|
+
const PREFIX = 140;
|
|
11
|
+
|
|
12
|
+
unary('...', PREFIX);
|
|
13
|
+
|
|
14
|
+
// Compile (for arrays/objects spread)
|
|
15
|
+
operator('...', a => (a = compile(a), ctx => Object.entries(a(ctx))));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ternary conditional operator
|
|
3
|
+
*
|
|
4
|
+
* a ? b : c → conditional expression
|
|
5
|
+
*
|
|
6
|
+
* Common in: C, JS, Java, PHP, etc.
|
|
7
|
+
*/
|
|
8
|
+
import { token, expr, next, operator, compile } from '../../parse.js';
|
|
9
|
+
|
|
10
|
+
const ASSIGN = 20;
|
|
11
|
+
|
|
12
|
+
token('?', ASSIGN, (a, b, c) => a && (b = expr(ASSIGN - 1)) && next(c => c === 58) && (c = expr(ASSIGN - 1), ['?', a, b, c]));
|
|
13
|
+
|
|
14
|
+
// Compile
|
|
15
|
+
operator('?', (a, b, c) => (a = compile(a), b = compile(b), c = compile(c), ctx => a(ctx) ? b(ctx) : c(ctx)));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type operators
|
|
3
|
+
*
|
|
4
|
+
* as: type cast/assertion (identity in JS)
|
|
5
|
+
* is: type check (instanceof in JS)
|
|
6
|
+
*
|
|
7
|
+
* Common in: TypeScript, Kotlin, Swift, C#
|
|
8
|
+
*/
|
|
9
|
+
import { binary, operator, compile } from '../../parse.js';
|
|
10
|
+
|
|
11
|
+
const COMP = 90;
|
|
12
|
+
|
|
13
|
+
binary('as', COMP);
|
|
14
|
+
binary('is', COMP);
|
|
15
|
+
|
|
16
|
+
// Compile (identity in JS)
|
|
17
|
+
operator('as', (a, b) => (a = compile(a), ctx => a(ctx)));
|
|
18
|
+
operator('is', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx) instanceof b(ctx)));
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unary keyword operators
|
|
3
|
+
*
|
|
4
|
+
* typeof x → type string
|
|
5
|
+
* void x → undefined
|
|
6
|
+
* delete x → remove property
|
|
7
|
+
* new X() → construct instance
|
|
8
|
+
*
|
|
9
|
+
* JS-specific keywords
|
|
10
|
+
*/
|
|
11
|
+
import { unary, operator, compile } from '../../parse.js';
|
|
12
|
+
|
|
13
|
+
const PREFIX = 140;
|
|
14
|
+
|
|
15
|
+
unary('typeof', PREFIX);
|
|
16
|
+
unary('void', PREFIX);
|
|
17
|
+
unary('delete', PREFIX);
|
|
18
|
+
unary('new', PREFIX);
|
|
19
|
+
|
|
20
|
+
// Compile
|
|
21
|
+
operator('typeof', a => (a = compile(a), ctx => typeof a(ctx)));
|
|
22
|
+
operator('void', a => (a = compile(a), ctx => (a(ctx), undefined)));
|
|
23
|
+
operator('delete', a => {
|
|
24
|
+
if (a[0] === '.') {
|
|
25
|
+
const obj = compile(a[1]), key = a[2];
|
|
26
|
+
return ctx => delete obj(ctx)[key];
|
|
27
|
+
}
|
|
28
|
+
if (a[0] === '[]') {
|
|
29
|
+
const obj = compile(a[1]), key = compile(a[2]);
|
|
30
|
+
return ctx => delete obj(ctx)[key(ctx)];
|
|
31
|
+
}
|
|
32
|
+
return () => true;
|
|
33
|
+
});
|
|
34
|
+
operator('new', (call) => {
|
|
35
|
+
const target = compile(call?.[0] === '()' ? call[1] : call);
|
|
36
|
+
const args = call?.[0] === '()' ? call[2] : null;
|
|
37
|
+
const argList = !args ? () => [] :
|
|
38
|
+
args[0] === ',' ? (a => ctx => a.map(f => f(ctx)))(args.slice(1).map(compile)) :
|
|
39
|
+
(a => ctx => [a(ctx)])(compile(args));
|
|
40
|
+
return ctx => new (target(ctx))(...argList(ctx));
|
|
41
|
+
});
|