subscript 3.0.3 → 5.2.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
- # <!--<img alt="subscript" src="/subscript2.svg" height=42/>--> sub͘<em>script</em> <!--<sub>SUB͘<em>SCRIPT</em></sub>-->
1
+ # <img alt="subscript" src="/subscript2.svg" height=42/> <!--sub͘<em>script</em>--> <!--<sub>SUB͘<em>SCRIPT</em></sub>-->
2
2
 
3
3
  _Subscript_ is micro-language with common syntax subset of C++, JS, Java, Python, Go, Rust, Swift, Objective C, Kotlin etc.<br/>
4
4
 
5
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,17 +26,28 @@ _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:
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/5.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.
31
+
32
+
33
+ ## Evaluation
34
+
35
+ _Subscript_ parser generates lispy calltree (compatible with [frisk](https://npmjs.com/frisk)), which is compared to esprima AST has:
36
+
37
+ + minimal possible overhead
38
+ + clear precedence
39
+ + overloading by context
40
+ + manual evaluation and debugging
41
+ + conventional form
42
+ + one-liner docs:
31
43
 
32
44
  ```js
33
45
  import {evaluate} from 'subscript.js'
34
46
 
35
47
  evaluate(['+', ['*', 'min', 60], '"sec"'], { min: 5 }) // min*60 + "sec" == "300sec"
36
- ```
37
-
48
+ ```
38
49
 
39
- ## Operators
50
+ ## Extending
40
51
 
41
52
  Default operators include common operators for the listed languages in the following precedence:
42
53
 
@@ -53,17 +64,15 @@ Default operators include common operators for the listed languages in the follo
53
64
  * `&&`
54
65
  * `||`
55
66
 
56
- All other operators can be extended via `parse.binary`, `parse.unary` and `evaluate.operator`.
67
+ Operators can be extended via `parse.operator(str, prec, type)` and `evaluate.operator(str, fn)` functions for any unary/binary/postfix operators, calls, props, groups, arrays, objects etc.
57
68
 
58
69
  ```js
59
70
  import { parse, evaluate } from 'subscript.js'
60
71
 
61
- // add precedences
62
- parse.binary['=>'] = 10
72
+ parse.operator('=>', 10) // precedence=10, type=default (0 - binary, 1 - postfix, -1 - prefix)
63
73
 
64
- // define evaluators
65
- evaluate.operator['=>'] = ( args, body ) => evaluate(body, args)
66
- evaluate.operator['|'] = ( a, ...b ) => a.pipe(...b)
74
+ evaluate.operator('=>', ( args, body ) => evaluate(body, args))
75
+ evaluate.operator('|', ( a, ...b ) => a.pipe(...b))
67
76
 
68
77
  let tree = parse(`
69
78
  interval(350)
@@ -74,28 +83,36 @@ let tree = parse(`
74
83
  evaluate(tree, { Math, map, take, interval, gaussian })
75
84
  ```
76
85
 
77
- ## Extending
86
+ ---
78
87
 
79
- By default subscript detects the following tokens:
88
+ Default tokens include:
80
89
 
81
- * `"` strings
90
+ * `"abc"` strings
82
91
  * `1.2e+3` floats
83
- * `true`, `false`, `null` literals
84
- * `()` expression groups or fn calls
85
- * `.`, `[]` property access
92
+ * identifiers
93
+
94
+ Tokens are extensible via `parse.token` list, can be added support of _literals_, _regexes_, _strings_, _numbers_ and others.
86
95
 
87
- Literals can be extended via `parse.literal` dict.
96
+ ```js
97
+ import parse, {char} from 'subscript/parse.js'
98
+ import evaluate from 'subscript/evaluate.js'
99
+
100
+ conts ctx = {x:1}
101
+ parse.token.unshift(c => char(4) === 'this' ? ctx : null)
102
+ evaluate(parse(`this.x`)) // 1
103
+ ```
88
104
 
89
- Token parsers are extensible via `parse.token` list, can be added support of _regex_, _array_, _object_, _interpolated string_ and others.
105
+ ---
90
106
 
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`.
107
+ Comments can be added via extending `parse.space`. See [justin.js](./justin.js) for more examples.
92
108
 
93
109
 
94
110
  ## Justin
95
111
 
96
112
  _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:
113
+ It adds support of:
98
114
 
115
+ + `===`, `!==` operators
99
116
  + `**` binary operator
100
117
  + `~` unary operator
101
118
  + `'` strings
@@ -105,8 +122,8 @@ It adds support for:
105
122
  + `in` binary operator
106
123
  + `;` expression separator
107
124
  + unary word operators
108
- <!-- + `//, /* */` comments -->
109
- <!-- + `undefined` literal -->
125
+ + `//`, `/* */` comments
126
+ + `true`, `false`, `null`, `undefined` literals
110
127
  <!-- + `?` chaining operator -->
111
128
  <!-- + `...x` unary operator -->
112
129
  <!-- + strings interpolation -->
@@ -263,8 +280,8 @@ Subscript shows relatively good performance within other evaluators:
263
280
  // 1 + (a * b / c % d) - 2.0 + -3e-3 * +4.4e4 / f.g[0] - i.j(+k == 1)(0)
264
281
  // parse 30k times
265
282
 
266
- subscript: ~280 ms
267
- jsep: ~281 ms
283
+ subscript: ~220 ms
284
+ jsep: ~280 ms
268
285
  expr-eval: ~480 ms
269
286
  jexl: ~1200 ms
270
287
  new Function: ~1400 ms
package/evaluate.js CHANGED
@@ -4,54 +4,22 @@ export const isCmd = a => Array.isArray(a) && (typeof a[0] === 'string' || isCmd
4
4
  evaluate = (s, ctx={}, c, op) => {
5
5
  if (isCmd(s)) {
6
6
  c = s[0]
7
- if (typeof c === 'string') op = operator[c]
7
+ if (typeof c === 'string') op = lookup[c]
8
8
  c = op || evaluate(c, ctx) // [[a,b], c]
9
9
  if (typeof c !== 'function') return c
10
-
11
10
  return c.call(...s.map(a => evaluate(a,ctx)))
12
11
  }
13
12
  if (s && typeof s === 'string')
14
13
  return s[0] === '"' ? s.slice(1,-1)
15
- : s[0]==='@' ? s.slice(1)
16
- : s in ctx ? ctx[s] : s
14
+ : s[0]==='@' ? s.slice(1)
15
+ : s in ctx ? ctx[s] : s
17
16
 
18
17
  return s
19
18
  },
19
+ lookup = {},
20
20
 
21
21
  // op evaluators
22
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
- }
23
+ operator = evaluate.operator = (op, fn) => lookup[op] = fn.length == 2 ? (...a)=>a.reduce(fn) : fn
56
24
 
57
25
  export default evaluate
package/justin.js CHANGED
@@ -1,97 +1,222 @@
1
1
  // justin lang https://github.com/endojs/Jessie/issues/66
2
- import {evaluate, operator} from './evaluate.js'
3
- import {parse, binary, unary, postfix, token, literal,
4
- code, char, skip, space, expr} from './parse.js'
5
-
6
- // 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
- (
2
+ import {evaluate} from './evaluate.js'
3
+ import {parse, code, char, skip, expr, err} from './parse.js'
4
+
5
+ const PERIOD=46, OPAREN=40, CPAREN=41, CBRACK=93, SPACE=32,
6
+
7
+ PREC_SEQ=1, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
8
+ 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
9
+
10
+
11
+ // tokens
12
+ const v = v => ({valueOf:()=>v})
13
+ parse.token.push(
14
+ // TODO: better parser
15
+ // 1.2e+3, .5 - fast & small version, but consumes corrupted nums as well
16
+ (number) => (
17
+ (number = skip(c => (c > 47 && c < 58) || c == PERIOD)) && (
18
+ (code() == 69 || code() == 101) && (number += skip(2) + skip(c => c >= 48 && c <= 57)),
19
+ isNaN(number = new Number(number)) ? err('Bad number') : number
20
+ )
21
+ ),
22
+
23
+ // "' with /
24
+ (q, qc, c, str) => {
25
+ if (q !== 34 && q !== 39) return
26
+ qc = char(), skip(), str = ''
27
+ while (c=code(), c-q) {
28
+ if (c === 92) skip(), str += escape[char()] || char(); else str+=char()
29
+ skip()
30
+ }
31
+ return skip(), qc + str + qc
32
+ },
33
+
34
+ // {}
35
+ (cc, node) => (
36
+ cc === 123 && (skip(), node = mapObj(['{', expr(0,125)]), skip(), node)
37
+ ),
38
+
39
+ // literal
40
+ c =>
41
+ c === 116 && char(4) === 'true' && skip(4) ? v(true) :
42
+ c === 102 && char(5) === 'false' && skip(5) ? v(false) :
43
+ c === 110 && char(4) === 'null' && skip(4) ? v(null) :
44
+ c === 117 && char(9) === 'undefined' && skip(9) ? v(undefined) :
45
+ null,
46
+
47
+ // id
48
+ c => skip(c =>
27
49
  (c >= 48 && c <= 57) || // 0..9
28
50
  (c >= 65 && c <= 90) || // A...Z
29
51
  (c >= 97 && c <= 122) || // a...z
30
52
  c == 36 || c == 95 || // $, _,
31
- c >= 192 // any non-ASCII
32
- ) && !binary[String.fromCharCode(c)]
33
- )),
34
-
35
-
36
- // **
37
- binary['**'] = 16
38
- operator['**'] = (...args)=>args.reduceRight((a,b)=>Math.pow(b,a))
39
-
40
- // ~
41
- unary['~'] = 17
42
- operator['~'] = a=>~a
53
+ (c >= 192 && c != 215 && c != 247) // any non-ASCII
54
+ )
55
+ )
43
56
 
44
- // ...
45
- // unary[1]['...']=true
57
+ const escape = {n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', v:'\v'}
46
58
 
47
- // ;
48
- binary[';'] = 1
59
+ // {}
60
+ const mapObj = (n, args) => (
61
+ args = !n[1] ? [] :
62
+ (n[1][0]==':') ? [n[1]] :
63
+ (n[1][0]==',') ? n[1].slice(1) : args,
64
+ ['{', ...args]
65
+ )
49
66
 
50
- // ?:
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
- })
60
67
 
61
68
  // /**/, //
62
- // comments['/*']='*/'
63
- // comments['//']='\n'
64
-
65
- // in
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)
90
- return ['{', ...args]
69
+ parse.space = cc => {
70
+ while (cc = code(), cc <= 32) {
71
+ skip()
72
+ if (code() === 47)
73
+ // /**/
74
+ if (code(1) === 42) skip(2), skip(c => c !== 42 && code(1) !== 47), skip(2)
75
+ // //
76
+ else if (code(1) === 47) skip(2), skip(c => c >= 32)
77
+ }
78
+ return cc
91
79
  }
92
80
 
93
81
 
82
+ // operators
83
+ const addOps = (add, stride=2, list) => {
84
+ for (let i = 0; i < list.length; i+=stride) add(list[i], list[i+1], list[i+2])
85
+ }
86
+
87
+ addOps(parse.operator, 3, [
88
+ // subscript ones
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) => (skip(), node = ['.', node, expr(0,CBRACK).valueOf()], skip(), node),
136
+ ']',,,
137
+
138
+ // a(b)
139
+ '(', PREC_CALL, (node,b) => ( skip(), b=expr(0,CPAREN), skip(),
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 && (skip(), b=expr(0,CPAREN) || err(), skip(), b),
144
+ ')',,,
145
+
146
+ // justin extension
147
+ ';', 1,,
148
+ '===', 9,,
149
+ '!==', 9,,
150
+ '**', 14,,
151
+ '~', 13, -1,
152
+ '?', 3, (node) => {
153
+ if (!node) err('Expected expression')
154
+ let a, b
155
+ skip(), parse.space(), a = expr()
156
+ if (code() !== 58) err('Expected :')
157
+ skip(), parse.space(), b = expr()
158
+ return ['?:', node, a, b]
159
+ },
160
+ '}',,,
161
+ ':', 0,,
162
+ 'in', 10, (node) => code(2) <= 32 && [skip(2), '"'+node+'"', expr(10)],
163
+ '[', 20, (node,arg) => !node && (
164
+ skip(), arg=expr(0,93), skip(),
165
+ !arg ? ['['] : arg[0] === ',' ? (arg[0]='[',arg) : ['[',arg]
166
+ )
167
+ ])
168
+
169
+ addOps(evaluate.operator, 2, [
170
+ // subscript
171
+ '!', a=>!a,
172
+ '++', a=>++a,
173
+ '--', a=>--a,
174
+
175
+ '.', (a,b)=>a?a[b]:a,
176
+
177
+ '%', (a,b)=>a%b,
178
+ '/', (a,b)=>a/b,
179
+ '*', (a,b)=>a*b,
180
+
181
+ '+', (a,b)=>a+b,
182
+ '-', (...a)=>a.length < 2 ? -a : a.reduce((a,b)=>a-b),
183
+
184
+ '>>>', (a,b)=>a>>>b,
185
+ '>>', (a,b)=>a>>b,
186
+ '<<', (a,b)=>a<<b,
187
+
188
+ '>=', (a,b)=>a>=b,
189
+ '>', (a,b)=>a>b,
190
+ '<=', (a,b)=>a<=b,
191
+ '<', (a,b)=>a<b,
192
+
193
+ '!=', (a,b)=>a!=b,
194
+ '==', (a,b)=>a==b,
195
+
196
+ '&', (a,b)=>a&b,
197
+ '^', (a,b)=>a^b,
198
+ '|', (a,b)=>a|b,
199
+ '&&', (...a)=>a.every(Boolean),
200
+ '||', (...a)=>a.some(Boolean),
201
+ ',', (a,b)=>(a,b),
202
+
203
+ // justin extension
204
+ '**', (...args)=>args.reduceRight((a,b)=>Math.pow(b,a)),
205
+ '~', a=>~a,
206
+ '?:', (a,b,c)=>a?b:c,
207
+ 'in', (a,b)=>a in b,
208
+
209
+ // []
210
+ '[', (...args) => Array(...args),
211
+ // as operator it's faster to lookup (no need to call extra rule check), smaller and no conflict with word names
212
+ '{', (...args)=>Object.fromEntries(args),
213
+ ':', (a,b)=>[a,b]
214
+ ])
215
+
216
+ // TODO ...
94
217
  // TODO: strings interpolation
95
218
 
96
- export { default } from './subscript.js';
97
219
  export { parse, evaluate }
220
+
221
+ // code → evaluator
222
+ export default s => (s = typeof s == 'string' ? parse(s) : s, ctx => evaluate(s, ctx))
package/justin.min.js CHANGED
@@ -1 +1 @@
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};
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,n={},o=(a.operator=(e,r)=>n[e]=2==r.length?(...e)=>e.reduce(r):r,(t,a)=>(r=t,e=0,a=p(),e<r.length?l():a.valueOf())),l=(t="Bad syntax")=>{throw Error(t+" `"+r[e]+"` at "+e)},s=(t=1,a=e)=>{if("number"==typeof t)e+=t;else for(;t(u());)e++;return r.slice(a,e)},u=(t=0)=>r.charCodeAt(e+t),f=(t=1)=>r.substr(e,t),p=(e=0,r,t,a,n=0,s,u)=>{for(;(t=o.space())&&(u=v[t]?.(a,e)||!a&&c(t));)a=u;return r&&t!==r&&l("Unclosed paren"),a},i=(o.space=r=>{for(;(r=u())<=32;)e++;return r},o.token=[]),c=(e,r=0,t)=>{for(;r<i.length;)if(t=i[r++](e))return t},v=[],d=(o.operator=(r,t=0,a=0,n,i=r.charCodeAt(0),c=r.length,d=v[i],h=r.toUpperCase()!==r,y)=>(y=c<2?h?e=>u(1)<=32:e=>1:h?e=>f(c)==r&&u(c)<=32:e=>f(c)==r,n=a?a>0?e=>e&&[s(c),e]:a<0?e=>!e&&[s(c),(p(t-1)||l()).valueOf()]:a:a=>{a=[r,a||l()];do{e+=c,a.push((p(t)||l()).valueOf())}while(o.space()==i&&y());return a},v[i]=(e,r)=>r<t&&y()&&n(e)||d&&d(e,r)),15),h=e=>({valueOf:()=>e});o.token.push((e=>(e=s((e=>e>47&&e<58||46==e)))&&((69==u()||101==u())&&(e+=s(2)+s((e=>e>=48&&e<=57))),isNaN(e=new Number(e))?l("Bad number"):e)),((e,r,t,a)=>{if(34===e||39===e){for(r=f(),s(),a="";(t=u())-e;)92===t?(s(),a+=y[f()]||f()):a+=f(),s();return s(),r+a+r}}),((e,r)=>123===e&&(s(),r=g(["{",p(0,125)]),s(),r)),(e=>116===e&&"true"===f(4)&&s(4)?h(!0):102===e&&"false"===f(5)&&s(5)?h(!1):110===e&&"null"===f(4)&&s(4)?h(null):117===e&&"undefined"===f(9)&&s(9)?h(void 0):null),(e=>s((e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e))));var y={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},g=(e,r)=>(r=e[1]?":"==e[1][0]?[e[1]]:","==e[1][0]?e[1].slice(1):r:[],["{",...r]);o.space=e=>{for(;(e=u())<=32;)s(),47===u()&&(42===u(1)?(s(2),s((e=>42!==e&&47!==u(1))),s(2)):47===u(1)&&(s(2),s((e=>e>=32))));return e};var O=(e,r=2,t)=>{for(let a=0;a<t.length;a+=r)e(t[a],t[a+1],t[a+2])};O(o.operator,3,[",",1,,"|",6,,"||",4,,"&",8,,"&&",5,,"^",7,,"==",9,,"!=",9,,">",10,,">=",10,,">>",11,,">>>",11,,"<",10,,"<=",10,,"<<",11,,"+",12,,"+",d,-1,"++",d,-1,"++",d,1,"-",12,,"-",d,-1,"--",d,-1,"--",d,1,"!",d,-1,"*",13,,"/",13,,"%",13,,".",18,(e,r)=>e&&[s(),e,"string"==typeof(r=p(18))?'"'+r+'"':r.valueOf()],"[",18,e=>(s(),e=[".",e,p(0,93).valueOf()],s(),e),"]",,,"(",18,(e,r)=>(s(),r=p(0,41),s(),Array.isArray(r)&&","===r[0]?(r[0]=e,r):r?[e,r.valueOf()]:[e]),"(",19,(e,r)=>!e&&(s(),r=p(0,41)||l(),s(),r),")",,,";",1,,"===",9,,"!==",9,,"**",14,,"~",13,-1,"?",3,e=>{let r,t;return e||l("Expected expression"),s(),o.space(),r=p(),58!==u()&&l("Expected :"),s(),o.space(),t=p(),["?:",e,r,t]},"}",,,":",0,,"in",10,e=>u(2)<=32&&[s(2),'"'+e+'"',p(10)],"[",20,(e,r)=>!e&&(s(),r=p(0,93),s(),r?","===r[0]?(r[0]="[",r):["[",r]:["["])]),O(a.operator,2,["!",e=>!e,"++",e=>++e,"--",e=>--e,".",(e,r)=>e&&e[r],"%",(e,r)=>e%r,"/",(e,r)=>e/r,"*",(e,r)=>e*r,"+",(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,r)=>r,"**",(...e)=>e.reduceRight(((e,r)=>Math.pow(r,e))),"~",e=>~e,"?:",(e,r,t)=>e?r:t,"in",(e,r)=>e in r,"[",(...e)=>Array(...e),"{",(...e)=>Object.fromEntries(e),":",(e,r)=>[e,r]]);var b=e=>(e="string"==typeof e?o(e):e,r=>a(e,r));export{b as default,a as evaluate,o as parse};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "subscript",
3
- "version": "3.0.3",
3
+ "version": "5.2.0",
4
4
  "description": "Microlanguage with common syntax for JS/C++/Python/Rust",
5
5
  "main": "subscript.js",
6
6
  "type": "module",
package/parse.js CHANGED
@@ -1,134 +1,64 @@
1
- const PERIOD = 46, OPAREN = 40, CPAREN = 41, OBRACK = 91, CBRACK = 93, PLUS = 43, MINUS = 45
1
+ const SPACE=32
2
2
 
3
- export let index, current, lastOp, end
3
+ // current string & index
4
+ let idx, cur
4
5
 
5
- export const parse = (str, tree) => (current=str, index=lastOp=0, tree=expr(), index < current.length ? err() : tree),
6
+ export const parse = (str, tree) => (cur=str, idx=0, tree=expr(), idx<cur.length ? err() : tree.valueOf()),
6
7
 
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
8
+ err = (msg='Bad syntax') => { throw Error(msg + ' `' + cur[idx] + '` at ' + idx) },
23
9
 
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
10
+ skip = (is=1, from=idx) => {
11
+ if (typeof is === 'number') idx += is
12
+ else while (is(code())) idx++;
13
+ return cur.slice(from, idx)
26
14
  },
27
15
 
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])])
16
+ code = (i=0) => cur.charCodeAt(idx+i),
40
17
 
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)))
18
+ char = (n=1) => cur.substr(idx, n),
48
19
 
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
- }
20
+ // a + b - c
21
+ expr = (prec=0, end, cc, node, i=0, map, newNode) => {
22
+ // chunk/token parser
23
+ while (
24
+ (cc=parse.space()) && (newNode = lookup[cc]?.(node, prec) || (!node && token(cc)) )
25
+ ) node = newNode;
56
26
 
57
- if (curEnd) end = code() !== curEnd ? err('Unclosed paren') : prevEnd
58
- // if (node == null) err('Missing argument')
27
+ if (end && cc !== end) err('Unclosed paren')
59
28
 
60
- return node;
29
+ return node
61
30
  },
62
31
 
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
-
32
+ // can be extended with comments, so we export
33
+ space = parse.space = cc => { while (cc = code(), cc <= SPACE) idx++; return cc },
34
+
35
+ // tokens
36
+ tokens = parse.token = [],
37
+ token = (c,i=0,node) => { while(i<tokens.length) if (node = tokens[i++](c)) return node },
38
+
39
+ // operator lookup table
40
+ lookup = [],
41
+
42
+ // create operator checker/mapper (see examples)
43
+ // @param op is operator string
44
+ // @param prec is operator precedenc to check
45
+ // @param map is either number +1 - postfix unary, -1 prefix unary, 0 binary, else - custom mapper function
46
+ operator = parse.operator = (op, prec=0, type=0, map, c=op.charCodeAt(0), l=op.length, prev=lookup[c], word=op.toUpperCase()!==op, isop) => (
47
+ isop = l<2 ? // word operator must have space after
48
+ !word ? c=>1 : c=>code(1)<=SPACE :
49
+ !word ? c=>char(l)==op : c=>char(l)==op&&code(l)<=SPACE,
50
+
51
+ map = !type ? node => { // binary, consume same-op group
52
+ node = [op, node || err()]
53
+ // in order to support literal tokens, we call valueOf any time we create or modify calltree node
54
+ do { idx+=l, node.push((expr(prec) || err()).valueOf()) } while (parse.space()==c && isop())
55
+ return node
56
+ } :
57
+ type > 0 ? node => node && [skip(l), node] : // postfix unary
58
+ type < 0 ? node => !node && [skip(l), (expr(prec-1) || err()).valueOf()] : // prefix unary
59
+ type,
60
+
61
+ lookup[c] = (node, curPrec) => curPrec < prec && isop() && map(node) || (prev && prev(node, curPrec))
62
+ )
133
63
 
134
64
  export default parse
package/subscript.js CHANGED
@@ -1,5 +1,128 @@
1
- import parse from './parse.js'
2
- import evaluate from './evaluate.js'
1
+ import parse, {skip, expr, code, tokens, operator as parseOp} from './parse.js'
2
+ import evaluate, {operator as evalOp} from './evaluate.js'
3
+
4
+ const PERIOD=46, OPAREN=40, CPAREN=41, CBRACK=93, SPACE=32,
5
+
6
+ PREC_SEQ=1, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
7
+ PREC_EQ=9, PREC_COMP=10, PREC_SHIFT=11, PREC_SUM=12, PREC_MULT=13, PREC_UNARY=15, PREC_POSTFIX=16, PREC_CALL=18, PREC_GROUP=19
8
+
9
+ tokens.push(
10
+ // 1.2e+3, .5 - fast & small version, but consumes corrupted nums as well
11
+ (number) => (
12
+ (number = skip(c => (c > 47 && c < 58) || c == PERIOD)) && (
13
+ (code() == 69 || code() == 101) && (number += skip(2) + skip(c => c >= 48 && c <= 57)),
14
+ isNaN(number = new Number(number)) ? err('Bad number') : number
15
+ )
16
+ ),
17
+ // "a"
18
+ (q, qc) => q == 34 && (skip() + skip(c => c-q) + skip()),
19
+ // id
20
+ c => skip(c =>
21
+ (c >= 48 && c <= 57) || // 0..9
22
+ (c >= 65 && c <= 90) || // A...Z
23
+ (c >= 97 && c <= 122) || // a...z
24
+ c == 36 || c == 95 || // $, _,
25
+ c >= 192 // any non-ASCII
26
+ )
27
+ )
28
+
29
+ const addOps = (add, stride=2, list) => {
30
+ for (let i = 0; i < list.length; i+=stride) add(list[i], list[i+1], list[i+2])
31
+ }
32
+
33
+ addOps(parseOp, 3, [
34
+ ',', PREC_SEQ,,
35
+
36
+ '|', PREC_OR,,
37
+ '||', PREC_SOME,,
38
+
39
+ '&', PREC_AND,,
40
+ '&&', PREC_EVERY,,
41
+
42
+ '^', PREC_XOR,,
43
+
44
+ // ==, !=
45
+ '==', PREC_EQ,,
46
+ '!=', PREC_EQ,,
47
+
48
+ // > >= >> >>>, < <= <<
49
+ '>', PREC_COMP,,
50
+ '>=', PREC_COMP,,
51
+ '>>', PREC_SHIFT,,
52
+ '>>>', PREC_SHIFT,,
53
+ '<', PREC_COMP,,
54
+ '<=', PREC_COMP,,
55
+ '<<', PREC_SHIFT,,
56
+
57
+ // + ++ - --
58
+ '+', PREC_SUM,,
59
+ '+', PREC_UNARY, -1,
60
+ '++', PREC_UNARY, -1,
61
+ '++', PREC_UNARY, +1,
62
+ '-', PREC_SUM,,
63
+ '-', PREC_UNARY, -1,
64
+ '--', PREC_UNARY, -1,
65
+ '--', PREC_UNARY, +1,
66
+
67
+ // ! ~
68
+ '!', PREC_UNARY, -1,
69
+
70
+ // * / %
71
+ '*', PREC_MULT,,
72
+ '/', PREC_MULT,,
73
+ '%', PREC_MULT,,
74
+
75
+ // a.b
76
+ '.', PREC_CALL, (node,b) => node && [skip(),node, typeof (b = expr(PREC_CALL)) === 'string' ? '"' + b + '"' : b.valueOf()],
77
+
78
+ // a[b]
79
+ '[', PREC_CALL, (node) => (skip(), node = ['.', node, expr(0,CBRACK).valueOf()], skip(), node),
80
+ ']',,,
81
+
82
+ // a(b)
83
+ '(', PREC_CALL, (node,b) => ( skip(), b=expr(0,CPAREN), skip(),
84
+ Array.isArray(b) && b[0]===',' ? (b[0]=node, b) : b ? [node, b.valueOf()] : [node]
85
+ ),
86
+ // (a+b)
87
+ '(', PREC_GROUP, (node,b) => !node && (skip(), b=expr(0,CPAREN) || err(), skip(), b),
88
+ ')',,,
89
+ ])
90
+
91
+
92
+ // evaluators
93
+ addOps(evalOp, 2, [
94
+ '!', a=>!a,
95
+ '++', a=>++a,
96
+ '--', a=>--a,
97
+
98
+ '.', (a,b)=>a?a[b]:a,
99
+
100
+ '%', (a,b)=>a%b,
101
+ '/', (a,b)=>a/b,
102
+ '*', (a,b)=>a*b,
103
+
104
+ '+', (a,b)=>a+b,
105
+ '-', (...a)=>a.length < 2 ? -a : a.reduce((a,b)=>a-b),
106
+
107
+ '>>>', (a,b)=>a>>>b,
108
+ '>>', (a,b)=>a>>b,
109
+ '<<', (a,b)=>a<<b,
110
+
111
+ '>=', (a,b)=>a>=b,
112
+ '>', (a,b)=>a>b,
113
+ '<=', (a,b)=>a<=b,
114
+ '<', (a,b)=>a<b,
115
+
116
+ '!=', (a,b)=>a!=b,
117
+ '==', (a,b)=>a==b,
118
+
119
+ '&', (a,b)=>a&b,
120
+ '^', (a,b)=>a^b,
121
+ '|', (a,b)=>a|b,
122
+ '&&', (...a)=>a.every(Boolean),
123
+ '||', (...a)=>a.some(Boolean),
124
+ ',', (a,b)=>(a,b)
125
+ ])
3
126
 
4
127
  export { parse, evaluate }
5
128
 
package/subscript.min.js CHANGED
@@ -1 +1 @@
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};
1
+ var e,r,a=(a,o)=>(r=a,e=0,o=l(),e<r.length?t():o.valueOf()),t=(a="Bad syntax")=>{throw Error(a+" `"+r[e]+"` at "+e)},o=(a=1,t=e)=>{if("number"==typeof a)e+=a;else for(;a(n());)e++;return r.slice(t,e)},n=(a=0)=>r.charCodeAt(e+a),s=(a=1)=>r.substr(e,a),l=(e=0,r,o,n,s=0,l,f)=>{for(;(o=a.space())&&(f=p[o]?.(n,e)||!n&&u(o));)n=f;return r&&o!==r&&t("Unclosed paren"),n},f=(a.space=r=>{for(;(r=n())<=32;)e++;return r},a.token=[]),u=(e,r=0,a)=>{for(;r<f.length;)if(a=f[r++](e))return a},p=[],i=a.operator=(r,f=0,u=0,i,c=r.charCodeAt(0),y=r.length,h=p[c],g=r.toUpperCase()!==r,v)=>(v=y<2?g?e=>n(1)<=32:e=>1:g?e=>s(y)==r&&n(y)<=32:e=>s(y)==r,i=u?u>0?e=>e&&[o(y),e]:u<0?e=>!e&&[o(y),(l(f-1)||t()).valueOf()]:u:o=>{o=[r,o||t()];do{e+=y,o.push((l(f)||t()).valueOf())}while(a.space()==c&&v());return o},p[c]=(e,r)=>r<f&&v()&&i(e)||h&&h(e,r)),c=a,y=e=>Array.isArray(e)&&("string"==typeof e[0]||y(e[0])),h=(e,r={},a,t)=>y(e)?("string"==typeof(a=e[0])&&(t=g[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,g={},v=h.operator=(e,r)=>g[e]=2==r.length?(...e)=>e.reduce(r):r,d=h,A=15;f.push((e=>(e=o((e=>e>47&&e<58||46==e)))&&((69==n()||101==n())&&(e+=o(2)+o((e=>e>=48&&e<=57))),isNaN(e=new Number(e))?err("Bad number"):e)),((e,r)=>34==e&&o()+o((r=>r-e))+o()),(e=>o((e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192))));var O=(e,r=2,a)=>{for(let t=0;t<a.length;t+=r)e(a[t],a[t+1],a[t+2])};O(i,3,[",",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&&[o(),e,"string"==typeof(r=l(18))?'"'+r+'"':r.valueOf()],"[",18,e=>(o(),e=[".",e,l(0,93).valueOf()],o(),e),"]",,,"(",18,(e,r)=>(o(),r=l(0,41),o(),Array.isArray(r)&&","===r[0]?(r[0]=e,r):r?[e,r.valueOf()]:[e]),"(",19,(e,r)=>!e&&(o(),r=l(0,41)||err(),o(),r),")",,,]),O(v,2,["!",e=>!e,"++",e=>++e,"--",e=>--e,".",(e,r)=>e&&e[r],"%",(e,r)=>e%r,"/",(e,r)=>e/r,"*",(e,r)=>e*r,"+",(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,r)=>r]);var m=e=>(e="string"==typeof e?c(e):e,r=>d(e,r));export{m as default,d as evaluate,c as parse};