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 +48 -42
- package/feature/access.js +4 -4
- package/feature/comment.js +1 -2
- package/feature/control.js +142 -0
- package/feature/group.js +1 -2
- package/feature/if.js +43 -0
- package/feature/increment.js +4 -7
- package/feature/loop.js +122 -0
- package/feature/number.js +36 -4
- package/feature/optional.js +8 -15
- package/feature/string.js +8 -7
- package/feature/var.js +49 -0
- package/justin.min.js +4 -1
- package/package.json +6 -10
- package/src/const.js +3 -0
- package/src/parse.d.ts +1 -1
- package/src/parse.js +14 -15
- package/src/stringify.js +1 -5
- package/subscript.js +1 -1
- package/subscript.min.js +4 -1
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
|
|
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
|
|
9
|
-
* sandboxes, playgrounds, safe eval
|
|
10
|
-
* custom DSL
|
|
11
|
-
* preprocessors
|
|
12
|
-
* templates
|
|
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_)
|
|
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`,
|
|
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
|
|
66
|
+
import justin from 'subscript/justin'
|
|
68
67
|
|
|
69
|
-
let
|
|
70
|
-
|
|
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;
|
|
135
|
+
'a'; // variable (from scope)
|
|
114
136
|
null|empty; // placeholder
|
|
115
137
|
|
|
116
138
|
// eg.
|
|
117
139
|
['()', 'a'] // (a)
|
|
118
|
-
['()', 'a'
|
|
140
|
+
['()', 'a', null] // a()
|
|
119
141
|
['()', 'a', 'b'] // a(b)
|
|
120
|
-
['
|
|
121
|
-
['
|
|
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"
|
|
154
|
+
// 'min * 60 + "sec"'
|
|
133
155
|
```
|
|
134
156
|
|
|
135
157
|
## Extending
|
|
136
158
|
|
|
137
|
-
_Subscript_ provides premade language [features](./
|
|
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('
|
|
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 {
|
|
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 =>
|
|
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]))
|
|
11
|
+
operator('.', (a, b) => (a = compile(a), b = !b[0] ? b[1] : b, unsafe(b) ? () => undefined : ctx => a(ctx)[b]))
|
package/feature/comment.js
CHANGED
|
@@ -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
|
-
|
|
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
|
+
})
|
package/feature/increment.js
CHANGED
|
@@ -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
|
-
|
|
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('++',
|
|
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 ? ['
|
|
9
|
+
token('--', PREC_POSTFIX, a => a ? ['--', a, null,] : ['--', expr(PREC_POSTFIX - 1)])
|
|
12
10
|
// --a, --a.b, --a[b]
|
|
13
|
-
operator('--',
|
|
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]))
|
package/feature/loop.js
ADDED
|
@@ -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,
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
+
}
|
package/feature/optional.js
CHANGED
|
@@ -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
|
-
|
|
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 ? () => [] :
|
|
18
|
-
b[0] === ',' ? (b = b.slice(1).map(compile), ctx => b.map(a => a(ctx))) :
|
|
19
|
-
(b = compile(b), ctx => [b(ctx)]),
|
|
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
|
-
|
|
27
|
-
ctx => (container(ctx)?.[
|
|
28
|
-
ctx => (container(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,
|
|
5
|
+
string = q => (qc, prec, str = '') => {
|
|
6
6
|
qc && err('Unexpected string') // must not follow another token
|
|
7
|
-
skip()
|
|
8
|
-
while (c = cur.charCodeAt(idx), c - q) {
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
|
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": "
|
|
35
|
-
"
|
|
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
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"tst": "^
|
|
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(
|
|
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 = '
|
|
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
|
-
|
|
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
|
-
//
|
|
26
|
-
skip = (
|
|
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
|
-
//
|
|
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
|
-
//
|
|
93
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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};
|