-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathsubscript.js
More file actions
56 lines (49 loc) · 2.1 KB
/
subscript.js
File metadata and controls
56 lines (49 loc) · 2.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
* 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;