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 +44 -27
- package/evaluate.js +5 -37
- package/justin.js +206 -81
- package/justin.min.js +1 -1
- package/package.json +1 -1
- package/parse.js +50 -120
- package/subscript.js +125 -2
- package/subscript.min.js +1 -1
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
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></sub>
|
|
8
|
-
* It's
|
|
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
|
|
30
|
-
_Subscript_ has [2.5kb](https://npmfs.com/package/subscript/
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
//
|
|
62
|
-
parse.binary['=>'] = 10
|
|
72
|
+
parse.operator('=>', 10) // precedence=10, type=default (0 - binary, 1 - postfix, -1 - prefix)
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
evaluate.operator
|
|
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
|
-
|
|
86
|
+
---
|
|
78
87
|
|
|
79
|
-
|
|
88
|
+
Default tokens include:
|
|
80
89
|
|
|
81
|
-
* `"` strings
|
|
90
|
+
* `"abc"` strings
|
|
82
91
|
* `1.2e+3` floats
|
|
83
|
-
*
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
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
|
-
|
|
105
|
+
---
|
|
90
106
|
|
|
91
|
-
|
|
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
|
|
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
|
-
|
|
109
|
-
|
|
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: ~
|
|
267
|
-
jsep: ~
|
|
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 =
|
|
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
|
-
|
|
16
|
-
|
|
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
|
|
3
|
-
import {parse,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
)
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
|
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
package/parse.js
CHANGED
|
@@ -1,134 +1,64 @@
|
|
|
1
|
-
const
|
|
1
|
+
const SPACE=32
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// current string & index
|
|
4
|
+
let idx, cur
|
|
4
5
|
|
|
5
|
-
export const parse = (str, tree) => (
|
|
6
|
+
export const parse = (str, tree) => (cur=str, idx=0, tree=expr(), idx<cur.length ? err() : tree.valueOf()),
|
|
6
7
|
|
|
7
|
-
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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 (
|
|
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
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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,
|
|
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};
|