subscript 9.0.2 → 9.2.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 CHANGED
@@ -1,17 +1,17 @@
1
1
  # sub<em>script</em> <a href="https://github.com/spectjs/subscript/actions/workflows/node.js.yml"><img src="https://github.com/spectjs/subscript/actions/workflows/node.js.yml/badge.svg"/></a> <a href="https://bundlejs.com/?q=subscript"><img alt="npm bundle size" src="https://img.shields.io/bundlejs/size/subscript"/></a> <a href="http://npmjs.org/subscript"><img src="https://img.shields.io/npm/v/subscript"/></a> <a href="http://microjs.com/#subscript"><img src="https://img.shields.io/badge/microjs-subscript-blue?color=darkslateblue"/></a>
2
2
 
3
- > _Subscript_ is fast, tiny & extensible parser / evaluator / microlanguage with standard syntax.
3
+ > _Subscript_ is fast, tiny & extensible parser / evaluator / microlanguage.
4
4
 
5
5
  #### Used for:
6
6
 
7
- * expressions evaluators, calculators
8
- * subsets of languages (eg. [justin](#justin)<!-- [jz](https://github.com/dy/jz) -->)
9
- * sandboxes, playgrounds, safe eval (eg. [glsl-transpiler](https://github.com/stackgl/glsl-transpiler))
10
- * custom DSL (eg. [piezo](https://github.com/dy/piezo)) <!-- uneural -->
11
- * preprocessors (eg. [prepr](https://github.com/dy/prepr))
12
- * templates (eg. [sprae](https://github.com/dy/sprae))
7
+ * expressions evaluators, calculators
8
+ * subsets of languages
9
+ * sandboxes, playgrounds, safe eval
10
+ * custom DSL
11
+ * preprocessors
12
+ * templates
13
13
 
14
- _Subscript_ has [3.5kb](https://npmfs.com/package/subscript/7.4.3/subscript.min.js) footprint (compare to [11.4kb](https://npmfs.com/package/jsep/1.2.0/dist/jsep.min.js) _jsep_ + [4.5kb](https://npmfs.com/package/expression-eval/5.0.0/dist/expression-eval.module.js) _expression-eval_), best [performance](#performance) and extensive test coverage.
14
+ _Subscript_ has [3.5kb](https://npmfs.com/package/subscript/7.4.3/subscript.min.js) footprint <!-- (compare to [11.4kb](https://npmfs.com/package/jsep/1.2.0/dist/jsep.min.js) _jsep_ + [4.5kb](https://npmfs.com/package/expression-eval/5.0.0/dist/expression-eval.module.js) _expression-eval_) -->, good [performance](#performance) and extensive test coverage.
15
15
 
16
16
 
17
17
  ## Usage
@@ -38,7 +38,7 @@ _Subscript_ supports [common syntax](https://en.wikipedia.org/wiki/Comparison_of
38
38
  * `a < b`, `a <= b`, `a > b`, `a >= b`, `a == b`, `a != b`
39
39
  * `~a`, `a & b`, `a ^ b`, `a | b`, `a << b`, `a >> b`
40
40
  * `!a`, `a && b`, `a || b`
41
- * `a = b`, `a += b`, `a -= b`, `a *= b`, `a /= b`, `a %= b`, , `a <<= b`, `a >>= b`
41
+ * `a = b`, `a += b`, `a -= b`, `a *= b`, `a /= b`, `a %= b`, `a <<= b`, `a >>= b`
42
42
  * `(a, (b))`, `a; b;`
43
43
  * `"abc"`, `'abc'`
44
44
  * `0.1`, `1.2e+3`
@@ -61,13 +61,35 @@ It extends _subscript_ with:
61
61
  + `// foo`, `/* bar */`
62
62
  + `true`, `false`, `null`, `NaN`, `undefined`
63
63
  + `a in b`
64
- <!-- + strings interpolation -->
65
64
 
66
65
  ```js
67
- import jstin from './justin.js'
66
+ import justin from 'subscript/justin'
68
67
 
69
- let xy = jstin('{ x: 1, "y": 2+2 }["x"]')
70
- xy() // 1
68
+ let fn = justin('{ x: 1, "y": 2+2 }["x"]')
69
+ fn() // 1
70
+ ```
71
+
72
+ ### Control Flow
73
+
74
+ For statement syntax, import `subscript/feature/control.js`:
75
+
76
+ + `if (c) a`, `if (c) a else b`
77
+ + `while (c) body`
78
+ + `for (init; cond; step) body`
79
+ + `{ a; b }` — block scope
80
+ + `let x`, `const x = 1`
81
+ + `break`, `continue`, `return x`
82
+
83
+ ```js
84
+ import subscript from 'subscript/justin'
85
+ import 'subscript/feature/control.js'
86
+
87
+ let sum = subscript(`
88
+ let sum = 0;
89
+ for (i = 0; i < 10; i += 1) sum += i;
90
+ sum
91
+ `)
92
+ sum() // 45
71
93
  ```
72
94
 
73
95
 
@@ -110,15 +132,15 @@ fn({min: 5}) // min*60 + "sec" == "300sec"
110
132
  ['()', a]; // group operator `(a)`
111
133
  ['()', a, b]; // access operator `a(b)`
112
134
  [, 'a']; // literal value `'a'`
113
- a; // variable (from scope)
135
+ 'a'; // variable (from scope)
114
136
  null|empty; // placeholder
115
137
 
116
138
  // eg.
117
139
  ['()', 'a'] // (a)
118
- ['()', 'a',,] // a()
140
+ ['()', 'a', null] // a()
119
141
  ['()', 'a', 'b'] // a(b)
120
- ['+', 'a'] // +a
121
- ['+','a',,] // a+
142
+ ['++', 'a'] // ++a
143
+ ['++','a', null] // a++
122
144
  ```
123
145
 
124
146
  ### Stringify
@@ -129,12 +151,12 @@ To convert tree back to code, there's codegenerator function:
129
151
  import { stringify } from 'subscript.js'
130
152
 
131
153
  stringify(['+', ['*', 'min', [,60]], [,'sec']])
132
- // 'min*60 + "sec" == "300sec"'
154
+ // 'min * 60 + "sec"'
133
155
  ```
134
156
 
135
157
  ## Extending
136
158
 
137
- _Subscript_ provides premade language [features](./features) and API to customize syntax:
159
+ _Subscript_ provides premade language [features](./feature) and API to customize syntax:
138
160
 
139
161
  * `unary(str, precedence, postfix=false)` − register unary operator, either prefix `⚬a` or postfix `a⚬`.
140
162
  * `binary(str, precedence, rassoc=false)` − register binary operator `a ⚬ b`, optionally right-associative.
@@ -156,7 +178,7 @@ import 'subscript/feature/object.js';
156
178
  // add identity operators (precedence of comparison)
157
179
  binary('===', 9), binary('!==', 9)
158
180
  operator('===', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx)===b(ctx)))
159
- operator('===', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx)!==b(ctx)))
181
+ operator('!==', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx)!==b(ctx)))
160
182
 
161
183
  // add nullish coalescing (precedence of logical or)
162
184
  binary('??', 3)
@@ -170,28 +192,6 @@ token('NaN', 20, a => a ? err() : [, NaN])
170
192
  See [`./feature/*`](./feature) or [`./justin.js`](./justin.js) for examples.
171
193
 
172
194
 
173
- <!--
174
- ## Ideas
175
-
176
- * Keyed arrays <code>[a:1, b:2, c:3]</code>
177
- * 7!` (factorial)
178
- * `5s`, `5rem` (units)
179
- * `arrᵀ` - transpose
180
- * `int 5` (typecast)
181
- * `$a` (parameter expansion)
182
- * `1 to 10 by 2`
183
- * `a if b else c`
184
- * `a, b in c`
185
- * `a.xyz` swizzles
186
- * vector operators
187
- * set operators
188
- * polynomial operators
189
- * versions
190
- * hashes, urls
191
- * regexes
192
- * 2a as `2*a`
193
- * string interpolation ` ${} 1 ${} `
194
- -->
195
195
 
196
196
  ## Performance
197
197
 
@@ -232,6 +232,12 @@ mr-parser: -
232
232
  math-parser: -
233
233
  ```
234
234
 
235
+ <!--
236
+ ## Used by
237
+
238
+ [prepr](https://github.com/dy/prepr), [justin](#justin), [jz](https://github.com/dy/jz), [glsl-transpiler](https://github.com/stackgl/glsl-transpiler), [piezo](https://github.com/dy/piezo)
239
+ -->
240
+
235
241
  ## Alternatives
236
242
 
237
243
  [jexpr](https://github.com/justinfagnani/jexpr), [jsep](https://github.com/EricSmekens/jsep), [jexl](https://github.com/TomFrost/Jexl), [mozjexl](https://github.com/mozilla/mozjexl), [expr-eval](https://github.com/silentmatt/expr-eval), [expression-eval](https://github.com/donmccurdy/expression-eval), [string-math](https://github.com/devrafalko/string-math), [nerdamer](https://github.com/jiggzson/nerdamer), [math-codegen](https://github.com/mauriciopoppe/math-codegen), [math-parser](https://www.npmjs.com/package/math-parser), [math.js](https://mathjs.org/docs/expressions/parsing.html), [nx-compile](https://github.com/nx-js/compiler-util), [built-in-math-eval](https://github.com/mauriciopoppe/built-in-math-eval)
package/feature/access.js CHANGED
@@ -1,11 +1,11 @@
1
- import { access, binary, group } from '../src/parse.js'
1
+ import { access, binary, group, err } from '../src/parse.js'
2
2
  import { operator, compile } from '../src/compile.js'
3
- import { CBRACK, PREC_ACCESS } from '../src/const.js'
3
+ import { PREC_ACCESS, unsafe } from '../src/const.js'
4
4
 
5
5
  // a[b]
6
6
  access('[]', PREC_ACCESS)
7
- operator('[]', (a, b) => !b ? err() : (a = compile(a), b = compile(b), ctx => a(ctx)[b(ctx)]))
7
+ operator('[]', (a, b) => !b ? err() : (a = compile(a), b = compile(b), ctx => { const k = b(ctx); return unsafe(k) ? undefined : a(ctx)[k] }))
8
8
 
9
9
  // a.b
10
10
  binary('.', PREC_ACCESS)
11
- operator('.', (a, b) => (a = compile(a), b = !b[0] ? b[1] : b, ctx => a(ctx)[b])) // a.true, a.1 → needs to work fine
11
+ operator('.', (a, b) => (a = compile(a), b = !b[0] ? b[1] : b, unsafe(b) ? () => undefined : ctx => a(ctx)[b]))
@@ -2,6 +2,5 @@ import { SPACE, STAR, PREC_TOKEN } from "../src/const.js"
2
2
  import { token, skip, next, cur, idx, expr } from "../src/parse.js"
3
3
 
4
4
  // /**/, //
5
- // FIXME: try replacing with group
6
- token('/*', PREC_TOKEN, (a, prec) => (next(c => c !== STAR && cur.charCodeAt(idx + 1) !== 47), skip(2), a || expr(prec) || []))
5
+ token('/*', PREC_TOKEN, (a, prec) => (next(c => c !== STAR && cur.charCodeAt(idx + 1) !== 47), skip(),skip(), a || expr(prec) || []))
7
6
  token('//', PREC_TOKEN, (a, prec) => (next(c => c >= SPACE), a || expr(prec) || []))
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Control flow: if/else, while, for, break, continue, return, blocks
3
+ *
4
+ * AST:
5
+ * if (c) a else b → ['if', c, a, b?]
6
+ * while (c) a → ['while', c, a]
7
+ * for (i;c;s) a → ['for', i, c, s, a]
8
+ * { a; b } → ['block', [';', a, b]]
9
+ * break/continue → ['break'] / ['continue']
10
+ * return x → ['return', x?]
11
+ */
12
+ import * as P from '../src/parse.js'
13
+ import { operator, compile } from '../src/compile.js'
14
+ import { PREC_STATEMENT, OPAREN, CPAREN, OBRACE, CBRACE, PREC_SEQ, PREC_TOKEN } from '../src/const.js'
15
+
16
+ const { token, expr, skip, space, err, parse, next } = P
17
+ const SEMI = 59
18
+
19
+ // Control signals
20
+ class Break {}
21
+ class Continue {}
22
+ class Return { constructor(v) { this.value = v } }
23
+ export const BREAK = new Break(), CONTINUE = new Continue()
24
+
25
+ // Shared loop body executor
26
+ const loop = (body, ctx) => {
27
+ try { return { val: body(ctx) } }
28
+ catch (e) {
29
+ if (e === BREAK) return { brk: 1 }
30
+ if (e === CONTINUE) return { cnt: 1 }
31
+ if (e instanceof Return) return { ret: 1, val: e.value }
32
+ throw e
33
+ }
34
+ }
35
+
36
+ // if (cond) body [else alt]
37
+ token('if', PREC_STATEMENT, a => {
38
+ if (a) return
39
+ space() === OPAREN || err('Expected (')
40
+ skip()
41
+ const cond = expr(0, CPAREN), body = parseBody()
42
+ space()
43
+ // check 'else' — skip 4 chars via skip() since P.idx is read-only from module
44
+ const alt = P.cur.substr(P.idx, 4) === 'else' && !parse.id(P.cur.charCodeAt(P.idx + 4))
45
+ ? (skip(), skip(), skip(), skip(), parseBody()) : undefined
46
+ return alt !== undefined ? ['if', cond, body, alt] : ['if', cond, body]
47
+ })
48
+
49
+ operator('if', (cond, body, alt) => {
50
+ cond = compile(cond); body = compile(body); alt = alt !== undefined ? compile(alt) : null
51
+ return ctx => cond(ctx) ? body(ctx) : alt?.(ctx)
52
+ })
53
+
54
+ // while (cond) body
55
+ token('while', PREC_STATEMENT, a => {
56
+ if (a) return
57
+ space() === OPAREN || err('Expected (')
58
+ skip()
59
+ return ['while', expr(0, CPAREN), parseBody()]
60
+ })
61
+
62
+ operator('while', (cond, body) => {
63
+ cond = compile(cond); body = compile(body)
64
+ return ctx => {
65
+ let r, res
66
+ while (cond(ctx)) {
67
+ r = loop(body, ctx)
68
+ if (r.brk) break
69
+ if (r.cnt) continue
70
+ if (r.ret) return r.val
71
+ res = r.val
72
+ }
73
+ return res
74
+ }
75
+ })
76
+
77
+ // for (init; cond; step) body
78
+ token('for', PREC_STATEMENT, a => {
79
+ if (a) return
80
+ space() === OPAREN || err('Expected (')
81
+ skip()
82
+ const init = space() === SEMI ? null : expr(PREC_SEQ)
83
+ space() === SEMI ? skip() : err('Expected ;')
84
+ const cond = space() === SEMI ? null : expr(PREC_SEQ)
85
+ space() === SEMI ? skip() : err('Expected ;')
86
+ const step = space() === CPAREN ? null : expr(PREC_SEQ)
87
+ space() === CPAREN ? skip() : err('Expected )')
88
+ return ['for', init, cond, step, parseBody()]
89
+ })
90
+
91
+ operator('for', (init, cond, step, body) => {
92
+ init = init ? compile(init) : null
93
+ cond = cond ? compile(cond) : () => true
94
+ step = step ? compile(step) : null
95
+ body = compile(body)
96
+ return ctx => {
97
+ let r, res
98
+ for (init?.(ctx); cond(ctx); step?.(ctx)) {
99
+ r = loop(body, ctx)
100
+ if (r.brk) break
101
+ if (r.cnt) continue
102
+ if (r.ret) return r.val
103
+ res = r.val
104
+ }
105
+ return res
106
+ }
107
+ })
108
+
109
+ // Block parsing helper - only used by control structures
110
+ const parseBody = () => {
111
+ if (space() === OBRACE) {
112
+ skip() // consume {
113
+ return ['block', expr(0, CBRACE)]
114
+ }
115
+ return expr(0) // prec=0 to allow nested control structures
116
+ }
117
+
118
+ operator('block', body => {
119
+ if (body === undefined) return () => {}
120
+ body = compile(body)
121
+ return ctx => body(Object.create(ctx)) // new scope
122
+ })
123
+
124
+ // break / continue / return
125
+ token('break', PREC_TOKEN, a => a ? null : ['break'])
126
+ operator('break', () => () => { throw BREAK })
127
+
128
+ token('continue', PREC_TOKEN, a => a ? null : ['continue'])
129
+ operator('continue', () => () => { throw CONTINUE })
130
+
131
+ token('return', PREC_STATEMENT, a => {
132
+ if (a) return
133
+ space()
134
+ const c = P.cur.charCodeAt(P.idx)
135
+ if (!c || c === CBRACE || c === SEMI) return ['return']
136
+ return ['return', expr(PREC_STATEMENT)]
137
+ })
138
+
139
+ operator('return', val => {
140
+ val = val !== undefined ? compile(val) : null
141
+ return ctx => { throw new Return(val?.(ctx)) }
142
+ })
package/feature/group.js CHANGED
@@ -2,8 +2,7 @@ import { err, nary, group } from '../src/parse.js'
2
2
  import { compile, operator } from '../src/compile.js'
3
3
  import { PREC_ACCESS, PREC_GROUP, PREC_SEQ, PREC_STATEMENT } from '../src/const.js'
4
4
 
5
- // (a,b,c), (a)
6
- // FIXME: try raising group precedence (it causes conflict in ?. though)
5
+ // (a,b,c), (a) — uses PREC_ACCESS to avoid conflict with ?.
7
6
  group('()', PREC_ACCESS)
8
7
  operator('()', (a, b) => b === undefined && (!a && err('Empty ()'), compile(a)))
9
8
 
package/feature/if.js ADDED
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Conditionals: if/else
3
+ *
4
+ * AST:
5
+ * if (c) a else b → ['if', c, a, b?]
6
+ */
7
+ import * as P from '../src/parse.js'
8
+ import { operator, compile } from '../src/compile.js'
9
+ import { PREC_STATEMENT, OPAREN, CPAREN, OBRACE, CBRACE } from '../src/const.js'
10
+
11
+ const { token, expr, skip, space, err, parse } = P
12
+
13
+ // Block parsing helper
14
+ const parseBody = () => {
15
+ if (space() === OBRACE) {
16
+ skip()
17
+ return ['block', expr(0, CBRACE)]
18
+ }
19
+ return expr(0)
20
+ }
21
+
22
+ operator('block', body => {
23
+ if (body === undefined) return () => {}
24
+ body = compile(body)
25
+ return ctx => body(Object.create(ctx))
26
+ })
27
+
28
+ // if (cond) body [else alt]
29
+ token('if', PREC_STATEMENT, a => {
30
+ if (a) return
31
+ space() === OPAREN || err('Expected (')
32
+ skip()
33
+ const cond = expr(0, CPAREN), body = parseBody()
34
+ space()
35
+ const alt = P.cur.substr(P.idx, 4) === 'else' && !parse.id(P.cur.charCodeAt(P.idx + 4))
36
+ ? (skip(), skip(), skip(), skip(), parseBody()) : undefined
37
+ return alt !== undefined ? ['if', cond, body, alt] : ['if', cond, body]
38
+ })
39
+
40
+ operator('if', (cond, body, alt) => {
41
+ cond = compile(cond); body = compile(body); alt = alt !== undefined ? compile(alt) : null
42
+ return ctx => cond(ctx) ? body(ctx) : alt?.(ctx)
43
+ })
@@ -2,13 +2,10 @@ import { token, expr } from "../src/parse.js"
2
2
  import { operator, compile, prop } from "../src/compile.js"
3
3
  import { PREC_POSTFIX } from "../src/const.js"
4
4
 
5
- let inc, dec
6
- token('++', PREC_POSTFIX, a => a ? ['++-', a] : ['++', expr(PREC_POSTFIX - 1)])
5
+ token('++', PREC_POSTFIX, a => a ? ['++', a, null,] : ['++', expr(PREC_POSTFIX - 1)])
7
6
  // ++a, ++((a)), ++a.b, ++a[b]
8
- operator('++', inc = (a) => prop(a, (obj, path) => ++obj[path]))
9
- operator('++-', inc = (a) => prop(a, (obj, path) => obj[path]++))
7
+ operator('++', (a,b) => prop(a, b === null ? (obj, path) => obj[path]++ : (obj, path) => ++obj[path]))
10
8
 
11
- token('--', PREC_POSTFIX, a => a ? ['--+', a] : ['--', expr(PREC_POSTFIX - 1)])
9
+ token('--', PREC_POSTFIX, a => a ? ['--', a, null,] : ['--', expr(PREC_POSTFIX - 1)])
12
10
  // --a, --a.b, --a[b]
13
- operator('--', dec = (a) => (prop(a, (obj, path) => --obj[path])))
14
- operator('--+', dec = (a) => (prop(a, (obj, path) => obj[path]--)))
11
+ operator('--', (a, b) => prop(a, b === null ? (obj, path) => obj[path]-- : (obj, path) => --obj[path]))
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Loops: while, for, break, continue, return
3
+ *
4
+ * AST:
5
+ * while (c) a → ['while', c, a]
6
+ * for (i;c;s) a → ['for', i, c, s, a]
7
+ * break/continue → ['break'] / ['continue']
8
+ * return x → ['return', x?]
9
+ */
10
+ import * as P from '../src/parse.js'
11
+ import { operator, compile } from '../src/compile.js'
12
+ import { PREC_STATEMENT, OPAREN, CPAREN, OBRACE, CBRACE, PREC_SEQ, PREC_TOKEN } from '../src/const.js'
13
+
14
+ const { token, expr, skip, space, err } = P
15
+ const SEMI = 59
16
+
17
+ // Control signals
18
+ class Break {}
19
+ class Continue {}
20
+ class Return { constructor(v) { this.value = v } }
21
+ export const BREAK = new Break(), CONTINUE = new Continue()
22
+
23
+ // Shared loop body executor
24
+ const loop = (body, ctx) => {
25
+ try { return { val: body(ctx) } }
26
+ catch (e) {
27
+ if (e === BREAK) return { brk: 1 }
28
+ if (e === CONTINUE) return { cnt: 1 }
29
+ if (e instanceof Return) return { ret: 1, val: e.value }
30
+ throw e
31
+ }
32
+ }
33
+
34
+ // Block parsing helper
35
+ const parseBody = () => {
36
+ if (space() === OBRACE) {
37
+ skip()
38
+ return ['block', expr(0, CBRACE)]
39
+ }
40
+ return expr(0)
41
+ }
42
+
43
+ operator('block', body => {
44
+ if (body === undefined) return () => {}
45
+ body = compile(body)
46
+ return ctx => body(Object.create(ctx))
47
+ })
48
+
49
+ // while (cond) body
50
+ token('while', PREC_STATEMENT, a => {
51
+ if (a) return
52
+ space() === OPAREN || err('Expected (')
53
+ skip()
54
+ return ['while', expr(0, CPAREN), parseBody()]
55
+ })
56
+
57
+ operator('while', (cond, body) => {
58
+ cond = compile(cond); body = compile(body)
59
+ return ctx => {
60
+ let r, res
61
+ while (cond(ctx)) {
62
+ r = loop(body, ctx)
63
+ if (r.brk) break
64
+ if (r.cnt) continue
65
+ if (r.ret) return r.val
66
+ res = r.val
67
+ }
68
+ return res
69
+ }
70
+ })
71
+
72
+ // for (init; cond; step) body
73
+ token('for', PREC_STATEMENT, a => {
74
+ if (a) return
75
+ space() === OPAREN || err('Expected (')
76
+ skip()
77
+ const init = space() === SEMI ? null : expr(PREC_SEQ)
78
+ space() === SEMI ? skip() : err('Expected ;')
79
+ const cond = space() === SEMI ? null : expr(PREC_SEQ)
80
+ space() === SEMI ? skip() : err('Expected ;')
81
+ const step = space() === CPAREN ? null : expr(PREC_SEQ)
82
+ space() === CPAREN ? skip() : err('Expected )')
83
+ return ['for', init, cond, step, parseBody()]
84
+ })
85
+
86
+ operator('for', (init, cond, step, body) => {
87
+ init = init ? compile(init) : null
88
+ cond = cond ? compile(cond) : () => true
89
+ step = step ? compile(step) : null
90
+ body = compile(body)
91
+ return ctx => {
92
+ let r, res
93
+ for (init?.(ctx); cond(ctx); step?.(ctx)) {
94
+ r = loop(body, ctx)
95
+ if (r.brk) break
96
+ if (r.cnt) continue
97
+ if (r.ret) return r.val
98
+ res = r.val
99
+ }
100
+ return res
101
+ }
102
+ })
103
+
104
+ // break / continue / return
105
+ token('break', PREC_TOKEN, a => a ? null : ['break'])
106
+ operator('break', () => () => { throw BREAK })
107
+
108
+ token('continue', PREC_TOKEN, a => a ? null : ['continue'])
109
+ operator('continue', () => () => { throw CONTINUE })
110
+
111
+ token('return', PREC_STATEMENT, a => {
112
+ if (a) return
113
+ space()
114
+ const c = P.cur.charCodeAt(P.idx)
115
+ if (!c || c === CBRACE || c === SEMI) return ['return']
116
+ return ['return', expr(PREC_STATEMENT)]
117
+ })
118
+
119
+ operator('return', val => {
120
+ val = val !== undefined ? compile(val) : null
121
+ return ctx => { throw new Return(val?.(ctx)) }
122
+ })
package/feature/number.js CHANGED
@@ -1,7 +1,12 @@
1
- import { lookup, next, skip, err, cur, idx } from "../src/parse.js"
1
+ import { lookup, next, err, skip } from "../src/parse.js"
2
+ import { cur, idx } from "../src/parse.js"
2
3
  import { PERIOD, _0, _E, _e, _9 } from "../src/const.js"
3
4
 
4
- // parse number
5
+ // Char codes for prefixes
6
+ const _b = 98, _B = 66, _o = 111, _O = 79, _x = 120, _X = 88
7
+ const _a = 97, _f = 102, _A = 65, _F = 70
8
+
9
+ // parse decimal number (with optional exponent)
5
10
  const num = (a, _) => [, (
6
11
  a = +next(c => (c === PERIOD) || (c >= _0 && c <= _9) || (c === _E || c === _e ? 2 : 0))
7
12
  ) != a ? err() : a]
@@ -9,5 +14,32 @@ const num = (a, _) => [, (
9
14
  // .1
10
15
  lookup[PERIOD] = a => !a && num()
11
16
 
12
- // 0-9
13
- for (let i = _0; i <= _9; i++) lookup[i] = a => a ? err() : num()
17
+ // 1-9 (non-zero starts decimal)
18
+ for (let i = _0 + 1; i <= _9; i++) lookup[i] = a => a ? err() : num()
19
+
20
+ // 0 - check for prefix (0b, 0o, 0x) or plain decimal
21
+ lookup[_0] = a => {
22
+ if (a) return err()
23
+ const nextChar = cur.charCodeAt(idx + 1)
24
+
25
+ // Binary: 0b
26
+ if (nextChar === _b || nextChar === _B) {
27
+ skip(); skip() // consume '0b'
28
+ const s = next(c => c === 48 || c === 49) // 0 or 1
29
+ return [, parseInt(s, 2)]
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)]
42
+ }
43
+
44
+ return num()
45
+ }
@@ -1,30 +1,23 @@
1
1
  import { token, expr } from '../src/parse.js'
2
2
  import { operator, compile } from '../src/compile.js'
3
- import { PREC_ACCESS } from '../src/const.js'
3
+ import { PREC_ACCESS, unsafe } from '../src/const.js'
4
4
 
5
5
  // a?.[, a?.( - postfix operator
6
6
  token('?.', PREC_ACCESS, a => a && ['?.', a])
7
- // a ?.
8
7
  operator('?.', a => (a = compile(a), ctx => a(ctx) || (() => { })))
9
8
 
10
9
  // a?.b, a?.() - optional chain operator
11
10
  token('?.', PREC_ACCESS, (a, b) => a && (b = expr(PREC_ACCESS), !b?.map) && ['?.', a, b])
12
- // a ?. b
13
- operator('?.', (a, b) => b && (a = compile(a), ctx => a(ctx)?.[b]))
11
+ operator('?.', (a, b) => b && (a = compile(a), unsafe(b) ? () => undefined : ctx => a(ctx)?.[b]))
14
12
 
15
13
  // a?.x() - keep context, but watch out a?.()
16
14
  operator('()', (a, b, container, args, path, optional) => b !== undefined && (a[0] === '?.') && (a[2] || Array.isArray(a[1])) && (
17
- args = !b ? () => [] : // a()
18
- b[0] === ',' ? (b = b.slice(1).map(compile), ctx => b.map(a => a(ctx))) : // a(b,c)
19
- (b = compile(b), ctx => [b(ctx)]), // a(b)
20
-
21
- // a?.()
15
+ args = !b ? () => [] :
16
+ b[0] === ',' ? (b = b.slice(1).map(compile), ctx => b.map(a => a(ctx))) :
17
+ (b = compile(b), ctx => [b(ctx)]),
22
18
  !a[2] && (optional = true, a = a[1]),
23
-
24
- // a?.['x']?.()
25
19
  a[0] === '[]' && a.length === 3 ? (path = compile(a[2])) : (path = () => a[2]),
26
- (container = compile(a[1]), optional ?
27
- ctx => (container(ctx)?.[path(ctx)]?.(...args(ctx))) :
28
- ctx => (container(ctx)?.[path(ctx)](...args(ctx)))
29
- )
20
+ container = compile(a[1]), optional ?
21
+ ctx => { const p = path(ctx); return unsafe(p) ? undefined : container(ctx)?.[p]?.(...args(ctx)) } :
22
+ ctx => { const p = path(ctx); return unsafe(p) ? undefined : container(ctx)?.[p](...args(ctx)) }
30
23
  ))
package/feature/string.js CHANGED
@@ -1,14 +1,15 @@
1
- import { skip, err, cur, idx, lookup } from '../src/parse.js'
1
+ import { skip, err, next, cur, idx, lookup } from '../src/parse.js'
2
2
  import { DQUOTE, QUOTE, BSLASH } from '../src/const.js'
3
3
 
4
4
  const escape = { n: '\n', r: '\r', t: '\t', b: '\b', f: '\f', v: '\v' },
5
- string = q => (qc, c, str = '') => {
5
+ string = q => (qc, prec, str = '') => {
6
6
  qc && err('Unexpected string') // must not follow another token
7
- skip() // first quote
8
- while (c = cur.charCodeAt(idx), c - q) {
9
- if (c === BSLASH) skip(), c = skip(), str += escape[c] || c
10
- else str += skip()
11
- }
7
+ skip()
8
+ // while (c = cur.charCodeAt(idx), c - q) {
9
+ // if (c === BSLASH) skip(), c = cur[idx], skip(), str += escape[c] || c
10
+ // else str += cur[idx], skip()
11
+ // }
12
+ next(c => c - q && (c === BSLASH ? (str += escape[cur[idx+1]] || cur[idx+1], 2 ) : (str += cur[idx], 1)))
12
13
  skip() || err('Bad string')
13
14
  return [, str]
14
15
  }
package/feature/var.js ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Variable declarations: let, const
3
+ *
4
+ * AST:
5
+ * let x → ['let', 'x']
6
+ * let x = 1 → ['let', 'x', val]
7
+ * const x = 1 → ['const', 'x', val]
8
+ */
9
+ import * as P from '../src/parse.js'
10
+ import { operator, compile } from '../src/compile.js'
11
+ import { PREC_STATEMENT } from '../src/const.js'
12
+
13
+ const { token, expr, skip, space, err, parse, next } = P
14
+
15
+ // let x [= val]
16
+ token('let', PREC_STATEMENT, a => {
17
+ if (a) return
18
+ space()
19
+ const name = next(parse.id)
20
+ if (!name) err('Expected identifier')
21
+ space()
22
+ if (P.cur.charCodeAt(P.idx) === 61 && P.cur.charCodeAt(P.idx + 1) !== 61) {
23
+ skip()
24
+ return ['let', name, expr(PREC_STATEMENT)]
25
+ }
26
+ return ['let', name]
27
+ })
28
+
29
+ operator('let', (name, val) => {
30
+ val = val !== undefined ? compile(val) : null
31
+ return ctx => { ctx[name] = val ? val(ctx) : undefined }
32
+ })
33
+
34
+ // const x = val
35
+ token('const', PREC_STATEMENT, a => {
36
+ if (a) return
37
+ space()
38
+ const name = next(parse.id)
39
+ if (!name) err('Expected identifier')
40
+ space()
41
+ P.cur.charCodeAt(P.idx) === 61 && P.cur.charCodeAt(P.idx + 1) !== 61 || err('Expected =')
42
+ skip()
43
+ return ['const', name, expr(PREC_STATEMENT)]
44
+ })
45
+
46
+ operator('const', (name, val) => {
47
+ val = compile(val)
48
+ return ctx => { ctx[name] = val(ctx) }
49
+ })
package/justin.min.js CHANGED
@@ -1 +1,4 @@
1
- let r,t,e=e=>(r=0,t=e,e=a(),t[r]?n():e||""),n=(e="Bad syntax",n=t.slice(0,r).split("\n"),l=n.pop())=>{const i=t.slice(r-108,r).split("\n").pop(),a=t.slice(r,r+108).split("\n").shift();throw EvalError(`${e} at ${n.length}:${l.length} \`${r>=108?"…":""}${i}┃${a}\``,"font-weight: bold")},l=(e,n=r,l)=>{for(;l=e(t.charCodeAt(r));)r+=l;return t.slice(n,r)},i=(e=1,n=r)=>(r+=e,t.slice(n,r)),a=(t=0,i)=>{let a,p,c,d;for(;(a=o())&&(c=((d=s[a])&&d(p,t))??(!p&&l(e.id)));)p=c;return i&&(a==i?r++:n()),p},o=e=>{for(;(e=t.charCodeAt(r))<=32;)r++;return e};e.id=r=>r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||36==r||95==r||r>=192&&215!=r&&247!=r;let s=[],p=(l,i=32,a,o=l.charCodeAt(0),p=l.length,c=s[o],d=l.toUpperCase()!==l)=>s[o]=(o,s,f,h=r)=>(f?l==f:(p<2||t.substr(r,p)==l)&&(f=l))&&s<i&&!(d&&e.id(t.charCodeAt(r+p)))&&(r+=p,a(o)||(r=h,!c&&n()))||c?.(o,s,f),c=(r,t,e=!1)=>p(r,t,((n,l)=>n&&(l=a(t-(e?.5:0)))&&[r,n,l])),d=(r,t,e)=>p(r,t,(n=>e?n&&[r,n]:!n&&(n=a(t-.5))&&[r,n])),f=(r,t,e)=>{p(r,t,((e,n)=>(n=a(t),e?.[0]!==r&&(e=[r,e||null]),n?.[0]===r?e.push(...n.slice(1)):e.push(n||null),e)))},h=(r,t)=>p(r[0],t,(t=>!t&&[r,a(0,r.charCodeAt(1))])),u=(r,t)=>p(r[0],t,(t=>t&&[r,t,a(0,r.charCodeAt(1))||null]));const A=r=>Array.isArray(r)?r[0]?g[r[0]].call(...r):()=>r[1]:A.id(r);A.id=r=>t=>t?.[r];const g={},m=(r,t,e=g[r])=>g[r]=(...r)=>t(...r)||e?.(...r),y=(r,t,e,l,i)=>"()"===r[0]&&2==r.length?y(r[1],t,e):"string"==typeof r?e=>t(e,r,e):"."===r[0]?(l=A(r[1]),i=r[2],r=>t(l(r),i,r)):"[]"===r[0]&&3===r.length?(l=A(r[1]),i=A(r[2]),r=>t(l(r),i(r),r)):e?(r=A(r),e=>t([r(e)],0,e)):()=>n("Bad left value"),v=(r,t)=>[,(r=+l((r=>46===r||r>=48&&r<=57||(69===r||101===r?2:0))))!=r?n():r];s[46]=r=>!r&&v();for(let r=48;r<=57;r++)s[r]=r=>r?n():v();const C={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},b=e=>(l,a,o="")=>{for(l&&n("Unexpected string"),i();(a=t.charCodeAt(r))-e;)92===a?(i(),a=i(),o+=C[a]||a):o+=i();return i()||n("Bad string"),[,o]};s[34]=b(34),s[39]=b(39),u("()",170),m("()",((r,t,e)=>void 0!==t&&(e=t?","===t[0]?(t=t.slice(1).map((r=>r?A(r):err())),r=>t.map((t=>t(r)))):(t=A(t),r=>[t(r)]):()=>[],y(r,((r,t,n)=>r[t](...e(n))),!0)))),u("[]",170),m("[]",((r,t)=>t?(r=A(r),t=A(t),e=>r(e)[t(e)]):err())),c(".",170),m(".",((r,t)=>(r=A(r),t=t[0]?t:t[1],e=>r(e)[t]))),h("()",170),m("()",((r,t)=>void 0===t&&(!r&&n("Empty ()"),A(r))));const $=(...r)=>(r=r.map(A),t=>r.map((r=>r(t))).pop());function N(r){if(!r)return"";if(Array.isArray(r)){const[t,...e]=r;return t?"[]"==t||"{}"==t||"()"==t?(e.length>1?N(e.shift()):"")+t[0]+N(e[0])+t[1]:1===e.length?t+N(e[0]):2===e.length?N(e[0])+("."===t?t:" "+t+" ")+N(e[1]):e.filter(Boolean).map((r=>N(r))).join(t+"\n"):JSON.stringify(e[0])}return r}f(",",10),m(",",$),f(";",5),m(";",$),c("=",20,!0),m("=",((r,t)=>(t=A(t),y(r,((r,e,n)=>r[e]=t(n)))))),c("*",120),m("*",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)*t(e)))),c("/",120),m("/",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)/t(e)))),c("%",120),m("%",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)%t(e)))),c("*=",20,!0),m("*=",((r,t)=>(t=A(t),y(r,((r,e,n)=>r[e]*=t(n)))))),c("/=",20,!0),m("/=",((r,t)=>(t=A(t),y(r,((r,e,n)=>r[e]/=t(n)))))),c("%=",20,!0),m("%=",((r,t)=>(t=A(t),y(r,((r,e,n)=>r[e]%=t(n)))))),c("+",110),m("+",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)+t(e)))),c("-",110),m("-",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)-t(e)))),d("+",140),m("+",((r,t)=>!t&&(r=A(r),t=>+r(t)))),d("-",140),m("-",((r,t)=>!t&&(r=A(r),t=>-r(t)))),c("+=",20,!0),m("+=",((r,t)=>(t=A(t),y(r,((r,e,n)=>r[e]+=t(n)))))),c("-=",20,!0),m("-=",((r,t)=>(t=A(t),y(r,((r,e,n)=>r[e]-=t(n)))))),p("++",150,(r=>r?["++-",r]:["++",a(149)])),m("++",(r=>y(r,((r,t)=>++r[t])))),m("++-",(r=>y(r,((r,t)=>r[t]++)))),p("--",150,(r=>r?["--+",r]:["--",a(149)])),m("--",(r=>y(r,((r,t)=>--r[t])))),m("--+",(r=>y(r,((r,t)=>r[t]--)))),d("~",140),m("~",((r,t)=>!t&&(r=A(r),t=>~r(t)))),c("|",50),m("|",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)|t(e)))),c("&",70),m("&",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)&t(e)))),c("^",60),m("^",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)^t(e)))),c("==",80),m("==",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)==t(e)))),c("!=",80),m("!=",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)!=t(e)))),c(">",90),m(">",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)>t(e)))),c("<",90),m("<",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)<t(e)))),c(">=",90),m(">=",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)>=t(e)))),c("<=",90),m("<=",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)<=t(e)))),c(">>",100),m(">>",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)>>t(e)))),c("<<",100),m("<<",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)<<t(e)))),c(">>=",20,!0),m(">>=",((r,t)=>(t=A(t),prop(r,((r,e,n)=>r[e]>>=t(n)))))),c("<<=",20,!0),m("<<=",((r,t)=>(t=A(t),prop(r,((r,e,n)=>r[e]<<=t(n)))))),d("!",140),m("!",((r,t)=>!t&&(r=A(r),t=>!r(t)))),c("||",30),m("||",((r,t)=>(r=A(r),t=A(t),e=>r(e)||t(e)))),c("&&",40),m("&&",((r,t)=>(r=A(r),t=A(t),e=>r(e)&&t(e))));var j=r=>A(e(r));p("/*",200,((e,n)=>(l((e=>42!==e&&47!==t.charCodeAt(r+1))),i(2),e||a(n)||[]))),p("//",200,((r,t)=>(l((r=>r>=32)),r||a(t)||[]))),c("**",130,!0),m("**",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)**t(e)))),p("?",20,((r,t,e)=>r&&(t=a(19))&&l((r=>58===r))&&["?",r,t,a(19)])),m("?",((r,t,e)=>(r=A(r),t=A(t),e=A(e),n=>r(n)?t(n):e(n)))),p("true",200,(r=>r?err():[,!0])),p("false",200,(r=>r?err():[,!1])),h("[]",200),m("[]",((r,t)=>void 0===t&&(r=(r=r?","===r[0]?r.slice(1):[r]:[]).map((r=>"..."===r[0]?(r=A(r[1]),t=>r(t)):(r=A(r),t=>[r(t)]))),t=>r.flatMap((r=>r(t)))))),h("{}",200),m("{}",((r,t)=>void 0===t&&(r=(r=r?","!==r[0]?[r]:r.slice(1):[]).map((r=>A("string"==typeof r?[":",r,r]:r))),t=>Object.fromEntries(r.flatMap((r=>r(t))))))),c(":",19,!0),m(":",((r,t)=>(t=A(t),Array.isArray(r)?(r=A(r),e=>[[r(e),t(e)]]):e=>[[r,t(e)]]))),c("=>",20,!0),m("=>",((r,t)=>(r=(r="()"===r[0]?r[1]:r)?r=","===r[0]?r.slice(1):[r]:[],t=A("{}"===t[0]?t[1]:t),(e=null)=>(e=Object.create(e),(...n)=>(r.map(((r,t)=>e[r]=n[t])),t(e)))))),c(""),p("?.",170,(r=>r&&["?.",r])),m("?.",(r=>(r=A(r),t=>r(t)||(()=>{})))),p("?.",170,((r,t)=>r&&!(t=a(170))?.map&&["?.",r,t])),m("?.",((r,t)=>t&&(r=A(r),e=>r(e)?.[t]))),m("()",((r,t,e,n,l,i)=>void 0!==t&&"?."===r[0]&&(r[2]||Array.isArray(r[1]))&&(n=t?","===t[0]?(t=t.slice(1).map(A),r=>t.map((t=>t(r)))):(t=A(t),r=>[t(r)]):()=>[],!r[2]&&(r=r[1]),l="[]"===r[0]&&3===r.length?A(r[2]):()=>r[2],e=A(r[1]),r=>e(r)?.[l(r)]?.(...n(r))))),d("...",140),m("...",(r=>(r=A(r),t=>Object.entries(r(t))))),c("in",90),m("in",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)in t(e)))),c("===",80),c("!==",9),m("===",((r,t)=>(r=A(r),t=A(t),e=>r(e)===t(e)))),m("!==",((r,t)=>(r=A(r),t=A(t),e=>r(e)!==t(e)))),c("??",30),m("??",((r,t)=>t&&(r=A(r),t=A(t),e=>r(e)??t(e)))),c("??=",20,!0),m("??=",((r,t)=>(t=A(t),y(r,((r,e,n)=>r[e]??=t(n)))))),c("||=",20,!0),m("||=",((r,t)=>(t=A(t),y(r,((r,e,n)=>r[e]||=t(n)))))),c("&&=",20,!0),m("&&=",((r,t)=>(t=A(t),y(r,((r,e,n)=>r[e]&&=t(n)))))),c(">>>",80),m(">>>",((r,t)=>(r=A(r),t=A(t),e=>r(e)>>>t(e)))),c(">>>=",20,!0),m(">>>=",((r,t)=>(t=A(t),y(r,((r,e,n)=>r[e]>>>=t(n)))))),p("undefined",20,(r=>r?n():[,void 0])),p("NaN",20,(r=>r?n():[,NaN])),p("null",20,(r=>r?n():[,null]));export{u as access,c as binary,A as compile,j as default,h as group,f as nary,m as operator,e as parse,N as stringify,p as token,d as unary};
1
+ var g=r=>r?.[0]==="_"&&r[1]==="_"||r==="constructor"||r==="prototype";var f,_,F=r=>(f=0,_=r,r=l(),_[f]?R():r||""),R=(r="Unexpected token",o=_.slice(0,f).split(`
2
+ `),e=o.pop(),t=_.slice(Math.max(0,f-40),f),p=_.slice(f,f+20))=>{throw SyntaxError(`${r} at ${o.length+1}:${e.length+1} \u2014 ${t}^${p}`)},S=(r,o=f,e)=>{for(;e=r(_.charCodeAt(f));)f+=e;return _.slice(o,f)},d=()=>_[f++],l=(r=0,o)=>{let e,t,p,E;for(;(e=rr())&&(p=((E=O[e])&&E(t,r))??(!t&&S(F.id)));)t=p;return o&&(e==o?f++:R("Unclosed "+String.fromCharCode(o-(o>42?2:1)))),t},rr=r=>{for(;(r=_.charCodeAt(f))<=32;)f++;return r},gr=F.id=r=>r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||r==36||r==95||r>=192&&r!=215&&r!=247,O=[],C=(r,o=32,e,t=r.charCodeAt(0),p=r.length,E=O[t],A=r.toUpperCase()!==r)=>O[t]=(y,j,U,b=f)=>(U?r==U:(p<2||_.substr(f,p)==r)&&(U=r))&&j<o&&!(A&&F.id(_.charCodeAt(f+p)))&&(f+=p,e(y)||(f=b,!E&&R()))||E?.(y,j,U),n=(r,o,e=!1)=>C(r,o,(t,p)=>t&&(p=l(o-(e?.5:0)))&&[r,t,p]),N=(r,o,e)=>C(r,o,t=>e?t&&[r,t]:!t&&(t=l(o-.5))&&[r,t]),M=(r,o,e)=>{C(r,o,(t,p)=>(p=l(o-(e?.5:0)),t?.[0]!==r&&(t=[r,t||null]),p?.[0]===r?t.push(...p.slice(1)):t.push(p||null),t))},k=(r,o)=>C(r[0],o,e=>!e&&[r,l(0,r.charCodeAt(1))]),G=(r,o)=>C(r[0],o,e=>e&&[r,e,l(0,r.charCodeAt(1))||null]),J=F;var m=r=>Array.isArray(r)?r[0]?c[r[0]].call(...r):()=>r[1]:m.id(r),Dr=m.id=r=>o=>o?.[r],c={},i=(r,o,e=c[r])=>c[r]=(...t)=>o(...t)||e?.(...t),P=(r,o,e,t,p)=>r[0]==="()"&&r.length==2?P(r[1],o,e):typeof r=="string"?E=>o(E,r,E):r[0]==="."?(t=m(r[1]),p=r[2],E=>o(t(E),p,E)):r[0]==="[]"&&r.length===3?(t=m(r[1]),p=m(r[2]),E=>o(t(E),p(E),E)):e?(r=m(r),E=>o([r(E)],0,E)):()=>R("Bad left value"),s=m;var tr=98,mr=66,ir=111,pr=79,nr=120,fr=88,Er=97,Cr=102,ur=65,Rr=70,w=(r,o)=>[,(r=+S(e=>e===46||e>=48&&e<=57||(e===69||e===101?2:0)))!=r?R():r];O[46]=r=>!r&&w();for(let r=49;r<=57;r++)O[r]=o=>o?R():w();O[48]=r=>{if(r)return R();let o=_.charCodeAt(f+1);if(o===tr||o===mr){d(),d();let e=S(t=>t===48||t===49);return[,parseInt(e,2)]}if(o===ir||o===pr){d(),d();let e=S(t=>t>=48&&t<=55);return[,parseInt(e,8)]}if(o===nr||o===fr){d(),d();let e=S(t=>t>=48&&t<=57||t>=Er&&t<=Cr||t>=ur&&t<=Rr);return[,parseInt(e,16)]}return w()};var _r={n:`
3
+ `,r:"\r",t:" ",b:"\b",f:"\f",v:"\v"},W=r=>(o,e,t="")=>(o&&R("Unexpected string"),d(),S(p=>p-r&&(p===92?(t+=_r[_[f+1]]||_[f+1],2):(t+=_[f],1))),d()||R("Bad string"),[,t]);O[34]=W(34);O[39]=W(39);G("()",170);i("()",(r,o,e)=>o!==void 0&&(e=o?o[0]===","?(o=o.slice(1).map(t=>t?m(t):err()),t=>o.map(p=>p(t))):(o=m(o),t=>[o(t)]):()=>[],P(r,(t,p,E)=>t[p](...e(E)),!0)));G("[]",170);i("[]",(r,o)=>o?(r=m(r),o=m(o),e=>{let t=o(e);return g(t)?void 0:r(e)[t]}):R());n(".",170);i(".",(r,o)=>(r=m(r),o=o[0]?o:o[1],g(o)?()=>{}:e=>r(e)[o]));k("()",170);i("()",(r,o)=>o===void 0&&(!r&&R("Empty ()"),m(r)));var Y=(...r)=>(r=r.map(m),o=>r.map(e=>e(o)).pop());M(",",10),i(",",Y);M(";",5,!0),i(";",Y);n("=",20,!0);i("=",(r,o)=>(o=m(o),P(r,(e,t,p)=>e[t]=o(p))));n("*",120),i("*",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)*o(e)));n("/",120),i("/",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)/o(e)));n("%",120),i("%",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)%o(e)));n("*=",20,!0);i("*=",(r,o)=>(o=m(o),P(r,(e,t,p)=>e[t]*=o(p))));n("/=",20,!0);i("/=",(r,o)=>(o=m(o),P(r,(e,t,p)=>e[t]/=o(p))));n("%=",20,!0);i("%=",(r,o)=>(o=m(o),P(r,(e,t,p)=>e[t]%=o(p))));n("+",110),i("+",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)+o(e)));n("-",110),i("-",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)-o(e)));N("+",140),i("+",(r,o)=>!o&&(r=m(r),e=>+r(e)));N("-",140),i("-",(r,o)=>!o&&(r=m(r),e=>-r(e)));n("+=",20,!0);i("+=",(r,o)=>(o=m(o),P(r,(e,t,p)=>e[t]+=o(p))));n("-=",20,!0);i("-=",(r,o)=>(o=m(o),P(r,(e,t,p)=>e[t]-=o(p))));C("++",150,r=>r?["++",r,null]:["++",l(149)]);i("++",(r,o)=>P(r,o===null?(e,t)=>e[t]++:(e,t)=>++e[t]));C("--",150,r=>r?["--",r,null]:["--",l(149)]);i("--",(r,o)=>P(r,o===null?(e,t)=>e[t]--:(e,t)=>--e[t]));N("~",140),i("~",(r,o)=>!o&&(r=m(r),e=>~r(e)));n("|",50),i("|",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)|o(e)));n("&",70),i("&",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)&o(e)));n("^",60),i("^",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)^o(e)));N("!",140),i("!",(r,o)=>!o&&(r=m(r),e=>!r(e)));n("||",30);i("||",(r,o)=>(r=m(r),o=m(o),e=>r(e)||o(e)));n("&&",40);i("&&",(r,o)=>(r=m(r),o=m(o),e=>r(e)&&o(e)));n("==",80),i("==",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)==o(e)));n("!=",80),i("!=",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)!=o(e)));n(">",90),i(">",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)>o(e)));n("<",90),i("<",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)<o(e)));n(">=",90),i(">=",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)>=o(e)));n("<=",90),i("<=",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)<=o(e)));n(">>",100),i(">>",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)>>o(e)));n("<<",100),i("<<",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)<<o(e)));n(">>=",20,!0);i(">>=",(r,o)=>(o=m(o),prop(r,(e,t,p)=>e[t]>>=o(p))));n("<<=",20,!0);i("<<=",(r,o)=>(o=m(o),prop(r,(e,t,p)=>e[t]<<=o(p))));function X(r){if(!r)return"";if(Array.isArray(r)){let[o,...e]=r;return o?o=="[]"||o=="{}"||o=="()"?(e.length>1?X(e.shift()):"")+o[0]+X(e[0])+o[1]:e.length===1?o+X(e[0]):e.length===2?X(e[0])+(o==="."?o:" "+o+" ")+X(e[1]):e.filter(Boolean).map(t=>X(t)).join(o+`
4
+ `):JSON.stringify(e[0])}return r}var a=r=>s(J(r));C("/*",200,(r,o)=>(S(e=>e!==42&&_.charCodeAt(f+1)!==47),d(),d(),r||l(o)||[]));C("//",200,(r,o)=>(S(e=>e>=32),r||l(o)||[]));n("**",130,!0),i("**",(r,o)=>o&&(r=m(r),o=m(o),e=>r(e)**o(e)));C("?",20,(r,o,e)=>r&&(o=l(19))&&S(t=>t===58)&&(e=l(19),["?",r,o,e]));i("?",(r,o,e)=>(r=m(r),o=m(o),e=m(e),t=>r(t)?o(t):e(t)));C("true",200,r=>r?err():[,!0]);C("false",200,r=>r?err():[,!1]);k("[]",200);i("[]",(r,o)=>o===void 0&&(r=r?r[0]===","?r.slice(1):[r]:[],r=r.map(e=>e[0]==="..."?(e=m(e[1]),t=>e(t)):(e=m(e),t=>[e(t)])),e=>r.flatMap(t=>t(e))));k("{}",200);i("{}",(r,o)=>o===void 0&&(r=r?r[0]!==","?[r]:r.slice(1):[],r=r.map(e=>m(typeof e=="string"?[":",e,e]:e)),e=>Object.fromEntries(r.flatMap(t=>t(e)))));n(":",19,!0);i(":",(r,o)=>(o=m(o),Array.isArray(r)?(r=m(r),e=>[[r(e),o(e)]]):e=>[[r,o(e)]]));n("=>",20,!0);i("=>",(r,o)=>(r=r[0]==="()"?r[1]:r,r=r?r[0]===","?r=r.slice(1):r=[r]:[],o=m(o[0]==="{}"?o[1]:o),(e=null)=>(e=Object.create(e),(...t)=>(r.map((p,E)=>e[p]=t[E]),o(e)))));n("");C("?.",170,r=>r&&["?.",r]);i("?.",r=>(r=m(r),o=>r(o)||(()=>{})));C("?.",170,(r,o)=>r&&(o=l(170),!o?.map)&&["?.",r,o]);i("?.",(r,o)=>o&&(r=m(r),g(o)?()=>{}:e=>r(e)?.[o]));i("()",(r,o,e,t,p,E)=>o!==void 0&&r[0]==="?."&&(r[2]||Array.isArray(r[1]))&&(t=o?o[0]===","?(o=o.slice(1).map(m),A=>o.map(y=>y(A))):(o=m(o),A=>[o(A)]):()=>[],!r[2]&&(E=!0,r=r[1]),r[0]==="[]"&&r.length===3?p=m(r[2]):p=()=>r[2],e=m(r[1]),E?A=>{let y=p(A);return g(y)?void 0:e(A)?.[y]?.(...t(A))}:A=>{let y=p(A);return g(y)?void 0:e(A)?.[y](...t(A))}));N("...",140);i("...",r=>(r=m(r),o=>Object.entries(r(o))));n("in",90),i("in",(r,o)=>o&&(r=s(r),o=s(o),e=>r(e)in o(e)));n("===",80),n("!==",9);i("===",(r,o)=>(r=s(r),o=s(o),e=>r(e)===o(e)));i("!==",(r,o)=>(r=s(r),o=s(o),e=>r(e)!==o(e)));n("??",30);i("??",(r,o)=>o&&(r=s(r),o=s(o),e=>r(e)??o(e)));n("??=",20,!0);i("??=",(r,o)=>(o=s(o),P(r,(e,t,p)=>e[t]??=o(p))));n("||=",20,!0);i("||=",(r,o)=>(o=s(o),P(r,(e,t,p)=>e[t]||=o(p))));n("&&=",20,!0);i("&&=",(r,o)=>(o=s(o),P(r,(e,t,p)=>e[t]&&=o(p))));n(">>>",80);i(">>>",(r,o)=>(r=s(r),o=s(o),e=>r(e)>>>o(e)));n(">>>=",20,!0);i(">>>=",(r,o)=>(o=s(o),P(r,(e,t,p)=>e[t]>>>=o(p))));C("undefined",20,r=>r?R():[,void 0]);C("NaN",20,r=>r?R():[,NaN]);C("null",20,r=>r?R():[,null]);var $e=a;export{G as access,n as binary,m as compile,$e as default,k as group,M as nary,i as operator,F as parse,X as stringify,C as token,N as unary};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "subscript",
3
- "version": "9.0.2",
3
+ "version": "9.2.0",
4
4
  "description": "Fast and tiny expression evaluator with minimal syntax.",
5
5
  "main": "subscript.js",
6
6
  "module": "subscript.js",
@@ -31,12 +31,8 @@
31
31
  "test": "test"
32
32
  },
33
33
  "scripts": {
34
- "build": "npm run build-subscript && npm run build-justin",
35
- "min": "npm run min-subscript && npm run min-justin",
36
- "build-subscript": "rollup subscript.js --file subscript.min.js --format esm --name \"Subscript\"",
37
- "min-subscript": "terser subscript.min.js -o subscript.min.js --module -c passes=3 -m",
38
- "build-justin": "rollup justin.js --file justin.min.js --format esm --name \"Justin\"",
39
- "min-justin": "terser justin.min.js -o justin.min.js --module -c passes=3 -m",
34
+ "build": "esbuild subscript.js --bundle --minify --format=esm --outfile=subscript.min.js && esbuild justin.js --bundle --minify --format=esm --outfile=justin.min.js",
35
+ "prepublishOnly": "npm run build",
40
36
  "test": "node test/test.js",
41
37
  "check-types": "tsc --noEmit subscript.d.ts"
42
38
  },
@@ -81,8 +77,8 @@
81
77
  },
82
78
  "homepage": "https://github.com/dy/subscript#readme",
83
79
  "devDependencies": {
84
- "rollup": "^2.60.2",
85
- "terser": "^5.10.0",
86
- "tst": "^8.0.2"
80
+ "@playwright/test": "^1.57.0",
81
+ "esbuild": "^0.27.2",
82
+ "tst": "^9.0.0"
87
83
  }
88
84
  }
package/src/const.js CHANGED
@@ -40,3 +40,6 @@ export const
40
40
  PREC_ACCESS = 170,
41
41
  PREC_GROUP = 180,
42
42
  PREC_TOKEN = 200
43
+
44
+ // Block prototype chain attacks: constructor, prototype, __proto__, __defineGetter__, etc.
45
+ export const unsafe = k => k?.[0] === '_' && k[1] === '_' || k === 'constructor' || k === 'prototype'
package/src/parse.d.ts CHANGED
@@ -6,7 +6,7 @@ export namespace parse {
6
6
  function id(n: any): any;
7
7
  }
8
8
  export function err(msg?: string, frag?: string): never;
9
- export function skip(n: number): string;
9
+ export function skip(): string;
10
10
  export function next(is: ((c: number) => number)): string;
11
11
  export const lookup: ((a: any, b: any) => any)[];
12
12
  export function token(op: string, prec: number, map: (a: any, curPrec: number, from: number) => any): (a: any, curPrec: number, from?: any) => any;
package/src/parse.js CHANGED
@@ -6,14 +6,14 @@ export let idx, cur,
6
6
  // no handling tagged literals since easily done on user side with cache, if needed
7
7
  parse = s => (idx = 0, cur = s, s = expr(), cur[idx] ? err() : s || ''),
8
8
 
9
- // display error
10
- err = (msg = 'Bad syntax',
9
+ // display error with context
10
+ err = (msg = 'Unexpected token',
11
11
  lines = cur.slice(0, idx).split('\n'),
12
- last = lines.pop()
12
+ last = lines.pop(),
13
+ before = cur.slice(Math.max(0, idx - 40), idx),
14
+ after = cur.slice(idx, idx + 20)
13
15
  ) => {
14
- const before = cur.slice(idx - 108, idx).split('\n').pop()
15
- const after = cur.slice(idx, idx + 108).split('\n').shift()
16
- throw EvalError(`${msg} at ${lines.length}:${last.length} \`${idx >= 108 ? '…' : ''}${before}┃${after}\``, 'font-weight: bold')
16
+ throw SyntaxError(`${msg} at ${lines.length + 1}:${last.length + 1} — ${before}^${after}`)
17
17
  },
18
18
 
19
19
  // advance until condition meets
@@ -22,8 +22,8 @@ export let idx, cur,
22
22
  return cur.slice(from, idx)
23
23
  },
24
24
 
25
- // consume n characters
26
- skip = (n = 1, from = idx) => (idx += n, cur.slice(from, idx)),
25
+ // advance n characters
26
+ skip = () => cur[idx++],
27
27
 
28
28
  // a + b - c
29
29
  expr = (prec = 0, end) => {
@@ -32,8 +32,7 @@ export let idx, cur,
32
32
  // chunk/token parser
33
33
  while (
34
34
  (cc = space()) && // till not end
35
- // FIXME: extra work is happening here, when lookup bails out due to lower precedence -
36
- // it makes extra `space` call for parent exprs on the same character to check precedence again
35
+ // NOTE: when lookup bails on lower precedence, parent expr re-calls space() acceptable overhead
37
36
  (newNode =
38
37
  ((fn = lookup[cc]) && fn(token, prec)) ?? // if operator with higher precedence isn't found
39
38
  (!token && next(parse.id)) // parse literal or quit. token seqs are forbidden: `a b`, `a "b"`, `1.32 a`
@@ -41,7 +40,7 @@ export let idx, cur,
41
40
  ) token = newNode;
42
41
 
43
42
  // check end character
44
- if (end) cc == end ? idx++ : err()
43
+ if (end) cc == end ? idx++ : err('Unclosed ' + String.fromCharCode(end - (end > 42 ? 2 : 1)))
45
44
 
46
45
  return token
47
46
  },
@@ -89,11 +88,12 @@ export let idx, cur,
89
88
  // post indicates postfix rather than prefix operator
90
89
  unary = (op, prec, post) => token(op, prec, a => post ? (a && [op, a]) : (!a && (a = expr(prec - .5)) && [op, a])),
91
90
 
92
- // FIXME: skips means ,,, ;;; are allowed
93
- nary = (op, prec, skips) => {
91
+ // NOTE: allows ;;; (valid empty statements) and ,,, (debatable but harmless)
92
+ // right=true allows same-precedence tokens on RHS (like statements after semicolon)
93
+ nary = (op, prec, right) => {
94
94
  token(op, prec,
95
95
  (a, b) => (
96
- b = expr(prec),
96
+ b = expr(prec - (right ? .5 : 0)),
97
97
  (
98
98
  (a?.[0] !== op) && (a = [op, a || null]), // if beginning of sequence - init node
99
99
  b?.[0] === op ? a.push(...b.slice(1)) : a.push(b || null), // comments can return same-token expr
@@ -103,7 +103,6 @@ export let idx, cur,
103
103
  },
104
104
 
105
105
  // register (a), [b], {c} etc groups
106
- // FIXME: add "Unclosed paren" error
107
106
  group = (op, prec) => token(op[0], prec, a => (!a && [op, expr(0, op.charCodeAt(1))])),
108
107
 
109
108
  // register a(b), a[b], a<b> etc,
package/src/stringify.js CHANGED
@@ -1,10 +1,6 @@
1
1
  // convert ast to code string (codegen)
2
2
 
3
- // FIXME: possible enhancements
4
- // * pairs via options
5
- // * custom spacing/newlines (`a.b` vs `a + b` vs `a, b` vs `a; b`)
6
- // * newlines?
7
- // * custom literals?
3
+ // NOTE: possible enhancements — pairs via options, custom spacing/newlines, custom literals
8
4
  export function stringify(node) {
9
5
  if (!node) return ''
10
6
 
package/subscript.js CHANGED
@@ -11,9 +11,9 @@ import './feature/mult.js'
11
11
  import './feature/add.js'
12
12
  import './feature/increment.js'
13
13
  import './feature/bitwise.js'
14
+ import './feature/logic.js'
14
15
  import './feature/compare.js'
15
16
  import './feature/shift.js'
16
- import './feature/logic.js'
17
17
  import compile from './src/compile.js'
18
18
  import parse from './src/parse.js'
19
19
 
package/subscript.min.js CHANGED
@@ -1 +1,4 @@
1
- let t,r,e=e=>(t=0,r=e,e=i(),r[t]?n():e||""),n=(e="Bad syntax",n=r.slice(0,t).split("\n"),l=n.pop())=>{const o=r.slice(t-108,t).split("\n").pop(),i=r.slice(t,t+108).split("\n").shift();throw EvalError(`${e} at ${n.length}:${l.length} \`${t>=108?"…":""}${o}┃${i}\``,"font-weight: bold")},l=(e,n=t,l)=>{for(;l=e(r.charCodeAt(t));)t+=l;return r.slice(n,t)},o=(e=1,n=t)=>(t+=e,r.slice(n,t)),i=(r=0,o)=>{let i,p,c,h;for(;(i=s())&&(c=((h=a[i])&&h(p,r))??(!p&&l(e.id)));)p=c;return o&&(i==o?t++:n()),p},s=e=>{for(;(e=r.charCodeAt(t))<=32;)t++;return e};e.id=t=>t>=48&&t<=57||t>=65&&t<=90||t>=97&&t<=122||36==t||95==t||t>=192&&215!=t&&247!=t;let a=[],p=(l,o=32,i,s=l.charCodeAt(0),p=l.length,c=a[s],h=l.toUpperCase()!==l)=>a[s]=(s,a,d,f=t)=>(d?l==d:(p<2||r.substr(t,p)==l)&&(d=l))&&a<o&&!(h&&e.id(r.charCodeAt(t+p)))&&(t+=p,i(s)||(t=f,!c&&n()))||c?.(s,a,d),c=(t,r,e=!1)=>p(t,r,((n,l)=>n&&(l=i(r-(e?.5:0)))&&[t,n,l])),h=(t,r,e)=>p(t,r,(n=>e?n&&[t,n]:!n&&(n=i(r-.5))&&[t,n])),d=(t,r,e)=>{p(t,r,((e,n)=>(n=i(r),e?.[0]!==t&&(e=[t,e||null]),n?.[0]===t?e.push(...n.slice(1)):e.push(n||null),e)))},f=(t,r)=>p(t[0],r,(r=>!r&&[t,i(0,t.charCodeAt(1))])),u=(t,r)=>p(t[0],r,(r=>r&&[t,r,i(0,t.charCodeAt(1))||null]));const g=(t,r)=>[,(t=+l((t=>46===t||t>=48&&t<=57||(69===t||101===t?2:0))))!=t?n():t];a[46]=t=>!t&&g();for(let t=48;t<=57;t++)a[t]=t=>t?n():g();const A={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},y=e=>(l,i,s="")=>{for(l&&n("Unexpected string"),o();(i=r.charCodeAt(t))-e;)92===i?(o(),i=o(),s+=A[i]||i):s+=o();return o()||n("Bad string"),[,s]};a[34]=y(34),a[39]=y(39);const C=t=>Array.isArray(t)?t[0]?v[t[0]].call(...t):()=>t[1]:C.id(t);C.id=t=>r=>r?.[t];const v={},m=(t,r,e=v[t])=>v[t]=(...t)=>r(...t)||e?.(...t),$=(t,r,e,l,o)=>"()"===t[0]&&2==t.length?$(t[1],r,e):"string"==typeof t?e=>r(e,t,e):"."===t[0]?(l=C(t[1]),o=t[2],t=>r(l(t),o,t)):"[]"===t[0]&&3===t.length?(l=C(t[1]),o=C(t[2]),t=>r(l(t),o(t),t)):e?(t=C(t),e=>r([t(e)],0,e)):()=>n("Bad left value");u("()",170),m("()",((t,r,e)=>void 0!==r&&(e=r?","===r[0]?(r=r.slice(1).map((t=>t?C(t):err())),t=>r.map((r=>r(t)))):(r=C(r),t=>[r(t)]):()=>[],$(t,((t,r,n)=>t[r](...e(n))),!0)))),u("[]",170),m("[]",((t,r)=>r?(t=C(t),r=C(r),e=>t(e)[r(e)]):err())),c(".",170),m(".",((t,r)=>(t=C(t),r=r[0]?r:r[1],e=>t(e)[r]))),f("()",170),m("()",((t,r)=>void 0===r&&(!t&&n("Empty ()"),C(t))));const b=(...t)=>(t=t.map(C),r=>t.map((t=>t(r))).pop());function B(t){if(!t)return"";if(Array.isArray(t)){const[r,...e]=t;return r?"[]"==r||"{}"==r||"()"==r?(e.length>1?B(e.shift()):"")+r[0]+B(e[0])+r[1]:1===e.length?r+B(e[0]):2===e.length?B(e[0])+("."===r?r:" "+r+" ")+B(e[1]):e.filter(Boolean).map((t=>B(t))).join(r+"\n"):JSON.stringify(e[0])}return t}d(",",10),m(",",b),d(";",5),m(";",b),c("=",20,!0),m("=",((t,r)=>(r=C(r),$(t,((t,e,n)=>t[e]=r(n)))))),c("*",120),m("*",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)*r(e)))),c("/",120),m("/",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)/r(e)))),c("%",120),m("%",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)%r(e)))),c("*=",20,!0),m("*=",((t,r)=>(r=C(r),$(t,((t,e,n)=>t[e]*=r(n)))))),c("/=",20,!0),m("/=",((t,r)=>(r=C(r),$(t,((t,e,n)=>t[e]/=r(n)))))),c("%=",20,!0),m("%=",((t,r)=>(r=C(r),$(t,((t,e,n)=>t[e]%=r(n)))))),c("+",110),m("+",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)+r(e)))),c("-",110),m("-",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)-r(e)))),h("+",140),m("+",((t,r)=>!r&&(t=C(t),r=>+t(r)))),h("-",140),m("-",((t,r)=>!r&&(t=C(t),r=>-t(r)))),c("+=",20,!0),m("+=",((t,r)=>(r=C(r),$(t,((t,e,n)=>t[e]+=r(n)))))),c("-=",20,!0),m("-=",((t,r)=>(r=C(r),$(t,((t,e,n)=>t[e]-=r(n)))))),p("++",150,(t=>t?["++-",t]:["++",i(149)])),m("++",(t=>$(t,((t,r)=>++t[r])))),m("++-",(t=>$(t,((t,r)=>t[r]++)))),p("--",150,(t=>t?["--+",t]:["--",i(149)])),m("--",(t=>$(t,((t,r)=>--t[r])))),m("--+",(t=>$(t,((t,r)=>t[r]--)))),h("~",140),m("~",((t,r)=>!r&&(t=C(t),r=>~t(r)))),c("|",50),m("|",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)|r(e)))),c("&",70),m("&",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)&r(e)))),c("^",60),m("^",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)^r(e)))),c("==",80),m("==",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)==r(e)))),c("!=",80),m("!=",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)!=r(e)))),c(">",90),m(">",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)>r(e)))),c("<",90),m("<",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)<r(e)))),c(">=",90),m(">=",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)>=r(e)))),c("<=",90),m("<=",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)<=r(e)))),c(">>",100),m(">>",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)>>r(e)))),c("<<",100),m("<<",((t,r)=>r&&(t=C(t),r=C(r),e=>t(e)<<r(e)))),c(">>=",20,!0),m(">>=",((t,r)=>(r=C(r),prop(t,((t,e,n)=>t[e]>>=r(n)))))),c("<<=",20,!0),m("<<=",((t,r)=>(r=C(r),prop(t,((t,e,n)=>t[e]<<=r(n)))))),h("!",140),m("!",((t,r)=>!r&&(t=C(t),r=>!t(r)))),c("||",30),m("||",((t,r)=>(t=C(t),r=C(r),e=>t(e)||r(e)))),c("&&",40),m("&&",((t,r)=>(t=C(t),r=C(r),e=>t(e)&&r(e))));var x=t=>C(e(t));export{u as access,c as binary,C as compile,x as default,f as group,d as nary,m as operator,e as parse,B as stringify,p as token,h as unary};
1
+ var F=r=>r?.[0]==="_"&&r[1]==="_"||r==="constructor"||r==="prototype";var E,f,I=r=>(E=0,f=r,r=s(),f[E]?R():r||""),R=(r="Unexpected token",o=f.slice(0,E).split(`
2
+ `),t=o.pop(),e=f.slice(Math.max(0,E-40),E),m=f.slice(E,E+20))=>{throw SyntaxError(`${r} at ${o.length+1}:${t.length+1} \u2014 ${e}^${m}`)},S=(r,o=E,t)=>{for(;t=r(f.charCodeAt(E));)E+=t;return f.slice(o,E)},l=()=>f[E++],s=(r=0,o)=>{let t,e,m,C;for(;(t=a())&&(m=((C=u[t])&&C(e,r))??(!e&&S(I.id)));)e=m;return o&&(t==o?E++:R("Unclosed "+String.fromCharCode(o-(o>42?2:1)))),e},a=r=>{for(;(r=f.charCodeAt(E))<=32;)E++;return r},dr=I.id=r=>r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||r==36||r==95||r>=192&&r!=215&&r!=247,u=[],A=(r,o=32,t,e=r.charCodeAt(0),m=r.length,C=u[e],Y=r.toUpperCase()!==r)=>u[e]=(c,M,T,Z=E)=>(T?r==T:(m<2||f.substr(E,m)==r)&&(T=r))&&M<o&&!(Y&&I.id(f.charCodeAt(E+m)))&&(E+=m,t(c)||(E=Z,!C&&R()))||C?.(c,M,T),n=(r,o,t=!1)=>A(r,o,(e,m)=>e&&(m=s(o-(t?.5:0)))&&[r,e,m]),y=(r,o,t)=>A(r,o,e=>t?e&&[r,e]:!e&&(e=s(o-.5))&&[r,e]),g=(r,o,t)=>{A(r,o,(e,m)=>(m=s(o-(t?.5:0)),e?.[0]!==r&&(e=[r,e||null]),m?.[0]===r?e.push(...m.slice(1)):e.push(m||null),e))},B=(r,o)=>A(r[0],o,t=>!t&&[r,s(0,r.charCodeAt(1))]),N=(r,o)=>A(r[0],o,t=>t&&[r,t,s(0,r.charCodeAt(1))||null]),w=I;var rr=98,or=66,tr=111,er=79,ir=120,pr=88,mr=97,nr=102,Er=65,Cr=70,k=(r,o)=>[,(r=+S(t=>t===46||t>=48&&t<=57||(t===69||t===101?2:0)))!=r?R():r];u[46]=r=>!r&&k();for(let r=49;r<=57;r++)u[r]=o=>o?R():k();u[48]=r=>{if(r)return R();let o=f.charCodeAt(E+1);if(o===rr||o===or){l(),l();let t=S(e=>e===48||e===49);return[,parseInt(t,2)]}if(o===tr||o===er){l(),l();let t=S(e=>e>=48&&e<=55);return[,parseInt(t,8)]}if(o===ir||o===pr){l(),l();let t=S(e=>e>=48&&e<=57||e>=mr&&e<=nr||e>=Er&&e<=Cr);return[,parseInt(t,16)]}return k()};var Rr={n:`
3
+ `,r:"\r",t:" ",b:"\b",f:"\f",v:"\v"},x=r=>(o,t,e="")=>(o&&R("Unexpected string"),l(),S(m=>m-r&&(m===92?(e+=Rr[f[E+1]]||f[E+1],2):(e+=f[E],1))),l()||R("Bad string"),[,e]);u[34]=x(34);u[39]=x(39);var i=r=>Array.isArray(r)?r[0]?G[r[0]].call(...r):()=>r[1]:i.id(r),Xr=i.id=r=>o=>o?.[r],G={},p=(r,o,t=G[r])=>G[r]=(...e)=>o(...e)||t?.(...e),_=(r,o,t,e,m)=>r[0]==="()"&&r.length==2?_(r[1],o,t):typeof r=="string"?C=>o(C,r,C):r[0]==="."?(e=i(r[1]),m=r[2],C=>o(e(C),m,C)):r[0]==="[]"&&r.length===3?(e=i(r[1]),m=i(r[2]),C=>o(e(C),m(C),C)):t?(r=i(r),C=>o([r(C)],0,C)):()=>R("Bad left value"),J=i;N("()",170);p("()",(r,o,t)=>o!==void 0&&(t=o?o[0]===","?(o=o.slice(1).map(e=>e?i(e):err()),e=>o.map(m=>m(e))):(o=i(o),e=>[o(e)]):()=>[],_(r,(e,m,C)=>e[m](...t(C)),!0)));N("[]",170);p("[]",(r,o)=>o?(r=i(r),o=i(o),t=>{let e=o(t);return F(e)?void 0:r(t)[e]}):R());n(".",170);p(".",(r,o)=>(r=i(r),o=o[0]?o:o[1],F(o)?()=>{}:t=>r(t)[o]));B("()",170);p("()",(r,o)=>o===void 0&&(!r&&R("Empty ()"),i(r)));var q=(...r)=>(r=r.map(i),o=>r.map(t=>t(o)).pop());g(",",10),p(",",q);g(";",5,!0),p(";",q);n("=",20,!0);p("=",(r,o)=>(o=i(o),_(r,(t,e,m)=>t[e]=o(m))));n("*",120),p("*",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)*o(t)));n("/",120),p("/",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)/o(t)));n("%",120),p("%",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)%o(t)));n("*=",20,!0);p("*=",(r,o)=>(o=i(o),_(r,(t,e,m)=>t[e]*=o(m))));n("/=",20,!0);p("/=",(r,o)=>(o=i(o),_(r,(t,e,m)=>t[e]/=o(m))));n("%=",20,!0);p("%=",(r,o)=>(o=i(o),_(r,(t,e,m)=>t[e]%=o(m))));n("+",110),p("+",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)+o(t)));n("-",110),p("-",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)-o(t)));y("+",140),p("+",(r,o)=>!o&&(r=i(r),t=>+r(t)));y("-",140),p("-",(r,o)=>!o&&(r=i(r),t=>-r(t)));n("+=",20,!0);p("+=",(r,o)=>(o=i(o),_(r,(t,e,m)=>t[e]+=o(m))));n("-=",20,!0);p("-=",(r,o)=>(o=i(o),_(r,(t,e,m)=>t[e]-=o(m))));A("++",150,r=>r?["++",r,null]:["++",s(149)]);p("++",(r,o)=>_(r,o===null?(t,e)=>t[e]++:(t,e)=>++t[e]));A("--",150,r=>r?["--",r,null]:["--",s(149)]);p("--",(r,o)=>_(r,o===null?(t,e)=>t[e]--:(t,e)=>--t[e]));y("~",140),p("~",(r,o)=>!o&&(r=i(r),t=>~r(t)));n("|",50),p("|",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)|o(t)));n("&",70),p("&",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)&o(t)));n("^",60),p("^",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)^o(t)));y("!",140),p("!",(r,o)=>!o&&(r=i(r),t=>!r(t)));n("||",30);p("||",(r,o)=>(r=i(r),o=i(o),t=>r(t)||o(t)));n("&&",40);p("&&",(r,o)=>(r=i(r),o=i(o),t=>r(t)&&o(t)));n("==",80),p("==",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)==o(t)));n("!=",80),p("!=",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)!=o(t)));n(">",90),p(">",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)>o(t)));n("<",90),p("<",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)<o(t)));n(">=",90),p(">=",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)>=o(t)));n("<=",90),p("<=",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)<=o(t)));n(">>",100),p(">>",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)>>o(t)));n("<<",100),p("<<",(r,o)=>o&&(r=i(r),o=i(o),t=>r(t)<<o(t)));n(">>=",20,!0);p(">>=",(r,o)=>(o=i(o),prop(r,(t,e,m)=>t[e]>>=o(m))));n("<<=",20,!0);p("<<=",(r,o)=>(o=i(o),prop(r,(t,e,m)=>t[e]<<=o(m))));function O(r){if(!r)return"";if(Array.isArray(r)){let[o,...t]=r;return o?o=="[]"||o=="{}"||o=="()"?(t.length>1?O(t.shift()):"")+o[0]+O(t[0])+o[1]:t.length===1?o+O(t[0]):t.length===2?O(t[0])+(o==="."?o:" "+o+" ")+O(t[1]):t.filter(Boolean).map(e=>O(e)).join(o+`
4
+ `):JSON.stringify(t[0])}return r}var $o=r=>J(w(r));export{N as access,n as binary,i as compile,$o as default,B as group,g as nary,p as operator,I as parse,O as stringify,A as token,y as unary};