subscript 7.6.1 → 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 +100 -215
- package/justin.js +24 -71
- package/justin.min.js +1 -1
- package/package.json +1 -1
- package/subscript.js +22 -127
- package/subscript.min.js +1 -1
- package/compile.js +0 -8
- package/parse.js +0 -94
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
|
|
3
|
+
> _Subscript_ is fast, tiny & extensible expression evaluator / microlanguage.
|
|
4
4
|
|
|
5
|
-
|
|
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 (
|
|
37
|
-
* preprocessors (
|
|
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
|
-
|
|
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
|
|
30
|
+
## Operators
|
|
43
31
|
|
|
44
|
-
|
|
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
|
-
* `
|
|
47
|
-
* `a
|
|
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
|
|
53
|
-
* `a
|
|
54
|
-
* `a
|
|
55
|
-
* `a
|
|
56
|
-
* `a
|
|
57
|
-
* `
|
|
58
|
-
* `
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
+
|
|
71
|
-
+
|
|
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 '
|
|
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
|
-
|
|
69
|
+
## Parse / Compile
|
|
94
70
|
|
|
95
|
-
|
|
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
|
|
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
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
76
|
+
// parse expression
|
|
77
|
+
let tree = parse('a.b + c - 1')
|
|
78
|
+
tree // ['-', ['+', ['.', 'a', 'b'], 'c'], [,1]]
|
|
111
79
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
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', [
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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('?.',
|
|
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('?.',
|
|
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 =
|
|
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() : [
|
|
69
|
-
token('
|
|
70
|
-
token('
|
|
71
|
-
// token('undefined', 20, a => a ? err() : ['', undefined])
|
|
72
|
-
// token('NaN', 20, a => a ? err() : ['', NaN])
|
|
73
|
-
|
|
74
|
-
set(';', -20, (...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
|
|
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
package/subscript.js
CHANGED
|
@@ -1,127 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 LHS');
|
|
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 t,e
|
|
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
|