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