subscript 7.6.2 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,126 +1,88 @@
1
- # <img alt="subscript" src="/subscript2.svg" height=28/> <!--sub͘<em>script</em>--> <!--<sub>SUB͘<em>SCRIPT</em></sub>--> <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="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>
1
+ # <img alt="subscript" src="/subscript2.svg" height=28/> <!--sub͘<em>script</em>--> <!--<sub>SUB͘<em>SCRIPT</em></sub>--> <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://bundlephobia.com/package/subscript"><img alt="npm bundle size" src="https://img.shields.io/bundlephobia/minzip/subscript/latest?color=brightgreen&label=gzip"/></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 expression evaluator / microlanguage with [common syntax](https://en.wikipedia.org/wiki/Comparison_of_programming_languages_(syntax)) (JavaScript, Java, C, C++, Rust, Go, Python, Kotlin etc).<br/>
3
+ > _Subscript_ is fast, tiny & extensible expression evaluator / microlanguage.
4
4
 
5
- * Tiny size <sub><a href="https://bundlephobia.com/package/subscript"><img alt="npm bundle size" src="https://img.shields.io/bundlephobia/minzip/subscript/latest?color=brightgreen&label=gzip"/></a></sub>
6
- * :rocket: Fast [performance](#performance)
7
- * Configurable & extensible
8
- * Trivial to use
9
-
10
- ```js
11
- import subscript, { parse, compile } from './subscript.js'
12
-
13
- // create expression evaluator
14
- let fn = subscript('a.b + Math.sqrt(c - 1)')
15
- fn({ a: { b:1 }, c: 5, Math }) // 3
16
-
17
- // --- or ---
18
- // parse expression
19
- let tree = parse('a.b + c')
20
- tree // ['+', ['.', 'a', 'b'], 'c']
21
-
22
- // compile tree to evaluable function
23
- fn = compile(tree)
24
- fn({a:{b:1}, c:2}) // 3
25
- ```
26
-
27
- ## Motivation
28
-
29
- _Subscript_ is designed to be useful for:
5
+ Used for:
30
6
 
31
7
  * templates (eg. [sprae](https://github.com/dy/sprae), [templize](https://github.com/dy/templize))
32
8
  * expressions evaluators, calculators
33
9
  * subsets of languages (eg. [justin](#justin))
34
- * pluggable/configurable/mock language features (eg. pipe operator)
35
10
  * sandboxes, playgrounds, safe eval
36
- * custom DSL (see [mell](https://github.com/dy/lino)) <!-- uneural -->
37
- * preprocessors (see [prepr](https://github.com/dy/prepr))
11
+ * custom DSL (eg. [mell](https://github.com/dy/lino)) <!-- uneural -->
12
+ * preprocessors (eg. [prepr](https://github.com/dy/prepr))
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_), good [performance](#performance) and wide test coverage.
15
+
16
+
17
+ ## Usage
18
+
19
+ ```js
20
+ import subscript from './subscript.js'
38
21
 
39
- _Subscript_ has [3.5kb](https://npmfs.com/package/subscript/7.4.3/subscript.min.js) footprint, compared 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_, with better test coverage and better performance.
22
+ // parse expression
23
+ const fn = subscript('a.b + Math.sqrt(c - 1)')
40
24
 
25
+ // evaluate with context
26
+ fn({ a: { b:1 }, c: 5, Math })
27
+ // 3
28
+ ```
41
29
 
42
- ## Operators / literals
30
+ ## Operators
43
31
 
44
- <small>↑ precedence order</small>
32
+ _Subscript_ supports [common syntax](https://en.wikipedia.org/wiki/Comparison_of_programming_languages_(syntax)) (shared by _JavaScript_,_C_, _C++_, _Java_, _C#_, _PHP_, _Swift_, _Objective-C_, _Kotlin_, _Perl_ etc.):
45
33
 
46
- * `( a, b, c )`
47
- * `a.b`, `a[b]`, `a(b, c)`
48
- * `a++`, `a--` unary postfix
49
- * `!a`, `+a`, `-a`, `++a`, `--a` unary prefix
34
+ * `a.b`, `a[b]`, `a(b)`
35
+ * `a++`, `a--`, `++a`, `--a`
50
36
  * `a * b`, `a / b`, `a % b`
51
- * `a + b`, `a - b`
52
- * `a << b`, `a >> b`, `a >>> b`
53
- * `a < b`, `a <= b`, `a > b`, `a >= b`
54
- * `a == b`, `a != b`
55
- * `a & b`
56
- * `a ^ b`
57
- * `a | b`
58
- * `a && b`
59
- * `a || b`
60
- * `a , b`
61
- * `"abc"` strings
62
- * `1.2e+3` numbers
63
-
64
- ## Justin
65
-
66
- _Justin_ is minimal JS subset − JSON with JS expressions (see original [thread](https://github.com/endojs/Jessie/issues/66)).<br/>
67
-
68
- It extends _subscript_ with:
69
-
70
- + `**` exponentiation operator (right-assoc)
71
- + `~` bit inversion operator
72
- + `'` strings
73
- + `?:` ternary operator
74
- + `?.` optional chain operator
75
- + `[...]` Array literal
76
- + `{...}` Object literal
77
- + `in` binary
78
- + `;` expression separator
79
- + `//`, `/* */` comments
80
- + `true`, `false`, `null` literals
37
+ * `+a`, `-a`, `a + b`, `a - b`
38
+ * `a < b`, `a <= b`, `a > b`, `a >= b`, `a == b`, `a != b`
39
+ * `~a`, `a & b`, `a ^ b`, `a | b`, `a << b`, `a >> b`
40
+ * `!a`, `a && b`, `a || b`
41
+ * `a = b`, `a += b`, `a -= b`, `a *= b`, `a /= b`, `a %= b`
42
+ * `(a, (b))`, `a; b;`
43
+ * `"abc"`, `'abc'`
44
+ * `0.1`, `1.2e+3`
45
+
46
+ ### Justin
47
+
48
+ _Justin_ is minimal JS subset, _JSON_ + _Expressions_ (see [thread](https://github.com/endojs/Jessie/issues/66)). It extends _subscript_ with:
49
+
50
+ + `a ** b` (right-assoc)
51
+ + `a ? b : c`
52
+ + `a?.b`
53
+ + `[a, b]` Array
54
+ + `{a: b}` Object
55
+ + `a in b`
56
+ + `// foo`, `/* bar */`
57
+ + `true`, `false`, `null`
81
58
  <!-- + `...x` unary operator -->
82
59
  <!-- + strings interpolation -->
83
60
 
84
61
  ```js
85
- import jstin from 'subscript/justin.js'
62
+ import jstin from './justin.js'
86
63
 
87
64
  let xy = jstin('{ x: 1, "y": 2+2 }["x"]')
88
65
  xy() // 1
89
66
  ```
90
67
 
91
- ## Extending
92
68
 
93
- Operators/tokens can be extended via:
69
+ ## Parse / Compile
94
70
 
95
- * `unary(str, precedence, postfix=false)` register unary operator, either prefix or postfix.
96
- * `binary(str, precedence, rightAssoc=false)` − register binary operator, optionally right-associative.
97
- * `nary(str, precedence, allowSkip=false)` − register n-ary (sequence) operator, optionally allowing skipping args.
98
- * `token(str, precedence, map)` − register custom token or literal. `map` takes last token argument and returns tree node.
99
- * `operator(str, fn)` − register evaluator for operator. `fn` takes node arguments and returns evaluator function.
71
+ Subscript exposes `parse` to build AST and `compile` to create evaluators.
100
72
 
101
73
  ```js
102
- import script, { operator, unary, binary, token } from './subscript.js'
103
-
104
- // add ~ unary operator with precedence 15
105
- unary('~', 15)
106
- operator('~', a => ctx => ~a)
74
+ import { parse, compile } from 'subscript'
107
75
 
108
- // add === binary operator with precedence 9
109
- binary('===', 9)
110
- operator('===', (a, b) => ctx => ctx[a]===ctx[b])
76
+ // parse expression
77
+ let tree = parse('a.b + c - 1')
78
+ tree // ['-', ['+', ['.', 'a', 'b'], 'c'], [,1]]
111
79
 
112
- // add boolean literals
113
- token('true', 20, prev => ['',true])
114
- token('false', 20, prev => ['',false])
115
- operator('', boolNode => ctx => boolNode[1])
80
+ // compile tree to evaluable function
81
+ fn = compile(tree)
82
+ fn({ a: {b: 1}, c: 2 }) // 3
116
83
  ```
117
84
 
118
- See [subscript.js](subscript.js) or [justin.js](./justin.js) for examples.
119
-
120
-
121
- ## Syntax tree
122
-
123
- Subscript exposes separate `./parse.js` and `./compile.js` entries. Parser builds AST, compiler converts it to evaluable function.
85
+ ## Syntax Tree
124
86
 
125
87
  AST has simplified lispy tree structure (inspired by [frisk](https://ghub.io/frisk) / [nisp](https://github.com/ysmood/nisp)), opposed to [ESTree](https://github.com/estree/estree):
126
88
 
@@ -133,11 +95,37 @@ AST has simplified lispy tree structure (inspired by [frisk](https://ghub.io/fri
133
95
  ```js
134
96
  import { compile } from 'subscript.js'
135
97
 
136
- const fn = compile(['+', ['*', 'min', ['',60]], ['','sec']])
98
+ const fn = compile(['+', ['*', 'min', [,60]], [,'sec']])
137
99
 
138
100
  fn({min: 5}) // min*60 + "sec" == "300sec"
139
101
  ```
140
102
 
103
+ ## Extending
104
+
105
+ _Subscript_ provides API to customize or extend syntax:
106
+
107
+ * `unary(str, precedence, postfix=false)` − register unary operator, either prefix or postfix.
108
+ * `binary(str, precedence, rightAssoc=false)` − register binary operator, optionally right-associative.
109
+ * `nary(str, precedence, allowSkip=false)` − register n-ary (sequence) operator, optionally allowing skipping args.
110
+ * `token(str, precedence, prevNode => curNode)` − register custom token or literal. Function takes last token and returns tree node.
111
+ * `operator(str, (a, b) => ctx => result)` − register evaluator for an operator. Function takes node arguments and returns evaluator function.
112
+
113
+ ```js
114
+ import script, { compile, operator, unary, binary, token } from './subscript.js'
115
+
116
+ // add identity operators with precedence 9
117
+ binary('===', 9), binary('!==', 9)
118
+ operator('===', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx)===b(ctx)))
119
+ operator('===', (a, b) => (a = compile(a), b = compile(b), ctx => a(ctx)!==b(ctx)))
120
+
121
+ // add JS literals
122
+ token('undefined', 20, a => a ? err() : [, undefined])
123
+ token('NaN', 20, a => a ? err() : [, NaN])
124
+ ```
125
+
126
+ See [`./feature/*`](./feature) for examples.
127
+
128
+
141
129
  <!--
142
130
  ## Ideas
143
131
 
@@ -151,133 +139,31 @@ template-parts proposal
151
139
  </template>
152
140
  ```
153
141
 
154
- // a.b.c
155
- // (node, c) => c === PERIOD ? (index++, space(), ['.', node, '"'+id()+'"']) : node,
156
-
157
- // a[b][c]
158
- // (node, c) => c === OBRACK ? (index++, node=['.', node, expr(CBRACK)], index++, node) : node,
159
-
160
- // a(b)(c)
161
- // (node, c, arg) => c === OPAREN ? (
162
- // index++, arg=expr(CPAREN),
163
- // node = Array.isArray(arg) && arg[0]===',' ? (arg[0]=node, arg) : arg == null ? [node] : [node, arg],
164
- // index++, node
165
- // ) : node,
166
-
167
- <details>
168
- <summary>Keyed arrays <code>[a:1, b:2, c:3]</code></summary>
169
-
170
- ```js
171
-
172
- ```
173
- </details>
174
-
175
- <details>
176
- <summary>`7!` (factorial)</summary>
177
-
178
- ```js
179
- ```
180
-
181
- </details>
182
- <details>
183
- <summary>`5s`, `5rem` (units)</summary>
184
-
185
- ```js
186
- ```
187
-
188
- </details>
189
- <details>
190
- <summary>`?`, `?.`, `??`</summary>
191
-
192
- ```js
193
- ```
194
-
195
- </details>
196
- <details>
197
- <summary>`arrᵀ` - transpose,</summary>
198
-
199
- ```js
200
- ```
201
-
202
- </details>
203
- <details>
204
- <summary>`int 5` (typecast)</summary>
205
-
206
- ```js
207
- ```
208
-
209
- </details>
210
- <details>
211
- <summary>`$a` (param expansion)</summary>
212
-
213
- ```js
214
- ```
215
-
216
- </details>
217
- <details>
218
- <summary>`1 to 10 by 2`</summary>
219
-
220
- ```js
221
- ```
222
-
223
- </details>
224
- <details>
225
- <summary>`a if b else c`</summary>
226
-
227
- ```js
228
- ```
229
-
230
- </details>
231
- <details>
232
- <summary>`a, b in c`</summary>
233
-
234
- ```js
235
- ```
236
-
237
- </details>
238
- <details>
239
- <summary>`a.xyz` swizzles</summary>
240
-
241
- ```js
242
- ```
243
-
244
- </details>
245
- <details>
246
- <summary>vector operators</summary>
247
-
248
- ```js
249
- ```
250
-
251
- </details>
252
- <details>
253
- <summary>set operators</summary>
254
-
255
- ```js
256
- ```
257
-
258
- </details>
259
- <details>
260
- <summary>polynomial operators</summary>
261
-
262
- ```js
263
- ```
264
-
265
- </details>
142
+ * Keyed arrays <code>[a:1, b:2, c:3]</code>
143
+ * 7!` (factorial)
144
+ * `5s`, `5rem` (units)
145
+ * `?`, `?.`, `??`
146
+ * `arrᵀ` - transpose
147
+ * `int 5` (typecast)
148
+ * `$a` (param expansion)
149
+ * `1 to 10 by 2`
150
+ * `a if b else c`
151
+ * `a, b in c`
152
+ * `a.xyz` swizzles
153
+ * vector operators
154
+ * set operators
155
+ * polynomial operators
266
156
 
267
157
  like versions, units, hashes, urls, regexes etc
268
158
 
269
159
  2a as `2*a`
270
160
 
271
161
  string interpolation ` ${} 1 ${} `
272
-
273
- keyed arrays? [a:1, b:2, c:3]
274
-
275
- Examples: sonr, template-parts, neural-chunks
276
162
  -->
277
163
 
278
164
  ## Performance
279
165
 
280
- Subscript shows relatively good performance within other evaluators. Example expression:
166
+ Subscript shows good performance within other evaluators. Example expression:
281
167
 
282
168
  ```
283
169
  1 + (a * b / c % d) - 2.0 + -3e-3 * +4.4e4 / f.g[0] - i.j(+k == 1)(0)
@@ -286,11 +172,10 @@ Subscript shows relatively good performance within other evaluators. Example exp
286
172
  Parse 30k times:
287
173
 
288
174
  ```
289
- es-module-lexer: 50ms 🥇
290
- subscript: ~150 ms 🥈
175
+ subscript: ~150 ms 🥇
291
176
  justin: ~183 ms
292
- jsep: ~270 ms 🥉
293
- jexpr: ~297 ms
177
+ jsep: ~270 ms 🥈
178
+ jexpr: ~297 ms 🥉
294
179
  mr-parser: ~420 ms
295
180
  expr-eval: ~480 ms
296
181
  math-parser: ~570 ms
@@ -0,0 +1,11 @@
1
+ import { token, expr, err } from '../src/parse.js'
2
+ import { operator, compile, access } from '../src/compile.js'
3
+ import { CBRACK, CPAREN, PREC_ACCESS } from '../src/const.js'
4
+
5
+ // a[b]
6
+ token('[', PREC_ACCESS, a => a && ['[', a, expr(0, CBRACK) || err()])
7
+ operator('[', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx)[b(ctx)]))
8
+
9
+ // a.b
10
+ token('.', PREC_ACCESS, (a, b) => a && (b = expr(PREC_ACCESS)) && ['.', a, b])
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
package/feature/add.js ADDED
@@ -0,0 +1,22 @@
1
+
2
+ import { binary, unary } from '../src/parse.js'
3
+ import { PREC_ADD, PREC_PREFIX, PREC_ASSIGN } from '../src/const.js'
4
+ import { compile, access, operator } from '../src/compile.js'
5
+
6
+ unary('+', PREC_PREFIX), operator('+', (a, b) => !b && (a = compile(a), ctx => +a(ctx)))
7
+ unary('-', PREC_PREFIX), operator('-', (a, b) => !b && (a = compile(a), ctx => -a(ctx)))
8
+
9
+ binary('+', PREC_ADD), operator('+', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) + b(ctx)))
10
+ binary('-', PREC_ADD), operator('-', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) - b(ctx)))
11
+
12
+ binary('+=', PREC_ASSIGN, true)
13
+ operator('+=', (a, b) => (
14
+ b = compile(b),
15
+ access(a, (container, path, ctx) => container(ctx)[path(ctx)] += b(ctx))
16
+ ))
17
+
18
+ binary('-=', PREC_ASSIGN, true)
19
+ operator('-=', (a, b) => (
20
+ b = compile(b),
21
+ access(a, (container, path, ctx) => (container(ctx)[path(ctx)] -= b(ctx)))
22
+ ))
@@ -0,0 +1,11 @@
1
+ import { token, expr } from '../src/parse.js'
2
+ import { operator, compile } from '../src/compile.js'
3
+ import { CBRACK, PREC_TOKEN } from '../src/const.js'
4
+
5
+ // [a,b,c]
6
+ token('[', PREC_TOKEN, (a) => !a && ['[', expr(0, CBRACK) || ''])
7
+ operator('[', (a, b) => !b && (
8
+ !a ? () => [] : // []
9
+ a[0] === ',' ? (a = a.slice(1).map(compile), ctx => a.map(a => a(ctx))) : // [a,b,c]
10
+ (a = compile(a), ctx => [a(ctx)]) // [a]
11
+ ))
@@ -0,0 +1,11 @@
1
+ import { binary, err } from "../src/parse.js";
2
+ import { compile, operator, operators, access } from "../src/compile.js";
3
+ import { PREC_ASSIGN } from "../src/const.js";
4
+
5
+ // assignments
6
+ binary('=', PREC_ASSIGN, true)
7
+ operator('=', (a, b) => (
8
+ b = compile(b),
9
+ // a = x, ((a)) = x, a.b = x, a['b'] = x
10
+ access(a, (container, path, ctx) => container(ctx)[path(ctx)] = b(ctx))
11
+ ))
@@ -0,0 +1,15 @@
1
+ import { PREC_OR, PREC_AND, PREC_SHIFT, PREC_XOR, PREC_PREFIX } from "../src/const.js"
2
+ import { unary, binary } from "../src/parse.js"
3
+ import { operator, compile } from "../src/compile.js"
4
+
5
+ unary('~', PREC_PREFIX), operator('~', (a, b) => !b && (a = compile(a), ctx => ~a(ctx)))
6
+
7
+ binary('|', PREC_OR), operator('|', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) | b(ctx)))
8
+
9
+ binary('&', PREC_AND), operator('&', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) & b(ctx)))
10
+
11
+ binary('^', PREC_XOR), operator('^', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) ^ b(ctx)))
12
+
13
+ binary('>>', PREC_SHIFT), operator('>>', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) >> b(ctx)))
14
+
15
+ binary('<<', PREC_SHIFT), operator('<<', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) << b(ctx)))
@@ -0,0 +1,5 @@
1
+ import { token } from "../src/parse.js"
2
+ import { PREC_TOKEN } from "../src/const.js"
3
+
4
+ token('true', PREC_TOKEN, a => a ? err() : [, true])
5
+ token('false', PREC_TOKEN, a => a ? err() : [, false])
@@ -0,0 +1,15 @@
1
+ import { token, expr, err } from '../src/parse.js'
2
+ import { operator, compile, access } from '../src/compile.js'
3
+ import { CBRACK, CPAREN, PREC_ACCESS } from '../src/const.js'
4
+
5
+ // a(b,c,d), a()
6
+ token('(', PREC_ACCESS, (a, b) => a && (b = expr(0, CPAREN), b ? ['(', a, b] : ['(', a, '']))
7
+ operator('(', (a, b, args) => (
8
+ args = b == '' ? () => [] : // a()
9
+ b[0] === ',' ? (b = b.slice(1).map(compile), ctx => b.map(arg => arg(ctx))) : // a(b,c)
10
+ (b = compile(b), ctx => [b(ctx)]), // a(b)
11
+
12
+ // a(...args), a.b(...args), a[b](...args)
13
+ access(a, (obj, path, ctx) => obj(ctx)[path(ctx)](...args(ctx)), true)
14
+ )
15
+ )
@@ -0,0 +1,6 @@
1
+ import { SPACE, STAR, PREC_TOKEN } from "../src/const.js"
2
+ import { token, skip, cur, idx, expr } from "../src/parse.js"
3
+
4
+ // /**/, //
5
+ token('/*', PREC_TOKEN, (a, prec) => (skip(c => c !== STAR && cur.charCodeAt(idx + 1) !== 47), skip(2), a || expr(prec) || ['']))
6
+ token('//', PREC_TOKEN, (a, prec) => (skip(c => c >= SPACE), a || expr(prec) || ['']))
@@ -0,0 +1,11 @@
1
+ import { PREC_EQ } from '../src/const.js'
2
+ import { unary, binary } from "../src/parse.js"
3
+ import { operator, compile } from "../src/compile.js"
4
+
5
+
6
+ binary('==', PREC_EQ), operator('==', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) == b(ctx)))
7
+ binary('!=', PREC_EQ), operator('!=', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) != b(ctx)))
8
+ binary('>', PREC_EQ), operator('>', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) > b(ctx)))
9
+ binary('<', PREC_EQ), operator('<', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) < b(ctx)))
10
+ binary('>=', PREC_EQ), operator('>=', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) >= b(ctx)))
11
+ binary('<=', PREC_EQ), operator('<=', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) <= b(ctx)))
@@ -0,0 +1,12 @@
1
+ import { token, expr, err, nary } from '../src/parse.js'
2
+ import { compile, operator } from '../src/compile.js'
3
+ import { CPAREN, PREC_ACCESS, PREC_GROUP, PREC_SEQ } from '../src/const.js'
4
+
5
+ // (a,b,c), (a)
6
+ // FIXME: try raising group precedence (it causes conflict in ?. though)
7
+ token('(', PREC_ACCESS, (a) => (!a && ['()', expr(0, CPAREN) || err('Empty group')]))
8
+ operator('()', (a) => (compile(a)))
9
+
10
+ const last = (...args) => (args = args.map(compile), ctx => args.map(arg => arg(ctx)).pop())
11
+ nary(',', PREC_SEQ), operator(',', last)
12
+ nary(';', PREC_SEQ, true), operator(';', last)
package/feature/in.js ADDED
@@ -0,0 +1,6 @@
1
+ import { binary } from "../src/parse.js";
2
+ import { compile, operator } from "../src/compile.js";
3
+ import { PREC_COMP } from "../src/const.js";
4
+
5
+ // a in b
6
+ binary('in', PREC_COMP), operator('in', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) in b(ctx)))
@@ -0,0 +1,16 @@
1
+ import { token, expr } from "../src/parse.js"
2
+ import { operator, compile, access } from "../src/compile.js"
3
+ import { PREC_POSTFIX } from "../src/const.js"
4
+
5
+ let inc, dec
6
+ token('++', PREC_POSTFIX, a => a ? ['-', ['++', a], [, 1]] : ['++', expr(PREC_POSTFIX - 1)])
7
+ operator('++', inc = (a) =>
8
+ // ++a, ++((a)), ++a.b, ++a[b]
9
+ access(a, (obj, path, ctx) => ++obj(ctx)[path(ctx)])
10
+ )
11
+
12
+ token('--', PREC_POSTFIX, a => a ? ['+', ['--', a], [, 1]] : ['--', expr(PREC_POSTFIX - 1)])
13
+ operator('--', dec = (a) => (
14
+ // --a, --a.b, --a[b]
15
+ access(a, (obj, path, ctx) => --obj(ctx)[path(ctx)])
16
+ ))
@@ -0,0 +1,15 @@
1
+ import { PREC_LOR, PREC_LAND, PREC_PREFIX } from '../src/const.js';
2
+ import { unary, binary, nary } from "../src/parse.js"
3
+ import { operator, compile } from "../src/compile.js"
4
+
5
+ unary('!', PREC_PREFIX), operator('!', (a, b) => !b && (a = compile(a), ctx => !a(ctx)))
6
+
7
+ nary('||', PREC_LOR), operator('||', (...args) => (
8
+ args = args.map(compile),
9
+ ctx => { let arg, res; for (arg of args) if (res = arg(ctx)) return res; return res }
10
+ ))
11
+
12
+ nary('&&', PREC_LAND), operator('&&', (...args) => (
13
+ args = args.map(compile),
14
+ ctx => { let arg, res; for (arg of args) if (!(res = arg(ctx))) return res; return res }
15
+ ))
@@ -0,0 +1,25 @@
1
+ import { binary } from '../src/parse.js'
2
+ import { operator, compile, access } from '../src/compile.js'
3
+ import { PREC_MULT, PREC_ASSIGN } from '../src/const.js'
4
+
5
+ binary('*', PREC_MULT), operator('*', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) * b(ctx)))
6
+ binary('/', PREC_MULT), operator('/', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) / b(ctx)))
7
+ binary('%', PREC_MULT), operator('%', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) % b(ctx)))
8
+
9
+ binary('*=', PREC_ASSIGN, true)
10
+ operator('*=', (a, b) => (
11
+ b = compile(b),
12
+ access(a, (container, path, ctx) => container(ctx)[path(ctx)] *= b(ctx))
13
+ ))
14
+
15
+ binary('/=', PREC_ASSIGN, true)
16
+ operator('/=', (a, b) => (
17
+ b = compile(b),
18
+ access(a, (container, path, ctx) => container(ctx)[path(ctx)] /= b(ctx))
19
+ ))
20
+
21
+ binary('%=', PREC_ASSIGN, true)
22
+ operator('%=', (a, b) => (
23
+ b = compile(b),
24
+ access(a, (container, path, ctx) => container(ctx)[path(ctx)] %= b(ctx))
25
+ ))
@@ -0,0 +1,11 @@
1
+ import { lookup, skip, err } from "../src/parse.js"
2
+ import { PERIOD, _0, _E, _e, _9 } from "../src/const.js"
3
+
4
+ // parse number
5
+ const num = a => a ? err() : [, (a = +skip(c => c === PERIOD || (c >= _0 && c <= _9) || (c === _E || c === _e ? 2 : 0))) != a ? err() : a]
6
+
7
+ // .1
8
+ lookup[PERIOD] = a => (!a && num())
9
+
10
+ // 0-9
11
+ for (let i = _0; i <= _9; i++) lookup[i] = num
@@ -0,0 +1,17 @@
1
+ import { token, expr } from '../src/parse.js'
2
+ import { operator, compile } from '../src/compile.js'
3
+ import { CBRACE, PREC_SEQ, PREC_TOKEN } from '../src/const.js'
4
+
5
+
6
+ // {a:1, b:2, c:3}
7
+ token('{', PREC_TOKEN, a => !a && (['{', expr(0, CBRACE) || '']))
8
+ operator('{', (a, b) => (
9
+ !a ? ctx => ({}) : // {}
10
+ a[0] === ',' ? (a = a.slice(1).map(compile), ctx => Object.fromEntries(a.map(a => a(ctx)))) : // {a:1,b:2}
11
+ a[0] === ':' ? (a = compile(a), ctx => Object.fromEntries([a(ctx)])) : // {a:1}
12
+ (b = compile(a), ctx => ({ [a]: b(ctx) }))
13
+ ))
14
+
15
+ // FIXME: mb we don't need this seq raise
16
+ token(':', PREC_SEQ + 0.1, (a, b) => (b = expr(PREC_SEQ + 0.1) || err(), [':', a, b]))
17
+ operator(':', (a, b) => (b = compile(b), a = Array.isArray(a) ? compile(a) : (a => a).bind(0, a), ctx => [a(ctx), b(ctx)]))
package/feature/pow.js ADDED
@@ -0,0 +1,5 @@
1
+ import { binary } from "../src/parse.js";
2
+ import { compile, operator } from "../src/compile.js";
3
+ import { PREC_EXP } from "../src/const.js";
4
+
5
+ binary('**', PREC_EXP, true), operator('**', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) ** b(ctx)))
@@ -0,0 +1,19 @@
1
+ import { skip, err, cur, idx, lookup } from '../src/parse.js'
2
+ import { DQUOTE, QUOTE, BSLASH } from '../src/const.js'
3
+
4
+ const escape = { n: '\n', r: '\r', t: '\t', b: '\b', f: '\f', v: '\v' },
5
+ string = q => (qc, c, str = '') => {
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
+ }
12
+ skip() || err('Bad string')
13
+ return [, str]
14
+ }
15
+
16
+
17
+ // "' with /
18
+ lookup[DQUOTE] = string(DQUOTE)
19
+ lookup[QUOTE] = string(QUOTE)
@@ -0,0 +1,7 @@
1
+ import { token, expr } from '../src/parse.js'
2
+ import { operator, compile } from '../src/compile.js'
3
+ import { PREC_ASSIGN, COLON } from '../src/const.js'
4
+
5
+ // ?:
6
+ token('?', PREC_ASSIGN, (a, b, c) => a && (b = expr(PREC_ASSIGN, COLON)) && (c = expr(PREC_ASSIGN + 1), ['?', a, b, c]))
7
+ operator('?', (a, b, c) => (a = compile(a), b = compile(b), c = compile(c), ctx => a(ctx) ? b(ctx) : c(ctx)))