Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

dy/subscript

Repository files navigation

subscript

Tiny expression parser & evaluator.

import subscript from 'subscript'
let fn = subscript('a + b * 2')
fn({ a: 1, b: 3 }) // 7
  • Safe — sandboxed, blocks __proto__, constructor, no global access
  • Fast — Pratt parser engine, see benchmarks
  • Portable — universal expression format, see spec
  • Extensible — pluggable syntax, see DSL builder
  • Metacircular — can parse and compile itself

Presets

Subscript: common expressions:

import subscript from 'subscript'
subscript('a.b + c * 2')({ a: { b: 1 }, c: 3 }) // 7

Justin: JSON + expressions + templates + arrows:

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):

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 for full description.

Extension

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))
))
import justin from 'subscript/justin.js'
justin('[1,2,3] ∩ [2,3,4]')({}) // [2, 3]

See docs.md for full API.

Syntax Tree

Expressions parse to a minimal JSON-compatible syntax tree:

import { parse } from 'subscript'
parse('a + b * 2')
// ['+', 'a', ['*', 'b', [, 2]]]

Three forms:

'x' // identifier — resolve from context
[, value] // literal — return as-is (empty slot = data)
[op, ...args] // operation — apply operator

See spec.md.

Safety

Blocked by default:

  • __proto__, __defineGetter__, __defineSetter__
  • constructor, prototype
  • Global access (only context is visible)
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:

import { codegen } from 'subscript/util/stringify.js'
codegen(['+', ['*', 'min', [,60]], [,'sec']])
// 'min * 60 + "sec"'

Bundle

Bundle imports into a single file:

import { bundle } from 'subscript/util/bundle.js'
const code = await bundle('subscript/jessie.js')
// → self-contained ES module

Used by

  • jz — JS subset → WASM compiler

AltStyle によって変換されたページ (->オリジナル) /