https://github.com/dy/subscript
Fast and tiny expression parser / evaluator.
https://github.com/dy/subscript
compute dsl eval evaluator expression-evaluator expression-parser jessie jexl jsep justin math math-expression-evaluator math-expression-parser microlanguage template
Last synced: 5 days ago
JSON representation
Fast and tiny expression parser / evaluator.
- Host: GitHub
- URL: https://github.com/dy/subscript
- Owner: dy
- License: isc
- Created: 2021-10-15T04:09:36.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2025-03-13T02:58:18.000Z (11 months ago)
- Last Synced: 2025-04-04T03:03:56.074Z (10 months ago)
- Topics: compute, dsl, eval, evaluator, expression-evaluator, expression-parser, jessie, jexl, jsep, justin, math, math-expression-evaluator, math-expression-parser, microlanguage, template
- Language: JavaScript
- Homepage:
- Size: 1.03 MB
- Stars: 125
- Watchers: 6
- Forks: 4
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
sub ✦ script
Tiny expression parser & evaluator.
[](https://github.com/dy/subscript/actions/workflows/node.js.yml) [](http://npmjs.org/subscript) [](https://bundlephobia.com/package/subscript) [](http://microjs.com/#subscript)
```js
import subscript from 'subscript'
let fn = subscript('a + b * 2')
fn({ a: 1, b: 3 }) // 7
```
* **Fast** — Pratt parser engine, see [benchmarks](#performance)
* **Portable** — universal expression format, see [spec](./spec.md)
* **Extensible** — pluggable syntax, see [playground](https://dy.github.io/subscript/)
* **Metacircular** — can parse and compile itself
* **Safe** — sandboxed, blocks `__proto__`, `constructor`, no global access
## Presets
**Subscript** – common expressions:
```js
import subscript from 'subscript'
subscript('a.b + c * 2')({ a: { b: 1 }, c: 3 }) // 7
```
**Justin** – JSON + expressions + templates + arrows:
```js
import justin from 'subscript/justin.js'
justin('{ x: a?.b ?? 0, y: [1, ...rest] }')({ a: null, rest: [2, 3] })
// { x: 0, y: [1, 2, 3] }
```
**Jessie** – JSON + expressions + statements, functions (JS subset):
```js
import jessie from 'subscript/jessie.js'
let fn = jessie(`
function factorial(n) {
if (n <= 1) return 1
return n * factorial(n - 1)
}
factorial(5)
`)
fn({}) // 120
```
See [docs](./docs.md#presets) for full description.
## Extension
Add operators, literals or custom syntax:
```js
import { binary, operator, compile } from 'subscript/justin.js'
// add intersection operator
binary('∩', 80) // register parser
operator('∩', (a, b) => ( // register compiler
a = compile(a), b = compile(b),
ctx => a(ctx).filter(x => b(ctx).includes(x))
))
```
```js
import justin from 'subscript/justin.js'
justin('[1,2,3] ∩ [2,3,4]')({}) // [2, 3]
```
See [docs.md](./docs.md) for full API.
## Syntax Tree
Expressions parse to a minimal JSON-compatible syntax tree:
```js
import { parse } from 'subscript'
parse('a + b * 2')
// ['+', 'a', ['*', 'b', [, 2]]]
```
Three forms:
```js
'x' // identifier — resolve from context
[, value] // literal — return as-is (empty slot = data)
[op, ...args] // operation — apply operator
```
See [spec.md](./spec.md).
## Safety
Blocked by default:
- `__proto__`, `__defineGetter__`, `__defineSetter__`
- `constructor`, `prototype`
- Global access (only context is visible)
```js
subscript('constructor.constructor("alert(1)")()')({})
// undefined (blocked)
```
## Performance
```
Parse 30k: subscript 150ms · justin 183ms · jsep 270ms · expr-eval 480ms · jexl 1056ms
Eval 30k: new Function 7ms · subscript 15ms · jsep+eval 30ms · expr-eval 72ms
```
## Utils
### Codegen
Convert tree back to code:
```js
import { codegen } from 'subscript/util/stringify.js'
codegen(['+', ['*', 'min', [,60]], [,'sec']])
// 'min * 60 + "sec"'
```
### Bundle
Bundle imports into a single file:
```js
// Node.js
import { bundleFile } from 'subscript/util/bundle.js'
console.log(await bundleFile('jessie.js'))
// Browser / custom sources
import { bundle } from 'subscript/util/bundle.js'
console.log(await bundle('main.js', {
'main.js': `import { x } from './lib.js'; export default x * 2`,
'lib.js': `export const x = 21`
}))
// → "const x = 21;\nexport { x as default }"
```
## Used by
* [jz](https://github.com/dy/jz) — JS subset → WASM compiler