subscript 7.0.7 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -62,28 +62,37 @@ Default literals:
62
62
  * `"abc"` strings
63
63
  * `1.2e+3` numbers
64
64
 
65
- Everything else can be extended via `subscript.set(str, prec, fn)` for unary, binary or n-ary operators (detected by number of arguments in `fn`), or via `subscript.set(str, prec, [parse, compile])` for custom tokens.
65
+ ## Extending
66
+
67
+ Operators/tokens can be extended via:
68
+
69
+ * `unary(str, prec, postfix=false)` − register unary operator, either prefix or postfix.
70
+ * `binary(str, prec, rightAssoc=false)` − register binary operator, optionally right-associative.
71
+ * `nary(str, prec)` − register n-ary (sequence) operator.
72
+ * `token(str, prec, fn)` − register custom token or literal. `fn` takes last token as argument and returns calltree node.
73
+ * `operator(str, fn)` − register evaluator for operator. `fn` takes node arguments and returns evaluator function.
66
74
 
67
75
  ```js
68
- import script, { compile } from './subscript.js'
76
+ import script, { operator, unary, binary, token } from './subscript.js'
69
77
 
70
78
  // add ~ unary operator with precedence 15
71
- script.set('~', 15, a => ~a)
79
+ unary('~', 15)
80
+ operator('~', a => ~a)
72
81
 
73
82
  // add === binary operator with precedence 9
74
- script.set('===', 9, (a, b) => a===b)
83
+ binary('===', 9)
84
+ operator('===', (a, b) => a===b)
75
85
 
76
86
  // add literals
77
- script.set('true', 20, [a => ['',true], a => ctx => a[1]])
78
- script.set('false', 20, [a => ['',false], a => ctx => a[1]])
79
-
80
- script(`true === false`)() // false
87
+ token('true', 20, a => ['',true])
88
+ token('false', 20, a => ['',false])
89
+ operator('', a => ctx => a[1]])
81
90
  ```
82
91
 
83
92
  See [subscript.js](subscript.js) or [justin.js](./justin.js) for examples.
84
93
 
85
94
 
86
- ## Parser & Compiler
95
+ ## Syntax tree
87
96
 
88
97
  Subscript exposes separate `./parse.js` and `./compile.js` entries. Parser builds AST, compiler converts it to evaluable function.
89
98
 
package/compile.js CHANGED
@@ -1,9 +1,8 @@
1
1
  // build optimized evaluator for the tree
2
- export const compile = (node) => !Array.isArray(node) ? ctx => ctx?.[node] : operator[node[0]](...node.slice(1)),
2
+ export const compile = (node) => !Array.isArray(node) ? ctx => ctx?.[node] : operators[node[0]](...node.slice(1)),
3
3
 
4
- set = compile.set = (op, fn, prev=operator[op]) => operator[op] = (...args) => fn(...args) || prev && prev(...args),
5
-
6
- operator = {}
4
+ operators = {},
7
5
 
6
+ operator = (op, fn, prev=operators[op]) => operators[op] = (...args) => fn(...args) || prev && prev(...args)
8
7
 
9
8
  export default compile
package/justin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // justin lang https://github.com/endojs/Jessie/issues/66
2
2
  import { skip, cur, idx, err, expr } from './parse.js'
3
3
  import compile from './compile.js'
4
- import subscript from './subscript.js'
4
+ import subscript, { set } from './subscript.js'
5
5
 
6
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
7
  PREC_SEQ=1, PREC_COND=3, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
@@ -88,7 +88,7 @@ list = [
88
88
  (a,b) => (b=compile(b),a=Array.isArray(a)?compile(a):(a=>a).bind(0,a), ctx=>[a(ctx),b(ctx)])
89
89
  ]
90
90
  ]
91
- for (;list[2];) subscript.set(...list.splice(0,3))
91
+ for (;list[2];) set(...list.splice(0,3))
92
92
 
93
93
  export default subscript
94
94
  export * from './subscript.js'
package/justin.min.js CHANGED
@@ -1 +1 @@
1
- let e,t,r=r=>(e=0,t=r,r=a(),t[e]?l():r||""),l=(r="Bad syntax",l=t[e],n=t.slice(0,e).split("\n"),a=n.pop())=>{throw SyntaxError(`${r} \`${a.slice(-10)}${l}\` at ${n.length}:${a.length}`)},n=(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)},a=(t=0,r,n,a,o,c)=>{for(;(n=s())&&(o=((c=i[n])&&c(a,t))??(!a&&i[0]()));)a=o;return r&&(n==r?e++:l()),a},s=r=>{for(;(r=t.charCodeAt(e))<=32;)e++;return r},o=e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e,i=[e=>n(o)];r.set=(r,l=32,n,a=r.charCodeAt(0),s=r.length,c=i[a],p=r.toUpperCase()!==r)=>i[a]=(a,i,f=e)=>i<l&&(s<2||t.substr(e,s)==r)&&(!p||!o(t.charCodeAt(e+s)))&&(e+=s,n(a,i))||(e=f,c?.(a,i));const c=e=>Array.isArray(e)?p[e[0]](...e.slice(1)):t=>t?.[e];c.set=(e,t,r=p[e])=>p[e]=(...e)=>t(...e)||r&&r(...e);const p={},f=e=>(e=r(e),t=>(e.call?e:e=c(e))(t)),h=f.set=(e,t,l,n=t<0,s=l[0],o=l[1],p=!s&&l.length)=>(s||=p?p>1?(r,l)=>r&&(l=a(t-n))&&[e,r,l]:r=>!r&&(r=a(t-1))&&[e,r]:(r,l)=>r&&(l=a(t))&&(r[0]===e&&r[2]?(r.push(l),r):[e,r,l]),o||=p?p>1?(e,t)=>t&&(e=c(e),t=c(t),e.length||t.length?r=>l(e(r),t(r)):(e=l(e(),t()),()=>e)):(e,t)=>!t&&((e=c(e)).length?t=>l(e(t)):(e=l(e()),()=>e)):(...e)=>(e=e.map(c),t=>l(...e.map((e=>e(t))))),(t=n?-t:t)?r.set(e,t,s):i[e.charCodeAt(0)||1]=s,c.set(e,o)),d=e=>e?l():["",(e=+n((e=>46===e||e>=48&&e<=57||(69===e||101===e?2:0))))!=e?l():e],u=(e,t,r,l)=>[e,t,[r=>r?["++"===e?"-":"+",[e,r],["",1]]:[e,a(t-1)],l=(e,t)=>"("===e[0]?l(e[1]):"."===e[0]?(t=e[2],e=c(e[1]),l=>r(e(l),t)):"["===e[0]?([,e,t]=e,e=c(e),t=c(t),l=>r(e(l),t(l))):t=>r(t,e)]],g=["",,[,e=>()=>e],'"',,[e=>e?l():["",(n()+n((e=>e-34?1:0))+(n()||l("Bad string"))).slice(1,-1)]],".",,[e=>!e&&d()],...Array(10).fill(0).flatMap(((e,t)=>[""+t,0,[d]])),",",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,...u("++",15,((e,t)=>++e[t])),...u("--",15,((e,t)=>--e[t])),"[",18,[e=>e&&["[",e,a(0,93)||l()],(e,t)=>t&&(e=c(e),t=c(t),r=>e(r)[t(r)])],".",18,[(e,t)=>e&&(t=a(18))&&[".",e,t],(e,t)=>(e=c(e),t=t[0]?t:t[1],r=>e(r)[t])],"(",18,[e=>!e&&["(",a(0,41)||l()],c],"(",18,[e=>e&&["(",e,a(0,41)||""],(e,t,r,l)=>null!=t&&(l=""==t?()=>[]:","===t[0]?(t=t.slice(1).map(c),e=>t.map((t=>t(e)))):(t=c(t),e=>[t(e)]),"."===e[0]?(r=e[2],e=c(e[1]),t=>e(t)[r](...l(t))):"["===e[0]?(r=c(e[2]),e=c(e[1]),t=>e(t)[r(t)](...l(t))):(e=c(e),t=>e(t)(...l(t))))]];for(;g[2];)h(...g.splice(0,3));let m={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},A=r=>(a,s,o="")=>{for(a&&l("Unexpected string"),n();(s=t.charCodeAt(e))-r;)92===s?(n(),s=n(),o+=m[s]||s):o+=n();return n(),["",o]},y=["===",9,(e,t)=>e===t,"!==",9,(e,t)=>e!==t,"~",15,e=>~e,"?",3,[(e,t,r)=>e&&(t=a(2,58))&&["?",e,t,a(3)],(e,t,r)=>(e=c(e),t=c(t),r=c(r),l=>e(l)?t(l):r(l))],"??",6,(e,t)=>e??t,"?.",18,[e=>e&&["?.",e],e=>(e=c(e),t=>e(t)||(()=>{}))],"?.",18,[(e,t)=>e&&!(t=a(18))?.map&&["?.",e,t],(e,t)=>t&&(e=c(e),r=>e(r)?.[t])],"in",10,(e,t)=>e in t,'"',,[A(34)],"'",,[A(39)],"/*",20,[(r,l)=>(n((r=>42!==r&&47!==t.charCodeAt(e+1))),n(2),r||a(l))],"//",20,[(e,t)=>(n((e=>e>=32)),e||a(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=>a()||[""]],"**",-14,(e,t)=>e**t,"[",20,[e=>!e&&["[",a(0,93)||""],(e,t)=>!t&&(e?","===e[0]?(e=e.slice(1).map(c),t=>e.map((e=>e(t)))):(e=c(e),t=>[e(t)]):()=>[])],"{",20,[e=>!e&&["{",a(0,125)||""],(e,t)=>e?","===e[0]?(e=e.slice(1).map(c),t=>Object.fromEntries(e.map((e=>e(t))))):":"===e[0]?(e=c(e),t=>Object.fromEntries([e(t)])):(t=c(e),r=>({[e]:t(r)})):e=>({})],":",1.1,[(e,t)=>[":",e,a(1.1)||l()],(e,t)=>(t=c(t),e=Array.isArray(e)?c(e):(e=>e).bind(0,e),r=>[e(r),t(r)])]];for(;y[2];)f.set(...y.splice(0,3));export{c as compile,f as default,r as parse};
1
+ let e,r,t=t=>(e=0,r=t,t=n(),r[e]?l():t||""),l=(t="Bad syntax",l=r[e],a=r.slice(0,e).split("\n"),n=a.pop())=>{throw SyntaxError(`${t} \`${l}\` at ${a.length}:${n.length}`)},a=(t=1,l=e,a)=>{if("number"==typeof t)e+=t;else for(;a=t(r.charCodeAt(e));)e+=a;return r.slice(l,e)},n=(r=0,a,n,s,i,o)=>{for(;(n=t.space())&&(i=((o=c[n])&&o(s,r))??(!s&&t.id()));)s=i;return a&&(n==a?e++:l()),s},s=e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e,i=t.space=t=>{for(;(t=r.charCodeAt(e))<=32;)e++;return t},o=t.id=e=>a(s),c=[],p=(t,l=32,a,n=t.charCodeAt(0),i=t.length,o=c[n],p=t.toUpperCase()!==t)=>c[n]=(n,c,h=e)=>c<l&&(i<2||r.substr(e,i)==t)&&(!p||!s(r.charCodeAt(e+i)))&&(e+=i,a(n,c))||(e=h,o?.(n,c)),h=(e,r,t)=>p(e,r,((l,a)=>l&&(a=n(r-!!t))&&[e,l,a])),f=(e,r,t)=>p(e,r,(l=>t?l&&[e,l]:!l&&(l=n(r-1))&&[e,l])),d=(e,r)=>p(e,r,((t,l)=>t&&(l=n(r))&&(t[0]===e&&t[2]?(t.push(l),t):[e,t,l])));const u=e=>Array.isArray(e)?g[e[0]](...e.slice(1)):r=>r?.[e],g={},m=(e,r,t=g[e])=>g[e]=(...e)=>r(...e)||t&&t(...e),A=e=>(e=t(e),r=>(e.call?e:e=u(e))(r)),b=(e,r,t)=>t[0]||t[1]?(r?p(e,r,t[0]):c[e.charCodeAt(0)||1]=t[0],m(e,t[1])):t.length?t.length>1?(h(e,Math.abs(r),r<0),m(e,((e,r)=>r&&(e=u(e),r=u(r),e.length||r.length?l=>t(e(l),r(l)):(e=t(e(),r()),()=>e))))):(f(e,r),m(e,((e,r)=>!r&&((e=u(e)).length?r=>t(e(r)):(e=t(e()),()=>e))))):(d(e,r),m(e,((...e)=>(e=e.map(u),r=>t(...e.map((e=>e(r)))))))),y=e=>e?l():["",(e=+a((e=>46===e||e>=48&&e<=57||(69===e||101===e?2:0))))!=e?l():e],C=(e,r,t,l)=>[e,r,[t=>t?["++"===e?"-":"+",[e,t],["",1]]:[e,n(r-1)],l=(e,r)=>"("===e[0]?l(e[1]):"."===e[0]?(r=e[2],e=u(e[1]),l=>t(e(l),r)):"["===e[0]?([,e,r]=e,e=u(e),r=u(r),l=>t(e(l),r(l))):r=>t(r,e)]],x=["",,[,e=>()=>e],'"',,[e=>e?l():["",(a()+a((e=>e-34?1:0))+(a()||l("Bad string"))).slice(1,-1)]],".",,[e=>!e&&y()],...Array(10).fill(0).flatMap(((e,r)=>[""+r,0,[y]])),",",1,(...e)=>e[e.length-1],"||",4,(...e)=>{let r,t=0;for(;!r&&t<e.length;)r=e[t++];return r},"&&",5,(...e)=>{let r=0,t=!0;for(;t&&r<e.length;)t=e[r++];return t},"+",12,(e,r)=>e+r,"-",12,(e,r)=>e-r,"*",13,(e,r)=>e*r,"/",13,(e,r)=>e/r,"%",13,(e,r)=>e%r,"|",6,(e,r)=>e|r,"&",8,(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,"<",10,(e,r)=>e<r,"<=",10,(e,r)=>e<=r,">>",11,(e,r)=>e>>r,">>>",11,(e,r)=>e>>>r,"<<",11,(e,r)=>e<<r,"+",15,e=>+e,"-",15,e=>-e,"!",15,e=>!e,...C("++",15,((e,r)=>++e[r])),...C("--",15,((e,r)=>--e[r])),"[",18,[e=>e&&["[",e,n(0,93)||l()],(e,r)=>r&&(e=u(e),r=u(r),t=>e(t)[r(t)])],".",18,[(e,r)=>e&&(r=n(18))&&[".",e,r],(e,r)=>(e=u(e),r=r[0]?r:r[1],t=>e(t)[r])],"(",18,[e=>!e&&["(",n(0,41)||l()],u],"(",18,[e=>e&&["(",e,n(0,41)||""],(e,r,t,l)=>null!=r&&(l=""==r?()=>[]:","===r[0]?(r=r.slice(1).map(u),e=>r.map((r=>r(e)))):(r=u(r),e=>[r(e)]),"."===e[0]?(t=e[2],e=u(e[1]),r=>e(r)[t](...l(r))):"["===e[0]?(t=u(e[2]),e=u(e[1]),r=>e(r)[t(r)](...l(r))):(e=u(e),r=>e(r)(...l(r))))]];for(;x[2];)b(...x.splice(0,3));let $={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},v=t=>(n,s,i="")=>{for(n&&l("Unexpected string"),a();(s=r.charCodeAt(e))-t;)92===s?(a(),s=a(),i+=$[s]||s):i+=a();return a(),["",i]},E=["===",9,(e,r)=>e===r,"!==",9,(e,r)=>e!==r,"~",15,e=>~e,"?",3,[(e,r,t)=>e&&(r=n(2,58))&&["?",e,r,n(3)],(e,r,t)=>(e=u(e),r=u(r),t=u(t),l=>e(l)?r(l):t(l))],"??",6,(e,r)=>e??r,"?.",18,[e=>e&&["?.",e],e=>(e=u(e),r=>e(r)||(()=>{}))],"?.",18,[(e,r)=>e&&!(r=n(18))?.map&&["?.",e,r],(e,r)=>r&&(e=u(e),t=>e(t)?.[r])],"in",10,(e,r)=>e in r,'"',,[v(34)],"'",,[v(39)],"/*",20,[(t,l)=>(a((t=>42!==t&&47!==r.charCodeAt(e+1))),a(2),t||n(l))],"//",20,[(e,r)=>(a((e=>e>=32)),e||n(r))],"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=>n()||[""]],"**",-14,(e,r)=>e**r,"[",20,[e=>!e&&["[",n(0,93)||""],(e,r)=>!r&&(e?","===e[0]?(e=e.slice(1).map(u),r=>e.map((e=>e(r)))):(e=u(e),r=>[e(r)]):()=>[])],"{",20,[e=>!e&&["{",n(0,125)||""],(e,r)=>e?","===e[0]?(e=e.slice(1).map(u),r=>Object.fromEntries(e.map((e=>e(r))))):":"===e[0]?(e=u(e),r=>Object.fromEntries([e(r)])):(r=u(e),t=>({[e]:r(t)})):e=>({})],":",1.1,[(e,r)=>[":",e,n(1.1)||l()],(e,r)=>(r=u(r),e=Array.isArray(e)?u(e):(e=>e).bind(0,e),t=>[e(t),r(t)])]];for(;E[2];)b(...E.splice(0,3));export{h as binary,u as compile,r as cur,A as default,l as err,n as expr,o as id,e as idx,s as isId,c as lookup,d as nary,m as operator,g as operators,t as parse,b as set,a as skip,i as space,p as token,f as unary};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "subscript",
3
- "version": "7.0.7",
3
+ "version": "7.2.0",
4
4
  "description": "Fast and tiny expression evaluator with common syntax microlanguage.",
5
5
  "main": "subscript.js",
6
6
  "module": "subscript.js",
@@ -28,11 +28,11 @@
28
28
  },
29
29
  "scripts": {
30
30
  "build": "npm run build-subscript && npm run build-justin",
31
- "minify": "npm run minify-subscript && npm run minify-justin",
31
+ "min": "npm run min-subscript && npm run min-justin",
32
32
  "build-subscript": "rollup subscript.js --file subscript.min.js --format esm --name \"Subscript\"",
33
- "minify-subscript": "terser subscript.min.js -o subscript.min.js --module -c passes=3 -m",
33
+ "min-subscript": "terser subscript.min.js -o subscript.min.js --module -c passes=3 -m",
34
34
  "build-justin": "rollup justin.js --file justin.min.js --format esm --name \"Justin\"",
35
- "minify-justin": "terser justin.min.js -o justin.min.js --module -c passes=3 -m",
35
+ "min-justin": "terser justin.min.js -o justin.min.js --module -c passes=3 -m",
36
36
  "test": "node test && node test/jsep && node test/perf"
37
37
  },
38
38
  "repository": {
@@ -78,6 +78,6 @@
78
78
  "devDependencies": {
79
79
  "rollup": "^2.60.2",
80
80
  "terser": "^5.10.0",
81
- "tst": "^7.1.0"
81
+ "tst": "^7.1.1"
82
82
  }
83
83
  }
package/parse.js CHANGED
@@ -6,9 +6,7 @@ export let idx, cur,
6
6
  // no handling tagged literals since easily done on user side with cache, if needed
7
7
  parse = s => (idx=0, cur=s, s = expr(), cur[idx] ? err() : s || ''),
8
8
 
9
- // err = (msg='Bad syntax',c=cur[idx]) => { throw SyntaxError(msg + ' `' + c + '` at ' + idx) },
10
-
11
- err = (msg='Bad syntax',frag=cur[idx],prev=cur.slice(0,idx).split('\n'),last=prev.pop()) => {
9
+ err = (msg='Bad syntax', frag=cur[idx], prev=cur.slice(0,idx).split('\n'), last=prev.pop()) => {
12
10
  throw SyntaxError(`${msg} \`${frag}\` at ${prev.length}:${last.length}`)
13
11
  },
14
12
 
@@ -20,15 +18,14 @@ skip = (is=1, from=idx, l) => {
20
18
 
21
19
  // a + b - c
22
20
  expr = (prec=0, end, cc, token, newNode, fn) => {
23
-
24
21
  // chunk/token parser
25
22
  while (
26
- ( cc=space() ) && // till not end
23
+ ( cc=parse.space() ) && // till not end
27
24
  // FIXME: extra work is happening here, when lookup bails out due to lower precedence -
28
25
  // it makes extra `space` call for parent exprs on the same character to check precedence again
29
26
  (newNode =
30
27
  ((fn=lookup[cc]) && fn(token, prec)) ?? // if operator with higher precedence isn't found
31
- (!token && lookup[0]()) // parse literal or quit. token seqs are forbidden: `a b`, `a "b"`, `1.32 a`
28
+ (!token && parse.id()) // parse literal or quit. token seqs are forbidden: `a b`, `a "b"`, `1.32 a`
32
29
  )
33
30
  ) token = newNode;
34
31
 
@@ -39,9 +36,6 @@ expr = (prec=0, end, cc, token, newNode, fn) => {
39
36
  return token
40
37
  },
41
38
 
42
- // skip space chars, return first non-space character
43
- space = cc => { while ((cc = cur.charCodeAt(idx)) <= SPACE) idx++; return cc },
44
-
45
39
  isId = c =>
46
40
  (c >= 48 && c <= 57) || // 0..9
47
41
  (c >= 65 && c <= 90) || // A...Z
@@ -49,12 +43,18 @@ isId = c =>
49
43
  c == 36 || c == 95 || // $, _,
50
44
  (c >= 192 && c != 215 && c != 247), // any non-ASCII
51
45
 
46
+ // skip space chars, return first non-space character
47
+ space = parse.space = cc => { while ((cc = cur.charCodeAt(idx)) <= SPACE) idx++; return cc },
48
+
49
+ id = parse.id = n => skip(isId),
50
+
52
51
  // operator/token lookup table
53
52
  // lookup[0] is id parser to let configs redefine it
54
- lookup = [n=>skip(isId)],
53
+ lookup = [],
54
+
55
55
 
56
56
  // create operator checker/mapper (see examples)
57
- set = parse.set = (
57
+ token = (
58
58
  op,
59
59
  prec=SPACE,
60
60
  map,
@@ -64,6 +64,11 @@ set = parse.set = (
64
64
  word=op.toUpperCase()!==op // make sure word boundary comes after word operator
65
65
  ) => lookup[c] = (a, curPrec, from=idx) =>
66
66
  (curPrec<prec && (l<2||cur.substr(idx,l)==op) && (!word||!isId(cur.charCodeAt(idx+l))) && (idx+=l, map(a, curPrec))) ||
67
- (idx=from, prev?.(a, curPrec))
67
+ (idx=from, prev?.(a, curPrec)),
68
+
69
+ // right assoc is indicated by negative precedence (meaning go from right to left)
70
+ binary = (op, prec, right) => token(op, prec, (a, b) => a && (b=expr(prec-!!right)) && [op,a,b] ),
71
+ unary = (op, prec, post) => token(op, prec, a => post ? (a && [op, a]) : (!a && (a=expr(prec-1)) && [op, a])),
72
+ nary = (op, prec) => token(op, prec, (a, b) => a && (b=expr(prec)) && (a[0] === op && a[2] ? (a.push(b), a) : [op,a,b]))
68
73
 
69
74
  export default parse
package/subscript.js CHANGED
@@ -1,5 +1,5 @@
1
- import parse, { lookup, skip, cur, idx, err, expr } from './parse.js'
2
- import compile from './compile.js'
1
+ import parse, { lookup, nary, binary, unary, token, skip, err, expr } from './parse.js'
2
+ import compile, { operator } from './compile.js'
3
3
 
4
4
  const OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, PERIOD=46, _0=48, _9=57,
5
5
  PREC_SEQ=1, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
@@ -9,19 +9,22 @@ const subscript = s => (s=parse(s), ctx => (s.call?s:(s=compile(s)))(ctx)),
9
9
 
10
10
  // set any operator
11
11
  // right assoc is indicated by negative precedence (meaning go from right to left)
12
- set = subscript.set = (op, prec, fn, right=prec<0, parseFn=fn[0], evalFn=fn[1], arity=!parseFn&&fn.length) => (
13
- parseFn ||=
14
- !arity ? (a, b) => a && (b=expr(prec)) && (a[0] === op && a[2] ? (a.push(b), a) : [op,a,b]) :
15
- arity > 1 ? (a, b) => a && (b=expr(prec-right)) && [op,a,b] :
16
- a => !a && (a=expr(prec-1)) && [op, a]
17
- ,
18
- evalFn ||=
19
- !arity ? (...args) => (args=args.map(compile), ctx => fn(...args.map(arg=>arg(ctx)))) :
20
- arity > 1 ? (a,b) => b && (a=compile(a),b=compile(b), !a.length&&!b.length ? (a=fn(a(),b()),()=>a) : ctx => fn(a(ctx),b(ctx))) :
21
- (a,b) => !b && (a=compile(a), !a.length ? (a=fn(a()),()=>a) : ctx => fn(a(ctx)))
22
- ,
23
- (prec=right?-prec:prec) ? parse.set(op,prec,parseFn) : (lookup[op.charCodeAt(0)||1]=parseFn),
24
- compile.set(op, evalFn)
12
+ set = (op, prec, fn) =>
13
+ (fn[0]||fn[1]) ? (prec ? token(op,prec,fn[0]) : (lookup[op.charCodeAt(0)||1]=fn[0]), operator(op, fn[1])) : (
14
+ !fn.length ? (
15
+ nary(op, prec),
16
+ operator(op, (...args) => (args=args.map(compile), ctx => fn(...args.map(arg=>arg(ctx)))))
17
+ ) :
18
+ fn.length > 1 ? (
19
+ binary(op, Math.abs(prec), prec<0),
20
+ operator(op,
21
+ (a,b) => b && (a=compile(a),b=compile(b), !a.length&&!b.length ? (a=fn(a(),b()),()=>a) : ctx => fn(a(ctx),b(ctx)))
22
+ )
23
+ ) :
24
+ (
25
+ unary(op, prec),
26
+ operator(op, (a,b) => !b && (a=compile(a), !a.length ? (a=fn(a()),()=>a) : ctx => fn(a(ctx))))
27
+ )
25
28
  ),
26
29
 
27
30
  num = a => a ? err() : ['', (a=+skip(c => c === PERIOD || (c>=_0 && c<=_9) || (c===69||c===101?2:0)))!=a?err():a],
@@ -35,6 +38,7 @@ inc = (op, prec, fn, ev) => [op, prec, [
35
38
  (ctx => fn(ctx,a)) // ++a
36
39
  )
37
40
  ]],
41
+
38
42
  list = [
39
43
  // literals
40
44
  // null operator returns first value (needed for direct literals)
@@ -119,4 +123,6 @@ list = [
119
123
  for (;list[2];) set(...list.splice(0,3))
120
124
 
121
125
  export default subscript
122
- export {compile, parse}
126
+ export {set}
127
+ export * from './parse.js'
128
+ export * from './compile.js'
package/subscript.min.js CHANGED
@@ -1 +1 @@
1
- let e,t,r=r=>(e=0,t=r,r=a(),t[e]?l():r||""),l=(r="Bad syntax",l=t[e],s=t.slice(0,e).split("\n"),a=s.pop())=>{throw SyntaxError(`${r} \`${a.slice(-10)}${l}\` at ${s.length}:${a.length}`)},s=(r=1,l=e,s)=>{if("number"==typeof r)e+=r;else for(;s=r(t.charCodeAt(e));)e+=s;return t.slice(l,e)},a=(t=0,r,s,a,o,c)=>{for(;(s=n())&&(o=((c=h[s])&&c(a,t))??(!a&&h[0]()));)a=o;return r&&(s==r?e++:l()),a},n=r=>{for(;(r=t.charCodeAt(e))<=32;)e++;return r},o=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=>s(o)];r.set=(r,l=32,s,a=r.charCodeAt(0),n=r.length,c=h[a],p=r.toUpperCase()!==r)=>h[a]=(a,h,i=e)=>h<l&&(n<2||t.substr(e,n)==r)&&(!p||!o(t.charCodeAt(e+n)))&&(e+=n,s(a,h))||(e=i,c?.(a,h));const c=e=>Array.isArray(e)?p[e[0]](...e.slice(1)):t=>t?.[e];c.set=(e,t,r=p[e])=>p[e]=(...e)=>t(...e)||r&&r(...e);const p={},i=e=>(e=r(e),t=>(e.call?e:e=c(e))(t)),f=i.set=(e,t,l,s=t<0,n=l[0],o=l[1],p=!n&&l.length)=>(n||=p?p>1?(r,l)=>r&&(l=a(t-s))&&[e,r,l]:r=>!r&&(r=a(t-1))&&[e,r]:(r,l)=>r&&(l=a(t))&&(r[0]===e&&r[2]?(r.push(l),r):[e,r,l]),o||=p?p>1?(e,t)=>t&&(e=c(e),t=c(t),e.length||t.length?r=>l(e(r),t(r)):(e=l(e(),t()),()=>e)):(e,t)=>!t&&((e=c(e)).length?t=>l(e(t)):(e=l(e()),()=>e)):(...e)=>(e=e.map(c),t=>l(...e.map((e=>e(t))))),(t=s?-t:t)?r.set(e,t,n):h[e.charCodeAt(0)||1]=n,c.set(e,o)),g=e=>e?l():["",(e=+s((e=>46===e||e>=48&&e<=57||(69===e||101===e?2:0))))!=e?l():e],u=(e,t,r,l)=>[e,t,[r=>r?["++"===e?"-":"+",[e,r],["",1]]:[e,a(t-1)],l=(e,t)=>"("===e[0]?l(e[1]):"."===e[0]?(t=e[2],e=c(e[1]),l=>r(e(l),t)):"["===e[0]?([,e,t]=e,e=c(e),t=c(t),l=>r(e(l),t(l))):t=>r(t,e)]],d=["",,[,e=>()=>e],'"',,[e=>e?l():["",(s()+s((e=>e-34?1:0))+(s()||l("Bad string"))).slice(1,-1)]],".",,[e=>!e&&g()],...Array(10).fill(0).flatMap(((e,t)=>[""+t,0,[g]])),",",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,...u("++",15,((e,t)=>++e[t])),...u("--",15,((e,t)=>--e[t])),"[",18,[e=>e&&["[",e,a(0,93)||l()],(e,t)=>t&&(e=c(e),t=c(t),r=>e(r)[t(r)])],".",18,[(e,t)=>e&&(t=a(18))&&[".",e,t],(e,t)=>(e=c(e),t=t[0]?t:t[1],r=>e(r)[t])],"(",18,[e=>!e&&["(",a(0,41)||l()],c],"(",18,[e=>e&&["(",e,a(0,41)||""],(e,t,r,l)=>null!=t&&(l=""==t?()=>[]:","===t[0]?(t=t.slice(1).map(c),e=>t.map((t=>t(e)))):(t=c(t),e=>[t(e)]),"."===e[0]?(r=e[2],e=c(e[1]),t=>e(t)[r](...l(t))):"["===e[0]?(r=c(e[2]),e=c(e[1]),t=>e(t)[r(t)](...l(t))):(e=c(e),t=>e(t)(...l(t))))]];for(;d[2];)f(...d.splice(0,3));export{c as compile,i as default,r as parse};
1
+ let e,t,r=r=>(e=0,t=r,r=n(),t[e]?l():r||""),l=(r="Bad syntax",l=t[e],a=t.slice(0,e).split("\n"),n=a.pop())=>{throw SyntaxError(`${r} \`${l}\` at ${a.length}:${n.length}`)},a=(r=1,l=e,a)=>{if("number"==typeof r)e+=r;else for(;a=r(t.charCodeAt(e));)e+=a;return t.slice(l,e)},n=(t=0,a,n,s,h,o)=>{for(;(n=r.space())&&(h=((o=p[n])&&o(s,t))??(!s&&r.id()));)s=h;return a&&(n==a?e++:l()),s},s=e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e,h=r.space=r=>{for(;(r=t.charCodeAt(e))<=32;)e++;return r},o=r.id=e=>a(s),p=[],c=(r,l=32,a,n=r.charCodeAt(0),h=r.length,o=p[n],c=r.toUpperCase()!==r)=>p[n]=(n,p,i=e)=>p<l&&(h<2||t.substr(e,h)==r)&&(!c||!s(t.charCodeAt(e+h)))&&(e+=h,a(n,p))||(e=i,o?.(n,p)),i=(e,t,r)=>c(e,t,((l,a)=>l&&(a=n(t-!!r))&&[e,l,a])),g=(e,t,r)=>c(e,t,(l=>r?l&&[e,l]:!l&&(l=n(t-1))&&[e,l])),f=(e,t)=>c(e,t,((r,l)=>r&&(l=n(t))&&(r[0]===e&&r[2]?(r.push(l),r):[e,r,l])));const d=e=>Array.isArray(e)?u[e[0]](...e.slice(1)):t=>t?.[e],u={},A=(e,t,r=u[e])=>u[e]=(...e)=>t(...e)||r&&r(...e),y=e=>(e=r(e),t=>(e.call?e:e=d(e))(t)),C=(e,t,r)=>r[0]||r[1]?(t?c(e,t,r[0]):p[e.charCodeAt(0)||1]=r[0],A(e,r[1])):r.length?r.length>1?(i(e,Math.abs(t),t<0),A(e,((e,t)=>t&&(e=d(e),t=d(t),e.length||t.length?l=>r(e(l),t(l)):(e=r(e(),t()),()=>e))))):(g(e,t),A(e,((e,t)=>!t&&((e=d(e)).length?t=>r(e(t)):(e=r(e()),()=>e))))):(f(e,t),A(e,((...e)=>(e=e.map(d),t=>r(...e.map((e=>e(t)))))))),m=e=>e?l():["",(e=+a((e=>46===e||e>=48&&e<=57||(69===e||101===e?2:0))))!=e?l():e],$=(e,t,r,l)=>[e,t,[r=>r?["++"===e?"-":"+",[e,r],["",1]]:[e,n(t-1)],l=(e,t)=>"("===e[0]?l(e[1]):"."===e[0]?(t=e[2],e=d(e[1]),l=>r(e(l),t)):"["===e[0]?([,e,t]=e,e=d(e),t=d(t),l=>r(e(l),t(l))):t=>r(t,e)]],b=["",,[,e=>()=>e],'"',,[e=>e?l():["",(a()+a((e=>e-34?1:0))+(a()||l("Bad string"))).slice(1,-1)]],".",,[e=>!e&&m()],...Array(10).fill(0).flatMap(((e,t)=>[""+t,0,[m]])),",",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,...$("++",15,((e,t)=>++e[t])),...$("--",15,((e,t)=>--e[t])),"[",18,[e=>e&&["[",e,n(0,93)||l()],(e,t)=>t&&(e=d(e),t=d(t),r=>e(r)[t(r)])],".",18,[(e,t)=>e&&(t=n(18))&&[".",e,t],(e,t)=>(e=d(e),t=t[0]?t:t[1],r=>e(r)[t])],"(",18,[e=>!e&&["(",n(0,41)||l()],d],"(",18,[e=>e&&["(",e,n(0,41)||""],(e,t,r,l)=>null!=t&&(l=""==t?()=>[]:","===t[0]?(t=t.slice(1).map(d),e=>t.map((t=>t(e)))):(t=d(t),e=>[t(e)]),"."===e[0]?(r=e[2],e=d(e[1]),t=>e(t)[r](...l(t))):"["===e[0]?(r=d(e[2]),e=d(e[1]),t=>e(t)[r(t)](...l(t))):(e=d(e),t=>e(t)(...l(t))))]];for(;b[2];)C(...b.splice(0,3));export{i as binary,d as compile,t as cur,y as default,l as err,n as expr,o as id,e as idx,s as isId,p as lookup,f as nary,A as operator,u as operators,r as parse,C as set,a as skip,h as space,c as token,g as unary};