subscript 7.6.2 → 8.0.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,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
package/justin.js CHANGED
@@ -1,41 +1,31 @@
1
1
  // justin lang https://github.com/endojs/Jessie/issues/66
2
- import { skip, cur, idx, err, expr, lookup, token, binary } from './parse.js'
3
- import compile, { operator } from './compile.js'
4
- import subscript, { set } from './subscript.js'
5
-
6
- const PERIOD = 46, OPAREN = 40, CPAREN = 41, OBRACK = 91, CBRACK = 93, SPACE = 32, DQUOTE = 34, QUOTE = 39, _0 = 48, _9 = 57, BSLASH = 92,
7
- PREC_SEQ = 1, PREC_COND = 3, PREC_SOME = 4, PREC_EVERY = 5, PREC_OR = 6, PREC_XOR = 7, PREC_AND = 8,
8
- PREC_EQ = 9, PREC_COMP = 10, PREC_SHIFT = 11, PREC_SUM = 12, PREC_MULT = 13, PREC_EXP = 14, PREC_UNARY = 15, PREC_POSTFIX = 16, PREC_CALL = 18, PREC_GROUP = 19
9
-
10
- let escape = { n: '\n', r: '\r', t: '\t', b: '\b', f: '\f', v: '\v' },
11
- string = q => (qc, c, str = '') => {
12
- qc && err('Unexpected string') // must not follow another token
13
- skip() // first quote
14
- while (c = cur.charCodeAt(idx), c - q) {
15
- if (c === BSLASH) skip(), c = skip(), str += escape[c] || c
16
- else str += skip()
17
- }
18
- skip() || err('Bad string')
19
- return ['', str]
20
- }
2
+ import { skip, cur, idx, err, expr, lookup, token, binary, unary } from './src/parse.js'
3
+ import compile, { operator } from './src/compile.js'
4
+ import { CPAREN, COLON, PREC_ASSIGN, PREC_PREFIX, PREC_OR, PREC_ACCESS, PREC_COMP, PREC_EXP, PREC_GROUP } from './src/const.js'
5
+
6
+ // register subscript operators set
7
+ import subscript from './subscript.js'
8
+ import './feature/comment.js'
9
+ import './feature/pow.js'
10
+ import './feature/in.js'
11
+ import './feature/ternary.js'
12
+ import './feature/bool.js'
13
+ import './feature/array.js'
14
+ import './feature/object.js'
21
15
 
22
16
  // operators
23
17
  // set('===', PREC_EQ, (a, b) => a === b)
24
18
  // set('!==', PREC_EQ, (a, b) => a !== b)
25
- set('~', PREC_UNARY, (a) => ~a)
26
-
27
- // ?:
28
- token('?', PREC_COND, (a, b, c) => a && (b = expr(2, 58)) && (c = expr(3), ['?', a, b, c]))
29
- operator('?', (a, b, c) => (a = compile(a), b = compile(b), c = compile(c), ctx => a(ctx) ? b(ctx) : c(ctx)))
30
-
31
- set('??', PREC_OR, (a, b) => a ?? b)
19
+ binary('??', PREC_OR), operator('??', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx) ?? b(ctx)))
32
20
 
33
21
  // a?.[, a?.( - postfix operator
34
- token('?.', PREC_CALL, a => a && ['?.', a])
22
+ token('?.', PREC_ACCESS, a => a && ['?.', a])
23
+ // a ?.
35
24
  operator('?.', a => (a = compile(a), ctx => a(ctx) || (() => { })))
36
25
 
37
26
  // a?.b, a?.() - optional chain operator
38
- token('?.', PREC_CALL, (a, b) => a && (b = expr(PREC_CALL), !b?.map) && (b ? ['?.', a, b] : ['?.', a, b]))
27
+ token('?.', PREC_ACCESS, (a, b) => a && (b = expr(PREC_ACCESS), !b?.map) && ['?.', a, b])
28
+ // a ?. b
39
29
  operator('?.', (a, b) => b && (a = compile(a), ctx => a(ctx)?.[b]))
40
30
 
41
31
  // a?.x() - keep context, but watch out a?.()
@@ -43,59 +33,22 @@ operator('(', (a, b, container, args, path, optional) => (b != null) && (a[0] ==
43
33
  args = b == '' ? () => [] : // a()
44
34
  b[0] === ',' ? (b = b.slice(1).map(compile), ctx => b.map(a => a(ctx))) : // a(b,c)
45
35
  (b = compile(b), ctx => [b(ctx)]), // a(b)
36
+
46
37
  // a?.()
47
38
  !a[2] && (optional = true, a = a[1]),
39
+
48
40
  // a?.['x']?.()
49
- a[0] === '[' ? (path = compile(a[2])) : (path = ctx => a[2]),
41
+ a[0] === '[' ? (path = compile(a[2])) : (path = () => a[2]),
50
42
  (container = compile(a[1]), optional ?
51
43
  ctx => (container(ctx)?.[path(ctx)]?.(...args(ctx))) :
52
44
  ctx => (container(ctx)?.[path(ctx)](...args(ctx)))
53
45
  )
54
46
  ))
55
47
 
56
- // a in b
57
- set('in', PREC_COMP, (a, b) => a in b)
58
-
59
- // "' with /
60
- lookup[DQUOTE] = string(DQUOTE)
61
- lookup[QUOTE] = string(QUOTE)
62
-
63
- // /**/, //
64
- token('/*', 20, (a, prec) => (skip(c => c !== 42 && cur.charCodeAt(idx + 1) !== 47), skip(2), a || expr(prec) || ['']))
65
- token('//', 20, (a, prec) => (skip(c => c >= 32), a || expr(prec) || ['']))
66
-
67
48
  // literals
68
- token('null', 20, a => a ? err() : ['', null])
69
- token('true', 20, a => a ? err() : ['', true])
70
- token('false', 20, a => a ? err() : ['', false])
71
- // token('undefined', 20, a => a ? err() : ['', undefined])
72
- // token('NaN', 20, a => a ? err() : ['', NaN])
73
-
74
- set(';', -1, (...args) => args[args.length - 1])
75
-
76
- // right order
77
- // '**', (a,prec,b=expr(PREC_EXP-1)) => ctx=>a(ctx)**b(ctx), PREC_EXP,
78
- set('**', -PREC_EXP, (a, b) => a ** b)
79
-
80
- // [a,b,c]
81
- token('[', 20, (a) => !a && ['[', expr(0, 93) || ''])
82
- operator('[', (a, b) => !b && (
83
- !a ? () => [] : // []
84
- a[0] === ',' ? (a = a.slice(1).map(compile), ctx => a.map(a => a(ctx))) : // [a,b,c]
85
- (a = compile(a), ctx => [a(ctx)]) // [a]
86
- ))
87
-
88
- // {a:1, b:2, c:3}
89
- token('{', 20, a => !a && (['{', expr(0, 125) || '']))
90
- operator('{', (a, b) => (
91
- !a ? ctx => ({}) : // {}
92
- a[0] === ',' ? (a = a.slice(1).map(compile), ctx => Object.fromEntries(a.map(a => a(ctx)))) : // {a:1,b:2}
93
- a[0] === ':' ? (a = compile(a), ctx => Object.fromEntries([a(ctx)])) : // {a:1}
94
- (b = compile(a), ctx => ({ [a]: b(ctx) }))
95
- ))
96
-
97
- token(':', 1.1, (a, b) => (b = expr(1.1) || err(), [':', a, b]))
98
- operator(':', (a, b) => (b = compile(b), a = Array.isArray(a) ? compile(a) : (a => a).bind(0, a), ctx => [a(ctx), b(ctx)]))
49
+ token('null', 20, a => a ? err() : [, null])
50
+ // token('undefined', 20, a => a ? err() : [, undefined])
51
+ // token('NaN', 20, a => a ? err() : [, NaN])
99
52
 
100
53
  export default subscript
101
54
  export * from './subscript.js'
package/justin.min.js CHANGED
@@ -1 +1 @@
1
- let e,t,r=r=>(e=0,t=r,r=n(),t[e]?l():r||""),l=(r="Bad syntax",l=t[e],a=t.slice(0,e).split("\n"),n=a.pop())=>{let s=t.slice(e-108,e).split("\n").pop(),i=t.slice(e+1,e+108).split("\n").shift();throw EvalError(`${r} at ${a.length}:${n.length} \`${s+l+i}\``,"font-weight: bold")},a=(r=1,l=e,a)=>{if("number"==typeof r)e+=r;else for(;a=r(t.charCodeAt(e));)e+=a;return t.slice(l,e)},n=(t=0,a,n,s,i,p)=>{for(;(n=r.space())&&(i=((p=o[n])&&p(s,t))??(!s&&r.id()));)s=i;return a&&(n==a?e++:l()),s},s=e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e,i=r.space=r=>{for(;(r=t.charCodeAt(e))<=32;)e++;return r},p=r.id=e=>a(s),o=[],h=(r,l=32,a,n=r.charCodeAt(0),i=r.length,p=o[n],h=r.toUpperCase()!==r)=>o[n]=(n,o,c=e)=>o<l&&(i<2||t.substr(e,i)==r)&&(!h||!s(t.charCodeAt(e+i)))&&(e+=i,a(n,o))||(e=c,p?.(n,o)),c=(e,t,r=0)=>h(e,t,((l,a)=>l&&(a=n(t-r/2))&&[e,l,a])),f=(e,t,r)=>h(e,t,(l=>r?l&&[e,l]:!l&&(l=n(t-.5))&&[e,l])),g=(e,t,r)=>{h(e,t,((l,a)=>(l||r)&&((a=n(t))||r)&&((!l||l[0]!==e)&&(l=[e,l]),(a||r)&&l.push(a),l)))};const u=e=>Array.isArray(e)?d[e[0]](...e.slice(1)):t=>t?.[e],d={},m=(e,t,r=d[e])=>d[e]=(...e)=>t(...e)||r&&r(...e),A=e=>(e=r(e),t=>(e.call?e:e=u(e))(t)),b=(e,t,r)=>r.length?r.length>1?(c(e,Math.abs(t),t<0),m(e,((e,t)=>t&&(e=u(e),t=u(t),e.length||t.length?l=>r(e(l),t(l)):(e=r(e(),t()),()=>e))))):(f(e,t),m(e,((e,t)=>!t&&((e=u(e)).length?t=>r(e(t)):(e=r(e()),()=>e))))):(g(e,Math.abs(t),t<0),m(e,((...e)=>(e=e.map(u),t=>r(...e.map((e=>e(t)))))))),y=e=>e?l():["",(e=+a((e=>46===e||e>=48&&e<=57||(69===e||101===e?2:0))))!=e?l():e],C=(e,t,r,l)=>(h(e,t,(r=>r?["++"===e?"-":"+",[e,r],["",1]]:[e,n(t-1)])),m(e,l=(e,t)=>"("===e[0]?l(e[1]):"."===e[0]?(t=e[2],e=u(e[1]),l=>r(e(l),t)):"["===e[0]?([,e,t]=e,e=u(e),t=u(t),l=>r(e(l),t(l))):t=>r(t,e)));m("",(e=>()=>e)),o[34]=e=>e?l():["",(a()+a((e=>e-34?1:0))+(a()||l("Bad string"))).slice(1,-1)],o[46]=e=>!e&&y();for(let e=0;e<=9;e++)o[48+e]=y;b(",",1,((...e)=>e[e.length-1])),b("||",4,((...e)=>{let t,r=0;for(;!t&&r<e.length;)t=e[r++];return t})),b("&&",5,((...e)=>{let t=0,r=!0;for(;r&&t<e.length;)r=e[t++];return r})),c("=",10,!0),m("=",((e,t)=>{let r,a,n=u(t),s="string"==typeof e?(t,r)=>t[e]=r:"."===e[0]?(r=u(e[1]),a=e[2],(e,t)=>r(e)[a]=t):"["===e[0]?(r=u(e[1]),a=u(e[2]),(e,t)=>r(e)[a(e)]=t):l("Bad left value");return e=>s(e,n(e))})),b("+",12,((e,t)=>e+t)),b("-",12,((e,t)=>e-t)),b("*",13,((e,t)=>e*t)),b("/",13,((e,t)=>e/t)),b("%",13,((e,t)=>e%t)),b("|",6,((e,t)=>e|t)),b("&",8,((e,t)=>e&t)),b("^",7,((e,t)=>e^t)),b("==",9,((e,t)=>e==t)),b("!=",9,((e,t)=>e!=t)),b(">",10,((e,t)=>e>t)),b(">=",10,((e,t)=>e>=t)),b("<",10,((e,t)=>e<t)),b("<=",10,((e,t)=>e<=t)),b(">>",11,((e,t)=>e>>t)),b(">>>",11,((e,t)=>e>>>t)),b("<<",11,((e,t)=>e<<t)),b("+",15,(e=>+e)),b("-",15,(e=>-e)),b("!",15,(e=>!e)),C("++",15,((e,t)=>++e[t])),C("--",15,((e,t)=>--e[t])),h("[",18,(e=>e&&["[",e,n(0,93)||l()])),m("[",((e,t)=>t&&(e=u(e),t=u(t),r=>e(r)[t(r)]))),h(".",18,((e,t)=>e&&(t=n(18))&&[".",e,t])),m(".",((e,t)=>(e=u(e),t=t[0]?t:t[1],r=>e(r)[t]))),h("(",18,(e=>!e&&["(",n(0,41)||l()])),h("(",18,((e,t)=>e&&((t=n(0,41))?["(",e,t]:["(",e,""]))),m("(",((e,t,r,l,a)=>null==t?u(e):(a=""==t?()=>[]:","===t[0]?(t=t.slice(1).map(u),e=>t.map((t=>t(e)))):(t=u(t),e=>[t(e)]),"."===e[0]?(r=e[2],e=u(e[1]),t=>e(t)[r](...a(t))):"["===e[0]?(r=u(e[2]),e=u(e[1]),t=>e(t)[r(t)](...a(t))):(e=u(e),t=>e(t)(...a(t))))));let v={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},B=r=>(n,s,i="")=>{for(n&&l("Unexpected string"),a();(s=t.charCodeAt(e))-r;)92===s?(a(),s=a(),i+=v[s]||s):i+=a();return a()||l("Bad string"),["",i]};b("~",15,(e=>~e)),h("?",3,((e,t,r)=>e&&(t=n(2,58))&&["?",e,t,n(3)])),m("?",((e,t,r)=>(e=u(e),t=u(t),r=u(r),l=>e(l)?t(l):r(l)))),b("??",6,((e,t)=>e??t)),h("?.",18,(e=>e&&["?.",e])),m("?.",(e=>(e=u(e),t=>e(t)||(()=>{})))),h("?.",18,((e,t)=>e&&!(t=n(18))?.map&&["?.",e,t])),m("?.",((e,t)=>t&&(e=u(e),r=>e(r)?.[t]))),m("(",((e,t,r,l,a,n)=>null!=t&&"?."===e[0]&&(e[2]||Array.isArray(e[1]))&&(l=""==t?()=>[]:","===t[0]?(t=t.slice(1).map(u),e=>t.map((t=>t(e)))):(t=u(t),e=>[t(e)]),!e[2]&&(e=e[1]),a="["===e[0]?u(e[2]):t=>e[2],r=u(e[1]),e=>r(e)?.[a(e)]?.(...l(e))))),b("in",10,((e,t)=>e in t)),o[34]=B(34),o[39]=B(39),h("/*",20,((r,l)=>(a((r=>42!==r&&47!==t.charCodeAt(e+1))),a(2),r||n(l)||[""]))),h("//",20,((e,t)=>(a((e=>e>=32)),e||n(t)||[""]))),h("null",20,(e=>e?l():["",null])),h("true",20,(e=>e?l():["",!0])),h("false",20,(e=>e?l():["",!1])),b(";",-1,((...e)=>e[e.length-1])),b("**",-14,((e,t)=>e**t)),h("[",20,(e=>!e&&["[",n(0,93)||""])),m("[",((e,t)=>!t&&(e?","===e[0]?(e=e.slice(1).map(u),t=>e.map((e=>e(t)))):(e=u(e),t=>[e(t)]):()=>[]))),h("{",20,(e=>!e&&["{",n(0,125)||""])),m("{",((e,t)=>e?","===e[0]?(e=e.slice(1).map(u),t=>Object.fromEntries(e.map((e=>e(t))))):":"===e[0]?(e=u(e),t=>Object.fromEntries([e(t)])):(t=u(e),r=>({[e]:t(r)})):e=>({}))),h(":",1.1,((e,t)=>[":",e,n(1.1)||l()])),m(":",((e,t)=>(t=u(t),e=Array.isArray(e)?u(e):(e=>e).bind(0,e),r=>[e(r),t(r)])));export{c as binary,u as compile,t as cur,A as default,l as err,n as expr,p as id,e as idx,s as isId,o as lookup,g as nary,m as operator,d as operators,r as parse,b as set,a as skip,i as space,h as token,f as unary};
1
+ let r,e,t=t=>(r=0,e=t,t=i(),e[r]?a():t||""),a=(t="Bad syntax",a=e.slice(0,r).split("\n"),n=a.pop())=>{let i=e.slice(r-108,r).split("\n").pop(),s=e.slice(r,r+108).split("\n").shift();throw EvalError(`${t} at ${a.length}:${n.length} \`${r>=108?"…":""}${i}▶${s}\``,"font-weight: bold")},n=(t=1,a=r,n)=>{if("number"==typeof t)r+=t;else for(;n=t(e.charCodeAt(r));)r+=n;return e.slice(a,r)},i=(e=0,n,i,s,l,p)=>{for(;(i=t.space())&&(l=((p=o[i])&&p(s,e))??(!s&&t.id()));)s=l;return n&&(i==n?r++:a()),s},s=r=>r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||36==r||95==r||r>=192&&215!=r&&247!=r,l=t.id=r=>n(s),p=t.space=t=>{for(;(t=e.charCodeAt(r))<=32;)r++;return t},o=[],c=(t,a=32,n,i=t.charCodeAt(0),l=t.length,p=o[i],c=t.toUpperCase()!==t)=>o[i]=(i,o,f=r)=>o<a&&(l<2||e.substr(r,l)==t)&&(!c||!s(e.charCodeAt(r+l)))&&(r+=l,n(i,o))||(r=f,p?.(i,o)),f=(r,e,t=0)=>c(r,e,((a,n)=>a&&(n=i(e-t/2))&&[r,a,n])),d=(r,e,t)=>c(r,e,(a=>t?a&&[r,a]:!a&&(a=i(e-.5))&&[r,a])),u=(r,e,t)=>{c(r,e,((a,n)=>(a||t)&&((n=i(e))||t)&&((!a||a[0]!==r)&&(a=[r,a]),(n||t)&&a.push(n),a)))};const m=r=>Array.isArray(r)?r[0]?h[r[0]](...r.slice(1)):()=>r[1]:e=>e?.[r],h={},b=(r,e,t=h[r])=>h[r]=(...r)=>e(...r)||t&&t(...r),A=(r,e,t)=>"()"===r[0]?A(r[1],e,t):"string"==typeof r?e.bind(0,(r=>r),(()=>r)):"."===r[0]?e.bind(0,m(r[1]),(r=r[2],()=>r)):"["===r[0]?e.bind(0,m(r[1]),m(r[2])):t?(r=m(r),e.bind(0,(e=>[r(e)]),(()=>0))):()=>a("Bad left value"),y=r=>r?a():[,(r=+n((r=>46===r||r>=48&&r<=57||(69===r||101===r?2:0))))!=r?a():r];o[46]=r=>!r&&y();for(let r=48;r<=57;r++)o[r]=y;const g={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},C=t=>(i,s,l="")=>{for(i&&a("Unexpected string"),n();(s=e.charCodeAt(r))-t;)92===s?(n(),s=n(),l+=g[s]||s):l+=n();return n()||a("Bad string"),[,l]};o[34]=C(34),o[39]=C(39),c("(",17,((r,e)=>r&&((e=i(0,41))?["(",r,e]:["(",r,""]))),b("(",((r,e,t)=>(t=""==e?()=>[]:","===e[0]?(e=e.slice(1).map(m),r=>e.map((e=>e(r)))):(e=m(e),r=>[e(r)]),A(r,((r,e,a)=>r(a)[e(a)](...t(a))),!0)))),c("[",17,(r=>r&&["[",r,i(0,93)||a()])),b("[",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)[e(t)]))),c(".",17,((r,e)=>r&&(e=i(17))&&[".",r,e])),b(".",((r,e)=>(r=m(r),e=e[0]?e:e[1],t=>r(t)[e]))),c("(",17,(r=>!r&&["()",i(0,41)||a("Empty group")])),b("()",(r=>m(r)));const $=(...r)=>(r=r.map(m),e=>r.map((r=>r(e))).pop());u(",",1),b(",",$),u(";",1,!0),b(";",$),f("*",12),b("*",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)*e(t)))),f("/",12),b("/",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)/e(t)))),f("%",12),b("%",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)%e(t)))),f("*=",2,!0),b("*=",((r,e)=>(e=m(e),A(r,((r,t,a)=>r(a)[t(a)]*=e(a)))))),f("/=",2,!0),b("/=",((r,e)=>(e=m(e),A(r,((r,t,a)=>r(a)[t(a)]/=e(a)))))),f("%=",2,!0),b("%=",((r,e)=>(e=m(e),A(r,((r,t,a)=>r(a)[t(a)]%=e(a)))))),d("+",14),b("+",((r,e)=>!e&&(r=m(r),e=>+r(e)))),d("-",14),b("-",((r,e)=>!e&&(r=m(r),e=>-r(e)))),f("+",11),b("+",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)+e(t)))),f("-",11),b("-",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)-e(t)))),f("+=",2,!0),b("+=",((r,e)=>(e=m(e),A(r,((r,t,a)=>r(a)[t(a)]+=e(a)))))),f("-=",2,!0),b("-=",((r,e)=>(e=m(e),A(r,((r,t,a)=>r(a)[t(a)]-=e(a)))))),c("++",15,(r=>r?["-",["++",r],[,1]]:["++",i(14)])),b("++",(r=>A(r,((r,e,t)=>++r(t)[e(t)])))),c("--",15,(r=>r?["+",["--",r],[,1]]:["--",i(14)])),b("--",(r=>A(r,((r,e,t)=>--r(t)[e(t)])))),d("~",14),b("~",((r,e)=>!e&&(r=m(r),e=>~r(e)))),f("|",5),b("|",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)|e(t)))),f("&",7),b("&",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)&e(t)))),f("^",6),b("^",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)^e(t)))),f(">>",10),b(">>",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)>>e(t)))),f("<<",10),b("<<",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)<<e(t)))),f("==",8),b("==",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)==e(t)))),f("!=",8),b("!=",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)!=e(t)))),f(">",8),b(">",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)>e(t)))),f("<",8),b("<",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)<e(t)))),f(">=",8),b(">=",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)>=e(t)))),f("<=",8),b("<=",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)<=e(t)))),d("!",14),b("!",((r,e)=>!e&&(r=m(r),e=>!r(e)))),u("||",3),b("||",((...r)=>(r=r.map(m),e=>{let t,a;for(t of r)if(a=t(e))return a;return a}))),u("&&",4),b("&&",((...r)=>(r=r.map(m),e=>{let t,a;for(t of r)if(!(a=t(e)))return a;return a}))),f("=",2,!0),b("=",((r,e)=>(e=m(e),A(r,((r,t,a)=>r(a)[t(a)]=e(a))))));var v=r=>m(t(r));c("/*",20,((t,a)=>(n((t=>42!==t&&47!==e.charCodeAt(r+1))),n(2),t||i(a)||[""]))),c("//",20,((r,e)=>(n((r=>r>=32)),r||i(e)||[""]))),f("**",13,!0),b("**",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)**e(t)))),f("in",9),b("in",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)in e(t)))),c("?",2,((r,e,t)=>r&&(e=i(2,58))&&["?",r,e,i(3)])),b("?",((r,e,t)=>(r=m(r),e=m(e),t=m(t),a=>r(a)?e(a):t(a)))),c("true",20,(r=>r?err():[,!0])),c("false",20,(r=>r?err():[,!1])),c("[",20,(r=>!r&&["[",i(0,93)||""])),b("[",((r,e)=>!e&&(r?","===r[0]?(r=r.slice(1).map(m),e=>r.map((r=>r(e)))):(r=m(r),e=>[r(e)]):()=>[]))),c("{",20,(r=>!r&&["{",i(0,125)||""])),b("{",((r,e)=>r?","===r[0]?(r=r.slice(1).map(m),e=>Object.fromEntries(r.map((r=>r(e))))):":"===r[0]?(r=m(r),e=>Object.fromEntries([r(e)])):(e=m(r),t=>({[r]:e(t)})):r=>({}))),c(":",1.1,((r,e)=>[":",r,i(1.1)||err()])),b(":",((r,e)=>(e=m(e),r=Array.isArray(r)?m(r):(r=>r).bind(0,r),t=>[r(t),e(t)]))),f("??",5),b("??",((r,e)=>e&&(r=m(r),e=m(e),t=>r(t)??e(t)))),c("?.",17,(r=>r&&["?.",r])),b("?.",(r=>(r=m(r),e=>r(e)||(()=>{})))),c("?.",17,((r,e)=>r&&!(e=i(17))?.map&&["?.",r,e])),b("?.",((r,e)=>e&&(r=m(r),t=>r(t)?.[e]))),b("(",((r,e,t,a,n,i)=>null!=e&&"?."===r[0]&&(r[2]||Array.isArray(r[1]))&&(a=""==e?()=>[]:","===e[0]?(e=e.slice(1).map(m),r=>e.map((e=>e(r)))):(e=m(e),r=>[e(r)]),!r[2]&&(r=r[1]),n="["===r[0]?m(r[2]):()=>r[2],t=m(r[1]),r=>t(r)?.[n(r)]?.(...a(r))))),c("null",20,(r=>r?a():[,null]));export{A as access,f as binary,m as compile,e as cur,v as default,a as err,i as expr,l as id,r as idx,s as isId,o as lookup,u as nary,b as operator,h as operators,t as parse,n as skip,p as space,c as token,d as unary};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "subscript",
3
- "version": "7.6.2",
3
+ "version": "8.0.0",
4
4
  "description": "Fast and tiny expression evaluator with minimal syntax.",
5
5
  "main": "subscript.js",
6
6
  "module": "subscript.js",
package/subscript.js CHANGED
@@ -1,127 +1,22 @@
1
- import parse, { lookup, nary, binary, unary, token, skip, err, expr } from './parse.js'
2
- import compile, { operator } from './compile.js'
3
-
4
- const OPAREN = 40, CPAREN = 41, OBRACK = 91, CBRACK = 93, SPACE = 32, DQUOTE = 34, PERIOD = 46, _0 = 48, _9 = 57,
5
- PREC_SEQ = 1, PREC_SOME = 4, PREC_EVERY = 5, PREC_OR = 6, PREC_XOR = 7, PREC_AND = 8,
6
- PREC_EQ = 9, PREC_COMP = 10, PREC_SHIFT = 11, PREC_SUM = 12, PREC_MULT = 13, PREC_UNARY = 15, PREC_POSTFIX = 16, PREC_CALL = 18
7
-
8
- const subscript = s => (s = parse(s), ctx => (s.call ? s : (s = compile(s)))(ctx)),
9
-
10
- // set any operator
11
- // right assoc is indicated by negative precedence (meaning go from right to left)
12
- set = (op, prec, fn) => (
13
- !fn.length ? (
14
- nary(op, Math.abs(prec), prec < 0),
15
- operator(op, (...args) => (args = args.map(compile), ctx => fn(...args.map(arg => arg(ctx)))))
16
- ) :
17
- fn.length > 1 ? (
18
- binary(op, Math.abs(prec), prec < 0),
19
- operator(op,
20
- (a, b) => b && (a = compile(a), b = compile(b), !a.length && !b.length ? (a = fn(a(), b()), () => a) : ctx => fn(a(ctx), b(ctx)))
21
- )
22
- ) :
23
- (
24
- unary(op, prec),
25
- operator(op, (a, b) => !b && (a = compile(a), !a.length ? (a = fn(a()), () => a) : ctx => fn(a(ctx))))
26
- )
27
- ),
28
-
29
- num = a => a ? err() : ['', (a = +skip(c => c === PERIOD || (c >= _0 && c <= _9) || (c === 69 || c === 101 ? 2 : 0))) != a ? err() : a],
30
-
31
- // create increment-assign pair from fn
32
- inc = (op, prec, fn, ev) => (
33
- token(op, prec, a => a ? [op === '++' ? '-' : '+', [op, a], ['', 1]] : [op, expr(prec - 1)]), // ++a → [++, a], a++ → [-,[++,a],1]
34
- operator(op, ev = (a, b) => (
35
- a[0] === '(' ? ev(a[1]) : // ++(((a)))
36
- a[0] === '.' ? (b = a[2], a = compile(a[1]), ctx => fn(a(ctx), b)) : // ++a.b
37
- a[0] === '[' ? ([, a, b] = a, a = compile(a), b = compile(b), ctx => fn(a(ctx), b(ctx))) : // ++a[b]
38
- (ctx => fn(ctx, a)) // ++a
39
- ))
40
- )
41
-
42
- // literals
43
- // null operator returns first value (needed for direct literals)
44
- operator('', v => () => v)
45
-
46
- // "a"
47
- lookup[DQUOTE] = (a) => (a ? err() : ['', (skip() + skip(c => c - DQUOTE ? 1 : 0) + (skip() || err('Bad string'))).slice(1, -1)])
48
-
49
- // .1
50
- lookup[PERIOD] = a => (!a && num())
51
-
52
- // 0-9
53
- for (let i = 0; i <= 9; i++) lookup[_0 + i] = num
54
-
55
- // sequences
56
- set(',', PREC_SEQ, (...args) => args[args.length - 1])
57
- set('||', PREC_SOME, (...args) => { let i = 0, v; for (; !v && i < args.length;) v = args[i++]; return v })
58
- set('&&', PREC_EVERY, (...args) => { let i = 0, v = true; for (; v && i < args.length;) v = args[i++]; return v })
59
-
60
- // assignment
61
- binary('=', 10, true)
62
- operator('=', (a, b) => {
63
- let calc = compile(b), container, path,
64
- set = typeof a === 'string' ? (ctx, v) => ctx[a] = v :
65
- a[0] === '.' ? (container = compile(a[1]), path = a[2], (ctx, v) => container(ctx)[path] = v) :
66
- a[0] === '[' ? (container = compile(a[1]), path = compile(a[2]), (ctx, v) => container(ctx)[path(ctx)] = v) :
67
- err('Bad left value');
68
- return ctx => set(ctx, calc(ctx))
69
- })
70
-
71
- // binaries
72
- set('+', PREC_SUM, (a, b) => a + b)
73
- set('-', PREC_SUM, (a, b) => a - b)
74
- set('*', PREC_MULT, (a, b) => a * b)
75
- set('/', PREC_MULT, (a, b) => a / b)
76
- set('%', PREC_MULT, (a, b) => a % b)
77
- set('|', PREC_OR, (a, b) => a | b)
78
- set('&', PREC_AND, (a, b) => a & b)
79
- set('^', PREC_XOR, (a, b) => a ^ b)
80
- set('==', PREC_EQ, (a, b) => a == b)
81
- set('!=', PREC_EQ, (a, b) => a != b)
82
- set('>', PREC_COMP, (a, b) => a > b)
83
- set('>=', PREC_COMP, (a, b) => a >= b)
84
- set('<', PREC_COMP, (a, b) => a < b)
85
- set('<=', PREC_COMP, (a, b) => a <= b)
86
- set('>>', PREC_SHIFT, (a, b) => a >> b)
87
- set('>>>', PREC_SHIFT, (a, b) => a >>> b)
88
- set('<<', PREC_SHIFT, (a, b) => a << b)
89
-
90
- // unaries
91
- set('+', PREC_UNARY, a => +a)
92
- set('-', PREC_UNARY, a => -a)
93
- set('!', PREC_UNARY, a => !a)
94
-
95
- // increments
96
- inc('++', PREC_UNARY, (a, b) => ++a[b])
97
- inc('--', PREC_UNARY, (a, b) => --a[b])
98
-
99
- // a[b]
100
- token('[', PREC_CALL, a => a && ['[', a, expr(0, CBRACK) || err()])
101
- operator('[', (a, b) => b && (a = compile(a), b = compile(b), ctx => a(ctx)[b(ctx)]))
102
-
103
- // a.b
104
- token('.', PREC_CALL, (a, b) => a && (b = expr(PREC_CALL)) && ['.', a, b])
105
- operator('.', (a, b) => (a = compile(a), b = !b[0] ? b[1] : b, ctx => a(ctx)[b])) // a.true, a.1 → needs to work fine
106
-
107
- // (a,b,c), (a)
108
- token('(', PREC_CALL, a => !a && ['(', expr(0, CPAREN) || err()])
109
-
110
- // a(b,c,d), a()
111
- token('(', PREC_CALL, (a, b) => a && (b = expr(0, CPAREN), b ? ['(', a, b] : ['(', a, '']))
112
- operator('(', (a, b, path, container, args) => b == null ? (compile(a, b)) : (
113
- args = b == '' ? () => [] : // a()
114
- b[0] === ',' ? (b = b.slice(1).map(compile), ctx => b.map(arg => arg(ctx))) : // a(b,c)
115
- (b = compile(b), ctx => [b(ctx)]), // a(b)
116
-
117
- a[0] === '.' ? (path = a[2], a = compile(a[1]), ctx => a(ctx)[path](...args(ctx))) : // a.b(...args)
118
- a[0] === '[' ? (path = compile(a[2]), a = compile(a[1]), ctx => a(ctx)[path(ctx)](...args(ctx))) : // a[b](...args)
119
- (a = compile(a), ctx => a(ctx)(...args(ctx))) // a(...args)
120
- )
121
- )
122
-
123
-
124
- export default subscript
125
- export { set }
126
- export * from './parse.js'
127
- export * from './compile.js'
1
+ /**
2
+ * Subscript dialect includes common operators / primitives for all languages
3
+ */
4
+ import './feature/number.js'
5
+ import './feature/string.js'
6
+ import './feature/call.js'
7
+ import './feature/access.js'
8
+ import './feature/group.js'
9
+ import './feature/mult.js'
10
+ import './feature/add.js'
11
+ import './feature/increment.js'
12
+ import './feature/bitwise.js'
13
+ import './feature/compare.js'
14
+ import './feature/logic.js'
15
+ import './feature/assign.js'
16
+ import compile from './src/compile.js'
17
+ import parse from './src/parse.js'
18
+
19
+ export * from './src/parse.js'
20
+ export * from './src/compile.js'
21
+
22
+ export default s => compile(parse(s))
package/subscript.min.js CHANGED
@@ -1 +1 @@
1
- let e,t,l=l=>(e=0,t=l,l=n(),t[e]?r():l||""),r=(l="Bad syntax",r=t[e],a=t.slice(0,e).split("\n"),n=a.pop())=>{let s=t.slice(e-108,e).split("\n").pop(),h=t.slice(e+1,e+108).split("\n").shift();throw EvalError(`${l} at ${a.length}:${n.length} \`${s+r+h}\``,"font-weight: bold")},a=(l=1,r=e,a)=>{if("number"==typeof l)e+=l;else for(;a=l(t.charCodeAt(e));)e+=a;return t.slice(r,e)},n=(t=0,a,n,s,h,o)=>{for(;(n=l.space())&&(h=((o=p[n])&&o(s,t))??(!s&&l.id()));)s=h;return a&&(n==a?e++:r()),s},s=e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e,h=l.space=l=>{for(;(l=t.charCodeAt(e))<=32;)e++;return l},o=l.id=e=>a(s),p=[],i=(l,r=32,a,n=l.charCodeAt(0),h=l.length,o=p[n],i=l.toUpperCase()!==l)=>p[n]=(n,p,c=e)=>p<r&&(h<2||t.substr(e,h)==l)&&(!i||!s(t.charCodeAt(e+h)))&&(e+=h,a(n,p))||(e=c,o?.(n,p)),c=(e,t,l=0)=>i(e,t,((r,a)=>r&&(a=n(t-l/2))&&[e,r,a])),g=(e,t,l)=>i(e,t,(r=>l?r&&[e,r]:!r&&(r=n(t-.5))&&[e,r])),f=(e,t,l)=>{i(e,t,((r,a)=>(r||l)&&((a=n(t))||l)&&((!r||r[0]!==e)&&(r=[e,r]),(a||l)&&r.push(a),r)))};const u=e=>Array.isArray(e)?d[e[0]](...e.slice(1)):t=>t?.[e],d={},A=(e,t,l=d[e])=>d[e]=(...e)=>t(...e)||l&&l(...e),b=e=>(e=l(e),t=>(e.call?e:e=u(e))(t)),m=(e,t,l)=>l.length?l.length>1?(c(e,Math.abs(t),t<0),A(e,((e,t)=>t&&(e=u(e),t=u(t),e.length||t.length?r=>l(e(r),t(r)):(e=l(e(),t()),()=>e))))):(g(e,t),A(e,((e,t)=>!t&&((e=u(e)).length?t=>l(e(t)):(e=l(e()),()=>e))))):(f(e,Math.abs(t),t<0),A(e,((...e)=>(e=e.map(u),t=>l(...e.map((e=>e(t)))))))),y=e=>e?r():["",(e=+a((e=>46===e||e>=48&&e<=57||(69===e||101===e?2:0))))!=e?r():e],C=(e,t,l,r)=>(i(e,t,(l=>l?["++"===e?"-":"+",[e,l],["",1]]:[e,n(t-1)])),A(e,r=(e,t)=>"("===e[0]?r(e[1]):"."===e[0]?(t=e[2],e=u(e[1]),r=>l(e(r),t)):"["===e[0]?([,e,t]=e,e=u(e),t=u(t),r=>l(e(r),t(r))):t=>l(t,e)));A("",(e=>()=>e)),p[34]=e=>e?r():["",(a()+a((e=>e-34?1:0))+(a()||r("Bad string"))).slice(1,-1)],p[46]=e=>!e&&y();for(let e=0;e<=9;e++)p[48+e]=y;m(",",1,((...e)=>e[e.length-1])),m("||",4,((...e)=>{let t,l=0;for(;!t&&l<e.length;)t=e[l++];return t})),m("&&",5,((...e)=>{let t=0,l=!0;for(;l&&t<e.length;)l=e[t++];return l})),c("=",10,!0),A("=",((e,t)=>{let l,a,n=u(t),s="string"==typeof e?(t,l)=>t[e]=l:"."===e[0]?(l=u(e[1]),a=e[2],(e,t)=>l(e)[a]=t):"["===e[0]?(l=u(e[1]),a=u(e[2]),(e,t)=>l(e)[a(e)]=t):r("Bad left value");return e=>s(e,n(e))})),m("+",12,((e,t)=>e+t)),m("-",12,((e,t)=>e-t)),m("*",13,((e,t)=>e*t)),m("/",13,((e,t)=>e/t)),m("%",13,((e,t)=>e%t)),m("|",6,((e,t)=>e|t)),m("&",8,((e,t)=>e&t)),m("^",7,((e,t)=>e^t)),m("==",9,((e,t)=>e==t)),m("!=",9,((e,t)=>e!=t)),m(">",10,((e,t)=>e>t)),m(">=",10,((e,t)=>e>=t)),m("<",10,((e,t)=>e<t)),m("<=",10,((e,t)=>e<=t)),m(">>",11,((e,t)=>e>>t)),m(">>>",11,((e,t)=>e>>>t)),m("<<",11,((e,t)=>e<<t)),m("+",15,(e=>+e)),m("-",15,(e=>-e)),m("!",15,(e=>!e)),C("++",15,((e,t)=>++e[t])),C("--",15,((e,t)=>--e[t])),i("[",18,(e=>e&&["[",e,n(0,93)||r()])),A("[",((e,t)=>t&&(e=u(e),t=u(t),l=>e(l)[t(l)]))),i(".",18,((e,t)=>e&&(t=n(18))&&[".",e,t])),A(".",((e,t)=>(e=u(e),t=t[0]?t:t[1],l=>e(l)[t]))),i("(",18,(e=>!e&&["(",n(0,41)||r()])),i("(",18,((e,t)=>e&&((t=n(0,41))?["(",e,t]:["(",e,""]))),A("(",((e,t,l,r,a)=>null==t?u(e):(a=""==t?()=>[]:","===t[0]?(t=t.slice(1).map(u),e=>t.map((t=>t(e)))):(t=u(t),e=>[t(e)]),"."===e[0]?(l=e[2],e=u(e[1]),t=>e(t)[l](...a(t))):"["===e[0]?(l=u(e[2]),e=u(e[1]),t=>e(t)[l(t)](...a(t))):(e=u(e),t=>e(t)(...a(t))))));export{c as binary,u as compile,t as cur,b as default,r as err,n as expr,o as id,e as idx,s as isId,p as lookup,f as nary,A as operator,d as operators,l as parse,m as set,a as skip,h as space,i as token,g as unary};
1
+ let t,r,e=e=>(t=0,r=e,e=s(),r[t]?n():e||""),n=(e="Bad syntax",n=r.slice(0,t).split("\n"),o=n.pop())=>{let s=r.slice(t-108,t).split("\n").pop(),a=r.slice(t,t+108).split("\n").shift();throw EvalError(`${e} at ${n.length}:${o.length} \`${t>=108?"…":""}${s}▶${a}\``,"font-weight: bold")},o=(e=1,n=t,o)=>{if("number"==typeof e)t+=e;else for(;o=e(r.charCodeAt(t));)t+=o;return r.slice(n,t)},s=(r=0,o,s,a,p,i)=>{for(;(s=e.space())&&(p=((i=l[s])&&i(a,r))??(!a&&e.id()));)a=p;return o&&(s==o?t++:n()),a},a=t=>t>=48&&t<=57||t>=65&&t<=90||t>=97&&t<=122||36==t||95==t||t>=192&&215!=t&&247!=t,p=e.id=t=>o(a),i=e.space=e=>{for(;(e=r.charCodeAt(t))<=32;)t++;return e},l=[],f=(e,n=32,o,s=e.charCodeAt(0),p=e.length,i=l[s],f=e.toUpperCase()!==e)=>l[s]=(s,l,c=t)=>l<n&&(p<2||r.substr(t,p)==e)&&(!f||!a(r.charCodeAt(t+p)))&&(t+=p,o(s,l))||(t=c,i?.(s,l)),c=(t,r,e=0)=>f(t,r,((n,o)=>n&&(o=s(r-e/2))&&[t,n,o])),d=(t,r,e)=>f(t,r,(n=>e?n&&[t,n]:!n&&(n=s(r-.5))&&[t,n])),u=(t,r,e)=>{f(t,r,((n,o)=>(n||e)&&((o=s(r))||e)&&((!n||n[0]!==t)&&(n=[t,n]),(o||e)&&n.push(o),n)))};const h=t=>t?n():[,(t=+o((t=>46===t||t>=48&&t<=57||(69===t||101===t?2:0))))!=t?n():t];l[46]=t=>!t&&h();for(let t=48;t<=57;t++)l[t]=h;const b={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},g=e=>(s,a,p="")=>{for(s&&n("Unexpected string"),o();(a=r.charCodeAt(t))-e;)92===a?(o(),a=o(),p+=b[a]||a):p+=o();return o()||n("Bad string"),[,p]};l[34]=g(34),l[39]=g(39);const m=t=>Array.isArray(t)?t[0]?A[t[0]](...t.slice(1)):()=>t[1]:r=>r?.[t],A={},y=(t,r,e=A[t])=>A[t]=(...t)=>r(...t)||e&&e(...t),C=(t,r,e)=>"()"===t[0]?C(t[1],r,e):"string"==typeof t?r.bind(0,(t=>t),(()=>t)):"."===t[0]?r.bind(0,m(t[1]),(t=t[2],()=>t)):"["===t[0]?r.bind(0,m(t[1]),m(t[2])):e?(t=m(t),r.bind(0,(r=>[t(r)]),(()=>0))):()=>n("Bad left value");f("(",17,((t,r)=>t&&((r=s(0,41))?["(",t,r]:["(",t,""]))),y("(",((t,r,e)=>(e=""==r?()=>[]:","===r[0]?(r=r.slice(1).map(m),t=>r.map((r=>r(t)))):(r=m(r),t=>[r(t)]),C(t,((t,r,n)=>t(n)[r(n)](...e(n))),!0)))),f("[",17,(t=>t&&["[",t,s(0,93)||n()])),y("[",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)[r(e)]))),f(".",17,((t,r)=>t&&(r=s(17))&&[".",t,r])),y(".",((t,r)=>(t=m(t),r=r[0]?r:r[1],e=>t(e)[r]))),f("(",17,(t=>!t&&["()",s(0,41)||n("Empty group")])),y("()",(t=>m(t)));const $=(...t)=>(t=t.map(m),r=>t.map((t=>t(r))).pop());u(",",1),y(",",$),u(";",1,!0),y(";",$),c("*",12),y("*",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)*r(e)))),c("/",12),y("/",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)/r(e)))),c("%",12),y("%",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)%r(e)))),c("*=",2,!0),y("*=",((t,r)=>(r=m(r),C(t,((t,e,n)=>t(n)[e(n)]*=r(n)))))),c("/=",2,!0),y("/=",((t,r)=>(r=m(r),C(t,((t,e,n)=>t(n)[e(n)]/=r(n)))))),c("%=",2,!0),y("%=",((t,r)=>(r=m(r),C(t,((t,e,n)=>t(n)[e(n)]%=r(n)))))),d("+",14),y("+",((t,r)=>!r&&(t=m(t),r=>+t(r)))),d("-",14),y("-",((t,r)=>!r&&(t=m(t),r=>-t(r)))),c("+",11),y("+",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)+r(e)))),c("-",11),y("-",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)-r(e)))),c("+=",2,!0),y("+=",((t,r)=>(r=m(r),C(t,((t,e,n)=>t(n)[e(n)]+=r(n)))))),c("-=",2,!0),y("-=",((t,r)=>(r=m(r),C(t,((t,e,n)=>t(n)[e(n)]-=r(n)))))),f("++",15,(t=>t?["-",["++",t],[,1]]:["++",s(14)])),y("++",(t=>C(t,((t,r,e)=>++t(e)[r(e)])))),f("--",15,(t=>t?["+",["--",t],[,1]]:["--",s(14)])),y("--",(t=>C(t,((t,r,e)=>--t(e)[r(e)])))),d("~",14),y("~",((t,r)=>!r&&(t=m(t),r=>~t(r)))),c("|",5),y("|",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)|r(e)))),c("&",7),y("&",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)&r(e)))),c("^",6),y("^",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)^r(e)))),c(">>",10),y(">>",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)>>r(e)))),c("<<",10),y("<<",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)<<r(e)))),c("==",8),y("==",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)==r(e)))),c("!=",8),y("!=",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)!=r(e)))),c(">",8),y(">",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)>r(e)))),c("<",8),y("<",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)<r(e)))),c(">=",8),y(">=",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)>=r(e)))),c("<=",8),y("<=",((t,r)=>r&&(t=m(t),r=m(r),e=>t(e)<=r(e)))),d("!",14),y("!",((t,r)=>!r&&(t=m(t),r=>!t(r)))),u("||",3),y("||",((...t)=>(t=t.map(m),r=>{let e,n;for(e of t)if(n=e(r))return n;return n}))),u("&&",4),y("&&",((...t)=>(t=t.map(m),r=>{let e,n;for(e of t)if(!(n=e(r)))return n;return n}))),c("=",2,!0),y("=",((t,r)=>(r=m(r),C(t,((t,e,n)=>t(n)[e(n)]=r(n))))));var v=t=>m(e(t));export{C as access,c as binary,m as compile,r as cur,v as default,n as err,s as expr,p as id,t as idx,a as isId,l as lookup,u as nary,y as operator,A as operators,e as parse,o as skip,i as space,f as token,d as unary};
package/compile.js DELETED
@@ -1,8 +0,0 @@
1
- // build optimized evaluator for the tree
2
- export const compile = (node) => !Array.isArray(node) ? ctx => ctx?.[node] : operators[node[0]](...node.slice(1)),
3
-
4
- operators = {},
5
-
6
- operator = (op, fn, prev = operators[op]) => (operators[op] = (...args) => fn(...args) || prev && prev(...args))
7
-
8
- export default compile
package/parse.js DELETED
@@ -1,94 +0,0 @@
1
- const SPACE = 32
2
-
3
- // current string, index and collected ids
4
- export let idx, cur,
5
-
6
- // no handling tagged literals since easily done on user side with cache, if needed
7
- parse = s => (idx = 0, cur = s, s = expr(), cur[idx] ? err() : s || ''),
8
-
9
- err = (msg = 'Bad syntax',
10
- frag = cur[idx],
11
- lines = cur.slice(0, idx).split('\n'),
12
- last = lines.pop()
13
- ) => {
14
- let before = cur.slice(idx - 108, idx).split('\n').pop()
15
- let after = cur.slice(idx + 1, idx + 108).split('\n').shift()
16
- throw EvalError(`${msg} at ${lines.length}:${last.length} \`${before + frag + after}\``, 'font-weight: bold')
17
- },
18
-
19
- skip = (is = 1, from = idx, l) => {
20
- if (typeof is == 'number') idx += is
21
- else while (l = is(cur.charCodeAt(idx))) idx += l
22
- return cur.slice(from, idx)
23
- },
24
-
25
- // a + b - c
26
- expr = (prec = 0, end, cc, token, newNode, fn) => {
27
- // chunk/token parser
28
- while (
29
- (cc = parse.space()) && // till not end
30
- // FIXME: extra work is happening here, when lookup bails out due to lower precedence -
31
- // it makes extra `space` call for parent exprs on the same character to check precedence again
32
- (newNode =
33
- ((fn = lookup[cc]) && fn(token, prec)) ?? // if operator with higher precedence isn't found
34
- (!token && parse.id()) // parse literal or quit. token seqs are forbidden: `a b`, `a "b"`, `1.32 a`
35
- )
36
- ) token = newNode;
37
-
38
- // check end character
39
- // FIXME: can't show "Unclosed paren", because can be unknown operator within group as well
40
- if (end) cc == end ? idx++ : err()
41
-
42
- return token
43
- },
44
-
45
- isId = c =>
46
- (c >= 48 && c <= 57) || // 0..9
47
- (c >= 65 && c <= 90) || // A...Z
48
- (c >= 97 && c <= 122) || // a...z
49
- c == 36 || c == 95 || // $, _,
50
- (c >= 192 && c != 215 && c != 247), // any non-ASCII
51
-
52
- // skip space chars, return first non-space character
53
- space = parse.space = cc => { while ((cc = cur.charCodeAt(idx)) <= SPACE) idx++; return cc },
54
-
55
- id = parse.id = n => skip(isId),
56
-
57
- // operator/token lookup table
58
- // lookup[0] is id parser to let configs redefine it
59
- lookup = [],
60
-
61
-
62
- // create operator checker/mapper (see examples)
63
- token = (
64
- op,
65
- prec = SPACE,
66
- map,
67
- c = op.charCodeAt(0),
68
- l = op.length,
69
- prev = lookup[c],
70
- word = op.toUpperCase() !== op // make sure word boundary comes after word operator
71
- ) => lookup[c] = (a, curPrec, from = idx) =>
72
- (curPrec < prec && (l < 2 || cur.substr(idx, l) == op) && (!word || !isId(cur.charCodeAt(idx + l))) && (idx += l, map(a, curPrec))) ||
73
- (idx = from, prev?.(a, curPrec)),
74
-
75
- // right assoc is indicated by negative precedence (meaning go from right to left)
76
- binary = (op, prec, right = 0) => token(op, prec, (a, b) => a && (b = expr(prec - right / 2)) && [op, a, b]),
77
-
78
- // post indicates postfix rather than prefix operator
79
- unary = (op, prec, post) => token(op, prec, a => post ? (a && [op, a]) : (!a && (a = expr(prec - .5)) && [op, a])),
80
-
81
- // skips means ,,, ;;; are allowed
82
- nary = (op, prec, skips) => {
83
- token(op, prec, (a, b) => (
84
- (a || skips) && // if lhs exists or we're ok to skip
85
- (b = expr(prec), b || skips) && // either rhs exists or we're ok to skip rhs
86
- (
87
- (!a || a[0] !== op) && (a = [op, a]), // if beginning of sequence - init node
88
- (b || skips) && a.push(b),
89
- a
90
- ))
91
- )
92
- }
93
-
94
- export default parse