UNPKG

@effectful/transducers-loose

Version:

@effectful/transducers built with faster generators

209 lines (204 loc) 6.31 kB
"use strict"; exports.__esModule = true; exports.emitConst = emitConst; exports.parse = parse; exports.template = template; exports.toks = toks; exports.toksRaw = toksRaw; var _parser = require("@babel/parser"); var _core = require("./core"); var _types = require("./types"); var M = require("@effectful/es-rt/opts/loose"); const memo = new Map(); function parse(jsCode) { return (0, _parser.parse)(jsCode, { sourceType: "module", allowReturnOutsideFunction: true, plugins: ["dynamicImport"] }); } /** * parses string `s` and outputs its token value * following prefixes are supported in the string * - '^' - module's top level * - '=' - the string is JS expression * - '*' - the string is a list of statements * - '>' - variable declarator * - otherwise it is considered to be a statement * * if in the code in `s` identifier starts with `$` it has special meaning * if next character is: * - digit - it will replaces symbol of identifier by `syms` * at the corresponding position (1 based) * - `$` - keeps only one `$` * - `I` - unshifts a symbol from `syms` and replaces the symbol of * the identifier */ function toks(pos, s, ...syms) { return toksRaw(pos, s, syms); } /** same as `toks` but avoiding variable length arguments */ function toksRaw(pos, s, syms) { if (s.substr != null) { let r = memo.get(s); if (r == null) { let topLevel = false; if (s[0] === "^") { s = s.slice(1); topLevel = true; } let mod = null; let js = s; switch (s[0]) { case "=": // expression case "*": // list of statements case ">": // var declarator mod = s[0]; js = s.slice(1); } js = topLevel ? js : `function main() { for(;;) {${js}} }`; const b = parse(js); const body = topLevel ? b.program.body : b.program.body[0].body.body[0].body.body; switch (mod) { case "=": r = body[0].expression; break; case ">": const s = body[0].expression; r = { type: "VariableDeclarator", id: s.left, init: s.right }; break; case "*": break; default: r = body[0]; } if (mod !== "=" && mod !== ">") { if (mod === "*") { r = body; } else { r = body[0]; } } memo.set(s, r = (0, _core.produceNode)(pos, r)); } s = r; } function replace(root) { if (!syms || !syms.length) return root; var loop = M.iterator((0, _core.dft)(root)); for (; !(loop = loop.step()).done;) { const i = loop.value; const { node } = i; if (!Array.isArray(node)) node.loc = node.start = node.end = null; if (i.type === _types.Tag.Identifier && node.name[0] === "$") { const { name } = node; const rest = name.substr(1); switch (rest[0]) { case "$": node.name = rest; break; case "I": node.name = (i.sym = syms.shift()).name; break; default: if (rest.length && !isNaN(rest)) node.name = (i.sym = syms[rest - 1]).name; } } } return root; } s = (0, _core.clone)(s); s.pos = pos; return replace(s); } /** * same as `toks` but extends formats with `$_`/'$E` identifier to mark * a position where the output stream must be stopped * * returns an array with the first item containing the whole node * and the next ones are placeholders * * in expression statements $E focuses only the expression, while `$_` is for * the statement, in other contexts they do matches the same */ function template(pos, code, ...syms) { const root = toks(pos, code, ...syms); const ret = [root]; var loop = M.iterator((0, _core.dft)(root)); for (; !(loop = loop.step()).done;) { const i = loop.value; switch (i.type) { case _types.Tag.ExpressionStatement: const child = i.firstChild; if (child.type === _types.Tag.Identifier && child.node.name === "$_") ret.push(i); break; case _types.Tag.Identifier: const name = i.node.name; if (name === "$_" || name === "$E") ret.push(i); break; } } return ret; } /** converts compile time constant into a list of tokens */ function emitConst(pos, value) { if (value === void 0) { const res = (0, _core.tok)(pos, _types.Tag.UnaryExpression, { operator: "void" }); (0, _core.append)(res, (0, _core.num)(_types.Tag.argument, 0)); return res; } switch (typeof value) { case "number": return (0, _core.num)(pos, value); case "string": return (0, _core.tok)(pos, _types.Tag.StringLiteral, { value }); case "boolean": return (0, _core.tok)(pos, _types.Tag.BooleanLiteral, { value }); case "object": if (value === null) return (0, _core.node)(pos, _types.Tag.NullLiteral); if (value.emitConstMethod) return value.emitConstMethod(pos); if (Array.isArray(value)) { const res = (0, _core.node)(pos, _types.Tag.ArrayExpression); const els = (0, _core.arr)(_types.Tag.elements); var loop = M.iterator(value); for (; !(loop = loop.step()).done;) { const j = loop.value; (0, _core.append)(els, emitConst(_types.Tag.push, j)); } (0, _core.append)(res, els); return res; } const res = (0, _core.node)(pos, _types.Tag.ObjectExpression); const props = (0, _core.arr)(_types.Tag.properties); for (const name in value) { const v = value[name]; if (v === void 0) continue; const prop = (0, _core.node)(_types.Tag.push, _types.Tag.ObjectProperty); (0, _core.append)(prop, (0, _core.tok)(_types.Tag.key, _types.Tag.Identifier, { name })); (0, _core.append)(prop, emitConst(_types.Tag.value, v)); (0, _core.append)(props, prop); } (0, _core.append)(res, props); return res; } return (0, _core.append)((0, _core.node)(pos, _types.Tag.UnaryExpression, { operator: "void" }), (0, _core.num)(_types.Tag.argument, 0)).firstChild; }