subscript 5.5.2 → 6.0.3
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 +104 -83
- package/index.js +71 -0
- package/justin.js +61 -200
- package/justin.min.js +1 -1
- package/package.json +16 -7
- package/subscript.js +67 -109
- package/subscript.min.js +1 -1
- package/evaluate.js +0 -27
- package/parse.js +0 -67
package/README.md
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
# <img alt="subscript" src="/subscript2.svg" height=
|
|
2
|
-
<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>
|
|
3
|
-
<a href="http://npmjs.org/subscript"><img src="https://img.shields.io/npm/v/subscript?color=indianred"/></a>
|
|
4
|
-
<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="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>
|
|
5
2
|
|
|
6
|
-
_Subscript_ is micro-language with common syntax subset of C++, JS, Java, Python, Go, Rust
|
|
3
|
+
_Subscript_ is micro-language with common syntax subset of C++, JS, Java, Python, Go, Rust etc.<br/>
|
|
7
4
|
|
|
8
|
-
*
|
|
9
|
-
* Any
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* Standard conventional syntax
|
|
6
|
+
* Any fragment can be copy-pasted to any target language
|
|
7
|
+
* Tiny size <sub><a href="https://bundlephobia.com/package/subscript@6.0.0"><img alt="npm bundle size" src="https://img.shields.io/bundlephobia/minzip/subscript/latest?color=brightgreen&label=gzip"/></a></sub>
|
|
8
|
+
* :rocket: Fast [performance](#performance)
|
|
12
9
|
* Configurable & extensible
|
|
13
|
-
* Trivial to use
|
|
10
|
+
* Trivial to use
|
|
14
11
|
|
|
15
12
|
```js
|
|
16
|
-
import
|
|
17
|
-
let fn =
|
|
13
|
+
import script from 'subscript.js'
|
|
14
|
+
let fn = script`a.b + c(d - 1)`
|
|
18
15
|
fn({ a: { b:1 }, c: x => x * 2, d: 3 }) // 5
|
|
19
16
|
```
|
|
20
17
|
|
|
@@ -29,115 +26,114 @@ _Subscript_ is designed to be useful for:
|
|
|
29
26
|
* sandboxes, playgrounds, safe eval
|
|
30
27
|
* custom DSL
|
|
31
28
|
|
|
32
|
-
[
|
|
33
|
-
_Subscript_ has [2.4kb](https://npmfs.com/package/subscript/5.5.0/subscript.min.js) footprint vs [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 _jsep_ test coverage and better performance.
|
|
29
|
+
_Subscript_ has [2kb](https://npmfs.com/package/subscript/6.0.0/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.
|
|
34
30
|
|
|
35
31
|
|
|
36
|
-
##
|
|
32
|
+
## Design
|
|
37
33
|
|
|
38
|
-
|
|
34
|
+
Default operators are (same as JS precedence order):
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
+
|
|
36
|
+
* `( a, b, c )`
|
|
37
|
+
* `a.b`, `a[b]`, `a(b, c)`
|
|
38
|
+
* `a++`, `a--` unary postfix
|
|
39
|
+
* `!a`, `+a`, `-a`, `++a`, `--a` unary prefix
|
|
40
|
+
* `a * b`, `a / b`, `a % b`
|
|
41
|
+
* `a + b`, `a - b`
|
|
42
|
+
* `a << b`, `a >> b`, `a >>> b`
|
|
43
|
+
* `a < b`, `a <= b`, `a > b`, `a >= b`
|
|
44
|
+
* `a == b`, `a != b`
|
|
45
|
+
* `a & b`
|
|
46
|
+
* `a ^ b`
|
|
47
|
+
* `a | b`
|
|
48
|
+
* `a && b`
|
|
49
|
+
* `a || b`
|
|
50
|
+
|
|
51
|
+
Default literals:
|
|
52
|
+
|
|
53
|
+
* `"abc"` strings
|
|
54
|
+
* `1.2e+3` numbers
|
|
55
|
+
|
|
56
|
+
Everything else can be extended via `parse.set(token, precedence, operator)` for unary or binary operators (detected by number of arguments in `operator`), or via `parse.set(token, parse, precedence)` for custom tokens.
|
|
46
57
|
|
|
47
58
|
```js
|
|
48
|
-
import
|
|
59
|
+
import script from 'subscript.js'
|
|
49
60
|
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
// add ~ unary operator with precedence 15
|
|
62
|
+
script.set('~', 15, a => ~a)
|
|
52
63
|
|
|
53
|
-
|
|
64
|
+
// add === binary operator
|
|
65
|
+
script.set('===', 9, (a, b) => a===b)
|
|
54
66
|
|
|
55
|
-
|
|
67
|
+
// add literals
|
|
68
|
+
script.set('true', a => ()=>true)
|
|
69
|
+
script.set('false', a => ()=>false)
|
|
56
70
|
|
|
57
|
-
|
|
71
|
+
script`true === false`() // false
|
|
72
|
+
```
|
|
58
73
|
|
|
59
|
-
|
|
60
|
-
* `! + - ++ --` unary prefix
|
|
61
|
-
* `* / %`
|
|
62
|
-
* `+ -`
|
|
63
|
-
* `<< >> >>>`
|
|
64
|
-
* `< <= > >=`
|
|
65
|
-
* `== !=`
|
|
66
|
-
* `&`
|
|
67
|
-
* `^`
|
|
68
|
-
* `|`
|
|
69
|
-
* `&&`
|
|
70
|
-
* `||`
|
|
74
|
+
See [subscript.js](subscript.js) or [justin.js](./justin.js) for examples.
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
<!--
|
|
77
|
+
Operators can be extended via .
|
|
73
78
|
|
|
74
79
|
```js
|
|
75
|
-
import
|
|
76
|
-
|
|
77
|
-
parse.operator('=>', 10) // precedence=10, type=default (0 - binary, 1 - postfix, -1 - prefix)
|
|
80
|
+
import script from 'subscript.js'
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
evaluate.operator('|', ( a, b ) => a.pipe(b))
|
|
82
|
+
script.set('|', 10, ( a, b ) => a.pipe(b))
|
|
81
83
|
|
|
82
|
-
let
|
|
84
|
+
let evaluate = script(`
|
|
83
85
|
interval(350)
|
|
84
86
|
| take(25)
|
|
85
87
|
| map(gaussian)
|
|
86
|
-
|
|
|
88
|
+
| "•".repeat(Math.floor(it * 65)))
|
|
87
89
|
`)
|
|
88
|
-
evaluate(
|
|
90
|
+
evaluate({ Math, map, take, interval, gaussian })
|
|
89
91
|
```
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
Default tokens include:
|
|
94
|
-
|
|
95
|
-
* `"abc"` strings
|
|
96
|
-
* `1.2e+3` floats
|
|
97
|
-
* `name` identifiers
|
|
98
|
-
|
|
99
|
-
Tokens are extensible via `parse.token` list, can be added support of _literals_, _regexes_, _strings_, _numbers_ and others.
|
|
93
|
+
Literals are extensible by providing custom parser to `lookup`, can be added support of _booleans_, function calls, prop chains, groups, _regexes_, _strings_, _numbers_ and any other constructs.
|
|
100
94
|
|
|
101
95
|
```js
|
|
102
|
-
import
|
|
103
|
-
import evaluate from 'subscript/evaluate.js'
|
|
96
|
+
import script from 'subscript.js'
|
|
104
97
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
evaluate(parse(`this.x`)) // 1
|
|
98
|
+
script.literal.unshift(c => skip('this') && {x:1})
|
|
99
|
+
script`this.x`() // 1
|
|
108
100
|
```
|
|
109
101
|
|
|
110
|
-
###
|
|
102
|
+
### Identifiers
|
|
103
|
+
|
|
104
|
+
Identifiers include
|
|
111
105
|
|
|
112
|
-
|
|
106
|
+
### Spaces/comments
|
|
113
107
|
|
|
108
|
+
Comments can be added via extending `parse.space`.
|
|
109
|
+
-->
|
|
114
110
|
|
|
115
111
|
## Justin
|
|
116
112
|
|
|
117
|
-
_Justin_
|
|
118
|
-
|
|
113
|
+
_Justin_ is minimal JS subset − JSON with JS expressions (see original [thread](https://github.com/endojs/Jessie/issues/66)).<br/>
|
|
114
|
+
|
|
115
|
+
It extends _subscript_ with:
|
|
119
116
|
|
|
120
117
|
+ `===`, `!==` operators
|
|
121
|
-
+ `**`
|
|
122
|
-
+ `~`
|
|
118
|
+
+ `**` exponentiation operator (right-assoc)
|
|
119
|
+
+ `~` bit inversion operator
|
|
123
120
|
+ `'` strings
|
|
124
121
|
+ `?:` ternary operator
|
|
122
|
+
+ `?.` optional chain operator
|
|
125
123
|
+ `[...]` Array literal
|
|
126
124
|
+ `{...}` Object literal
|
|
127
|
-
+ `in` binary
|
|
125
|
+
+ `in` binary
|
|
128
126
|
+ `;` expression separator
|
|
129
|
-
+ unary word operators
|
|
130
127
|
+ `//`, `/* */` comments
|
|
131
128
|
+ `true`, `false`, `null`, `undefined` literals
|
|
132
|
-
<!-- + `?` chaining operator -->
|
|
133
129
|
<!-- + `...x` unary operator -->
|
|
134
130
|
<!-- + strings interpolation -->
|
|
135
131
|
|
|
136
132
|
```js
|
|
137
|
-
import
|
|
133
|
+
import jstin from 'subscript/justin.js'
|
|
138
134
|
|
|
139
|
-
let xy =
|
|
140
|
-
|
|
135
|
+
let xy = jstin('{ x: 1, "y": 2+2 }["x"]')
|
|
136
|
+
xy() // 1
|
|
141
137
|
```
|
|
142
138
|
|
|
143
139
|
<!--
|
|
@@ -282,19 +278,40 @@ Examples: sonr, template-parts, neural-chunks
|
|
|
282
278
|
Subscript shows relatively good performance within other evaluators:
|
|
283
279
|
|
|
284
280
|
```
|
|
285
|
-
|
|
286
|
-
|
|
281
|
+
1 + (a * b / c % d) - 2.0 + -3e-3 * +4.4e4 / f.g[0] - i.j(+k == 1)(0)
|
|
282
|
+
```
|
|
287
283
|
|
|
288
|
-
|
|
289
|
-
|
|
284
|
+
Parse 30k times:
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
subscript: ~170 ms 🥇
|
|
288
|
+
justin: ~183 ms 🥈
|
|
289
|
+
jsep: ~270 ms 🥉
|
|
290
|
+
mr-parser: ~420 ms
|
|
290
291
|
expr-eval: ~480 ms
|
|
291
|
-
|
|
292
|
-
|
|
292
|
+
math-parser: ~570 ms
|
|
293
|
+
math-expression-evaluator: ~900ms
|
|
294
|
+
jexl: ~1056 ms
|
|
295
|
+
mathjs: ~1200 ms
|
|
296
|
+
new Function: ~1154 ms
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Eval 30k times:
|
|
300
|
+
```
|
|
301
|
+
new Function: ~7 ms 🥇
|
|
302
|
+
subscript: ~17 ms 🥈
|
|
303
|
+
justin: ~17 ms 🥈
|
|
304
|
+
jsep (expression-eval): ~30 ms 🥉
|
|
305
|
+
math-expression-evaluator: ~50ms
|
|
306
|
+
expr-eval: ~72 ms
|
|
307
|
+
jexl: ~110 ms
|
|
308
|
+
mathjs: ~119 ms
|
|
309
|
+
mr-parser: -
|
|
310
|
+
math-parser: -
|
|
293
311
|
```
|
|
294
312
|
|
|
295
|
-
##
|
|
313
|
+
## Alternatives
|
|
296
314
|
|
|
297
|
-
* [Jessie](https://github.com/endojs/Jessie) − Minimal JS subset.
|
|
298
315
|
* [jexl](https://github.com/TomFrost/Jexl)
|
|
299
316
|
* [mozjexl](https://github.com/mozilla/mozjexl)
|
|
300
317
|
* [expr-eval](https://github.com/silentmatt/expr-eval)
|
|
@@ -302,5 +319,9 @@ new Function: ~1400 ms
|
|
|
302
319
|
* [jsep](https://github.com/EricSmekens/jsep)
|
|
303
320
|
* [string-math](https://github.com/devrafalko/string-math)
|
|
304
321
|
* [nerdamer](https://github.com/jiggzson/nerdamer)
|
|
322
|
+
* [math-codegen](https://github.com/mauriciopoppe/math-codegen)
|
|
323
|
+
* [math-parser](https://www.npmjs.com/package/math-parser)
|
|
324
|
+
* [math.js](https://mathjs.org/docs/expressions/parsing.html)
|
|
325
|
+
* [Jessie](https://github.com/endojs/Jessie)
|
|
305
326
|
|
|
306
327
|
<p align=center>🕉</p>
|
package/index.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const SPACE=32
|
|
2
|
+
|
|
3
|
+
// current string & index
|
|
4
|
+
export let idx, cur,
|
|
5
|
+
|
|
6
|
+
parse = (s, ...fields) => !(cur=s.raw ? String.raw(s,...fields) : s, idx=0, s=expr()) || cur[idx] ? err('Unexpected end') : ctx=>s(ctx||{}),
|
|
7
|
+
|
|
8
|
+
isId = c =>
|
|
9
|
+
(c >= 48 && c <= 57) || // 0..9
|
|
10
|
+
(c >= 65 && c <= 90) || // A...Z
|
|
11
|
+
(c >= 97 && c <= 122) || // a...z
|
|
12
|
+
c == 36 || c == 95 || // $, _,
|
|
13
|
+
(c >= 192 && c != 215 && c != 247), // any non-ASCII
|
|
14
|
+
|
|
15
|
+
err = (msg='Unexpected token',c=cur[idx]) => { throw SyntaxError(msg + ' `' + c + '` at ' + idx) },
|
|
16
|
+
|
|
17
|
+
skip = (is=1, from=idx, l) => {
|
|
18
|
+
if (typeof is == 'number') idx += is
|
|
19
|
+
else while (is(cur.charCodeAt(idx))) idx++
|
|
20
|
+
return cur.slice(from, idx)
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
// a + b - c
|
|
24
|
+
expr = (prec=0, end, cc, token, newNode, fn) => {
|
|
25
|
+
// chunk/token parser
|
|
26
|
+
while (
|
|
27
|
+
( cc=space() ) && // till not end
|
|
28
|
+
// FIXME: extra work is happening here, when lookup bails out due to lower precedence -
|
|
29
|
+
// it makes extra `space` call for parent exprs on the same character to check precedence again
|
|
30
|
+
( newNode =
|
|
31
|
+
(fn=lookup[cc]) && fn(token, prec) || // if operator with higher precedence isn't found
|
|
32
|
+
(!token && id()) // parse literal or quit. token seqs are forbidden: `a b`, `a "b"`, `1.32 a`
|
|
33
|
+
)
|
|
34
|
+
) token = newNode;
|
|
35
|
+
|
|
36
|
+
// check end character
|
|
37
|
+
if (end) cc==end?idx++:err('Missing', String.fromCharCode(end))
|
|
38
|
+
|
|
39
|
+
return token
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
// skip space chars, return first non-space character
|
|
43
|
+
space = cc => { while ((cc = cur.charCodeAt(idx)) <= SPACE) idx++; return cc },
|
|
44
|
+
|
|
45
|
+
// variable identifier
|
|
46
|
+
id = (name=skip(isId), fn) => name ? (fn=ctx => ctx[name], fn.id=()=>name, fn) : 0,
|
|
47
|
+
|
|
48
|
+
// operator/token lookup table
|
|
49
|
+
lookup = [],
|
|
50
|
+
|
|
51
|
+
// create operator checker/mapper (see examples)
|
|
52
|
+
set = parse.set = (
|
|
53
|
+
op,
|
|
54
|
+
opPrec, fn=SPACE, // if opPrec & fn come in reverse order - consider them raw parse fn case, still precedence possible
|
|
55
|
+
c=op.charCodeAt(0),
|
|
56
|
+
l=op.length,
|
|
57
|
+
prev=lookup[c],
|
|
58
|
+
arity=fn.length || ([fn,opPrec]=[opPrec,fn], 0),
|
|
59
|
+
word=op.toUpperCase()!==op, // make sure word boundary comes after word operator
|
|
60
|
+
map=
|
|
61
|
+
// binary
|
|
62
|
+
arity>1 ? (a,b) => a && (b=expr(opPrec)) && (
|
|
63
|
+
!a.length && !b.length ? (a=fn(a(),b()), ()=>a) : // static pre-eval like `"a"+"b"`
|
|
64
|
+
ctx => fn(a(ctx),b(ctx))
|
|
65
|
+
) :
|
|
66
|
+
// unary prefix (0 args)
|
|
67
|
+
arity ? a => !a && (a=expr(opPrec-1)) && (ctx => fn(a(ctx))) :
|
|
68
|
+
fn // custom parser
|
|
69
|
+
) =>
|
|
70
|
+
// FIXME: find out if that's possible to globalize precision and instead of passing it to map, just provide global
|
|
71
|
+
lookup[c] = (a, curPrec, from=idx) => curPrec<opPrec && (l<2||cur.substr(idx,l)==op) && (!word||!isId(cur.charCodeAt(idx+l))) && (idx+=l, map(a, curPrec)) || (idx=from, prev&&prev(a, curPrec))
|
package/justin.js
CHANGED
|
@@ -1,212 +1,73 @@
|
|
|
1
1
|
// justin lang https://github.com/endojs/Jessie/issues/66
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import {parse, set, lookup, skip, cur, idx, err, expr, isId, space} from './index.js'
|
|
3
|
+
import './subscript.js'
|
|
4
|
+
|
|
5
|
+
const PERIOD=46, OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, QUOTE=39, _0=48, _9=57, BSLASH=92,
|
|
6
|
+
PREC_SEQ=1, PREC_COND=3, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
|
|
7
|
+
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
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
let u, list, op, prec, fn,
|
|
11
|
+
escape = {n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', v:'\v'},
|
|
12
|
+
string = q => (qc, c, str='') => {
|
|
13
|
+
qc&&err('Unexpected string') // must not follow another token
|
|
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
|
+
return skip()||err('Bad string'), () => str
|
|
19
|
+
}
|
|
4
20
|
|
|
5
|
-
|
|
21
|
+
// operators
|
|
22
|
+
for (list=[
|
|
23
|
+
// "' with /
|
|
24
|
+
'"', string(DQUOTE),,
|
|
25
|
+
"'", string(QUOTE),,
|
|
6
26
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
27
|
+
// /**/, //
|
|
28
|
+
'/*', (a, prec) => (skip(c => c !== 42 && cur.charCodeAt(idx+1) !== 47), skip(2), a||expr(prec)),,
|
|
29
|
+
'//', (a, prec) => (skip(c => c >= 32), a||expr(prec)),,
|
|
10
30
|
|
|
31
|
+
// literals
|
|
32
|
+
'null', a => a ? err('Unexpected literal') : ()=>null,,
|
|
33
|
+
'true', a => a ? err('Unexpected literal') : ()=>true,,
|
|
34
|
+
'false', a => a ? err('Unexpected literal') : ()=>false,,
|
|
35
|
+
'undefined', a => a ? err('Unexpected literal') : ()=>undefined,,
|
|
11
36
|
|
|
12
|
-
|
|
13
|
-
parse.token.push(
|
|
14
|
-
// 1.2e+3, .5 - fast & small version, but consumes corrupted nums as well
|
|
15
|
-
(number) => (
|
|
16
|
-
(number = skip(c => (c > 47 && c < 58) || c == PERIOD)) && (
|
|
17
|
-
(code() == 69 || code() == 101) && (number += skip(2) + skip(c => c >= 48 && c <= 57)),
|
|
18
|
-
isNaN(number = new Number(number)) ? err('Bad number') : number
|
|
19
|
-
)
|
|
20
|
-
),
|
|
37
|
+
';', a => expr()||(()=>{}),,
|
|
21
38
|
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
while (c=code(), c-q) {
|
|
27
|
-
if (c === 92) skip(), str += escape[char()] || char(); else str+=char()
|
|
28
|
-
skip()
|
|
29
|
-
}
|
|
30
|
-
return skip(), '@' + str
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
// id
|
|
34
|
-
c => skip(c =>
|
|
35
|
-
(c >= 48 && c <= 57) || // 0..9
|
|
36
|
-
(c >= 65 && c <= 90) || // A...Z
|
|
37
|
-
(c >= 97 && c <= 122) || // a...z
|
|
38
|
-
c == 36 || c == 95 || // $, _,
|
|
39
|
-
(c >= 192 && c != 215 && c != 247) // any non-ASCII
|
|
40
|
-
)
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
const escape = {n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', v:'\v'}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// /**/, //
|
|
47
|
-
parse.space = cc => {
|
|
48
|
-
while (cc = code(), cc <= 32 || cc === 47) {
|
|
49
|
-
if (cc <= 32) skip()
|
|
50
|
-
else if (cc === 47)
|
|
51
|
-
// /**/
|
|
52
|
-
if (code(1) === 42) skip(2), skip(c => c !== 42 && code(1) !== 47), skip(2)
|
|
53
|
-
// //
|
|
54
|
-
else if (code(1) === 47) skip(2), skip(c => c >= 32)
|
|
55
|
-
else break
|
|
56
|
-
}
|
|
57
|
-
return cc
|
|
58
|
-
}
|
|
39
|
+
// operators
|
|
40
|
+
'===', PREC_EQ, (a,b) => a===b,
|
|
41
|
+
'!==', PREC_EQ, (a,b) => a!==b,
|
|
42
|
+
'~', PREC_UNARY, (a) => ~a,
|
|
59
43
|
|
|
44
|
+
// right order
|
|
45
|
+
'**', (a,prec,b=expr(PREC_EXP-1)) => ctx=>a(ctx)**b(ctx), PREC_EXP,
|
|
46
|
+
|
|
47
|
+
// ?:
|
|
48
|
+
':', 3.1, (a,b) => [a,b],
|
|
49
|
+
'?', 3, (a,b) => a ? b[2] : b[1],
|
|
50
|
+
|
|
51
|
+
// a?.[, a?.( - postfix operator
|
|
52
|
+
'?.', a => a && (ctx => a(ctx)||(()=>{})),,//(a) => a||(()=>{}),
|
|
53
|
+
// a?.b - optional chain operator
|
|
54
|
+
'?.', (a,id) => (space(), id=skip(isId)) && (ctx => a(ctx)?.[id]),,
|
|
55
|
+
|
|
56
|
+
'in', PREC_COMP, (a,b) => a in b,
|
|
60
57
|
|
|
61
|
-
// operators
|
|
62
|
-
const addOps = (add, stride=2, list) => {
|
|
63
|
-
for (let i = 0; i < list.length; i+=stride) add(list[i], list[i+1], list[i+2])
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
addOps(parse.operator, 3, [
|
|
67
|
-
// subscript ones
|
|
68
|
-
',', PREC_SEQ,,
|
|
69
|
-
|
|
70
|
-
'|', PREC_OR,,
|
|
71
|
-
'||', PREC_SOME,,
|
|
72
|
-
|
|
73
|
-
'&', PREC_AND,,
|
|
74
|
-
'&&', PREC_EVERY,,
|
|
75
|
-
|
|
76
|
-
'^', PREC_XOR,,
|
|
77
|
-
|
|
78
|
-
// ==, !=
|
|
79
|
-
'==', PREC_EQ,,
|
|
80
|
-
'!=', PREC_EQ,,
|
|
81
|
-
|
|
82
|
-
// > >= >> >>>, < <= <<
|
|
83
|
-
'>', PREC_COMP,,
|
|
84
|
-
'>=', PREC_COMP,,
|
|
85
|
-
'>>', PREC_SHIFT,,
|
|
86
|
-
'>>>', PREC_SHIFT,,
|
|
87
|
-
'<', PREC_COMP,,
|
|
88
|
-
'<=', PREC_COMP,,
|
|
89
|
-
'<<', PREC_SHIFT,,
|
|
90
|
-
|
|
91
|
-
// + ++ - --
|
|
92
|
-
'+', PREC_SUM,,
|
|
93
|
-
'+', PREC_UNARY, -1,
|
|
94
|
-
'++', PREC_UNARY, -1,
|
|
95
|
-
'++', PREC_POSTFIX, +1,
|
|
96
|
-
'-', PREC_SUM,,
|
|
97
|
-
'-', PREC_UNARY, -1,
|
|
98
|
-
'--', PREC_UNARY, -1,
|
|
99
|
-
'--', PREC_POSTFIX, +1,
|
|
100
|
-
|
|
101
|
-
// ! ~
|
|
102
|
-
'!', PREC_UNARY, -1,
|
|
103
|
-
|
|
104
|
-
// * / %
|
|
105
|
-
'*', PREC_MULT,,
|
|
106
|
-
'/', PREC_MULT,,
|
|
107
|
-
'%', PREC_MULT,,
|
|
108
|
-
|
|
109
|
-
// a.b
|
|
110
|
-
'.', PREC_CALL, (node,b) => node && [skip(), node, '@' + expr(PREC_CALL)],
|
|
111
|
-
|
|
112
|
-
// a[b]
|
|
113
|
-
'[', PREC_CALL, (node) => (skip(), node = ['.', node, val(expr(0,CBRACK))], node),
|
|
114
|
-
']',,,
|
|
115
|
-
|
|
116
|
-
// a(b)
|
|
117
|
-
'(', PREC_CALL, (node,b) => (skip(), b=expr(0,CPAREN),
|
|
118
|
-
Array.isArray(b) && b[0]===',' ? (b[0]=node, b) : b ? [node, val(b)] : [node]
|
|
119
|
-
),
|
|
120
|
-
// (a+b)
|
|
121
|
-
'(', PREC_GROUP, (node,b) => !node && (skip(), b=expr(0,CPAREN) || err(), b),
|
|
122
|
-
')',,,
|
|
123
|
-
|
|
124
|
-
// justin extension
|
|
125
|
-
';', PREC_SEQ,,
|
|
126
|
-
'===', PREC_EQ,,
|
|
127
|
-
'!==', PREC_EQ,,
|
|
128
|
-
'**', PREC_EXP,,
|
|
129
|
-
'~', PREC_UNARY, -1,
|
|
130
|
-
'?', PREC_TERN, (node) => {
|
|
131
|
-
if (!node) err('Expected expression')
|
|
132
|
-
let a, b
|
|
133
|
-
skip(), parse.space(), a = expr()
|
|
134
|
-
return ['?:', node, a[1], a[2]]
|
|
135
|
-
},
|
|
136
|
-
':', PREC_COMP,,
|
|
137
|
-
'in', PREC_COMP, (node) => code(2)<=32 && [skip(2), '@'+node, expr(PREC_COMP)],
|
|
138
|
-
|
|
139
|
-
// as operator it's faster to lookup (no need to extra rule check), smaller and no conflict with word names
|
|
140
58
|
// [a,b,c]
|
|
141
|
-
'[',
|
|
142
|
-
|
|
143
|
-
!
|
|
144
|
-
)
|
|
59
|
+
'[', (a, args) => !a && (
|
|
60
|
+
a=expr(0,CBRACK),
|
|
61
|
+
!a ? ctx => [] : a.all ? ctx => a.all(ctx) : ctx => [a(ctx)]
|
|
62
|
+
),,
|
|
145
63
|
|
|
146
|
-
// {a:
|
|
147
|
-
'{',
|
|
148
|
-
|
|
149
|
-
|
|
64
|
+
// {a:1, b:2, c:3}
|
|
65
|
+
'{', (a, args) => !a && (
|
|
66
|
+
a=expr(0,125),
|
|
67
|
+
!a ? ctx => ({}) : ctx => (args=(a.all||a)(ctx), Object.fromEntries(a.all?args:[args]))
|
|
68
|
+
),,
|
|
69
|
+
':', (a, prec, b) => (b=expr(3.1)||err(), ctx => [(a.id||a)(ctx), b(ctx), a(ctx)]), 3.1
|
|
150
70
|
|
|
71
|
+
]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
|
|
151
72
|
|
|
152
|
-
|
|
153
|
-
'null', PREC_TOKEN, node=>!node&&(skip(4),v(null)),
|
|
154
|
-
'false', PREC_TOKEN, node=>!node&&(skip(5),v(false)),
|
|
155
|
-
'true', PREC_TOKEN, node=>!node&&(skip(4),v(true)),
|
|
156
|
-
'undefined', PREC_TOKEN, node=>!node&&(skip(9),v(undefined)),
|
|
157
|
-
])
|
|
158
|
-
const strkey = a => Array.isArray(a) ? (a[1]=(a[1][0]==='@'?'':'@')+a[1],a) : a
|
|
159
|
-
const v = v => ({valueOf:()=>v})
|
|
160
|
-
|
|
161
|
-
addOps(evaluate.operator, 2, [
|
|
162
|
-
// subscript
|
|
163
|
-
'!', a=>!a,
|
|
164
|
-
'++', a=>++a,
|
|
165
|
-
'--', a=>--a,
|
|
166
|
-
|
|
167
|
-
'.', (a,b)=>a?a[b]:a,
|
|
168
|
-
|
|
169
|
-
'%', (a,b)=>a%b,
|
|
170
|
-
'/', (a,b)=>a/b,
|
|
171
|
-
'*', (a,b)=>a*b,
|
|
172
|
-
|
|
173
|
-
'+', (a,b)=>a+(b||0),
|
|
174
|
-
'-', (a,b)=>b==null ? -a : a-b,
|
|
175
|
-
|
|
176
|
-
'>>>', (a,b)=>a>>>b,
|
|
177
|
-
'>>', (a,b)=>a>>b,
|
|
178
|
-
'<<', (a,b)=>a<<b,
|
|
179
|
-
|
|
180
|
-
'>=', (a,b)=>a>=b,
|
|
181
|
-
'>', (a,b)=>a>b,
|
|
182
|
-
'<=', (a,b)=>a<=b,
|
|
183
|
-
'<', (a,b)=>a<b,
|
|
184
|
-
|
|
185
|
-
'!=', (a,b)=>a!=b,
|
|
186
|
-
'==', (a,b)=>a==b,
|
|
187
|
-
|
|
188
|
-
'&', (a,b)=>a&b,
|
|
189
|
-
'^', (a,b)=>a^b,
|
|
190
|
-
'|', (a,b)=>a|b,
|
|
191
|
-
'&&', (...a)=>a.every(Boolean),
|
|
192
|
-
'||', (...a)=>a.some(Boolean),
|
|
193
|
-
',', (a,b)=>(a,b),
|
|
194
|
-
|
|
195
|
-
// justin extension
|
|
196
|
-
'**', (...args)=>args.reduceRight((a,b)=>Math.pow(b,a)),
|
|
197
|
-
'~', a=>~a,
|
|
198
|
-
'?:', (a,b,c)=>a?b:c,
|
|
199
|
-
'in', (a,b)=>a in b,
|
|
200
|
-
|
|
201
|
-
'[', (...args) => Array(...args),
|
|
202
|
-
'{', (...args)=>Object.fromEntries(args),
|
|
203
|
-
':', (a,b)=>[a,b]
|
|
204
|
-
])
|
|
205
|
-
|
|
206
|
-
// TODO ...
|
|
207
|
-
// TODO: strings interpolation
|
|
208
|
-
|
|
209
|
-
export { parse, evaluate }
|
|
210
|
-
|
|
211
|
-
// code → evaluator
|
|
212
|
-
export default s => (s = typeof s == 'string' ? parse(s) : s, ctx => evaluate(s, ctx))
|
|
73
|
+
export default parse
|
package/justin.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
let e,r,t,l,n,a,d=(t,...l)=>(r=t.raw?String.raw(t,...l):t,e=0,!(t=p())||r[e]?i("Unexpected end"):e=>t(e||{})),o=e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e,i=(t="Unexpected token",l=r[e])=>{throw SyntaxError(t+" `"+l+"` at "+e)},f=(t=1,l=e,n)=>{if("number"==typeof t)e+=t;else for(;t(r.charCodeAt(e));)e++;return r.slice(l,e)},p=(r=0,t,l,n,a,d)=>{for(;(l=c())&&(a=(d=u[l])&&d(n,r)||!n&&s());)n=a;return t&&(l==t?e++:i("Missing",String.fromCharCode(t))),n},c=t=>{for(;(t=r.charCodeAt(e))<=32;)e++;return t},s=(e=f(o),r)=>e?((r=r=>r[e]).id=()=>e,r):0,u=[],h=d.set=(t,l,n=32,a=t.charCodeAt(0),d=t.length,i=u[a],f=n.length||([n,l]=[l,n],0),c=t.toUpperCase()!==t,s=(f>1?(e,r)=>e&&(r=p(l))&&(e.length||r.length?t=>n(e(t),r(t)):(e=n(e(),r()),()=>e)):f?e=>!e&&(e=p(l-1))&&(r=>n(e(r))):n))=>u[a]=(n,a,f=e)=>a<l&&(d<2||r.substr(e,d)==t)&&(!c||!o(r.charCodeAt(e+d)))&&(e+=d,s(n,a))||(e=f,i&&i(n,a)),g=e=>e>=48&&e<=57,x=t=>(t&&i("Unexpected number"),t=f((e=>46==e||g(e))),(69==r.charCodeAt(e)||101==r.charCodeAt(e))&&(t+=f(2)+f(g)),(t=+t)!=t?i("Bad number"):()=>t),C=(e,r,t=e.of)=>l=>r(t?t(l):l,e.id());for(l=48;l<=57;)u[l++]=x;for(t=['"',e=>(e=e?i("Unexpected string"):f((e=>e-34)),f()||i("Bad string"),()=>e),,".",(e,r,t)=>e?(c(),r=f(o)||i(),(t=t=>e(t)[r]).id=()=>r,t.of=e,t):x(f(-1)),18,"[",(e,r,t)=>e&&(r=p(0,93)||i("Empty group"),(t=t=>e(t)[r(t)]).id=r,t.of=e,t),18,"(",(e,r,t)=>(r=p(0,41),e?t=>e(t).apply(e.of?.(t),r?r.all?r.all(t):[r(t)]:[]):r||i("Empty group")),18,",",(e,r,t=p(1))=>(t.all=e.all?(r,l,n=e.all(r))=>n.push(t(r))&&n:r=>[e(r),t(r)],t),1,"|",6,(e,r)=>e|r,"||",4,(e,r)=>e||r,"&",8,(e,r)=>e&r,"&&",5,(e,r)=>e&&r,"^",7,(e,r)=>e^r,"==",9,(e,r)=>e==r,"!=",9,(e,r)=>e!=r,">",10,(e,r)=>e>r,">=",10,(e,r)=>e>=r,">>",11,(e,r)=>e>>r,">>>",11,(e,r)=>e>>>r,"<",10,(e,r)=>e<r,"<=",10,(e,r)=>e<=r,"<<",11,(e,r)=>e<<r,"+",12,(e,r)=>e+r,"+",15,e=>+e,"++",e=>C(e||p(14),e?(e,r)=>e[r]++:(e,r)=>++e[r]),15,"-",12,(e,r)=>e-r,"-",15,e=>-e,"--",e=>C(e||p(14),e?(e,r)=>e[r]--:(e,r)=>--e[r]),15,"!",15,e=>!e,"*",13,(e,r)=>e*r,"/",13,(e,r)=>e/r,"%",13,(e,r)=>e%r];[l,n,a,...t]=t,l;)h(l,n,a);let U,A,b,m,y={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},E=t=>(l,n,a="")=>{for(l&&i("Unexpected string");(n=r.charCodeAt(e))-t;)92===n?(f(),n=f(),a+=y[n]||n):a+=f();return f()||i("Bad string"),()=>a};for((U=['"',E(34),,"'",E(39),,"/*",(t,l)=>(f((t=>42!==t&&47!==r.charCodeAt(e+1))),f(2),t||p(l)),,"//",(e,r)=>(f((e=>e>=32)),e||p(r)),,"null",e=>e?i("Unexpected literal"):()=>null,,"true",e=>e?i("Unexpected literal"):()=>!0,,"false",e=>e?i("Unexpected literal"):()=>!1,,"undefined",e=>e?i("Unexpected literal"):()=>{},,";",e=>p()||(()=>{}),,"===",9,(e,r)=>e===r,"!==",9,(e,r)=>e!==r,"~",15,e=>~e,"**",(e,r,t=p(13))=>r=>e(r)**t(r),14,":",3.1,(e,r)=>[e,r],"?",3,(e,r)=>e?r[2]:r[1],"?.",e=>e&&(r=>e(r)||(()=>{})),,"?.",(e,r)=>(c(),(r=f(o))&&(t=>e(t)?.[r])),,"in",10,(e,r)=>e in r,"[",(e,r)=>!e&&((e=p(0,93))?e.all?r=>e.all(r):r=>[e(r)]:e=>[]),,"{",(e,r)=>!e&&((e=p(0,125))?t=>(r=(e.all||e)(t),Object.fromEntries(e.all?r:[r])):e=>({})),,":",(e,r,t)=>(t=p(3.1)||i(),r=>[(e.id||e)(r),t(r),e(r)]),3.1]);[A,b,m,...U]=U,A;)h(A,b,m);export{d as default};
|
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "subscript",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "6.0.3",
|
|
4
|
+
"description": "Fast and tiny expression evaluator with common syntax microlanguage.",
|
|
5
5
|
"main": "subscript.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
8
|
-
"
|
|
9
|
-
"evaluate.js",
|
|
8
|
+
"index.js",
|
|
10
9
|
"subscript.js",
|
|
11
10
|
"subscript.min.js",
|
|
12
11
|
"justin.js",
|
|
@@ -32,11 +31,16 @@
|
|
|
32
31
|
"jexl",
|
|
33
32
|
"jsep",
|
|
34
33
|
"expression",
|
|
34
|
+
"evaluator",
|
|
35
|
+
"parser",
|
|
35
36
|
"evaluation",
|
|
36
37
|
"math",
|
|
37
38
|
"arithmetic",
|
|
38
|
-
"evaluator",
|
|
39
39
|
"justin",
|
|
40
|
+
"eval",
|
|
41
|
+
"math-eval",
|
|
42
|
+
"math-evaluator",
|
|
43
|
+
"calculation",
|
|
40
44
|
"jessie",
|
|
41
45
|
"jessica",
|
|
42
46
|
"eval",
|
|
@@ -44,8 +48,13 @@
|
|
|
44
48
|
"json",
|
|
45
49
|
"calculator",
|
|
46
50
|
"calc",
|
|
47
|
-
"
|
|
48
|
-
"
|
|
51
|
+
"math.js",
|
|
52
|
+
"mathjs",
|
|
53
|
+
"math-codegen",
|
|
54
|
+
"math-parser",
|
|
55
|
+
"formula",
|
|
56
|
+
"operator",
|
|
57
|
+
"overload"
|
|
49
58
|
],
|
|
50
59
|
"author": "Dmitry Iv.",
|
|
51
60
|
"license": "ISC",
|
package/subscript.js
CHANGED
|
@@ -1,130 +1,88 @@
|
|
|
1
|
-
import parse,
|
|
2
|
-
import evaluate, {operator as evalOp} from './evaluate.js'
|
|
3
|
-
|
|
4
|
-
const PERIOD=46, OPAREN=40, CPAREN=41, CBRACK=93, SPACE=32,
|
|
1
|
+
import {parse, set, lookup, skip, cur, idx, err, expr, isId, space} from './index.js'
|
|
5
2
|
|
|
3
|
+
const PERIOD=46, OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, _0=48, _9=57,
|
|
6
4
|
PREC_SEQ=1, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
|
|
7
5
|
PREC_EQ=9, PREC_COMP=10, PREC_SHIFT=11, PREC_SUM=12, PREC_MULT=13, PREC_UNARY=15, PREC_POSTFIX=16, PREC_CALL=18, PREC_GROUP=19
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
let u, list, op, prec, fn,
|
|
8
|
+
isNum = c => c>=_0 && c<=_9,
|
|
9
|
+
// 1.2e+3, .5
|
|
10
|
+
num = n => (
|
|
11
|
+
n&&err('Unexpected number'),
|
|
12
|
+
n = skip(c=>c==PERIOD || isNum(c)),
|
|
13
|
+
(cur.charCodeAt(idx) == 69 || cur.charCodeAt(idx) == 101) && (n += skip(2) + skip(isNum)),
|
|
14
|
+
n=+n, n!=n ? err('Bad number') : () => n // 0 args means token is static
|
|
15
|
+
),
|
|
16
|
+
|
|
17
|
+
inc = (a,fn,c=a.of) => ctx => fn(c?c(ctx):ctx, a.id())
|
|
18
|
+
|
|
19
|
+
// numbers
|
|
20
|
+
for (op=_0;op<=_9;) lookup[op++] = num
|
|
21
|
+
|
|
22
|
+
// operators
|
|
23
|
+
for (list=[
|
|
17
24
|
// "a"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
(
|
|
23
|
-
(c >= 97 && c <= 122) || // a...z
|
|
24
|
-
c == 36 || c == 95 || // $, _,
|
|
25
|
-
c >= 192 // any non-ASCII
|
|
26
|
-
)
|
|
27
|
-
)
|
|
25
|
+
'"', a => (a=a?err('Unexpected string'):skip(c => c-DQUOTE), skip()||err('Bad string'), ()=>a),,
|
|
26
|
+
|
|
27
|
+
// a.b, .2, 1.2 parser in one
|
|
28
|
+
'.', (a,id,fn) => !a ? num(skip(-1)) : // FIXME: .123 is not operator, so we skip back, but mb reorganizing num would be better
|
|
29
|
+
(space(), id=skip(isId)||err(), fn=ctx=>a(ctx)[id], fn.id=()=>id, fn.of=a, fn), PREC_CALL,
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
// a[b]
|
|
32
|
+
'[', (a,b,fn) => a && (b=expr(0,CBRACK)||err(), fn=ctx=>a(ctx)[b(ctx)], fn.id=b, fn.of=a, fn), PREC_CALL,
|
|
33
|
+
|
|
34
|
+
// a(), a(b), (a,b), (a+b)
|
|
35
|
+
'(', (a,b,fn) => (
|
|
36
|
+
b=expr(0,CPAREN),
|
|
37
|
+
// a(), a(b), a(b,c,d)
|
|
38
|
+
a ? ctx => a(ctx).apply(a.of?.(ctx), b ? b.all ? b.all(ctx) : [b(ctx)] : []) :
|
|
39
|
+
// (a+b)
|
|
40
|
+
b || err()
|
|
41
|
+
), PREC_CALL,
|
|
32
42
|
|
|
33
|
-
|
|
34
|
-
',', PREC_SEQ
|
|
43
|
+
// [a,b,c] or (a,b,c)
|
|
44
|
+
',', (a,prec,b=expr(PREC_SEQ)) => (
|
|
45
|
+
b.all = a.all ? ctx => [...a.all(ctx), b(ctx)] : ctx => [a(ctx),b(ctx)],
|
|
46
|
+
b
|
|
47
|
+
), PREC_SEQ,
|
|
35
48
|
|
|
36
|
-
'|', PREC_OR
|
|
37
|
-
'||', PREC_SOME
|
|
49
|
+
'|', PREC_OR, (a,b)=>a|b,
|
|
50
|
+
'||', PREC_SOME, (a,b)=>a||b,
|
|
38
51
|
|
|
39
|
-
'&', PREC_AND
|
|
40
|
-
'&&', PREC_EVERY
|
|
52
|
+
'&', PREC_AND, (a,b)=>a&b,
|
|
53
|
+
'&&', PREC_EVERY, (a,b)=>a&&b,
|
|
41
54
|
|
|
42
|
-
'^', PREC_XOR
|
|
55
|
+
'^', PREC_XOR, (a,b)=>a^b,
|
|
43
56
|
|
|
44
57
|
// ==, !=
|
|
45
|
-
'==', PREC_EQ
|
|
46
|
-
'!=', PREC_EQ
|
|
58
|
+
'==', PREC_EQ, (a,b)=>a==b,
|
|
59
|
+
'!=', PREC_EQ, (a,b)=>a!=b,
|
|
47
60
|
|
|
48
61
|
// > >= >> >>>, < <= <<
|
|
49
|
-
'>', PREC_COMP
|
|
50
|
-
'>=', PREC_COMP
|
|
51
|
-
'>>', PREC_SHIFT
|
|
52
|
-
'>>>', PREC_SHIFT
|
|
53
|
-
'<', PREC_COMP
|
|
54
|
-
'<=', PREC_COMP
|
|
55
|
-
'<<', PREC_SHIFT
|
|
62
|
+
'>', PREC_COMP, (a,b)=>a>b,
|
|
63
|
+
'>=', PREC_COMP, (a,b)=>a>=b,
|
|
64
|
+
'>>', PREC_SHIFT, (a,b)=>a>>b,
|
|
65
|
+
'>>>', PREC_SHIFT, (a,b)=>a>>>b,
|
|
66
|
+
'<', PREC_COMP, (a,b)=>a<b,
|
|
67
|
+
'<=', PREC_COMP, (a,b)=>a<=b,
|
|
68
|
+
'<<', PREC_SHIFT, (a,b)=>a<<b,
|
|
56
69
|
|
|
57
70
|
// + ++ - --
|
|
58
|
-
'+', PREC_SUM
|
|
59
|
-
'+', PREC_UNARY,
|
|
60
|
-
'++', PREC_UNARY
|
|
61
|
-
|
|
62
|
-
'-', PREC_SUM
|
|
63
|
-
'-', PREC_UNARY,
|
|
64
|
-
'--', PREC_UNARY
|
|
65
|
-
'--', PREC_POSTFIX, +1,
|
|
71
|
+
'+', PREC_SUM, (a,b)=>a+b,
|
|
72
|
+
'+', PREC_UNARY, (a)=>+a,
|
|
73
|
+
'++', a => inc(a||expr(PREC_UNARY-1), a ? (a,b)=>a[b]++ : (a,b)=>++a[b]), PREC_UNARY,
|
|
74
|
+
|
|
75
|
+
'-', PREC_SUM, (a,b)=>a-b,
|
|
76
|
+
'-', PREC_UNARY, (a)=>-a,
|
|
77
|
+
'--', a => inc(a||expr(PREC_UNARY-1), a ? (a,b)=>a[b]-- : (a,b)=>--a[b]), PREC_UNARY,
|
|
66
78
|
|
|
67
79
|
// ! ~
|
|
68
|
-
'!', PREC_UNARY,
|
|
80
|
+
'!', PREC_UNARY, (a)=>!a,
|
|
69
81
|
|
|
70
82
|
// * / %
|
|
71
|
-
'*', PREC_MULT
|
|
72
|
-
'/', PREC_MULT
|
|
73
|
-
'%', PREC_MULT
|
|
83
|
+
'*', PREC_MULT, (a,b)=>a*b,
|
|
84
|
+
'/', PREC_MULT, (a,b)=>a/b,
|
|
85
|
+
'%', PREC_MULT, (a,b)=>a%b
|
|
86
|
+
]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
|
|
74
87
|
|
|
75
|
-
|
|
76
|
-
'.', PREC_CALL, (node,b) => node && [skip(), node, '@' + expr(PREC_CALL)],
|
|
77
|
-
|
|
78
|
-
// a[b]
|
|
79
|
-
'[', PREC_CALL, (node) => (skip(), node = ['.', node, val(expr(0,CBRACK))], node),
|
|
80
|
-
']',,,
|
|
81
|
-
|
|
82
|
-
// a(b)
|
|
83
|
-
'(', PREC_CALL, (node,b) => (skip(), b=expr(0,CPAREN),
|
|
84
|
-
Array.isArray(b) && b[0]===',' ? (b[0]=node, b) : b ? [node, val(b)] : [node]
|
|
85
|
-
),
|
|
86
|
-
// (a+b)
|
|
87
|
-
'(', PREC_GROUP, (node,b) => !node && (skip(), b=expr(0,CPAREN) || err(), b),
|
|
88
|
-
')',,,
|
|
89
|
-
])
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
// evaluators
|
|
93
|
-
addOps(evalOp, 2, [
|
|
94
|
-
'!', a=>!a,
|
|
95
|
-
'++', a=>++a,
|
|
96
|
-
'--', a=>--a,
|
|
97
|
-
|
|
98
|
-
'.', (a,b)=>a?a[b]:a,
|
|
99
|
-
|
|
100
|
-
'%', (a,b)=>a%b,
|
|
101
|
-
'/', (a,b)=>a/b,
|
|
102
|
-
'*', (a,b)=>a*b,
|
|
103
|
-
|
|
104
|
-
'+', (a,b)=>a+(b||0),
|
|
105
|
-
'-', (a,b)=>b==null ? -a : a-b,
|
|
106
|
-
|
|
107
|
-
'>>>', (a,b)=>a>>>b,
|
|
108
|
-
'>>', (a,b)=>a>>b,
|
|
109
|
-
'<<', (a,b)=>a<<b,
|
|
110
|
-
|
|
111
|
-
'>=', (a,b)=>a>=b,
|
|
112
|
-
'>', (a,b)=>a>b,
|
|
113
|
-
'<=', (a,b)=>a<=b,
|
|
114
|
-
'<', (a,b)=>a<b,
|
|
115
|
-
|
|
116
|
-
'!=', (a,b)=>a!=b,
|
|
117
|
-
'==', (a,b)=>a==b,
|
|
118
|
-
|
|
119
|
-
'&', (a,b)=>a&b,
|
|
120
|
-
'^', (a,b)=>a^b,
|
|
121
|
-
'|', (a,b)=>a|b,
|
|
122
|
-
'&&', (a,b)=>a&&b,
|
|
123
|
-
'||', (a,b)=>a||b,
|
|
124
|
-
',', (a,b)=>(a,b)
|
|
125
|
-
])
|
|
126
|
-
|
|
127
|
-
export { parse, evaluate }
|
|
128
|
-
|
|
129
|
-
// code → evaluator
|
|
130
|
-
export default s => (s = typeof s == 'string' ? parse(s) : s, ctx => evaluate(s, ctx))
|
|
88
|
+
export default parse
|
package/subscript.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let r,
|
|
1
|
+
let e,r,t,o,a,n,d=(t,...o)=>(r=t.raw?String.raw(t,...o):t,e=0,!(t=i())||r[e]?f("Unexpected end"):e=>t(e||{})),l=e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e,f=(t="Unexpected token",o=r[e])=>{throw SyntaxError(t+" `"+o+"` at "+e)},h=(t=1,o=e,a)=>{if("number"==typeof t)e+=t;else for(;t(r.charCodeAt(e));)e++;return r.slice(o,e)},i=(r=0,t,o,a,n,d)=>{for(;(o=c())&&(n=(d=p[o])&&d(a,r)||!a&&s());)a=n;return t&&(o==t?e++:f("Missing",String.fromCharCode(t))),a},c=t=>{for(;(t=r.charCodeAt(e))<=32;)e++;return t},s=(e=h(l),r)=>e?((r=r=>r[e]).id=()=>e,r):0,p=[],g=d.set=(t,o,a=32,n=t.charCodeAt(0),d=t.length,f=p[n],h=a.length||([a,o]=[o,a],0),c=t.toUpperCase()!==t,s=(h>1?(e,r)=>e&&(r=i(o))&&(e.length||r.length?t=>a(e(t),r(t)):(e=a(e(),r()),()=>e)):h?e=>!e&&(e=i(o-1))&&(r=>a(e(r))):a))=>p[n]=(a,n,h=e)=>n<o&&(d<2||r.substr(e,d)==t)&&(!c||!l(r.charCodeAt(e+d)))&&(e+=d,s(a,n))||(e=h,f&&f(a,n)),C=e=>e>=48&&e<=57,u=t=>(t&&f("Unexpected number"),t=h((e=>46==e||C(e))),(69==r.charCodeAt(e)||101==r.charCodeAt(e))&&(t+=h(2)+h(C)),(t=+t)!=t?f("Bad number"):()=>t),x=(e,r,t=e.of)=>o=>r(t?t(o):o,e.id());for(o=48;o<=57;)p[o++]=u;for(t=['"',e=>(e=e?f("Unexpected string"):h((e=>e-34)),h()||f("Bad string"),()=>e),,".",(e,r,t)=>e?(c(),r=h(l)||f(),(t=t=>e(t)[r]).id=()=>r,t.of=e,t):u(h(-1)),18,"[",(e,r,t)=>e&&(r=i(0,93)||f(),(t=t=>e(t)[r(t)]).id=r,t.of=e,t),18,"(",(e,r,t)=>(r=i(0,41),e?t=>e(t).apply(e.of?.(t),r?r.all?r.all(t):[r(t)]:[]):r||f()),18,",",(e,r,t=i(1))=>(t.all=e.all?r=>[...e.all(r),t(r)]:r=>[e(r),t(r)],t),1,"|",6,(e,r)=>e|r,"||",4,(e,r)=>e||r,"&",8,(e,r)=>e&r,"&&",5,(e,r)=>e&&r,"^",7,(e,r)=>e^r,"==",9,(e,r)=>e==r,"!=",9,(e,r)=>e!=r,">",10,(e,r)=>e>r,">=",10,(e,r)=>e>=r,">>",11,(e,r)=>e>>r,">>>",11,(e,r)=>e>>>r,"<",10,(e,r)=>e<r,"<=",10,(e,r)=>e<=r,"<<",11,(e,r)=>e<<r,"+",12,(e,r)=>e+r,"+",15,e=>+e,"++",e=>x(e||i(14),e?(e,r)=>e[r]++:(e,r)=>++e[r]),15,"-",12,(e,r)=>e-r,"-",15,e=>-e,"--",e=>x(e||i(14),e?(e,r)=>e[r]--:(e,r)=>--e[r]),15,"!",15,e=>!e,"*",13,(e,r)=>e*r,"/",13,(e,r)=>e/r,"%",13,(e,r)=>e%r];[o,a,n,...t]=t,o;)g(o,a,n);export{d as default};
|
package/evaluate.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// calltree → result
|
|
2
|
-
export const evaluate = (node, ctx={}) => {
|
|
3
|
-
if (typeof node === 'string')
|
|
4
|
-
return node[0]==='@' ? node.slice(1) : ctx[node]
|
|
5
|
-
|
|
6
|
-
if (Array.isArray(node)) {
|
|
7
|
-
// [[a,b], c] or ['+', a, b] or ['myfn', a, b], or
|
|
8
|
-
let c = node[0], l = node.length,
|
|
9
|
-
fn = Array.isArray(c) ? evaluate(c, ctx) : (lookup[c] || ctx[c] || c),
|
|
10
|
-
i=1, res=[]
|
|
11
|
-
|
|
12
|
-
if (fn.ab) { res=evaluate(node[i++],ctx); do { res = fn(res, evaluate(node[i++],ctx)) } while ( i < l ) }
|
|
13
|
-
else { while ( i < l ) res.push(evaluate(node[i++], ctx)); res = fn.apply(ctx,res) }
|
|
14
|
-
|
|
15
|
-
return res
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return node
|
|
19
|
-
},
|
|
20
|
-
lookup = {},
|
|
21
|
-
|
|
22
|
-
// op evaluators
|
|
23
|
-
// multiple args (fn.r=false) are caused by:
|
|
24
|
-
// ||&& shortcuts, lisp compatiblity, manual eval, functions multiple arguments, right precedence
|
|
25
|
-
operator = evaluate.operator = (op, fn) => (lookup[op] = fn).ab = fn.length==2
|
|
26
|
-
|
|
27
|
-
export default evaluate
|
package/parse.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
const SPACE=32
|
|
2
|
-
|
|
3
|
-
// current string & index
|
|
4
|
-
let idx, cur
|
|
5
|
-
|
|
6
|
-
export const parse = (str, tree) => (cur=str, idx=0, tree=expr(), idx<cur.length ? err() : val(tree)),
|
|
7
|
-
|
|
8
|
-
err = (msg='Bad syntax') => { throw Error(msg + ' `' + cur[idx] + '` at ' + idx) },
|
|
9
|
-
|
|
10
|
-
skip = (is=1, from=idx) => {
|
|
11
|
-
if (typeof is === 'number') idx += is
|
|
12
|
-
else while (is(code())) idx++;
|
|
13
|
-
return cur.slice(from, idx)
|
|
14
|
-
},
|
|
15
|
-
|
|
16
|
-
code = (i=0) => cur.charCodeAt(idx+i),
|
|
17
|
-
|
|
18
|
-
char = (n=1) => cur.substr(idx, n),
|
|
19
|
-
|
|
20
|
-
// a + b - c
|
|
21
|
-
expr = (prec=0, end, cc, node, i=0, newNode, op) => {
|
|
22
|
-
// chunk/token parser
|
|
23
|
-
while (
|
|
24
|
-
(cc=parse.space()) && (newNode = (op=lookup[cc]) && op(node, prec) || (!node && token(cc)) )
|
|
25
|
-
) node = newNode;
|
|
26
|
-
|
|
27
|
-
// skip end character, if expected
|
|
28
|
-
if (end) cc != end ? err('Unclosed paren') : idx++
|
|
29
|
-
|
|
30
|
-
return node
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
// can be extended with comments, so we export
|
|
34
|
-
space = parse.space = cc => { while (cc = code(), cc <= SPACE) idx++; return cc },
|
|
35
|
-
|
|
36
|
-
// tokens
|
|
37
|
-
tokens = parse.token = [],
|
|
38
|
-
token = (c,i=0,node) => { while(i<tokens.length) if (node = tokens[i++](c)) return node },
|
|
39
|
-
|
|
40
|
-
// operator lookup table
|
|
41
|
-
lookup = [],
|
|
42
|
-
|
|
43
|
-
// create operator checker/mapper (see examples)
|
|
44
|
-
// @param op is operator string
|
|
45
|
-
// @param prec is operator precedenc to check
|
|
46
|
-
// @param map is either number +1 - postfix unary, -1 prefix unary, 0 binary, else - custom mapper function
|
|
47
|
-
operator = parse.operator = (
|
|
48
|
-
op, prec=0, type=0, map, c=op.charCodeAt(0), l=op.length, prev=lookup[c]
|
|
49
|
-
) => (
|
|
50
|
-
map = !type ? node => { // binary
|
|
51
|
-
node = [op, val(node)]
|
|
52
|
-
do { idx+=l, node.push(val(expr(prec))) } // consume same-op group
|
|
53
|
-
while (parse.space()==c && (l<2||char(l)==op))
|
|
54
|
-
return node
|
|
55
|
-
} :
|
|
56
|
-
type > 0 ? node => node && [skip(l), val(node)] : // postfix unary
|
|
57
|
-
type < 0 ? node => !node && [skip(l), val(expr(prec-1))] : // prefix unary
|
|
58
|
-
type,
|
|
59
|
-
|
|
60
|
-
lookup[c] = (node, curPrec) =>
|
|
61
|
-
curPrec < prec && (l<2||char(l)==op) && map(node) || (prev && prev(node, curPrec))
|
|
62
|
-
),
|
|
63
|
-
|
|
64
|
-
// in order to support literal tokens, we call valueOf any time we create or modify calltree node
|
|
65
|
-
val = node => Array.isArray(node) ? node : (node || err()).valueOf()
|
|
66
|
-
|
|
67
|
-
export default parse
|