UNPKG

subscript

Version:

Modular expression parser & evaluator

57 lines (49 loc) 2.15 kB
/** * subscript: Minimal expression parser + compiler * * Usage: * subscript`a + b`(ctx) - template tag, returns compiled evaluator * subscript`a + ${x}`(ctx) - interpolations: primitives, objects, AST nodes * subscript('a + b')(ctx) - direct call (no caching) * * For more features: * import { parse, compile } from 'subscript/justin.js' // + JSON, arrows, templates * import { parse, compile } from 'subscript/jessie.js' // + statements, functions * * For parse-only (no eval bundle weight): * import { parse } from 'subscript/feature/subscript.js' * import { parse } from 'subscript/feature/justin.js' * import { parse } from 'subscript/feature/jessie.js' */ import './feature/subscript.js'; import './eval/subscript.js'; import { parse, compile } from './parse.js'; export * from './parse.js'; // Cache for compiled templates (keyed by template strings array reference) const cache = new WeakMap(); // Template tag: subscript`a + b` or subscript`a + ${x}` const subscript = (strings, ...values) => // Direct call subscript('code') - strings is just a string typeof strings === 'string' ? compile(parse(strings)) : // Template literal - use cache cache.get(strings) || cache.set(strings, compileTemplate(strings, values)).get(strings); // Compile template with placeholders (using Private Use Area chars) const PUA = 0xE000; const compileTemplate = (strings, values) => { const code = strings.reduce((acc, s, i) => acc + (i ? String.fromCharCode(PUA + i - 1) : '') + s, ''); const ast = parse(code); const inject = node => { if (typeof node === 'string' && node.length === 1) { let i = node.charCodeAt(0) - PUA, v; if (i >= 0 && i < values.length) return v = values[i], isAST(v) ? v : [, v]; } return Array.isArray(node) ? node.map(inject) : node; }; return compile(inject(ast)); }; // Detect AST node vs regular value // AST: string (identifier), or array with string/undefined first element const isAST = v => typeof v === 'string' || (Array.isArray(v) && (typeof v[0] === 'string' || v[0] === undefined)); export default subscript;