subscript 10.3.6 → 10.4.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 +9 -2
- package/eval/access.js +57 -0
- package/eval/accessor.js +19 -0
- package/eval/async.js +13 -0
- package/eval/class.js +35 -0
- package/eval/collection.js +41 -0
- package/eval/function.js +37 -0
- package/eval/if.js +7 -0
- package/eval/jessie.js +19 -0
- package/eval/justin.js +21 -0
- package/eval/loop.js +108 -0
- package/eval/module.js +8 -0
- package/eval/op/arithmetic.js +12 -0
- package/eval/op/arrow.js +35 -0
- package/eval/op/assign-logical.js +23 -0
- package/eval/op/assignment.js +15 -0
- package/eval/op/bitwise-unsigned.js +8 -0
- package/eval/op/bitwise.js +9 -0
- package/eval/op/comparison.js +7 -0
- package/eval/op/defer.js +4 -0
- package/eval/op/equality.js +5 -0
- package/eval/op/identity.js +5 -0
- package/eval/op/increment.js +13 -0
- package/eval/op/logical.js +6 -0
- package/eval/op/membership.js +4 -0
- package/eval/op/nullish.js +4 -0
- package/eval/op/optional.js +38 -0
- package/eval/op/pow.js +8 -0
- package/eval/op/range.js +13 -0
- package/eval/op/spread.js +4 -0
- package/eval/op/ternary.js +4 -0
- package/eval/op/type.js +5 -0
- package/eval/op/unary.js +24 -0
- package/eval/prop.js +16 -0
- package/eval/regex.js +7 -0
- package/eval/seq.js +10 -0
- package/eval/subscript.js +15 -0
- package/eval/switch.js +27 -0
- package/eval/template.js +14 -0
- package/eval/try.js +36 -0
- package/eval/unit.js +10 -0
- package/eval/var.js +69 -0
- package/feature/access.js +2 -58
- package/feature/accessor.js +2 -21
- package/feature/asi.js +50 -11
- package/feature/async.js +2 -14
- package/feature/class.js +2 -35
- package/feature/collection.js +2 -43
- package/feature/function.js +2 -39
- package/feature/if.js +2 -8
- package/feature/jessie.js +32 -0
- package/feature/justin.js +36 -0
- package/feature/loop.js +2 -109
- package/feature/module.js +1 -10
- package/feature/op/arithmetic.js +2 -13
- package/feature/op/arrow.js +2 -38
- package/feature/op/assign-logical.js +3 -24
- package/feature/op/assignment.js +2 -18
- package/feature/op/bitwise-unsigned.js +2 -8
- package/feature/op/bitwise.js +2 -14
- package/feature/op/comparison.js +2 -8
- package/feature/op/defer.js +2 -7
- package/feature/op/equality.js +2 -7
- package/feature/op/identity.js +2 -6
- package/feature/op/increment.js +2 -14
- package/feature/op/logical.js +2 -8
- package/feature/op/membership.js +2 -7
- package/feature/op/nullish.js +2 -5
- package/feature/op/optional.js +2 -41
- package/feature/op/pow.js +2 -10
- package/feature/op/range.js +2 -16
- package/feature/op/spread.js +2 -7
- package/feature/op/ternary.js +2 -7
- package/feature/op/type.js +2 -8
- package/feature/op/unary.js +2 -27
- package/feature/prop.js +2 -17
- package/feature/regex.js +3 -12
- package/feature/seq.js +2 -11
- package/feature/shebang.js +7 -0
- package/feature/subscript.js +23 -0
- package/feature/switch.js +2 -28
- package/feature/template.js +4 -16
- package/feature/try.js +4 -38
- package/feature/unit.js +4 -7
- package/feature/var.js +2 -70
- package/jessie.js +4 -25
- package/jessie.min.js +9 -8
- package/justin.js +4 -30
- package/justin.min.js +8 -8
- package/package.json +9 -1
- package/parse.js +27 -29
- package/subscript.js +7 -16
- package/subscript.min.js +5 -5
package/feature/op/range.js
CHANGED
|
@@ -1,26 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Range operators
|
|
2
|
+
* Range operators - parse half
|
|
3
3
|
*
|
|
4
4
|
* .. (inclusive range): 1..5 → [1,2,3,4,5]
|
|
5
5
|
* ..< (exclusive range): 1..<5 → [1,2,3,4]
|
|
6
|
-
*
|
|
7
|
-
* Common in: Swift, Kotlin, Rust, Ruby
|
|
8
6
|
*/
|
|
9
|
-
import { binary
|
|
7
|
+
import { binary } from '../../parse.js';
|
|
10
8
|
|
|
11
9
|
const COMP = 90;
|
|
12
10
|
|
|
13
11
|
binary('..', COMP);
|
|
14
12
|
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
|
-
}));
|
package/feature/op/spread.js
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Spread/rest operator
|
|
2
|
+
* Spread/rest operator - parse half
|
|
3
3
|
*
|
|
4
4
|
* ...x → spread in arrays/calls, rest in params
|
|
5
|
-
*
|
|
6
|
-
* Common in: JS, TS, Python (*args), Ruby (*splat)
|
|
7
5
|
*/
|
|
8
|
-
import { unary
|
|
6
|
+
import { unary } from '../../parse.js';
|
|
9
7
|
|
|
10
8
|
const PREFIX = 140;
|
|
11
9
|
|
|
12
10
|
unary('...', PREFIX);
|
|
13
|
-
|
|
14
|
-
// Compile (for arrays/objects spread)
|
|
15
|
-
operator('...', a => (a = compile(a), ctx => Object.entries(a(ctx))));
|
package/feature/op/ternary.js
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Ternary conditional operator
|
|
2
|
+
* Ternary conditional operator - parse half
|
|
3
3
|
*
|
|
4
4
|
* a ? b : c → conditional expression
|
|
5
|
-
*
|
|
6
|
-
* Common in: C, JS, Java, PHP, etc.
|
|
7
5
|
*/
|
|
8
|
-
import { token, expr, next
|
|
6
|
+
import { token, expr, next } from '../../parse.js';
|
|
9
7
|
|
|
10
8
|
const ASSIGN = 20;
|
|
11
9
|
|
|
12
10
|
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)));
|
package/feature/op/type.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Type operators
|
|
2
|
+
* Type operators - parse half
|
|
3
3
|
*
|
|
4
4
|
* as: type cast/assertion (identity in JS)
|
|
5
5
|
* is: type check (instanceof in JS)
|
|
6
|
-
*
|
|
7
|
-
* Common in: TypeScript, Kotlin, Swift, C#
|
|
8
6
|
*/
|
|
9
|
-
import { binary
|
|
7
|
+
import { binary } from '../../parse.js';
|
|
10
8
|
|
|
11
9
|
const COMP = 90;
|
|
12
10
|
|
|
13
11
|
binary('as', COMP);
|
|
14
12
|
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)));
|
package/feature/op/unary.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Unary keyword operators
|
|
2
|
+
* Unary keyword operators - parse half
|
|
3
3
|
*
|
|
4
4
|
* typeof x → type string
|
|
5
5
|
* void x → undefined
|
|
6
6
|
* delete x → remove property
|
|
7
7
|
* new X() → construct instance
|
|
8
|
-
*
|
|
9
|
-
* JS-specific keywords
|
|
10
8
|
*/
|
|
11
|
-
import { unary, keyword,
|
|
9
|
+
import { unary, keyword, skip, expr, word } from '../../parse.js';
|
|
12
10
|
|
|
13
11
|
const PREFIX = 140;
|
|
14
12
|
|
|
@@ -19,26 +17,3 @@ unary('delete', PREFIX);
|
|
|
19
17
|
keyword('new', PREFIX, () =>
|
|
20
18
|
word('.target') ? (skip(7), ['new.target']) : ['new', expr(PREFIX)]
|
|
21
19
|
);
|
|
22
|
-
|
|
23
|
-
// Compile
|
|
24
|
-
operator('typeof', a => (a = compile(a), ctx => typeof a(ctx)));
|
|
25
|
-
operator('void', a => (a = compile(a), ctx => (a(ctx), undefined)));
|
|
26
|
-
operator('delete', a => {
|
|
27
|
-
if (a[0] === '.') {
|
|
28
|
-
const obj = compile(a[1]), key = a[2];
|
|
29
|
-
return ctx => delete obj(ctx)[key];
|
|
30
|
-
}
|
|
31
|
-
if (a[0] === '[]') {
|
|
32
|
-
const obj = compile(a[1]), key = compile(a[2]);
|
|
33
|
-
return ctx => delete obj(ctx)[key(ctx)];
|
|
34
|
-
}
|
|
35
|
-
return () => true;
|
|
36
|
-
});
|
|
37
|
-
operator('new', (call) => {
|
|
38
|
-
const target = compile(call?.[0] === '()' ? call[1] : call);
|
|
39
|
-
const args = call?.[0] === '()' ? call[2] : null;
|
|
40
|
-
const argList = !args ? () => [] :
|
|
41
|
-
args[0] === ',' ? (a => ctx => a.map(f => f(ctx)))(args.slice(1).map(compile)) :
|
|
42
|
-
(a => ctx => [a(ctx)])(compile(args));
|
|
43
|
-
return ctx => new (target(ctx))(...argList(ctx));
|
|
44
|
-
});
|
package/feature/prop.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Minimal property access: a.b, a[b], f()
|
|
2
|
+
* Minimal property access: a.b, a[b], f() - parse half
|
|
3
3
|
* For array literals, private fields, see member.js
|
|
4
4
|
*/
|
|
5
|
-
import { access, binary, group
|
|
5
|
+
import { access, binary, group } from '../parse.js';
|
|
6
6
|
|
|
7
7
|
const ACCESS = 170;
|
|
8
8
|
|
|
@@ -17,18 +17,3 @@ group('()', ACCESS);
|
|
|
17
17
|
|
|
18
18
|
// a(b,c,d), a() - function calls
|
|
19
19
|
access('()', ACCESS);
|
|
20
|
-
|
|
21
|
-
// Compile
|
|
22
|
-
const err = msg => { throw Error(msg) };
|
|
23
|
-
operator('[]', (a, b) => (b == null && err('Missing index'), a = compile(a), b = compile(b), ctx => a(ctx)[b(ctx)]));
|
|
24
|
-
operator('.', (a, b) => (a = compile(a), b = !b[0] ? b[1] : b, ctx => a(ctx)[b]));
|
|
25
|
-
operator('()', (a, b) => {
|
|
26
|
-
// Group: (expr) - no second argument means grouping, not call
|
|
27
|
-
if (b === undefined) return a == null ? err('Empty ()') : compile(a);
|
|
28
|
-
// Function call: a(b,c)
|
|
29
|
-
const args = !b ? () => [] :
|
|
30
|
-
b[0] === ',' ? (b = b.slice(1).map(compile), ctx => b.map(arg => arg(ctx))) :
|
|
31
|
-
(b = compile(b), ctx => [b(ctx)]);
|
|
32
|
-
a = compile(a);
|
|
33
|
-
return ctx => a(ctx)(...args(ctx));
|
|
34
|
-
});
|
package/feature/regex.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Regex literals: /pattern/flags
|
|
2
|
+
* Regex literals: /pattern/flags - parse half
|
|
3
3
|
*
|
|
4
4
|
* AST (constructor form, JSON-serializable):
|
|
5
5
|
* /abc/gi → ['//', 'abc', 'gi']
|
|
6
6
|
* /abc/ → ['//', 'abc']
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* Disambiguates from division by context:
|
|
9
9
|
* - `/` after value = division (falls through to prev)
|
|
10
10
|
* - `/` at start or after operator = regex
|
|
11
|
-
*
|
|
12
|
-
* Compile:
|
|
13
|
-
* ['//', 'abc', 'gi'] → new RegExp('abc', 'gi')
|
|
14
11
|
*/
|
|
15
|
-
import { token, skip, err, next, idx, cur
|
|
12
|
+
import { token, skip, err, next, idx, cur } from '../parse.js';
|
|
16
13
|
|
|
17
14
|
const PREFIX = 140, SLASH = 47, BSLASH = 92;
|
|
18
15
|
|
|
@@ -37,9 +34,3 @@ token('/', PREFIX, a => {
|
|
|
37
34
|
|
|
38
35
|
return flags ? ['//', pattern, flags] : ['//', pattern];
|
|
39
36
|
});
|
|
40
|
-
|
|
41
|
-
// Compile: ['//', pattern, flags?] → RegExp
|
|
42
|
-
operator('//', (a, b) => {
|
|
43
|
-
const re = new RegExp(a, b || '');
|
|
44
|
-
return () => re;
|
|
45
|
-
});
|
package/feature/seq.js
CHANGED
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Sequence operators (C-family)
|
|
2
|
+
* Sequence operators (C-family) - parse half
|
|
3
3
|
*
|
|
4
4
|
* , ; — returns last evaluated value
|
|
5
5
|
*/
|
|
6
|
-
import { nary
|
|
6
|
+
import { nary } from '../parse.js';
|
|
7
7
|
|
|
8
8
|
const STATEMENT = 5, SEQ = 10;
|
|
9
9
|
|
|
10
10
|
// Sequences
|
|
11
11
|
nary(',', SEQ);
|
|
12
12
|
nary(';', STATEMENT, true); // right-assoc to allow same-prec statements
|
|
13
|
-
|
|
14
|
-
// Compile - returns last evaluated value
|
|
15
|
-
const seq = (...args) => (args = args.map(compile), ctx => {
|
|
16
|
-
let r;
|
|
17
|
-
for (const arg of args) r = arg(ctx);
|
|
18
|
-
return r;
|
|
19
|
-
});
|
|
20
|
-
operator(',', seq);
|
|
21
|
-
operator(';', seq);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Shebang `#!...` line treated as a line comment.
|
|
2
|
+
// `#!` is not valid syntax outside the shebang context (private fields require an identifier),
|
|
3
|
+
// so registering it globally is safe and lets the existing comment machinery handle it.
|
|
4
|
+
import './comment.js';
|
|
5
|
+
import { parse } from '../parse.js';
|
|
6
|
+
|
|
7
|
+
parse.comment['#!'] = '\n';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* subscript - parse aggregator
|
|
3
|
+
* Minimal expression parser. No eval - import 'subscript/eval/subscript.js' for runtime.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Literals
|
|
7
|
+
import './number.js'; // Decimal numbers: 123, 1.5, 1e3
|
|
8
|
+
import './string.js'; // Double-quoted strings with escapes
|
|
9
|
+
|
|
10
|
+
// Operators (C-family common set) - order matters for token chain performance
|
|
11
|
+
import './op/assignment.js'; // = += -= *= /= %= |= &= ^= >>= <<=
|
|
12
|
+
import './op/logical.js'; // ! && ||
|
|
13
|
+
import './op/bitwise.js'; // ~ | & ^ >> <<
|
|
14
|
+
import './op/comparison.js'; // < > <= >=
|
|
15
|
+
import './op/equality.js'; // == !=
|
|
16
|
+
import './op/arithmetic.js'; // + - * / %
|
|
17
|
+
import './op/increment.js'; // ++ --
|
|
18
|
+
|
|
19
|
+
import './seq.js'; // Sequences: a, b; a; b
|
|
20
|
+
import './group.js'; // Grouping: (a)
|
|
21
|
+
import './access.js'; // Property access: a.b, a[b], f(), [a,b]
|
|
22
|
+
|
|
23
|
+
export * from '../parse.js';
|
package/feature/switch.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// Switch/case/default
|
|
1
|
+
// Switch/case/default - parse half
|
|
2
2
|
// AST: ['switch', val, ['case', test, body], ['default', body], ...]
|
|
3
|
-
import { expr, skip, space, keyword, parens,
|
|
4
|
-
import { BREAK } from './loop.js';
|
|
3
|
+
import { expr, skip, space, keyword, parens, idx, err, seek, lookup, word } from '../parse.js';
|
|
5
4
|
|
|
6
5
|
const STATEMENT = 5, ASSIGN = 20, COLON = 58, SEMI = 59, CBRACE = 125;
|
|
7
6
|
|
|
@@ -49,28 +48,3 @@ const switchBody = () => {
|
|
|
49
48
|
|
|
50
49
|
// switch (x) { ... }
|
|
51
50
|
keyword('switch', STATEMENT + 1, () => space() === 40 && ['switch', parens(), ...switchBody()]);
|
|
52
|
-
|
|
53
|
-
// Compile
|
|
54
|
-
operator('switch', (val, ...cases) => {
|
|
55
|
-
val = compile(val);
|
|
56
|
-
if (!cases.length) return ctx => val(ctx);
|
|
57
|
-
|
|
58
|
-
cases = cases.map(c => [
|
|
59
|
-
c[0] === 'case' ? compile(c[1]) : null,
|
|
60
|
-
(c[0] === 'case' ? c[2] : c[1])?.[0] === ';'
|
|
61
|
-
? (c[0] === 'case' ? c[2] : c[1]).slice(1).map(compile)
|
|
62
|
-
: (c[0] === 'case' ? c[2] : c[1]) ? [compile(c[0] === 'case' ? c[2] : c[1])] : []
|
|
63
|
-
]);
|
|
64
|
-
|
|
65
|
-
return ctx => {
|
|
66
|
-
const v = val(ctx);
|
|
67
|
-
let matched = false, r;
|
|
68
|
-
for (const [test, stmts] of cases)
|
|
69
|
-
if (matched || test === null || test(ctx) === v)
|
|
70
|
-
for (matched = true, i = 0; i < stmts.length; i++)
|
|
71
|
-
try { r = stmts[i](ctx); }
|
|
72
|
-
catch (e) { if (e === BREAK) return r; throw e; }
|
|
73
|
-
var i;
|
|
74
|
-
return r;
|
|
75
|
-
};
|
|
76
|
-
});
|
package/feature/template.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Template literals
|
|
3
|
-
*
|
|
2
|
+
* Template literals - parse half
|
|
3
|
+
* `a ${x} b` → ['`', [,'a '], 'x', [,' b']]
|
|
4
|
+
* Tagged: tag`...` → ['``', 'tag', ...]
|
|
4
5
|
*/
|
|
5
|
-
import { parse, skip, err, expr, lookup, cur, idx
|
|
6
|
+
import { parse, skip, err, expr, lookup, cur, idx } from '../parse.js';
|
|
6
7
|
|
|
7
8
|
const ACCESS = 170, BACKTICK = 96, DOLLAR = 36, OBRACE = 123, BSLASH = 92;
|
|
8
9
|
const esc = { n: '\n', r: '\r', t: '\t', b: '\b', f: '\f', v: '\v' };
|
|
@@ -24,16 +25,3 @@ lookup[BACKTICK] = (a, prec) =>
|
|
|
24
25
|
a && prec < ACCESS ? (parse.asi && parse.newline ? void 0 : (skip(), ['``', a, ...parseBody()])) : // tagged
|
|
25
26
|
!a ? (skip(), (p => p.length < 2 && p[0]?.[0] === undefined ? p[0] || [,''] : ['`', ...p])(parseBody())) : // plain
|
|
26
27
|
prev?.(a, prec);
|
|
27
|
-
|
|
28
|
-
// Compile
|
|
29
|
-
operator('`', (...parts) => (parts = parts.map(compile), ctx => parts.map(p => p(ctx)).join('')));
|
|
30
|
-
operator('``', (tag, ...parts) => {
|
|
31
|
-
tag = compile(tag);
|
|
32
|
-
const strings = [], exprs = [];
|
|
33
|
-
for (const p of parts) {
|
|
34
|
-
if (Array.isArray(p) && p[0] === undefined) strings.push(p[1]);
|
|
35
|
-
else exprs.push(compile(p));
|
|
36
|
-
}
|
|
37
|
-
const strs = Object.assign([...strings], { raw: strings });
|
|
38
|
-
return ctx => tag(ctx)(strs, ...exprs.map(e => e(ctx)));
|
|
39
|
-
});
|
package/feature/try.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
// try/catch/finally/throw statements
|
|
1
|
+
// try/catch/finally/throw statements - parse half
|
|
2
2
|
// AST (faithful): ['try', body, ['catch', param, handler]?, ['finally', cleanup]?]
|
|
3
|
-
|
|
4
|
-
import { space, parse, keyword, parens, expr, word, skip, operator, compile } from '../parse.js';
|
|
3
|
+
import { space, parse, keyword, parens, expr, word, skip, peek } from '../parse.js';
|
|
5
4
|
import { block } from './if.js';
|
|
6
|
-
import { RETURN } from './op/arrow.js';import { BREAK, CONTINUE } from './loop.js';
|
|
7
5
|
|
|
8
6
|
const STATEMENT = 5;
|
|
9
7
|
|
|
@@ -13,7 +11,8 @@ keyword('try', STATEMENT + 1, () => {
|
|
|
13
11
|
space();
|
|
14
12
|
if (word('catch')) {
|
|
15
13
|
skip(5); space();
|
|
16
|
-
|
|
14
|
+
// ES2019 optional catch binding: `catch { ... }` (no parameter)
|
|
15
|
+
node.push(['catch', peek() === 40 ? parens() : null, block()]);
|
|
17
16
|
}
|
|
18
17
|
space();
|
|
19
18
|
if (word('finally')) {
|
|
@@ -29,36 +28,3 @@ keyword('throw', STATEMENT + 1, () => {
|
|
|
29
28
|
if (parse.newline) throw SyntaxError('Unexpected newline after throw');
|
|
30
29
|
return ['throw', expr(STATEMENT)];
|
|
31
30
|
});
|
|
32
|
-
|
|
33
|
-
// Compile try - normalize in compiler, not parser
|
|
34
|
-
operator('try', (tryBody, ...clauses) => {
|
|
35
|
-
tryBody = tryBody ? compile(tryBody) : null;
|
|
36
|
-
|
|
37
|
-
let catchClause = clauses.find(c => c?.[0] === 'catch');
|
|
38
|
-
let finallyClause = clauses.find(c => c?.[0] === 'finally');
|
|
39
|
-
|
|
40
|
-
const catchParam = catchClause?.[1];
|
|
41
|
-
const catchBody = catchClause?.[2] ? compile(catchClause[2]) : null;
|
|
42
|
-
const finallyBody = finallyClause?.[1] ? compile(finallyClause[1]) : null;
|
|
43
|
-
|
|
44
|
-
return ctx => {
|
|
45
|
-
let result;
|
|
46
|
-
try {
|
|
47
|
-
result = tryBody?.(ctx);
|
|
48
|
-
} catch (e) {
|
|
49
|
-
if (e === BREAK || e === CONTINUE || e === RETURN) throw e;
|
|
50
|
-
if (catchParam !== null && catchParam !== undefined && catchBody) {
|
|
51
|
-
const had = catchParam in ctx, orig = ctx[catchParam];
|
|
52
|
-
ctx[catchParam] = e;
|
|
53
|
-
try { result = catchBody(ctx); }
|
|
54
|
-
finally { had ? ctx[catchParam] = orig : delete ctx[catchParam]; }
|
|
55
|
-
} else if (!catchBody) throw e;
|
|
56
|
-
}
|
|
57
|
-
finally {
|
|
58
|
-
finallyBody?.(ctx);
|
|
59
|
-
}
|
|
60
|
-
return result;
|
|
61
|
-
};
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
operator('throw', val => (val = compile(val), ctx => { throw val(ctx); }));
|
package/feature/unit.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Unit suffixes: 5px, 10rem, 2s, 500ms
|
|
2
|
+
* Unit suffixes - parse half: 5px, 10rem, 2s, 500ms
|
|
3
3
|
*
|
|
4
4
|
* AST:
|
|
5
5
|
* 5px → ['px', [,5]]
|
|
@@ -9,14 +9,11 @@
|
|
|
9
9
|
* import { unit } from 'subscript/feature/unit.js'
|
|
10
10
|
* unit('px', 'em', 'rem', 's', 'ms')
|
|
11
11
|
*/
|
|
12
|
-
import { lookup, next, parse, idx, seek
|
|
12
|
+
import { lookup, next, parse, idx, seek } from '../parse.js';
|
|
13
13
|
|
|
14
|
-
const units = {};
|
|
14
|
+
export const units = {};
|
|
15
15
|
|
|
16
|
-
export const unit = (...names) => names.forEach(name => {
|
|
17
|
-
units[name] = 1;
|
|
18
|
-
operator(name, val => (val = compile(val), ctx => ({ value: val(ctx), unit: name })));
|
|
19
|
-
});
|
|
16
|
+
export const unit = (...names) => names.forEach(name => { units[name] = 1; });
|
|
20
17
|
|
|
21
18
|
// Wrap number handler to check for unit suffix
|
|
22
19
|
const wrapNum = cc => {
|
package/feature/var.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Variable declarations: let, const, var
|
|
2
|
+
* Variable declarations: let, const, var - parse half
|
|
3
3
|
*
|
|
4
4
|
* AST:
|
|
5
5
|
* let x = 1 → ['let', ['=', 'x', 1]]
|
|
@@ -8,55 +8,10 @@
|
|
|
8
8
|
* for (let x in o) → ['for', ['in', ['let', 'x'], 'o'], body]
|
|
9
9
|
* var x → ['var', 'x'] (acts as assignment target)
|
|
10
10
|
*/
|
|
11
|
-
import { expr, space, keyword,
|
|
11
|
+
import { expr, space, keyword, seek, word, idx } from '../parse.js';
|
|
12
12
|
|
|
13
13
|
const STATEMENT = 5, SEQ = 10, ASSIGN = 20;
|
|
14
14
|
|
|
15
|
-
// Flatten comma into array: [',', a, b, c] → [a, b, c]
|
|
16
|
-
const flatten = items => items[0]?.[0] === ',' ? items[0].slice(1) : items;
|
|
17
|
-
|
|
18
|
-
// Destructure value into context
|
|
19
|
-
export const destructure = (pattern, value, ctx) => {
|
|
20
|
-
if (typeof pattern === 'string') { ctx[pattern] = value; return; }
|
|
21
|
-
const [op, ...raw] = pattern;
|
|
22
|
-
const items = flatten(raw);
|
|
23
|
-
if (op === '{}') {
|
|
24
|
-
const used = [];
|
|
25
|
-
for (const item of items) {
|
|
26
|
-
// Rest: {...rest}
|
|
27
|
-
if (Array.isArray(item) && item[0] === '...') {
|
|
28
|
-
const rest = {};
|
|
29
|
-
for (const k in value) if (!used.includes(k)) rest[k] = value[k];
|
|
30
|
-
ctx[item[1]] = rest;
|
|
31
|
-
break;
|
|
32
|
-
}
|
|
33
|
-
let key, binding, def;
|
|
34
|
-
// Shorthand: {x} → item is 'x'
|
|
35
|
-
// With default: {x = 1} → ['=', 'x', default]
|
|
36
|
-
// Rename: {x: y} → [':', 'x', 'y']
|
|
37
|
-
if (typeof item === 'string') { key = binding = item }
|
|
38
|
-
else if (item[0] === '=') { typeof item[1] === 'string' ? (key = binding = item[1]) : ([, key, binding] = item[1]); def = item[2] }
|
|
39
|
-
else { [, key, binding] = item }
|
|
40
|
-
used.push(key);
|
|
41
|
-
let val = value[key];
|
|
42
|
-
if (val === undefined && def) val = compile(def)(ctx);
|
|
43
|
-
destructure(binding, val, ctx);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
else if (op === '[]') {
|
|
47
|
-
let i = 0;
|
|
48
|
-
for (const item of items) {
|
|
49
|
-
if (item === null) { i++; continue; }
|
|
50
|
-
if (Array.isArray(item) && item[0] === '...') { ctx[item[1]] = value.slice(i); break; }
|
|
51
|
-
let binding = item, def;
|
|
52
|
-
if (Array.isArray(item) && item[0] === '=') [, binding, def] = item;
|
|
53
|
-
let val = value[i++];
|
|
54
|
-
if (val === undefined && def) val = compile(def)(ctx);
|
|
55
|
-
destructure(binding, val, ctx);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
15
|
// let/const: expr(SEQ-1) consumes assignment, stops before comma
|
|
61
16
|
// For for-in/of, return ['in/of', ['let', x], iterable] not ['let', ['in', x, it]]
|
|
62
17
|
// For comma, return ['let', decl1, decl2, ...] not ['let', [',', ...]]
|
|
@@ -84,26 +39,3 @@ keyword('const', STATEMENT + 1, () => decl('const'));
|
|
|
84
39
|
// var: just declares identifier, assignment happens separately
|
|
85
40
|
// var x = 5 → ['=', ['var', 'x'], 5]
|
|
86
41
|
keyword('var', STATEMENT, () => (space(), ['var', expr(ASSIGN)]));
|
|
87
|
-
|
|
88
|
-
// Compile
|
|
89
|
-
const varOp = (...decls) => {
|
|
90
|
-
decls = decls.map(d => {
|
|
91
|
-
// Just identifier: let x
|
|
92
|
-
if (typeof d === 'string') return ctx => { ctx[d] = undefined; };
|
|
93
|
-
// Assignment: let x = 1
|
|
94
|
-
if (d[0] === '=') {
|
|
95
|
-
const [, pattern, val] = d;
|
|
96
|
-
const v = compile(val);
|
|
97
|
-
if (typeof pattern === 'string') return ctx => { ctx[pattern] = v(ctx); };
|
|
98
|
-
return ctx => destructure(pattern, v(ctx), ctx);
|
|
99
|
-
}
|
|
100
|
-
return compile(d);
|
|
101
|
-
});
|
|
102
|
-
return ctx => { for (const d of decls) d(ctx); };
|
|
103
|
-
};
|
|
104
|
-
operator('let', varOp);
|
|
105
|
-
operator('const', varOp);
|
|
106
|
-
// var just declares the variable (assignment handled by = operator)
|
|
107
|
-
operator('var', name => (typeof name === 'string'
|
|
108
|
-
? ctx => { ctx[name] = undefined; }
|
|
109
|
-
: () => {}));
|
package/jessie.js
CHANGED
|
@@ -3,32 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Builds on justin with statements: blocks, variables, if/else,
|
|
5
5
|
* loops, functions, try/catch, switch, throw.
|
|
6
|
+
*
|
|
7
|
+
* For parse-only: import { parse } from 'subscript/feature/jessie.js'
|
|
6
8
|
*/
|
|
7
|
-
import './
|
|
8
|
-
|
|
9
|
-
// JS source compatibility
|
|
10
|
-
import './feature/unicode.js';
|
|
11
|
-
|
|
12
|
-
// Statement features
|
|
13
|
-
import './feature/var.js';
|
|
14
|
-
import './feature/function.js';
|
|
15
|
-
import './feature/async.js';
|
|
16
|
-
import './feature/class.js';
|
|
17
|
-
import './feature/regex.js';
|
|
18
|
-
|
|
19
|
-
// Control flow
|
|
20
|
-
import './feature/if.js';
|
|
21
|
-
import './feature/loop.js';
|
|
22
|
-
import './feature/try.js';
|
|
23
|
-
import './feature/switch.js';
|
|
24
|
-
import './feature/statement.js'; // debugger, with, labeled statements
|
|
25
|
-
|
|
26
|
-
// Module system
|
|
27
|
-
import './feature/module.js';
|
|
28
|
-
import './feature/accessor.js';
|
|
29
|
-
|
|
30
|
-
// Automatic Semicolon Insertion
|
|
31
|
-
import './feature/asi.js';
|
|
9
|
+
import './feature/jessie.js';
|
|
10
|
+
import './eval/jessie.js';
|
|
32
11
|
|
|
33
12
|
export * from './parse.js';
|
|
34
13
|
export { default } from './subscript.js';
|