subscript 5.2.0 → 5.3.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
@@ -1,4 +1,7 @@
1
- # <img alt="subscript" src="/subscript2.svg" height=42/> <!--sub͘<em>script</em>--> <!--<sub>SUB͘<em>SCRIPT</em></sub>-->
1
+ # <img alt="subscript" src="/subscript2.svg" height=42/> <!--sub͘<em>script</em>--> <!--<sub>SUB͘<em>SCRIPT</em></sub>-->
2
+ <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>
3
+ <a href="http://npmjs.org/subscript"><img src="https://img.shields.io/npm/v/subscript?color=indianred"/></a>
4
+ <a href="http://microjs.com/#subscript"><img src="https://img.shields.io/badge/microjs-subscript-blue?color=darkslateblue"/></a>
2
5
 
3
6
  _Subscript_ is micro-language with common syntax subset of C++, JS, Java, Python, Go, Rust, Swift, Objective C, Kotlin etc.<br/>
4
7
 
@@ -21,13 +24,13 @@ _Subscript_ is designed to be useful for:
21
24
 
22
25
  * templates (perfect match with [template parts](https://github.com/github/template-parts))
23
26
  * expressions evaluators, calculators
24
- * subsets of languages (eg. [justin](#justin)) <!-- see sonr, mineural -->
27
+ * configurable subsets of languages (eg. [justin](#justin)) <!-- see sonr, mineural -->
25
28
  * mocking language features (eg. pipe operator)
26
29
  * sandboxes, playgrounds, safe eval
27
30
  * custom DSL
28
31
 
29
32
  [_Jsep_](https://github.com/EricSmekens/jsep) is generally fine for the listed tasks, unless you need dependencies as small as possible.
30
- _Subscript_ has [2.5kb](https://npmfs.com/package/subscript/5.0.0/subscript.min.js) footprint vs [11.4kb](https://npmfs.com/package/jsep/1.2.0/dist/jsep.min.js) _jsep_, with better performance.
33
+ _Subscript_ has [2.5kb](https://npmfs.com/package/subscript/5.2.0/subscript.min.js) footprint vs [11.4kb](https://npmfs.com/package/jsep/1.2.0/dist/jsep.min.js) _jsep_, with _jsep+_ test coverage and better performance.
31
34
 
32
35
 
33
36
  ## Evaluation
@@ -49,6 +52,8 @@ evaluate(['+', ['*', 'min', 60], '"sec"'], { min: 5 }) // min*60 + "sec" == "300
49
52
 
50
53
  ## Extending
51
54
 
55
+ ### Operators
56
+
52
57
  Default operators include common operators for the listed languages in the following precedence:
53
58
 
54
59
  * `++ --` unary postfix
@@ -83,13 +88,13 @@ let tree = parse(`
83
88
  evaluate(tree, { Math, map, take, interval, gaussian })
84
89
  ```
85
90
 
86
- ---
91
+ ### Tokens
87
92
 
88
93
  Default tokens include:
89
94
 
90
95
  * `"abc"` strings
91
96
  * `1.2e+3` floats
92
- * identifiers
97
+ * `name` identifiers
93
98
 
94
99
  Tokens are extensible via `parse.token` list, can be added support of _literals_, _regexes_, _strings_, _numbers_ and others.
95
100
 
@@ -102,7 +107,7 @@ parse.token.unshift(c => char(4) === 'this' ? ctx : null)
102
107
  evaluate(parse(`this.x`)) // 1
103
108
  ```
104
109
 
105
- ---
110
+ ### Spaces/comments
106
111
 
107
112
  Comments can be added via extending `parse.space`. See [justin.js](./justin.js) for more examples.
108
113
 
@@ -280,7 +285,7 @@ Subscript shows relatively good performance within other evaluators:
280
285
  // 1 + (a * b / c % d) - 2.0 + -3e-3 * +4.4e4 / f.g[0] - i.j(+k == 1)(0)
281
286
  // parse 30k times
282
287
 
283
- subscript: ~220 ms
288
+ subscript: ~230 ms
284
289
  jsep: ~280 ms
285
290
  expr-eval: ~480 ms
286
291
  jexl: ~1200 ms
package/justin.js CHANGED
@@ -1,17 +1,16 @@
1
1
  // justin lang https://github.com/endojs/Jessie/issues/66
2
2
  import {evaluate} from './evaluate.js'
3
- import {parse, code, char, skip, expr, err} from './parse.js'
3
+ import {parse, code, char, skip, expr, err, val} from './parse.js'
4
4
 
5
5
  const PERIOD=46, OPAREN=40, CPAREN=41, CBRACK=93, SPACE=32,
6
6
 
7
- PREC_SEQ=1, 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_UNARY=15, PREC_POSTFIX=16, PREC_CALL=18, PREC_GROUP=19
7
+ PREC_SEQ=1, PREC_TERN=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_UNARY=15, PREC_POSTFIX=16, PREC_CALL=18, PREC_GROUP=19,
9
+ PREC_EXP=14, PREC_TOKEN=20
9
10
 
10
11
 
11
12
  // tokens
12
- const v = v => ({valueOf:()=>v})
13
13
  parse.token.push(
14
- // TODO: better parser
15
14
  // 1.2e+3, .5 - fast & small version, but consumes corrupted nums as well
16
15
  (number) => (
17
16
  (number = skip(c => (c > 47 && c < 58) || c == PERIOD)) && (
@@ -31,19 +30,6 @@ parse.token.push(
31
30
  return skip(), qc + str + qc
32
31
  },
33
32
 
34
- // {}
35
- (cc, node) => (
36
- cc === 123 && (skip(), node = mapObj(['{', expr(0,125)]), skip(), node)
37
- ),
38
-
39
- // literal
40
- c =>
41
- c === 116 && char(4) === 'true' && skip(4) ? v(true) :
42
- c === 102 && char(5) === 'false' && skip(5) ? v(false) :
43
- c === 110 && char(4) === 'null' && skip(4) ? v(null) :
44
- c === 117 && char(9) === 'undefined' && skip(9) ? v(undefined) :
45
- null,
46
-
47
33
  // id
48
34
  c => skip(c =>
49
35
  (c >= 48 && c <= 57) || // 0..9
@@ -56,24 +42,17 @@ parse.token.push(
56
42
 
57
43
  const escape = {n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', v:'\v'}
58
44
 
59
- // {}
60
- const mapObj = (n, args) => (
61
- args = !n[1] ? [] :
62
- (n[1][0]==':') ? [n[1]] :
63
- (n[1][0]==',') ? n[1].slice(1) : args,
64
- ['{', ...args]
65
- )
66
-
67
45
 
68
46
  // /**/, //
69
47
  parse.space = cc => {
70
- while (cc = code(), cc <= 32) {
71
- skip()
72
- if (code() === 47)
48
+ while (cc = code(), cc <= 32 || cc === 47) {
49
+ if (cc <= 32) skip()
50
+ else if (cc === 47)
73
51
  // /**/
74
52
  if (code(1) === 42) skip(2), skip(c => c !== 42 && code(1) !== 47), skip(2)
75
53
  // //
76
54
  else if (code(1) === 47) skip(2), skip(c => c >= 32)
55
+ else break
77
56
  }
78
57
  return cc
79
58
  }
@@ -86,7 +65,7 @@ const addOps = (add, stride=2, list) => {
86
65
 
87
66
  addOps(parse.operator, 3, [
88
67
  // subscript ones
89
- // TODO: add ,, as node here
68
+ // TODO: add ,, as node here?
90
69
  ',', PREC_SEQ,,
91
70
 
92
71
  '|', PREC_OR,,
@@ -132,24 +111,24 @@ addOps(parse.operator, 3, [
132
111
  '.', PREC_CALL, (node,b) => node && [skip(),node, typeof (b = expr(PREC_CALL)) === 'string' ? '"' + b + '"' : b.valueOf()],
133
112
 
134
113
  // a[b]
135
- '[', PREC_CALL, (node) => (skip(), node = ['.', node, expr(0,CBRACK).valueOf()], skip(), node),
114
+ '[', PREC_CALL, (node) => (skip(), node = ['.', node, val(expr(0,CBRACK))], node),
136
115
  ']',,,
137
116
 
138
117
  // a(b)
139
- '(', PREC_CALL, (node,b) => ( skip(), b=expr(0,CPAREN), skip(),
140
- Array.isArray(b) && b[0]===',' ? (b[0]=node, b) : b ? [node, b.valueOf()] : [node]
118
+ '(', PREC_CALL, (node,b) => ( skip(), b=expr(0,CPAREN),
119
+ Array.isArray(b) && b[0]===',' ? (b[0]=node, b) : b ? [node, val(b)] : [node]
141
120
  ),
142
121
  // (a+b)
143
- '(', PREC_GROUP, (node,b) => !node && (skip(), b=expr(0,CPAREN) || err(), skip(), b),
122
+ '(', PREC_GROUP, (node,b) => !node && (skip(), b=expr(0,CPAREN) || err(), b),
144
123
  ')',,,
145
124
 
146
125
  // justin extension
147
- ';', 1,,
148
- '===', 9,,
149
- '!==', 9,,
150
- '**', 14,,
151
- '~', 13, -1,
152
- '?', 3, (node) => {
126
+ ';', PREC_SEQ,,
127
+ '===', PREC_EQ,,
128
+ '!==', PREC_EQ,,
129
+ '**', PREC_EXP,,
130
+ '~', PREC_UNARY, -1,
131
+ '?', PREC_TERN, (node) => {
153
132
  if (!node) err('Expected expression')
154
133
  let a, b
155
134
  skip(), parse.space(), a = expr()
@@ -158,13 +137,28 @@ addOps(parse.operator, 3, [
158
137
  return ['?:', node, a, b]
159
138
  },
160
139
  '}',,,
161
- ':', 0,,
162
- 'in', 10, (node) => code(2) <= 32 && [skip(2), '"'+node+'"', expr(10)],
163
- '[', 20, (node,arg) => !node && (
164
- skip(), arg=expr(0,93), skip(),
165
- !arg ? ['['] : arg[0] === ',' ? (arg[0]='[',arg) : ['[',arg]
166
- )
140
+ ':',,,
141
+ 'in', PREC_COMP, (node) => code(2) <= 32 && [skip(2), '"'+node+'"', expr(PREC_COMP)],
142
+
143
+ // as operator it's faster to lookup (no need to extra rule check), smaller and no conflict with word names
144
+ // [a,b,c]
145
+ '[', PREC_TOKEN, (node,arg) => !node && (
146
+ skip(), arg=expr(0,93),
147
+ !arg ? ['['] : arg[0] == ',' ? (arg[0]='[',arg) : ['[',arg]
148
+ ),
149
+
150
+ // {a:0, b:1}
151
+ '{', PREC_TOKEN, (node,arg) => !node && (skip(), arg=expr(0,125),
152
+ !arg ? ['{'] : arg[0] == ':' ? ['{',arg] : arg[0] == ',' ? (arg[0]='{',arg) : ['{',arg])
153
+ ,
154
+
155
+ // literals
156
+ 'null', PREC_TOKEN, node=>!node&&(skip(4),v(null)),
157
+ 'false', PREC_TOKEN, node=>!node&&(skip(5),v(false)),
158
+ 'true', PREC_TOKEN, node=>!node&&(skip(4),v(true)),
159
+ 'undefined', PREC_TOKEN, node=>!node&&(skip(9),v(undefined)),
167
160
  ])
161
+ const v = v => ({valueOf:()=>v})
168
162
 
169
163
  addOps(evaluate.operator, 2, [
170
164
  // subscript
@@ -206,9 +200,7 @@ addOps(evaluate.operator, 2, [
206
200
  '?:', (a,b,c)=>a?b:c,
207
201
  'in', (a,b)=>a in b,
208
202
 
209
- // []
210
203
  '[', (...args) => Array(...args),
211
- // as operator it's faster to lookup (no need to call extra rule check), smaller and no conflict with word names
212
204
  '{', (...args)=>Object.fromEntries(args),
213
205
  ':', (a,b)=>[a,b]
214
206
  ])
package/justin.min.js CHANGED
@@ -1 +1 @@
1
- var e,r,t=e=>Array.isArray(e)&&("string"==typeof e[0]||t(e[0])),a=(e,r={},o,l)=>t(e)?("string"==typeof(o=e[0])&&(l=n[o]),"function"!=typeof(o=l||a(o,r))?o:o.call(...e.map((e=>a(e,r))))):e&&"string"==typeof e?'"'===e[0]?e.slice(1,-1):"@"===e[0]?e.slice(1):e in r?r[e]:e:e,n={},o=(a.operator=(e,r)=>n[e]=2==r.length?(...e)=>e.reduce(r):r,(t,a)=>(r=t,e=0,a=p(),e<r.length?l():a.valueOf())),l=(t="Bad syntax")=>{throw Error(t+" `"+r[e]+"` at "+e)},s=(t=1,a=e)=>{if("number"==typeof t)e+=t;else for(;t(u());)e++;return r.slice(a,e)},u=(t=0)=>r.charCodeAt(e+t),f=(t=1)=>r.substr(e,t),p=(e=0,r,t,a,n=0,s,u)=>{for(;(t=o.space())&&(u=v[t]?.(a,e)||!a&&c(t));)a=u;return r&&t!==r&&l("Unclosed paren"),a},i=(o.space=r=>{for(;(r=u())<=32;)e++;return r},o.token=[]),c=(e,r=0,t)=>{for(;r<i.length;)if(t=i[r++](e))return t},v=[],d=(o.operator=(r,t=0,a=0,n,i=r.charCodeAt(0),c=r.length,d=v[i],h=r.toUpperCase()!==r,y)=>(y=c<2?h?e=>u(1)<=32:e=>1:h?e=>f(c)==r&&u(c)<=32:e=>f(c)==r,n=a?a>0?e=>e&&[s(c),e]:a<0?e=>!e&&[s(c),(p(t-1)||l()).valueOf()]:a:a=>{a=[r,a||l()];do{e+=c,a.push((p(t)||l()).valueOf())}while(o.space()==i&&y());return a},v[i]=(e,r)=>r<t&&y()&&n(e)||d&&d(e,r)),15),h=e=>({valueOf:()=>e});o.token.push((e=>(e=s((e=>e>47&&e<58||46==e)))&&((69==u()||101==u())&&(e+=s(2)+s((e=>e>=48&&e<=57))),isNaN(e=new Number(e))?l("Bad number"):e)),((e,r,t,a)=>{if(34===e||39===e){for(r=f(),s(),a="";(t=u())-e;)92===t?(s(),a+=y[f()]||f()):a+=f(),s();return s(),r+a+r}}),((e,r)=>123===e&&(s(),r=g(["{",p(0,125)]),s(),r)),(e=>116===e&&"true"===f(4)&&s(4)?h(!0):102===e&&"false"===f(5)&&s(5)?h(!1):110===e&&"null"===f(4)&&s(4)?h(null):117===e&&"undefined"===f(9)&&s(9)?h(void 0):null),(e=>s((e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e))));var y={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},g=(e,r)=>(r=e[1]?":"==e[1][0]?[e[1]]:","==e[1][0]?e[1].slice(1):r:[],["{",...r]);o.space=e=>{for(;(e=u())<=32;)s(),47===u()&&(42===u(1)?(s(2),s((e=>42!==e&&47!==u(1))),s(2)):47===u(1)&&(s(2),s((e=>e>=32))));return e};var O=(e,r=2,t)=>{for(let a=0;a<t.length;a+=r)e(t[a],t[a+1],t[a+2])};O(o.operator,3,[",",1,,"|",6,,"||",4,,"&",8,,"&&",5,,"^",7,,"==",9,,"!=",9,,">",10,,">=",10,,">>",11,,">>>",11,,"<",10,,"<=",10,,"<<",11,,"+",12,,"+",d,-1,"++",d,-1,"++",d,1,"-",12,,"-",d,-1,"--",d,-1,"--",d,1,"!",d,-1,"*",13,,"/",13,,"%",13,,".",18,(e,r)=>e&&[s(),e,"string"==typeof(r=p(18))?'"'+r+'"':r.valueOf()],"[",18,e=>(s(),e=[".",e,p(0,93).valueOf()],s(),e),"]",,,"(",18,(e,r)=>(s(),r=p(0,41),s(),Array.isArray(r)&&","===r[0]?(r[0]=e,r):r?[e,r.valueOf()]:[e]),"(",19,(e,r)=>!e&&(s(),r=p(0,41)||l(),s(),r),")",,,";",1,,"===",9,,"!==",9,,"**",14,,"~",13,-1,"?",3,e=>{let r,t;return e||l("Expected expression"),s(),o.space(),r=p(),58!==u()&&l("Expected :"),s(),o.space(),t=p(),["?:",e,r,t]},"}",,,":",0,,"in",10,e=>u(2)<=32&&[s(2),'"'+e+'"',p(10)],"[",20,(e,r)=>!e&&(s(),r=p(0,93),s(),r?","===r[0]?(r[0]="[",r):["[",r]:["["])]),O(a.operator,2,["!",e=>!e,"++",e=>++e,"--",e=>--e,".",(e,r)=>e&&e[r],"%",(e,r)=>e%r,"/",(e,r)=>e/r,"*",(e,r)=>e*r,"+",(e,r)=>e+r,"-",(...e)=>e.length<2?-e:e.reduce(((e,r)=>e-r)),">>>",(e,r)=>e>>>r,">>",(e,r)=>e>>r,"<<",(e,r)=>e<<r,">=",(e,r)=>e>=r,">",(e,r)=>e>r,"<=",(e,r)=>e<=r,"<",(e,r)=>e<r,"!=",(e,r)=>e!=r,"==",(e,r)=>e==r,"&",(e,r)=>e&r,"^",(e,r)=>e^r,"|",(e,r)=>e|r,"&&",(...e)=>e.every(Boolean),"||",(...e)=>e.some(Boolean),",",(e,r)=>r,"**",(...e)=>e.reduceRight(((e,r)=>Math.pow(r,e))),"~",e=>~e,"?:",(e,r,t)=>e?r:t,"in",(e,r)=>e in r,"[",(...e)=>Array(...e),"{",(...e)=>Object.fromEntries(e),":",(e,r)=>[e,r]]);var b=e=>(e="string"==typeof e?o(e):e,r=>a(e,r));export{b as default,a as evaluate,o as parse};
1
+ var e,r,t=e=>Array.isArray(e)&&("string"==typeof e[0]||t(e[0])),a=(e,r={},o,s)=>t(e)?("string"==typeof(o=e[0])&&(s=n[o]),"function"!=typeof(o=s||a(o,r))?o:o.call(...e.map((e=>a(e,r))))):e&&"string"==typeof e?'"'===e[0]?e.slice(1,-1):"@"===e[0]?e.slice(1):e in r?r[e]:e:e,n={},o=(a.operator=(e,r)=>n[e]=2==r.length?(...e)=>e.reduce(r):r,(t,a)=>(r=t,e=0,a=p(),e<r.length?s():d(a))),s=(t="Bad syntax")=>{throw Error(t+" `"+r[e]+"` at "+e)},f=(t=1,a=e)=>{if("number"==typeof t)e+=t;else for(;t(i());)e++;return r.slice(a,e)},i=(t=0)=>r.charCodeAt(e+t),l=(t=1)=>r.substr(e,t),p=(e=0,r,t,a,n=0,i,l)=>{for(;(t=o.space())&&(l=y[t]?.(a,e)||!a&&c(t));)a=l;return r&&(t!=r?s("Unclosed paren"):f()),a},u=(o.space=r=>{for(;(r=i())<=32;)e++;return r},o.token=[]),c=(e,r=0,t)=>{for(;r<u.length;)if(t=u[r++](e))return t},y=[],d=(o.operator=(r,t=0,a=0,n,u=r.charCodeAt(0),c=r.length,h=y[u],v=a<=0&&r.toUpperCase()!==r)=>(n=a?a>0?e=>e&&[f(c),d(e)]:a<0?e=>!e&&[f(c),d(p(t-1))]:a:a=>{a=[r,a||s()];do{e+=c,a.push(d(p(t)))}while(o.space()==u&&(c<2||l(c)==r)&&(!v||i(c)<=32));return a},y[u]=(e,a)=>a<t&&(c<2||l(c)==r)&&(!v||i(c)<=32)&&n(e)||h&&h(e,a)),e=>Array.isArray(e)?e:(e||s()).valueOf()),h=10,v=15,g=20;o.token.push((e=>(e=f((e=>e>47&&e<58||46==e)))&&((69==i()||101==i())&&(e+=f(2)+f((e=>e>=48&&e<=57))),isNaN(e=new Number(e))?s("Bad number"):e)),((e,r,t,a)=>{if(34===e||39===e){for(r=l(),f(),a="";(t=i())-e;)92===t?(f(),a+=A[l()]||l()):a+=l(),f();return f(),r+a+r}}),(e=>f((e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e))));var A={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"};o.space=e=>{for(;(e=i())<=32||47===e;)if(e<=32)f();else if(47===e)if(42===i(1))f(2),f((e=>42!==e&&47!==i(1))),f(2);else{if(47!==i(1))break;f(2),f((e=>e>=32))}return e};var b=(e,r=2,t)=>{for(let a=0;a<t.length;a+=r)e(t[a],t[a+1],t[a+2])};b(o.operator,3,[",",1,,"|",6,,"||",4,,"&",8,,"&&",5,,"^",7,,"==",9,,"!=",9,,">",h,,">=",h,,">>",11,,">>>",11,,"<",h,,"<=",h,,"<<",11,,"+",12,,"+",v,-1,"++",v,-1,"++",v,1,"-",12,,"-",v,-1,"--",v,-1,"--",v,1,"!",v,-1,"*",13,,"/",13,,"%",13,,".",18,(e,r)=>e&&[f(),e,"string"==typeof(r=p(18))?'"'+r+'"':r.valueOf()],"[",18,e=>(f(),[".",e,d(p(0,93))]),"]",,,"(",18,(e,r)=>(f(),r=p(0,41),Array.isArray(r)&&","===r[0]?(r[0]=e,r):r?[e,d(r)]:[e]),"(",19,(e,r)=>!e&&(f(),p(0,41)||s()),")",,,";",1,,"===",9,,"!==",9,,"**",14,,"~",v,-1,"?",3,e=>{let r,t;return e||s("Expected expression"),f(),o.space(),r=p(),58!==i()&&s("Expected :"),f(),o.space(),t=p(),["?:",e,r,t]},"}",,,":",,,"in",h,e=>i(2)<=32&&[f(2),'"'+e+'"',p(h)],"[",g,(e,r)=>!e&&(f(),(r=p(0,93))?","==r[0]?(r[0]="[",r):["[",r]:["["]),"{",g,(e,r)=>!e&&(f(),(r=p(0,125))?":"==r[0]?["{",r]:","==r[0]?(r[0]="{",r):["{",r]:["{"]),"null",g,e=>!e&&(f(4),m(null)),"false",g,e=>!e&&(f(5),m(!1)),"true",g,e=>!e&&(f(4),m(!0)),"undefined",g,e=>!e&&(f(9),m(void 0))]);var m=e=>({valueOf:()=>e});b(a.operator,2,["!",e=>!e,"++",e=>++e,"--",e=>--e,".",(e,r)=>e&&e[r],"%",(e,r)=>e%r,"/",(e,r)=>e/r,"*",(e,r)=>e*r,"+",(e,r)=>e+r,"-",(...e)=>e.length<2?-e:e.reduce(((e,r)=>e-r)),">>>",(e,r)=>e>>>r,">>",(e,r)=>e>>r,"<<",(e,r)=>e<<r,">=",(e,r)=>e>=r,">",(e,r)=>e>r,"<=",(e,r)=>e<=r,"<",(e,r)=>e<r,"!=",(e,r)=>e!=r,"==",(e,r)=>e==r,"&",(e,r)=>e&r,"^",(e,r)=>e^r,"|",(e,r)=>e|r,"&&",(...e)=>e.every(Boolean),"||",(...e)=>e.some(Boolean),",",(e,r)=>r,"**",(...e)=>e.reduceRight(((e,r)=>Math.pow(r,e))),"~",e=>~e,"?:",(e,r,t)=>e?r:t,"in",(e,r)=>e in r,"[",(...e)=>Array(...e),"{",(...e)=>Object.fromEntries(e),":",(e,r)=>[e,r]]);var x=e=>(e="string"==typeof e?o(e):e,r=>a(e,r));export{x as default,a as evaluate,o as parse};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "subscript",
3
- "version": "5.2.0",
3
+ "version": "5.3.0",
4
4
  "description": "Microlanguage with common syntax for JS/C++/Python/Rust",
5
5
  "main": "subscript.js",
6
6
  "type": "module",
package/parse.js CHANGED
@@ -3,7 +3,7 @@ const SPACE=32
3
3
  // current string & index
4
4
  let idx, cur
5
5
 
6
- export const parse = (str, tree) => (cur=str, idx=0, tree=expr(), idx<cur.length ? err() : tree.valueOf()),
6
+ export const parse = (str, tree) => (cur=str, idx=0, tree=expr(), idx<cur.length ? err() : val(tree)),
7
7
 
8
8
  err = (msg='Bad syntax') => { throw Error(msg + ' `' + cur[idx] + '` at ' + idx) },
9
9
 
@@ -24,7 +24,8 @@ expr = (prec=0, end, cc, node, i=0, map, newNode) => {
24
24
  (cc=parse.space()) && (newNode = lookup[cc]?.(node, prec) || (!node && token(cc)) )
25
25
  ) node = newNode;
26
26
 
27
- if (end && cc !== end) err('Unclosed paren')
27
+ // skip end character, if expected
28
+ if (end) cc != end ? err('Unclosed paren') : skip()
28
29
 
29
30
  return node
30
31
  },
@@ -43,22 +44,27 @@ lookup = [],
43
44
  // @param op is operator string
44
45
  // @param prec is operator precedenc to check
45
46
  // @param map is either number +1 - postfix unary, -1 prefix unary, 0 binary, else - custom mapper function
46
- operator = parse.operator = (op, prec=0, type=0, map, c=op.charCodeAt(0), l=op.length, prev=lookup[c], word=op.toUpperCase()!==op, isop) => (
47
- isop = l<2 ? // word operator must have space after
48
- !word ? c=>1 : c=>code(1)<=SPACE :
49
- !word ? c=>char(l)==op : c=>char(l)==op&&code(l)<=SPACE,
50
-
47
+ operator = parse.operator = (
48
+ op, prec=0, type=0, map, c=op.charCodeAt(0), l=op.length,
49
+ prev=lookup[c],
50
+ spaced=type<=0&&op.toUpperCase()!==op // non-postfix word operator must have space after
51
+ ) => (
51
52
  map = !type ? node => { // binary, consume same-op group
52
53
  node = [op, node || err()]
53
- // in order to support literal tokens, we call valueOf any time we create or modify calltree node
54
- do { idx+=l, node.push((expr(prec) || err()).valueOf()) } while (parse.space()==c && isop())
54
+ do { idx+=l, node.push(val(expr(prec))) }
55
+ while (parse.space()==c && (l<2||char(l)==op) && (!spaced||code(l)<=SPACE))
55
56
  return node
56
57
  } :
57
- type > 0 ? node => node && [skip(l), node] : // postfix unary
58
- type < 0 ? node => !node && [skip(l), (expr(prec-1) || err()).valueOf()] : // prefix unary
58
+ type > 0 ? node => node && [skip(l), val(node)] : // postfix unary
59
+ type < 0 ? node => !node && [skip(l), val(expr(prec-1))] : // prefix unary
59
60
  type,
60
61
 
61
- lookup[c] = (node, curPrec) => curPrec < prec && isop() && map(node) || (prev && prev(node, curPrec))
62
- )
62
+ lookup[c] = (node, curPrec) =>
63
+ curPrec < prec && (l<2||char(l)==op) && (!spaced||code(l)<=SPACE) &&
64
+ map(node) || (prev && prev(node, curPrec))
65
+ ),
66
+
67
+ // in order to support literal tokens, we call valueOf any time we create or modify calltree node
68
+ val = node => Array.isArray(node) ? node : (node || err()).valueOf()
63
69
 
64
70
  export default parse
package/subscript.js CHANGED
@@ -1,4 +1,4 @@
1
- import parse, {skip, expr, code, tokens, operator as parseOp} from './parse.js'
1
+ import parse, {skip, expr, code, tokens, val, operator as parseOp} from './parse.js'
2
2
  import evaluate, {operator as evalOp} from './evaluate.js'
3
3
 
4
4
  const PERIOD=46, OPAREN=40, CPAREN=41, CBRACK=93, SPACE=32,
@@ -58,11 +58,11 @@ addOps(parseOp, 3, [
58
58
  '+', PREC_SUM,,
59
59
  '+', PREC_UNARY, -1,
60
60
  '++', PREC_UNARY, -1,
61
- '++', PREC_UNARY, +1,
61
+ '++', PREC_POSTFIX, +1,
62
62
  '-', PREC_SUM,,
63
63
  '-', PREC_UNARY, -1,
64
64
  '--', PREC_UNARY, -1,
65
- '--', PREC_UNARY, +1,
65
+ '--', PREC_POSTFIX, +1,
66
66
 
67
67
  // ! ~
68
68
  '!', PREC_UNARY, -1,
@@ -76,15 +76,15 @@ addOps(parseOp, 3, [
76
76
  '.', PREC_CALL, (node,b) => node && [skip(),node, typeof (b = expr(PREC_CALL)) === 'string' ? '"' + b + '"' : b.valueOf()],
77
77
 
78
78
  // a[b]
79
- '[', PREC_CALL, (node) => (skip(), node = ['.', node, expr(0,CBRACK).valueOf()], skip(), node),
79
+ '[', PREC_CALL, (node) => (skip(), node = ['.', node, val(expr(0,CBRACK))], node),
80
80
  ']',,,
81
81
 
82
82
  // a(b)
83
- '(', PREC_CALL, (node,b) => ( skip(), b=expr(0,CPAREN), skip(),
84
- Array.isArray(b) && b[0]===',' ? (b[0]=node, b) : b ? [node, b.valueOf()] : [node]
83
+ '(', PREC_CALL, (node,b) => ( skip(), b=expr(0,CPAREN),
84
+ Array.isArray(b) && b[0]===',' ? (b[0]=node, b) : b ? [node, val(b)] : [node]
85
85
  ),
86
86
  // (a+b)
87
- '(', PREC_GROUP, (node,b) => !node && (skip(), b=expr(0,CPAREN) || err(), skip(), b),
87
+ '(', PREC_GROUP, (node,b) => !node && (skip(), b=expr(0,CPAREN) || err(), b),
88
88
  ')',,,
89
89
  ])
90
90
 
package/subscript.min.js CHANGED
@@ -1 +1 @@
1
- var e,r,a=(a,o)=>(r=a,e=0,o=l(),e<r.length?t():o.valueOf()),t=(a="Bad syntax")=>{throw Error(a+" `"+r[e]+"` at "+e)},o=(a=1,t=e)=>{if("number"==typeof a)e+=a;else for(;a(n());)e++;return r.slice(t,e)},n=(a=0)=>r.charCodeAt(e+a),s=(a=1)=>r.substr(e,a),l=(e=0,r,o,n,s=0,l,f)=>{for(;(o=a.space())&&(f=p[o]?.(n,e)||!n&&u(o));)n=f;return r&&o!==r&&t("Unclosed paren"),n},f=(a.space=r=>{for(;(r=n())<=32;)e++;return r},a.token=[]),u=(e,r=0,a)=>{for(;r<f.length;)if(a=f[r++](e))return a},p=[],i=a.operator=(r,f=0,u=0,i,c=r.charCodeAt(0),y=r.length,h=p[c],g=r.toUpperCase()!==r,v)=>(v=y<2?g?e=>n(1)<=32:e=>1:g?e=>s(y)==r&&n(y)<=32:e=>s(y)==r,i=u?u>0?e=>e&&[o(y),e]:u<0?e=>!e&&[o(y),(l(f-1)||t()).valueOf()]:u:o=>{o=[r,o||t()];do{e+=y,o.push((l(f)||t()).valueOf())}while(a.space()==c&&v());return o},p[c]=(e,r)=>r<f&&v()&&i(e)||h&&h(e,r)),c=a,y=e=>Array.isArray(e)&&("string"==typeof e[0]||y(e[0])),h=(e,r={},a,t)=>y(e)?("string"==typeof(a=e[0])&&(t=g[a]),"function"!=typeof(a=t||h(a,r))?a:a.call(...e.map((e=>h(e,r))))):e&&"string"==typeof e?'"'===e[0]?e.slice(1,-1):"@"===e[0]?e.slice(1):e in r?r[e]:e:e,g={},v=h.operator=(e,r)=>g[e]=2==r.length?(...e)=>e.reduce(r):r,d=h,A=15;f.push((e=>(e=o((e=>e>47&&e<58||46==e)))&&((69==n()||101==n())&&(e+=o(2)+o((e=>e>=48&&e<=57))),isNaN(e=new Number(e))?err("Bad number"):e)),((e,r)=>34==e&&o()+o((r=>r-e))+o()),(e=>o((e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192))));var O=(e,r=2,a)=>{for(let t=0;t<a.length;t+=r)e(a[t],a[t+1],a[t+2])};O(i,3,[",",1,,"|",6,,"||",4,,"&",8,,"&&",5,,"^",7,,"==",9,,"!=",9,,">",10,,">=",10,,">>",11,,">>>",11,,"<",10,,"<=",10,,"<<",11,,"+",12,,"+",A,-1,"++",A,-1,"++",A,1,"-",12,,"-",A,-1,"--",A,-1,"--",A,1,"!",A,-1,"*",13,,"/",13,,"%",13,,".",18,(e,r)=>e&&[o(),e,"string"==typeof(r=l(18))?'"'+r+'"':r.valueOf()],"[",18,e=>(o(),e=[".",e,l(0,93).valueOf()],o(),e),"]",,,"(",18,(e,r)=>(o(),r=l(0,41),o(),Array.isArray(r)&&","===r[0]?(r[0]=e,r):r?[e,r.valueOf()]:[e]),"(",19,(e,r)=>!e&&(o(),r=l(0,41)||err(),o(),r),")",,,]),O(v,2,["!",e=>!e,"++",e=>++e,"--",e=>--e,".",(e,r)=>e&&e[r],"%",(e,r)=>e%r,"/",(e,r)=>e/r,"*",(e,r)=>e*r,"+",(e,r)=>e+r,"-",(...e)=>e.length<2?-e:e.reduce(((e,r)=>e-r)),">>>",(e,r)=>e>>>r,">>",(e,r)=>e>>r,"<<",(e,r)=>e<<r,">=",(e,r)=>e>=r,">",(e,r)=>e>r,"<=",(e,r)=>e<=r,"<",(e,r)=>e<r,"!=",(e,r)=>e!=r,"==",(e,r)=>e==r,"&",(e,r)=>e&r,"^",(e,r)=>e^r,"|",(e,r)=>e|r,"&&",(...e)=>e.every(Boolean),"||",(...e)=>e.some(Boolean),",",(e,r)=>r]);var m=e=>(e="string"==typeof e?c(e):e,r=>d(e,r));export{m as default,d as evaluate,c as parse};
1
+ var r,e,t=(t,o)=>(e=t,r=0,o=l(),r<e.length?a():y(o)),a=(t="Bad syntax")=>{throw Error(t+" `"+e[r]+"` at "+r)},o=(t=1,a=r)=>{if("number"==typeof t)r+=t;else for(;t(n());)r++;return e.slice(a,r)},n=(t=0)=>e.charCodeAt(r+t),s=(t=1)=>e.substr(r,t),l=(r=0,e,n,s,l=0,p,i)=>{for(;(n=t.space())&&(i=u[n]?.(s,r)||!s&&f(n));)s=i;return e&&(n!=e?a("Unclosed paren"):o()),s},p=(t.space=e=>{for(;(e=n())<=32;)r++;return e},t.token=[]),f=(r,e=0,t)=>{for(;e<p.length;)if(t=p[e++](r))return t},u=[],i=t.operator=(e,p=0,f=0,i,c=e.charCodeAt(0),h=e.length,g=u[c],d=f<=0&&e.toUpperCase()!==e)=>(i=f?f>0?r=>r&&[o(h),y(r)]:f<0?r=>!r&&[o(h),y(l(p-1))]:f:o=>{o=[e,o||a()];do{r+=h,o.push(y(l(p)))}while(t.space()==c&&(h<2||s(h)==e)&&(!d||n(h)<=32));return o},u[c]=(r,t)=>t<p&&(h<2||s(h)==e)&&(!d||n(h)<=32)&&i(r)||g&&g(r,t)),y=r=>Array.isArray(r)?r:(r||a()).valueOf(),c=t,h=r=>Array.isArray(r)&&("string"==typeof r[0]||h(r[0])),g=(r,e={},t,a)=>h(r)?("string"==typeof(t=r[0])&&(a=d[t]),"function"!=typeof(t=a||g(t,e))?t:t.call(...r.map((r=>g(r,e))))):r&&"string"==typeof r?'"'===r[0]?r.slice(1,-1):"@"===r[0]?r.slice(1):r in e?e[r]:r:r,d={},A=g.operator=(r,e)=>d[r]=2==e.length?(...r)=>r.reduce(e):e,v=g;p.push((r=>(r=o((r=>r>47&&r<58||46==r)))&&((69==n()||101==n())&&(r+=o(2)+o((r=>r>=48&&r<=57))),isNaN(r=new Number(r))?err("Bad number"):r)),((r,e)=>34==r&&o()+o((e=>e-r))+o()),(r=>o((r=>r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||36==r||95==r||r>=192))));var m=(r,e=2,t)=>{for(let a=0;a<t.length;a+=e)r(t[a],t[a+1],t[a+2])};m(i,3,[",",1,,"|",6,,"||",4,,"&",8,,"&&",5,,"^",7,,"==",9,,"!=",9,,">",10,,">=",10,,">>",11,,">>>",11,,"<",10,,"<=",10,,"<<",11,,"+",12,,"+",15,-1,"++",15,-1,"++",16,1,"-",12,,"-",15,-1,"--",15,-1,"--",16,1,"!",15,-1,"*",13,,"/",13,,"%",13,,".",18,(r,e)=>r&&[o(),r,"string"==typeof(e=l(18))?'"'+e+'"':e.valueOf()],"[",18,r=>(o(),[".",r,y(l(0,93))]),"]",,,"(",18,(r,e)=>(o(),e=l(0,41),Array.isArray(e)&&","===e[0]?(e[0]=r,e):e?[r,y(e)]:[r]),"(",19,(r,e)=>!r&&(o(),l(0,41)||err()),")",,,]),m(A,2,["!",r=>!r,"++",r=>++r,"--",r=>--r,".",(r,e)=>r&&r[e],"%",(r,e)=>r%e,"/",(r,e)=>r/e,"*",(r,e)=>r*e,"+",(r,e)=>r+e,"-",(...r)=>r.length<2?-r:r.reduce(((r,e)=>r-e)),">>>",(r,e)=>r>>>e,">>",(r,e)=>r>>e,"<<",(r,e)=>r<<e,">=",(r,e)=>r>=e,">",(r,e)=>r>e,"<=",(r,e)=>r<=e,"<",(r,e)=>r<e,"!=",(r,e)=>r!=e,"==",(r,e)=>r==e,"&",(r,e)=>r&e,"^",(r,e)=>r^e,"|",(r,e)=>r|e,"&&",(...r)=>r.every(Boolean),"||",(...r)=>r.some(Boolean),",",(r,e)=>e]);var b=r=>(r="string"==typeof r?c(r):r,e=>v(r,e));export{b as default,v as evaluate,c as parse};