subscript 5.4.0 → 6.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
@@ -3,18 +3,18 @@
3
3
  <a href="http://npmjs.org/subscript"><img src="https://img.shields.io/npm/v/subscript?color=indianred"/></a>
4
4
  <a href="http://microjs.com/#subscript"><img src="https://img.shields.io/badge/microjs-subscript-blue?color=darkslateblue"/></a>
5
5
 
6
- _Subscript_ is micro-language with common syntax subset of C++, JS, Java, Python, Go, Rust, Swift, Objective C, Kotlin etc.<br/>
6
+ _Subscript_ is micro-language with common syntax subset of C++, JS, Java, Python, Go, Rust etc.<br/>
7
7
 
8
- * Well-known syntax
9
- * Any _subscript_ fragment can be copy-pasted to any target language
10
- * It's tiny <sub>![npm bundle size](https://img.shields.io/bundlephobia/minzip/subscript/latest?color=brightgreen&label=gzip)</sub>
11
- * It's :rocket: fast ([see performance](#performance))
8
+ * Standard conventional syntax
9
+ * Any fragment can be copy-pasted to any target language
10
+ * Tiny size <sub><a href="https://bundlephobia.com/package/subscript@6.0.0"><img alt="npm bundle size" src="https://img.shields.io/bundlephobia/minzip/subscript/latest?color=brightgreen&label=gzip"/></a></sub>
11
+ * :rocket: fast ([performance](#performance))
12
12
  * Configurable & extensible
13
- * Trivial to use...
13
+ * Trivial to use
14
14
 
15
15
  ```js
16
- import subscript from 'subscript.js'
17
- let fn = subscript(`a.b + c(d - 1)`)
16
+ import script from 'subscript.js'
17
+ let fn = script`a.b + c(d - 1)`
18
18
  fn({ a: { b:1 }, c: x => x * 2, d: 3 }) // 5
19
19
  ```
20
20
 
@@ -29,32 +29,12 @@ _Subscript_ is designed to be useful for:
29
29
  * sandboxes, playgrounds, safe eval
30
30
  * custom DSL
31
31
 
32
- [_Jsep_](https://github.com/EricSmekens/jsep) is generally fine for the listed tasks, unless you need dependencies as small as possible.
33
- _Subscript_ has [2.5kb](https://npmfs.com/package/subscript/5.2.0/subscript.min.js) footprint vs [11.4kb](https://npmfs.com/package/jsep/1.2.0/dist/jsep.min.js) _jsep_ + [4.5kb](https://npmfs.com/package/expression-eval/5.0.0/dist/expression-eval.module.js) _expression-eval_, with _jsep_ test coverage and better performance.
32
+ _Subscript_ has [2kb](https://npmfs.com/package/subscript/6.0.0/subscript.min.js) footprint, compared to [11.4kb](https://npmfs.com/package/jsep/1.2.0/dist/jsep.min.js) _jsep_ + [4.5kb](https://npmfs.com/package/expression-eval/5.0.0/dist/expression-eval.module.js) _expression-eval_, with better test coverage and better performance.
34
33
 
35
34
 
36
- ## Evaluation
35
+ ## Design
37
36
 
38
- _Subscript_ parser generates lispy calltree (compatible with [frisk](https://npmjs.com/frisk)), which is compared to esprima AST has:
39
-
40
- + minimal possible overhead
41
- + clear precedence
42
- + overloading by context
43
- + manual evaluation and debugging
44
- + conventional form
45
- + one-liner docs:
46
-
47
- ```js
48
- import {evaluate} from 'subscript.js'
49
-
50
- evaluate(['+', ['*', 'min', 60], '"sec"'], { min: 5 }) // min*60 + "sec" == "300sec"
51
- ```
52
-
53
- ## Extending
54
-
55
- ### Operators
56
-
57
- Default operators include common operators for the listed languages in the following precedence:
37
+ Default operators (precedence order):
58
38
 
59
39
  * `++ --` unary postfix
60
40
  * `! + - ++ --` unary prefix
@@ -69,74 +49,75 @@ Default operators include common operators for the listed languages in the follo
69
49
  * `&&`
70
50
  * `||`
71
51
 
72
- 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.
52
+ Default literals:
73
53
 
74
- ```js
75
- import { parse, evaluate } from 'subscript.js'
54
+ * `"abc"` strings
55
+ * `1.2e+3` numbers
56
+
57
+ Everything else can be extended via `parse.set(operator, precedence, fn)` for unary or binary operators (detected by number of arguments in `fn`), or via `parse.set(operator, parser, precedence)` for custom tokens.
58
+
59
+ See [subscript.js](subscript.js) or [justin.js](./justin.js) for examples.
76
60
 
77
- parse.operator('=>', 10) // precedence=10, type=default (0 - binary, 1 - postfix, -1 - prefix)
61
+ <!--
62
+ Operators can be extended via .
63
+
64
+ ```js
65
+ import script from 'subscript.js'
78
66
 
79
- evaluate.operator('=>', ( args, body ) => evaluate(body, args))
80
- evaluate.operator('|', ( a, b ) => a.pipe(b))
67
+ script.set('|', 10, ( a, b ) => a.pipe(b))
81
68
 
82
- let tree = parse(`
69
+ let evaluate = script(`
83
70
  interval(350)
84
71
  | take(25)
85
72
  | map(gaussian)
86
- | map(num => "•".repeat(Math.floor(num * 65)))
73
+ | "•".repeat(Math.floor(it * 65)))
87
74
  `)
88
- evaluate(tree, { Math, map, take, interval, gaussian })
75
+ evaluate({ Math, map, take, interval, gaussian })
89
76
  ```
90
77
 
91
- ### Tokens
92
-
93
- Default tokens include:
94
-
95
- * `"abc"` strings
96
- * `1.2e+3` floats
97
- * `name` identifiers
98
-
99
- Tokens are extensible via `parse.token` list, can be added support of _literals_, _regexes_, _strings_, _numbers_ and others.
78
+ Literals are extensible by providing custom parser to `lookup`, can be added support of _booleans_, function calls, prop chains, groups, _regexes_, _strings_, _numbers_ and any other constructs.
100
79
 
101
80
  ```js
102
- import parse, {char} from 'subscript/parse.js'
103
- import evaluate from 'subscript/evaluate.js'
81
+ import script from 'subscript.js'
104
82
 
105
- conts ctx = {x:1}
106
- parse.token.unshift(c => char(4) === 'this' ? ctx : null)
107
- evaluate(parse(`this.x`)) // 1
83
+ script.literal.unshift(c => skip('this') && {x:1})
84
+ script`this.x`() // 1
108
85
  ```
109
86
 
110
- ### Spaces/comments
87
+ ### Identifiers
88
+
89
+ Identifiers include
111
90
 
112
- Comments can be added via extending `parse.space`. See [justin.js](./justin.js) for more examples.
91
+ ### Spaces/comments
113
92
 
93
+ Comments can be added via extending `parse.space`.
94
+ -->
114
95
 
115
96
  ## Justin
116
97
 
117
- _Justin_ extension (original [thread](https://github.com/endojs/Jessie/issues/66)) is minimal JS subset − JSON with JS expressions.<br/>
118
- It adds support of:
98
+ _Justin_ is minimal JS subset − JSON with JS expressions (see original [thread](https://github.com/endojs/Jessie/issues/66)).<br/>
99
+
100
+ It extends _subscript_ with:
119
101
 
120
102
  + `===`, `!==` operators
121
- + `**` binary operator
122
- + `~` unary operator
103
+ + `**` exponentiation operator (right-assoc)
104
+ + `~` bit inversion operator
123
105
  + `'` strings
124
106
  + `?:` ternary operator
107
+ + `?.` optional chain operator
125
108
  + `[...]` Array literal
126
109
  + `{...}` Object literal
127
- + `in` binary operator
110
+ + `in` binary
128
111
  + `;` expression separator
129
- + unary word operators
130
112
  + `//`, `/* */` comments
131
113
  + `true`, `false`, `null`, `undefined` literals
132
- <!-- + `?` chaining operator -->
133
114
  <!-- + `...x` unary operator -->
134
115
  <!-- + strings interpolation -->
135
116
 
136
117
  ```js
137
118
  import { parse, evaluate } from 'subscript/justin.js'
138
119
 
139
- let xy = parse('{ x: 1, "y": 2+2 }["x"]') // ['[', {x:1, y: ['+', 2, 2]}, '"x"']
120
+ let xy = parse('{ x: 1, "y": 2+2 }["x"]') // ['[', {x:1, y: ['+', 2, 2]}, '@x']
140
121
  evaluate(xy) // 1
141
122
  ```
142
123
 
@@ -282,19 +263,38 @@ Examples: sonr, template-parts, neural-chunks
282
263
  Subscript shows relatively good performance within other evaluators:
283
264
 
284
265
  ```
285
- // 1 + (a * b / c % d) - 2.0 + -3e-3 * +4.4e4 / f.g[0] - i.j(+k == 1)(0)
286
- // parse 30k times
266
+ 1 + (a * b / c % d) - 2.0 + -3e-3 * +4.4e4 / f.g[0] - i.j(+k == 1)(0)
267
+ ```
287
268
 
288
- subscript: ~230 ms
289
- jsep: ~280 ms
269
+ Parse 30k times:
270
+
271
+ ```
272
+ subscript: ~170 ms
273
+ justin: ~183 ms
274
+ jsep: ~250 ms
275
+ mr-parser: ~420 ms
290
276
  expr-eval: ~480 ms
291
- jexl: ~1200 ms
292
- new Function: ~1400 ms
277
+ math-parser: ~570 ms
278
+ jexl: ~1056 ms
279
+ mathjs: ~1200 ms
280
+ new Function: ~1154 ms
281
+ ```
282
+
283
+ Eval 30k times:
284
+ ```
285
+ subscript: ~15 ms
286
+ justin: ~15 ms
287
+ jsep (expression-eval): ~30 ms
288
+ mr-parser: -
289
+ expr-eval: ~72 ms
290
+ math-parser: -
291
+ jexl: ~110 ms
292
+ mathjs: ~119 ms
293
+ new Function: ~5 ms
293
294
  ```
294
295
 
295
- ## See also
296
+ ## Alternatives
296
297
 
297
- * [Jessie](https://github.com/endojs/Jessie) − Minimal JS subset.
298
298
  * [jexl](https://github.com/TomFrost/Jexl)
299
299
  * [mozjexl](https://github.com/mozilla/mozjexl)
300
300
  * [expr-eval](https://github.com/silentmatt/expr-eval)
@@ -302,5 +302,9 @@ new Function: ~1400 ms
302
302
  * [jsep](https://github.com/EricSmekens/jsep)
303
303
  * [string-math](https://github.com/devrafalko/string-math)
304
304
  * [nerdamer](https://github.com/jiggzson/nerdamer)
305
+ * [math-codegen](https://github.com/mauriciopoppe/math-codegen)
306
+ * [math-parser](https://www.npmjs.com/package/math-parser)
307
+ * [math.js](https://mathjs.org/docs/expressions/parsing.html)
308
+ * [Jessie](https://github.com/endojs/Jessie)
305
309
 
306
310
  <p align=center>🕉</p>
package/justin.js CHANGED
@@ -1,212 +1,73 @@
1
1
  // justin lang https://github.com/endojs/Jessie/issues/66
2
- import {evaluate} from './evaluate.js'
3
- import {parse, code, char, skip, expr, err, val} from './parse.js'
2
+ import {parse, set, lookup, skip, cur, idx, err, expr, isId, space} from './index.js'
3
+ import './subscript.js'
4
+
5
+ const PERIOD=46, OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, QUOTE=39, _0=48, _9=57, BSLASH=92,
6
+ PREC_SEQ=1, PREC_COND=3, 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_EXP=14, PREC_UNARY=15, PREC_POSTFIX=16, PREC_CALL=18, PREC_GROUP=19
8
+
9
+
10
+ let u, list, op, prec, fn,
11
+ escape = {n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', v:'\v'},
12
+ string = q => (qc, c, str='') => {
13
+ qc&&err() // must not follow another token
14
+ while (c=cur.charCodeAt(idx), c-q) {
15
+ if (c === BSLASH) skip(), c=skip(), str += escape[c] || c
16
+ else str += skip()
17
+ }
18
+ return skip()||err('Bad string'), () => str
19
+ }
4
20
 
5
- const PERIOD=46, OPAREN=40, CPAREN=41, CBRACK=93, SPACE=32,
21
+ // operators
22
+ for (list=[
23
+ // "' with /
24
+ '"', string(DQUOTE),,
25
+ "'", string(QUOTE),,
6
26
 
7
- PREC_SEQ=1, PREC_TERN=3, 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
- PREC_EXP=14, PREC_TOKEN=20
27
+ // /**/, //
28
+ '/*', (a, prec) => (skip(c => c !== 42 && cur.charCodeAt(idx+1) !== 47), skip(2), a||expr(prec)),,
29
+ '//', (a, prec) => (skip(c => c >= 32), a||expr(prec)),,
10
30
 
31
+ // literals
32
+ 'null', a => a ? err() : ()=>null,,
33
+ 'true', a => a ? err() : ()=>true,,
34
+ 'false', a => a ? err() : ()=>false,,
35
+ 'undefined', a => a ? err() : ()=>undefined,,
11
36
 
12
- // tokens
13
- parse.token.push(
14
- // 1.2e+3, .5 - fast & small version, but consumes corrupted nums as well
15
- (number) => (
16
- (number = skip(c => (c > 47 && c < 58) || c == PERIOD)) && (
17
- (code() == 69 || code() == 101) && (number += skip(2) + skip(c => c >= 48 && c <= 57)),
18
- isNaN(number = new Number(number)) ? err('Bad number') : number
19
- )
20
- ),
37
+ ';', a => expr()||(()=>{}),,
21
38
 
22
- // "' with /
23
- (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
-
33
- // id
34
- c => skip(c =>
35
- (c >= 48 && c <= 57) || // 0..9
36
- (c >= 65 && c <= 90) || // A...Z
37
- (c >= 97 && c <= 122) || // a...z
38
- c == 36 || c == 95 || // $, _,
39
- (c >= 192 && c != 215 && c != 247) // any non-ASCII
40
- )
41
- )
42
-
43
- const escape = {n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', v:'\v'}
44
-
45
-
46
- // /**/, //
47
- parse.space = cc => {
48
- while (cc = code(), cc <= 32 || cc === 47) {
49
- if (cc <= 32) skip()
50
- else if (cc === 47)
51
- // /**/
52
- if (code(1) === 42) skip(2), skip(c => c !== 42 && code(1) !== 47), skip(2)
53
- // //
54
- else if (code(1) === 47) skip(2), skip(c => c >= 32)
55
- else break
56
- }
57
- return cc
58
- }
39
+ // operators
40
+ '===', PREC_EQ, (a,b) => a===b,
41
+ '!==', PREC_EQ, (a,b) => a!==b,
42
+ '~', PREC_UNARY, (a) => ~a,
59
43
 
44
+ // right order
45
+ '**', (a,prec,b=expr(PREC_EXP-1)) => ctx=>a(ctx)**b(ctx), PREC_EXP,
46
+
47
+ // ?:
48
+ ':', 3.1, (a,b) => [a,b],
49
+ '?', 3, (a,b) => a ? b[2] : b[1],
50
+
51
+ // a?.[, a?.( - postfix operator
52
+ '?.', a => a && (ctx => a(ctx)||(()=>{})),,//(a) => a||(()=>{}),
53
+ // a?.b - optional chain operator
54
+ '?.', (a,id) => (space(), id=skip(isId)) && (ctx => a(ctx)?.[id]),,
55
+
56
+ 'in', PREC_COMP, (a,b) => a in b,
60
57
 
61
- // operators
62
- const addOps = (add, stride=2, list) => {
63
- for (let i = 0; i < list.length; i+=stride) add(list[i], list[i+1], list[i+2])
64
- }
65
-
66
- addOps(parse.operator, 3, [
67
- // subscript ones
68
- // TODO: add ,, as node here?
69
- ',', PREC_SEQ,,
70
-
71
- '|', PREC_OR,,
72
- '||', PREC_SOME,,
73
-
74
- '&', PREC_AND,,
75
- '&&', PREC_EVERY,,
76
-
77
- '^', PREC_XOR,,
78
-
79
- // ==, !=
80
- '==', PREC_EQ,,
81
- '!=', PREC_EQ,,
82
-
83
- // > >= >> >>>, < <= <<
84
- '>', PREC_COMP,,
85
- '>=', PREC_COMP,,
86
- '>>', PREC_SHIFT,,
87
- '>>>', PREC_SHIFT,,
88
- '<', PREC_COMP,,
89
- '<=', PREC_COMP,,
90
- '<<', PREC_SHIFT,,
91
-
92
- // + ++ - --
93
- '+', PREC_SUM,,
94
- '+', PREC_UNARY, -1,
95
- '++', PREC_UNARY, -1,
96
- '++', PREC_UNARY, +1,
97
- '-', PREC_SUM,,
98
- '-', PREC_UNARY, -1,
99
- '--', PREC_UNARY, -1,
100
- '--', PREC_UNARY, +1,
101
-
102
- // !
103
- '!', PREC_UNARY, -1,
104
-
105
- // * / %
106
- '*', PREC_MULT,,
107
- '/', PREC_MULT,,
108
- '%', PREC_MULT,,
109
-
110
- // a.b
111
- '.', PREC_CALL, (node,b) => node && [skip(),node, typeof (b = expr(PREC_CALL)) === 'string' ? '"' + b + '"' : b.valueOf()],
112
-
113
- // a[b]
114
- '[', PREC_CALL, (node) => (skip(), node = ['.', node, val(expr(0,CBRACK))], node),
115
- ']',,,
116
-
117
- // a(b)
118
- '(', PREC_CALL, (node,b) => ( skip(), b=expr(0,CPAREN),
119
- Array.isArray(b) && b[0]===',' ? (b[0]=node, b) : b ? [node, val(b)] : [node]
120
- ),
121
- // (a+b)
122
- '(', PREC_GROUP, (node,b) => !node && (skip(), b=expr(0,CPAREN) || err(), b),
123
- ')',,,
124
-
125
- // justin extension
126
- ';', PREC_SEQ,,
127
- '===', PREC_EQ,,
128
- '!==', PREC_EQ,,
129
- '**', PREC_EXP,,
130
- '~', PREC_UNARY, -1,
131
- '?', PREC_TERN, (node) => {
132
- if (!node) err('Expected expression')
133
- let a, b
134
- skip(), parse.space(), a = expr()
135
- return ['?:', node, a[1], a[2]]
136
- },
137
- '}',,,
138
- ':',2,,
139
- 'in', PREC_COMP, (node) => code(2) <= 32 && [skip(2), '"'+node+'"', expr(PREC_COMP)],
140
-
141
- // as operator it's faster to lookup (no need to extra rule check), smaller and no conflict with word names
142
58
  // [a,b,c]
143
- '[', PREC_TOKEN, (node,arg) => !node && (
144
- skip(), arg=expr(0,93),
145
- !arg ? ['['] : arg[0] == ',' ? (arg[0]='[',arg) : ['[',arg]
146
- ),
59
+ '[', (a, args) => !a && (
60
+ a=expr(0,CBRACK),
61
+ !a ? ctx => [] : a.all ? ctx => a.all(ctx) : ctx => [a(ctx)]
62
+ ),,
147
63
 
148
- // {a:0, b:1}
149
- '{', PREC_TOKEN, (node,arg) => !node && (skip(), arg=expr(0,125),
150
- !arg ? ['{'] : arg[0] == ':' ? ['{',arg] : arg[0] == ',' ? (arg[0]='{',arg) : ['{',arg])
151
- ,
64
+ // {a:1, b:2, c:3}
65
+ '{', (a, args) => !a && (
66
+ a=expr(0,125),
67
+ !a ? ctx => ({}) : ctx => (args=(a.all||a)(ctx), Object.fromEntries(a.all?args:[args]))
68
+ ),,
69
+ ':', (a, prec, b) => (b=expr(3.1)||err(), ctx => [(a.id||a)(ctx), b(ctx), a(ctx)]), 3.1
152
70
 
153
- // literals
154
- 'null', PREC_TOKEN, node=>!node&&(skip(4),v(null)),
155
- 'false', PREC_TOKEN, node=>!node&&(skip(5),v(false)),
156
- 'true', PREC_TOKEN, node=>!node&&(skip(4),v(true)),
157
- 'undefined', PREC_TOKEN, node=>!node&&(skip(9),v(undefined)),
158
- ])
159
- const v = v => ({valueOf:()=>v})
160
-
161
- addOps(evaluate.operator, 2, [
162
- // subscript
163
- '!', a=>!a,
164
- '++', a=>++a,
165
- '--', a=>--a,
166
-
167
- '.', (a,b)=>a?a[b]:a,
168
-
169
- '%', (a,b)=>a%b,
170
- '/', (a,b)=>a/b,
171
- '*', (a,b)=>a*b,
172
-
173
- '+', (a,b=0)=>a+b,
174
- '-', (a,b)=>b==null ? -a : a-b,
175
-
176
- '>>>', (a,b)=>a>>>b,
177
- '>>', (a,b)=>a>>b,
178
- '<<', (a,b)=>a<<b,
179
-
180
- '>=', (a,b)=>a>=b,
181
- '>', (a,b)=>a>b,
182
- '<=', (a,b)=>a<=b,
183
- '<', (a,b)=>a<b,
184
-
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)=>a.every(Boolean),
192
- '||', (...a)=>a.some(Boolean),
193
- ',', (a,b)=>(a,b),
194
-
195
- // justin extension
196
- '**', (...args)=>args.reduceRight((a,b)=>Math.pow(b,a)),
197
- '~', a=>~a,
198
- '?:', (a,b,c)=>a?b:c,
199
- 'in', (a,b)=>a in b,
200
-
201
- '[', (...args) => Array(...args),
202
- '{', (...args)=>Object.fromEntries(args),
203
- ':', (a,b)=>[a,b]
204
- ])
205
-
206
- // TODO ...
207
- // TODO: strings interpolation
208
-
209
- export { parse, evaluate }
210
-
211
- // code → evaluator
212
- export default s => (s = typeof s == 'string' ? parse(s) : s, ctx => evaluate(s, ctx))
71
+ ]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
72
+
73
+ export default parse
package/justin.min.js CHANGED
@@ -1 +1 @@
1
- const e=(t,n={},o,s)=>{if("string"==typeof t)return'"'===t[0]?t.slice(1,-1):"@"===t[0]?t.slice(1):t in n?n[t]:t;if(Array.isArray(t)&&("string"==typeof t[0]||Array.isArray(t[0]))){let[o,...s]=t,a="string"==typeof o?r[o]||n[o]:e(o,n);return s=s.map((r=>e(r,n))),a.apply(o,s)}return t},r={};let t,n;e.operator=(e,t)=>r[e]=2==t.length?(...e)=>e.reduce(t):t;const o=(e,r)=>(n=e,t=0,r=l(),t<n.length?s():y(r)),s=(e="Bad syntax")=>{throw Error(e+" `"+n[t]+"` at "+t)},a=(e=1,r=t)=>{if("number"==typeof e)t+=e;else for(;e(i());)t++;return n.slice(r,t)},i=(e=0)=>n.charCodeAt(t+e),f=(e=1)=>n.substr(t,e),l=(e=0,r,n,a,i=0,f,l)=>{for(;(n=o.space())&&(l=c[n]?.(a,e)||!a&&p(n));)a=l;return r&&(n!=r?s("Unclosed paren"):t++),a};o.space=e=>{for(;(e=i())<=32;)t++;return e};const u=o.token=[],p=(e,r=0,t)=>{for(;r<u.length;)if(t=u[r++](e))return t},c=[];o.operator=(e,r=0,n=0,s,u=e.charCodeAt(0),p=e.length,d=c[u],h=n<=0&&e.toUpperCase()!==e)=>(s=n?n>0?e=>e&&[a(p),y(e)]:n<0?e=>!e&&[a(p),y(l(r-1))]:n:n=>{n=[e,y(n)];do{t+=p,n.push(y(l(r)))}while(o.space()==u&&(p<2||f(p)==e)&&(!h||i(p)<=32));return n},c[u]=(t,n)=>n<r&&(p<2||f(p)==e)&&(!h||i(p)<=32)&&s(t)||d&&d(t,n));const y=e=>Array.isArray(e)?e:(e||s()).valueOf();o.token.push((e=>(e=a((e=>e>47&&e<58||46==e)))&&((69==i()||101==i())&&(e+=a(2)+a((e=>e>=48&&e<=57))),isNaN(e=new Number(e))?s("Bad number"):e)),((e,r,t,n)=>{if(34===e||39===e){for(r=f(),a(),n="";(t=i())-e;)92===t?(a(),n+=d[f()]||f()):n+=f(),a();return a(),r+n+r}}),(e=>a((e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e))));const d={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"};o.space=e=>{for(;(e=i())<=32||47===e;)if(e<=32)a();else if(47===e)if(42===i(1))a(2),a((e=>42!==e&&47!==i(1))),a(2);else{if(47!==i(1))break;a(2),a((e=>e>=32))}return e};const h=(e,r=2,t)=>{for(let n=0;n<t.length;n+=r)e(t[n],t[n+1],t[n+2])};h(o.operator,3,[",",1,,"|",6,,"||",4,,"&",8,,"&&",5,,"^",7,,"==",9,,"!=",9,,">",10,,">=",10,,">>",11,,">>>",11,,"<",10,,"<=",10,,"<<",11,,"+",12,,"+",15,-1,"++",15,-1,"++",15,1,"-",12,,"-",15,-1,"--",15,-1,"--",15,1,"!",15,-1,"*",13,,"/",13,,"%",13,,".",18,(e,r)=>e&&[a(),e,"string"==typeof(r=l(18))?'"'+r+'"':r.valueOf()],"[",18,e=>(a(),[".",e,y(l(0,93))]),"]",,,"(",18,(e,r)=>(a(),r=l(0,41),Array.isArray(r)&&","===r[0]?(r[0]=e,r):r?[e,y(r)]:[e]),"(",19,(e,r)=>!e&&(a(),l(0,41)||s()),")",,,";",1,,"===",9,,"!==",9,,"**",14,,"~",15,-1,"?",3,e=>{let r;return e||s("Expected expression"),a(),o.space(),r=l(),["?:",e,r[1],r[2]]},"}",,,":",2,,"in",10,e=>i(2)<=32&&[a(2),'"'+e+'"',l(10)],"[",20,(e,r)=>!e&&(a(),(r=l(0,93))?","==r[0]?(r[0]="[",r):["[",r]:["["]),"{",20,(e,r)=>!e&&(a(),(r=l(0,125))?":"==r[0]?["{",r]:","==r[0]?(r[0]="{",r):["{",r]:["{"]),"null",20,e=>!e&&(a(4),g(null)),"false",20,e=>!e&&(a(5),g(!1)),"true",20,e=>!e&&(a(4),g(!0)),"undefined",20,e=>!e&&(a(9),g(void 0))]);const g=e=>({valueOf:()=>e});h(e.operator,2,["!",e=>!e,"++",e=>++e,"--",e=>--e,".",(e,r)=>e?e[r]:e,"%",(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 A=r=>(r="string"==typeof r?o(r):r,t=>e(r,t));export{A as default,e as evaluate,o as parse};
1
+ let r,e,t,l,a,o,n=(t,...l)=>(e=t.raw?String.raw(t,...l):t,r=0,!(t=i())||e[r]?f():r=>t(r||{})),d=r=>r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||36==r||95==r||r>=192&&215!=r&&247!=r,f=(t="Bad syntax",l=e[r])=>{throw Error(t+" `"+l+"` at "+r)},h=(t=1,l=r,a)=>{if("number"==typeof t)r+=t;else for(;t(e.charCodeAt(r));)r++;return e.slice(l,r)},i=(e=0,t,l,a,o,n)=>{for(;(l=s())&&(o=(n=c[l])&&n(a,e)||!a&&u());)a=o;return t&&(l==t?r++:f("Unclosed")),a},s=t=>{for(;(t=e.charCodeAt(r))<=32;)r++;return t},u=(r=h(d),e)=>r?((e=e=>e[r]).id=()=>r,e):0,c=[],C=n.set=(t,l,a=32,o=t.charCodeAt(0),n=t.length,f=c[o],h=a.length||([a,l]=[l,a],0),s=t.toUpperCase()!==t,u=(h>1?(r,e)=>r&&(e=i(l))&&(r.length||e.length?t=>a(r(t),e(t)):(r=a(r(),e()),()=>r)):h?r=>!r&&(r=i(l-1))&&(e=>a(r(e))):a))=>c[o]=(a,o,h=r)=>o<l&&(n<2||e.substr(r,n)==t)&&(!s||!d(e.charCodeAt(r+n)))&&(r+=n,u(a,o))||(r=h,f&&f(a,o)),A=r=>r>=48&&r<=57,g=t=>(t&&f(),t=h((r=>46==r||A(r))),(69==e.charCodeAt(r)||101==e.charCodeAt(r))&&(t+=h(2)+h(A)),(t=+t)!=t?f("Bad number"):()=>t),p=(r,e,t=r.of)=>l=>e(t?t(l):l,r.id());for(l=48;l<=57;)c[l++]=g;for(t=['"',r=>(r=r?f():h((r=>r-34)),h()||f("Bad string"),()=>r),,".",(r,e,t)=>r?(s(),e=h(d)||f(),(t=t=>r(t)[e]).id=()=>e,t.of=r,t):g(h(-1)),18,"[",(r,e,t)=>r&&(e=i(0,93)||f(),(t=t=>r(t)[e(t)]).id=e,t.of=r,t),18,"(",(r,e,t)=>(e=i(0,41),r?t=>r(t).apply(r.of?.(t),e?e.all?e.all(t):[e(t)]:[]):e||f()),18,",",(r,e,t=i(1))=>(t.all=r.all?(e,l,a=r.all(e))=>a.push(t(e))&&a:e=>[r(e),t(e)],t),1,"|",6,(r,e)=>r|e,"||",4,(r,e)=>r||e,"&",8,(r,e)=>r&e,"&&",5,(r,e)=>r&&e,"^",7,(r,e)=>r^e,"==",9,(r,e)=>r==e,"!=",9,(r,e)=>r!=e,">",10,(r,e)=>r>e,">=",10,(r,e)=>r>=e,">>",11,(r,e)=>r>>e,">>>",11,(r,e)=>r>>>e,"<",10,(r,e)=>r<e,"<=",10,(r,e)=>r<=e,"<<",11,(r,e)=>r<<e,"+",12,(r,e)=>r+e,"+",15,r=>+r,"++",r=>p(r||i(14),r?(r,e)=>r[e]++:(r,e)=>++r[e]),15,"-",12,(r,e)=>r-e,"-",15,r=>-r,"--",r=>p(r||i(14),r?(r,e)=>r[e]--:(r,e)=>--r[e]),15,"!",15,r=>!r,"*",13,(r,e)=>r*e,"/",13,(r,e)=>r/e,"%",13,(r,e)=>r%e];[l,a,o,...t]=t,l;)C(l,a,o);let b,B,m,w,y={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},v=t=>(l,a,o="")=>{for(l&&f();(a=e.charCodeAt(r))-t;)92===a?(h(),a=h(),o+=y[a]||a):o+=h();return h()||f("Bad string"),()=>o};for((b=['"',v(34),,"'",v(39),,"/*",(t,l)=>(h((t=>42!==t&&47!==e.charCodeAt(r+1))),h(2),t||i(l)),,"//",(r,e)=>(h((r=>r>=32)),r||i(e)),,"null",r=>r?f():()=>null,,"true",r=>r?f():()=>!0,,"false",r=>r?f():()=>!1,,"undefined",r=>r?f():()=>{},,";",r=>i()||(()=>{}),,"===",9,(r,e)=>r===e,"!==",9,(r,e)=>r!==e,"~",15,r=>~r,"**",(r,e,t=i(13))=>e=>r(e)**t(e),14,":",3.1,(r,e)=>[r,e],"?",3,(r,e)=>r?e[2]:e[1],"?.",r=>r&&(e=>r(e)||(()=>{})),,"?.",(r,e)=>(s(),(e=h(d))&&(t=>r(t)?.[e])),,"in",10,(r,e)=>r in e,"[",(r,e)=>!r&&((r=i(0,93))?r.all?e=>r.all(e):e=>[r(e)]:r=>[]),,"{",(r,e)=>!r&&((r=i(0,125))?t=>(e=(r.all||r)(t),Object.fromEntries(r.all?e:[e])):r=>({})),,":",(r,e,t)=>(t=i(3.1)||f(),e=>[(r.id||r)(e),t(e),r(e)]),3.1]);[B,m,w,...b]=b,B;)C(B,m,w);export{n as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "subscript",
3
- "version": "5.4.0",
3
+ "version": "6.0.0",
4
4
  "description": "Microlanguage with common syntax for JS/C++/Python/Rust",
5
5
  "main": "subscript.js",
6
6
  "type": "module",
@@ -45,7 +45,10 @@
45
45
  "calculator",
46
46
  "calc",
47
47
  "lisp",
48
- "frisk"
48
+ "frisk",
49
+ "math.js",
50
+ "math-codegen",
51
+ "math-parser"
49
52
  ],
50
53
  "author": "Dmitry Iv.",
51
54
  "license": "ISC",
package/subscript.js CHANGED
@@ -1,130 +1,88 @@
1
- import parse, {skip, expr, code, tokens, val, operator as parseOp, err} 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,
1
+ import {parse, set, lookup, skip, cur, idx, err, expr, isId, space} from './index.js'
5
2
 
3
+ const PERIOD=46, OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, _0=48, _9=57,
6
4
  PREC_SEQ=1, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
7
5
  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
6
 
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
- ),
7
+ let u, list, op, prec, fn,
8
+ isNum = c => c>=_0 && c<=_9,
9
+ // 1.2e+3, .5
10
+ num = n => (
11
+ n&&err(),
12
+ n = skip(c=>c==PERIOD || isNum(c)),
13
+ (cur.charCodeAt(idx) == 69 || cur.charCodeAt(idx) == 101) && (n += skip(2) + skip(isNum)),
14
+ n=+n, n!=n ? err('Bad number') : () => n // 0 args means token is static
15
+ ),
16
+
17
+ inc = (a,fn,c=a.of) => ctx => fn(c?c(ctx):ctx, a.id())
18
+
19
+ // numbers
20
+ for (op=_0;op<=_9;) lookup[op++] = num
21
+
22
+ // operators
23
+ for (list=[
17
24
  // "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
- )
25
+ '"', a => (a=a?err():skip(c => c-DQUOTE), skip()||err('Bad string'), ()=>a),,
26
+
27
+ // a.b, .2, 1.2 parser in one
28
+ '.', (a,id,fn) => !a ? num(skip(-1)) : // FIXME: .123 is not operator, so we skip back, but mb reorganizing num would be better
29
+ (space(), id=skip(isId)||err(), fn=ctx=>a(ctx)[id], fn.id=()=>id, fn.of=a, fn), PREC_CALL,
28
30
 
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
- }
31
+ // a[b]
32
+ '[', (a,b,fn) => a && (b=expr(0,CBRACK)||err(), fn=ctx=>a(ctx)[b(ctx)], fn.id=b, fn.of=a, fn), PREC_CALL,
33
+
34
+ // a(), a(b), (a,b), (a+b)
35
+ '(', (a,b,fn) => (
36
+ b=expr(0,CPAREN),
37
+ // a(), a(b), a(b,c,d)
38
+ a ? ctx => a(ctx).apply(a.of?.(ctx), b ? b.all ? b.all(ctx) : [b(ctx)] : []) :
39
+ // (a+b)
40
+ b || err()
41
+ ), PREC_CALL,
32
42
 
33
- addOps(parseOp, 3, [
34
- ',', PREC_SEQ,,
43
+ // [a,b,c] or (a,b,c)
44
+ ',', (a,prec,b=expr(PREC_SEQ)) => (
45
+ b.all = a.all ? (ctx,prec,arr=a.all(ctx)) => arr.push(b(ctx)) && arr : ctx => [a(ctx),b(ctx)],
46
+ b
47
+ ), PREC_SEQ,
35
48
 
36
- '|', PREC_OR,,
37
- '||', PREC_SOME,,
49
+ '|', PREC_OR, (a,b)=>a|b,
50
+ '||', PREC_SOME, (a,b)=>a||b,
38
51
 
39
- '&', PREC_AND,,
40
- '&&', PREC_EVERY,,
52
+ '&', PREC_AND, (a,b)=>a&b,
53
+ '&&', PREC_EVERY, (a,b)=>a&&b,
41
54
 
42
- '^', PREC_XOR,,
55
+ '^', PREC_XOR, (a,b)=>a^b,
43
56
 
44
57
  // ==, !=
45
- '==', PREC_EQ,,
46
- '!=', PREC_EQ,,
58
+ '==', PREC_EQ, (a,b)=>a==b,
59
+ '!=', PREC_EQ, (a,b)=>a!=b,
47
60
 
48
61
  // > >= >> >>>, < <= <<
49
- '>', PREC_COMP,,
50
- '>=', PREC_COMP,,
51
- '>>', PREC_SHIFT,,
52
- '>>>', PREC_SHIFT,,
53
- '<', PREC_COMP,,
54
- '<=', PREC_COMP,,
55
- '<<', PREC_SHIFT,,
62
+ '>', PREC_COMP, (a,b)=>a>b,
63
+ '>=', PREC_COMP, (a,b)=>a>=b,
64
+ '>>', PREC_SHIFT, (a,b)=>a>>b,
65
+ '>>>', PREC_SHIFT, (a,b)=>a>>>b,
66
+ '<', PREC_COMP, (a,b)=>a<b,
67
+ '<=', PREC_COMP, (a,b)=>a<=b,
68
+ '<<', PREC_SHIFT, (a,b)=>a<<b,
56
69
 
57
70
  // + ++ - --
58
- '+', PREC_SUM,,
59
- '+', PREC_UNARY, -1,
60
- '++', PREC_UNARY, -1,
61
- '++', PREC_POSTFIX, +1,
62
- '-', PREC_SUM,,
63
- '-', PREC_UNARY, -1,
64
- '--', PREC_UNARY, -1,
65
- '--', PREC_POSTFIX, +1,
71
+ '+', PREC_SUM, (a,b)=>a+b,
72
+ '+', PREC_UNARY, (a)=>+a,
73
+ '++', a => inc(a||expr(PREC_UNARY-1), a ? (a,b)=>a[b]++ : (a,b)=>++a[b]), PREC_UNARY,
74
+
75
+ '-', PREC_SUM, (a,b)=>a-b,
76
+ '-', PREC_UNARY, (a)=>-a,
77
+ '--', a => inc(a||expr(PREC_UNARY-1), a ? (a,b)=>a[b]-- : (a,b)=>--a[b]), PREC_UNARY,
66
78
 
67
79
  // ! ~
68
- '!', PREC_UNARY, -1,
80
+ '!', PREC_UNARY, (a)=>!a,
69
81
 
70
82
  // * / %
71
- '*', PREC_MULT,,
72
- '/', PREC_MULT,,
73
- '%', PREC_MULT,,
83
+ '*', PREC_MULT, (a,b)=>a*b,
84
+ '/', PREC_MULT, (a,b)=>a/b,
85
+ '%', PREC_MULT, (a,b)=>a%b
86
+ ]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
74
87
 
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, val(expr(0,CBRACK))], node),
80
- ']',,,
81
-
82
- // a(b)
83
- '(', PREC_CALL, (node,b) => ( skip(), b=expr(0,CPAREN),
84
- Array.isArray(b) && b[0]===',' ? (b[0]=node, b) : b ? [node, val(b)] : [node]
85
- ),
86
- // (a+b)
87
- '(', PREC_GROUP, (node,b) => !node && (skip(), b=expr(0,CPAREN) || err(), 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=0)=>a+b,
105
- '-', (a,b)=>b==null ? -a : 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
- ])
126
-
127
- export { parse, evaluate }
128
-
129
- // code → evaluator
130
- export default s => (s = typeof s == 'string' ? parse(s) : s, ctx => evaluate(s, ctx))
88
+ export default parse
package/subscript.min.js CHANGED
@@ -1 +1 @@
1
- let r,e;const t=(t,o)=>(e=t,r=0,o=l(),r<e.length?n():h(o)),n=(t="Bad syntax")=>{throw Error(t+" `"+e[r]+"` at "+r)},o=(t=1,n=r)=>{if("number"==typeof t)r+=t;else for(;t(a());)r++;return e.slice(n,r)},a=(t=0)=>e.charCodeAt(r+t),s=(t=1)=>e.substr(r,t),l=(e=0,o,a,s,l=0,u,i)=>{for(;(a=t.space())&&(i=f[a]?.(s,e)||!s&&p(a));)s=i;return o&&(a!=o?n("Unclosed paren"):r++),s};t.space=e=>{for(;(e=a())<=32;)r++;return e};const u=t.token=[],p=(r,e=0,t)=>{for(;e<u.length;)if(t=u[e++](r))return t},f=[],i=t.operator=(e,n=0,u=0,p,i=e.charCodeAt(0),y=e.length,c=f[i],g=u<=0&&e.toUpperCase()!==e)=>(p=u?u>0?r=>r&&[o(y),h(r)]:u<0?r=>!r&&[o(y),h(l(n-1))]:u:o=>{o=[e,h(o)];do{r+=y,o.push(h(l(n)))}while(t.space()==i&&(y<2||s(y)==e)&&(!g||a(y)<=32));return o},f[i]=(r,t)=>t<n&&(y<2||s(y)==e)&&(!g||a(y)<=32)&&p(r)||c&&c(r,t)),h=r=>Array.isArray(r)?r:(r||n()).valueOf(),y=(r,e={})=>{if("string"==typeof r)return'"'===r[0]?r.slice(1,-1):"@"===r[0]?r.slice(1):r in e?e[r]:r;if(Array.isArray(r)){let t=r[0],n=Array.isArray(t)?y(t,e):c[t]||e[t]||t,o=[],a=1;for(;a<r.length;a++)o.push(y(r[a],e));return o.length>n.length&&n.length?o.reduce(n):n.apply(t,o)}return r},c={},g=y.operator=(r,e)=>c[r]=e;u.push((r=>(r=o((r=>r>47&&r<58||46==r)))&&((69==a()||101==a())&&(r+=o(2)+o((r=>r>=48&&r<=57))),isNaN(r=new Number(r))?n("Bad number"):r)),((r,e)=>34==r&&o()+o((e=>e-r))+o()),(r=>o((r=>r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||36==r||95==r||r>=192))));const A=(r,e=2,t)=>{for(let n=0;n<t.length;n+=e)r(t[n],t[n+1],t[n+2])};A(i,3,[",",1,,"|",6,,"||",4,,"&",8,,"&&",5,,"^",7,,"==",9,,"!=",9,,">",10,,">=",10,,">>",11,,">>>",11,,"<",10,,"<=",10,,"<<",11,,"+",12,,"+",15,-1,"++",15,-1,"++",16,1,"-",12,,"-",15,-1,"--",15,-1,"--",16,1,"!",15,-1,"*",13,,"/",13,,"%",13,,".",18,(r,e)=>r&&[o(),r,"string"==typeof(e=l(18))?'"'+e+'"':e.valueOf()],"[",18,r=>(o(),[".",r,h(l(0,93))]),"]",,,"(",18,(r,e)=>(o(),e=l(0,41),Array.isArray(e)&&","===e[0]?(e[0]=r,e):e?[r,h(e)]:[r]),"(",19,(r,e)=>!r&&(o(),l(0,41)||n()),")",,,]),A(g,2,["!",r=>!r,"++",r=>++r,"--",r=>--r,".",(r,e)=>r?r[e]:r,"%",(r,e)=>r%e,"/",(r,e)=>r/e,"*",(r,e)=>r*e,"+",(r,e=0)=>r+e,"-",(r,e)=>null==e?-r: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,e)=>e]);var d=r=>(r="string"==typeof r?t(r):r,e=>y(r,e));export{d as default,y as evaluate,t as parse};
1
+ let r,e,t,a,o,l,d=(t,...a)=>(e=t.raw?String.raw(t,...a):t,r=0,!(t=s())||e[r]?f():r=>t(r||{})),n=r=>r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||36==r||95==r||r>=192&&215!=r&&247!=r,f=(t="Bad syntax",a=e[r])=>{throw Error(t+" `"+a+"` at "+r)},h=(t=1,a=r,o)=>{if("number"==typeof t)r+=t;else for(;t(e.charCodeAt(r));)r++;return e.slice(a,r)},s=(e=0,t,a,o,l,d)=>{for(;(a=c())&&(l=(d=u[a])&&d(o,e)||!o&&i());)o=l;return t&&(a==t?r++:f("Unclosed")),o},c=t=>{for(;(t=e.charCodeAt(r))<=32;)r++;return t},i=(r=h(n),e)=>r?((e=e=>e[r]).id=()=>r,e):0,u=[],p=d.set=(t,a,o=32,l=t.charCodeAt(0),d=t.length,f=u[l],h=o.length||([o,a]=[a,o],0),c=t.toUpperCase()!==t,i=(h>1?(r,e)=>r&&(e=s(a))&&(r.length||e.length?t=>o(r(t),e(t)):(r=o(r(),e()),()=>r)):h?r=>!r&&(r=s(a-1))&&(e=>o(r(e))):o))=>u[l]=(o,l,h=r)=>l<a&&(d<2||e.substr(r,d)==t)&&(!c||!n(e.charCodeAt(r+d)))&&(r+=d,i(o,l))||(r=h,f&&f(o,l)),C=r=>r>=48&&r<=57,g=t=>(t&&f(),t=h((r=>46==r||C(r))),(69==e.charCodeAt(r)||101==e.charCodeAt(r))&&(t+=h(2)+h(C)),(t=+t)!=t?f("Bad number"):()=>t),A=(r,e,t=r.of)=>a=>e(t?t(a):a,r.id());for(a=48;a<=57;)u[a++]=g;for(t=['"',r=>(r=r?f():h((r=>r-34)),h()||f("Bad string"),()=>r),,".",(r,e,t)=>r?(c(),e=h(n)||f(),(t=t=>r(t)[e]).id=()=>e,t.of=r,t):g(h(-1)),18,"[",(r,e,t)=>r&&(e=s(0,93)||f(),(t=t=>r(t)[e(t)]).id=e,t.of=r,t),18,"(",(r,e,t)=>(e=s(0,41),r?t=>r(t).apply(r.of?.(t),e?e.all?e.all(t):[e(t)]:[]):e||f()),18,",",(r,e,t=s(1))=>(t.all=r.all?(e,a,o=r.all(e))=>o.push(t(e))&&o:e=>[r(e),t(e)],t),1,"|",6,(r,e)=>r|e,"||",4,(r,e)=>r||e,"&",8,(r,e)=>r&e,"&&",5,(r,e)=>r&&e,"^",7,(r,e)=>r^e,"==",9,(r,e)=>r==e,"!=",9,(r,e)=>r!=e,">",10,(r,e)=>r>e,">=",10,(r,e)=>r>=e,">>",11,(r,e)=>r>>e,">>>",11,(r,e)=>r>>>e,"<",10,(r,e)=>r<e,"<=",10,(r,e)=>r<=e,"<<",11,(r,e)=>r<<e,"+",12,(r,e)=>r+e,"+",15,r=>+r,"++",r=>A(r||s(14),r?(r,e)=>r[e]++:(r,e)=>++r[e]),15,"-",12,(r,e)=>r-e,"-",15,r=>-r,"--",r=>A(r||s(14),r?(r,e)=>r[e]--:(r,e)=>--r[e]),15,"!",15,r=>!r,"*",13,(r,e)=>r*e,"/",13,(r,e)=>r/e,"%",13,(r,e)=>r%e];[a,o,l,...t]=t,a;)p(a,o,l);export{d as default};
package/evaluate.js DELETED
@@ -1,21 +0,0 @@
1
- // calltree → result
2
- export const evaluate = (node, ctx={}) => {
3
- if (typeof node === 'string')
4
- return node[0] === '"' ? node.slice(1,-1) : node[0]==='@' ? node.slice(1) : node in ctx ? ctx[node] : node
5
-
6
- if (Array.isArray(node)) {
7
- // [[a,b], c] or ['+', a, b] or ['myfn', a, b], or
8
- let c = node[0], fn = Array.isArray(c) ? evaluate(c, ctx) : (lookup[c] || ctx[c] || c), args=[], i = 1
9
- for (;i<node.length;i++) args.push(evaluate(node[i], ctx))
10
- return args.length > fn.length && fn.length ? args.reduce(fn) : fn.apply(c,args)
11
- }
12
-
13
- return node
14
- },
15
- lookup = {},
16
-
17
- // op evaluators
18
- // multiple args allows shortcuts, lisp compatible, easy manual eval, functions anyways take multiple arguments
19
- operator = evaluate.operator = (op, fn) => lookup[op] = fn
20
-
21
- export default evaluate
package/parse.js DELETED
@@ -1,70 +0,0 @@
1
- const SPACE=32
2
-
3
- // current string & index
4
- let idx, cur
5
-
6
- export const parse = (str, tree) => (cur=str, idx=0, tree=expr(), idx<cur.length ? err() : val(tree)),
7
-
8
- err = (msg='Bad syntax') => { throw Error(msg + ' `' + cur[idx] + '` at ' + idx) },
9
-
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)
14
- },
15
-
16
- code = (i=0) => cur.charCodeAt(idx+i),
17
-
18
- char = (n=1) => cur.substr(idx, n),
19
-
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;
26
-
27
- // skip end character, if expected
28
- if (end) cc != end ? err('Unclosed paren') : idx++
29
-
30
- return node
31
- },
32
-
33
- // can be extended with comments, so we export
34
- space = parse.space = cc => { while (cc = code(), cc <= SPACE) idx++; return cc },
35
-
36
- // tokens
37
- tokens = parse.token = [],
38
- token = (c,i=0,node) => { while(i<tokens.length) if (node = tokens[i++](c)) return node },
39
-
40
- // operator lookup table
41
- lookup = [],
42
-
43
- // create operator checker/mapper (see examples)
44
- // @param op is operator string
45
- // @param prec is operator precedenc to check
46
- // @param map is either number +1 - postfix unary, -1 prefix unary, 0 binary, else - custom mapper function
47
- operator = parse.operator = (
48
- op, prec=0, type=0, map, c=op.charCodeAt(0), l=op.length,
49
- prev=lookup[c],
50
- spaced=type<=0&&op.toUpperCase()!==op // non-postfix word operator must have space after
51
- ) => (
52
- map = !type ? node => { // binary, consume same-op group
53
- node = [op, val(node)]
54
- do { idx+=l, node.push(val(expr(prec))) }
55
- while (parse.space()==c && (l<2||char(l)==op) && (!spaced||code(l)<=SPACE))
56
- return node
57
- } :
58
- type > 0 ? node => node && [skip(l), val(node)] : // postfix unary
59
- type < 0 ? node => !node && [skip(l), val(expr(prec-1))] : // prefix unary
60
- type,
61
-
62
- lookup[c] = (node, curPrec) =>
63
- curPrec < prec && (l<2||char(l)==op) && (!spaced||code(l)<=SPACE) &&
64
- map(node) || (prev && prev(node, curPrec))
65
- ),
66
-
67
- // in order to support literal tokens, we call valueOf any time we create or modify calltree node
68
- val = node => Array.isArray(node) ? node : (node || err()).valueOf()
69
-
70
- export default parse