subscript 6.3.1 → 6.4.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/justin.js +11 -175
- package/justin.min.js +1 -1
- package/package.json +11 -10
- package/{src/index.js → parser.js} +0 -0
- package/subscript.js +10 -85
- package/subscript.min.js +1 -1
- package/src/justin.js +0 -74
- package/src/subscript.js +0 -91
package/justin.js
CHANGED
|
@@ -1,184 +1,22 @@
|
|
|
1
|
-
const SPACE=32;
|
|
2
|
-
|
|
3
|
-
// current string, index and collected ids
|
|
4
|
-
let idx, cur, args,
|
|
5
|
-
|
|
6
|
-
// no handling tagged literals since easily done on user side with cache, if needed
|
|
7
|
-
parse = (s, fn) => (
|
|
8
|
-
idx=0, args=[], cur=s.trim(),
|
|
9
|
-
!(s = cur ? expr() : ctx=>fn) || cur[idx] ? err() :
|
|
10
|
-
fn = ctx=>s(ctx||{}), fn.args = args, fn
|
|
11
|
-
),
|
|
12
|
-
|
|
13
|
-
isId = c =>
|
|
14
|
-
(c >= 48 && c <= 57) || // 0..9
|
|
15
|
-
(c >= 65 && c <= 90) || // A...Z
|
|
16
|
-
(c >= 97 && c <= 122) || // a...z
|
|
17
|
-
c == 36 || c == 95 || // $, _,
|
|
18
|
-
(c >= 192 && c != 215 && c != 247), // any non-ASCII
|
|
19
|
-
|
|
20
|
-
err = (msg='Bad syntax',c=cur[idx]) => { throw SyntaxError(msg + ' `' + c + '` at ' + idx) },
|
|
21
|
-
|
|
22
|
-
skip = (is=1, from=idx, l) => {
|
|
23
|
-
if (typeof is == 'number') idx += is;
|
|
24
|
-
else while (is(cur.charCodeAt(idx))) idx++;
|
|
25
|
-
return cur.slice(from, idx)
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
// a + b - c
|
|
29
|
-
expr = (prec=0, end, cc, token, newNode, fn) => {
|
|
30
|
-
// chunk/token parser
|
|
31
|
-
while (
|
|
32
|
-
( cc=space() ) && // till not end
|
|
33
|
-
// FIXME: extra work is happening here, when lookup bails out due to lower precedence -
|
|
34
|
-
// it makes extra `space` call for parent exprs on the same character to check precedence again
|
|
35
|
-
( newNode =
|
|
36
|
-
(fn=lookup[cc]) && fn(token, prec) || // if operator with higher precedence isn't found
|
|
37
|
-
(!token && id()) // parse literal or quit. token seqs are forbidden: `a b`, `a "b"`, `1.32 a`
|
|
38
|
-
)
|
|
39
|
-
) token = newNode;
|
|
40
|
-
|
|
41
|
-
// check end character
|
|
42
|
-
// FIXME: can't show "Unclose paren", because can be unknown operator within group as well
|
|
43
|
-
if (end) cc==end?idx++:err();
|
|
44
|
-
|
|
45
|
-
return token
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
// skip space chars, return first non-space character
|
|
49
|
-
space = cc => { while ((cc = cur.charCodeAt(idx)) <= SPACE) idx++; return cc },
|
|
50
|
-
|
|
51
|
-
// variable identifier
|
|
52
|
-
id = (name=skip(isId), fn) => name ? (fn=ctx => ctx[name], args.push(name), fn.id=()=>name, fn) : 0,
|
|
53
|
-
|
|
54
|
-
// operator/token lookup table
|
|
55
|
-
lookup = [],
|
|
56
|
-
|
|
57
|
-
// create operator checker/mapper (see examples)
|
|
58
|
-
set = parse.set = (
|
|
59
|
-
op,
|
|
60
|
-
opPrec, fn=SPACE, // if opPrec & fn come in reverse order - consider them raw parse fn case, still precedence possible
|
|
61
|
-
c=op.charCodeAt(0),
|
|
62
|
-
l=op.length,
|
|
63
|
-
prev=lookup[c],
|
|
64
|
-
arity=fn.length || ([fn,opPrec]=[opPrec,fn], 0),
|
|
65
|
-
word=op.toUpperCase()!==op, // make sure word boundary comes after word operator
|
|
66
|
-
map=
|
|
67
|
-
// binary
|
|
68
|
-
arity>1 ? (a,b) => a && (b=expr(opPrec)) && (
|
|
69
|
-
!a.length && !b.length ? (a=fn(a(),b()), ()=>a) : // static pre-eval like `"a"+"b"`
|
|
70
|
-
ctx => fn(a(ctx),b(ctx),a.id?.(ctx),b.id?.(ctx))
|
|
71
|
-
) :
|
|
72
|
-
// unary prefix (0 args)
|
|
73
|
-
arity ? a => !a && (a=expr(opPrec-1)) && (ctx => fn(a(ctx))) :
|
|
74
|
-
fn // custom parser
|
|
75
|
-
) =>
|
|
76
|
-
lookup[c] = (a, curPrec, from=idx) => curPrec<opPrec && (l<2||cur.substr(idx,l)==op) && (!word||!isId(cur.charCodeAt(idx+l))) && (idx+=l, map(a, curPrec)) || (idx=from, prev&&prev(a, curPrec));
|
|
77
|
-
|
|
78
|
-
const PERIOD=46, CPAREN=41, CBRACK$1=93, DQUOTE$1=34, _0=48, _9=57,
|
|
79
|
-
PREC_SEQ=1, PREC_SOME=4, PREC_EVERY=5, PREC_OR$1=6, PREC_XOR=7, PREC_AND=8,
|
|
80
|
-
PREC_EQ$1=9, PREC_COMP$1=10, PREC_SHIFT=11, PREC_SUM=12, PREC_MULT=13, PREC_UNARY$1=15, PREC_CALL=18;
|
|
81
|
-
|
|
82
|
-
let list$1, op$1, prec$1, fn$1,
|
|
83
|
-
isNum = c => c>=_0 && c<=_9,
|
|
84
|
-
// 1.2e+3, .5
|
|
85
|
-
num = n => (
|
|
86
|
-
n&&err('Unexpected number'),
|
|
87
|
-
n = skip(c=>c == PERIOD || isNum(c)),
|
|
88
|
-
(cur.charCodeAt(idx) == 69 || cur.charCodeAt(idx) == 101) && (n += skip(2) + skip(isNum)),
|
|
89
|
-
n=+n, n!=n ? err('Bad number') : () => n // 0 args means token is static
|
|
90
|
-
),
|
|
91
|
-
|
|
92
|
-
inc = (a,fn) => ctx => fn(a.of?a.of(ctx):ctx, a.id(ctx));
|
|
93
|
-
|
|
94
|
-
// numbers
|
|
95
|
-
for (op$1=_0;op$1<=_9;) lookup[op$1++] = num;
|
|
96
|
-
|
|
97
|
-
// operators
|
|
98
|
-
for (list$1=[
|
|
99
|
-
// "a"
|
|
100
|
-
'"', a => (a=a?err('Unexpected string'):skip(c => c-DQUOTE$1), skip()||err('Bad string'), ()=>a),,
|
|
101
|
-
|
|
102
|
-
// a.b
|
|
103
|
-
'.', (a,id) => (space(), id=skip(isId)||err(), fn$1=ctx=>a(ctx)[id], fn$1.id=()=>id, fn$1.of=a, fn$1), PREC_CALL,
|
|
104
|
-
|
|
105
|
-
// .2
|
|
106
|
-
// FIXME: .123 is not operator, so we skip back, but mb reorganizing num would be better
|
|
107
|
-
'.', a => !a && num(skip(-1)),,
|
|
108
|
-
|
|
109
|
-
// a[b]
|
|
110
|
-
'[', (a,b,fn) => a && (b=expr(0,CBRACK$1)||err(), fn=ctx=>a(ctx)[b(ctx)], fn.id=b, fn.of=a, fn), PREC_CALL,
|
|
111
|
-
|
|
112
|
-
// a(), a(b), (a,b), (a+b)
|
|
113
|
-
'(', (a,b,fn) => (
|
|
114
|
-
b=expr(0,CPAREN),
|
|
115
|
-
// a(), a(b), a(b,c,d)
|
|
116
|
-
a ? ctx => a(ctx).apply(a.of?.(ctx), b ? b.all ? b.all(ctx) : [b(ctx)] : []) :
|
|
117
|
-
// (a+b)
|
|
118
|
-
b || err()
|
|
119
|
-
), PREC_CALL,
|
|
120
|
-
|
|
121
|
-
// [a,b,c] or (a,b,c)
|
|
122
|
-
',', (a,prec,b=expr(PREC_SEQ),fn=ctx => (a(ctx), b(ctx))) => (
|
|
123
|
-
b ? (fn.all = a.all ? ctx => [...a.all(ctx),b(ctx)] : ctx => [a(ctx),b(ctx)]) : err('Skipped argument',),
|
|
124
|
-
fn
|
|
125
|
-
), PREC_SEQ,
|
|
126
|
-
|
|
127
|
-
'|', PREC_OR$1, (a,b)=>a|b,
|
|
128
|
-
'||', PREC_SOME, (a,b)=>a||b,
|
|
129
|
-
|
|
130
|
-
'&', PREC_AND, (a,b)=>a&b,
|
|
131
|
-
'&&', PREC_EVERY, (a,b)=>a&&b,
|
|
132
|
-
|
|
133
|
-
'^', PREC_XOR, (a,b)=>a^b,
|
|
134
|
-
|
|
135
|
-
// ==, !=
|
|
136
|
-
'==', PREC_EQ$1, (a,b)=>a==b,
|
|
137
|
-
'!=', PREC_EQ$1, (a,b)=>a!=b,
|
|
138
|
-
|
|
139
|
-
// > >= >> >>>, < <= <<
|
|
140
|
-
'>', PREC_COMP$1, (a,b)=>a>b,
|
|
141
|
-
'>=', PREC_COMP$1, (a,b)=>a>=b,
|
|
142
|
-
'>>', PREC_SHIFT, (a,b)=>a>>b,
|
|
143
|
-
'>>>', PREC_SHIFT, (a,b)=>a>>>b,
|
|
144
|
-
'<', PREC_COMP$1, (a,b)=>a<b,
|
|
145
|
-
'<=', PREC_COMP$1, (a,b)=>a<=b,
|
|
146
|
-
'<<', PREC_SHIFT, (a,b)=>a<<b,
|
|
147
|
-
|
|
148
|
-
// + ++ - --
|
|
149
|
-
'+', PREC_SUM, (a,b)=>a+b,
|
|
150
|
-
'+', PREC_UNARY$1, (a)=>+a,
|
|
151
|
-
'++', a => inc(a||expr(PREC_UNARY$1-1), a ? (a,b)=>a[b]++ : (a,b)=>++a[b]), PREC_UNARY$1,
|
|
152
|
-
|
|
153
|
-
'-', PREC_SUM, (a,b)=>a-b,
|
|
154
|
-
'-', PREC_UNARY$1, (a)=>-a,
|
|
155
|
-
'--', a => inc(a||expr(PREC_UNARY$1-1), a ? (a,b)=>a[b]-- : (a,b)=>--a[b]), PREC_UNARY$1,
|
|
156
|
-
|
|
157
|
-
// ! ~
|
|
158
|
-
'!', PREC_UNARY$1, (a)=>!a,
|
|
159
|
-
|
|
160
|
-
// * / %
|
|
161
|
-
'*', PREC_MULT, (a,b)=>a*b,
|
|
162
|
-
'/', PREC_MULT, (a,b)=>a/b,
|
|
163
|
-
'%', PREC_MULT, (a,b)=>a%b
|
|
164
|
-
]; [op$1,prec$1,fn$1,...list$1]=list$1, op$1;) set(op$1,prec$1,fn$1);
|
|
165
|
-
|
|
166
1
|
// justin lang https://github.com/endojs/Jessie/issues/66
|
|
2
|
+
import {set, lookup, skip, cur, idx, err, expr, isId, space, args} from './parser.js'
|
|
3
|
+
export { default } from './subscript.js'
|
|
167
4
|
|
|
168
|
-
const CBRACK=93, DQUOTE=34, QUOTE=39, BSLASH=92,
|
|
169
|
-
|
|
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
|
|
170
8
|
|
|
171
9
|
|
|
172
|
-
let list, op, prec, fn,
|
|
10
|
+
let u, list, op, prec, fn,
|
|
173
11
|
escape = {n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', v:'\v'},
|
|
174
12
|
string = q => (qc, c, str='') => {
|
|
175
|
-
qc&&err('Unexpected string')
|
|
13
|
+
qc&&err('Unexpected string') // must not follow another token
|
|
176
14
|
while (c=cur.charCodeAt(idx), c-q) {
|
|
177
|
-
if (c === BSLASH) skip(), c=skip(), str += escape[c] || c
|
|
178
|
-
else str += skip()
|
|
15
|
+
if (c === BSLASH) skip(), c=skip(), str += escape[c] || c
|
|
16
|
+
else str += skip()
|
|
179
17
|
}
|
|
180
18
|
return skip()||err('Bad string'), () => str
|
|
181
|
-
}
|
|
19
|
+
}
|
|
182
20
|
|
|
183
21
|
// operators
|
|
184
22
|
for (list=[
|
|
@@ -233,6 +71,4 @@ for (list=[
|
|
|
233
71
|
// for JSON case we should not collect arg (different evaluator than ternary)
|
|
234
72
|
':', (a, prec, b) => (b=expr(1.1)||err(), a.id&&args.pop(), ctx => [(a.id||a)(ctx), b(ctx)]), 1.1
|
|
235
73
|
|
|
236
|
-
]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
|
|
237
|
-
|
|
238
|
-
export { parse as default };
|
|
74
|
+
]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
|
package/justin.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let e,t,r,l,a,n,d,o=(l,a)=>(e=0,r=[],t=l.trim(),!(l=t?c():e=>
|
|
1
|
+
let e,t,r,l,a,n,d,o=(l,a)=>(e=0,r=[],t=l.trim(),!(l=t?c():e=>{})||t[e]?f():a=e=>l(e||{}),a.args=r,a),i=e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e,f=(r="Bad syntax",l=t[e])=>{throw SyntaxError(r+" `"+l+"` at "+e)},p=(r=1,l=e,a)=>{if("number"==typeof r)e+=r;else for(;r(t.charCodeAt(e));)e++;return t.slice(l,e)},c=(t=0,r,l,a,n,d)=>{for(;(l=s())&&(n=(d=h[l])&&d(a,t)||!a&&u());)a=n;return r&&(l==r?e++:f()),a},s=r=>{for(;(r=t.charCodeAt(e))<=32;)e++;return r},u=(e=p(i),t)=>e?(t=t=>t[e],r.push(e),t.id=()=>e,t):0,h=[],g=o.set=(r,l,a=32,n=r.charCodeAt(0),d=r.length,o=h[n],f=a.length||([a,l]=[l,a],0),p=r.toUpperCase()!==r,s=(f>1?(e,t)=>e&&(t=c(l))&&(e.length||t.length?r=>a(e(r),t(r),e.id?.(r),t.id?.(r)):(e=a(e(),t()),()=>e)):f?e=>!e&&(e=c(l-1))&&(t=>a(e(t))):a))=>h[n]=(a,n,f=e)=>n<l&&(d<2||t.substr(e,d)==r)&&(!p||!i(t.charCodeAt(e+d)))&&(e+=d,s(a,n))||(e=f,o&&o(a,n)),x=e=>e>=48&&e<=57,C=r=>(r&&f("Unexpected number"),r=p((e=>46==e||x(e))),(69==t.charCodeAt(e)||101==t.charCodeAt(e))&&(r+=p(2)+p(x)),(r=+r)!=r?f("Bad number"):()=>r),A=(e,t)=>r=>t(e.of?e.of(r):r,e.id(r));for(a=48;a<=57;)h[a++]=C;for(l=['"',e=>(e=e?f("Unexpected string"):p((e=>e-34)),p()||f("Bad string"),()=>e),,".",(e,t)=>(s(),t=p(i)||f(),d=r=>e(r)[t],d.id=()=>t,d.of=e,d),18,".",e=>!e&&C(p(-1)),,"[",(e,t,r)=>e&&(t=c(0,93)||f(),(r=r=>e(r)[t(r)]).id=t,r.of=e,r),18,"(",(e,t,r)=>(t=c(0,41),e?r=>e(r).apply(e.of?.(r),t?t.all?t.all(r):[t(r)]:[]):t||f()),18,",",(e,t,r=c(1),l=(t=>(e(t),r(t))))=>(r?l.all=e.all?t=>[...e.all(t),r(t)]:t=>[e(t),r(t)]:f("Skipped argument"),l),1,"|",6,(e,t)=>e|t,"||",4,(e,t)=>e||t,"&",8,(e,t)=>e&t,"&&",5,(e,t)=>e&&t,"^",7,(e,t)=>e^t,"==",9,(e,t)=>e==t,"!=",9,(e,t)=>e!=t,">",10,(e,t)=>e>t,">=",10,(e,t)=>e>=t,">>",11,(e,t)=>e>>t,">>>",11,(e,t)=>e>>>t,"<",10,(e,t)=>e<t,"<=",10,(e,t)=>e<=t,"<<",11,(e,t)=>e<<t,"+",12,(e,t)=>e+t,"+",15,e=>+e,"++",e=>A(e||c(14),e?(e,t)=>e[t]++:(e,t)=>++e[t]),15,"-",12,(e,t)=>e-t,"-",15,e=>-e,"--",e=>A(e||c(14),e?(e,t)=>e[t]--:(e,t)=>--e[t]),15,"!",15,e=>!e,"*",13,(e,t)=>e*t,"/",13,(e,t)=>e/t,"%",13,(e,t)=>e%t];[a,n,d,...l]=l,a;)g(a,n,d);let U,b,m,y,B={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},v=r=>(l,a,n="")=>{for(l&&f("Unexpected string");(a=t.charCodeAt(e))-r;)92===a?(p(),a=p(),n+=B[a]||a):n+=p();return p()||f("Bad string"),()=>n};for((U=['"',v(34),,"'",v(39),,"/*",(r,l)=>(p((r=>42!==r&&47!==t.charCodeAt(e+1))),p(2),r||c(l)),,"//",(e,t)=>(p((e=>e>=32)),e||c(t)),,"null",e=>e?f("Unexpected literal"):()=>null,,"true",e=>e?f("Unexpected literal"):()=>!0,,"false",e=>e?f("Unexpected literal"):()=>!1,,"undefined",e=>e?f("Unexpected literal"):()=>{},,";",e=>c()||(()=>{}),,"===",9,(e,t)=>e===t,"!==",9,(e,t)=>e!==t,"~",15,e=>~e,"**",(e,t,r=c(13))=>t=>e(t)**r(t),14,":",3.1,(e,t)=>[e,t],"?",3,(e,t)=>e?t[0]:t[1],"??",6,(e,t)=>e??t,"?.",e=>e&&(t=>e(t)||(()=>{})),,"?.",(e,t)=>(s(),(t=p(i))&&(r=>e(r)?.[t])),,"in",10,(e,t)=>e in t,"[",e=>!e&&((e=c(0,93))?e.all?t=>e.all(t):t=>[e(t)]:e=>[]),,"{",(e,t)=>!e&&((e=c(0,125))?r=>(t=(e.all||e)(r),Object.fromEntries(e.all?t:[t])):e=>({})),,":",(e,t,l)=>(l=c(1.1)||f(),e.id&&r.pop(),t=>[(e.id||e)(t),l(t)]),1.1]);[b,m,y,...U]=U,b;)g(b,m,y);export{o as default};
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "subscript",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.4.0",
|
|
4
4
|
"description": "Fast and tiny expression evaluator with common syntax microlanguage.",
|
|
5
|
-
"main": "
|
|
6
|
-
"module": "
|
|
5
|
+
"main": "subscript.js",
|
|
6
|
+
"module": "subscript.js",
|
|
7
7
|
"browser": "subscript.js",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"files": [
|
|
10
|
-
"
|
|
10
|
+
"parser.js",
|
|
11
11
|
"subscript.js",
|
|
12
12
|
"subscript.min.js",
|
|
13
13
|
"justin.js",
|
|
@@ -19,12 +19,13 @@
|
|
|
19
19
|
"test": "test"
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
|
-
"build": "npm run build-subscript && npm run
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
22
|
+
"build": "npm run build-subscript && npm run build-justin",
|
|
23
|
+
"minify": "npm run minify-subscript && npm run minify-justin",
|
|
24
|
+
"build-subscript": "rollup subscript.js --file subscript.min.js --format esm --name \"Subscript\"",
|
|
25
|
+
"minify-subscript": "terser subscript.min.js -o subscript.min.js --module -c passes=3 -m",
|
|
26
|
+
"build-justin": "rollup justin.js --file justin.min.js --format esm --name \"Justin\"",
|
|
27
|
+
"minify-justin": "terser justin.min.js -o justin.min.js --module -c passes=3 -m",
|
|
28
|
+
"test": "node test && node test/jsep && node test/perf"
|
|
28
29
|
},
|
|
29
30
|
"repository": {
|
|
30
31
|
"type": "git",
|
|
File without changes
|
package/subscript.js
CHANGED
|
@@ -1,85 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
let idx, cur, args,
|
|
5
|
-
|
|
6
|
-
// no handling tagged literals since easily done on user side with cache, if needed
|
|
7
|
-
parse = (s, fn) => (
|
|
8
|
-
idx=0, args=[], cur=s.trim(),
|
|
9
|
-
!(s = cur ? expr() : ctx=>fn) || cur[idx] ? err() :
|
|
10
|
-
fn = ctx=>s(ctx||{}), fn.args = args, fn
|
|
11
|
-
),
|
|
12
|
-
|
|
13
|
-
isId = c =>
|
|
14
|
-
(c >= 48 && c <= 57) || // 0..9
|
|
15
|
-
(c >= 65 && c <= 90) || // A...Z
|
|
16
|
-
(c >= 97 && c <= 122) || // a...z
|
|
17
|
-
c == 36 || c == 95 || // $, _,
|
|
18
|
-
(c >= 192 && c != 215 && c != 247), // any non-ASCII
|
|
19
|
-
|
|
20
|
-
err = (msg='Bad syntax',c=cur[idx]) => { throw SyntaxError(msg + ' `' + c + '` at ' + idx) },
|
|
21
|
-
|
|
22
|
-
skip = (is=1, from=idx, l) => {
|
|
23
|
-
if (typeof is == 'number') idx += is;
|
|
24
|
-
else while (is(cur.charCodeAt(idx))) idx++;
|
|
25
|
-
return cur.slice(from, idx)
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
// a + b - c
|
|
29
|
-
expr = (prec=0, end, cc, token, newNode, fn) => {
|
|
30
|
-
// chunk/token parser
|
|
31
|
-
while (
|
|
32
|
-
( cc=space() ) && // till not end
|
|
33
|
-
// FIXME: extra work is happening here, when lookup bails out due to lower precedence -
|
|
34
|
-
// it makes extra `space` call for parent exprs on the same character to check precedence again
|
|
35
|
-
( newNode =
|
|
36
|
-
(fn=lookup[cc]) && fn(token, prec) || // if operator with higher precedence isn't found
|
|
37
|
-
(!token && id()) // parse literal or quit. token seqs are forbidden: `a b`, `a "b"`, `1.32 a`
|
|
38
|
-
)
|
|
39
|
-
) token = newNode;
|
|
40
|
-
|
|
41
|
-
// check end character
|
|
42
|
-
// FIXME: can't show "Unclose paren", because can be unknown operator within group as well
|
|
43
|
-
if (end) cc==end?idx++:err();
|
|
44
|
-
|
|
45
|
-
return token
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
// skip space chars, return first non-space character
|
|
49
|
-
space = cc => { while ((cc = cur.charCodeAt(idx)) <= SPACE) idx++; return cc },
|
|
50
|
-
|
|
51
|
-
// variable identifier
|
|
52
|
-
id = (name=skip(isId), fn) => name ? (fn=ctx => ctx[name], args.push(name), fn.id=()=>name, fn) : 0,
|
|
53
|
-
|
|
54
|
-
// operator/token lookup table
|
|
55
|
-
lookup = [],
|
|
56
|
-
|
|
57
|
-
// create operator checker/mapper (see examples)
|
|
58
|
-
set = parse.set = (
|
|
59
|
-
op,
|
|
60
|
-
opPrec, fn=SPACE, // if opPrec & fn come in reverse order - consider them raw parse fn case, still precedence possible
|
|
61
|
-
c=op.charCodeAt(0),
|
|
62
|
-
l=op.length,
|
|
63
|
-
prev=lookup[c],
|
|
64
|
-
arity=fn.length || ([fn,opPrec]=[opPrec,fn], 0),
|
|
65
|
-
word=op.toUpperCase()!==op, // make sure word boundary comes after word operator
|
|
66
|
-
map=
|
|
67
|
-
// binary
|
|
68
|
-
arity>1 ? (a,b) => a && (b=expr(opPrec)) && (
|
|
69
|
-
!a.length && !b.length ? (a=fn(a(),b()), ()=>a) : // static pre-eval like `"a"+"b"`
|
|
70
|
-
ctx => fn(a(ctx),b(ctx),a.id?.(ctx),b.id?.(ctx))
|
|
71
|
-
) :
|
|
72
|
-
// unary prefix (0 args)
|
|
73
|
-
arity ? a => !a && (a=expr(opPrec-1)) && (ctx => fn(a(ctx))) :
|
|
74
|
-
fn // custom parser
|
|
75
|
-
) =>
|
|
76
|
-
lookup[c] = (a, curPrec, from=idx) => curPrec<opPrec && (l<2||cur.substr(idx,l)==op) && (!word||!isId(cur.charCodeAt(idx+l))) && (idx+=l, map(a, curPrec)) || (idx=from, prev&&prev(a, curPrec));
|
|
77
|
-
|
|
78
|
-
const PERIOD=46, CPAREN=41, CBRACK=93, DQUOTE=34, _0=48, _9=57,
|
|
1
|
+
import {parse, set, lookup, skip, cur, idx, err, expr, isId, args, space} from './parser.js'
|
|
2
|
+
|
|
3
|
+
const PERIOD=46, OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, _0=48, _9=57,
|
|
79
4
|
PREC_SEQ=1, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
|
|
80
|
-
PREC_EQ=9, PREC_COMP=10, PREC_SHIFT=11, PREC_SUM=12, PREC_MULT=13, PREC_UNARY=15, PREC_CALL=18
|
|
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
|
|
81
6
|
|
|
82
|
-
let list, op, prec, fn,
|
|
7
|
+
let u, list, op, prec, fn,
|
|
83
8
|
isNum = c => c>=_0 && c<=_9,
|
|
84
9
|
// 1.2e+3, .5
|
|
85
10
|
num = n => (
|
|
@@ -89,10 +14,10 @@ let list, op, prec, fn,
|
|
|
89
14
|
n=+n, n!=n ? err('Bad number') : () => n // 0 args means token is static
|
|
90
15
|
),
|
|
91
16
|
|
|
92
|
-
inc = (a,fn) => ctx => fn(a.of?a.of(ctx):ctx, a.id(ctx))
|
|
17
|
+
inc = (a,fn) => ctx => fn(a.of?a.of(ctx):ctx, a.id(ctx))
|
|
93
18
|
|
|
94
19
|
// numbers
|
|
95
|
-
for (op=_0;op<=_9;) lookup[op++] = num
|
|
20
|
+
for (op=_0;op<=_9;) lookup[op++] = num
|
|
96
21
|
|
|
97
22
|
// operators
|
|
98
23
|
for (list=[
|
|
@@ -161,6 +86,6 @@ for (list=[
|
|
|
161
86
|
'*', PREC_MULT, (a,b)=>a*b,
|
|
162
87
|
'/', PREC_MULT, (a,b)=>a/b,
|
|
163
88
|
'%', PREC_MULT, (a,b)=>a%b
|
|
164
|
-
]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
|
|
165
|
-
|
|
166
|
-
export
|
|
89
|
+
]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
|
|
90
|
+
|
|
91
|
+
export default parse
|
package/subscript.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let e,r,t,a,o,d,l,n=(a,o)=>(e=0,t=[],r=a.trim(),!(a=r?s():e=>
|
|
1
|
+
let e,r,t,a,o,d,l,n=(a,o)=>(e=0,t=[],r=a.trim(),!(a=r?s():e=>{})||r[e]?h():o=e=>a(e||{}),o.args=t,o),f=e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e,h=(t="Bad syntax",a=r[e])=>{throw SyntaxError(t+" `"+a+"` at "+e)},i=(t=1,a=e,o)=>{if("number"==typeof t)e+=t;else for(;t(r.charCodeAt(e));)e++;return r.slice(a,e)},s=(r=0,t,a,o,d,l)=>{for(;(a=p())&&(d=(l=c[a])&&l(o,r)||!o&&u());)o=d;return t&&(a==t?e++:h()),o},p=t=>{for(;(t=r.charCodeAt(e))<=32;)e++;return t},u=(e=i(f),r)=>e?(r=r=>r[e],t.push(e),r.id=()=>e,r):0,c=[],g=n.set=(t,a,o=32,d=t.charCodeAt(0),l=t.length,n=c[d],h=o.length||([o,a]=[a,o],0),i=t.toUpperCase()!==t,p=(h>1?(e,r)=>e&&(r=s(a))&&(e.length||r.length?t=>o(e(t),r(t),e.id?.(t),r.id?.(t)):(e=o(e(),r()),()=>e)):h?e=>!e&&(e=s(a-1))&&(r=>o(e(r))):o))=>c[d]=(o,d,h=e)=>d<a&&(l<2||r.substr(e,l)==t)&&(!i||!f(r.charCodeAt(e+l)))&&(e+=l,p(o,d))||(e=h,n&&n(o,d)),C=e=>e>=48&&e<=57,A=t=>(t&&h("Unexpected number"),t=i((e=>46==e||C(e))),(69==r.charCodeAt(e)||101==r.charCodeAt(e))&&(t+=i(2)+i(C)),(t=+t)!=t?h("Bad number"):()=>t),m=(e,r)=>t=>r(e.of?e.of(t):t,e.id(t));for(o=48;o<=57;)c[o++]=A;for(a=['"',e=>(e=e?h("Unexpected string"):i((e=>e-34)),i()||h("Bad string"),()=>e),,".",(e,r)=>(p(),r=i(f)||h(),l=t=>e(t)[r],l.id=()=>r,l.of=e,l),18,".",e=>!e&&A(i(-1)),,"[",(e,r,t)=>e&&(r=s(0,93)||h(),(t=t=>e(t)[r(t)]).id=r,t.of=e,t),18,"(",(e,r,t)=>(r=s(0,41),e?t=>e(t).apply(e.of?.(t),r?r.all?r.all(t):[r(t)]:[]):r||h()),18,",",(e,r,t=s(1),a=(r=>(e(r),t(r))))=>(t?a.all=e.all?r=>[...e.all(r),t(r)]:r=>[e(r),t(r)]:h("Skipped argument"),a),1,"|",6,(e,r)=>e|r,"||",4,(e,r)=>e||r,"&",8,(e,r)=>e&r,"&&",5,(e,r)=>e&&r,"^",7,(e,r)=>e^r,"==",9,(e,r)=>e==r,"!=",9,(e,r)=>e!=r,">",10,(e,r)=>e>r,">=",10,(e,r)=>e>=r,">>",11,(e,r)=>e>>r,">>>",11,(e,r)=>e>>>r,"<",10,(e,r)=>e<r,"<=",10,(e,r)=>e<=r,"<<",11,(e,r)=>e<<r,"+",12,(e,r)=>e+r,"+",15,e=>+e,"++",e=>m(e||s(14),e?(e,r)=>e[r]++:(e,r)=>++e[r]),15,"-",12,(e,r)=>e-r,"-",15,e=>-e,"--",e=>m(e||s(14),e?(e,r)=>e[r]--:(e,r)=>--e[r]),15,"!",15,e=>!e,"*",13,(e,r)=>e*r,"/",13,(e,r)=>e/r,"%",13,(e,r)=>e%r];[o,d,l,...a]=a,o;)g(o,d,l);export{n as default};
|
package/src/justin.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
// justin lang https://github.com/endojs/Jessie/issues/66
|
|
2
|
-
import {set, lookup, skip, cur, idx, err, expr, isId, space, args} from './index.js'
|
|
3
|
-
export { default } from './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('Unexpected string') // 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
|
-
}
|
|
20
|
-
|
|
21
|
-
// operators
|
|
22
|
-
for (list=[
|
|
23
|
-
// "' with /
|
|
24
|
-
'"', string(DQUOTE),,
|
|
25
|
-
"'", string(QUOTE),,
|
|
26
|
-
|
|
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)),,
|
|
30
|
-
|
|
31
|
-
// literals
|
|
32
|
-
'null', a => a ? err('Unexpected literal') : ()=>null,,
|
|
33
|
-
'true', a => a ? err('Unexpected literal') : ()=>true,,
|
|
34
|
-
'false', a => a ? err('Unexpected literal') : ()=>false,,
|
|
35
|
-
'undefined', a => a ? err('Unexpected literal') : ()=>undefined,,
|
|
36
|
-
|
|
37
|
-
';', a => expr()||(()=>{}),,
|
|
38
|
-
|
|
39
|
-
// operators
|
|
40
|
-
'===', PREC_EQ, (a,b) => a===b,
|
|
41
|
-
'!==', PREC_EQ, (a,b) => a!==b,
|
|
42
|
-
'~', PREC_UNARY, (a) => ~a,
|
|
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[0] : b[1],
|
|
50
|
-
|
|
51
|
-
'??', PREC_OR, (a,b) => a??b,
|
|
52
|
-
|
|
53
|
-
// a?.[, a?.( - postfix operator
|
|
54
|
-
'?.', a => a && (ctx => a(ctx)||(()=>{})),,//(a) => a||(()=>{}),
|
|
55
|
-
// a?.b - optional chain operator
|
|
56
|
-
'?.', (a,id) => (space(), id=skip(isId)) && (ctx => a(ctx)?.[id]),,
|
|
57
|
-
|
|
58
|
-
'in', PREC_COMP, (a,b) => a in b,
|
|
59
|
-
|
|
60
|
-
// [a,b,c]
|
|
61
|
-
'[', (a) => !a && (
|
|
62
|
-
a=expr(0,CBRACK),
|
|
63
|
-
!a ? ctx => [] : a.all ? ctx => a.all(ctx) : ctx => [a(ctx)]
|
|
64
|
-
),,
|
|
65
|
-
|
|
66
|
-
// {a:1, b:2, c:3}
|
|
67
|
-
'{', (a, entries) => !a && (
|
|
68
|
-
a=expr(0,125),
|
|
69
|
-
!a ? ctx => ({}) : ctx => (entries=(a.all||a)(ctx), Object.fromEntries(a.all?entries:[entries]))
|
|
70
|
-
),,
|
|
71
|
-
// for JSON case we should not collect arg (different evaluator than ternary)
|
|
72
|
-
':', (a, prec, b) => (b=expr(1.1)||err(), a.id&&args.pop(), ctx => [(a.id||a)(ctx), b(ctx)]), 1.1
|
|
73
|
-
|
|
74
|
-
]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
|
package/src/subscript.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import {parse, set, lookup, skip, cur, idx, err, expr, isId, args, space} from './index.js'
|
|
2
|
-
|
|
3
|
-
const PERIOD=46, OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, _0=48, _9=57,
|
|
4
|
-
PREC_SEQ=1, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
|
|
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
|
|
6
|
-
|
|
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('Unexpected number'),
|
|
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) => ctx => fn(a.of?a.of(ctx):ctx, a.id(ctx))
|
|
18
|
-
|
|
19
|
-
// numbers
|
|
20
|
-
for (op=_0;op<=_9;) lookup[op++] = num
|
|
21
|
-
|
|
22
|
-
// operators
|
|
23
|
-
for (list=[
|
|
24
|
-
// "a"
|
|
25
|
-
'"', a => (a=a?err('Unexpected string'):skip(c => c-DQUOTE), skip()||err('Bad string'), ()=>a),,
|
|
26
|
-
|
|
27
|
-
// a.b
|
|
28
|
-
'.', (a,id) => (space(), id=skip(isId)||err(), fn=ctx=>a(ctx)[id], fn.id=()=>id, fn.of=a, fn), PREC_CALL,
|
|
29
|
-
|
|
30
|
-
// .2
|
|
31
|
-
// FIXME: .123 is not operator, so we skip back, but mb reorganizing num would be better
|
|
32
|
-
'.', a => !a && num(skip(-1)),,
|
|
33
|
-
|
|
34
|
-
// a[b]
|
|
35
|
-
'[', (a,b,fn) => a && (b=expr(0,CBRACK)||err(), fn=ctx=>a(ctx)[b(ctx)], fn.id=b, fn.of=a, fn), PREC_CALL,
|
|
36
|
-
|
|
37
|
-
// a(), a(b), (a,b), (a+b)
|
|
38
|
-
'(', (a,b,fn) => (
|
|
39
|
-
b=expr(0,CPAREN),
|
|
40
|
-
// a(), a(b), a(b,c,d)
|
|
41
|
-
a ? ctx => a(ctx).apply(a.of?.(ctx), b ? b.all ? b.all(ctx) : [b(ctx)] : []) :
|
|
42
|
-
// (a+b)
|
|
43
|
-
b || err()
|
|
44
|
-
), PREC_CALL,
|
|
45
|
-
|
|
46
|
-
// [a,b,c] or (a,b,c)
|
|
47
|
-
',', (a,prec,b=expr(PREC_SEQ),fn=ctx => (a(ctx), b(ctx))) => (
|
|
48
|
-
b ? (fn.all = a.all ? ctx => [...a.all(ctx),b(ctx)] : ctx => [a(ctx),b(ctx)]) : err('Skipped argument',),
|
|
49
|
-
fn
|
|
50
|
-
), PREC_SEQ,
|
|
51
|
-
|
|
52
|
-
'|', PREC_OR, (a,b)=>a|b,
|
|
53
|
-
'||', PREC_SOME, (a,b)=>a||b,
|
|
54
|
-
|
|
55
|
-
'&', PREC_AND, (a,b)=>a&b,
|
|
56
|
-
'&&', PREC_EVERY, (a,b)=>a&&b,
|
|
57
|
-
|
|
58
|
-
'^', PREC_XOR, (a,b)=>a^b,
|
|
59
|
-
|
|
60
|
-
// ==, !=
|
|
61
|
-
'==', PREC_EQ, (a,b)=>a==b,
|
|
62
|
-
'!=', PREC_EQ, (a,b)=>a!=b,
|
|
63
|
-
|
|
64
|
-
// > >= >> >>>, < <= <<
|
|
65
|
-
'>', PREC_COMP, (a,b)=>a>b,
|
|
66
|
-
'>=', PREC_COMP, (a,b)=>a>=b,
|
|
67
|
-
'>>', PREC_SHIFT, (a,b)=>a>>b,
|
|
68
|
-
'>>>', PREC_SHIFT, (a,b)=>a>>>b,
|
|
69
|
-
'<', PREC_COMP, (a,b)=>a<b,
|
|
70
|
-
'<=', PREC_COMP, (a,b)=>a<=b,
|
|
71
|
-
'<<', PREC_SHIFT, (a,b)=>a<<b,
|
|
72
|
-
|
|
73
|
-
// + ++ - --
|
|
74
|
-
'+', PREC_SUM, (a,b)=>a+b,
|
|
75
|
-
'+', PREC_UNARY, (a)=>+a,
|
|
76
|
-
'++', a => inc(a||expr(PREC_UNARY-1), a ? (a,b)=>a[b]++ : (a,b)=>++a[b]), PREC_UNARY,
|
|
77
|
-
|
|
78
|
-
'-', PREC_SUM, (a,b)=>a-b,
|
|
79
|
-
'-', PREC_UNARY, (a)=>-a,
|
|
80
|
-
'--', a => inc(a||expr(PREC_UNARY-1), a ? (a,b)=>a[b]-- : (a,b)=>--a[b]), PREC_UNARY,
|
|
81
|
-
|
|
82
|
-
// ! ~
|
|
83
|
-
'!', PREC_UNARY, (a)=>!a,
|
|
84
|
-
|
|
85
|
-
// * / %
|
|
86
|
-
'*', PREC_MULT, (a,b)=>a*b,
|
|
87
|
-
'/', PREC_MULT, (a,b)=>a/b,
|
|
88
|
-
'%', PREC_MULT, (a,b)=>a%b
|
|
89
|
-
]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
|
|
90
|
-
|
|
91
|
-
export default parse
|