subscript 2.0.0 → 3.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 CHANGED
@@ -1,12 +1,11 @@
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 with common syntax 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, Swift, Objective C, Kotlin etc.<br/>
4
4
 
5
- * Everyone knows _subscript_ syntax
6
- * Any _subscript_ fragment can be copy-pasted to a target language and it will work
5
+ * Well-known syntax
6
+ * Any _subscript_ fragment can be copy-pasted to any target language
7
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 ([see performance](#performance))
9
- * Enables operators overloading
8
+ * It's very fast ([see performance](#performance))
10
9
  * Configurable & extensible
11
10
  * Trivial to use...
12
11
 
@@ -16,65 +15,33 @@ let fn = subscript(`a.b + c(d - 1)`)
16
15
  fn({ a: { b:1 }, c: x => x * 2, d: 3 }) // 5
17
16
  ```
18
17
 
19
- ## Useful in:
18
+ ## Motivation
20
19
 
21
- * templates (awesome match with [template parts](https://github.com/github/template-parts))
22
- * expressions evaluators (math, arithmetic)
23
- * subsets of languages (eg. jessie, justin) <!-- see sonr -->
24
- * prototyping language features (eg. pipe operator)
25
- * simulating languages (eg. glsl <!--, FORTRAN?, COBOL?-->)
26
- * sandboxes, playgrounds
27
- * safe, secure eval
28
- * custom DSL
20
+ _Subscript_ is designed to be useful for:
29
21
 
30
- ```html
31
- <!-- template-parts proposal -->
32
- <template id="timer">
33
- <time datetime="{{ date.toUTCString() }}">{{ date.toLocaleTimeString() }}</time>
34
- </template>
35
- ```
36
-
37
- ## Lispy tree
38
-
39
- It compiles code to lispy calltree (compatible with [frisk](https://npmjs.com/frisk)). Why?
22
+ * templates (perfect match with [template parts](https://github.com/github/template-parts))
23
+ * expressions evaluators, calculators
24
+ * subsets of languages (eg. [justin](#justin)) <!-- see sonr, mineural -->
25
+ * mocking language features (eg. pipe operator)
26
+ * sandboxes, playgrounds, safe eval
27
+ * custom DSL
40
28
 
41
- + minimal possible AST overhead
42
- + clear operators precedence
43
- + overloading operators by context
44
- + simple manual evaluation, debugging
45
- + conventional form
46
- + one-liner docs...
29
+ [_Jsep_](https://github.com/EricSmekens/jsep) is generally fine for the listed tasks, unless you design a tiny module and prefer to keep dependencies as small as possible.
30
+ _Subscript_ has [2.5kb](https://npmfs.com/package/subscript/3.0.0/subscript.min.js) footprint vs [11.4kb](https://npmfs.com/package/jsep/1.2.0/dist/jsep.min.js) _jsep_, with same or better performance. It also has more open API and generates lispy calltree (compatible with [frisk](https://npmjs.com/frisk)), compared to esprima AST: minimal possible overhead, clear precedence, overloading by context, manual evaluation, debugging, conventional form, one-liner docs:
47
31
 
48
32
  ```js
49
33
  import {evaluate} from 'subscript.js'
50
- evaluate(['+', ['*', 'min', 60], '"sec"'], { min: 5 }) // min*60 + "sec" == "300sec"
51
- ```
52
34
 
53
- ## Core primitives
54
-
55
- By default subscript reserves:
56
-
57
- * `[]`, `()` groups
58
- * `true`, `false`, `null` literals
59
- * `"` quotes.
60
-
61
- All primitives are extensible via `parse.literal`, `parse.quote`, `parse.group`, `parse.comment` dicts.
62
-
63
- ```js
64
- import { parse } from 'subscript.js'
35
+ evaluate(['+', ['*', 'min', 60], '"sec"'], { min: 5 }) // min*60 + "sec" == "300sec"
36
+ ```
65
37
 
66
- parse.quote["'"] = "'"
67
- parse.comment["//"] = "\n"
68
-
69
- parse(`'a' + 'b' // concat`) // ['+', 'a':String, 'b':String]
70
- ```
71
38
 
72
39
  ## Operators
73
40
 
74
41
  Default operators include common operators for the listed languages in the following precedence:
75
42
 
76
- * `.`, `(…)`, `…(…)`, `…[…]`
77
- * `! + - ++ --` prefix
43
+ * `++ --` unary postfix
44
+ * `! + - ++ --` unary prefix
78
45
  * `* / %`
79
46
  * `+ -`
80
47
  * `<< >> >>>`
@@ -86,7 +53,7 @@ Default operators include common operators for the listed languages in the follo
86
53
  * `&&`
87
54
  * `||`
88
55
 
89
- All other operators can be extended via `parse.binary`, `parse.prefix`, `parse.postfix` and `evaluate.operator`.
56
+ All other operators can be extended via `parse.binary`, `parse.unary` and `evaluate.operator`.
90
57
 
91
58
  ```js
92
59
  import { parse, evaluate } from 'subscript.js'
@@ -107,24 +74,21 @@ let tree = parse(`
107
74
  evaluate(tree, { Math, map, take, interval, gaussian })
108
75
  ```
109
76
 
110
- ## Transforms
111
-
112
- Transform rules are applied to raw parsed calltree groups, eg.:
77
+ ## Extending
113
78
 
114
- * Flatten calls `a(b,c)(d)` `['(', 'a', [',', 'b', 'c'], 'd']` → `[['a', 'b', 'c'], 'd']`
115
- * Property access `a.b.c` → `['.', 'a', 'b', 'c']` → `['.', 'a', '"b"', '"c"']`
79
+ By default subscript detects the following tokens:
116
80
 
117
- That can be used to organize ternary/combining operators:
81
+ * `"` strings
82
+ * `1.2e+3` floats
83
+ * `true`, `false`, `null` literals
84
+ * `()` expression groups or fn calls
85
+ * `.`, `[]` property access
118
86
 
119
- ```js
120
- import { parse, evaluate } from 'subscript.js'
87
+ Literals can be extended via `parse.literal` dict.
121
88
 
122
- evaluate.operator['?:'] = (a,b) => a ? b : c
123
- parse.binary[':'] = parse.binary['?'] = 5
124
- parse.map[':'] = node => node[1][0]=='?' ? ['?:',node[1][1],node[1][2],node[2]] : node // [:, [?, a, b], c] → [?:, a, b, c]
89
+ Token parsers are extensible via `parse.token` list, can be added support of _regex_, _array_, _object_, _interpolated string_ and others.
125
90
 
126
- parse('a ? b : c') // ['?:', 'a', 'b', 'c']
127
- ```
91
+ Postfix parsers are applied to parsed tokens and can be used to provide _property chains_, _function calls_, _postfix operators_, _token mapping_, _ternary operators_ and so on. They're extensible via `parse.postfix`.
128
92
 
129
93
 
130
94
  ## Justin
@@ -134,20 +98,24 @@ It adds support for:
134
98
 
135
99
  + `**` binary operator
136
100
  + `~` unary operator
101
+ + `'` strings
137
102
  + `?:` ternary operator
138
103
  + `[...]` Array literal
139
104
  + `{...}` Object literal
140
105
  + `in` binary operator
141
106
  + `;` expression separator
142
- + `//, /* */` comments
143
- + `undefined` literal
107
+ + unary word operators
108
+ <!-- + `//, /* */` comments -->
109
+ <!-- + `undefined` literal -->
110
+ <!-- + `?` chaining operator -->
144
111
  <!-- + `...x` unary operator -->
145
112
  <!-- + strings interpolation -->
146
113
 
147
114
  ```js
148
- import { parse } from 'subscript/justin.js'
115
+ import { parse, evaluate } from 'subscript/justin.js'
149
116
 
150
- let tree = parse('{x:1, "y":2+2}["x"]') // ['[', {x:1, y: ['+', 2, 2]}, '"x"']
117
+ let xy = parse('{ x: 1, "y": 2+2 }["x"]') // ['[', {x:1, y: ['+', 2, 2]}, '"x"']
118
+ evaluate(xy) // 1
151
119
  ```
152
120
 
153
121
  <!--
@@ -155,6 +123,27 @@ let tree = parse('{x:1, "y":2+2}["x"]') // ['[', {x:1, y: ['+', 2, 2]}, '"x"']
155
123
 
156
124
  These are custom DSL operators snippets for your inspiration:
157
125
 
126
+
127
+ ```html
128
+ template-parts proposal
129
+ <template id="timer">
130
+ <time datetime="{{ date.toUTCString() }}">{{ date.toLocaleTimeString() }}</time>
131
+ </template>
132
+ ```
133
+
134
+ // a.b.c
135
+ // (node, c) => c === PERIOD ? (index++, space(), ['.', node, '"'+id()+'"']) : node,
136
+
137
+ // a[b][c]
138
+ // (node, c) => c === OBRACK ? (index++, node=['.', node, expr(CBRACK)], index++, node) : node,
139
+
140
+ // a(b)(c)
141
+ // (node, c, arg) => c === OPAREN ? (
142
+ // index++, arg=expr(CPAREN),
143
+ // node = Array.isArray(arg) && arg[0]===',' ? (arg[0]=node, arg) : arg == null ? [node] : [node, arg],
144
+ // index++, node
145
+ // ) : node,
146
+
158
147
  <details>
159
148
  <summary>Keyed arrays <code>[a:1, b:2, c:3]</code></summary>
160
149
 
@@ -254,6 +243,16 @@ These are custom DSL operators snippets for your inspiration:
254
243
  ```
255
244
 
256
245
  </details>
246
+
247
+ like versions, units, hashes, urls, regexes etc
248
+
249
+ 2a as `2*a`
250
+
251
+ string interpolation ` ${} 1 ${} `
252
+
253
+ keyed arrays? [a:1, b:2, c:3]
254
+
255
+ Examples: sonr, template-parts, neural-chunks
257
256
  -->
258
257
 
259
258
  ## Performance
@@ -264,11 +263,11 @@ Subscript shows relatively good performance within other evaluators:
264
263
  // 1 + (a * b / c % d) - 2.0 + -3e-3 * +4.4e4 / f.g[0] - i.j(+k == 1)(0)
265
264
  // parse 30k times
266
265
 
267
- expr-eval: 712 ms
268
- subscript: 336 ms
269
- jsep: 278 ms
266
+ subscript: ~280 ms
267
+ jsep: ~281 ms
268
+ expr-eval: ~480 ms
270
269
  jexl: ~1200 ms
271
- new Function: 1466 ms
270
+ new Function: ~1400 ms
272
271
  ```
273
272
 
274
273
  ## See also
@@ -280,6 +279,6 @@ new Function: 1466 ms
280
279
  * [expression-eval](https://github.com/donmccurdy/expression-eval)
281
280
  * [jsep](https://github.com/EricSmekens/jsep)
282
281
  * [string-math](https://github.com/devrafalko/string-math)
283
-
282
+ * [nerdamer](https://github.com/jiggzson/nerdamer)
284
283
 
285
284
  <p align=center>🕉</p>
package/evaluate.js ADDED
@@ -0,0 +1,57 @@
1
+ export const isCmd = a => Array.isArray(a) && (typeof a[0] === 'string' || isCmd(a[0])),
2
+
3
+ // calltree → result
4
+ evaluate = (s, ctx={}, c, op) => {
5
+ if (isCmd(s)) {
6
+ c = s[0]
7
+ if (typeof c === 'string') op = operator[c]
8
+ c = op || evaluate(c, ctx) // [[a,b], c]
9
+ if (typeof c !== 'function') return c
10
+
11
+ return c.call(...s.map(a => evaluate(a,ctx)))
12
+ }
13
+ if (s && typeof s === 'string')
14
+ return s[0] === '"' ? s.slice(1,-1)
15
+ : s[0]==='@' ? s.slice(1)
16
+ : s in ctx ? ctx[s] : s
17
+
18
+ return s
19
+ },
20
+
21
+ // op evaluators
22
+ // multiple args allows shortcuts, lisp compatible, easy manual eval, functions anyways take multiple arguments
23
+ operator = evaluate.operator = {
24
+ '!':a=>!a,
25
+ '++':a=>++a,
26
+ '--':a=>--a,
27
+
28
+ '.':(...a)=>a.reduce((a,b)=>a?a[b]:a),
29
+
30
+ '%':(...a)=>a.reduce((a,b)=>a%b),
31
+ '/':(...a)=>a.reduce((a,b)=>a/b),
32
+ '*':(...a)=>a.reduce((a,b)=>a*b),
33
+
34
+ '+':(...a)=>a.reduce((a,b)=>a+b),
35
+ '-':(...a)=>a.length < 2 ? -a : a.reduce((a,b)=>a-b),
36
+
37
+ '>>>':(a,b)=>a>>>b,
38
+ '>>':(a,b)=>a>>b,
39
+ '<<':(a,b)=>a<<b,
40
+
41
+ '>=':(a,b)=>a>=b,
42
+ '>':(a,b)=>a>b,
43
+ '<=':(a,b)=>a<=b,
44
+ '<':(a,b)=>a<b,
45
+
46
+ '!=':(a,b)=>a!=b,
47
+ '==':(a,b)=>a==b,
48
+
49
+ '&':(a,b)=>a&b,
50
+ '^':(a,b)=>a^b,
51
+ '|':(a,b)=>a|b,
52
+ '&&':(...a)=>a.every(Boolean),
53
+ '||':(...a)=>a.some(Boolean),
54
+ ',':(...a)=>a.reduce((a,b)=>(a,b))
55
+ }
56
+
57
+ export default evaluate
package/justin.js CHANGED
@@ -1,68 +1,97 @@
1
1
  // justin lang https://github.com/endojs/Jessie/issues/66
2
- import {binary, unary, transforms, groups, comments, quotes, literals} from './subscript.js'
2
+ import {evaluate, operator} from './evaluate.js'
3
+ import {parse, binary, unary, postfix, token, literal,
4
+ code, char, skip, space, expr} from './parse.js'
3
5
 
4
6
  // undefined
5
- literals['undefined'] = undefined
7
+ literal['undefined'] = undefined
8
+
9
+ // "' with /
10
+ token[2] = (q, qc, c, str) => {
11
+ if (q !== 34 && q !== 39) return
12
+ qc = char(), skip(), str = ''
13
+ while (c=code(), c-q) {
14
+ if (c === 92) skip(), str += escape[char()] || char(); else str+=char()
15
+ skip()
16
+ }
17
+ return skip(), qc + str + qc
18
+ }
19
+ const escape = {n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', v:'\v'}
20
+
21
+ // unary word
22
+ postfix.push((node, prec) => typeof node === 'string' && (prec=unary[node]) ? [node, expr(prec)] : node)
23
+
24
+ // detect custom operators
25
+ token[3] = name => (name = skip(c =>
26
+ (
27
+ (c >= 48 && c <= 57) || // 0..9
28
+ (c >= 65 && c <= 90) || // A...Z
29
+ (c >= 97 && c <= 122) || // a...z
30
+ c == 36 || c == 95 || // $, _,
31
+ c >= 192 // any non-ASCII
32
+ ) && !binary[String.fromCharCode(c)]
33
+ )),
34
+
6
35
 
7
36
  // **
8
- binary.splice(2,0,{'**': (...args)=>args.reduceRight((a,b)=>Math.pow(b,a))})
37
+ binary['**'] = 16
38
+ operator['**'] = (...args)=>args.reduceRight((a,b)=>Math.pow(b,a))
9
39
 
10
40
  // ~
11
- unary[1]['~'] = a=>~a
41
+ unary['~'] = 17
42
+ operator['~'] = a=>~a
12
43
 
13
44
  // ...
14
45
  // unary[1]['...']=true
15
46
 
16
47
  // ;
17
- binary[binary.length-1][';'] = binary[binary.length-1][',']
48
+ binary[';'] = 1
18
49
 
19
50
  // ?:
20
- binary.splice(binary.length-2,0, {':':(a,b)=>[a,b], '?':(a,b)=>a??b, '?:':(a,b,c)=>a?b:c})
21
- transforms[':'] = node => node[1][0]=='?' ? ['?:',node[1][1],node[1][2],node[2]] : node // [:, [?, a, b], c] → [?:, a, b, c]
22
-
23
- // {}
24
- groups['{']='}'
51
+ operator['?:']=(a,b,c)=>a?b:c
52
+ postfix.push(node => {
53
+ let a, b
54
+ if (code() !== 63) return node
55
+ skip(), space(), a = expr(-1,58)
56
+ if (code() !== 58) return node
57
+ skip(), space(), b = expr()
58
+ return ['?:',node, a, b]
59
+ })
25
60
 
26
61
  // /**/, //
27
- comments['/*']='*/'
28
- comments['//']='\n'
62
+ // comments['/*']='*/'
63
+ // comments['//']='\n'
29
64
 
30
65
  // in
31
- binary[5]['in'] = (a,b)=>a in b
32
- transforms['in'] = n => (n[1]=`"${n[1]}"`, n)
33
-
34
- // {}, []
35
- unary[0]['['] = binary[0]['['] = (...args) => Array(...args)
36
- transforms['['] = n => n.length > 2 ? ['.',n[1],n[2]] :
37
- n[1]==='' ? [n[0]] : [n[0], ...(n[1][0]===','?n[1].slice(1).map(x=>x===''?undefined:x):[n[1]])]
38
-
39
- groups['{']='}'
40
- unary[0]['{'] = binary[binary.length-2]['{'] = (...args)=>Object.fromEntries(args)
41
- binary[binary.length-2][':']=(a,b)=>[a,b]
42
- transforms['{'] = (s, args) => {
43
- if (s[1]==='') args = []
44
- else if (s[1][0]==':') args = [s[1]]
45
- else if (s[1][0]==',') args = s[1].slice(1)
66
+ evaluate.operator['in'] = (a,b)=>a in b
67
+ parse.postfix.unshift(node => (char(2) === 'in' ? (skip(2), ['in', '"' + node + '"', expr()]) : node))
68
+
69
+ // []
70
+ operator['['] = (...args) => Array(...args)
71
+ token.push((node, arg) =>
72
+ code() === 91 ?
73
+ (
74
+ skip(), arg=expr(-1,93),
75
+ node = arg==null ? ['['] : arg[0] === ',' ? (arg[0]='[',arg) : ['[',arg],
76
+ skip(), node
77
+ ) : null
78
+ )
79
+
80
+ // {}
81
+ binary[':'] = 2
82
+ token.unshift((node) => code() === 123 ? (skip(), node = map(['{',expr(-1,125)]), skip(), node) : null)
83
+ operator['{'] = (...args)=>Object.fromEntries(args)
84
+ operator[':'] = (a,b)=>[a,b]
85
+
86
+ const map = (n, args) => {
87
+ if (n[1]==null) args = []
88
+ else if (n[1][0]==':') args = [n[1]]
89
+ else if (n[1][0]==',') args = n[1].slice(1)
46
90
  return ['{', ...args]
47
91
  }
48
92
 
49
- // groups ['{']='}'
50
- // binary.unshift({'[':a=>Array(...a), '{':a=>Object.fromEntries(a)})
51
- // transforms['['] = s => s.length > 2 ? s
52
- // : s[1] === undefined
53
- // ? [Array]
54
- // // ['[',[',',a,[',',b],c]] → ['[',[',',a,,b,c]]
55
- // : [Array].concat(isnode(s[1]) ? s[1].slice(1).reduce((a,b)=>a.push(...(isnode(b)&&b[0]==','?(b[0]=undefined,b):[b]))&&a,[])
56
- // : s[1])
57
- // transforms['{'] = (s,entries) => {
58
- // if (s[1]===undefined) return {}
59
- // if (s[1][0]==':') entries = [s[1].slice(1)]
60
- // else entries = s[1].slice(1).map(n=>n.slice(1))
61
- // entries = entries.map(n=>quotes[n[0][0]]?[n[0].slice(1,-1),n[1]]:n)
62
- // return Object.fromEntries(entries)
63
- // }
64
93
 
65
94
  // TODO: strings interpolation
66
95
 
67
96
  export { default } from './subscript.js';
68
- export * from './subscript.js'
97
+ export { parse, evaluate }
package/justin.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,o,s)=>{if(e&&"string"==typeof e&&!quotes[e[0]])for(s=operators.length;s--;)if((o=operators[s][e])&&1==r?o.length==r:o)return o},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 o=(n,a="",l,i,p,c,u,f=[])=>{const d=e=>{""!==a&&f.push(l?parseFloat(a):a in literals?literals[a]:a),e&&f.push(e),l=a=i=""};for(;r<=e.length;a+=i)if(c=(p=(i=e[r++])+e[r])+e[r+1],!l||"e"!=i&&"E"!=i)if(space.includes(i))d();else if(u=comments[p]||comments[i])d(),s(e,u);else if(u=quotes[c]||quotes[p]||quotes[i])a=i+e.slice(r,s(e,u)),d();else if(u=blocks[i])d(i),f.push(o(u));else if(!a&&i>="0"&&i<="9"||"."==i&&e[r]>="0"&&e[r]<="9")l=1;else{if(i==n)return d(),t(f);(operator(i=c)||operator(i=p)||operator(i=i[0]))&&(r+=i.length-1,(i.toLowerCase()==i.toUpperCase()||!a&&space.includes(e[r]))&&d(i))}else i+=e[r++]},s=(e,o,s)=>r=(r=e.indexOf(o,r))<0?e.length:r+o.length,t=e=>{if(!e.length)return;let r,o,s,t,n,a,l;const i=()=>{return~s&&(e[s]=(r=e[s],(o=isnode(r)&&transforms[r[0]])?o(r):r),s=-1);var r,o};for(r of operators){for(s=-1,o=1;o<e.length;)t=e[o-2],a=e[o-1],n=e[o],l="string"==typeof a&&r[a],l&&!operator(n)&&o>1&&!operator(t)?1==r[a].length?(i(),o++):s==o-2&&t[0]==a?(t.push(n),e.splice(o-1,2)):(i(),e.splice(s=o-2,3,[a,e[s],n])):l&&!operator(n)?(e.splice(s=o-1,2,[a,n]),o--):(i(),o++);i()}return e.length>1?e:e[0]};return e=o()},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));operators.splice(2,0,{"**":(...e)=>e.reduceRight(((e,r)=>Math.pow(r,e)))}),operators[1]["~"]=e=>~e,operators[operators.length-1][";"]=operators[operators.length-1][","],operators.splice(operators.length-2,0,{":":(e,r)=>[e,r],"?":(e,r)=>e??r,"?:":(e,r,o)=>e?r:o}),transforms[":"]=e=>"?"==e[1][0]?["?:",e[1][1],e[1][2],e[2]]:e,blocks["{"]="}",comments["/*"]="*/",comments["//"]="\n",operators[5].in=(e,r)=>e in r,transforms.in=e=>(e[1]=`"${e[1]}"`,e),blocks["{"]="}",operators.unshift({"[":e=>Array(...e),"{":e=>Object.fromEntries(e)}),transforms["["]=e=>e.length>2?e:void 0===e[1]?[Array]:[Array].concat(isnode(e[1])?e[1].slice(1).reduce(((e,r)=>e.push(...isnode(r)&&","==r[0]?(r[0]=void 0,r):[r])&&e),[]):e[1]),transforms["{"]=(e,r)=>void 0===e[1]?{}:(r=(r=":"==e[1][0]?[e[1].slice(1)]:e[1].slice(1).map((e=>e.slice(1)))).map((e=>quotes[e[0][0]]?[e[0].slice(1,-1),e[1]]:e)),Object.fromEntries(r));
1
+ var r,e,n,t,l=r=>Array.isArray(r)&&("string"==typeof r[0]||l(r[0])),o=(r,e={},n,t)=>l(r)?("string"==typeof(n=r[0])&&(t=u[n]),"function"!=typeof(n=t||o(n,e))?n:n.call(...r.map((r=>o(r,e))))):r&&"string"==typeof r?'"'===r[0]?r.slice(1,-1):"@"===r[0]?r.slice(1):r in e?e[r]:r:r,u=o.operator={"!":r=>!r,"++":r=>++r,"--":r=>--r,".":(...r)=>r.reduce(((r,e)=>r&&r[e])),"%":(...r)=>r.reduce(((r,e)=>r%e)),"/":(...r)=>r.reduce(((r,e)=>r/e)),"*":(...r)=>r.reduce(((r,e)=>r*e)),"+":(...r)=>r.reduce(((r,e)=>r+e)),"-":(...r)=>r.length<2?-r:r.reduce(((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,"&&":(...r)=>r.every(Boolean),"||":(...r)=>r.some(Boolean),",":(...r)=>r.reduce(((r,e)=>e))},i=o,a=(t,l)=>(e=t,r=n=0,l=c(),r<e.length?p():l),s=()=>e.charCodeAt(r),f=(n=1)=>e.substr(r,n),p=(e="Bad syntax "+f())=>{throw Error(e+" at "+r)},d=(n=1,t=r)=>{if("number"==typeof n)r+=n;else for(;n(s());)++r>e.length&&p("End by "+n);return r>t?e.slice(t,r):null},h=()=>{for(;s()<=32;)r++},y=(e,t,l,o=3)=>{if(r&&n&&n[3]===r)return n;for(;o;)if(null!=(l=e[t=f(o--)]))return n=[t,l,t.length,r]},c=(e=-1,n)=>{h();let l,o,u,i,a=s(),d=0,g=r;if(n){if(a===n)return;i=t,t=n}for(;g===r&&d<b.length;)o=b[d++](a);if(g===r)(l=y(m))&&(r+=l[2],o=[l[0],c(l[1])]);else for(h(),a=s(),d=0;d<A.length;)(u=A[d](o,a))!==o?(o=u,h(),a=s()):d++;for(;a=s()&&a!==t&&(l=y(w))&&l[1]>e;){o=[l[0],o];do{r+=l[2],o.push(c(l[1]))}while(f(l[2])===l[0]);h()}return n&&(t=s()!==n?p("Unclosed paren"):i),o},g=r=>d((r=>r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||36==r||95==r||r>=192)),b=a.token=[r=>{if(r=d((r=>r>=48&&r<=57||46===r)))return(69===s()||101===s())&&(r+=d(2)+d((r=>r>=48&&r<=57))),isNaN(r=parseFloat(r))?p("Bad number"):r},(e,n)=>40===e?(r++,n=c(-1,41),r++,n):null,(e,n)=>34===e?(n=f(),r++,n+d((r=>r-e))+(r++,n)):null,g],v=a.literal={true:!0,false:!1,null:null},A=a.postfix=[(n,t,l)=>(46===t?(r++,h(),n=[".",n,'"'+g()+'"']):91===t?(r++,n=[".",n,c(-1,93)],r++):40===t?(r++,l=c(-1,41),n=Array.isArray(l)&&","===l[0]?(l[0]=n,l):null==l?[n]:[n,l],r++):43!==t&&45!==t||e.charCodeAt(r+1)!==t?"string"==typeof n&&v.hasOwnProperty(n)&&(n=v[n]):n=[d(2),n],n)],m=a.unary={"-":17,"!":17,"+":17,"++":17,"--":17},w=a.binary={",":1,"||":6,"&&":7,"|":8,"^":9,"&":10,"==":11,"!=":11,"<":12,">":12,"<=":12,">=":12,"<<":13,">>":13,">>>":13,"+":14,"-":14,"*":15,"/":15,"%":15},x=a,B=r=>(r="string"==typeof r?x(r):r,e=>i(r,e));v.undefined=void 0,b[2]=(r,e,n,t)=>{if(34===r||39===r){for(e=f(),d(),t="";(n=s())-r;)92===n?(d(),t+=C[f()]||f()):t+=f(),d();return d(),e+t+e}};var C={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"};A.push(((r,e)=>"string"==typeof r&&(e=m[r])?[r,c(e)]:r)),b[3]=r=>d((r=>(r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||36==r||95==r||r>=192)&&!w[String.fromCharCode(r)])),w["**"]=16,u["**"]=(...r)=>r.reduceRight(((r,e)=>Math.pow(e,r))),m["~"]=17,u["~"]=r=>~r,w[";"]=1,u["?:"]=(r,e,n)=>r?e:n,A.push((r=>{let e,n;return 63!==s()||(d(),h(),e=c(-1,58),58!==s())?r:(d(),h(),n=c(),["?:",r,e,n])})),o.operator.in=(r,e)=>r in e,a.postfix.unshift((r=>"in"===f(2)?(d(2),["in",'"'+r+'"',c()]):r)),u["["]=(...r)=>Array(...r),b.push(((r,e)=>91===s()?(d(),r=null==(e=c(-1,93))?["["]:","===e[0]?(e[0]="[",e):["[",e],d(),r):null)),w[":"]=2,b.unshift((r=>123===s()?(d(),r=E(["{",c(-1,125)]),d(),r):null)),u["{"]=(...r)=>Object.fromEntries(r),u[":"]=(r,e)=>[r,e];var E=(r,e)=>(null==r[1]?e=[]:":"==r[1][0]?e=[r[1]]:","==r[1][0]&&(e=r[1].slice(1)),["{",...e]);export{B as default,o as evaluate,a as parse};
package/package.json CHANGED
@@ -1,18 +1,28 @@
1
1
  {
2
2
  "name": "subscript",
3
- "version": "2.0.0",
3
+ "version": "3.0.3",
4
4
  "description": "Microlanguage with common syntax for JS/C++/Python/Rust",
5
5
  "main": "subscript.js",
6
+ "type": "module",
6
7
  "files": [
8
+ "parse.js",
9
+ "evaluate.js",
10
+ "subscript.js",
7
11
  "subscript.min.js",
8
12
  "justin.js",
9
13
  "justin.min.js"
10
14
  ],
11
15
  "directories": {
12
- "lib": "lib"
16
+ "lib": "lib",
17
+ "src": "src",
18
+ "test": "test"
13
19
  },
14
20
  "scripts": {
15
- "test": "echo \"Error: no test specified\" && exit 1"
21
+ "build": "esbuild subscript.js --bundle --format=esm --minify --outfile=subscript.min.js",
22
+ "minify": "terser subscript.min.js -o subscript.min.js --module -c passes=3 -m",
23
+ "build-justin": "esbuild justin.js --bundle --format=esm --minify --outfile=justin.min.js",
24
+ "minify-justin": "terser justin.min.js -o justin.min.js --module -c passes=3 -m",
25
+ "test": "node test"
16
26
  },
17
27
  "repository": {
18
28
  "type": "git",
@@ -33,12 +43,18 @@
33
43
  "dsl",
34
44
  "json",
35
45
  "calculator",
36
- "calc"
46
+ "calc",
47
+ "lisp",
48
+ "frisk"
37
49
  ],
38
50
  "author": "Dmitry Iv.",
39
51
  "license": "ISC",
40
52
  "bugs": {
41
53
  "url": "https://github.com/spectjs/subscript/issues"
42
54
  },
43
- "homepage": "https://github.com/spectjs/subscript#readme"
55
+ "homepage": "https://github.com/spectjs/subscript#readme",
56
+ "devDependencies": {
57
+ "esbuild": "^0.13.14",
58
+ "terser": "^5.10.0"
59
+ }
44
60
  }
package/parse.js ADDED
@@ -0,0 +1,134 @@
1
+ const PERIOD = 46, OPAREN = 40, CPAREN = 41, OBRACK = 91, CBRACK = 93, PLUS = 43, MINUS = 45
2
+
3
+ export let index, current, lastOp, end
4
+
5
+ export const parse = (str, tree) => (current=str, index=lastOp=0, tree=expr(), index < current.length ? err() : tree),
6
+
7
+ // ------------ util
8
+ code = () => current.charCodeAt(index), // current char code
9
+ char = (n=1) => current.substr(index, n), // skip n chars
10
+ err = (msg='Bad syntax '+char()) => { throw Error(msg + ' at ' + index) },
11
+ skip = (is=1, from=index) => { // consume N or until condition matches
12
+ if (typeof is === 'number') index += is
13
+ else while (is(code())) ++index > current.length && err('End by ' + is) // 1 + true === 2;
14
+ return index > from ? current.slice(from, index) : null
15
+ },
16
+ space = () => { while (code() <= 32) index++ },
17
+
18
+ // ------------- expr
19
+ // consume operator
20
+ operator = (ops, op, prec, l=3) => {
21
+ // memoize by index - saves 20% to perf
22
+ if (index && lastOp && lastOp[3] === index) return lastOp
23
+
24
+ // ascending lookup is faster for 1-char operators, longer for 2+ char ops, so we use descending
25
+ while (l) if ((prec=ops[op=char(l--)])!=null) return lastOp = [op, prec, op.length, index] //opinfo
26
+ },
27
+
28
+ expr = (prec=-1, curEnd) => {
29
+ space()
30
+
31
+ let cc = code(), op, node, i=0, mapped, from=index, prevEnd
32
+
33
+ if (curEnd) if (cc === curEnd) return; else prevEnd = end, end = curEnd // global end marker saves operator lookups
34
+
35
+ // parse node by token parsers (direct loop is faster than token.find)
36
+ while (from===index && i < token.length) node = token[i++](cc)
37
+
38
+ // unary prefix
39
+ if (from===index) (op = operator(unary)) && (index += op[2], node = [op[0], expr(op[1])])
40
+
41
+ // postfix handlers
42
+ else {
43
+ for (space(), cc=code(), i=0; i < postfix.length;)
44
+ if ((mapped = postfix[i](node, cc)) !== node) node = mapped, space(), cc=code(); else i++
45
+ }
46
+ // ALT: seems to be slower
47
+ // else do {space(), cc=code()} while (postfix.find((parse, mapped) => (mapped = parse(node, cc)) !== node && (node = mapped)))
48
+
49
+ // consume binary expression for current precedence or higher
50
+ while (cc = code() && (cc !== end) && (op = operator(binary)) && op[1] > prec) {
51
+ node = [op[0], node]
52
+ // consume same-op group, do..while both saves op lookups and space
53
+ do { index += op[2], node.push(expr(op[1])) } while (char(op[2]) === op[0])
54
+ space()
55
+ }
56
+
57
+ if (curEnd) end = code() !== curEnd ? err('Unclosed paren') : prevEnd
58
+ // if (node == null) err('Missing argument')
59
+
60
+ return node;
61
+ },
62
+
63
+ // ------------------- tokens
64
+ // 1.2e+3, .5 - fast & small version, but consumes corrupted nums as well
65
+ float = (number) => {
66
+ if (number = skip(c => (c >= 48 && c <= 57) || c === PERIOD)) {
67
+ if (code() === 69 || code() === 101) number += skip(2) + skip(c => c >= 48 && c <= 57)
68
+ return isNaN(number = parseFloat(number)) ? err('Bad number') : number
69
+ }
70
+ },
71
+
72
+ // "a"
73
+ string = (q, qc) => q === 34 ? (qc = char(), index++, qc) + skip(c => c-q) + (index++, qc) : null,
74
+
75
+ // (...exp)
76
+ group = (c, node) => c === OPAREN ? (index++, node = expr(-1,CPAREN), index++, node) : null,
77
+
78
+ // var or literal
79
+ id = name => (name = skip(c =>
80
+ (
81
+ (c >= 48 && c <= 57) || // 0..9
82
+ (c >= 65 && c <= 90) || // A...Z
83
+ (c >= 97 && c <= 122) || // a...z
84
+ c == 36 || c == 95 || // $, _,
85
+ c >= 192 // any non-ASCII
86
+ )
87
+ )),
88
+
89
+ // ----------- config
90
+ token = parse.token = [ float, group, string, id ],
91
+
92
+ literal = parse.literal = {true:true, false:false, null:null},
93
+
94
+ postfix = parse.postfix = [
95
+ // postfix parsers merged into 1 for performance & compactness
96
+ (node, cc, arg) => {
97
+ // a.b[c](d)
98
+ if (cc === PERIOD) index++, space(), node = ['.', node, '"'+id()+'"']
99
+ else if (cc === OBRACK) index++, node = ['.', node, expr(-1,CBRACK)], index++
100
+ else if (cc === OPAREN)
101
+ index++, arg=expr(-1,CPAREN),
102
+ node = Array.isArray(arg) && arg[0]===',' ? (arg[0]=node, arg) : arg == null ? [node] : [node, arg],
103
+ index++
104
+
105
+ // a++, a--
106
+ else if ((cc===0x2b || cc===0x2d) && current.charCodeAt(index+1)===cc) node = [skip(2), node]
107
+
108
+ // literal
109
+ else if (typeof node === 'string' && literal.hasOwnProperty(node)) node = literal[node]
110
+
111
+ return node
112
+ }
113
+ ],
114
+
115
+ unary = parse.unary = {
116
+ '-': 17,
117
+ '!': 17,
118
+ '+': 17,
119
+ '++': 17,
120
+ '--': 17
121
+ },
122
+
123
+ binary = parse.binary = {
124
+ ',': 1,
125
+ '||': 6, '&&': 7, '|': 8, '^': 9, '&': 10,
126
+ '==': 11, '!=': 11,
127
+ '<': 12, '>': 12, '<=': 12, '>=': 12,
128
+ '<<': 13, '>>': 13, '>>>': 13,
129
+ '+': 14, '-': 14,
130
+ '*': 15, '/': 15, '%': 15
131
+ }
132
+
133
+
134
+ export default parse
package/subscript.js CHANGED
@@ -1,177 +1,5 @@
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 => isDigit(c) || isIdentifierStart(c),
7
- isSpace = c => c <= 32,
8
- isCmd = (a,op) => Array.isArray(a) && a.length && a[0] && (op ? a[0]===op : typeof a[0] === 'string' || isCmd(a[0])),
9
- map = (node, t) => isCmd(node) ? (t = parse.map[node[0]], t?t(node):node) : node
10
-
11
- const parse = (expr, index=0, curOp, curEnd) => {
12
- const char = (n=1) => expr.substr(index, n), // get next n chars (as fast as expr[index])
13
- code = () => expr.charCodeAt(index),
14
-
15
- err = msg => {throw Error(msg + ' at ' + index)},
16
-
17
- // skip index until condition matches
18
- skip = is => { while (index < expr.length && is(code())) index++ },
19
-
20
- // skip index, return skipped part
21
- consume = is => expr.slice(index, (skip(is), index)),
22
-
23
- // consume operator that resides within current group by precedence
24
- consumeOp = (ops, op, prec, l=3) => {
25
- if (index >= expr.length) return
26
-
27
- // memoize by index - saves 20% to perf
28
- if (index && curOp[3] === index) return curOp
29
-
30
- // don't look up for end characters - saves 5-10% to perf
31
- if (curEnd && curEnd === char(curEnd.length)) return
32
-
33
- // ascending lookup is faster 1-char operators, longer for 2+ char ops
34
- // for (let i=0, prec0, op0; i < l;) if (prec0=ops[op0=char(++i)]) prec=prec0,op=op0; else if (prec) return opinfo(op, prec)
35
- while (l) if ((prec=ops[op=char(l--)])!=null) return curOp = [op, prec, parse.group[op], index] //opinfo
36
- },
37
-
38
- // `foo.bar(baz)`, `1`, `"abc"`, `(a % 2)`
39
- consumeGroup = (group, end=curEnd) => {
40
- index += group[0].length // group always starts with an operator +-b, a(b, +(b, a+b+c, so we skip it
41
- if (group[2]) curEnd = group[2] // also we write root end marker
42
-
43
- skip(isSpace);
44
-
45
- let cc = code(), op, c = char(),
46
- node='' // indicates "nothing", or "empty", as in [a,,b] - impossible to get as result of parsing
47
-
48
- // `.` can start off a numeric literal
49
- if (isDigit(cc)) node = parseInt(consume(isDigit));
50
- else if (parse.quote[c]) index++, node = c + consume(c=>c!=cc) + c, index++
51
- else if (isIdentifierStart(cc)) node = (node = consume(isIdentifierPart)) in parse.literal ? parse.literal[node] : node
52
- // unaries can't be mixed in binary expressions loop due to operator names conflict, must be parsed before
53
- else if (op = consumeOp(parse.prefix)) node = map([op[0], consumeGroup(op)])
54
-
55
- skip(isSpace)
56
-
57
- // consume expression for current precedence or group (== highest precedence)
58
- while ((op = consumeOp(parse.binary)) && (group[2] || op[1] < group[1])) {
59
- node = [op[0], node, consumeGroup(op)]
60
- // consume same-op group, that also saves op lookups
61
- while (char(op[0].length) === op[0]) node.push(consumeGroup(op))
62
- node = map(node)
63
- skip(isSpace)
64
- }
65
-
66
- // if group has end operator eg + a ) or + a ]
67
- if (group[2]) index+=group[2].length, curEnd=end
68
-
69
- return node;
70
- }
71
-
72
- return consumeGroup(curOp = ['', 108])
73
- },
74
-
75
- // calltree → result
76
- evaluate = (s, ctx={}, c, op) => {
77
- if (isCmd(s)) {
78
- c = s[0]
79
- if (typeof c === 'string') op = evaluate.operator[c]
80
- c = op || evaluate(c, ctx) // [[a,b], c]
81
- if (typeof c !== 'function') return c
82
-
83
- return c.call(...s.map(a=>evaluate(a,ctx)))
84
- }
85
- if (s && typeof s === 'string')
86
- return parse.quote[s[0]] ? s.slice(1,-1)
87
- : s[0]==='@' ? s.slice(1)
88
- : s in ctx ? ctx[s] : s
89
-
90
- return s
91
- }
92
-
93
- Object.assign(parse, {
94
- literal: {
95
- true: true,
96
- false: false,
97
- null: null
98
- },
99
- group: {'(':')','[':']'},
100
- quote: {'"':'"'},
101
- comment: {},
102
- prefix: {
103
- '-': 2,
104
- '!': 2,
105
- '+': 2,
106
- '(': 2,
107
- '++': 2,
108
- '--': 2,
109
- '.': 1
110
- },
111
- postfix: {
112
- '++': 2,
113
- '--': 2
114
- },
115
- binary: {
116
- ',': 12,
117
- '||': 11, '&&': 10, '|': 9, '^': 8, '&': 7,
118
- '==': 6, '!=': 6,
119
- '<': 5, '>': 5, '<=': 5, '>=': 5,
120
- '<<': 4, '>>': 4, '>>>': 4,
121
- '+': 3, '-': 3,
122
- '*': 2, '/': 2, '%': 2,
123
- '.': 1, '(': 1, '[': 1,
124
- 'e': 1, 'E': 1
125
- },
126
- map: {
127
- '(': n => n.length < 3 ? n[1] : n.slice(1).reduce(
128
- (a,b)=>[a].concat(b==='' ? [] : b[0]==',' ? b.slice(1).map(x=>x===''?undefined:x) : [b]),
129
- ), // [(,a,args1,args2] → [[a,...args1],...args2]
130
- '[': n => (n[0]='.',n),
131
- '.': n => typeof n[1] === 'number' ? parseFloat(n.length < 3 ? '.'+n[1] : n[1]+n[0]+n[2]) : // [.,2,1] → 2.1
132
- ['.',n[1],...n.slice(2).map(s=>typeof s === 'string' ? '"'+s+'"' : s)], // [.,a,b] → [.,a,"b"]
133
- 'e': n => parseFloat(n[1]+'e'+(Array.isArray(n[2])?n[2].join(''):n[2])),
134
- 'E': n => parse.map['e'](n)
135
- }
136
- })
137
-
138
- // op evaluators
139
- // multiple args allows shortcuts, lisp compatible, easy manual eval, functions anyways take multiple arguments
140
- evaluate.operator = {
141
- '!':a=>!a,
142
- '++':a=>++a,
143
- '--':a=>--a,
144
-
145
- '.':(...a)=>a.reduce((a,b)=>a?a[b]:a),
146
- '(':(a,...args)=>a(...args),
147
- '[':(a,...args)=>a[args.pop()],
148
-
149
- '%':(...a)=>a.reduce((a,b)=>a%b),
150
- '/':(...a)=>a.reduce((a,b)=>a/b),
151
- '*':(...a)=>a.reduce((a,b)=>a*b),
152
-
153
- '+':(...a)=>a.reduce((a,b)=>a+b),
154
- '-':(...a)=>a.length < 2 ? -a : a.reduce((a,b)=>a-b),
155
-
156
- '>>>':(a,b)=>a>>>b,
157
- '>>':(a,b)=>a>>b,
158
- '<<':(a,b)=>a<<b,
159
-
160
- '>=':(a,b)=>a>=b,
161
- '>':(a,b)=>a>b,
162
- '<=':(a,b)=>a<=b,
163
- '<':(a,b)=>a<b,
164
-
165
- '!=':(a,b)=>a!=b,
166
- '==':(a,b)=>a==b,
167
-
168
- '&':(a,b)=>a&b,
169
- '^':(a,b)=>a^b,
170
- '|':(a,b)=>a|b,
171
- '&&':(...a)=>a.every(Boolean),
172
- '||':(...a)=>a.some(Boolean),
173
- ',':(...a)=>a.reduce((a,b)=>(a,b))
174
- }
1
+ import parse from './parse.js'
2
+ import evaluate from './evaluate.js'
175
3
 
176
4
  export { parse, evaluate }
177
5
 
package/subscript.min.js CHANGED
@@ -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));
1
+ var e,r,t,n,l=(n,l)=>(r=n,e=t=0,l=y(),e<r.length?u():l),a=()=>r.charCodeAt(e),o=(t=1)=>r.substr(e,t),u=(r="Bad syntax "+o())=>{throw Error(r+" at "+e)},s=(t=1,n=e)=>{if("number"==typeof t)e+=t;else for(;t(a());)++e>r.length&&u("End by "+t);return e>n?r.slice(n,e):null},f=()=>{for(;a()<=32;)e++},i=(r,n,l,a=3)=>{if(e&&t&&t[3]===e)return t;for(;a;)if(null!=(l=r[n=o(a--)]))return t=[n,l,n.length,e]},y=(r=-1,t)=>{f();let l,s,p,d,b=a(),m=0,B=e;if(t){if(b===t)return;d=n,n=t}for(;B===e&&m<c.length;)s=c[m++](b);if(B===e)(l=i(g))&&(e+=l[2],s=[l[0],y(l[1])]);else for(f(),b=a(),m=0;m<h.length;)(p=h[m](s,b))!==s?(s=p,f(),b=a()):m++;for(;b=a()&&b!==n&&(l=i(A))&&l[1]>r;){s=[l[0],s];do{e+=l[2],s.push(y(l[1]))}while(o(l[2])===l[0]);f()}return t&&(n=a()!==t?u("Unclosed paren"):d),s},p=e=>s((e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192)),c=l.token=[e=>{if(e=s((e=>e>=48&&e<=57||46===e)))return(69===a()||101===a())&&(e+=s(2)+s((e=>e>=48&&e<=57))),isNaN(e=parseFloat(e))?u("Bad number"):e},(r,t)=>40===r?(e++,t=y(-1,41),e++,t):null,(r,t)=>34===r?(t=o(),e++,t+s((e=>e-r))+(e++,t)):null,p],d=l.literal={true:!0,false:!1,null:null},h=l.postfix=[(t,n,l)=>(46===n?(e++,f(),t=[".",t,'"'+p()+'"']):91===n?(e++,t=[".",t,y(-1,93)],e++):40===n?(e++,l=y(-1,41),t=Array.isArray(l)&&","===l[0]?(l[0]=t,l):null==l?[t]:[t,l],e++):43!==n&&45!==n||r.charCodeAt(e+1)!==n?"string"==typeof t&&d.hasOwnProperty(t)&&(t=d[t]):t=[s(2),t],t)],g=l.unary={"-":17,"!":17,"+":17,"++":17,"--":17},A=l.binary={",":1,"||":6,"&&":7,"|":8,"^":9,"&":10,"==":11,"!=":11,"<":12,">":12,"<=":12,">=":12,"<<":13,">>":13,">>>":13,"+":14,"-":14,"*":15,"/":15,"%":15},b=l,m=e=>Array.isArray(e)&&("string"==typeof e[0]||m(e[0])),B=(e,r={},t,n)=>m(e)?("string"==typeof(t=e[0])&&(n=v[t]),"function"!=typeof(t=n||B(t,r))?t:t.call(...e.map((e=>B(e,r))))):e&&"string"==typeof e?'"'===e[0]?e.slice(1,-1):"@"===e[0]?e.slice(1):e in r?r[e]:e:e,v=B.operator={"!":e=>!e,"++":e=>++e,"--":e=>--e,".":(...e)=>e.reduce(((e,r)=>e&&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.length<2?-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))},w=B,x=e=>(e="string"==typeof e?b(e):e,r=>w(e,r));export{x as default,w as evaluate,b as parse};