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 +100 -215
- package/feature/access.js +11 -0
- package/feature/add.js +22 -0
- package/feature/array.js +11 -0
- package/feature/assign.js +11 -0
- package/feature/bitwise.js +15 -0
- package/feature/bool.js +5 -0
- package/feature/call.js +15 -0
- package/feature/comment.js +6 -0
- package/feature/compare.js +11 -0
- package/feature/group.js +12 -0
- package/feature/in.js +6 -0
- package/feature/increment.js +16 -0
- package/feature/logic.js +15 -0
- package/feature/mult.js +25 -0
- package/feature/number.js +11 -0
- package/feature/object.js +17 -0
- package/feature/pow.js +5 -0
- package/feature/string.js +19 -0
- package/feature/ternary.js +7 -0
- package/justin.js +24 -71
- package/justin.min.js +1 -1
- package/package.json +5 -5
- package/src/compile.d.ts +17 -0
- package/src/compile.js +26 -0
- package/src/const.js +40 -0
- package/src/parse.d.ts +19 -0
- package/{parse.js → src/parse.js} +6 -6
- package/subscript.js +22 -127
- package/subscript.min.js +1 -1
- package/compile.js +0 -8
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
|
|
@@ -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
|
+
))
|
package/feature/array.js
ADDED
|
@@ -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)))
|
package/feature/bool.js
ADDED
package/feature/call.js
ADDED
|
@@ -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)))
|
package/feature/group.js
ADDED
|
@@ -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
|
+
))
|
package/feature/logic.js
ADDED
|
@@ -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
|
+
))
|
package/feature/mult.js
ADDED
|
@@ -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,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)))
|