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 CHANGED
@@ -50,4 +50,5 @@ transforms['{'] = (s,entries) => {
50
50
 
51
51
  // TODO: strings interpolation
52
52
 
53
+ export { default } from './subscript.js';
53
54
  export * from './subscript.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "subscript",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Microlanguage with common syntax for JS/C++/Python/Rust",
5
5
  "main": "subscript.js",
6
6
  "files": [
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
- Subscript is micro-language, common subset of C++, JS, Java, Python, Go, Rust.<br/>
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>![npm bundle size](https://img.shields.io/bundlephobia/minzip/subscript?color=brightgreen&label=gzip)</sub>
8
- * Enables easy operators overloading
7
+ * It's tiny <sub>![npm bundle size](https://img.shields.io/bundlephobia/minzip/subscript/latest?color=brightgreen&label=gzip)</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 (\~[frisk](https://npmjs.com/frisk)). Why?
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
- + easy to overload operators, mimic lang subsets
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`) // ['+', "'a'", "'b'"]
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 {operators, parse, evaluate} from 'subscript.js'
92
+ import {binary, parse, evaluate} from 'subscript.js'
94
93
 
95
94
  // add operators to precedence groups
96
- operators[0]['=>'] = (args, body) => evaluate(body, args)
97
- operators[5]['|'] = (a,...b) => a.pipe(...b)
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
- Operator arity is detected from number of arguments:
107
+ Operators are defined in by precedence index.
109
108
 
110
109
  ```js
111
- operators[1]['&'] = a=>address(a) // unary prefix: &a
112
- operators[9]['U'] = (a,b)=>a.union(b) // binary: a U b
113
- operators[9]['|'] = (...a)=>a[0].pipe(...) // also binary: a | b
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 operator groups, eg.:
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
- Compare against js eval, Function, quickjs, SES, jscan, alternatives from see-also
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
- export const operators = [
2
- {
3
- '(':(...a)=>a(...args),
4
- '[':(...a)=>a.reduce((a,b)=>a?a[b]:a),
5
- '.':(...a)=>a.reduce((a,b)=>a?a[b]:a)
6
- },
7
- {
8
- '!':a=>!a,
9
- '+':a=>+a,
10
- '-':a=>-a,
11
- '++':a=>++a,
12
- '--':a=>--a
13
- },
14
- // {
15
- // '++':a=>a++,
16
- // '--':a=>a--
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
- '%':(...a)=>a.reduce((a,b)=>a%b),
20
- '/':(...a)=>a.reduce((a,b)=>a/b),
21
- '*':(...a)=>a.reduce((a,b)=>a*b),
50
+ '!=':(a,b)=>a!=b,
51
+ '==':(a,b)=>a==b,
22
52
  },
23
53
  {
24
- '+':(...a)=>a.reduce((a,b)=>a+b),
25
- '-':(...a)=>a.reduce((a,b)=>a-b),
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
- '>=':(a,b)=>a>=b,
34
- '>':(a,b)=>a>b,
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
- '!=':(a,b)=>a!=b,
40
- '==':(a,b)=>a==b,
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
- {'&':(a,b)=>a&b},
43
- {'^':(a,b)=>a^b},
44
- {'|':(a,b)=>a|b},
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
- operator = (s,l,o,i) => {
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 < 2 ? s[1] : s.slice(1).reduce((a,b)=>[a].concat(!b?[]:b[0]==','?b.slice(1):[b])),
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 = isnode(n)&&transforms[n[0]], t?t(n):n),
63
-
64
- isnode = a=>Array.isArray(a)&&a.length&&a[0],
65
- space = ' \r\n\t',
66
-
67
- // code calltree
68
- parse = (s, i=0) => {
69
- const tokenize = (end, buf='', n, c, c2, c3, to, cur=[]) => {
70
- const commit = op => {
71
- if (buf!=='') cur.push(n ? parseFloat(buf) : buf in literals ? literals[buf] : buf)
72
- if (op) cur.push(op)
73
- n=buf=c=''
74
- }
75
- for (; i<=s.length; buf+=c) {
76
- c = s[i++], c2=c+s[i], c3=c2+s[i+1]
77
- if (n && (c=='e'||c=='E')) c+=s[i++]
78
- else if (space.includes(c)) commit()
79
- else if (to=comments[c2]||comments[c]) commit(),skip(s,to)
80
- else if (to=(quotes[c3]||quotes[c2]||quotes[c])) buf=c+s.slice(i,skip(s,to)),commit()
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
- skip = (s,tok,n)=>(i= (i=s.indexOf(tok,i),i)<0 ? s.length : i+tok.length),
90
-
91
- // group into calltree nodes by precedence
92
- group = (s) => {
93
- if (!s.length) return undefined
94
-
95
- let prec, i, gi, a,b,x, f
96
-
97
- const commit=() => ~gi && (s[gi]=transform(s[gi]), gi=-1)
98
-
99
- for (prec of operators) {
100
- for (gi=-1,i=1;i<s.length;) {
101
- a=s[i-2],x=s[i-1],b=s[i], f = typeof x === 'string' && prec[x]
102
- if (f && !operator(b) && i>1&&!operator(a)) { // binary: a+b
103
- if (prec[x].length==1) commit(), i++ // skip non-binary op
104
- else if (gi==i-2 && a[0]==x) a.push(b), s.splice(i-1,2) // ,[+,a,b],+,c → ,[+,a,b]
105
- else commit(), s.splice(gi=i-2,3,[x,s[gi],b]) // ,a,+,b, ,[+,a,b],
106
- }
107
- else if (f && !operator(b)) { // unary prefix: +b, -+b
108
- s.splice(gi=i-1,2,[x,b]) // _,-,b _,[-,b]
109
- i-- // (shift left to consume prefix unary or binary)
110
- }
111
- // TODO: detect postfix unary
112
- else commit(),i++
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
- commit() // last binary can be hanging
137
+ if (nil==(node = gobbleToken())) err("Expected expression after " + curOp);
138
+ stack.push([curOp, prec], node);
115
139
  }
116
140
 
117
- return s.length>1?s:s[0]
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
- s=tokenize()
121
- return s
193
+ return unlist(gobbleSequence())
122
194
  },
123
195
 
124
196
  // calltree → result
125
- evaluate = (s, ctx={}) => isnode(s)
126
- ? (isnode(s[0]) ? evaluate(s[0]) : typeof s[0]==='string' ? ctx[s[0]]||operator(s[0],s.length-1) : s[0])
127
- (...s.slice(1).map(a=>evaluate(a,ctx)))
128
- : typeof s == 'string'
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
- // code evaluator
133
- export default s => (s = typeof s == 'string' ? parse(s) : s, ctx => evaluate(s, ctx))
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
- export const operators=[{"(":(...e)=>e(...args),"[":(...e)=>e.reduce(((e,r)=>e?e[r]:e)),".":(...e)=>e.reduce(((e,r)=>e?e[r]:e))},{"!":e=>!e,"+":e=>+e,"-":e=>-e,"++":e=>++e,"--":e=>--e},{"%":(...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,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.every(Boolean)},{"||":(...e)=>e.some(Boolean)},{",":(...e)=>e.reduce(((e,r)=>r))}],operator=(e,r,t,s)=>{if(e&&"string"==typeof e&&!quotes[e[0]])for(s=operators.length;s--;)if((t=operators[s][e])&&1==r?t.length==r:t)return t},literals={true:!0,false:!1,null:null,undefined:void 0},blocks={"(":")","[":"]"},quotes={'"':'"'},comments={},transforms={"(":e=>e.length<2?e[1]:e.slice(1).reduce(((e,r)=>[e].concat(r?","==r[0]?r.slice(1):[r]:[]))),".":e=>[e[0],e[1],...e.slice(2).map((e=>"string"==typeof e?`"${e}"`:e))]},transform=(e,r)=>(r=isnode(e)&&transforms[e[0]])?r(e):e,isnode=e=>Array.isArray(e)&&e.length&&e[0],space=" \r\n\t",parse=(e,r=0)=>{const t=(n,l="",a,i,p,u,c,f=[])=>{const d=e=>{""!==l&&f.push(a?parseFloat(l):l in literals?literals[l]:l),e&&f.push(e),a=l=i=""};for(;r<=e.length;l+=i)if(u=(p=(i=e[r++])+e[r])+e[r+1],!a||"e"!=i&&"E"!=i)if(space.includes(i))d();else if(c=comments[p]||comments[i])d(),s(e,c);else if(c=quotes[u]||quotes[p]||quotes[i])l=i+e.slice(r,s(e,c)),d();else if(c=blocks[i])d(i),f.push(t(c));else if(!l&&i>="0"&&i<="9"||"."==i&&e[r]>="0"&&e[r]<="9")a=1;else{if(i==n)return d(),o(f);(operator(i=u)||operator(i=p)||operator(i=i[0]))&&(r+=i.length-1,(i.toLowerCase()==i.toUpperCase()||!l&&space.includes(e[r]))&&d(i))}else i+=e[r++]},s=(e,t,s)=>r=(r=e.indexOf(t,r))<0?e.length:r+t.length,o=e=>{if(!e.length)return;let r,t,s,o,n,l,a;const i=()=>{return~s&&(e[s]=(r=e[s],(t=isnode(r)&&transforms[r[0]])?t(r):r),s=-1);var r,t};for(r of operators){for(s=-1,t=1;t<e.length;)o=e[t-2],l=e[t-1],n=e[t],a="string"==typeof l&&r[l],a&&!operator(n)&&t>1&&!operator(o)?1==r[l].length?(i(),t++):s==t-2&&o[0]==l?(o.push(n),e.splice(t-1,2)):(i(),e.splice(s=t-2,3,[l,e[s],n])):a&&!operator(n)?(e.splice(s=t-1,2,[l,n]),t--):(i(),t++);i()}return e.length>1?e:e[0]};return e=t()},evaluate=(e,r={})=>isnode(e)?(isnode(e[0])?evaluate(e[0]):"string"==typeof e[0]?r[e[0]]||operator(e[0],e.length-1):e[0])(...e.slice(1).map((e=>evaluate(e,r)))):"string"==typeof e?quotes[e[0]]?e.slice(1,-1):r[e]:e;export default e=>(e="string"==typeof e?parse(e):e,r=>evaluate(e,r));
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));