subscript 7.3.1 → 7.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,376 +1,337 @@
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
-
3
- _Subscript_ is expression evaluator / microlanguage with [common syntax](https://en.wikipedia.org/wiki/Comparison_of_programming_languages_(syntax)).<br/>
4
-
5
- * 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>
6
- * :rocket: Fast [performance](#performance)
7
- * Configurable & extensible
8
- * Trivial to use
9
-
10
- ```js
11
- import script, { parse, compile } from './subscript.js'
12
-
13
- // create expression evaluator
14
- let fn = script('a.b + c(d - 1)')
15
- fn({ a: { b:1 }, c: x => x * 2, d: 3 }) // 5
16
-
17
- // or
18
- // parse expression
19
- let tree = parse('a.b + c')
20
- tree // ['+', ['.', 'a', 'b'], 'c']
21
-
22
- // compile tree to evaluable function
23
- let evaluate = compile(tree)
24
- ```
25
-
26
- ## Motivation
27
-
28
- _Subscript_ is designed to be useful for:
29
-
30
- * templates (perfect match with [template parts](https://github.com/github/template-parts), see [templize](https://github.com/spectjs/templize))
31
- * expressions evaluators, calculators
32
- * configurable subsets of languages (eg. [justin](#justin))
33
- * pluggable/mock language features (eg. pipe operator)
34
- * sandboxes, playgrounds, safe eval
35
- * custom DSL <!-- see sonr, mineural -->
36
-
37
- _Subscript_ has [2.8kb](https://npmfs.com/package/subscript/7.0.0/subscript.min.js) footprint, compared to [11.4kb](https://npmfs.com/package/jsep/1.2.0/dist/jsep.min.js) _jsep_ + [4.5kb](https://npmfs.com/package/expression-eval/5.0.0/dist/expression-eval.module.js) _expression-eval_, with better test coverage and better performance.
38
-
39
-
40
- ## Design
41
-
42
- Default operators are (same as JS precedence order):
43
-
44
- * `( a, b, c )`
45
- * `a.b`, `a[b]`, `a(b, c)`
46
- * `a++`, `a--` unary postfix
47
- * `!a`, `+a`, `-a`, `++a`, `--a` unary prefix
48
- * `a * b`, `a / b`, `a % b`
49
- * `a + b`, `a - b`
50
- * `a << b`, `a >> b`, `a >>> b`
51
- * `a < b`, `a <= b`, `a > b`, `a >= b`
52
- * `a == b`, `a != b`
53
- * `a & b`
54
- * `a ^ b`
55
- * `a | b`
56
- * `a && b`
57
- * `a || b`
58
- * `a , b`
59
-
60
- Default literals:
61
-
62
- * `"abc"` strings
63
- * `1.2e+3` numbers
64
-
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, allowSkip=false)` − register n-ary (sequence) operator, optionally allowing skipping args.
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.
74
-
75
- ```js
76
- import script, { operator, unary, binary, token } from './subscript.js'
77
-
78
- // add ~ unary operator with precedence 15
79
- unary('~', 15)
80
- operator('~', a => ~a)
81
-
82
- // add === binary operator with precedence 9
83
- binary('===', 9)
84
- operator('===', (a, b) => a===b)
85
-
86
- // add literals
87
- token('true', 20, a => ['',true])
88
- token('false', 20, a => ['',false])
89
- operator('', a => ctx => a[1]])
90
- ```
91
-
92
- See [subscript.js](subscript.js) or [justin.js](./justin.js) for examples.
93
-
94
-
95
- ## Syntax tree
96
-
97
- Subscript exposes separate `./parse.js` and `./compile.js` entries. Parser builds AST, compiler converts it to evaluable function.
98
-
99
- AST has simplified lispy calltree structure (inspired by [frisk](https://ghub.io/frisk)), opposed to [ESTree](https://github.com/estree/estree):
100
-
101
- * is not limited to particular language, can be cross-compiled;
102
- * reflects execution sequence, rather than code layout;
103
- * has minimal possible overhead, directly maps to operators;
104
- * simplifies manual evaluation and debugging;
105
- * has conventional form and one-liner docs:
106
-
107
- ```js
108
- import { compile } from 'subscript.js'
109
-
110
- const fn = compile(['+', ['*', 'min', ['',60]], ['','sec']])
111
-
112
- fn({min: 5}) // min*60 + "sec" == "300sec"
113
- ```
114
-
115
- <!--
116
- Operators can be extended via .
117
-
118
- ```js
119
- import script from 'subscript.js'
120
-
121
- script.set('|', 10, ( a, b ) => a.pipe(b))
122
-
123
- let evaluate = script(`
124
- interval(350)
125
- | take(25)
126
- | map(gaussian)
127
- | "•".repeat(Math.floor(it * 65)))
128
- `)
129
- evaluate({ Math, map, take, interval, gaussian })
130
- ```
131
-
132
- Literals are extensible by providing custom parser to `lookup`, can be added support of _booleans_, function calls, prop chains, groups, _regexes_, _strings_, _numbers_ and any other constructs.
133
-
134
- ```js
135
- import script from 'subscript.js'
136
-
137
- script.literal.unshift(c => skip('this') && {x:1})
138
- script`this.x`() // 1
139
- ```
140
-
141
- ### Identifiers
142
-
143
- Identifiers include
144
-
145
- ### Spaces/comments
146
-
147
- Comments can be added via extending `parse.space`.
148
- -->
149
-
150
- ## Justin
151
-
152
- _Justin_ is minimal JS subset − JSON with JS expressions (see original [thread](https://github.com/endojs/Jessie/issues/66)).<br/>
153
-
154
- It extends _subscript_ with:
155
-
156
- + `===`, `!==` operators
157
- + `**` exponentiation operator (right-assoc)
158
- + `~` bit inversion operator
159
- + `'` strings
160
- + `?:` ternary operator
161
- + `?.` optional chain operator
162
- + `??` nullish coalesce operator
163
- + `[...]` Array literal
164
- + `{...}` Object literal
165
- + `in` binary
166
- + `;` expression separator
167
- + `//`, `/* */` comments
168
- + `true`, `false`, `null`, `undefined` literals
169
- <!-- + `...x` unary operator -->
170
- <!-- + strings interpolation -->
171
-
172
- ```js
173
- import jstin from 'subscript/justin.js'
174
-
175
- let xy = jstin('{ x: 1, "y": 2+2 }["x"]')
176
- xy() // 1
177
- ```
178
-
179
- <!--
180
- ## Ideas
181
-
182
- These are custom DSL operators snippets for your inspiration:
183
-
184
-
185
- ```html
186
- template-parts proposal
187
- <template id="timer">
188
- <time datetime="{{ date.toUTCString() }}">{{ date.toLocaleTimeString() }}</time>
189
- </template>
190
- ```
191
-
192
- // a.b.c
193
- // (node, c) => c === PERIOD ? (index++, space(), ['.', node, '"'+id()+'"']) : node,
194
-
195
- // a[b][c]
196
- // (node, c) => c === OBRACK ? (index++, node=['.', node, expr(CBRACK)], index++, node) : node,
197
-
198
- // a(b)(c)
199
- // (node, c, arg) => c === OPAREN ? (
200
- // index++, arg=expr(CPAREN),
201
- // node = Array.isArray(arg) && arg[0]===',' ? (arg[0]=node, arg) : arg == null ? [node] : [node, arg],
202
- // index++, node
203
- // ) : node,
204
-
205
- <details>
206
- <summary>Keyed arrays <code>[a:1, b:2, c:3]</code></summary>
207
-
208
- ```js
209
-
210
- ```
211
- </details>
212
-
213
- <details>
214
- <summary>`7!` (factorial)</summary>
215
-
216
- ```js
217
- ```
218
-
219
- </details>
220
- <details>
221
- <summary>`5s`, `5rem` (units)</summary>
222
-
223
- ```js
224
- ```
225
-
226
- </details>
227
- <details>
228
- <summary>`?`, `?.`, `??`</summary>
229
-
230
- ```js
231
- ```
232
-
233
- </details>
234
- <details>
235
- <summary>`arrᵀ` - transpose,</summary>
236
-
237
- ```js
238
- ```
239
-
240
- </details>
241
- <details>
242
- <summary>`int 5` (typecast)</summary>
243
-
244
- ```js
245
- ```
246
-
247
- </details>
248
- <details>
249
- <summary>`$a` (param expansion)</summary>
250
-
251
- ```js
252
- ```
253
-
254
- </details>
255
- <details>
256
- <summary>`1 to 10 by 2`</summary>
257
-
258
- ```js
259
- ```
260
-
261
- </details>
262
- <details>
263
- <summary>`a if b else c`</summary>
264
-
265
- ```js
266
- ```
267
-
268
- </details>
269
- <details>
270
- <summary>`a, b in c`</summary>
271
-
272
- ```js
273
- ```
274
-
275
- </details>
276
- <details>
277
- <summary>`a.xyz` swizzles</summary>
278
-
279
- ```js
280
- ```
281
-
282
- </details>
283
- <details>
284
- <summary>vector operators</summary>
285
-
286
- ```js
287
- ```
288
-
289
- </details>
290
- <details>
291
- <summary>set operators</summary>
292
-
293
- ```js
294
- ```
295
-
296
- </details>
297
- <details>
298
- <summary>polynomial operators</summary>
299
-
300
- ```js
301
- ```
302
-
303
- </details>
304
-
305
- like versions, units, hashes, urls, regexes etc
306
-
307
- 2a as `2*a`
308
-
309
- string interpolation ` ${} 1 ${} `
310
-
311
- keyed arrays? [a:1, b:2, c:3]
312
-
313
- Examples: sonr, template-parts, neural-chunks
314
- -->
315
-
316
- ## Performance
317
-
318
- Subscript shows relatively good performance within other evaluators:
319
-
320
- ```
321
- 1 + (a * b / c % d) - 2.0 + -3e-3 * +4.4e4 / f.g[0] - i.j(+k == 1)(0)
322
- ```
323
-
324
- Parse 30k times:
325
-
326
- ```
327
- es-module-lexer: 50ms 🥇
328
- subscript: ~150 ms 🥈
329
- justin: ~183 ms
330
- jsep: ~270 ms 🥉
331
- jexpr: ~297 ms
332
- mr-parser: ~420 ms
333
- expr-eval: ~480 ms
334
- math-parser: ~570 ms
335
- math-expression-evaluator: ~900ms
336
- jexl: ~1056 ms
337
- mathjs: ~1200 ms
338
- new Function: ~1154 ms
339
- ```
340
-
341
- Eval 30k times:
342
- ```
343
- new Function: ~7 ms 🥇
344
- subscript: ~15 ms 🥈
345
- justin: ~17 ms
346
- jexpr: ~23 ms 🥉
347
- jsep (expression-eval): ~30 ms
348
- math-expression-evaluator: ~50ms
349
- expr-eval: ~72 ms
350
- jexl: ~110 ms
351
- mathjs: ~119 ms
352
- mr-parser: -
353
- math-parser: -
354
- ```
355
-
356
- ## Alternatives
357
-
358
- * [jexpr](https://github.com/justinfagnani/jexpr)
359
- * [jsep](https://github.com/EricSmekens/jsep)
360
- * [jexl](https://github.com/TomFrost/Jexl)
361
- * [mozjexl](https://github.com/mozilla/mozjexl)
362
- * [expr-eval](https://github.com/silentmatt/expr-eval)
363
- * [expression-eval](https://github.com/donmccurdy/expression-eval)
364
- * [string-math](https://github.com/devrafalko/string-math)
365
- * [nerdamer](https://github.com/jiggzson/nerdamer)
366
- * [math-codegen](https://github.com/mauriciopoppe/math-codegen)
367
- * [math-parser](https://www.npmjs.com/package/math-parser)
368
- * [math.js](https://mathjs.org/docs/expressions/parsing.html)
369
-
370
- ## JS engines
371
-
372
- * [engine262](https://github.com/engine262/engine262)
373
- * [Jessie](https://github.com/endojs/Jessie)
374
- * [xst](https://github.com/Moddable-OpenSource/moddable-xst)
375
-
376
- <p align=center><a href="https://github.com/krsnzd/license/">🕉</a></p>
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
+
3
+ _Subscript_ is expression evaluator / microlanguage with [common syntax](https://en.wikipedia.org/wiki/Comparison_of_programming_languages_(syntax)) (JavaScript, Java, C, C++, Rust, Go, Python, Kotlin etc).<br/>
4
+
5
+ * 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>
6
+ * :rocket: Fast [performance](#performance)
7
+ * Configurable & extensible
8
+ * Trivial to use
9
+
10
+ ```js
11
+ import createFn, { parse, compile } from './subscript.js'
12
+
13
+ // create expression evaluator
14
+ let fn = createFn('a.b + c(d - 1)')
15
+ fn({ a: { b:1 }, c: x => x * 2, d: 3 }) // 5
16
+
17
+ // or
18
+ // parse expression
19
+ let tree = parse('a.b + c')
20
+ tree // ['+', ['.', 'a', 'b'], 'c']
21
+
22
+ // compile tree to evaluable function
23
+ fn = compile(tree)
24
+ fn({a:{b:1}, c:2}) // 3
25
+ ```
26
+
27
+ ## Motivation
28
+
29
+ _Subscript_ is designed to be useful for:
30
+
31
+ * templates (perfect match with [template parts](https://github.com/github/template-parts), see [templize](https://github.com/spectjs/templize))
32
+ * expressions evaluators, calculators
33
+ * configurable subsets of languages (eg. [justin](#justin))
34
+ * pluggable/mock language features (eg. pipe operator)
35
+ * sandboxes, playgrounds, safe eval
36
+ * custom DSL <!-- see sonr, mineural -->
37
+
38
+ _Subscript_ has [2.8kb](https://npmfs.com/package/subscript/7.0.0/subscript.min.js) footprint, compared to [11.4kb](https://npmfs.com/package/jsep/1.2.0/dist/jsep.min.js) _jsep_ + [4.5kb](https://npmfs.com/package/expression-eval/5.0.0/dist/expression-eval.module.js) _expression-eval_, with better test coverage and better performance.
39
+
40
+
41
+ ## Operators
42
+
43
+ <small>↑ precedence order</small>
44
+
45
+ * `( a, b, c )`
46
+ * `a.b`, `a[b]`, `a(b, c)`
47
+ * `a++`, `a--` unary postfix
48
+ * `!a`, `+a`, `-a`, `++a`, `--a` unary prefix
49
+ * `a * b`, `a / b`, `a % b`
50
+ * `a + b`, `a - b`
51
+ * `a << b`, `a >> b`, `a >>> b`
52
+ * `a < b`, `a <= b`, `a > b`, `a >= b`
53
+ * `a == b`, `a != b`
54
+ * `a & b`
55
+ * `a ^ b`
56
+ * `a | b`
57
+ * `a && b`
58
+ * `a || b`
59
+ * `a , b`
60
+
61
+ ## Literals
62
+
63
+ * `"abc"` strings
64
+ * `1.2e+3` numbers
65
+
66
+ ## Extending
67
+
68
+ Operators/tokens can be extended via:
69
+
70
+ * `unary(str, precedence, postfix=false)` − register unary operator, either prefix or postfix.
71
+ * `binary(str, precedence, rightAssoc=false)` − register binary operator, optionally right-associative.
72
+ * `nary(str, precedence, allowSkip=false)` − register n-ary (sequence) operator, optionally allowing skipping args.
73
+ * `token(str, precedence, map)` − register custom token or literal. `map` takes last token argument and returns calltree node.
74
+ * `operator(str, fn)` − register evaluator for operator. `fn` takes node arguments and returns evaluator function.
75
+
76
+ ```js
77
+ import script, { operator, unary, binary, token } from './subscript.js'
78
+
79
+ // add ~ unary operator with precedence 15
80
+ unary('~', 15)
81
+ operator('~', a => ~a)
82
+
83
+ // add === binary operator with precedence 9
84
+ binary('===', 9)
85
+ operator('===', (a, b) => a===b)
86
+
87
+ // add boolean literals
88
+ token('true', 20, prev => ['',true])
89
+ token('false', 20, prev => ['',false])
90
+ operator('', boolNode => ctx => boolNode[1]])
91
+ ```
92
+
93
+ See [subscript.js](subscript.js) or [justin.js](./justin.js) for examples.
94
+
95
+
96
+ ## Syntax tree
97
+
98
+ Subscript exposes separate `./parse.js` and `./compile.js` entries. Parser builds AST, compiler converts it to evaluable function.
99
+
100
+ AST has simplified lispy calltree structure (inspired by [frisk](https://ghub.io/frisk) / [nisp](https://github.com/ysmood/nisp)), opposed to [ESTree](https://github.com/estree/estree):
101
+
102
+ * is not limited to particular language, can be compiled to different targets;
103
+ * reflects execution sequence, rather than code layout;
104
+ * has minimal possible overhead, directly maps to operators;
105
+ * simplifies manual evaluation and debugging;
106
+ * has conventional form and one-liner docs:
107
+
108
+ ```js
109
+ import { compile } from 'subscript.js'
110
+
111
+ const fn = compile(['+', ['*', 'min', ['',60]], ['','sec']])
112
+
113
+ fn({min: 5}) // min*60 + "sec" == "300sec"
114
+ ```
115
+
116
+ ## Justin
117
+
118
+ _Justin_ is minimal JS subset − JSON with JS expressions (see original [thread](https://github.com/endojs/Jessie/issues/66)).<br/>
119
+
120
+ It extends _subscript_ with:
121
+
122
+ + `===`, `!==` operators
123
+ + `**` exponentiation operator (right-assoc)
124
+ + `~` bit inversion operator
125
+ + `'` strings
126
+ + `?:` ternary operator
127
+ + `?.` optional chain operator
128
+ + `??` nullish coalesce operator
129
+ + `[...]` Array literal
130
+ + `{...}` Object literal
131
+ + `in` binary
132
+ + `;` expression separator
133
+ + `//`, `/* */` comments
134
+ + `true`, `false`, `null`, `undefined` literals
135
+ <!-- + `...x` unary operator -->
136
+ <!-- + strings interpolation -->
137
+
138
+ ```js
139
+ import jstin from 'subscript/justin.js'
140
+
141
+ let xy = jstin('{ x: 1, "y": 2+2 }["x"]')
142
+ xy() // 1
143
+ ```
144
+
145
+ <!--
146
+ ## Ideas
147
+
148
+ These are custom DSL operators snippets for your inspiration:
149
+
150
+
151
+ ```html
152
+ template-parts proposal
153
+ <template id="timer">
154
+ <time datetime="{{ date.toUTCString() }}">{{ date.toLocaleTimeString() }}</time>
155
+ </template>
156
+ ```
157
+
158
+ // a.b.c
159
+ // (node, c) => c === PERIOD ? (index++, space(), ['.', node, '"'+id()+'"']) : node,
160
+
161
+ // a[b][c]
162
+ // (node, c) => c === OBRACK ? (index++, node=['.', node, expr(CBRACK)], index++, node) : node,
163
+
164
+ // a(b)(c)
165
+ // (node, c, arg) => c === OPAREN ? (
166
+ // index++, arg=expr(CPAREN),
167
+ // node = Array.isArray(arg) && arg[0]===',' ? (arg[0]=node, arg) : arg == null ? [node] : [node, arg],
168
+ // index++, node
169
+ // ) : node,
170
+
171
+ <details>
172
+ <summary>Keyed arrays <code>[a:1, b:2, c:3]</code></summary>
173
+
174
+ ```js
175
+
176
+ ```
177
+ </details>
178
+
179
+ <details>
180
+ <summary>`7!` (factorial)</summary>
181
+
182
+ ```js
183
+ ```
184
+
185
+ </details>
186
+ <details>
187
+ <summary>`5s`, `5rem` (units)</summary>
188
+
189
+ ```js
190
+ ```
191
+
192
+ </details>
193
+ <details>
194
+ <summary>`?`, `?.`, `??`</summary>
195
+
196
+ ```js
197
+ ```
198
+
199
+ </details>
200
+ <details>
201
+ <summary>`arrᵀ` - transpose,</summary>
202
+
203
+ ```js
204
+ ```
205
+
206
+ </details>
207
+ <details>
208
+ <summary>`int 5` (typecast)</summary>
209
+
210
+ ```js
211
+ ```
212
+
213
+ </details>
214
+ <details>
215
+ <summary>`$a` (param expansion)</summary>
216
+
217
+ ```js
218
+ ```
219
+
220
+ </details>
221
+ <details>
222
+ <summary>`1 to 10 by 2`</summary>
223
+
224
+ ```js
225
+ ```
226
+
227
+ </details>
228
+ <details>
229
+ <summary>`a if b else c`</summary>
230
+
231
+ ```js
232
+ ```
233
+
234
+ </details>
235
+ <details>
236
+ <summary>`a, b in c`</summary>
237
+
238
+ ```js
239
+ ```
240
+
241
+ </details>
242
+ <details>
243
+ <summary>`a.xyz` swizzles</summary>
244
+
245
+ ```js
246
+ ```
247
+
248
+ </details>
249
+ <details>
250
+ <summary>vector operators</summary>
251
+
252
+ ```js
253
+ ```
254
+
255
+ </details>
256
+ <details>
257
+ <summary>set operators</summary>
258
+
259
+ ```js
260
+ ```
261
+
262
+ </details>
263
+ <details>
264
+ <summary>polynomial operators</summary>
265
+
266
+ ```js
267
+ ```
268
+
269
+ </details>
270
+
271
+ like versions, units, hashes, urls, regexes etc
272
+
273
+ 2a as `2*a`
274
+
275
+ string interpolation ` ${} 1 ${} `
276
+
277
+ keyed arrays? [a:1, b:2, c:3]
278
+
279
+ Examples: sonr, template-parts, neural-chunks
280
+ -->
281
+
282
+ ## Performance
283
+
284
+ Subscript shows relatively good performance within other evaluators. Example expression:
285
+
286
+ ```
287
+ 1 + (a * b / c % d) - 2.0 + -3e-3 * +4.4e4 / f.g[0] - i.j(+k == 1)(0)
288
+ ```
289
+
290
+ Parse 30k times:
291
+
292
+ ```
293
+ es-module-lexer: 50ms 🥇
294
+ subscript: ~150 ms 🥈
295
+ justin: ~183 ms
296
+ jsep: ~270 ms 🥉
297
+ jexpr: ~297 ms
298
+ mr-parser: ~420 ms
299
+ expr-eval: ~480 ms
300
+ math-parser: ~570 ms
301
+ math-expression-evaluator: ~900ms
302
+ jexl: ~1056 ms
303
+ mathjs: ~1200 ms
304
+ new Function: ~1154 ms
305
+ ```
306
+
307
+ Eval 30k times:
308
+ ```
309
+ new Function: ~7 ms 🥇
310
+ subscript: ~15 ms 🥈
311
+ justin: ~17 ms
312
+ jexpr: ~23 ms 🥉
313
+ jsep (expression-eval): ~30 ms
314
+ math-expression-evaluator: ~50ms
315
+ expr-eval: ~72 ms
316
+ jexl: ~110 ms
317
+ mathjs: ~119 ms
318
+ mr-parser: -
319
+ math-parser: -
320
+ ```
321
+
322
+ ## Alternatives
323
+
324
+ * [jexpr](https://github.com/justinfagnani/jexpr)
325
+ * [jsep](https://github.com/EricSmekens/jsep)
326
+ * [jexl](https://github.com/TomFrost/Jexl)
327
+ * [mozjexl](https://github.com/mozilla/mozjexl)
328
+ * [expr-eval](https://github.com/silentmatt/expr-eval)
329
+ * [expression-eval](https://github.com/donmccurdy/expression-eval)
330
+ * [string-math](https://github.com/devrafalko/string-math)
331
+ * [nerdamer](https://github.com/jiggzson/nerdamer)
332
+ * [math-codegen](https://github.com/mauriciopoppe/math-codegen)
333
+ * [math-parser](https://www.npmjs.com/package/math-parser)
334
+ * [math.js](https://mathjs.org/docs/expressions/parsing.html)
335
+ * [nx-compile](https://github.com/nx-js/compiler-util)
336
+
337
+ <p align=center><a href="https://github.com/krsnzd/license/">🕉</a></p>