subscript 6.0.3 → 6.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,25 +1,25 @@
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 micro-language with common syntax subset of C++, JS, Java, Python, Go, Rust etc.<br/>
3
+ _Subscript_ is expression evaluator / microlanguage with standard syntax<br/>
4
4
 
5
- * Standard conventional syntax
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
19
19
 
20
20
  _Subscript_ is designed to be useful for:
21
21
 
22
- * templates (perfect match with [template parts](https://github.com/github/template-parts))
22
+ * templates (perfect match with [template parts](https://github.com/github/template-parts), [templize](https://github.com/spectjs/templize))
23
23
  * expressions evaluators, calculators
24
24
  * configurable subsets of languages (eg. [justin](#justin)) <!-- see sonr, mineural -->
25
25
  * pluggable/mock language features (eg. pipe operator)
@@ -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
- jsep (expression-eval): ~30 ms 🥉
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,16 +316,22 @@ math-parser: -
312
316
 
313
317
  ## Alternatives
314
318
 
319
+ * [jexpr](https://github.com/justinfagnani/jexpr)
320
+ * [jsep](https://github.com/EricSmekens/jsep)
315
321
  * [jexl](https://github.com/TomFrost/Jexl)
316
322
  * [mozjexl](https://github.com/mozilla/mozjexl)
317
323
  * [expr-eval](https://github.com/silentmatt/expr-eval)
318
324
  * [expression-eval](https://github.com/donmccurdy/expression-eval)
319
- * [jsep](https://github.com/EricSmekens/jsep)
320
325
  * [string-math](https://github.com/devrafalko/string-math)
321
326
  * [nerdamer](https://github.com/jiggzson/nerdamer)
322
327
  * [math-codegen](https://github.com/mauriciopoppe/math-codegen)
323
328
  * [math-parser](https://www.npmjs.com/package/math-parser)
324
329
  * [math.js](https://mathjs.org/docs/expressions/parsing.html)
330
+
331
+ ## Next door
332
+
333
+ * [engine262](https://github.com/engine262/engine262)
325
334
  * [Jessie](https://github.com/endojs/Jessie)
335
+ * [xst](https://github.com/Moddable-OpenSource/moddable-xst)
326
336
 
327
337
  <p align=center>🕉</p>
package/justin.js CHANGED
@@ -1,22 +1,180 @@
1
+ const SPACE=32;
2
+
3
+ // current string, index and collected ids
4
+ let idx, cur, args,
5
+
6
+ // no handling tagged literals since easily done on user side with cache, if needed
7
+ parse = (s, fn= !(cur=s, idx=0, args=[], s=expr()) || cur[idx] ? err() : ctx=>s(ctx||{})) => (fn.args = args, fn),
8
+
9
+ isId = c =>
10
+ (c >= 48 && c <= 57) || // 0..9
11
+ (c >= 65 && c <= 90) || // A...Z
12
+ (c >= 97 && c <= 122) || // a...z
13
+ c == 36 || c == 95 || // $, _,
14
+ (c >= 192 && c != 215 && c != 247), // any non-ASCII
15
+
16
+ err = (msg='Bad syntax',c=cur[idx]) => { throw SyntaxError(msg + ' `' + c + '` at ' + idx) },
17
+
18
+ skip = (is=1, from=idx, l) => {
19
+ if (typeof is == 'number') idx += is;
20
+ else while (is(cur.charCodeAt(idx))) idx++;
21
+ return cur.slice(from, idx)
22
+ },
23
+
24
+ // a + b - c
25
+ expr = (prec=0, end, cc, token, newNode, fn) => {
26
+ // chunk/token parser
27
+ while (
28
+ ( cc=space() ) && // till not end
29
+ // FIXME: extra work is happening here, when lookup bails out due to lower precedence -
30
+ // it makes extra `space` call for parent exprs on the same character to check precedence again
31
+ ( newNode =
32
+ (fn=lookup[cc]) && fn(token, prec) || // if operator with higher precedence isn't found
33
+ (!token && id()) // parse literal or quit. token seqs are forbidden: `a b`, `a "b"`, `1.32 a`
34
+ )
35
+ ) token = newNode;
36
+
37
+ // check end character
38
+ // FIXME: can't show "Unclose paren", because can be unknown operator within group as well
39
+ if (end) cc==end?idx++:err();
40
+
41
+ return token
42
+ },
43
+
44
+ // skip space chars, return first non-space character
45
+ space = cc => { while ((cc = cur.charCodeAt(idx)) <= SPACE) idx++; return cc },
46
+
47
+ // variable identifier
48
+ id = (name=skip(isId), fn) => name ? (fn=ctx => ctx[name], args.push(name), fn.id=()=>name, fn) : 0,
49
+
50
+ // operator/token lookup table
51
+ lookup = [],
52
+
53
+ // create operator checker/mapper (see examples)
54
+ set = parse.set = (
55
+ op,
56
+ opPrec, fn=SPACE, // if opPrec & fn come in reverse order - consider them raw parse fn case, still precedence possible
57
+ c=op.charCodeAt(0),
58
+ l=op.length,
59
+ prev=lookup[c],
60
+ arity=fn.length || ([fn,opPrec]=[opPrec,fn], 0),
61
+ word=op.toUpperCase()!==op, // make sure word boundary comes after word operator
62
+ map=
63
+ // binary
64
+ arity>1 ? (a,b) => a && (b=expr(opPrec)) && (
65
+ !a.length && !b.length ? (a=fn(a(),b()), ()=>a) : // static pre-eval like `"a"+"b"`
66
+ ctx => fn(a(ctx),b(ctx),a.id?.(ctx),b.id?.(ctx))
67
+ ) :
68
+ // unary prefix (0 args)
69
+ arity ? a => !a && (a=expr(opPrec-1)) && (ctx => fn(a(ctx))) :
70
+ fn // custom parser
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));
73
+
74
+ const PERIOD=46, CPAREN=41, CBRACK$1=93, DQUOTE$1=34, _0=48, _9=57,
75
+ PREC_SEQ=1, PREC_SOME=4, PREC_EVERY=5, PREC_OR$1=6, PREC_XOR=7, PREC_AND=8,
76
+ PREC_EQ$1=9, PREC_COMP$1=10, PREC_SHIFT=11, PREC_SUM=12, PREC_MULT=13, PREC_UNARY$1=15, PREC_CALL=18;
77
+
78
+ let list$1, op$1, prec$1, fn$1,
79
+ isNum = c => c>=_0 && c<=_9,
80
+ // 1.2e+3, .5
81
+ num = n => (
82
+ n&&err('Unexpected number'),
83
+ n = skip(c=>c == PERIOD || isNum(c)),
84
+ (cur.charCodeAt(idx) == 69 || cur.charCodeAt(idx) == 101) && (n += skip(2) + skip(isNum)),
85
+ n=+n, n!=n ? err('Bad number') : () => n // 0 args means token is static
86
+ ),
87
+
88
+ inc = (a,fn) => ctx => fn(a.of?a.of(ctx):ctx, a.id(ctx));
89
+
90
+ // numbers
91
+ for (op$1=_0;op$1<=_9;) lookup[op$1++] = num;
92
+
93
+ // operators
94
+ for (list$1=[
95
+ // "a"
96
+ '"', a => (a=a?err('Unexpected string'):skip(c => c-DQUOTE$1), skip()||err('Bad string'), ()=>a),,
97
+
98
+ // a.b
99
+ '.', (a,id) => (space(), id=skip(isId)||err(), fn$1=ctx=>a(ctx)[id], fn$1.id=()=>id, fn$1.of=a, fn$1), PREC_CALL,
100
+
101
+ // .2
102
+ // FIXME: .123 is not operator, so we skip back, but mb reorganizing num would be better
103
+ '.', a => !a && num(skip(-1)),,
104
+
105
+ // a[b]
106
+ '[', (a,b,fn) => a && (b=expr(0,CBRACK$1)||err(), fn=ctx=>a(ctx)[b(ctx)], fn.id=b, fn.of=a, fn), PREC_CALL,
107
+
108
+ // a(), a(b), (a,b), (a+b)
109
+ '(', (a,b,fn) => (
110
+ b=expr(0,CPAREN),
111
+ // a(), a(b), a(b,c,d)
112
+ a ? ctx => a(ctx).apply(a.of?.(ctx), b ? b.all ? b.all(ctx) : [b(ctx)] : []) :
113
+ // (a+b)
114
+ b || err()
115
+ ), PREC_CALL,
116
+
117
+ // [a,b,c] or (a,b,c)
118
+ ',', (a,prec,b=expr(PREC_SEQ),fn=ctx => (a(ctx), b(ctx))) => (
119
+ b ? (fn.all = a.all ? ctx => [...a.all(ctx),b(ctx)] : ctx => [a(ctx),b(ctx)]) : err('Skipped argument',),
120
+ fn
121
+ ), PREC_SEQ,
122
+
123
+ '|', PREC_OR$1, (a,b)=>a|b,
124
+ '||', PREC_SOME, (a,b)=>a||b,
125
+
126
+ '&', PREC_AND, (a,b)=>a&b,
127
+ '&&', PREC_EVERY, (a,b)=>a&&b,
128
+
129
+ '^', PREC_XOR, (a,b)=>a^b,
130
+
131
+ // ==, !=
132
+ '==', PREC_EQ$1, (a,b)=>a==b,
133
+ '!=', PREC_EQ$1, (a,b)=>a!=b,
134
+
135
+ // > >= >> >>>, < <= <<
136
+ '>', PREC_COMP$1, (a,b)=>a>b,
137
+ '>=', PREC_COMP$1, (a,b)=>a>=b,
138
+ '>>', PREC_SHIFT, (a,b)=>a>>b,
139
+ '>>>', PREC_SHIFT, (a,b)=>a>>>b,
140
+ '<', PREC_COMP$1, (a,b)=>a<b,
141
+ '<=', PREC_COMP$1, (a,b)=>a<=b,
142
+ '<<', PREC_SHIFT, (a,b)=>a<<b,
143
+
144
+ // + ++ - --
145
+ '+', PREC_SUM, (a,b)=>a+b,
146
+ '+', PREC_UNARY$1, (a)=>+a,
147
+ '++', a => inc(a||expr(PREC_UNARY$1-1), a ? (a,b)=>a[b]++ : (a,b)=>++a[b]), PREC_UNARY$1,
148
+
149
+ '-', PREC_SUM, (a,b)=>a-b,
150
+ '-', PREC_UNARY$1, (a)=>-a,
151
+ '--', a => inc(a||expr(PREC_UNARY$1-1), a ? (a,b)=>a[b]-- : (a,b)=>--a[b]), PREC_UNARY$1,
152
+
153
+ // ! ~
154
+ '!', PREC_UNARY$1, (a)=>!a,
155
+
156
+ // * / %
157
+ '*', PREC_MULT, (a,b)=>a*b,
158
+ '/', PREC_MULT, (a,b)=>a/b,
159
+ '%', PREC_MULT, (a,b)=>a%b
160
+ ]; [op$1,prec$1,fn$1,...list$1]=list$1, op$1;) set(op$1,prec$1,fn$1);
161
+
1
162
  // justin lang https://github.com/endojs/Jessie/issues/66
2
- import {parse, set, lookup, skip, cur, idx, err, expr, isId, space} from './index.js'
3
- import './subscript.js'
4
163
 
5
- const PERIOD=46, OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, QUOTE=39, _0=48, _9=57, BSLASH=92,
6
- PREC_SEQ=1, PREC_COND=3, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
7
- PREC_EQ=9, PREC_COMP=10, PREC_SHIFT=11, PREC_SUM=12, PREC_MULT=13, PREC_EXP=14, PREC_UNARY=15, PREC_POSTFIX=16, PREC_CALL=18, PREC_GROUP=19
164
+ const CBRACK=93, DQUOTE=34, QUOTE=39, BSLASH=92,
165
+ PREC_OR=6, PREC_EQ=9, PREC_COMP=10, PREC_EXP=14, PREC_UNARY=15;
8
166
 
9
167
 
10
- let u, list, op, prec, fn,
168
+ let list, op, prec, fn,
11
169
  escape = {n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', v:'\v'},
12
170
  string = q => (qc, c, str='') => {
13
- qc&&err('Unexpected string') // must not follow another token
171
+ qc&&err('Unexpected string'); // must not follow another token
14
172
  while (c=cur.charCodeAt(idx), c-q) {
15
- if (c === BSLASH) skip(), c=skip(), str += escape[c] || c
16
- else str += skip()
173
+ if (c === BSLASH) skip(), c=skip(), str += escape[c] || c;
174
+ else str += skip();
17
175
  }
18
176
  return skip()||err('Bad string'), () => str
19
- }
177
+ };
20
178
 
21
179
  // operators
22
180
  for (list=[
@@ -46,7 +204,9 @@ for (list=[
46
204
 
47
205
  // ?:
48
206
  ':', 3.1, (a,b) => [a,b],
49
- '?', 3, (a,b) => a ? b[2] : b[1],
207
+ '?', 3, (a,b) => a ? b[0] : b[1],
208
+
209
+ '??', PREC_OR, (a,b) => a??b,
50
210
 
51
211
  // a?.[, a?.( - postfix operator
52
212
  '?.', a => a && (ctx => a(ctx)||(()=>{})),,//(a) => a||(()=>{}),
@@ -56,18 +216,19 @@ for (list=[
56
216
  'in', PREC_COMP, (a,b) => a in b,
57
217
 
58
218
  // [a,b,c]
59
- '[', (a, args) => !a && (
219
+ '[', (a) => !a && (
60
220
  a=expr(0,CBRACK),
61
221
  !a ? ctx => [] : a.all ? ctx => a.all(ctx) : ctx => [a(ctx)]
62
222
  ),,
63
223
 
64
224
  // {a:1, b:2, c:3}
65
- '{', (a, args) => !a && (
225
+ '{', (a, entries) => !a && (
66
226
  a=expr(0,125),
67
- !a ? ctx => ({}) : ctx => (args=(a.all||a)(ctx), Object.fromEntries(a.all?args:[args]))
227
+ !a ? ctx => ({}) : ctx => (entries=(a.all||a)(ctx), Object.fromEntries(a.all?entries:[entries]))
68
228
  ),,
69
- ':', (a, prec, b) => (b=expr(3.1)||err(), ctx => [(a.id||a)(ctx), b(ctx), a(ctx)]), 3.1
70
-
71
- ]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
229
+ // for JSON case we should not collect arg (different evaluator than ternary)
230
+ ':', (a, prec, b) => (b=expr(1.1)||err(), a.id&&args.pop(), ctx => [(a.id||a)(ctx), b(ctx)]), 1.1
72
231
 
73
- export default parse
232
+ ]; [op,prec,fn,...list]=list, op;) set(op,prec,fn);
233
+
234
+ export { parse as default };
package/justin.min.js CHANGED
@@ -1 +1 @@
1
- let e,r,t,l,n,a,d=(t,...l)=>(r=t.raw?String.raw(t,...l):t,e=0,!(t=p())||r[e]?i("Unexpected end"):e=>t(e||{})),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=(t="Unexpected token",l=r[e])=>{throw SyntaxError(t+" `"+l+"` at "+e)},f=(t=1,l=e,n)=>{if("number"==typeof t)e+=t;else for(;t(r.charCodeAt(e));)e++;return r.slice(l,e)},p=(r=0,t,l,n,a,d)=>{for(;(l=c())&&(a=(d=u[l])&&d(n,r)||!n&&s());)n=a;return t&&(l==t?e++:i("Missing",String.fromCharCode(t))),n},c=t=>{for(;(t=r.charCodeAt(e))<=32;)e++;return t},s=(e=f(o),r)=>e?((r=r=>r[e]).id=()=>e,r):0,u=[],h=d.set=(t,l,n=32,a=t.charCodeAt(0),d=t.length,i=u[a],f=n.length||([n,l]=[l,n],0),c=t.toUpperCase()!==t,s=(f>1?(e,r)=>e&&(r=p(l))&&(e.length||r.length?t=>n(e(t),r(t)):(e=n(e(),r()),()=>e)):f?e=>!e&&(e=p(l-1))&&(r=>n(e(r))):n))=>u[a]=(n,a,f=e)=>a<l&&(d<2||r.substr(e,d)==t)&&(!c||!o(r.charCodeAt(e+d)))&&(e+=d,s(n,a))||(e=f,i&&i(n,a)),g=e=>e>=48&&e<=57,x=t=>(t&&i("Unexpected number"),t=f((e=>46==e||g(e))),(69==r.charCodeAt(e)||101==r.charCodeAt(e))&&(t+=f(2)+f(g)),(t=+t)!=t?i("Bad number"):()=>t),C=(e,r,t=e.of)=>l=>r(t?t(l):l,e.id());for(l=48;l<=57;)u[l++]=x;for(t=['"',e=>(e=e?i("Unexpected string"):f((e=>e-34)),f()||i("Bad string"),()=>e),,".",(e,r,t)=>e?(c(),r=f(o)||i(),(t=t=>e(t)[r]).id=()=>r,t.of=e,t):x(f(-1)),18,"[",(e,r,t)=>e&&(r=p(0,93)||i("Empty group"),(t=t=>e(t)[r(t)]).id=r,t.of=e,t),18,"(",(e,r,t)=>(r=p(0,41),e?t=>e(t).apply(e.of?.(t),r?r.all?r.all(t):[r(t)]:[]):r||i("Empty group")),18,",",(e,r,t=p(1))=>(t.all=e.all?(r,l,n=e.all(r))=>n.push(t(r))&&n: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=>C(e||p(14),e?(e,r)=>e[r]++:(e,r)=>++e[r]),15,"-",12,(e,r)=>e-r,"-",15,e=>-e,"--",e=>C(e||p(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];[l,n,a,...t]=t,l;)h(l,n,a);let U,A,b,m,y={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},E=t=>(l,n,a="")=>{for(l&&i("Unexpected string");(n=r.charCodeAt(e))-t;)92===n?(f(),n=f(),a+=y[n]||n):a+=f();return f()||i("Bad string"),()=>a};for((U=['"',E(34),,"'",E(39),,"/*",(t,l)=>(f((t=>42!==t&&47!==r.charCodeAt(e+1))),f(2),t||p(l)),,"//",(e,r)=>(f((e=>e>=32)),e||p(r)),,"null",e=>e?i("Unexpected literal"):()=>null,,"true",e=>e?i("Unexpected literal"):()=>!0,,"false",e=>e?i("Unexpected literal"):()=>!1,,"undefined",e=>e?i("Unexpected literal"):()=>{},,";",e=>p()||(()=>{}),,"===",9,(e,r)=>e===r,"!==",9,(e,r)=>e!==r,"~",15,e=>~e,"**",(e,r,t=p(13))=>r=>e(r)**t(r),14,":",3.1,(e,r)=>[e,r],"?",3,(e,r)=>e?r[2]:r[1],"?.",e=>e&&(r=>e(r)||(()=>{})),,"?.",(e,r)=>(c(),(r=f(o))&&(t=>e(t)?.[r])),,"in",10,(e,r)=>e in r,"[",(e,r)=>!e&&((e=p(0,93))?e.all?r=>e.all(r):r=>[e(r)]:e=>[]),,"{",(e,r)=>!e&&((e=p(0,125))?t=>(r=(e.all||e)(t),Object.fromEntries(e.all?r:[r])):e=>({})),,":",(e,r,t)=>(t=p(3.1)||i(),r=>[(e.id||e)(r),t(r),e(r)]),3.1]);[A,b,m,...U]=U,A;)h(A,b,m);export{d as default};
1
+ let e,t,r,l,a,n,d,o=(l,a=(t=l,e=0,r=[],!(l=c())||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)},p=(r=1,l=e,a)=>{if("number"==typeof r)e+=r;else for(;r(t.charCodeAt(e));)e++;return t.slice(l,e)},c=(t=0,r,l,a,n,d)=>{for(;(l=s())&&(n=(d=h[l])&&d(a,t)||!a&&u());)a=n;return r&&(l==r?e++:f()),a},s=r=>{for(;(r=t.charCodeAt(e))<=32;)e++;return r},u=(e=p(i),t)=>e?(t=t=>t[e],r.push(e),t.id=()=>e,t):0,h=[],g=o.set=(r,l,a=32,n=r.charCodeAt(0),d=r.length,o=h[n],f=a.length||([a,l]=[l,a],0),p=r.toUpperCase()!==r,s=(f>1?(e,t)=>e&&(t=c(l))&&(e.length||t.length?r=>a(e(r),t(r),e.id?.(r),t.id?.(r)):(e=a(e(),t()),()=>e)):f?e=>!e&&(e=c(l-1))&&(t=>a(e(t))):a))=>h[n]=(a,n,f=e)=>n<l&&(d<2||t.substr(e,d)==r)&&(!p||!i(t.charCodeAt(e+d)))&&(e+=d,s(a,n))||(e=f,o&&o(a,n)),x=e=>e>=48&&e<=57,C=r=>(r&&f("Unexpected number"),r=p((e=>46==e||x(e))),(69==t.charCodeAt(e)||101==t.charCodeAt(e))&&(r+=p(2)+p(x)),(r=+r)!=r?f("Bad number"):()=>r),A=(e,t)=>r=>t(e.of?e.of(r):r,e.id(r));for(a=48;a<=57;)h[a++]=C;for(l=['"',e=>(e=e?f("Unexpected string"):p((e=>e-34)),p()||f("Bad string"),()=>e),,".",(e,t)=>(s(),t=p(i)||f(),d=r=>e(r)[t],d.id=()=>t,d.of=e,d),18,".",e=>!e&&C(p(-1)),,"[",(e,t,r)=>e&&(t=c(0,93)||f(),(r=r=>e(r)[t(r)]).id=t,r.of=e,r),18,"(",(e,t,r)=>(t=c(0,41),e?r=>e(r).apply(e.of?.(r),t?t.all?t.all(r):[t(r)]:[]):t||f()),18,",",(e,t,r=c(1),l=(t=>(e(t),r(t))))=>(r?l.all=e.all?t=>[...e.all(t),r(t)]:t=>[e(t),r(t)]:f("Skipped argument"),l),1,"|",6,(e,t)=>e|t,"||",4,(e,t)=>e||t,"&",8,(e,t)=>e&t,"&&",5,(e,t)=>e&&t,"^",7,(e,t)=>e^t,"==",9,(e,t)=>e==t,"!=",9,(e,t)=>e!=t,">",10,(e,t)=>e>t,">=",10,(e,t)=>e>=t,">>",11,(e,t)=>e>>t,">>>",11,(e,t)=>e>>>t,"<",10,(e,t)=>e<t,"<=",10,(e,t)=>e<=t,"<<",11,(e,t)=>e<<t,"+",12,(e,t)=>e+t,"+",15,e=>+e,"++",e=>A(e||c(14),e?(e,t)=>e[t]++:(e,t)=>++e[t]),15,"-",12,(e,t)=>e-t,"-",15,e=>-e,"--",e=>A(e||c(14),e?(e,t)=>e[t]--:(e,t)=>--e[t]),15,"!",15,e=>!e,"*",13,(e,t)=>e*t,"/",13,(e,t)=>e/t,"%",13,(e,t)=>e%t];[a,n,d,...l]=l,a;)g(a,n,d);let U,b,m,y,B={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},v=r=>(l,a,n="")=>{for(l&&f("Unexpected string");(a=t.charCodeAt(e))-r;)92===a?(p(),a=p(),n+=B[a]||a):n+=p();return p()||f("Bad string"),()=>n};for((U=['"',v(34),,"'",v(39),,"/*",(r,l)=>(p((r=>42!==r&&47!==t.charCodeAt(e+1))),p(2),r||c(l)),,"//",(e,t)=>(p((e=>e>=32)),e||c(t)),,"null",e=>e?f("Unexpected literal"):()=>null,,"true",e=>e?f("Unexpected literal"):()=>!0,,"false",e=>e?f("Unexpected literal"):()=>!1,,"undefined",e=>e?f("Unexpected literal"):()=>{},,";",e=>c()||(()=>{}),,"===",9,(e,t)=>e===t,"!==",9,(e,t)=>e!==t,"~",15,e=>~e,"**",(e,t,r=c(13))=>t=>e(t)**r(t),14,":",3.1,(e,t)=>[e,t],"?",3,(e,t)=>e?t[0]:t[1],"??",6,(e,t)=>e??t,"?.",e=>e&&(t=>e(t)||(()=>{})),,"?.",(e,t)=>(s(),(t=p(i))&&(r=>e(r)?.[t])),,"in",10,(e,t)=>e in t,"[",e=>!e&&((e=c(0,93))?e.all?t=>e.all(t):t=>[e(t)]:e=>[]),,"{",(e,t)=>!e&&((e=c(0,125))?r=>(t=(e.all||e)(r),Object.fromEntries(e.all?t:[t])):e=>({})),,":",(e,t,l)=>(l=c(1.1)||f(),e.id&&r.pop(),t=>[(e.id||e)(t),l(t)]),1.1]);[b,m,y,...U]=U,b;)g(b,m,y);export{o as default};
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "subscript",
3
- "version": "6.0.3",
3
+ "version": "6.3.0",
4
4
  "description": "Fast and tiny expression evaluator with common syntax microlanguage.",
5
- "main": "subscript.js",
5
+ "main": "src/subscript.js",
6
+ "module": "src/subscript.js",
7
+ "browser": "subscript.js",
6
8
  "type": "module",
7
9
  "files": [
8
- "index.js",
10
+ "src",
9
11
  "subscript.js",
10
12
  "subscript.min.js",
11
13
  "justin.js",
@@ -17,10 +19,11 @@
17
19
  "test": "test"
18
20
  },
19
21
  "scripts": {
20
- "build": "rollup subscript.js --file subscript.min.js --format esm --name \"Subscript\"",
21
- "minify": "terser subscript.min.js -o subscript.min.js --module -c passes=3 -m",
22
- "build-justin": "rollup justin.js --file justin.min.js --format esm --name \"Justin\"",
23
- "minify-justin": "terser justin.min.js -o justin.min.js --module -c passes=3 -m",
22
+ "build": "npm run build-subscript && npm run minify-subscript && npm run build-justin && npm run minify-justin",
23
+ "build-subscript": "rollup src/subscript.js --file subscript.js --format esm --name \"Subscript\"",
24
+ "minify-subscript": "terser subscript.js -o subscript.min.js --module -c passes=3 -m",
25
+ "build-justin": "rollup src/justin.js --file justin.js --format esm --name \"Justin\"",
26
+ "minify-justin": "terser justin.js -o justin.min.js --module -c passes=3 -m",
24
27
  "test": "node test"
25
28
  },
26
29
  "repository": {
@@ -40,6 +43,7 @@
40
43
  "eval",
41
44
  "math-eval",
42
45
  "math-evaluator",
46
+ "math-expression-evaluator",
43
47
  "calculation",
44
48
  "jessie",
45
49
  "jessica",
@@ -1,9 +1,10 @@
1
- const SPACE=32
1
+ const SPACE=32, CPAREN=41
2
2
 
3
- // current string & index
4
- export let idx, cur,
3
+ // current string, index and collected ids
4
+ export let idx, cur, args,
5
5
 
6
- parse = (s, ...fields) => !(cur=s.raw ? String.raw(s,...fields) : s, idx=0, s=expr()) || cur[idx] ? err('Unexpected end') : ctx=>s(ctx||{}),
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='Unexpected token',c=cur[idx]) => { throw SyntaxError(msg + ' `' + c + '` at ' + idx) },
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
- if (end) cc==end?idx++:err('Missing', String.fromCharCode(end))
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 = [],
@@ -61,11 +63,10 @@ set = parse.set = (
61
63
  // binary
62
64
  arity>1 ? (a,b) => a && (b=expr(opPrec)) && (
63
65
  !a.length && !b.length ? (a=fn(a(),b()), ()=>a) : // static pre-eval like `"a"+"b"`
64
- ctx => fn(a(ctx),b(ctx))
66
+ ctx => fn(a(ctx),b(ctx),a.id?.(ctx),b.id?.(ctx))
65
67
  ) :
66
68
  // unary prefix (0 args)
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/src/justin.js ADDED
@@ -0,0 +1,74 @@
1
+ // justin lang https://github.com/endojs/Jessie/issues/66
2
+ import {set, lookup, skip, cur, idx, err, expr, isId, space, args} from './index.js'
3
+ export { default } from './subscript.js'
4
+
5
+ const PERIOD=46, OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, QUOTE=39, _0=48, _9=57, BSLASH=92,
6
+ PREC_SEQ=1, PREC_COND=3, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
7
+ PREC_EQ=9, PREC_COMP=10, PREC_SHIFT=11, PREC_SUM=12, PREC_MULT=13, PREC_EXP=14, PREC_UNARY=15, PREC_POSTFIX=16, PREC_CALL=18, PREC_GROUP=19
8
+
9
+
10
+ let u, list, op, prec, fn,
11
+ escape = {n:'\n', r:'\r', t:'\t', b:'\b', f:'\f', v:'\v'},
12
+ string = q => (qc, c, str='') => {
13
+ qc&&err('Unexpected string') // must not follow another token
14
+ while (c=cur.charCodeAt(idx), c-q) {
15
+ if (c === BSLASH) skip(), c=skip(), str += escape[c] || c
16
+ else str += skip()
17
+ }
18
+ return skip()||err('Bad string'), () => str
19
+ }
20
+
21
+ // operators
22
+ for (list=[
23
+ // "' with /
24
+ '"', string(DQUOTE),,
25
+ "'", string(QUOTE),,
26
+
27
+ // /**/, //
28
+ '/*', (a, prec) => (skip(c => c !== 42 && cur.charCodeAt(idx+1) !== 47), skip(2), a||expr(prec)),,
29
+ '//', (a, prec) => (skip(c => c >= 32), a||expr(prec)),,
30
+
31
+ // literals
32
+ 'null', a => a ? err('Unexpected literal') : ()=>null,,
33
+ 'true', a => a ? err('Unexpected literal') : ()=>true,,
34
+ 'false', a => a ? err('Unexpected literal') : ()=>false,,
35
+ 'undefined', a => a ? err('Unexpected literal') : ()=>undefined,,
36
+
37
+ ';', a => expr()||(()=>{}),,
38
+
39
+ // operators
40
+ '===', PREC_EQ, (a,b) => a===b,
41
+ '!==', PREC_EQ, (a,b) => a!==b,
42
+ '~', PREC_UNARY, (a) => ~a,
43
+
44
+ // right order
45
+ '**', (a,prec,b=expr(PREC_EXP-1)) => ctx=>a(ctx)**b(ctx), PREC_EXP,
46
+
47
+ // ?:
48
+ ':', 3.1, (a,b) => [a,b],
49
+ '?', 3, (a,b) => a ? b[0] : b[1],
50
+
51
+ '??', PREC_OR, (a,b) => a??b,
52
+
53
+ // a?.[, a?.( - postfix operator
54
+ '?.', a => a && (ctx => a(ctx)||(()=>{})),,//(a) => a||(()=>{}),
55
+ // a?.b - optional chain operator
56
+ '?.', (a,id) => (space(), id=skip(isId)) && (ctx => a(ctx)?.[id]),,
57
+
58
+ 'in', PREC_COMP, (a,b) => a in b,
59
+
60
+ // [a,b,c]
61
+ '[', (a) => !a && (
62
+ a=expr(0,CBRACK),
63
+ !a ? ctx => [] : a.all ? ctx => a.all(ctx) : ctx => [a(ctx)]
64
+ ),,
65
+
66
+ // {a:1, b:2, c:3}
67
+ '{', (a, entries) => !a && (
68
+ a=expr(0,125),
69
+ !a ? ctx => ({}) : ctx => (entries=(a.all||a)(ctx), Object.fromEntries(a.all?entries:[entries]))
70
+ ),,
71
+ // for JSON case we should not collect arg (different evaluator than ternary)
72
+ ':', (a, prec, b) => (b=expr(1.1)||err(), a.id&&args.pop(), ctx => [(a.id||a)(ctx), b(ctx)]), 1.1
73
+
74
+ ]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
@@ -0,0 +1,91 @@
1
+ import {parse, set, lookup, skip, cur, idx, err, expr, isId, args, space} from './index.js'
2
+
3
+ const PERIOD=46, OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, _0=48, _9=57,
4
+ PREC_SEQ=1, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
5
+ PREC_EQ=9, PREC_COMP=10, PREC_SHIFT=11, PREC_SUM=12, PREC_MULT=13, PREC_UNARY=15, PREC_POSTFIX=16, PREC_CALL=18, PREC_GROUP=19
6
+
7
+ let u, list, op, prec, fn,
8
+ isNum = c => c>=_0 && c<=_9,
9
+ // 1.2e+3, .5
10
+ num = n => (
11
+ n&&err('Unexpected number'),
12
+ n = skip(c=>c == PERIOD || isNum(c)),
13
+ (cur.charCodeAt(idx) == 69 || cur.charCodeAt(idx) == 101) && (n += skip(2) + skip(isNum)),
14
+ n=+n, n!=n ? err('Bad number') : () => n // 0 args means token is static
15
+ ),
16
+
17
+ inc = (a,fn) => ctx => fn(a.of?a.of(ctx):ctx, a.id(ctx))
18
+
19
+ // numbers
20
+ for (op=_0;op<=_9;) lookup[op++] = num
21
+
22
+ // operators
23
+ for (list=[
24
+ // "a"
25
+ '"', a => (a=a?err('Unexpected string'):skip(c => c-DQUOTE), skip()||err('Bad string'), ()=>a),,
26
+
27
+ // a.b
28
+ '.', (a,id) => (space(), id=skip(isId)||err(), fn=ctx=>a(ctx)[id], fn.id=()=>id, fn.of=a, fn), PREC_CALL,
29
+
30
+ // .2
31
+ // FIXME: .123 is not operator, so we skip back, but mb reorganizing num would be better
32
+ '.', a => !a && num(skip(-1)),,
33
+
34
+ // a[b]
35
+ '[', (a,b,fn) => a && (b=expr(0,CBRACK)||err(), fn=ctx=>a(ctx)[b(ctx)], fn.id=b, fn.of=a, fn), PREC_CALL,
36
+
37
+ // a(), a(b), (a,b), (a+b)
38
+ '(', (a,b,fn) => (
39
+ b=expr(0,CPAREN),
40
+ // a(), a(b), a(b,c,d)
41
+ a ? ctx => a(ctx).apply(a.of?.(ctx), b ? b.all ? b.all(ctx) : [b(ctx)] : []) :
42
+ // (a+b)
43
+ b || err()
44
+ ), PREC_CALL,
45
+
46
+ // [a,b,c] or (a,b,c)
47
+ ',', (a,prec,b=expr(PREC_SEQ),fn=ctx => (a(ctx), b(ctx))) => (
48
+ b ? (fn.all = a.all ? ctx => [...a.all(ctx),b(ctx)] : ctx => [a(ctx),b(ctx)]) : err('Skipped argument',),
49
+ fn
50
+ ), PREC_SEQ,
51
+
52
+ '|', PREC_OR, (a,b)=>a|b,
53
+ '||', PREC_SOME, (a,b)=>a||b,
54
+
55
+ '&', PREC_AND, (a,b)=>a&b,
56
+ '&&', PREC_EVERY, (a,b)=>a&&b,
57
+
58
+ '^', PREC_XOR, (a,b)=>a^b,
59
+
60
+ // ==, !=
61
+ '==', PREC_EQ, (a,b)=>a==b,
62
+ '!=', PREC_EQ, (a,b)=>a!=b,
63
+
64
+ // > >= >> >>>, < <= <<
65
+ '>', PREC_COMP, (a,b)=>a>b,
66
+ '>=', PREC_COMP, (a,b)=>a>=b,
67
+ '>>', PREC_SHIFT, (a,b)=>a>>b,
68
+ '>>>', PREC_SHIFT, (a,b)=>a>>>b,
69
+ '<', PREC_COMP, (a,b)=>a<b,
70
+ '<=', PREC_COMP, (a,b)=>a<=b,
71
+ '<<', PREC_SHIFT, (a,b)=>a<<b,
72
+
73
+ // + ++ - --
74
+ '+', PREC_SUM, (a,b)=>a+b,
75
+ '+', PREC_UNARY, (a)=>+a,
76
+ '++', a => inc(a||expr(PREC_UNARY-1), a ? (a,b)=>a[b]++ : (a,b)=>++a[b]), PREC_UNARY,
77
+
78
+ '-', PREC_SUM, (a,b)=>a-b,
79
+ '-', PREC_UNARY, (a)=>-a,
80
+ '--', a => inc(a||expr(PREC_UNARY-1), a ? (a,b)=>a[b]-- : (a,b)=>--a[b]), PREC_UNARY,
81
+
82
+ // ! ~
83
+ '!', PREC_UNARY, (a)=>!a,
84
+
85
+ // * / %
86
+ '*', PREC_MULT, (a,b)=>a*b,
87
+ '/', PREC_MULT, (a,b)=>a/b,
88
+ '%', PREC_MULT, (a,b)=>a%b
89
+ ]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
90
+
91
+ export default parse
package/subscript.js CHANGED
@@ -1,32 +1,106 @@
1
- import {parse, set, lookup, skip, cur, idx, err, expr, isId, space} from './index.js'
2
-
3
- const PERIOD=46, OPAREN=40, CPAREN=41, OBRACK=91, CBRACK=93, SPACE=32, DQUOTE=34, _0=48, _9=57,
1
+ const SPACE=32;
2
+
3
+ // current string, index and collected ids
4
+ let idx, cur, args,
5
+
6
+ // no handling tagged literals since easily done on user side with cache, if needed
7
+ parse = (s, fn= !(cur=s, idx=0, args=[], s=expr()) || cur[idx] ? err() : ctx=>s(ctx||{})) => (fn.args = args, fn),
8
+
9
+ isId = c =>
10
+ (c >= 48 && c <= 57) || // 0..9
11
+ (c >= 65 && c <= 90) || // A...Z
12
+ (c >= 97 && c <= 122) || // a...z
13
+ c == 36 || c == 95 || // $, _,
14
+ (c >= 192 && c != 215 && c != 247), // any non-ASCII
15
+
16
+ err = (msg='Bad syntax',c=cur[idx]) => { throw SyntaxError(msg + ' `' + c + '` at ' + idx) },
17
+
18
+ skip = (is=1, from=idx, l) => {
19
+ if (typeof is == 'number') idx += is;
20
+ else while (is(cur.charCodeAt(idx))) idx++;
21
+ return cur.slice(from, idx)
22
+ },
23
+
24
+ // a + b - c
25
+ expr = (prec=0, end, cc, token, newNode, fn) => {
26
+ // chunk/token parser
27
+ while (
28
+ ( cc=space() ) && // till not end
29
+ // FIXME: extra work is happening here, when lookup bails out due to lower precedence -
30
+ // it makes extra `space` call for parent exprs on the same character to check precedence again
31
+ ( newNode =
32
+ (fn=lookup[cc]) && fn(token, prec) || // if operator with higher precedence isn't found
33
+ (!token && id()) // parse literal or quit. token seqs are forbidden: `a b`, `a "b"`, `1.32 a`
34
+ )
35
+ ) token = newNode;
36
+
37
+ // check end character
38
+ // FIXME: can't show "Unclose paren", because can be unknown operator within group as well
39
+ if (end) cc==end?idx++:err();
40
+
41
+ return token
42
+ },
43
+
44
+ // skip space chars, return first non-space character
45
+ space = cc => { while ((cc = cur.charCodeAt(idx)) <= SPACE) idx++; return cc },
46
+
47
+ // variable identifier
48
+ id = (name=skip(isId), fn) => name ? (fn=ctx => ctx[name], args.push(name), fn.id=()=>name, fn) : 0,
49
+
50
+ // operator/token lookup table
51
+ lookup = [],
52
+
53
+ // create operator checker/mapper (see examples)
54
+ set = parse.set = (
55
+ op,
56
+ opPrec, fn=SPACE, // if opPrec & fn come in reverse order - consider them raw parse fn case, still precedence possible
57
+ c=op.charCodeAt(0),
58
+ l=op.length,
59
+ prev=lookup[c],
60
+ arity=fn.length || ([fn,opPrec]=[opPrec,fn], 0),
61
+ word=op.toUpperCase()!==op, // make sure word boundary comes after word operator
62
+ map=
63
+ // binary
64
+ arity>1 ? (a,b) => a && (b=expr(opPrec)) && (
65
+ !a.length && !b.length ? (a=fn(a(),b()), ()=>a) : // static pre-eval like `"a"+"b"`
66
+ ctx => fn(a(ctx),b(ctx),a.id?.(ctx),b.id?.(ctx))
67
+ ) :
68
+ // unary prefix (0 args)
69
+ arity ? a => !a && (a=expr(opPrec-1)) && (ctx => fn(a(ctx))) :
70
+ fn // custom parser
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));
73
+
74
+ const PERIOD=46, CPAREN=41, CBRACK=93, DQUOTE=34, _0=48, _9=57,
4
75
  PREC_SEQ=1, PREC_SOME=4, PREC_EVERY=5, PREC_OR=6, PREC_XOR=7, PREC_AND=8,
5
- PREC_EQ=9, PREC_COMP=10, PREC_SHIFT=11, PREC_SUM=12, PREC_MULT=13, PREC_UNARY=15, PREC_POSTFIX=16, PREC_CALL=18, PREC_GROUP=19
76
+ PREC_EQ=9, PREC_COMP=10, PREC_SHIFT=11, PREC_SUM=12, PREC_MULT=13, PREC_UNARY=15, PREC_CALL=18;
6
77
 
7
- let u, list, op, prec, fn,
78
+ let list, op, prec, fn,
8
79
  isNum = c => c>=_0 && c<=_9,
9
80
  // 1.2e+3, .5
10
81
  num = n => (
11
82
  n&&err('Unexpected number'),
12
- n = skip(c=>c==PERIOD || isNum(c)),
83
+ n = skip(c=>c == PERIOD || isNum(c)),
13
84
  (cur.charCodeAt(idx) == 69 || cur.charCodeAt(idx) == 101) && (n += skip(2) + skip(isNum)),
14
85
  n=+n, n!=n ? err('Bad number') : () => n // 0 args means token is static
15
86
  ),
16
87
 
17
- inc = (a,fn,c=a.of) => ctx => fn(c?c(ctx):ctx, a.id())
88
+ inc = (a,fn) => ctx => fn(a.of?a.of(ctx):ctx, a.id(ctx));
18
89
 
19
90
  // numbers
20
- for (op=_0;op<=_9;) lookup[op++] = num
91
+ for (op=_0;op<=_9;) lookup[op++] = num;
21
92
 
22
93
  // operators
23
94
  for (list=[
24
95
  // "a"
25
96
  '"', a => (a=a?err('Unexpected string'):skip(c => c-DQUOTE), skip()||err('Bad string'), ()=>a),,
26
97
 
27
- // a.b, .2, 1.2 parser in one
28
- '.', (a,id,fn) => !a ? num(skip(-1)) : // FIXME: .123 is not operator, so we skip back, but mb reorganizing num would be better
29
- (space(), id=skip(isId)||err(), fn=ctx=>a(ctx)[id], fn.id=()=>id, fn.of=a, fn), PREC_CALL,
98
+ // a.b
99
+ '.', (a,id) => (space(), id=skip(isId)||err(), fn=ctx=>a(ctx)[id], fn.id=()=>id, fn.of=a, fn), PREC_CALL,
100
+
101
+ // .2
102
+ // FIXME: .123 is not operator, so we skip back, but mb reorganizing num would be better
103
+ '.', a => !a && num(skip(-1)),,
30
104
 
31
105
  // a[b]
32
106
  '[', (a,b,fn) => a && (b=expr(0,CBRACK)||err(), fn=ctx=>a(ctx)[b(ctx)], fn.id=b, fn.of=a, fn), PREC_CALL,
@@ -41,9 +115,9 @@ for (list=[
41
115
  ), PREC_CALL,
42
116
 
43
117
  // [a,b,c] or (a,b,c)
44
- ',', (a,prec,b=expr(PREC_SEQ)) => (
45
- b.all = a.all ? ctx => [...a.all(ctx), b(ctx)] : ctx => [a(ctx),b(ctx)],
46
- b
118
+ ',', (a,prec,b=expr(PREC_SEQ),fn=ctx => (a(ctx), b(ctx))) => (
119
+ b ? (fn.all = a.all ? ctx => [...a.all(ctx),b(ctx)] : ctx => [a(ctx),b(ctx)]) : err('Skipped argument',),
120
+ fn
47
121
  ), PREC_SEQ,
48
122
 
49
123
  '|', PREC_OR, (a,b)=>a|b,
@@ -83,6 +157,6 @@ for (list=[
83
157
  '*', PREC_MULT, (a,b)=>a*b,
84
158
  '/', PREC_MULT, (a,b)=>a/b,
85
159
  '%', PREC_MULT, (a,b)=>a%b
86
- ]; [op,prec,fn,...list]=list, op;) set(op,prec,fn)
87
-
88
- export default parse
160
+ ]; [op,prec,fn,...list]=list, op;) set(op,prec,fn);
161
+
162
+ export { parse as default };
package/subscript.min.js CHANGED
@@ -1 +1 @@
1
- let e,r,t,o,a,n,d=(t,...o)=>(r=t.raw?String.raw(t,...o):t,e=0,!(t=i())||r[e]?f("Unexpected end"):e=>t(e||{})),l=e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e,f=(t="Unexpected token",o=r[e])=>{throw SyntaxError(t+" `"+o+"` at "+e)},h=(t=1,o=e,a)=>{if("number"==typeof t)e+=t;else for(;t(r.charCodeAt(e));)e++;return r.slice(o,e)},i=(r=0,t,o,a,n,d)=>{for(;(o=c())&&(n=(d=p[o])&&d(a,r)||!a&&s());)a=n;return t&&(o==t?e++:f("Missing",String.fromCharCode(t))),a},c=t=>{for(;(t=r.charCodeAt(e))<=32;)e++;return t},s=(e=h(l),r)=>e?((r=r=>r[e]).id=()=>e,r):0,p=[],g=d.set=(t,o,a=32,n=t.charCodeAt(0),d=t.length,f=p[n],h=a.length||([a,o]=[o,a],0),c=t.toUpperCase()!==t,s=(h>1?(e,r)=>e&&(r=i(o))&&(e.length||r.length?t=>a(e(t),r(t)):(e=a(e(),r()),()=>e)):h?e=>!e&&(e=i(o-1))&&(r=>a(e(r))):a))=>p[n]=(a,n,h=e)=>n<o&&(d<2||r.substr(e,d)==t)&&(!c||!l(r.charCodeAt(e+d)))&&(e+=d,s(a,n))||(e=h,f&&f(a,n)),C=e=>e>=48&&e<=57,u=t=>(t&&f("Unexpected number"),t=h((e=>46==e||C(e))),(69==r.charCodeAt(e)||101==r.charCodeAt(e))&&(t+=h(2)+h(C)),(t=+t)!=t?f("Bad number"):()=>t),x=(e,r,t=e.of)=>o=>r(t?t(o):o,e.id());for(o=48;o<=57;)p[o++]=u;for(t=['"',e=>(e=e?f("Unexpected string"):h((e=>e-34)),h()||f("Bad string"),()=>e),,".",(e,r,t)=>e?(c(),r=h(l)||f(),(t=t=>e(t)[r]).id=()=>r,t.of=e,t):u(h(-1)),18,"[",(e,r,t)=>e&&(r=i(0,93)||f(),(t=t=>e(t)[r(t)]).id=r,t.of=e,t),18,"(",(e,r,t)=>(r=i(0,41),e?t=>e(t).apply(e.of?.(t),r?r.all?r.all(t):[r(t)]:[]):r||f()),18,",",(e,r,t=i(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||i(14),e?(e,r)=>e[r]++:(e,r)=>++e[r]),15,"-",12,(e,r)=>e-r,"-",15,e=>-e,"--",e=>x(e||i(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,a,n,...t]=t,o;)g(o,a,n);export{d as default};
1
+ let e,r,t,a,o,d,l,n=(a,o=(r=a,e=0,t=[],!(a=i())||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)},i=(r=0,t,a,o,d,l)=>{for(;(a=p())&&(d=(l=c[a])&&l(o,r)||!o&&u());)o=d;return t&&(a==t?e++:h()),o},p=t=>{for(;(t=r.charCodeAt(e))<=32;)e++;return t},u=(e=s(f),r)=>e?(r=r=>r[e],t.push(e),r.id=()=>e,r):0,c=[],g=n.set=(t,a,o=32,d=t.charCodeAt(0),l=t.length,n=c[d],h=o.length||([o,a]=[a,o],0),s=t.toUpperCase()!==t,p=(h>1?(e,r)=>e&&(r=i(a))&&(e.length||r.length?t=>o(e(t),r(t),e.id?.(t),r.id?.(t)):(e=o(e(),r()),()=>e)):h?e=>!e&&(e=i(a-1))&&(r=>o(e(r))):o))=>c[d]=(o,d,h=e)=>d<a&&(l<2||r.substr(e,l)==t)&&(!s||!f(r.charCodeAt(e+l)))&&(e+=l,p(o,d))||(e=h,n&&n(o,d)),C=e=>e>=48&&e<=57,A=t=>(t&&h("Unexpected number"),t=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=>r(e.of?e.of(t):t,e.id(t));for(o=48;o<=57;)c[o++]=A;for(a=['"',e=>(e=e?h("Unexpected string"):s((e=>e-34)),s()||h("Bad string"),()=>e),,".",(e,r)=>(p(),r=s(f)||h(),l=t=>e(t)[r],l.id=()=>r,l.of=e,l),18,".",e=>!e&&A(s(-1)),,"[",(e,r,t)=>e&&(r=i(0,93)||h(),(t=t=>e(t)[r(t)]).id=r,t.of=e,t),18,"(",(e,r,t)=>(r=i(0,41),e?t=>e(t).apply(e.of?.(t),r?r.all?r.all(t):[r(t)]:[]):r||h()),18,",",(e,r,t=i(1),a=(r=>(e(r),t(r))))=>(t?a.all=e.all?r=>[...e.all(r),t(r)]:r=>[e(r),t(r)]:h("Skipped argument"),a),1,"|",6,(e,r)=>e|r,"||",4,(e,r)=>e||r,"&",8,(e,r)=>e&r,"&&",5,(e,r)=>e&&r,"^",7,(e,r)=>e^r,"==",9,(e,r)=>e==r,"!=",9,(e,r)=>e!=r,">",10,(e,r)=>e>r,">=",10,(e,r)=>e>=r,">>",11,(e,r)=>e>>r,">>>",11,(e,r)=>e>>>r,"<",10,(e,r)=>e<r,"<=",10,(e,r)=>e<=r,"<<",11,(e,r)=>e<<r,"+",12,(e,r)=>e+r,"+",15,e=>+e,"++",e=>x(e||i(14),e?(e,r)=>e[r]++:(e,r)=>++e[r]),15,"-",12,(e,r)=>e-r,"-",15,e=>-e,"--",e=>x(e||i(14),e?(e,r)=>e[r]--:(e,r)=>--e[r]),15,"!",15,e=>!e,"*",13,(e,r)=>e*r,"/",13,(e,r)=>e/r,"%",13,(e,r)=>e%r];[o,d,l,...a]=a,o;)g(o,d,l);export{n as default};