subscript 3.0.1 → 5.0.0

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,11 +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
- * It has well-known syntax
5
+ * Well-known syntax
6
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 very fast ([see performance](#performance))
8
+ * It's :rocket: fast ([see performance](#performance))
9
9
  * Configurable & extensible
10
10
  * Trivial to use...
11
11
 
@@ -26,14 +26,8 @@ _Subscript_ is designed to be useful for:
26
26
  * sandboxes, playgrounds, safe eval
27
27
  * custom DSL
28
28
 
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:
31
-
32
- ```js
33
- import {evaluate} from 'subscript.js'
34
-
35
- evaluate(['+', ['*', 'min', 60], '"sec"'], { min: 5 }) // min*60 + "sec" == "300sec"
36
- ```
29
+ [_Jsep_](https://github.com/EricSmekens/jsep) is generally fine for the listed tasks, unless you need dependencies as small as possible.
30
+ _Subscript_ has [2.5kb](https://npmfs.com/package/subscript/4.0.0/subscript.min.js) footprint vs [11.4kb](https://npmfs.com/package/jsep/1.2.0/dist/jsep.min.js) _jsep_, with better performance.
37
31
 
38
32
 
39
33
  ## Operators
@@ -53,17 +47,53 @@ Default operators include common operators for the listed languages in the follo
53
47
  * `&&`
54
48
  * `||`
55
49
 
56
- All other operators can be extended via `parse.binary`, `parse.unary` and `evaluate.operator`.
50
+ All other operators can be extended, see [extension](#extension).
51
+
52
+ ## Evaluation
53
+
54
+ _Subscript_ parser generates lispy calltree (compatible with [frisk](https://npmjs.com/frisk)), which is compared to esprima AST has:
55
+
56
+ + minimal possible overhead
57
+ + clear precedence
58
+ + overloading by context
59
+ + manual evaluation and debugging
60
+ + conventional form
61
+ + one-liner docs:
57
62
 
63
+ ```js
64
+ import {evaluate} from 'subscript.js'
65
+
66
+ evaluate(['+', ['*', 'min', 60], '"sec"'], { min: 5 }) // min*60 + "sec" == "300sec"
67
+ ```
68
+
69
+ ## Extension
70
+
71
+ Tokens are extensible via `parse.token` list, can be added support of _regex_, _array_, _object_, _interpolated string_ and others.
72
+ Default tokens include:
73
+
74
+ * `"abc"` strings
75
+ * `1.2e+3` floats
76
+ * `()` expression groups or fn calls
77
+ * `.`, `[]` property access
78
+
79
+ Operators can be extended via `parse.operator` to add support for any unary/binary/postfix operators, calls, props or chains.
80
+
81
+ Comments can be added via extending `parse.space`.
82
+
83
+ For now see justin extension how things can be done.
84
+
85
+ <!--
58
86
  ```js
59
87
  import { parse, evaluate } from 'subscript.js'
60
88
 
61
89
  // add precedences
62
- parse.binary['=>'] = 10
90
+ // TODO
91
+ // parse.operator['=>'] = 10
63
92
 
64
93
  // define evaluators
65
- evaluate.operator['=>'] = ( args, body ) => evaluate(body, args)
66
- evaluate.operator['|'] = ( a, ...b ) => a.pipe(...b)
94
+ // TODO
95
+ // evaluate.operator['=>'] = ( args, body ) => evaluate(body, args)
96
+ // evaluate.operator['|'] = ( a, ...b ) => a.pipe(...b)
67
97
 
68
98
  let tree = parse(`
69
99
  interval(350)
@@ -73,28 +103,13 @@ let tree = parse(`
73
103
  `)
74
104
  evaluate(tree, { Math, map, take, interval, gaussian })
75
105
  ```
76
-
77
- ## Extending
78
-
79
- By default subscript detects the following tokens:
80
-
81
- * `"` strings
82
- * `1.2e+3` floats
83
- * `true`, `false`, `null` literals
84
- * `()` expression groups or fn calls
85
- * `.`, `[]` property access
86
-
87
- Literals can be extended via `parse.literal` dict.
88
-
89
- Token parsers are extensible via `parse.token` list, can be added support of _regex_, _array_, _object_, _interpolated string_ and others.
90
-
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`.
106
+ -->
92
107
 
93
108
 
94
109
  ## Justin
95
110
 
96
111
  _Justin_ extension (original [thread](https://github.com/endojs/Jessie/issues/66)) is minimal JS subset − JSON with JS expressions.<br/>
97
- It adds support for:
112
+ It adds support of:
98
113
 
99
114
  + `**` binary operator
100
115
  + `~` unary operator
@@ -104,8 +119,10 @@ It adds support for:
104
119
  + `{...}` Object literal
105
120
  + `in` binary operator
106
121
  + `;` expression separator
107
- <!-- + `//, /* */` comments -->
108
- <!-- + `undefined` literal -->
122
+ + unary word operators
123
+ + `//`, `/* */` comments
124
+ + `true`, `false`, `null`, `undefined` literals
125
+ <!-- + `===`, `!==` operators -->
109
126
  <!-- + `?` chaining operator -->
110
127
  <!-- + `...x` unary operator -->
111
128
  <!-- + strings interpolation -->
@@ -262,8 +279,8 @@ Subscript shows relatively good performance within other evaluators:
262
279
  // 1 + (a * b / c % d) - 2.0 + -3e-3 * +4.4e4 / f.g[0] - i.j(+k == 1)(0)
263
280
  // parse 30k times
264
281
 
265
- subscript: ~280 ms
266
- jsep: ~282 ms
282
+ subscript: ~200 ms
283
+ jsep: ~280 ms
267
284
  expr-eval: ~480 ms
268
285
  jexl: ~1200 ms
269
286
  new Function: ~1400 ms
@@ -278,6 +295,6 @@ new Function: ~1400 ms
278
295
  * [expression-eval](https://github.com/donmccurdy/expression-eval)
279
296
  * [jsep](https://github.com/EricSmekens/jsep)
280
297
  * [string-math](https://github.com/devrafalko/string-math)
281
-
298
+ * [nerdamer](https://github.com/jiggzson/nerdamer)
282
299
 
283
300
  <p align=center>🕉</p>
@@ -12,12 +12,14 @@ evaluate = (s, ctx={}, c, op) => {
12
12
  }
13
13
  if (s && typeof s === 'string')
14
14
  return s[0] === '"' ? s.slice(1,-1)
15
- : s[0]==='@' ? s.slice(1)
16
- : s in ctx ? ctx[s] : s
15
+ : s[0]==='@' ? s.slice(1)
16
+ : s in ctx ? ctx[s] : s
17
17
 
18
18
  return s
19
19
  },
20
20
 
21
+ reducer=fn=>(...a)=>a.reduce(fn),
22
+
21
23
  // op evaluators
22
24
  // multiple args allows shortcuts, lisp compatible, easy manual eval, functions anyways take multiple arguments
23
25
  operator = evaluate.operator = {
@@ -25,13 +27,13 @@ operator = evaluate.operator = {
25
27
  '++':a=>++a,
26
28
  '--':a=>--a,
27
29
 
28
- '.':(...a)=>a.reduce((a,b)=>a?a[b]:a),
30
+ '.':reducer((a,b)=>a?a[b]:a),
29
31
 
30
- '%':(...a)=>a.reduce((a,b)=>a%b),
31
- '/':(...a)=>a.reduce((a,b)=>a/b),
32
- '*':(...a)=>a.reduce((a,b)=>a*b),
32
+ '%':reducer((a,b)=>a%b),
33
+ '/':reducer((a,b)=>a/b),
34
+ '*':reducer((a,b)=>a*b),
33
35
 
34
- '+':(...a)=>a.reduce((a,b)=>a+b),
36
+ '+':reducer((a,b)=>a+b),
35
37
  '-':(...a)=>a.length < 2 ? -a : a.reduce((a,b)=>a-b),
36
38
 
37
39
  '>>>':(a,b)=>a>>>b,
@@ -51,7 +53,7 @@ operator = evaluate.operator = {
51
53
  '|':(a,b)=>a|b,
52
54
  '&&':(...a)=>a.every(Boolean),
53
55
  '||':(...a)=>a.some(Boolean),
54
- ',':(...a)=>a.reduce((a,b)=>(a,b))
56
+ ',':reducer((a,b)=>(a,b))
55
57
  }
56
58
 
57
59
  export default evaluate
package/justin.js CHANGED
@@ -1,73 +1,101 @@
1
1
  // justin lang https://github.com/endojs/Jessie/issues/66
2
- import {evaluate, operator} from './src/evaluate.js'
3
- import {parse, binary, unary, postfix, token, literal,
4
- code, char, skip, space, expr} from './src/parse.js'
2
+ import {evaluate} from './evaluate.js'
3
+ import {parse, code, char, skip, expr, operator, err} from './parse.js'
4
+
5
+ // ;
6
+ operator(';', 1)
7
+
8
+ // ===, !==
9
+ operator('===', 9)
10
+ operator('!==', 9)
5
11
 
6
12
  // undefined
7
- literal['undefined'] = undefined
13
+ const v = v => ({valueOf:()=>v})
14
+ parse.token.splice(2,0, c =>
15
+ c === 116 && char(4) === 'true' && skip(4) ? v(true) :
16
+ c === 102 && char(5) === 'false' && skip(5) ? v(false) :
17
+ c === 110 && char(4) === 'null' && skip(4) ? v(null) :
18
+ c === 117 && char(9) === 'undefined' && skip(9) ? v(undefined) :
19
+ null
20
+ )
8
21
 
9
- // '
10
- token.push((q, qc) => q === 39 ? (qc = char(), index++, qc) + skip(c => c !== q) + (index++, qc) : null)
22
+ // "' with /
23
+ parse.token[1] = (q, qc, c, str) => {
24
+ if (q !== 34 && q !== 39) return
25
+ qc = char(), skip(), str = ''
26
+ while (c=code(), c-q) {
27
+ if (c === 92) skip(), str += escape[char()] || char(); else str+=char()
28
+ skip()
29
+ }
30
+ return skip(), qc + str + qc
31
+ }
32
+ const escape = {n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', v:'\v'}
11
33
 
12
34
  // **
13
- binary['**'] = 16
14
- operator['**'] = (...args)=>args.reduceRight((a,b)=>Math.pow(b,a))
35
+ evaluate.operator['**'] = (...args)=>args.reduceRight((a,b)=>Math.pow(b,a))
36
+ operator('**', 14)
15
37
 
16
38
  // ~
17
- unary['~'] = 17
18
- operator['~'] = a=>~a
39
+ operator('~', 13, -1)
40
+ evaluate.operator['~'] = a=>~a
19
41
 
20
- // ...
21
- // unary[1]['...']=true
22
-
23
- // ;
24
- binary[';'] = 1
42
+ // TODO ...
25
43
 
26
44
  // ?:
27
- operator['?:']=(a,b,c)=>a?b:c
28
- postfix.push(node => {
45
+ evaluate.operator['?:']=(a,b,c)=>a?b:c
46
+ operator('?', 3, (node) => {
47
+ if (!node) err('Expected expression')
29
48
  let a, b
30
- if (code() !== 63) return node
31
- skip(), space(), a = expr(58)
32
- if (code() !== 58) return node
33
- skip(), space(), b = expr()
34
- return ['?:',node, a, b]
49
+ skip(), parse.space(), a = expr()
50
+ if (code() !== 58) err('Expected :')
51
+ skip(), parse.space(), b = expr()
52
+ return ['?:', node, a, b]
35
53
  })
54
+ // operator(':')
36
55
 
37
56
  // /**/, //
38
- // comments['/*']='*/'
39
- // comments['//']='\n'
57
+ parse.space = cc => {
58
+ while (cc = code(), cc <= 32) {
59
+ skip()
60
+ if (code() === 47)
61
+ // /**/
62
+ if (code(1) === 42) skip(2), skip(c => c !== 42 && code(1) !== 47), skip(2)
63
+ // //
64
+ else if (code(1) === 47) skip(2), skip(c => c >= 32)
65
+ }
66
+ return cc
67
+ }
40
68
 
41
69
  // in
42
70
  evaluate.operator['in'] = (a,b)=>a in b
43
- parse.postfix.unshift(node => (char(2) === 'in' ? (skip(2), ['in', '"' + node + '"', expr()]) : node))
71
+ operator('in', 10, (node) => code(2) <= 32 && [skip(2), '"'+node+'"', expr(10)])
44
72
 
45
73
  // []
46
- operator['['] = (...args) => Array(...args)
47
- token.push((node, arg) =>
48
- code() === 91 ?
49
- (
50
- skip(), arg=expr(93),
51
- node = arg==null ? ['['] : arg[0] === ',' ? (arg[0]='[',arg) : ['[',arg],
52
- skip(), node
53
- ) : null
54
- )
74
+ evaluate.operator['['] = (...args) => Array(...args)
75
+ // as operator it's faster to lookup (no need to call extra rule check), smaller and no conflict with word names
76
+ operator('[', 20, (node,arg) => !node && (
77
+ skip(), arg=expr(0,93), skip(),
78
+ !arg ? ['['] : arg[0] === ',' ? (arg[0]='[',arg) : ['[',arg]
79
+ ))
55
80
 
56
81
  // {}
57
- binary[':'] = 2
58
- token.unshift((node) => code() === 123 ? (skip(), node = map(['{',expr(125)]), skip(), node) : null)
59
- operator['{'] = (...args)=>Object.fromEntries(args)
60
- operator[':'] = (a,b)=>[a,b]
82
+ parse.token.unshift((cc, node) => (
83
+ cc === 123 && (skip(), node = map(['{', expr(0,125)]), skip(), node)
84
+ ))
85
+
86
+ operator('}')
87
+ operator(':', 0)
88
+ evaluate.operator['{'] = (...args)=>Object.fromEntries(args)
89
+ evaluate.operator[':'] = (a,b)=>[a,b]
61
90
 
62
91
  const map = (n, args) => {
63
- if (n[1]==null) args = []
92
+ if (!n[1]) args = []
64
93
  else if (n[1][0]==':') args = [n[1]]
65
94
  else if (n[1][0]==',') args = n[1].slice(1)
66
95
  return ['{', ...args]
67
96
  }
68
97
 
69
-
70
98
  // TODO: strings interpolation
71
99
 
72
- export { default } from './subscript.js';
100
+ export default parse
73
101
  export { parse, evaluate }
package/justin.min.js CHANGED
@@ -1 +1 @@
1
- var e,r,n,t=e=>Array.isArray(e)&&("string"==typeof e[0]||t(e[0])),l=(e,r={},n,i)=>t(e)?("string"==typeof(n=e[0])&&(i=u[n]),"function"!=typeof(n=i||l(n,r))?n:n.call(...e.map((e=>l(e,r))))):e&&"string"==typeof e?'"'===e[0]?e.slice(1,-1):"@"===e[0]?e.slice(1):e in r?r[e]:e:e,u=l.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))},i=l,o=t=>(r=t,e=n=0,f()),a=()=>{for(;d()<=32;)e++},s=(r,t,l,u=3)=>{if(e&&n&&n[3]===e)return n;for(;u;)if(null!=(l=r[t=c(u--)]))return n=[t,l,t.length,e]},f=(r,n=-1)=>{a();let t,l,u,i=d(),o=0,p=e;if(i!==r){for(;p===e&&o<g.length;)l=g[o++](i);if(p===e)(t=s(v))&&(e+=t[2],l=[t[0],f(r,t[1])]);else{if(a(),i=d(),i===r)return l;for(o=0;o<x.length;)(u=x[o](l,i))!==l?(l=u,o=0,a(),i=d()):o++}for(a();(i=d())&&i!==r&&(t=s(b))&&t[1]>n;){l=[t[0],l];do{e+=t[2],l.push(f(r,t[1]))}while(c(t[2])===t[0]);a()}return l}},p=e=>(e=y((e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192)))&&A.hasOwnProperty(e)?A[e]:e,d=()=>r.charCodeAt(e),c=(n=1)=>r.substr(e,n),h=r=>{throw Error(r+" at "+e)},y=(n=1,t=e)=>{if("number"==typeof n)e+=n;else for(;n(d());)++e>r.length&&h("Unexpected end "+n);return e>t?r.slice(t,e):null},g=o.token=[(r,n)=>40===r?(e++,n=f(41),e++,n):null,(r,n,t)=>{if(r=y(t=e=>e>=48&&e<=57)||"",46===d()&&(e++,r+="."+y(t)),r)return(69===(n=d())||101===n)&&(e++,r+="e",(43===(n=d())||45===n)&&(r+=c(),e++),r+=y(t)),parseFloat(r)},(r,n)=>34===r?(n=c(),e++,n+y((e=>e!==r))+(e++,n)):null,p],A=o.literal={true:!0,false:!1,null:null},x=o.postfix=[(r,n,t)=>(46===n?(e++,a(),r=[".",r,'"'+p()+'"']):91===n?(e++,r=[".",r,f(93)],e++):40===n&&(e++,t=f(41),r=Array.isArray(t)&&","===t[0]?(t[0]=r,t):null==t?[r]:[r,t],e++),r),(n,t)=>43!==t&&45!==t||r.charCodeAt(e+1)!==t?n:[y(2),n]],v=o.unary={"-":17,"!":17,"+":17,"++":17,"--":17},b=o.binary={",":1,"||":6,"&&":7,"|":8,"^":9,"&":10,"==":11,"!=":11,"<":12,">":12,"<=":12,">=":12,"<<":13,">>":13,">>>":13,"+":14,"-":14,"*":15,"/":15,"%":15},m=o,w=e=>(e="string"==typeof e?m(e):e,r=>i(e,r));A.undefined=void 0,g.push(((e,r)=>39===e?(r=c(),index++,r+y((r=>r!==e))+(index++,r)):null)),b["**"]=16,u["**"]=(...e)=>e.reduceRight(((e,r)=>Math.pow(r,e))),v["~"]=17,u["~"]=e=>~e,b[";"]=1,u["?:"]=(e,r,n)=>e?r:n,x.push((e=>{let r,n;return 63!==d()||(y(),a(),r=f(58),58!==d())?e:(y(),a(),n=f(),["?:",e,r,n])})),l.operator.in=(e,r)=>e in r,o.postfix.unshift((e=>"in"===c(2)?(y(2),["in",'"'+e+'"',f()]):e)),u["["]=(...e)=>Array(...e),g.push(((e,r)=>91===d()?(y(),e=null==(r=f(93))?["["]:","===r[0]?(r[0]="[",r):["[",r],y(),e):null)),b[":"]=2,g.unshift((e=>123===d()?(y(),e=B(["{",f(125)]),y(),e):null)),u["{"]=(...e)=>Object.fromEntries(e),u[":"]=(e,r)=>[e,r];var B=(e,r)=>(null==e[1]?r=[]:":"==e[1][0]?r=[e[1]]:","==e[1][0]&&(r=e[1].slice(1)),["{",...r]);export{w as default,l as evaluate,o as parse};
1
+ var e,r,t=e=>Array.isArray(e)&&("string"==typeof e[0]||t(e[0])),a=(e,r={},o,l)=>t(e)?("string"==typeof(o=e[0])&&(l=n[o]),"function"!=typeof(o=l||a(o,r))?o:o.call(...e.map((e=>a(e,r))))):e&&"string"==typeof e?'"'===e[0]?e.slice(1,-1):"@"===e[0]?e.slice(1):e in r?r[e]:e:e,o=e=>(...r)=>r.reduce(e),n=a.operator={"!":e=>!e,"++":e=>++e,"--":e=>--e,".":o(((e,r)=>e&&e[r])),"%":o(((e,r)=>e%r)),"/":o(((e,r)=>e/r)),"*":o(((e,r)=>e*r)),"+":o(((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),",":o(((e,r)=>r))},l=15,s=(t,a)=>(r=t,e=0,a=c(),e<r.length?p():a.valueOf()),p=(t="Bad syntax")=>{throw Error(t+" `"+r[e]+"` at "+e)},u=(t=1,a=e)=>{if("number"==typeof t)e+=t;else for(;t(f());)e++;return r.slice(a,e)},f=(t=0)=>r.charCodeAt(e+t),i=(t=1)=>r.substr(e,t),c=(e=0,r,t,a,o=0,n,l)=>{for(;(t=s.space())&&(l=h[t]?.(a,e)||!a&&d(t));)a=l;return r&&t!==r&&p("Unclosed paren"),a},v=(s.space=r=>{for(;(r=f())<=32;)e++;return r},s.token=[e=>(e=u((e=>e>47&&e<58||46==e)))&&((69==f()||101==f())&&(e+=u(2)+u((e=>e>=48&&e<=57))),isNaN(e=new Number(e))?p("Bad number"):e),(e,r)=>34==e&&u()+u((r=>r-e))+u(),e=>u((e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e))]),d=(e,r=0,t)=>{for(;r<v.length;)if(t=v[r++](e))return t},h=[],y=(r,t=0,a=0,o,n=r.charCodeAt(0),l=r.length,v=h[n],d=r.toUpperCase()!==r,y)=>(y=l<2?d?e=>f(1)<=32:e=>1:d?e=>i(l)==r&&f(l)<=32:e=>i(l)==r,o=a?a>0?e=>e&&[u(l),e]:a<0?e=>!e&&[u(l),(c(t-1)||p()).valueOf()]:a:a=>{a=[r,a||p()];do{e+=l,a.push((c(t)||p()).valueOf())}while(s.space()==n&&y());return a},h[n]=(e,r)=>r<t&&y()&&o(e)||v&&v(e,r));for(let r=0,t=[",",1,,"|",6,,"||",4,,"&",8,,"&&",5,,"^",7,,"==",9,,"!=",9,,">",10,,">=",10,,">>",11,,">>>",11,,"<",10,,"<=",10,,"<<",11,,"+",12,,"+",l,-1,"++",l,-1,"++",l,1,"-",12,,"-",l,-1,"--",l,-1,"--",l,1,"!",l,-1,"*",13,,"/",13,,"%",13,,".",18,(e,r)=>e&&[u(),e,"string"==typeof(r=c(18))?'"'+r+'"':r.valueOf()],"[",18,r=>(e++,r=[".",r,c(0,93).valueOf()],e++,r),"]",,,"(",18,(r,t)=>(e++,t=c(0,41),e++,Array.isArray(t)&&","===t[0]?(t[0]=r,t):t?[r,t.valueOf()]:[r]),"(",19,(r,t)=>!r&&(++e,t=c(0,41)||p(),++e,t),")",,,];r<t.length;)y(t[r++],t[r++],t[r++]);y(";",1),y("===",9),y("!==",9);var g=e=>({valueOf:()=>e});s.token.splice(2,0,(e=>116===e&&"true"===i(4)&&u(4)?g(!0):102===e&&"false"===i(5)&&u(5)?g(!1):110===e&&"null"===i(4)&&u(4)?g(null):117===e&&"undefined"===i(9)&&u(9)?g(void 0):null)),s.token[1]=(e,r,t,a)=>{if(34===e||39===e){for(r=i(),u(),a="";(t=f())-e;)92===t?(u(),a+=O[i()]||i()):a+=i(),u();return u(),r+a+r}};var O={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"};a.operator["**"]=(...e)=>e.reduceRight(((e,r)=>Math.pow(r,e))),y("**",14),y("~",13,-1),a.operator["~"]=e=>~e,a.operator["?:"]=(e,r,t)=>e?r:t,y("?",3,(e=>{let r,t;return e||p("Expected expression"),u(),s.space(),r=c(),58!==f()&&p("Expected :"),u(),s.space(),t=c(),["?:",e,r,t]})),s.space=e=>{for(;(e=f())<=32;)u(),47===f()&&(42===f(1)?(u(2),u((e=>42!==e&&47!==f(1))),u(2)):47===f(1)&&(u(2),u((e=>e>=32))));return e},a.operator.in=(e,r)=>e in r,y("in",10,(e=>f(2)<=32&&[u(2),'"'+e+'"',c(10)])),a.operator["["]=(...e)=>Array(...e),y("[",20,((e,r)=>!e&&(u(),r=c(0,93),u(),r?","===r[0]?(r[0]="[",r):["[",r]:["["]))),s.token.unshift(((e,r)=>123===e&&(u(),r=b(["{",c(0,125)]),u(),r))),y("}"),y(":",0),a.operator["{"]=(...e)=>Object.fromEntries(e),a.operator[":"]=(e,r)=>[e,r];var b=(e,r)=>(e[1]?":"==e[1][0]?r=[e[1]]:","==e[1][0]&&(r=e[1].slice(1)):r=[],["{",...r]),A=s;export{A as default,a as evaluate,s as parse};
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "subscript",
3
- "version": "3.0.1",
3
+ "version": "5.0.0",
4
4
  "description": "Microlanguage with common syntax for JS/C++/Python/Rust",
5
5
  "main": "subscript.js",
6
6
  "type": "module",
7
7
  "files": [
8
- "src/*",
8
+ "parse.js",
9
+ "evaluate.js",
9
10
  "subscript.js",
10
11
  "subscript.min.js",
11
12
  "justin.js",
package/parse.js ADDED
@@ -0,0 +1,148 @@
1
+ const PERIOD=46, OPAREN=40, CPAREN=41, CBRACK=93, SPACE=32,
2
+
3
+ PREC_SEQ=1, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
4
+ 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
5
+
6
+
7
+ // current string & index
8
+ let idx, cur
9
+
10
+ export const parse = (str, tree) => (cur=str, idx=0, tree=expr(), idx<cur.length ? err() : tree.valueOf()),
11
+
12
+ err = (msg='Bad syntax') => { throw Error(msg + ' `' + cur[idx] + '` at ' + idx) },
13
+
14
+ skip = (is=1, from=idx) => {
15
+ if (typeof is === 'number') idx += is
16
+ else while (is(code())) idx++;
17
+ return cur.slice(from, idx)
18
+ },
19
+
20
+ code = (i=0) => cur.charCodeAt(idx+i),
21
+
22
+ char = (n=1) => cur.substr(idx, n),
23
+
24
+ // a + b - c
25
+ expr = (prec=0, end, cc, node, i=0, map, newNode) => {
26
+ // chunk/token parser
27
+ while (
28
+ (cc=parse.space()) && (newNode = lookup[cc]?.(node, prec) || (!node && token(cc)) )
29
+ ) node = newNode;
30
+
31
+ if (end && cc !== end) err('Unclosed paren')
32
+
33
+ return node
34
+ },
35
+
36
+ // can be extended with comments, so we export
37
+ space = parse.space = cc => { while (cc = code(), cc <= SPACE) idx++; return cc },
38
+
39
+ // tokens
40
+ tokens = parse.token = [
41
+ // 1.2e+3, .5 - fast & small version, but consumes corrupted nums as well
42
+ (number) => (
43
+ (number = skip(c => (c > 47 && c < 58) || c == PERIOD)) && (
44
+ (code() == 69 || code() == 101) && (number += skip(2) + skip(c => c >= 48 && c <= 57)),
45
+ isNaN(number = new Number(number)) ? err('Bad number') : number
46
+ )
47
+ ),
48
+ // "a"
49
+ (q, qc) => q == 34 && (skip() + skip(c => c-q) + skip()),
50
+ // id
51
+ c => skip(c =>
52
+ (c >= 48 && c <= 57) || // 0..9
53
+ (c >= 65 && c <= 90) || // A...Z
54
+ (c >= 97 && c <= 122) || // a...z
55
+ c == 36 || c == 95 || // $, _,
56
+ (c >= 192 && c != 215 && c != 247) // any non-ASCII
57
+ )
58
+ ],
59
+
60
+ token = (c,i=0,node) => { while(i<tokens.length) if (node = tokens[i++](c)) return node },
61
+
62
+ // operator lookup table
63
+ lookup = [],
64
+
65
+ // create operator checker/mapper (see examples)
66
+ // @param op is operator string
67
+ // @param prec is operator precedenc to check
68
+ // @param map is either number +1 - postfix unary, -1 prefix unary, 0 binary, else - custom mapper function
69
+ operator = (op, prec=0, type=0, map, c=op.charCodeAt(0), l=op.length, prev=lookup[c], word=op.toUpperCase()!==op, isop) => (
70
+ isop = l<2 ? // word operator must have space after
71
+ !word ? c=>1 : c=>code(1)<=SPACE :
72
+ !word ? c=>char(l)==op : c=>char(l)==op&&code(l)<=SPACE,
73
+
74
+ map = !type ? node => { // binary, consume same-op group
75
+ node = [op, node || err()]
76
+ // in order to support literal tokens, we call valueOf any time we create or modify calltree node
77
+ do { idx+=l, node.push((expr(prec) || err()).valueOf()) } while (parse.space()==c && isop())
78
+ return node
79
+ } :
80
+ type > 0 ? node => node && [skip(l), node] : // postfix unary
81
+ type < 0 ? node => !node && [skip(l), (expr(prec-1) || err()).valueOf()] : // prefix unary
82
+ type,
83
+
84
+ lookup[c] = (node, curPrec) => curPrec < prec && isop() && map(node) || (prev && prev(node, curPrec))
85
+ )
86
+
87
+ // ,
88
+ for (let i = 0, ops = [
89
+ // TODO: add ,, as node here
90
+ ',', PREC_SEQ,,
91
+
92
+ '|', PREC_OR,,
93
+ '||', PREC_SOME,,
94
+
95
+ '&', PREC_AND,,
96
+ '&&', PREC_EVERY,,
97
+
98
+ '^', PREC_XOR,,
99
+
100
+ // ==, !=
101
+ '==', PREC_EQ,,
102
+ '!=', PREC_EQ,,
103
+
104
+ // > >= >> >>>, < <= <<
105
+ '>', PREC_COMP,,
106
+ '>=', PREC_COMP,,
107
+ '>>', PREC_SHIFT,,
108
+ '>>>', PREC_SHIFT,,
109
+ '<', PREC_COMP,,
110
+ '<=', PREC_COMP,,
111
+ '<<', PREC_SHIFT,,
112
+
113
+ // + ++ - --
114
+ '+', PREC_SUM,,
115
+ '+', PREC_UNARY, -1,
116
+ '++', PREC_UNARY, -1,
117
+ '++', PREC_UNARY, +1,
118
+ '-', PREC_SUM,,
119
+ '-', PREC_UNARY, -1,
120
+ '--', PREC_UNARY, -1,
121
+ '--', PREC_UNARY, +1,
122
+
123
+ // ! ~
124
+ '!', PREC_UNARY, -1,
125
+
126
+ // * / %
127
+ '*', PREC_MULT,,
128
+ '/', PREC_MULT,,
129
+ '%', PREC_MULT,,
130
+
131
+ // a.b
132
+ '.', PREC_CALL, (node,b) => node && [skip(),node, typeof (b = expr(PREC_CALL)) === 'string' ? '"' + b + '"' : b.valueOf()],
133
+
134
+ // a[b]
135
+ '[', PREC_CALL, (node) => (idx++, node = ['.', node, expr(0,CBRACK).valueOf()], idx++, node),
136
+ ']',,,
137
+
138
+ // a(b)
139
+ '(', PREC_CALL, (node,b) => ( idx++, b=expr(0,CPAREN), idx++,
140
+ Array.isArray(b) && b[0]===',' ? (b[0]=node, b) : b ? [node, b.valueOf()] : [node]
141
+ ),
142
+ // (a+b)
143
+ '(', PREC_GROUP, (node,b) => !node && (++idx, b=expr(0,CPAREN) || err(), ++idx, b),
144
+ ')',,,
145
+ ]; i < ops.length;) operator(ops[i++],ops[i++],ops[i++])
146
+
147
+
148
+ export default parse
package/subscript.js CHANGED
@@ -1,5 +1,5 @@
1
- import parse from './src/parse.js'
2
- import evaluate from './src/evaluate.js'
1
+ import parse from './parse.js'
2
+ import evaluate from './evaluate.js'
3
3
 
4
4
  export { parse, evaluate }
5
5
 
package/subscript.min.js CHANGED
@@ -1 +1 @@
1
- var e,r,t,l=l=>(r=l,e=t=0,u()),n=()=>{for(;f()<=32;)e++},o=(r,l,n,o=3)=>{if(e&&t&&t[3]===e)return t;for(;o;)if(null!=(n=r[l=s(o--)]))return t=[l,n,l.length,e]},u=(r,t=-1)=>{n();let l,a,i,c=f(),y=0,A=e;if(c!==r){for(;A===e&&y<p.length;)a=p[y++](c);if(A===e)(l=o(h))&&(e+=l[2],a=[l[0],u(r,l[1])]);else{if(n(),c=f(),c===r)return a;for(y=0;y<d.length;)(i=d[y](a,c))!==a?(a=i,y=0,n(),c=f()):y++}for(n();(c=f())&&c!==r&&(l=o(g))&&l[1]>t;){a=[l[0],a];do{e+=l[2],a.push(u(r,l[1]))}while(s(l[2])===l[0]);n()}return a}},a=e=>(e=c((e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192)))&&y.hasOwnProperty(e)?y[e]:e,f=()=>r.charCodeAt(e),s=(t=1)=>r.substr(e,t),i=r=>{throw Error(r+" at "+e)},c=(t=1,l=e)=>{if("number"==typeof t)e+=t;else for(;t(f());)++e>r.length&&i("Unexpected end "+t);return e>l?r.slice(l,e):null},p=l.token=[(r,t)=>40===r?(e++,t=u(41),e++,t):null,(r,t,l)=>{if(r=c(l=e=>e>=48&&e<=57)||"",46===f()&&(e++,r+="."+c(l)),r)return(69===(t=f())||101===t)&&(e++,r+="e",(43===(t=f())||45===t)&&(r+=s(),e++),r+=c(l)),parseFloat(r)},(r,t)=>34===r?(t=s(),e++,t+c((e=>e!==r))+(e++,t)):null,a],y=l.literal={true:!0,false:!1,null:null},d=l.postfix=[(r,t,l)=>(46===t?(e++,n(),r=[".",r,'"'+a()+'"']):91===t?(e++,r=[".",r,u(93)],e++):40===t&&(e++,l=u(41),r=Array.isArray(l)&&","===l[0]?(l[0]=r,l):null==l?[r]:[r,l],e++),r),(t,l)=>43!==l&&45!==l||r.charCodeAt(e+1)!==l?t:[c(2),t]],h=l.unary={"-":17,"!":17,"+":17,"++":17,"--":17},g=l.binary={",":1,"||":6,"&&":7,"|":8,"^":9,"&":10,"==":11,"!=":11,"<":12,">":12,"<=":12,">=":12,"<<":13,">>":13,">>>":13,"+":14,"-":14,"*":15,"/":15,"%":15},A=l,b=e=>Array.isArray(e)&&("string"==typeof e[0]||b(e[0])),m=(e,r={},t,l)=>b(e)?("string"==typeof(t=e[0])&&(l=v[t]),"function"!=typeof(t=l||m(t,r))?t:t.call(...e.map((e=>m(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=m.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=m,x=e=>(e="string"==typeof e?A(e):e,r=>w(e,r));export{x as default,w as evaluate,A as parse};
1
+ var e,r,a=15,t=(a,t)=>(r=a,e=0,t=f(),e<r.length?o():t.valueOf()),o=(a="Bad syntax")=>{throw Error(a+" `"+r[e]+"` at "+e)},n=(a=1,t=e)=>{if("number"==typeof a)e+=a;else for(;a(s());)e++;return r.slice(t,e)},s=(a=0)=>r.charCodeAt(e+a),l=(a=1)=>r.substr(e,a),f=(e=0,r,a,n,s=0,l,f)=>{for(;(a=t.space())&&(f=i[a]?.(n,e)||!n&&p(a));)n=f;return r&&a!==r&&o("Unclosed paren"),n},u=(t.space=r=>{for(;(r=s())<=32;)e++;return r},t.token=[e=>(e=n((e=>e>47&&e<58||46==e)))&&((69==s()||101==s())&&(e+=n(2)+n((e=>e>=48&&e<=57))),isNaN(e=new Number(e))?o("Bad number"):e),(e,r)=>34==e&&n()+n((r=>r-e))+n(),e=>n((e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e))]),p=(e,r=0,a)=>{for(;r<u.length;)if(a=u[r++](e))return a},i=[],c=(r,a=0,u=0,p,c=r.charCodeAt(0),y=r.length,g=i[c],h=r.toUpperCase()!==r,v)=>(v=y<2?h?e=>s(1)<=32:e=>1:h?e=>l(y)==r&&s(y)<=32:e=>l(y)==r,p=u?u>0?e=>e&&[n(y),e]:u<0?e=>!e&&[n(y),(f(a-1)||o()).valueOf()]:u:n=>{n=[r,n||o()];do{e+=y,n.push((f(a)||o()).valueOf())}while(t.space()==c&&v());return n},i[c]=(e,r)=>r<a&&v()&&p(e)||g&&g(e,r));for(let r=0,t=[",",1,,"|",6,,"||",4,,"&",8,,"&&",5,,"^",7,,"==",9,,"!=",9,,">",10,,">=",10,,">>",11,,">>>",11,,"<",10,,"<=",10,,"<<",11,,"+",12,,"+",a,-1,"++",a,-1,"++",a,1,"-",12,,"-",a,-1,"--",a,-1,"--",a,1,"!",a,-1,"*",13,,"/",13,,"%",13,,".",18,(e,r)=>e&&[n(),e,"string"==typeof(r=f(18))?'"'+r+'"':r.valueOf()],"[",18,r=>(e++,r=[".",r,f(0,93).valueOf()],e++,r),"]",,,"(",18,(r,a)=>(e++,a=f(0,41),e++,Array.isArray(a)&&","===a[0]?(a[0]=r,a):a?[r,a.valueOf()]:[r]),"(",19,(r,a)=>!r&&(++e,a=f(0,41)||o(),++e,a),")",,,];r<t.length;)c(t[r++],t[r++],t[r++]);var y=t,g=e=>Array.isArray(e)&&("string"==typeof e[0]||g(e[0])),h=(e,r={},a,t)=>g(e)?("string"==typeof(a=e[0])&&(t=d[a]),"function"!=typeof(a=t||h(a,r))?a:a.call(...e.map((e=>h(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=e=>(...r)=>r.reduce(e),d=h.operator={"!":e=>!e,"++":e=>++e,"--":e=>--e,".":v(((e,r)=>e&&e[r])),"%":v(((e,r)=>e%r)),"/":v(((e,r)=>e/r)),"*":v(((e,r)=>e*r)),"+":v(((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),",":v(((e,r)=>r))},A=h,O=e=>(e="string"==typeof e?y(e):e,r=>A(e,r));export{O as default,A as evaluate,y as parse};
package/src/parse.js DELETED
@@ -1,134 +0,0 @@
1
- const PERIOD = 46, OPAREN = 40, CPAREN = 41, OBRACK = 91, CBRACK = 93, PLUS = 43, MINUS = 45
2
-
3
- export let index, current, lastOp
4
-
5
- export const parse = str => (current=str, index=lastOp=0, expr()),
6
-
7
- space = () => { while (code() <= 32) index++ },
8
-
9
- // consume operator
10
- operator = (ops, op, prec, l=3) => {
11
- // memoize by index - saves 20% to perf
12
- if (index && lastOp && lastOp[3] === index) return lastOp
13
-
14
- // ascending lookup is faster for 1-char operators, longer for 2+ char ops, so we use descending
15
- while (l) if ((prec=ops[op=char(l--)])!=null) return lastOp = [op, prec, op.length, index] //opinfo
16
- },
17
-
18
- expr = (end, prec=-1) => {
19
- space()
20
-
21
- let cc = code(), op, node, i=0, mapped, from=index
22
-
23
- if (cc === end) return
24
-
25
- // parse node by token parsers (direct loop is faster than token.find)
26
- while (from===index && i < token.length) node = token[i++](cc)
27
-
28
- // unary prefix
29
- if (from===index) (op = operator(unary)) && (index += op[2], node = [op[0], expr(end, op[1])])
30
-
31
- // postfix handlers allow a.b[c](d).e, postfix operators, literals etc.
32
- else {
33
- if (space(), cc=code(), cc === end) return node
34
- for (i=0; i < postfix.length;) if ((mapped=postfix[i](node, cc)) !== node) node=mapped, i=0, space(), cc=code(); else i++
35
- }
36
- // ALT: seems to be slower
37
- // else do {space(), cc=code()} while (postfix.find((parse, mapped) => (mapped = parse(node, cc)) !== node && (node = mapped)))
38
-
39
- space()
40
-
41
- // consume expression for current precedence or higher
42
- while ((cc = code()) && cc !== end && (op = operator(binary)) && op[1] > prec) {
43
- node = [op[0], node]
44
- // consume same-op group, do..while both saves op lookups and space
45
- do { index += op[2], node.push(expr(end, op[1])) } while (char(op[2]) === op[0])
46
- space()
47
- }
48
-
49
- return node;
50
- },
51
-
52
- // tokens
53
- // 1.2e+3, .5
54
- float = (number, c, isDigit) => {
55
- number = skip(isDigit = c => c >= 48 && c <= 57) || ''
56
- if (code() === PERIOD) index++, number += '.' + skip(isDigit)
57
- if (number) {
58
- if ((c = code()) === 69 || c === 101) { // e, E
59
- index++, number += 'e'
60
- if ((c=code()) === PLUS || c === MINUS) // +-
61
- number += char(), index++
62
- number += skip(isDigit)
63
- }
64
- return parseFloat(number)
65
- }
66
- },
67
-
68
- // "a"
69
- string = (q, qc) => q === 34 ? (qc = char(), index++, qc) + skip(c => c !== q) + (index++, qc) : null,
70
-
71
- // (...exp)
72
- group = (c, node) => c === OPAREN ? (index++, node = expr(CPAREN), index++, node) : null,
73
-
74
- // var or literal
75
- id = name => (name = skip(c =>
76
- (c >= 48 && c <= 57) || // 0..9
77
- (c >= 65 && c <= 90) || // A...Z
78
- (c >= 97 && c <= 122) || // a...z
79
- c == 36 || c == 95 || // $, _,
80
- c >= 192 // any non-ASCII
81
- )) && literal.hasOwnProperty(name) ? literal[name] : name,
82
-
83
-
84
- // ------------ util
85
- code = () => current.charCodeAt(index), // current char code
86
- char = (n=1) => current.substr(index, n), // skip n chars
87
- err = (msg) => { throw Error(msg + ' at ' + index) },
88
- skip = (is=1, from=index) => { // consume N or until condition matches
89
- if (typeof is === 'number') index += is
90
- else while (is(code())) ++index > current.length && err('Unexpected end ' + is) // 1 + true === 2;
91
- return index > from ? current.slice(from, index) : null
92
- },
93
-
94
-
95
- // ----------- config
96
- token = parse.token = [ group, float, string, id ],
97
-
98
- literal = parse.literal = {true:true, false:false, null:null},
99
-
100
- postfix = parse.postfix = [
101
- // a.b[c](d), 3 in 1 for performance
102
- (node, cc, arg) => {
103
- if (cc === PERIOD) index++, space(), node = ['.', node, '"'+id()+'"']
104
- else if (cc === OBRACK) index++, node = ['.', node, expr(CBRACK)], index++
105
- else if (cc === OPAREN)
106
- index++, arg=expr(CPAREN),
107
- node = Array.isArray(arg) && arg[0]===',' ? (arg[0]=node, arg) : arg == null ? [node] : [node, arg],
108
- index++
109
- return node
110
- },
111
-
112
- // a++, a--
113
- (node, cc) => (cc===0x2b || cc===0x2d) && current.charCodeAt(index+1)===cc ? [skip(2), node] : node,
114
- ],
115
-
116
- unary = parse.unary = {
117
- '-': 17,
118
- '!': 17,
119
- '+': 17,
120
- '++': 17,
121
- '--': 17
122
- },
123
-
124
- binary = parse.binary = {
125
- ',': 1,
126
- '||': 6, '&&': 7, '|': 8, '^': 9, '&': 10,
127
- '==': 11, '!=': 11,
128
- '<': 12, '>': 12, '<=': 12, '>=': 12,
129
- '<<': 13, '>>': 13, '>>>': 13,
130
- '+': 14, '-': 14,
131
- '*': 15, '/': 15, '%': 15
132
- }
133
-
134
- export default parse