subscript 6.0.4 → 6.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 +11 -6
- package/index.js +9 -8
- package/justin.js +10 -9
- package/justin.min.js +1 -1
- package/package.json +1 -1
- package/subscript.js +2 -2
- package/subscript.min.js +1 -1
package/README.md
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# <img alt="subscript" src="/subscript2.svg" height=28/> <!--sub͘<em>script</em>--> <!--<sub>SUB͘<em>SCRIPT</em></sub>--> <a href="https://github.com/spectjs/subscript/actions/workflows/node.js.yml"><img src="https://github.com/spectjs/subscript/actions/workflows/node.js.yml/badge.svg"/></a> <a href="http://npmjs.org/subscript"><img src="https://img.shields.io/npm/v/subscript"/></a> <a href="http://microjs.com/#subscript"><img src="https://img.shields.io/badge/microjs-subscript-blue?color=darkslateblue"/></a>
|
|
2
2
|
|
|
3
|
-
_Subscript_ is expression evaluator
|
|
3
|
+
_Subscript_ is expression evaluator with standard syntax<br/>
|
|
4
4
|
|
|
5
|
-
*
|
|
6
|
-
* Any fragment can be copy-pasted to any target language
|
|
5
|
+
* Any fragment can be copy-pasted to any language: C++, JS, Java, Python, Go, Rust etc.
|
|
7
6
|
* Tiny size <sub><a href="https://bundlephobia.com/package/subscript@6.0.0"><img alt="npm bundle size" src="https://img.shields.io/bundlephobia/minzip/subscript/latest?color=brightgreen&label=gzip"/></a></sub>
|
|
8
7
|
* :rocket: Fast [performance](#performance)
|
|
9
8
|
* Configurable & extensible
|
|
10
9
|
* Trivial to use
|
|
11
10
|
|
|
12
11
|
```js
|
|
13
|
-
import script from 'subscript.js'
|
|
12
|
+
import script from './subscript.js'
|
|
14
13
|
let fn = script`a.b + c(d - 1)`
|
|
15
14
|
fn({ a: { b:1 }, c: x => x * 2, d: 3 }) // 5
|
|
15
|
+
fn.args // ['a', 'c', 'd']
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## Motivation
|
|
@@ -47,6 +47,7 @@ Default operators are (same as JS precedence order):
|
|
|
47
47
|
* `a | b`
|
|
48
48
|
* `a && b`
|
|
49
49
|
* `a || b`
|
|
50
|
+
* `a , b`
|
|
50
51
|
|
|
51
52
|
Default literals:
|
|
52
53
|
|
|
@@ -56,7 +57,7 @@ Default literals:
|
|
|
56
57
|
Everything else can be extended via `parse.set(token, precedence, operator)` for unary or binary operators (detected by number of arguments in `operator`), or via `parse.set(token, parse, precedence)` for custom tokens.
|
|
57
58
|
|
|
58
59
|
```js
|
|
59
|
-
import script from 'subscript.js'
|
|
60
|
+
import script from './subscript.js'
|
|
60
61
|
|
|
61
62
|
// add ~ unary operator with precedence 15
|
|
62
63
|
script.set('~', 15, a => ~a)
|
|
@@ -120,6 +121,7 @@ It extends _subscript_ with:
|
|
|
120
121
|
+ `'` strings
|
|
121
122
|
+ `?:` ternary operator
|
|
122
123
|
+ `?.` optional chain operator
|
|
124
|
+
+ `??` nullish coalesce operator
|
|
123
125
|
+ `[...]` Array literal
|
|
124
126
|
+ `{...}` Object literal
|
|
125
127
|
+ `in` binary
|
|
@@ -287,6 +289,7 @@ Parse 30k times:
|
|
|
287
289
|
subscript: ~170 ms 🥇
|
|
288
290
|
justin: ~183 ms 🥈
|
|
289
291
|
jsep: ~270 ms 🥉
|
|
292
|
+
jexpr: ~297 ms
|
|
290
293
|
mr-parser: ~420 ms
|
|
291
294
|
expr-eval: ~480 ms
|
|
292
295
|
math-parser: ~570 ms
|
|
@@ -301,7 +304,8 @@ Eval 30k times:
|
|
|
301
304
|
new Function: ~7 ms 🥇
|
|
302
305
|
subscript: ~17 ms 🥈
|
|
303
306
|
justin: ~17 ms 🥈
|
|
304
|
-
|
|
307
|
+
jexpr: ~23 ms 🥉
|
|
308
|
+
jsep (expression-eval): ~30 ms
|
|
305
309
|
math-expression-evaluator: ~50ms
|
|
306
310
|
expr-eval: ~72 ms
|
|
307
311
|
jexl: ~110 ms
|
|
@@ -312,6 +316,7 @@ math-parser: -
|
|
|
312
316
|
|
|
313
317
|
## Alternatives
|
|
314
318
|
|
|
319
|
+
* [jexpr](https://github.com/justinfagnani/jexpr)
|
|
315
320
|
* [jexl](https://github.com/TomFrost/Jexl)
|
|
316
321
|
* [mozjexl](https://github.com/mozilla/mozjexl)
|
|
317
322
|
* [expr-eval](https://github.com/silentmatt/expr-eval)
|
package/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
const SPACE=32
|
|
1
|
+
const SPACE=32, CPAREN=41
|
|
2
2
|
|
|
3
|
-
// current string
|
|
4
|
-
export let idx, cur,
|
|
3
|
+
// current string, index and collected ids
|
|
4
|
+
export let idx, cur, args,
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
// no handling tagged literals since easily done on user side with cache, if needed
|
|
7
|
+
parse = (s, fn= !(cur=s, idx=0, args=[], s=expr()) || cur[idx] ? err() : ctx=>s(ctx||{})) => (fn.args = args, fn),
|
|
7
8
|
|
|
8
9
|
isId = c =>
|
|
9
10
|
(c >= 48 && c <= 57) || // 0..9
|
|
@@ -12,7 +13,7 @@ isId = c =>
|
|
|
12
13
|
c == 36 || c == 95 || // $, _,
|
|
13
14
|
(c >= 192 && c != 215 && c != 247), // any non-ASCII
|
|
14
15
|
|
|
15
|
-
err = (msg='
|
|
16
|
+
err = (msg='Bad syntax',c=cur[idx]) => { throw SyntaxError(msg + ' `' + c + '` at ' + idx) },
|
|
16
17
|
|
|
17
18
|
skip = (is=1, from=idx, l) => {
|
|
18
19
|
if (typeof is == 'number') idx += is
|
|
@@ -34,7 +35,8 @@ expr = (prec=0, end, cc, token, newNode, fn) => {
|
|
|
34
35
|
) token = newNode;
|
|
35
36
|
|
|
36
37
|
// check end character
|
|
37
|
-
|
|
38
|
+
// FIXME: can't show "Unclose paren", because can be unknown operator within group as well
|
|
39
|
+
if (end) cc==end?idx++:err()
|
|
38
40
|
|
|
39
41
|
return token
|
|
40
42
|
},
|
|
@@ -43,7 +45,7 @@ expr = (prec=0, end, cc, token, newNode, fn) => {
|
|
|
43
45
|
space = cc => { while ((cc = cur.charCodeAt(idx)) <= SPACE) idx++; return cc },
|
|
44
46
|
|
|
45
47
|
// variable identifier
|
|
46
|
-
id = (name=skip(isId), fn) => name ? (fn=ctx => ctx[name], fn.id=()=>name, fn) : 0,
|
|
48
|
+
id = (name=skip(isId), fn) => name ? (fn=ctx => ctx[name], args.push(name), fn.id=()=>name, fn) : 0,
|
|
47
49
|
|
|
48
50
|
// operator/token lookup table
|
|
49
51
|
lookup = [],
|
|
@@ -67,5 +69,4 @@ set = parse.set = (
|
|
|
67
69
|
arity ? a => !a && (a=expr(opPrec-1)) && (ctx => fn(a(ctx))) :
|
|
68
70
|
fn // custom parser
|
|
69
71
|
) =>
|
|
70
|
-
// FIXME: find out if that's possible to globalize precision and instead of passing it to map, just provide global
|
|
71
72
|
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))
|
package/justin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// justin lang https://github.com/endojs/Jessie/issues/66
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {set, lookup, skip, cur, idx, err, expr, isId, space, args} from './index.js'
|
|
3
|
+
export { default } from './subscript.js'
|
|
4
4
|
|
|
5
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
6
|
PREC_SEQ=1, PREC_COND=3, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
|
|
@@ -46,7 +46,9 @@ for (list=[
|
|
|
46
46
|
|
|
47
47
|
// ?:
|
|
48
48
|
':', 3.1, (a,b) => [a,b],
|
|
49
|
-
'?', 3, (a,b) => a ? b[
|
|
49
|
+
'?', 3, (a,b) => a ? b[0] : b[1],
|
|
50
|
+
|
|
51
|
+
'??', PREC_OR, (a,b) => a??b,
|
|
50
52
|
|
|
51
53
|
// a?.[, a?.( - postfix operator
|
|
52
54
|
'?.', a => a && (ctx => a(ctx)||(()=>{})),,//(a) => a||(()=>{}),
|
|
@@ -56,18 +58,17 @@ for (list=[
|
|
|
56
58
|
'in', PREC_COMP, (a,b) => a in b,
|
|
57
59
|
|
|
58
60
|
// [a,b,c]
|
|
59
|
-
'[', (a
|
|
61
|
+
'[', (a) => !a && (
|
|
60
62
|
a=expr(0,CBRACK),
|
|
61
63
|
!a ? ctx => [] : a.all ? ctx => a.all(ctx) : ctx => [a(ctx)]
|
|
62
64
|
),,
|
|
63
65
|
|
|
64
66
|
// {a:1, b:2, c:3}
|
|
65
|
-
'{', (a,
|
|
67
|
+
'{', (a, entries) => !a && (
|
|
66
68
|
a=expr(0,125),
|
|
67
|
-
!a ? ctx => ({}) : ctx => (
|
|
69
|
+
!a ? ctx => ({}) : ctx => (entries=(a.all||a)(ctx), Object.fromEntries(a.all?entries:[entries]))
|
|
68
70
|
),,
|
|
69
|
-
|
|
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
|
|
70
73
|
|
|
71
74
|
]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
|
|
72
|
-
|
|
73
|
-
export default parse
|
package/justin.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let e,r,
|
|
1
|
+
let e,t,r,l,a,n,d,o=(l,a=(t=l,e=0,r=[],!(l=p())||t[e]?f():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)},c=(r=1,l=e,a)=>{if("number"==typeof r)e+=r;else for(;r(t.charCodeAt(e));)e++;return t.slice(l,e)},p=(t=0,r,l,a,n,d)=>{for(;(l=s())&&(n=(d=u[l])&&d(a,t)||!a&&h());)a=n;return r&&(l==r?e++:f()),a},s=r=>{for(;(r=t.charCodeAt(e))<=32;)e++;return r},h=(e=c(i),t)=>e?(t=t=>t[e],r.push(e),t.id=()=>e,t):0,u=[],x=o.set=(r,l,a=32,n=r.charCodeAt(0),d=r.length,o=u[n],f=a.length||([a,l]=[l,a],0),c=r.toUpperCase()!==r,s=(f>1?(e,t)=>e&&(t=p(l))&&(e.length||t.length?r=>a(e(r),t(r)):(e=a(e(),t()),()=>e)):f?e=>!e&&(e=p(l-1))&&(t=>a(e(t))):a))=>u[n]=(a,n,f=e)=>n<l&&(d<2||t.substr(e,d)==r)&&(!c||!i(t.charCodeAt(e+d)))&&(e+=d,s(a,n))||(e=f,o&&o(a,n)),g=e=>e>=48&&e<=57,C=r=>(r&&f("Unexpected number"),r=c((e=>46==e||g(e))),(69==t.charCodeAt(e)||101==t.charCodeAt(e))&&(r+=c(2)+c(g)),(r=+r)!=r?f("Bad number"):()=>r),A=(e,t,r=e.of)=>l=>t(r?r(l):l,e.id());for(a=48;a<=57;)u[a++]=C;for(l=['"',e=>(e=e?f("Unexpected string"):c((e=>e-34)),c()||f("Bad string"),()=>e),,".",(e,t,r)=>e?(s(),t=c(i)||f(),(r=r=>e(r)[t]).id=()=>t,r.of=e,r):C(c(-1)),18,"[",(e,t,r)=>e&&(t=p(0,93)||f(),(r=r=>e(r)[t(r)]).id=t,r.of=e,r),18,"(",(e,t,r)=>(t=p(0,41),e?r=>e(r).apply(e.of?.(r),t?t.all?t.all(r):[t(r)]:[]):t||f()),18,",",(e,t,r=p(1))=>(r.all=e.all?t=>[...e.all(t),r(t)]:t=>[e(t),r(t)],r),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||p(14),e?(e,t)=>e[t]++:(e,t)=>++e[t]),15,"-",12,(e,t)=>e-t,"-",15,e=>-e,"--",e=>A(e||p(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;)x(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?(c(),a=c(),n+=B[a]||a):n+=c();return c()||f("Bad string"),()=>n};for((U=['"',v(34),,"'",v(39),,"/*",(r,l)=>(c((r=>42!==r&&47!==t.charCodeAt(e+1))),c(2),r||p(l)),,"//",(e,t)=>(c((e=>e>=32)),e||p(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=>p()||(()=>{}),,"===",9,(e,t)=>e===t,"!==",9,(e,t)=>e!==t,"~",15,e=>~e,"**",(e,t,r=p(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=c(i))&&(r=>e(r)?.[t])),,"in",10,(e,t)=>e in t,"[",e=>!e&&((e=p(0,93))?e.all?t=>e.all(t):t=>[e(t)]:e=>[]),,"{",(e,t)=>!e&&((e=p(0,125))?r=>(t=(e.all||e)(r),Object.fromEntries(e.all?t:[t])):e=>({})),,":",(e,t,l)=>(l=p(1.1)||f(),e.id&&r.pop(),t=>[(e.id||e)(t),l(t)]),1.1]);[b,m,y,...U]=U,b;)x(b,m,y);export{o as default};
|
package/package.json
CHANGED
package/subscript.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {parse, set, lookup, skip, cur, idx, err, expr, isId, space} from './index.js'
|
|
1
|
+
import {parse, set, lookup, skip, cur, idx, err, expr, isId, args, space} from './index.js'
|
|
2
2
|
|
|
3
3
|
const PERIOD=46, OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, _0=48, _9=57,
|
|
4
4
|
PREC_SEQ=1, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
|
|
@@ -9,7 +9,7 @@ let u, list, op, prec, fn,
|
|
|
9
9
|
// 1.2e+3, .5
|
|
10
10
|
num = n => (
|
|
11
11
|
n&&err('Unexpected number'),
|
|
12
|
-
n = skip(c=>c==PERIOD || isNum(c)),
|
|
12
|
+
n = skip(c=>c == PERIOD || isNum(c)),
|
|
13
13
|
(cur.charCodeAt(idx) == 69 || cur.charCodeAt(idx) == 101) && (n += skip(2) + skip(isNum)),
|
|
14
14
|
n=+n, n!=n ? err('Bad number') : () => n // 0 args means token is static
|
|
15
15
|
),
|
package/subscript.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let e,r,t,o,
|
|
1
|
+
let e,r,t,a,o,l,d,n=(a,o=(r=a,e=0,t=[],!(a=c())||r[e]?h():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)},s=(t=1,a=e,o)=>{if("number"==typeof t)e+=t;else for(;t(r.charCodeAt(e));)e++;return r.slice(a,e)},c=(r=0,t,a,o,l,d)=>{for(;(a=p())&&(l=(d=i[a])&&d(o,r)||!o&&u());)o=l;return t&&(a==t?e++:h()),o},p=t=>{for(;(t=r.charCodeAt(e))<=32;)e++;return t},u=(e=s(f),r)=>e?(r=r=>r[e],t.push(e),r.id=()=>e,r):0,i=[],g=n.set=(t,a,o=32,l=t.charCodeAt(0),d=t.length,n=i[l],h=o.length||([o,a]=[a,o],0),s=t.toUpperCase()!==t,p=(h>1?(e,r)=>e&&(r=c(a))&&(e.length||r.length?t=>o(e(t),r(t)):(e=o(e(),r()),()=>e)):h?e=>!e&&(e=c(a-1))&&(r=>o(e(r))):o))=>i[l]=(o,l,h=e)=>l<a&&(d<2||r.substr(e,d)==t)&&(!s||!f(r.charCodeAt(e+d)))&&(e+=d,p(o,l))||(e=h,n&&n(o,l)),C=e=>e>=48&&e<=57,A=t=>(t&&h("Unexpected number"),t=s((e=>46==e||C(e))),(69==r.charCodeAt(e)||101==r.charCodeAt(e))&&(t+=s(2)+s(C)),(t=+t)!=t?h("Bad number"):()=>t),x=(e,r,t=e.of)=>a=>r(t?t(a):a,e.id());for(o=48;o<=57;)i[o++]=A;for(a=['"',e=>(e=e?h("Unexpected string"):s((e=>e-34)),s()||h("Bad string"),()=>e),,".",(e,r,t)=>e?(p(),r=s(f)||h(),(t=t=>e(t)[r]).id=()=>r,t.of=e,t):A(s(-1)),18,"[",(e,r,t)=>e&&(r=c(0,93)||h(),(t=t=>e(t)[r(t)]).id=r,t.of=e,t),18,"(",(e,r,t)=>(r=c(0,41),e?t=>e(t).apply(e.of?.(t),r?r.all?r.all(t):[r(t)]:[]):r||h()),18,",",(e,r,t=c(1))=>(t.all=e.all?r=>[...e.all(r),t(r)]:r=>[e(r),t(r)],t),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=>x(e||c(14),e?(e,r)=>e[r]++:(e,r)=>++e[r]),15,"-",12,(e,r)=>e-r,"-",15,e=>-e,"--",e=>x(e||c(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,l,d,...a]=a,o;)g(o,l,d);export{n as default};
|