subscript 1.0.3 → 1.0.4
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/justin.js +1 -0
- package/package.json +1 -1
- package/readme.md +29 -21
- package/subscript.js +189 -105
- package/subscript.min.js +1 -1
package/justin.js
CHANGED
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# <!--<img alt="subscript" src="/subscript2.svg" height=42/>--> sub͘<em>script</em> <!--<sub>SUB͘<em>SCRIPT</em></sub>-->
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
_Subscript_ is micro-language with common syntax subset of C++, JS, Java, Python, Go, Rust.<br/>
|
|
4
4
|
|
|
5
5
|
* Everyone knows _subscript_ syntax
|
|
6
6
|
* Any _subscript_ fragment can be copy-pasted to a target language and it will work
|
|
7
|
-
* It's tiny <sub></sub>
|
|
8
|
-
*
|
|
7
|
+
* It's tiny <sub></sub>
|
|
8
|
+
* It's fast ([~10 times faster than eval](#performance))
|
|
9
|
+
* Enables operators overloading
|
|
9
10
|
* Configurable & extensible
|
|
10
|
-
* Performant?
|
|
11
11
|
* Trivial to use...
|
|
12
12
|
|
|
13
13
|
```js
|
|
@@ -36,12 +36,12 @@ fn({a:{b:1}, c:x=>x*2, d:3}) // 5
|
|
|
36
36
|
|
|
37
37
|
## Lispy tree
|
|
38
38
|
|
|
39
|
-
It compiles code to lispy calltree (
|
|
39
|
+
It compiles code to lispy calltree (compatible with [frisk](https://npmjs.com/frisk)). Why?
|
|
40
40
|
|
|
41
41
|
+ minimal possible AST overhead
|
|
42
42
|
+ clear operators precedence
|
|
43
|
-
+
|
|
44
|
-
+ manual evaluation, debugging
|
|
43
|
+
+ overloading operators by context
|
|
44
|
+
+ simple manual evaluation, debugging
|
|
45
45
|
+ conventional form
|
|
46
46
|
+ one-liner docs...
|
|
47
47
|
|
|
@@ -66,14 +66,14 @@ import {quotes, comments, parse} from 'subscript.js'
|
|
|
66
66
|
quotes["'"] = "'"
|
|
67
67
|
comments["//"] = "\n"
|
|
68
68
|
|
|
69
|
-
parse(`'a' + 'b' // concat`) // ['+',
|
|
69
|
+
parse(`'a' + 'b' // concat`) // ['+', 'a':String, 'b':String]
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
## Operators
|
|
73
73
|
|
|
74
74
|
Default operators include common operators for the listed languages in the following precedence:
|
|
75
75
|
|
|
76
|
-
*
|
|
76
|
+
* `.`
|
|
77
77
|
* `! + - ++ --` unary
|
|
78
78
|
* `* / %`
|
|
79
79
|
* `+ -`
|
|
@@ -85,16 +85,15 @@ Default operators include common operators for the listed languages in the follo
|
|
|
85
85
|
* `|`
|
|
86
86
|
* `&&`
|
|
87
87
|
* `||`
|
|
88
|
-
* `,`
|
|
89
88
|
|
|
90
89
|
All other operators can be extended.
|
|
91
90
|
|
|
92
91
|
```js
|
|
93
|
-
import {
|
|
92
|
+
import {binary, parse, evaluate} from 'subscript.js'
|
|
94
93
|
|
|
95
94
|
// add operators to precedence groups
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
binary[0]['=>'] = (args, body) => evaluate(body, args)
|
|
96
|
+
binary[5]['|'] = (a,...b) => a.pipe(...b)
|
|
98
97
|
|
|
99
98
|
let tree = parse(`
|
|
100
99
|
interval(350)
|
|
@@ -105,20 +104,19 @@ let tree = parse(`
|
|
|
105
104
|
evaluate(tree, {Math, map, take, interval, gaussian})
|
|
106
105
|
```
|
|
107
106
|
|
|
108
|
-
|
|
107
|
+
Operators are defined in by precedence index.
|
|
109
108
|
|
|
110
109
|
```js
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
unary[0]['&'] = a => address(a) // unary prefix: &a
|
|
111
|
+
unary[1]['!'] = a => factorial(a) // TODO: unary postfix: a!
|
|
112
|
+
binary[9]['U'] = (a,b) => a.union(b) // binary: a U b
|
|
113
|
+
binary[9]['|'] = (...a) => a[0].pipe(...) // also binary: a | b
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
TODO: postfix unary operators are not yet supported.
|
|
117
|
-
|
|
118
116
|
|
|
119
117
|
## Transforms
|
|
120
118
|
|
|
121
|
-
Transform rules are applied to raw parsed
|
|
119
|
+
Transform rules are applied to raw parsed calltree groups, eg.:
|
|
122
120
|
|
|
123
121
|
* Flatten calls `a(b,c)(d)` → `['(', 'a', [',', 'b', 'c'], 'd']` → `[['a', 'b', 'c'], 'd']`
|
|
124
122
|
* Property access `a.b.c` → `['.', 'a', 'b', 'c']` → `['.', 'a', '"b"', '"c"']`
|
|
@@ -141,6 +139,7 @@ parse('a ? b') // ['?', 'a', 'b']
|
|
|
141
139
|
parse('a : b') // [':', 'a', 'b']
|
|
142
140
|
```
|
|
143
141
|
|
|
142
|
+
|
|
144
143
|
## Justin
|
|
145
144
|
|
|
146
145
|
_Justin_ extension (original [thread](https://github.com/endojs/Jessie/issues/66)) is minimal JS subset − JSON with JS expressions.<br/>
|
|
@@ -272,14 +271,23 @@ These are custom DSL operators snippets for your inspiration:
|
|
|
272
271
|
|
|
273
272
|
## Performance
|
|
274
273
|
|
|
275
|
-
|
|
274
|
+
Subscript shows fastest known performance within other evaluators:
|
|
276
275
|
|
|
276
|
+
```
|
|
277
|
+
expr-eval: 105.385986328125 ms
|
|
278
|
+
jsep: 71.583251953125 ms
|
|
279
|
+
subscript: 34.6259765625 ms
|
|
280
|
+
jexl: 88.382080078125 ms
|
|
281
|
+
string-math: 107.573974609375 ms
|
|
282
|
+
new Function: 354.0400390625 ms
|
|
283
|
+
```
|
|
277
284
|
|
|
278
285
|
## See also
|
|
279
286
|
|
|
280
287
|
* [Jessie](https://github.com/endojs/Jessie) − Minimal JS subset.
|
|
281
288
|
* [jexl](https://github.com/TomFrost/Jexl)
|
|
282
289
|
* [expr-eval](https://github.com/silentmatt/expr-eval)
|
|
290
|
+
* [expression-eval](https://github.com/donmccurdy/expression-eval)
|
|
283
291
|
* [jsep](https://github.com/EricSmekens/jsep)
|
|
284
292
|
* [string-math](https://github.com/devrafalko/string-math)
|
|
285
293
|
|
package/subscript.js
CHANGED
|
@@ -1,28 +1,60 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
const isDigit = c => c >= 48 && c <= 57, // 0...9,
|
|
2
|
+
isIdentifierStart = c => (c >= 65 && c <= 90) || // A...Z
|
|
3
|
+
(c >= 97 && c <= 122) || // a...z
|
|
4
|
+
(c == 36 || c == 95) || // $, _,
|
|
5
|
+
c >= 192, // any non-ASCII
|
|
6
|
+
isIdentifierPart = c => isIdentifierStart(c) || isDigit(c),
|
|
7
|
+
isSpace = c => c <= 32,
|
|
8
|
+
isNotQuote = c => !quotes[String.fromCharCode(c)],
|
|
9
|
+
|
|
10
|
+
oper = (op, ops=binary, f, res, i) => { for (i=ops.length;i--;) if (res=ops[i][op]) return f?res:i+1 }, // get precedence
|
|
11
|
+
isCmd = (a,op) => Array.isArray(a) && a.length && a[0] && (op ? a[0]===op : typeof a[0] === 'string' || isCmd(a[0])), // is calltree node
|
|
12
|
+
|
|
13
|
+
unlist = l => l.length<2?l[0]:l,
|
|
14
|
+
nil = Symbol.for('nil'),
|
|
15
|
+
|
|
16
|
+
Err = e => {throw new Error(e)}
|
|
17
|
+
|
|
18
|
+
export const literals = {
|
|
19
|
+
true: true,
|
|
20
|
+
false: false,
|
|
21
|
+
null: null
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
blocks = {'(':')','[':']'},
|
|
25
|
+
quotes = {'"':'"'},
|
|
26
|
+
comments = {},
|
|
27
|
+
|
|
28
|
+
unary = [{ // prefix
|
|
29
|
+
'!':a=>!a,
|
|
30
|
+
'+':a=>+a,
|
|
31
|
+
'-':a=>-a,
|
|
32
|
+
'++':a=>++a,
|
|
33
|
+
'--':a=>--a
|
|
34
|
+
}, { // postfix
|
|
35
|
+
}],
|
|
36
|
+
|
|
37
|
+
// binaries take multiple args because
|
|
38
|
+
// + that allows shortcuts
|
|
39
|
+
// + that's lisp/frisk compatible
|
|
40
|
+
// + that allows simpler manual evaluator calls
|
|
41
|
+
// + functions anyways take multiple arguments
|
|
42
|
+
// parser still generates binary groups - it's safer and clearer
|
|
43
|
+
binary = [
|
|
44
|
+
{'||':(...a)=>a.some(Boolean)},
|
|
45
|
+
{'&&':(...a)=>a.every(Boolean)},
|
|
46
|
+
{'|':(a,b)=>a|b},
|
|
47
|
+
{'^':(a,b)=>a^b},
|
|
48
|
+
{'&':(a,b)=>a&b},
|
|
18
49
|
{
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'*':(...a)=>a.reduce((a,b)=>a*b),
|
|
50
|
+
'!=':(a,b)=>a!=b,
|
|
51
|
+
'==':(a,b)=>a==b,
|
|
22
52
|
},
|
|
23
53
|
{
|
|
24
|
-
'
|
|
25
|
-
'
|
|
54
|
+
'>=':(a,b)=>a>=b,
|
|
55
|
+
'>':(a,b)=>a>b,
|
|
56
|
+
'<=':(a,b)=>a<=b,
|
|
57
|
+
'<':(a,b)=>a<b,
|
|
26
58
|
},
|
|
27
59
|
{
|
|
28
60
|
'>>>':(a,b)=>a>>>b,
|
|
@@ -30,105 +62,157 @@ export const operators = [
|
|
|
30
62
|
'<<':(a,b)=>a<<b,
|
|
31
63
|
},
|
|
32
64
|
{
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'<=':(a,b)=>a<=b,
|
|
36
|
-
'<':(a,b)=>a<b,
|
|
65
|
+
'+':(...a)=>a.reduce((a,b)=>a+b),
|
|
66
|
+
'-':(...a)=>a.reduce((a,b)=>a-b),
|
|
37
67
|
},
|
|
38
68
|
{
|
|
39
|
-
'
|
|
40
|
-
'
|
|
69
|
+
'%':(...a)=>a.reduce((a,b)=>a%b),
|
|
70
|
+
'/':(...a)=>a.reduce((a,b)=>a/b),
|
|
71
|
+
'*':(...a)=>a.reduce((a,b)=>a*b),
|
|
41
72
|
},
|
|
42
|
-
{
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{'&&':(...a)=>a.every(Boolean)},
|
|
46
|
-
{'||':(...a)=>a.some(Boolean)},
|
|
47
|
-
{',':(...a)=>a.reduce((a,b)=>(a,b))},
|
|
73
|
+
{
|
|
74
|
+
'.':(...a)=>a.reduce((a,b)=>a?a[b]:a)
|
|
75
|
+
}
|
|
48
76
|
],
|
|
49
|
-
|
|
50
|
-
if (!s || typeof s != 'string' || quotes[s[0]]) return
|
|
51
|
-
for (i=operators.length;i--;) if (o=operators[i][s], o&&l==1?o.length==l:o) return o
|
|
52
|
-
},
|
|
53
|
-
literals = {true:true, false:false, null:null, undefined:undefined},
|
|
54
|
-
blocks = {'(':')','[':']'},
|
|
55
|
-
quotes = {'"':'"'},
|
|
56
|
-
comments = {},
|
|
77
|
+
|
|
57
78
|
transforms = {
|
|
79
|
+
// Array literal
|
|
80
|
+
// Object literal
|
|
81
|
+
// Ternary
|
|
58
82
|
// [(, a] → a, [(,a,''] → [a], [(,a,[',',b,c],d] → [[a,b,c],d]
|
|
59
|
-
'(': s => s.length <
|
|
60
|
-
'.': s => [s[0],s[1], ...s.slice(2).map(a=>typeof a === 'string' ? `"${a}"` : a)] // [.,a,b → [.,a,'"b"'
|
|
83
|
+
'(': s => s.length < 3 ? s[1] : s // : s.slice(1).reduce((a,b)=>[a].concat(!b?[]:b[0]==','?b.slice(1):[b])),
|
|
84
|
+
// '.': s => [s[0],s[1], ...s.slice(2).map(a=>typeof a === 'string' ? `"${a}"` : a)] // [.,a,b → [.,a,'"b"'
|
|
61
85
|
},
|
|
62
|
-
transform = (n, t) => (t =
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
else
|
|
81
|
-
else if (to=blocks[c]) commit(c), cur.push(tokenize(to))
|
|
82
|
-
else if (!buf && c>='0' && c<='9' || c=='.' && s[i]>='0' && s[i]<='9') n=1
|
|
83
|
-
else if (c==end) return commit(), group(cur)
|
|
84
|
-
else if (operator(c=c3)||operator(c=c2)||operator(c=c[0]))
|
|
85
|
-
if (i+=c.length-1, c.toLowerCase()==c.toUpperCase() || !buf&&space.includes(s[i])) // word operators
|
|
86
|
-
commit(c)
|
|
86
|
+
transform = (n, t) => (t = isCmd(n)&&transforms[n[0]], t?t(n):n),
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
parse = (expr, index=0, len=expr.length) => {
|
|
90
|
+
const char = () => expr.charAt(index),
|
|
91
|
+
code = () => expr.charCodeAt(index),
|
|
92
|
+
err = message => Err(message + ' at character ' + index),
|
|
93
|
+
|
|
94
|
+
// skip index until condition matches
|
|
95
|
+
skip = (f, c=code()) => { while (index < len && f(c)) c=expr.charCodeAt(++index); return index },
|
|
96
|
+
|
|
97
|
+
// skip index, returning skipped part
|
|
98
|
+
gobble = f => expr.slice(index, skip(f)),
|
|
99
|
+
|
|
100
|
+
gobbleSequence = (end) => {
|
|
101
|
+
let list = [], c;
|
|
102
|
+
while (index < len && (c=char()) !== end) {
|
|
103
|
+
if (c === ';' || c === ',') index++; // ignore separators
|
|
104
|
+
else list.push(gobbleExpression()), skip(isSpace)
|
|
87
105
|
}
|
|
106
|
+
if (end) index++
|
|
107
|
+
|
|
108
|
+
return list;
|
|
88
109
|
},
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
|
|
111
|
+
gobbleOp = (ops=binary, op, l=3) => {
|
|
112
|
+
skip(isSpace);
|
|
113
|
+
while (!oper(op=expr.substr(index, l--),ops)) if (!l) return
|
|
114
|
+
index+=op.length
|
|
115
|
+
return op
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
// `1`, `1+2`, `a+(b*2)-Math.sqrt(2)`
|
|
119
|
+
gobbleExpression = () => {
|
|
120
|
+
let node, op, prec, stack, op_info, left, right, i, curOp;
|
|
121
|
+
|
|
122
|
+
if (nil==(left = gobbleToken())) return;
|
|
123
|
+
if (!(op = gobbleOp())) return left;
|
|
124
|
+
if (nil==(right = gobbleToken())) err("Expected expression after " + op);
|
|
125
|
+
|
|
126
|
+
// Otherwise, start a stack to properly place the binary operations in their precedence structure
|
|
127
|
+
stack = [left, [ op, oper(op)||0 ], right];
|
|
128
|
+
|
|
129
|
+
// Properly deal with precedence using [recursive descent](http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm)
|
|
130
|
+
// Stripped from jsep, not much mind given optimizing, but good enough
|
|
131
|
+
while ((curOp = gobbleOp()) && (prec = oper(curOp))) {
|
|
132
|
+
// Reduce: make a binary expression from the three topmost entries.
|
|
133
|
+
while ((stack.length > 2) && stack[stack.length-2][1] >= prec) {
|
|
134
|
+
right = stack.pop(), op = stack.pop()[0], left = stack.pop();
|
|
135
|
+
stack.push([op, left, right]); // BINARY_EXP
|
|
113
136
|
}
|
|
114
|
-
|
|
137
|
+
if (nil==(node = gobbleToken())) err("Expected expression after " + curOp);
|
|
138
|
+
stack.push([curOp, prec], node);
|
|
115
139
|
}
|
|
116
140
|
|
|
117
|
-
|
|
141
|
+
i = stack.length - 1, node = stack[i];
|
|
142
|
+
while (i > 1) { node = [stack[i-1][0], stack[i-2], node], i-=2 } // BINARY_EXP
|
|
143
|
+
|
|
144
|
+
return node;
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
// `foo.bar(baz)`, `1`, `"abc"`, `(a % 2)`
|
|
148
|
+
gobbleToken = () => {
|
|
149
|
+
let cc, c, op, node;
|
|
150
|
+
skip(isSpace);
|
|
151
|
+
|
|
152
|
+
cc = code(), c = char()
|
|
153
|
+
|
|
154
|
+
// `.` can start off a numeric literal
|
|
155
|
+
if (isDigit(cc) || c === '.') node = new Number(gobbleNumber());
|
|
156
|
+
else if (!isNotQuote(cc)) index++, node = new String(gobble(isNotQuote)), index++
|
|
157
|
+
else if (blocks[c]) index++, node = transform([c, ...gobbleSequence(blocks[c])])
|
|
158
|
+
else if (isIdentifierStart(cc)) node = (node = gobble(isIdentifierPart)) in literals ? literals[node] : node
|
|
159
|
+
else if (op = gobbleOp(unary)) return nil==(node = gobbleToken()) ? err('missing unaryOp argument') : [op, node];
|
|
160
|
+
else return nil
|
|
161
|
+
|
|
162
|
+
// a.b[c](d)
|
|
163
|
+
// FIXME: that's heuristic of more generic something, like transform
|
|
164
|
+
// FIXME: optimize condition
|
|
165
|
+
while (skip(isSpace), c=char(), c === '.' || c === '[' || c === '(') {
|
|
166
|
+
index++, skip(isSpace)
|
|
167
|
+
if (c === '[') (node = ['.',node]).push(unlist(gobbleSequence(']'))) // MEMBER_EXP
|
|
168
|
+
else if (c === '(') node = [node, ...gobbleSequence(')')] // CALL_EXP
|
|
169
|
+
else if (c === '.') (node = ['.',node]).push(gobble(isIdentifierPart)) // MEMBER_EXP
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return node;
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
// `12`, `3.4`, `.5`
|
|
176
|
+
gobbleNumber = () => {
|
|
177
|
+
let number = '', c = char();
|
|
178
|
+
|
|
179
|
+
number += gobble(isDigit)
|
|
180
|
+
if (char() === '.') number += expr.charAt(index++) + gobble(isDigit) // .1
|
|
181
|
+
|
|
182
|
+
c = char();
|
|
183
|
+
if (c === 'e' || c === 'E') { // exponent marker
|
|
184
|
+
number += c, index++
|
|
185
|
+
c = char();
|
|
186
|
+
if (c === '+' || c === '-') number += c, index++; // exponent sign
|
|
187
|
+
number += gobble(isDigit)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return number // LITERAL
|
|
118
191
|
}
|
|
119
192
|
|
|
120
|
-
|
|
121
|
-
return s
|
|
193
|
+
return unlist(gobbleSequence())
|
|
122
194
|
},
|
|
123
195
|
|
|
124
196
|
// calltree → result
|
|
125
|
-
evaluate = (s, ctx={}) =>
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
? quotes[s[0]] ? s.slice(1,-1) : ctx[s]
|
|
130
|
-
: s
|
|
197
|
+
evaluate = (s, ctx={}, c, op) => {
|
|
198
|
+
if (isCmd(s)) {
|
|
199
|
+
// FIXME: move to transforms
|
|
200
|
+
if ((c=s[0])=='.') return oper(c,binary,true)(evaluate(s[1], ctx),...s.slice(2))
|
|
131
201
|
|
|
132
|
-
|
|
133
|
-
|
|
202
|
+
if (typeof c === 'string') op = oper(c, s.length<3?unary:binary, true)
|
|
203
|
+
c = op || evaluate(c, ctx)
|
|
204
|
+
if (typeof c !== 'function') return c
|
|
134
205
|
|
|
206
|
+
return c.call(...s.map(a=>evaluate(a,ctx)))
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (s && typeof s === 'string')
|
|
210
|
+
return quotes[s[0]] ? s.slice(1,-1)
|
|
211
|
+
: s[0]==='@' ? s.slice(1)
|
|
212
|
+
: s in ctx ? ctx[s] : s
|
|
213
|
+
|
|
214
|
+
return s
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// code → evaluator
|
|
218
|
+
export default s => (s = typeof s == 'string' ? parse(s) : s, ctx => evaluate(s, ctx))
|
package/subscript.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
const e=e=>e>=48&&e<=57,r=e=>e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192,t=t=>r(t)||e(t),n=e=>e<=32,s=e=>!quotes[String.fromCharCode(e)],a=(e,r=binary,t,n,s)=>{for(s=r.length;s--;)if(n=r[s][e])return t?n:s+1},o=(e,r)=>Array.isArray(e)&&e.length&&e[0]&&(r?e[0]===r:"string"==typeof e[0]||o(e[0])),l=e=>e.length<2?e[0]:e,u=Symbol.for("nil");export const literals={true:!0,false:!1,null:null},blocks={"(":")","[":"]"},quotes={'"':'"'},comments={},unary=[{"!":e=>!e,"+":e=>+e,"-":e=>-e,"++":e=>++e,"--":e=>--e},{}],binary=[{"||":(...e)=>e.some(Boolean)},{"&&":(...e)=>e.every(Boolean)},{"|":(e,r)=>e|r},{"^":(e,r)=>e^r},{"&":(e,r)=>e&r},{"!=":(e,r)=>e!=r,"==":(e,r)=>e==r},{">=":(e,r)=>e>=r,">":(e,r)=>e>r,"<=":(e,r)=>e<=r,"<":(e,r)=>e<r},{">>>":(e,r)=>e>>>r,">>":(e,r)=>e>>r,"<<":(e,r)=>e<<r},{"+":(...e)=>e.reduce(((e,r)=>e+r)),"-":(...e)=>e.reduce(((e,r)=>e-r))},{"%":(...e)=>e.reduce(((e,r)=>e%r)),"/":(...e)=>e.reduce(((e,r)=>e/r)),"*":(...e)=>e.reduce(((e,r)=>e*r))},{".":(...e)=>e.reduce(((e,r)=>e?e[r]:e))}],transforms={"(":e=>e.length<3?e[1]:e},transform=(e,r)=>(r=o(e)&&transforms[e[0]])?r(e):e,parse=(i,f=0,c=i.length)=>{const p=()=>i.charAt(f),h=()=>i.charCodeAt(f),g=e=>(e=>{throw new Error(e)})(e+" at character "+f),y=(e,r=h())=>{for(;f<c&&e(r);)r=i.charCodeAt(++f);return f},m=e=>i.slice(f,y(e)),d=e=>{let r,t=[];for(;f<c&&(r=p())!==e;)";"===r||","===r?f++:(t.push(v()),y(n));return e&&f++,t},b=(e=binary,r,t=3)=>{for(y(n);!a(r=i.substr(f,t--),e);)if(!t)return;return f+=r.length,r},v=()=>{let e,r,t,n,s,o,l,i;if(u!=(s=x())){if(!(r=b()))return s;for(u==(o=x())&&g("Expected expression after "+r),n=[s,[r,a(r)||0],o];(i=b())&&(t=a(i));){for(;n.length>2&&n[n.length-2][1]>=t;)o=n.pop(),r=n.pop()[0],s=n.pop(),n.push([r,s,o]);u==(e=x())&&g("Expected expression after "+i),n.push([i,t],e)}for(l=n.length-1,e=n[l];l>1;)e=[n[l-1][0],n[l-2],e],l-=2;return e}},x=()=>{let a,i,c,v;if(y(n),a=h(),i=p(),e(a)||"."===i)v=new Number(A());else if(s(a))if(blocks[i])f++,w=[i,...d(blocks[i])],v=(C=o(w)&&transforms[w[0]])?C(w):w;else{if(!r(a))return(c=b(unary))?u==(v=x())?g("missing unaryOp argument"):[c,v]:u;v=(v=m(t))in literals?literals[v]:v}else f++,v=new String(m(s)),f++;for(var w,C;y(n),i=p(),"."===i||"["===i||"("===i;)f++,y(n),"["===i?(v=[".",v]).push(l(d("]"))):"("===i?v=[v,...d(")")]:"."===i&&(v=[".",v]).push(m(t));return v},A=()=>{let r="",t=p();return r+=m(e),"."===p()&&(r+=i.charAt(f++)+m(e)),t=p(),"e"!==t&&"E"!==t||(r+=t,f++,t=p(),"+"!==t&&"-"!==t||(r+=t,f++),r+=m(e)),r};return l(d())},evaluate=(e,r={},t,n)=>o(e)?"."==(t=e[0])?a(t,binary,!0)(evaluate(e[1],r),...e.slice(2)):("string"==typeof t&&(n=a(t,e.length<3?unary:binary,!0)),"function"!=typeof(t=n||evaluate(t,r))?t:t.call(...e.map((e=>evaluate(e,r))))):e&&"string"==typeof e?quotes[e[0]]?e.slice(1,-1):"@"===e[0]?e.slice(1):e in r?r[e]:e:e;export default e=>(e="string"==typeof e?parse(e):e,r=>evaluate(e,r));
|