subscript 7.3.0 → 7.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/LICENSE +6 -6
- package/README.md +337 -376
- package/compile.js +8 -8
- package/justin.js +94 -94
- package/justin.min.js +1 -1
- package/package.json +83 -83
- package/parse.js +85 -85
- package/subscript.js +128 -128
- package/subscript.min.js +1 -1
package/compile.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// build optimized evaluator for the tree
|
|
2
|
-
export const compile = (node) => !Array.isArray(node) ? ctx => ctx?.[node] : operators[node[0]](...node.slice(1)),
|
|
3
|
-
|
|
4
|
-
operators = {},
|
|
5
|
-
|
|
6
|
-
operator = (op, fn, prev=operators[op]) => operators[op] = (...args) => fn(...args) || prev && prev(...args)
|
|
7
|
-
|
|
8
|
-
export default compile
|
|
1
|
+
// build optimized evaluator for the tree
|
|
2
|
+
export const compile = (node) => !Array.isArray(node) ? ctx => ctx?.[node] : operators[node[0]](...node.slice(1)),
|
|
3
|
+
|
|
4
|
+
operators = {},
|
|
5
|
+
|
|
6
|
+
operator = (op, fn, prev=operators[op]) => operators[op] = (...args) => fn(...args) || prev && prev(...args)
|
|
7
|
+
|
|
8
|
+
export default compile
|
package/justin.js
CHANGED
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
// justin lang https://github.com/endojs/Jessie/issues/66
|
|
2
|
-
import { skip, cur, idx, err, expr } from './parse.js'
|
|
3
|
-
import compile from './compile.js'
|
|
4
|
-
import subscript, { set } from './subscript.js'
|
|
5
|
-
|
|
6
|
-
const PERIOD=46, OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, QUOTE=39, _0=48, _9=57, BSLASH=92,
|
|
7
|
-
PREC_SEQ=1, PREC_COND=3, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
|
|
8
|
-
PREC_EQ=9, PREC_COMP=10, PREC_SHIFT=11, PREC_SUM=12, PREC_MULT=13, PREC_EXP=14, PREC_UNARY=15, PREC_POSTFIX=16, PREC_CALL=18, PREC_GROUP=19
|
|
9
|
-
|
|
10
|
-
let escape = {n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', v:'\v'},
|
|
11
|
-
string = q => (qc, c, str='') => {
|
|
12
|
-
qc&&err('Unexpected string') // must not follow another token
|
|
13
|
-
skip()
|
|
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
|
-
skip()
|
|
19
|
-
return ['', str]
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
list = [
|
|
23
|
-
// operators
|
|
24
|
-
'===', PREC_EQ, (a,b) => a===b,
|
|
25
|
-
'!==', PREC_EQ, (a,b) => a!==b,
|
|
26
|
-
'~', PREC_UNARY, (a) => ~a,
|
|
27
|
-
|
|
28
|
-
// ?:
|
|
29
|
-
'?', PREC_COND, [
|
|
30
|
-
(a, b, c) => a && (b=expr(2,58)) && (c=expr(3), ['?', a, b, c]),
|
|
31
|
-
(a, b, c) => (a=compile(a),b=compile(b),c=compile(c), ctx => a(ctx) ? b(ctx) : c(ctx))
|
|
32
|
-
],
|
|
33
|
-
|
|
34
|
-
'??', PREC_OR, (a,b) => a ?? b,
|
|
35
|
-
|
|
36
|
-
// a?.[, a?.( - postfix operator
|
|
37
|
-
'?.', PREC_CALL, [a => a && ['?.', a], a => (a=compile(a), ctx => a(ctx)||(()=>{})) ],
|
|
38
|
-
// a?.b - optional chain operator
|
|
39
|
-
'?.', PREC_CALL, [
|
|
40
|
-
(a,b) => a && (b=expr(PREC_CALL),!b?.map) && ['?.',a,b],
|
|
41
|
-
(a,b) => b && (a=compile(a), ctx => a(ctx)?.[b])
|
|
42
|
-
],
|
|
43
|
-
|
|
44
|
-
'in', PREC_COMP , (a,b) => a in b,
|
|
45
|
-
|
|
46
|
-
// "' with /
|
|
47
|
-
'"', , [string(DQUOTE)],
|
|
48
|
-
"'", , [string(QUOTE)],
|
|
49
|
-
|
|
50
|
-
// /**/, //
|
|
51
|
-
'/*', 20, [(a, prec) => (skip(c => c !== 42 && cur.charCodeAt(idx+1) !== 47), skip(2), a||expr(prec))],
|
|
52
|
-
'//', 20, [(a, prec) => (skip(c => c >= 32), a||expr(prec))],
|
|
53
|
-
|
|
54
|
-
// literals
|
|
55
|
-
'null', 20, [a => a ? err() : ['',null]],
|
|
56
|
-
'true', 20, [a => a ? err() : ['',true]],
|
|
57
|
-
'false', 20, [a => a ? err() : ['',false]],
|
|
58
|
-
'undefined', 20, [a => a ? err() : ['',undefined]],
|
|
59
|
-
|
|
60
|
-
// FIXME: make sure that is right
|
|
61
|
-
';', 20,
|
|
62
|
-
|
|
63
|
-
// right order
|
|
64
|
-
// '**', (a,prec,b=expr(PREC_EXP-1)) => ctx=>a(ctx)**b(ctx), PREC_EXP,
|
|
65
|
-
'**', -PREC_EXP, (a,b)=>a**b,
|
|
66
|
-
|
|
67
|
-
// [a,b,c]
|
|
68
|
-
'[', 20, [
|
|
69
|
-
(a) => !a && ['[', expr(0,93)||''],
|
|
70
|
-
(a,b) => !b && (
|
|
71
|
-
!a ? () => [] : // []
|
|
72
|
-
a[0] === ',' ? (a=a.slice(1).map(compile), ctx => a.map(a=>a(ctx))) : // [a,b,c]
|
|
73
|
-
(a=compile(a), ctx => [a(ctx)]) // [a]
|
|
74
|
-
)],
|
|
75
|
-
|
|
76
|
-
// {a:1, b:2, c:3}
|
|
77
|
-
'{', 20, [
|
|
78
|
-
a => !a && (['{', expr(0,125)||'']),
|
|
79
|
-
(a,b) => (
|
|
80
|
-
!a ? ctx => ({}) : // {}
|
|
81
|
-
a[0] === ',' ? (a=a.slice(1).map(compile), ctx=>Object.fromEntries(a.map(a=>a(ctx)))) : // {a:1,b:2}
|
|
82
|
-
a[0] === ':' ? (a=compile(a), ctx => Object.fromEntries([a(ctx)])) : // {a:1}
|
|
83
|
-
(b=compile(a), ctx=>({[a]:b(ctx)}))
|
|
84
|
-
)
|
|
85
|
-
],
|
|
86
|
-
':', 1.1, [
|
|
87
|
-
(a, b) => (b=expr(1.1)||err(), [':',a,b]),
|
|
88
|
-
(a,b) => (b=compile(b),a=Array.isArray(a)?compile(a):(a=>a).bind(0,a), ctx=>[a(ctx),b(ctx)])
|
|
89
|
-
]
|
|
90
|
-
]
|
|
91
|
-
for (;list[2];) set(...list.splice(0,3))
|
|
92
|
-
|
|
93
|
-
export default subscript
|
|
94
|
-
export * from './subscript.js'
|
|
1
|
+
// justin lang https://github.com/endojs/Jessie/issues/66
|
|
2
|
+
import { skip, cur, idx, err, expr } from './parse.js'
|
|
3
|
+
import compile from './compile.js'
|
|
4
|
+
import subscript, { set } from './subscript.js'
|
|
5
|
+
|
|
6
|
+
const PERIOD=46, OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, QUOTE=39, _0=48, _9=57, BSLASH=92,
|
|
7
|
+
PREC_SEQ=1, PREC_COND=3, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
|
|
8
|
+
PREC_EQ=9, PREC_COMP=10, PREC_SHIFT=11, PREC_SUM=12, PREC_MULT=13, PREC_EXP=14, PREC_UNARY=15, PREC_POSTFIX=16, PREC_CALL=18, PREC_GROUP=19
|
|
9
|
+
|
|
10
|
+
let escape = {n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', v:'\v'},
|
|
11
|
+
string = q => (qc, c, str='') => {
|
|
12
|
+
qc&&err('Unexpected string') // must not follow another token
|
|
13
|
+
skip()
|
|
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
|
+
skip()
|
|
19
|
+
return ['', str]
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
list = [
|
|
23
|
+
// operators
|
|
24
|
+
'===', PREC_EQ, (a,b) => a===b,
|
|
25
|
+
'!==', PREC_EQ, (a,b) => a!==b,
|
|
26
|
+
'~', PREC_UNARY, (a) => ~a,
|
|
27
|
+
|
|
28
|
+
// ?:
|
|
29
|
+
'?', PREC_COND, [
|
|
30
|
+
(a, b, c) => a && (b=expr(2,58)) && (c=expr(3), ['?', a, b, c]),
|
|
31
|
+
(a, b, c) => (a=compile(a),b=compile(b),c=compile(c), ctx => a(ctx) ? b(ctx) : c(ctx))
|
|
32
|
+
],
|
|
33
|
+
|
|
34
|
+
'??', PREC_OR, (a,b) => a ?? b,
|
|
35
|
+
|
|
36
|
+
// a?.[, a?.( - postfix operator
|
|
37
|
+
'?.', PREC_CALL, [a => a && ['?.', a], a => (a=compile(a), ctx => a(ctx)||(()=>{})) ],
|
|
38
|
+
// a?.b - optional chain operator
|
|
39
|
+
'?.', PREC_CALL, [
|
|
40
|
+
(a,b) => a && (b=expr(PREC_CALL),!b?.map) && ['?.',a,b],
|
|
41
|
+
(a,b) => b && (a=compile(a), ctx => a(ctx)?.[b])
|
|
42
|
+
],
|
|
43
|
+
|
|
44
|
+
'in', PREC_COMP , (a,b) => a in b,
|
|
45
|
+
|
|
46
|
+
// "' with /
|
|
47
|
+
'"', , [string(DQUOTE)],
|
|
48
|
+
"'", , [string(QUOTE)],
|
|
49
|
+
|
|
50
|
+
// /**/, //
|
|
51
|
+
'/*', 20, [(a, prec) => (skip(c => c !== 42 && cur.charCodeAt(idx+1) !== 47), skip(2), a||expr(prec))],
|
|
52
|
+
'//', 20, [(a, prec) => (skip(c => c >= 32), a||expr(prec))],
|
|
53
|
+
|
|
54
|
+
// literals
|
|
55
|
+
'null', 20, [a => a ? err() : ['',null]],
|
|
56
|
+
'true', 20, [a => a ? err() : ['',true]],
|
|
57
|
+
'false', 20, [a => a ? err() : ['',false]],
|
|
58
|
+
'undefined', 20, [a => a ? err() : ['',undefined]],
|
|
59
|
+
|
|
60
|
+
// FIXME: make sure that is right
|
|
61
|
+
';', -20, (...args) => args.findLast(i => i != null),
|
|
62
|
+
|
|
63
|
+
// right order
|
|
64
|
+
// '**', (a,prec,b=expr(PREC_EXP-1)) => ctx=>a(ctx)**b(ctx), PREC_EXP,
|
|
65
|
+
'**', -PREC_EXP, (a,b)=>a**b,
|
|
66
|
+
|
|
67
|
+
// [a,b,c]
|
|
68
|
+
'[', 20, [
|
|
69
|
+
(a) => !a && ['[', expr(0,93)||''],
|
|
70
|
+
(a,b) => !b && (
|
|
71
|
+
!a ? () => [] : // []
|
|
72
|
+
a[0] === ',' ? (a=a.slice(1).map(compile), ctx => a.map(a=>a(ctx))) : // [a,b,c]
|
|
73
|
+
(a=compile(a), ctx => [a(ctx)]) // [a]
|
|
74
|
+
)],
|
|
75
|
+
|
|
76
|
+
// {a:1, b:2, c:3}
|
|
77
|
+
'{', 20, [
|
|
78
|
+
a => !a && (['{', expr(0,125)||'']),
|
|
79
|
+
(a,b) => (
|
|
80
|
+
!a ? ctx => ({}) : // {}
|
|
81
|
+
a[0] === ',' ? (a=a.slice(1).map(compile), ctx=>Object.fromEntries(a.map(a=>a(ctx)))) : // {a:1,b:2}
|
|
82
|
+
a[0] === ':' ? (a=compile(a), ctx => Object.fromEntries([a(ctx)])) : // {a:1}
|
|
83
|
+
(b=compile(a), ctx=>({[a]:b(ctx)}))
|
|
84
|
+
)
|
|
85
|
+
],
|
|
86
|
+
':', 1.1, [
|
|
87
|
+
(a, b) => (b=expr(1.1)||err(), [':',a,b]),
|
|
88
|
+
(a,b) => (b=compile(b),a=Array.isArray(a)?compile(a):(a=>a).bind(0,a), ctx=>[a(ctx),b(ctx)])
|
|
89
|
+
]
|
|
90
|
+
]
|
|
91
|
+
for (;list[2];) set(...list.splice(0,3))
|
|
92
|
+
|
|
93
|
+
export default subscript
|
|
94
|
+
export * from './subscript.js'
|
package/justin.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let e,t,r=r=>(e=0,t=r,r=s(),t[e]?l():r||""),l=(r="Bad syntax",l=t[e],n=t.slice(0,e).split("\n"),a=n.pop())=>{throw SyntaxError(`${r}
|
|
1
|
+
let e,t,r=r=>(e=0,t=r,r=s(),t[e]?l():r||""),l=(r="Bad syntax",l=t[e],n=t.slice(0,e).split("\n"),a=n.pop())=>{throw SyntaxError(`${r} ${l?`\`${l}\` `:""}at ${n.length}:${a.length}`,n.length)},n=(r="Bad syntax",l=t[e],n=t.slice(0,e).split("\n"),a=n.pop())=>{let s=t.slice(e-10,e).split("\n").pop(),i=t.slice(e+1,e+10).split("\n").shift(),p=n.length+":"+a.length;throw EvalError(`${r} at ${p} \`${s+l+i}\`\n${" ".repeat(18+r.length+p.length+s.length+1)}^`)},a=(r=1,l=e,n)=>{if("number"==typeof r)e+=r;else for(;n=r(t.charCodeAt(e));)e+=n;return t.slice(l,e)},s=(t=0,n,a,s,i,p)=>{for(;(a=r.space())&&(i=((p=o[a])&&p(s,t))??(!s&&r.id()));)s=i;return n&&(a==n?e++:l()),s},i=e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e,p=r.space=r=>{for(;(r=t.charCodeAt(e))<=32;)e++;return r},h=r.id=e=>a(i),o=[],c=(r,l=32,n,a=r.charCodeAt(0),s=r.length,p=o[a],h=r.toUpperCase()!==r)=>o[a]=(a,o,c=e)=>o<l&&(s<2||t.substr(e,s)==r)&&(!h||!i(t.charCodeAt(e+s)))&&(e+=s,n(a,o))||(e=c,p?.(a,o)),f=(e,t,r)=>c(e,t,((l,n)=>l&&(n=s(t-!!r))&&[e,l,n])),d=(e,t,r)=>c(e,t,(l=>r?l&&[e,l]:!l&&(l=s(t-1))&&[e,l])),g=(e,t,r)=>c(e,t,((l,n)=>(l||r&&(l=[e]))&&((n=s(t))||r)&&(l[0]===e&&l[2]?(n&&l.push(n),l):[e,l,n])));const u=e=>Array.isArray(e)?m[e[0]](...e.slice(1)):t=>t?.[e],m={},A=(e,t,r=m[e])=>m[e]=(...e)=>t(...e)||r&&r(...e),b=e=>(e=r(e),t=>(e.call?e:e=u(e))(t)),y=(e,t,r)=>r[0]||r[1]?(t?c(e,t,r[0]):o[e.charCodeAt(0)||1]=r[0],A(e,r[1])):r.length?r.length>1?(f(e,Math.abs(t),t<0),A(e,((e,t)=>t&&(e=u(e),t=u(t),e.length||t.length?l=>r(e(l),t(l)):(e=r(e(),t()),()=>e))))):(d(e,t),A(e,((e,t)=>!t&&((e=u(e)).length?t=>r(e(t)):(e=r(e()),()=>e))))):(g(e,Math.abs(t),t<0),A(e,((...e)=>(e=e.map(u),t=>r(...e.map((e=>e(t)))))))),$=e=>e?l():["",(e=+a((e=>46===e||e>=48&&e<=57||(69===e||101===e?2:0))))!=e?l():e],C=(e,t,r,l)=>[e,t,[r=>r?["++"===e?"-":"+",[e,r],["",1]]:[e,s(t-1)],l=(e,t)=>"("===e[0]?l(e[1]):"."===e[0]?(t=e[2],e=u(e[1]),l=>r(e(l),t)):"["===e[0]?([,e,t]=e,e=u(e),t=u(t),l=>r(e(l),t(l))):t=>r(t,e)]],x=["",,[,e=>()=>e],'"',,[e=>e?l():["",(a()+a((e=>e-34?1:0))+(a()||l("Bad string"))).slice(1,-1)]],".",,[e=>!e&&$()],...Array(10).fill(0).flatMap(((e,t)=>[""+t,0,[$]])),",",1,(...e)=>e[e.length-1],"||",4,(...e)=>{let t,r=0;for(;!t&&r<e.length;)t=e[r++];return t},"&&",5,(...e)=>{let t=0,r=!0;for(;r&&t<e.length;)r=e[t++];return r},"+",12,(e,t)=>e+t,"-",12,(e,t)=>e-t,"*",13,(e,t)=>e*t,"/",13,(e,t)=>e/t,"%",13,(e,t)=>e%t,"|",6,(e,t)=>e|t,"&",8,(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,"<",10,(e,t)=>e<t,"<=",10,(e,t)=>e<=t,">>",11,(e,t)=>e>>t,">>>",11,(e,t)=>e>>>t,"<<",11,(e,t)=>e<<t,"+",15,e=>+e,"-",15,e=>-e,"!",15,e=>!e,...C("++",15,((e,t)=>++e[t])),...C("--",15,((e,t)=>--e[t])),"[",18,[e=>e&&["[",e,s(0,93)||l()],(e,t)=>t&&(e=u(e),t=u(t),r=>e(r)[t(r)])],".",18,[(e,t)=>e&&(t=s(18))&&[".",e,t],(e,t)=>(e=u(e),t=t[0]?t:t[1],r=>e(r)[t])],"(",18,[e=>!e&&["(",s(0,41)||l()],u],"(",18,[(e,t)=>e&&((t=s(0,41))?["(",e,t]:["(",e,""]),(e,t,r,l)=>null!=t&&(l=""==t?()=>[]:","===t[0]?(t=t.slice(1).map(u),e=>t.map((t=>t(e)))):(t=u(t),e=>[t(e)]),"."===e[0]?(r=e[2],e=u(e[1]),t=>e(t)[r](...l(t))):"["===e[0]?(r=u(e[2]),e=u(e[1]),t=>e(t)[r(t)](...l(t))):(e=u(e),t=>e(t)(...l(t))))]];for(;x[2];)y(...x.splice(0,3));let E={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},v=r=>(n,s,i="")=>{for(n&&l("Unexpected string"),a();(s=t.charCodeAt(e))-r;)92===s?(a(),s=a(),i+=E[s]||s):i+=a();return a(),["",i]},B=["===",9,(e,t)=>e===t,"!==",9,(e,t)=>e!==t,"~",15,e=>~e,"?",3,[(e,t,r)=>e&&(t=s(2,58))&&["?",e,t,s(3)],(e,t,r)=>(e=u(e),t=u(t),r=u(r),l=>e(l)?t(l):r(l))],"??",6,(e,t)=>e??t,"?.",18,[e=>e&&["?.",e],e=>(e=u(e),t=>e(t)||(()=>{}))],"?.",18,[(e,t)=>e&&!(t=s(18))?.map&&["?.",e,t],(e,t)=>t&&(e=u(e),r=>e(r)?.[t])],"in",10,(e,t)=>e in t,'"',,[v(34)],"'",,[v(39)],"/*",20,[(r,l)=>(a((r=>42!==r&&47!==t.charCodeAt(e+1))),a(2),r||s(l))],"//",20,[(e,t)=>(a((e=>e>=32)),e||s(t))],"null",20,[e=>e?l():["",null]],"true",20,[e=>e?l():["",!0]],"false",20,[e=>e?l():["",!1]],"undefined",20,[e=>e?l():["",void 0]],";",-20,(...e)=>e.findLast((e=>null!=e)),"**",-14,(e,t)=>e**t,"[",20,[e=>!e&&["[",s(0,93)||""],(e,t)=>!t&&(e?","===e[0]?(e=e.slice(1).map(u),t=>e.map((e=>e(t)))):(e=u(e),t=>[e(t)]):()=>[])],"{",20,[e=>!e&&["{",s(0,125)||""],(e,t)=>e?","===e[0]?(e=e.slice(1).map(u),t=>Object.fromEntries(e.map((e=>e(t))))):":"===e[0]?(e=u(e),t=>Object.fromEntries([e(t)])):(t=u(e),r=>({[e]:t(r)})):e=>({})],":",1.1,[(e,t)=>[":",e,s(1.1)||l()],(e,t)=>(t=u(t),e=Array.isArray(e)?u(e):(e=>e).bind(0,e),r=>[e(r),t(r)])]];for(;B[2];)y(...B.splice(0,3));export{f as binary,u as compile,t as cur,b as default,l as err,s as expr,h as id,e as idx,i as isId,n as longErr,o as lookup,g as nary,A as operator,m as operators,r as parse,y as set,a as skip,p as space,c as token,d as unary};
|
package/package.json
CHANGED
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "subscript",
|
|
3
|
-
"version": "7.
|
|
4
|
-
"description": "Fast and tiny expression evaluator with common syntax microlanguage.",
|
|
5
|
-
"main": "subscript.js",
|
|
6
|
-
"module": "subscript.js",
|
|
7
|
-
"browser": "subscript.js",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": "./subscript.js",
|
|
10
|
-
"./parse.js": "./parse.js",
|
|
11
|
-
"./compile.js": "./compile.js",
|
|
12
|
-
"./subscript.js": "./subscript.js",
|
|
13
|
-
"./justin.js": "./justin.js"
|
|
14
|
-
},
|
|
15
|
-
"type": "module",
|
|
16
|
-
"files": [
|
|
17
|
-
"parse.js",
|
|
18
|
-
"compile.js",
|
|
19
|
-
"subscript.js",
|
|
20
|
-
"subscript.min.js",
|
|
21
|
-
"justin.js",
|
|
22
|
-
"justin.min.js"
|
|
23
|
-
],
|
|
24
|
-
"directories": {
|
|
25
|
-
"lib": "lib",
|
|
26
|
-
"src": "src",
|
|
27
|
-
"test": "test"
|
|
28
|
-
},
|
|
29
|
-
"scripts": {
|
|
30
|
-
"build": "npm run build-subscript && npm run build-justin",
|
|
31
|
-
"min": "npm run min-subscript && npm run min-justin",
|
|
32
|
-
"build-subscript": "rollup subscript.js --file subscript.min.js --format esm --name \"Subscript\"",
|
|
33
|
-
"min-subscript": "terser subscript.min.js -o subscript.min.js --module -c passes=3 -m",
|
|
34
|
-
"build-justin": "rollup justin.js --file justin.min.js --format esm --name \"Justin\"",
|
|
35
|
-
"min-justin": "terser justin.min.js -o justin.min.js --module -c passes=3 -m",
|
|
36
|
-
"test": "node test && node test/jsep && node test/perf"
|
|
37
|
-
},
|
|
38
|
-
"repository": {
|
|
39
|
-
"type": "git",
|
|
40
|
-
"url": "git+https://github.com/spectjs/subscript.git"
|
|
41
|
-
},
|
|
42
|
-
"keywords": [
|
|
43
|
-
"jexl",
|
|
44
|
-
"jsep",
|
|
45
|
-
"expression",
|
|
46
|
-
"evaluator",
|
|
47
|
-
"parser",
|
|
48
|
-
"evaluation",
|
|
49
|
-
"math",
|
|
50
|
-
"arithmetic",
|
|
51
|
-
"justin",
|
|
52
|
-
"eval",
|
|
53
|
-
"math-eval",
|
|
54
|
-
"math-evaluator",
|
|
55
|
-
"math-expression-evaluator",
|
|
56
|
-
"calculation",
|
|
57
|
-
"jessie",
|
|
58
|
-
"jessica",
|
|
59
|
-
"eval",
|
|
60
|
-
"dsl",
|
|
61
|
-
"json",
|
|
62
|
-
"calculator",
|
|
63
|
-
"calc",
|
|
64
|
-
"math.js",
|
|
65
|
-
"mathjs",
|
|
66
|
-
"math-codegen",
|
|
67
|
-
"math-parser",
|
|
68
|
-
"formula",
|
|
69
|
-
"operator",
|
|
70
|
-
"overload"
|
|
71
|
-
],
|
|
72
|
-
"author": "Dmitry Iv.",
|
|
73
|
-
"license": "ISC",
|
|
74
|
-
"bugs": {
|
|
75
|
-
"url": "https://github.com/spectjs/subscript/issues"
|
|
76
|
-
},
|
|
77
|
-
"homepage": "https://github.com/spectjs/subscript#readme",
|
|
78
|
-
"devDependencies": {
|
|
79
|
-
"rollup": "^2.60.2",
|
|
80
|
-
"terser": "^5.10.0",
|
|
81
|
-
"tst": "^7.1.1"
|
|
82
|
-
}
|
|
83
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "subscript",
|
|
3
|
+
"version": "7.4.0",
|
|
4
|
+
"description": "Fast and tiny expression evaluator with common syntax microlanguage.",
|
|
5
|
+
"main": "subscript.js",
|
|
6
|
+
"module": "subscript.js",
|
|
7
|
+
"browser": "subscript.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./subscript.js",
|
|
10
|
+
"./parse.js": "./parse.js",
|
|
11
|
+
"./compile.js": "./compile.js",
|
|
12
|
+
"./subscript.js": "./subscript.js",
|
|
13
|
+
"./justin.js": "./justin.js"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"files": [
|
|
17
|
+
"parse.js",
|
|
18
|
+
"compile.js",
|
|
19
|
+
"subscript.js",
|
|
20
|
+
"subscript.min.js",
|
|
21
|
+
"justin.js",
|
|
22
|
+
"justin.min.js"
|
|
23
|
+
],
|
|
24
|
+
"directories": {
|
|
25
|
+
"lib": "lib",
|
|
26
|
+
"src": "src",
|
|
27
|
+
"test": "test"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "npm run build-subscript && npm run build-justin",
|
|
31
|
+
"min": "npm run min-subscript && npm run min-justin",
|
|
32
|
+
"build-subscript": "rollup subscript.js --file subscript.min.js --format esm --name \"Subscript\"",
|
|
33
|
+
"min-subscript": "terser subscript.min.js -o subscript.min.js --module -c passes=3 -m",
|
|
34
|
+
"build-justin": "rollup justin.js --file justin.min.js --format esm --name \"Justin\"",
|
|
35
|
+
"min-justin": "terser justin.min.js -o justin.min.js --module -c passes=3 -m",
|
|
36
|
+
"test": "node test && node test/jsep && node test/perf"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/spectjs/subscript.git"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"jexl",
|
|
44
|
+
"jsep",
|
|
45
|
+
"expression",
|
|
46
|
+
"evaluator",
|
|
47
|
+
"parser",
|
|
48
|
+
"evaluation",
|
|
49
|
+
"math",
|
|
50
|
+
"arithmetic",
|
|
51
|
+
"justin",
|
|
52
|
+
"eval",
|
|
53
|
+
"math-eval",
|
|
54
|
+
"math-evaluator",
|
|
55
|
+
"math-expression-evaluator",
|
|
56
|
+
"calculation",
|
|
57
|
+
"jessie",
|
|
58
|
+
"jessica",
|
|
59
|
+
"eval",
|
|
60
|
+
"dsl",
|
|
61
|
+
"json",
|
|
62
|
+
"calculator",
|
|
63
|
+
"calc",
|
|
64
|
+
"math.js",
|
|
65
|
+
"mathjs",
|
|
66
|
+
"math-codegen",
|
|
67
|
+
"math-parser",
|
|
68
|
+
"formula",
|
|
69
|
+
"operator",
|
|
70
|
+
"overload"
|
|
71
|
+
],
|
|
72
|
+
"author": "Dmitry Iv.",
|
|
73
|
+
"license": "ISC",
|
|
74
|
+
"bugs": {
|
|
75
|
+
"url": "https://github.com/spectjs/subscript/issues"
|
|
76
|
+
},
|
|
77
|
+
"homepage": "https://github.com/spectjs/subscript#readme",
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"rollup": "^2.60.2",
|
|
80
|
+
"terser": "^5.10.0",
|
|
81
|
+
"tst": "^7.1.1"
|
|
82
|
+
}
|
|
83
|
+
}
|
package/parse.js
CHANGED
|
@@ -1,85 +1,85 @@
|
|
|
1
|
-
const SPACE=32
|
|
2
|
-
|
|
3
|
-
// current string, index and collected ids
|
|
4
|
-
export let idx, cur,
|
|
5
|
-
|
|
6
|
-
// no handling tagged literals since easily done on user side with cache, if needed
|
|
7
|
-
parse = s => (idx=0, cur=s, s = expr(), cur[idx] ? err() : s || ''),
|
|
8
|
-
|
|
9
|
-
err = (msg='Bad syntax', frag=cur[idx], prev=cur.slice(0,idx).split('\n'), last=prev.pop()) => {
|
|
10
|
-
throw SyntaxError(`${msg}
|
|
11
|
-
},
|
|
12
|
-
|
|
13
|
-
longErr = (msg='Bad syntax',
|
|
14
|
-
frag=cur[idx],
|
|
15
|
-
lines=cur.slice(0,idx).split('\n'),
|
|
16
|
-
last=lines.pop()
|
|
17
|
-
) => {
|
|
18
|
-
let before = cur.slice(idx-10,idx).split('\n').pop()
|
|
19
|
-
let after = cur.slice(idx+1, idx+10).split('\n').shift()
|
|
20
|
-
let location = lines.length + ':' + last.length
|
|
21
|
-
throw
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
skip = (is=1, from=idx, l) => {
|
|
25
|
-
if (typeof is == 'number') idx += is
|
|
26
|
-
else while (l=is(cur.charCodeAt(idx))) idx+=l
|
|
27
|
-
return cur.slice(from, idx)
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
// a + b - c
|
|
31
|
-
expr = (prec=0, end, cc, token, newNode, fn) => {
|
|
32
|
-
// chunk/token parser
|
|
33
|
-
while (
|
|
34
|
-
( cc=parse.space() ) && // till not end
|
|
35
|
-
// FIXME: extra work is happening here, when lookup bails out due to lower precedence -
|
|
36
|
-
// it makes extra `space` call for parent exprs on the same character to check precedence again
|
|
37
|
-
(newNode =
|
|
38
|
-
((fn=lookup[cc]) && fn(token, prec)) ?? // if operator with higher precedence isn't found
|
|
39
|
-
(!token && parse.id()) // parse literal or quit. token seqs are forbidden: `a b`, `a "b"`, `1.32 a`
|
|
40
|
-
)
|
|
41
|
-
) token = newNode;
|
|
42
|
-
|
|
43
|
-
// check end character
|
|
44
|
-
// FIXME: can't show "Unclose paren", because can be unknown operator within group as well
|
|
45
|
-
if (end) cc==end?idx++:err()
|
|
46
|
-
|
|
47
|
-
return token
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
isId = c =>
|
|
51
|
-
(c >= 48 && c <= 57) || // 0..9
|
|
52
|
-
(c >= 65 && c <= 90) || // A...Z
|
|
53
|
-
(c >= 97 && c <= 122) || // a...z
|
|
54
|
-
c == 36 || c == 95 || // $, _,
|
|
55
|
-
(c >= 192 && c != 215 && c != 247), // any non-ASCII
|
|
56
|
-
|
|
57
|
-
// skip space chars, return first non-space character
|
|
58
|
-
space = parse.space = cc => { while ((cc = cur.charCodeAt(idx)) <= SPACE) idx++; return cc },
|
|
59
|
-
|
|
60
|
-
id = parse.id = n => skip(isId),
|
|
61
|
-
|
|
62
|
-
// operator/token lookup table
|
|
63
|
-
// lookup[0] is id parser to let configs redefine it
|
|
64
|
-
lookup = [],
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// create operator checker/mapper (see examples)
|
|
68
|
-
token = (
|
|
69
|
-
op,
|
|
70
|
-
prec=SPACE,
|
|
71
|
-
map,
|
|
72
|
-
c=op.charCodeAt(0),
|
|
73
|
-
l=op.length,
|
|
74
|
-
prev=lookup[c],
|
|
75
|
-
word=op.toUpperCase()!==op // make sure word boundary comes after word operator
|
|
76
|
-
) => lookup[c] = (a, curPrec, from=idx) =>
|
|
77
|
-
(curPrec<prec && (l<2||cur.substr(idx,l)==op) && (!word||!isId(cur.charCodeAt(idx+l))) && (idx+=l, map(a, curPrec))) ||
|
|
78
|
-
(idx=from, prev?.(a, curPrec)),
|
|
79
|
-
|
|
80
|
-
// right assoc is indicated by negative precedence (meaning go from right to left)
|
|
81
|
-
binary = (op, prec, right) => token(op, prec, (a, b) => a && (b=expr(prec-!!right)) && [op,a,b] ),
|
|
82
|
-
unary = (op, prec, post) => token(op, prec, a => post ? (a && [op, a]) : (!a && (a=expr(prec-1)) && [op, a])),
|
|
83
|
-
nary = (op, prec, skips) => token(op, prec, (a, b) => a && (b=expr(prec),b||skips) && (a[0] === op && a[2] ? (b&&a.push(b), a) : [op,a,b]))
|
|
84
|
-
|
|
85
|
-
export default parse
|
|
1
|
+
const SPACE=32
|
|
2
|
+
|
|
3
|
+
// current string, index and collected ids
|
|
4
|
+
export let idx, cur,
|
|
5
|
+
|
|
6
|
+
// no handling tagged literals since easily done on user side with cache, if needed
|
|
7
|
+
parse = s => (idx=0, cur=s, s = expr(), cur[idx] ? err() : s || ''),
|
|
8
|
+
|
|
9
|
+
err = (msg='Bad syntax', frag=cur[idx], prev=cur.slice(0,idx).split('\n'), last=prev.pop()) => {
|
|
10
|
+
throw SyntaxError(`${msg} ${frag ? `\`${frag}\` ` : ''}at ${prev.length}:${last.length}`, prev.length)
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
longErr = (msg='Bad syntax',
|
|
14
|
+
frag=cur[idx],
|
|
15
|
+
lines=cur.slice(0,idx).split('\n'),
|
|
16
|
+
last=lines.pop()
|
|
17
|
+
) => {
|
|
18
|
+
let before = cur.slice(idx-10,idx).split('\n').pop()
|
|
19
|
+
let after = cur.slice(idx+1, idx+10).split('\n').shift()
|
|
20
|
+
let location = lines.length + ':' + last.length
|
|
21
|
+
throw EvalError(`${msg} at ${location} \`${before+frag+after}\`\n${' '.repeat(18 + msg.length + location.length + before.length + 1)}^`)
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
skip = (is=1, from=idx, l) => {
|
|
25
|
+
if (typeof is == 'number') idx += is
|
|
26
|
+
else while (l=is(cur.charCodeAt(idx))) idx+=l
|
|
27
|
+
return cur.slice(from, idx)
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
// a + b - c
|
|
31
|
+
expr = (prec=0, end, cc, token, newNode, fn) => {
|
|
32
|
+
// chunk/token parser
|
|
33
|
+
while (
|
|
34
|
+
( cc=parse.space() ) && // till not end
|
|
35
|
+
// FIXME: extra work is happening here, when lookup bails out due to lower precedence -
|
|
36
|
+
// it makes extra `space` call for parent exprs on the same character to check precedence again
|
|
37
|
+
(newNode =
|
|
38
|
+
((fn=lookup[cc]) && fn(token, prec)) ?? // if operator with higher precedence isn't found
|
|
39
|
+
(!token && parse.id()) // parse literal or quit. token seqs are forbidden: `a b`, `a "b"`, `1.32 a`
|
|
40
|
+
)
|
|
41
|
+
) token = newNode;
|
|
42
|
+
|
|
43
|
+
// check end character
|
|
44
|
+
// FIXME: can't show "Unclose paren", because can be unknown operator within group as well
|
|
45
|
+
if (end) cc==end?idx++:err()
|
|
46
|
+
|
|
47
|
+
return token
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
isId = c =>
|
|
51
|
+
(c >= 48 && c <= 57) || // 0..9
|
|
52
|
+
(c >= 65 && c <= 90) || // A...Z
|
|
53
|
+
(c >= 97 && c <= 122) || // a...z
|
|
54
|
+
c == 36 || c == 95 || // $, _,
|
|
55
|
+
(c >= 192 && c != 215 && c != 247), // any non-ASCII
|
|
56
|
+
|
|
57
|
+
// skip space chars, return first non-space character
|
|
58
|
+
space = parse.space = cc => { while ((cc = cur.charCodeAt(idx)) <= SPACE) idx++; return cc },
|
|
59
|
+
|
|
60
|
+
id = parse.id = n => skip(isId),
|
|
61
|
+
|
|
62
|
+
// operator/token lookup table
|
|
63
|
+
// lookup[0] is id parser to let configs redefine it
|
|
64
|
+
lookup = [],
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
// create operator checker/mapper (see examples)
|
|
68
|
+
token = (
|
|
69
|
+
op,
|
|
70
|
+
prec=SPACE,
|
|
71
|
+
map,
|
|
72
|
+
c=op.charCodeAt(0),
|
|
73
|
+
l=op.length,
|
|
74
|
+
prev=lookup[c],
|
|
75
|
+
word=op.toUpperCase()!==op // make sure word boundary comes after word operator
|
|
76
|
+
) => lookup[c] = (a, curPrec, from=idx) =>
|
|
77
|
+
(curPrec<prec && (l<2||cur.substr(idx,l)==op) && (!word||!isId(cur.charCodeAt(idx+l))) && (idx+=l, map(a, curPrec))) ||
|
|
78
|
+
(idx=from, prev?.(a, curPrec)),
|
|
79
|
+
|
|
80
|
+
// right assoc is indicated by negative precedence (meaning go from right to left)
|
|
81
|
+
binary = (op, prec, right) => token(op, prec, (a, b) => a && (b=expr(prec-!!right)) && [op,a,b] ),
|
|
82
|
+
unary = (op, prec, post) => token(op, prec, a => post ? (a && [op, a]) : (!a && (a=expr(prec-1)) && [op, a])),
|
|
83
|
+
nary = (op, prec, skips) => token(op, prec, (a, b) => (a || (skips && (a=[op]))) && (b=expr(prec),b||skips) && (a[0] === op && a[2] ? (b && a.push(b), a) : [op,a,b]))
|
|
84
|
+
|
|
85
|
+
export default parse
|