UNPKG

@effectful/transducers

Version:

JS syntax transformation framework for @effectful/js

1,619 lines (1,548 loc) 43 kB
"use strict"; exports.__esModule = true; var _exportNames = { optsScope: true, optsScopeLift: true, setOpts: true, getOpts: true, parse: true, toks: true, skip: true, setType: true, setPos: true, repos: true, reposOne: true, reposOneArr: true, Subst: true, completeSubst: true, toArray: true, result: true, tillLevel: true, toBlockBody: true, inBlockBody: true, fileBody: true, hasAnnot: true, clone: true, till: true, find: true, Opts: true, UpdateOpts: true, concat: true, prepend: true, share: true, wrap: true, stage: true, checkpointLazy: true, checkpoint: true, babelBridge: true, babelPreset: true, babelPlugin: true, transform: true, tee: true, makeExpr: true, makeStmt: true, makeExprPass: true, removeEmptyBlocks: true, adjustFieldType: true, adjustFieldTypeSimple: true, la: true, select: true, enableIf: true, packed: true, pack: true, unpack: true, cleanEmptyExprs: true, time: true, timeEnd: true, Wrapper: true, tillVal: true, single: true, auto: true }; exports.UpdateOpts = exports.Subst = exports.Opts = void 0; exports.Wrapper = Wrapper; exports.adjustFieldTypeSimple = exports.adjustFieldType = void 0; exports.auto = auto; exports.babelBridge = void 0; exports.babelPlugin = babelPlugin; exports.babelPreset = babelPreset; exports.cleanEmptyExprs = exports.checkpointLazy = exports.checkpoint = void 0; exports.clone = clone; exports.completeSubst = completeSubst; exports.concat = concat; exports.enableIf = void 0; exports.fileBody = fileBody; exports.find = void 0; exports.getOpts = getOpts; exports.hasAnnot = hasAnnot; exports.inBlockBody = inBlockBody; exports.la = la; exports.makeExpr = void 0; exports.makeExprPass = makeExprPass; exports.makeStmt = void 0; exports.optsScope = optsScope; exports.optsScopeLift = optsScopeLift; exports.packed = exports.pack = void 0; exports.parse = parse; exports.prepend = prepend; exports.removeEmptyBlocks = removeEmptyBlocks; exports.repos = repos; exports.reposOne = reposOne; exports.reposOneArr = reposOneArr; exports.result = result; exports.select = void 0; exports.setOpts = setOpts; exports.setPos = setPos; exports.setType = setType; exports.share = share; exports.single = single; exports.skip = skip; exports.stage = stage; exports.tee = tee; exports.till = till; exports.tillLevel = tillLevel; exports.tillVal = tillVal; exports.timeEnd = exports.time = void 0; exports.toArray = toArray; exports.toBlockBody = toBlockBody; exports.toks = toks; exports.transform = void 0; exports.unpack = unpack; exports.wrap = void 0; var _core = require("./core"); var _combinators = require("./v2/combinators"); Object.keys(_combinators).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; if (key in exports && exports[key] === _combinators[key]) return; exports[key] = _combinators[key]; }); var T = require("@babel/types"); var _parser = require("@babel/parser"); function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } const BROWSER_DEBUG = typeof window !== "undefined" && window.chrome || process.env.EFFECTFUL_DEBUG; let _opts = {}; let leanWrap; function LeanIterator(iter) { this.value = void 0; this.done = false; this.iter = iter[Symbol.iterator](); } LeanIterator.prototype[Symbol.iterator] = function () { return this.iter; }; LeanIterator.prototype.step = function step(v) { var next = this.iter.next(v); this.done = next.done; this.value = next.value; return this; }; if (Symbol.effectfulIterator) { leanWrap = function leanWrap(cont) { return cont[Symbol.effectfulIterator] ? cont[Symbol.effectfulIterator]() : new LeanIterator(cont); }; } else { leanWrap = function leanWrap(cont) { return new LeanIterator(cont); }; } /** runs `fun` and reset global options after its exit */ function optsScope(fun) { const save = _opts; try { return fun(); } finally { _opts = save; } } /** returns function calling `fun` and reseting global options after exit */ function optsScopeLift(fun) { return function () { const save = _opts; try { return fun.apply(this, arguments); } finally { _opts = save; } }; } /** sets global options */ function setOpts(opts) { _opts = opts; } function getOpts() { return _opts; } const memo = new Map(); function parse(jsCode) { return (0, _parser.parse)(jsCode, { sourceType: "module", allowReturnOutsideFunction: true, plugins: ["dynamicImport"] }); } /** * parses string `s` and outuputs its token stream at `pos` * 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) { if (Array.isArray(s)) yield* clone(s); let value; if (syms.length && syms[0].name == null) value = syms.shift(); 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; if (!mod === "*") (0, _core.invariant)(body.length === 1); switch (mod) { case "=": (0, _core.invariant)(body[0].type === "ExpressionStatement"); r = body[0].expression; break; case ">": (0, _core.invariant)(body[0].type === "ExpressionStatement"); const s = body[0].expression; (0, _core.invariant)(s.type === "AssignmentExpression"); r = T.variableDeclarator(s.left, s.right); break; case "*": break; default: r = body[0]; } if (mod !== "=" && mod !== ">") { if (mod === "*") { r = body; } else { r = body[0]; } } memo.set(s, r); } s = r; } function* replace(s) { if (!syms.length) { yield* s; return s; } for (const i of s) { if (i.enter) { const { node } = i.value; node.loc = node.start = node.end = null; if (i.type === _core.Tag.Identifier && i.value.node.name[0] === "$") { const { name } = node; const rest = name.substr(1); switch (rest[0]) { case "$": node.name = rest; break; case "I": (0, _core.invariant)(syms.length > 0); i.value.node.name = (i.value.sym = syms.shift()).name; break; default: if (rest.length && !isNaN(rest)) i.value.node.name = (i.value.sym = syms[rest - 1]).name; } } } yield i; } return s; } if (Array.isArray(s)) { for (const i of s) yield* replace(clone((0, _core.produce)(i, pos, value))); } else yield* replace(clone((0, _core.produce)(s, pos, value))); } /** runs iterable `s` and ignores its output, returns its result */ function skip(s) { let i = leanWrap(s).step(); if (i.done) return i.value; let node = i.value.node; const babel = _opts.babel; if (!babel || _opts.debug || BROWSER_DEBUG) { for (; !(i = i.step()).done;) {} } else { try { for (; !(i = i.step()).done;) { const next = i.value.node; if (next && (next._loc || next.loc)) node = next; } } catch (e) { if (!e.esNode) e.esNode = node; throw e; } } return i.value; } /** modifies token replacing its `type` field */ function setType(i, type) { return { enter: i.enter, leave: i.leave, type, pos: i.pos, value: i.value }; } /** modifies token replacing its `pos` field */ function setPos(i, pos) { return { enter: i.enter, leave: i.leave, type: i.type, pos, value: i.value }; } /** changes position on a first level of Iterable `s` */ function* repos(s, pos) { let i = leanWrap(s), level = 0; while (!(i = i.step()).done) { const v = i.value; if (v.enter) level++; yield level === 1 ? setPos(v, pos) : v; if (v.leave) level--; } return i.value; } /** probably faster version of `reposOne` if there is only one child */ function* reposOne(s, pos) { let i = leanWrap(s).step(); if (i.done) return i.value; let p = setPos(i.value, pos); for (; !(i = i.step()).done; p = i.value) yield p; yield setPos(p, pos); return i.value; } /** same as `reposOne` but works only for arrays avoiding traversal */ function reposOneArr(arr, pos) { arr[0] = setPos(arr[0], pos); arr[arr.length - 1] = setPos(arr[arr.length - 1], pos); return arr; } /** token to mark inner tag position must be changed to this tag postion */ const Subst = exports.Subst = (0, _core.symbol)("Subst", "ctrl"); /** applied `Subst` tokens */ function* completeSubst(s) { const sl = auto(s); function* subst(pos) { for (const i of sl.sub()) { if (i.type === Subst) { if (i.enter) yield* subst(pos); } else { yield sl.peel(setPos(i, pos)); yield* walk(); yield* sl.leave(); } } } function* walk() { for (const i of sl.sub()) { if (i.type === Subst) { if (i.enter) { (0, _core.invariant)(!i.leave); yield* subst(i.pos); } } else yield i; } } yield* walk(); } /** * same as `Array.from` but returns `s` if it is already `Array` */ function toArray(s) { if (Array.isArray(s)) return s; if (s.toArray) return s.toArray(); const res = []; result(s, res); return res; } /** * same as `Array.from` but outputs the array into `buf` * and returns iterable result */ function result(s, buf) { let i = leanWrap(s).step(); if (i.done) return i.value; buf.push(i.value); const name = i.value.value && i.value.value.stageName; const babel = _opts.babel; // for debugging purposes, // let the debugger catch the exception in browser if (!babel || _opts.debug || BROWSER_DEBUG) { for (; !(i = i.step()).done;) buf.push(i.value); return i.value; } try { for (; !(i = i.step()).done;) buf.push(i.value); } catch (e) { if (e.esNode) throw e; let msg = e.origMessage = e.origMessage || e.message; if (name) msg += ` during ${name}`; let node = e.esNode || i && i.value.node; // || si._last if (!node || !node.loc && !node._loc) { msg += " (the position is approximated)"; for (const i of (0, _combinators.reverse)(buf)) { node = i.value.node; if (node && (node.loc || node._loc)) { e.esNode = node; break; } } } else e.esNode = node; e.message = msg; throw e; } return i.value; } /** * values until leaving specified level */ function* tillLevel(level, s) { for (const i of s) { yield i; if (i.leave && s.level === level) return; } } /** to be removed */ function* toBlockBody(s) { const lab = s.label(); const i = s.cur(); if (i.type === _core.Tag.BlockStatement) { s.peel(); skip(s.peelTo(_core.Tag.body)); return function* () { skip(lab()); }; } else { yield s.enter(_core.Tag.push, Subst); return lab; } } /** to be removed */ function* inBlockBody(s, inner) { const lab = s.label(); const i = s.cur(); if (i.type !== _core.Tag.BlockStatement) { yield s.enter(_core.Tag.push, Subst); yield* inner; yield* lab(); } else { s.peel(); skip(s.peelTo(_core.Tag.body)); yield* inner; skip(lab()); } } /** copies everything until first not-import statement */ function* fileBody(s) { yield* s.till(i => i.pos === _core.Tag.body && i.type === _core.Tag.Array); while (s.cur().type === _core.Tag.ImportDeclaration) yield* s.one(); } function hasAnnot(node, name) { return node.leadingComments && node.leadingComments.length && node.leadingComments.find(v => v.value.trim() === name) !== undefined; } /** clones all tags and their `value` and `value.node` fields */ function* clone(s) { const stack = []; for (const i of s) { let value = null; if (i.enter) { stack.push(value = Object.assign({}, i.value)); const isArray = value.isArray = i.type === _core.Tag.Array; if (isArray) value.node = value.node.concat();else if (value.node != null && _core.Tag[i.type] != null) { value.node = Object.assign({}, value.node); if (value.node.leadingComments != null) value.node.leadingComments = value.node.leadingComments.concat(); if (value.node.trealingComments != null) value.node.trealingComments = value.node.trealingComments.concat(); } } if (i.leave) value = stack.pop(); yield { enter: i.enter, leave: i.leave, type: i.type, pos: i.pos, value }; } } /** * leaves all items un-amended until (and including) an item where * `pred` is true */ function* till(pred, s) { for (const i of s) { yield i; if (pred(i)) return i; } return null; } /** * copies the stream until `pred` returns true for next token * the found token is not consumed * returns true if found */ const find = exports.find = (0, _combinators.curry)(function* find(pred, s) { if (pred(s.cur())) return true; for (const i of s) { yield i; if (pred(s.cur())) return true; } return false; }); const Opts = exports.Opts = (0, _core.symbol)("Options"); const UpdateOpts = exports.UpdateOpts = (0, _core.symbol)("MergeOptions"); /** concatenates iterables in arguments into a single iterable */ function* concat(...args) { for (const i of args) yield* i; } /** like `concat` but with its last argument application curried */ function prepend(...args) { return function* (s) { for (const i of args) yield* i; yield* s; }; } /** shares single iterator between several uses */ function share(s) { let i = leanWrap(s); return { [Symbol.iterator]() { return this; }, next(v) { i = i.step(v); return { value: i.value, done: i.done }; }, take(v) { i = i.step(v); return i.done ? null : i.value; } }; } /** to be removed */ const wrap = exports.wrap = (0, _combinators.curry)(function* wrap(name, f, s) { const si = auto(s); const iter = f(si)[Symbol.iterator](); let i; try { let j; for (; !(j = iter.next()).done;) { i = j.value; yield i; } return j.value; } catch (e) { const babel = _opts.babel; if (babel != null) { let msg = `${e.message} during ${name}`; let node = e.esNode || i && i.value.node; // || si._last if (!node || !node.loc && !node._loc) { for (const i of si) { node = i.value.node; if (node && (node.loc || node._loc)) throw babel.root.hub.file.buildCodeFrameError(node, msg); } node = babel.root.node; e.esNode = node; } throw babel.root.hub.file.buildCodeFrameError(node, msg); } throw e; } }); /** marks all next exceptions with `name`, eagerly consumes the former stage */ function stage(name, s) { const res = toArray(s); res[0].value.stageName = name; return res; } /** to be removed */ const checkpointLazy = exports.checkpointLazy = (0, _combinators.curry)(function* checkpointLazy(name, s) { const babel = _opts.babel; const iter = s[Symbol.iterator](); let last, i; try { for (;;) { const j = iter.next(); i = j.value; if (j.done) return i; if (i.enter && i.value.node != null && i.value.node.loc != null) last = i.value.node; yield i; } } catch (e) { if (babel != null) { const node = e.esNode || i && i.value.node || last || babel.root.node; throw babel.root.hub.file.buildCodeFrameError(node, `${e.message} during ${name}`); } throw e; } }); /** to be removed */ const checkpoint = exports.checkpoint = (0, _combinators.curry)(function (name, s) { return _toConsumableArray(checkpointLazy(name, s)); }); /** * babel plugin visitor methods, typically to be applied only to Program node */ const babelBridge = exports.babelBridge = (0, _combinators.curry)(function babelBridge(pass, path, state) { const optSave = _opts; _opts = Object.assign({ args: Object.assign({}, state.opts), file: Object.assign(state.file.opts), babel: { root: path, state } }, _opts); if (_opts.debug || BROWSER_DEBUG) { pass((0, _core.produce)(state.file.ast)); } else { try { pass((0, _core.produce)(state.file.ast)); } catch (e) { if (_opts.verbose) console.log(e); throw path.hub.file.buildCodeFrameError(e.esNode, e.message); } } _opts = optSave; }); function babelPreset(pass) { return { plugins: [function () { return { visitor: { Program(path, state) { path.skip(); babelBridge(i => (0, _core.consume)(pass(i)), path, state); } } }; }] }; } function babelPlugin(pass) { return function (_, opts) { return { visitor: { Program(path, state) { _opts.pluginOpts = opts; babelBridge(function (i) { (0, _core.consume)(pass(i)); }, path, state); path.skip(); } } }; }; } const transform = exports.transform = (0, _combinators.curryN)(2, function transform(pass, ast, opts) { const optSave = _opts; Object.assign(_opts = {}, { args: {}, file: {}, babel: false }, opts); try { return (0, _core.consume)(pass((0, _core.produce)(ast))).top; } finally { _opts = optSave; } }); /** * copies input stream to output and returns it as array */ function* tee(s, buf) { if (buf == null) buf = []; for (const i of s) { yield i; buf.push(i); } return buf; } const makeExpr = exports.makeExpr = (0, _core.symbol)("makeExpr"); const makeStmt = exports.makeStmt = (0, _core.symbol)("makeStmt"); function makeExprPass(s) { s = auto(s); function* subst(pos) { const t = s.peel(); yield s.enter(pos, t.type, t.value); yield* walk(); yield* s.leave(); skip(s.leave()); } function* toExpr(pos) { const j = s.curLev(); if (j == null) return; if (j.type === makeExpr || j.type === makeStmt) { yield s.peel(j); yield* toExpr(pos); yield s.leave(); } else { const ti = (0, _core.typeInfo)(j); if (ti.block) { yield s.enter(pos, _core.Tag.CallExpression); yield s.enter(_core.Tag.callee, _core.Tag.ArrowFunctionExpression); yield s.tok(_core.Tag.params, _core.Tag.Array); yield* subst(_core.Tag.body); yield* s.leave(); yield s.tok(_core.Tag.arguments, _core.Tag.Array); yield* s.leave(); } else if (ti.stmt) { yield s.enter(pos, _core.Tag.CallExpression); const lab = s.label(); yield s.enter(_core.Tag.callee, _core.Tag.ArrowFunctionExpression); yield s.tok(_core.Tag.params, _core.Tag.Array); yield s.enter(_core.Tag.body, _core.Tag.BlockStatement); yield s.enter(_core.Tag.body, _core.Tag.Array); yield* subst(_core.Tag.push); yield* lab(); yield s.tok(_core.Tag.arguments, _core.Tag.Array); yield* s.leave(); } else if (ti.expr) { yield* subst(pos); } else { throw new Error("internal: cannot convert to expression"); } } } function* toStmt(pos) { const j = s.curLev(); if (j == null) return; if (j.type === makeExpr || j.type === makeStmt) { yield s.peel(j); yield* toStmt(pos); yield s.leave(); } else { const ti = (0, _core.symInfo)(j.type); if (ti.stmt || ti.block) { yield* subst(pos); } else { yield s.enter(pos, _core.Tag.ExpressionStatement); yield* subst(_core.Tag.expression); yield* s.leave(); } } } function* walk(pos) { for (const i of s.sub()) { switch (i.type) { case makeExpr: if (i.enter) { pos = pos || i.pos; yield* toExpr(pos); } break; case makeStmt: if (i.enter) { pos = pos || i.pos; yield* toStmt(pos); } break; default: yield i; } } } return walk(); } /** * detects if there it is a block statement with only a single result, and removes it */ function removeEmptyBlocks(si) { const s = auto(si); function* walk(sw) { for (const i of sw) { if (i.enter && i.type === _core.Tag.BlockStatement) { const lab = s.label(); const buf = [s.peel(i)].concat(_toConsumableArray(s.peelTo(_core.Tag.body))); if (s.curLev() == null) { yield* buf; yield* lab(); continue; } const fi = i.value.fieldInfo; if (fi.expr || fi.stmt) { const one = _toConsumableArray(walk(s.one())); if (s.curLev() != null || fi.expr && fi.block && (0, _core.typeInfo)(one[0]).stmt) { yield* buf; yield* one; yield* walk(s.sub()); yield* lab(); continue; } one[0].pos = one[one.length - 1].pos = i.pos; one[0].value.fieldInfo = fi; yield* one; skip(lab()); } continue; } yield i; } } return walk(s); } function* adjustFieldTypeImpl(s) { s = auto(s); yield s.peel(); yield* walk(); yield* s.leave(); function* subst(pos, i) { if (i.leave) { yield s.tok(pos, i.type, i.value); } else { yield s.peel(setPos(i, pos)); yield* walk(); yield* s.leave(); } } function* walk() { for (const i of s.sub()) { if (i.enter) { const fi = i.value.fieldInfo || {}, ti = (0, _core.typeInfo)(i); if (i.type === _core.Tag.Array && fi.array || i.type !== _core.Tag.Array && !ti.stmt && !ti.block && !ti.expr || fi.stmt && ti.stmt || fi.expr && ti.expr || fi.block && ti.block || ti.decl && fi.decl) { yield i; continue; } if (i.type === _core.Tag.Array) { if (fi.block || fi.stmt) { yield s.enter(i.pos, _core.Tag.BlockStatement); yield* subst(_core.Tag.body, i); yield* s.leave(); } else if (fi.expr) { yield s.enter(i.pos, _core.Tag.SequenceExpression); yield* subst(_core.Tag.expressions, i); yield* s.leave(); } else yield i; continue; } if (fi.block) { const lab = s.label(); yield s.enter(i.pos, _core.Tag.BlockStatement); yield s.enter(_core.Tag.body, _core.Tag.Array); if (ti.expr) { if (i.value.result) { yield s.enter(_core.Tag.push, _core.Tag.ReturnStatement); yield* subst(_core.Tag.argument, i); } else { yield s.enter(_core.Tag.push, _core.Tag.ExpressionStatement); yield* subst(_core.Tag.expression, i); } } else { (0, _core.invariant)(ti.stmt); yield* subst(_core.Tag.push, i); } yield* lab(); continue; } if (fi.stmt && ti.expr) { if (i.value.result) { yield s.enter(i.pos, _core.Tag.ReturnStatement); yield* subst(_core.Tag.argument, i); yield* s.leave(); } else { yield s.enter(i.pos, _core.Tag.ExpressionStatement); yield* subst(_core.Tag.expression, i); yield* s.leave(); } continue; } if (fi.expr) { yield s.enter(i.pos, _core.Tag.CallExpression); yield s.enter(_core.Tag.callee, _core.Tag.ArrowFunctionExpression, { node: { expression: true } }); yield s.tok(_core.Tag.params, _core.Tag.Array); if (!ti.block) { yield s.enter(_core.Tag.body, _core.Tag.BlockStatement); yield s.enter(_core.Tag.body, _core.Tag.Array); yield* subst(_core.Tag.push, i); yield* s.leave(); yield* s.leave(); } else { yield* subst(_core.Tag.body, i); } yield* s.leave(); yield s.tok(_core.Tag.arguments, _core.Tag.Array); yield* s.leave(); continue; } } yield i; } } } /** * for expr/stmt if field type is different to actual value assigned * it tries to change the value's type */ const adjustFieldType = exports.adjustFieldType = (0, _combinators.pipe)(_core.resetFieldInfo, adjustFieldTypeImpl); /** like `adjustFieldType` but with some simplifications */ const adjustFieldTypeSimple = exports.adjustFieldTypeSimple = (0, _combinators.pipe)(_core.resetFieldInfo, removeEmptyBlocks, adjustFieldTypeImpl); /** * lookahead: returns first element and a stream with same elements * * `Iterable<A> -> [A, Iterable<A>]` */ function la(s) { const i = leanWrap(s).step(); const v = i.value; return [v, (0, _combinators.cons)(v, i)]; } /** * runs `fn` passing `value.opts` of the stream's first output * if its resulting value is positive applies it to the original * stream or returns it unchanged * * (Opts -> Toks?) -> Toks -> Toks */ const select = exports.select = (0, _combinators.curry)(function select(fn, s) { const [h, sn] = la(s); const pass = fn(h.value.opts); return pass ? pass(sn) : sn; }); /** * if `pred` returns true on `opts` field of some first element * wraps transforms `s` using `pass` otherwise returns s unchanged * * (Value -> boolean) -> (Toks -> Toks) -> (Toks -> Toks)? -> Toks -> Toks */ const enableIf = exports.enableIf = (0, _combinators.curryN)(2, function enableIf(pred, t, e, s) { if (s == null) { if (e != null && typeof e !== "function") { s = e; e = i => i; } } if (e == null) e = i => i; const f = function (s) { const [h, sn] = la(s); if (!h.value.opts) h.value.opts = _opts; return pred(h.value) ? t(sn) : e(sn); }; return s != null ? f(s) : f; }); const packed = exports.packed = (0, _core.symbol)("packed"); /** packs not interested tokens into `packed` node */ const pack = exports.pack = (0, _combinators.curry)(function* pack(pred, s) { let buf = []; for (const i of s) { if (i.type === packed) { var _buf; (_buf = buf).push.apply(_buf, _toConsumableArray(i.value.node)); } else if (pred(i)) { if (buf.length) { yield (0, _core.tok)(packed, packed, { node: buf }); buf = []; } yield i; } else buf.push(i); } if (buf.length) yield (0, _core.tok)(packed, packed, { node: buf }); }); /** unpacks all `packed` nodes */ function* unpack(s) { for (const i of s) { if (i.type === packed) { if (i.enter) yield* i.value.node; } else yield i; } } /** a postprocess pass for any pass removing some expressions */ const cleanEmptyExprs = exports.cleanEmptyExprs = (0, _combinators.pipe)(function markLastSubExpr(si) { const s = auto(si); function* _markLastSubExp() { for (const i of s.sub()) { yield i; if (i.type === _core.Tag.SequenceExpression) { const inner = _toConsumableArray(_markLastSubExp()); if (inner.length > 2) inner[inner.length - 2].value.last = true; yield* inner; } } } return _markLastSubExp(); }, function cleanEmptyExprs(si) { const s = auto(si); function canSkip(inner) { if (!inner.length) return true; const t = inner[0]; if (t.type === _core.Tag.Null || t.value.canIgnore) return true; return false; } function* _cleanEmptyExprs(sw, ignore) { for (const i of sw) { if (i.enter) { switch (i.type) { case _core.Tag.NullLiteral: i.value.canIgnore = ignore; break; case _core.Tag.SequenceExpression: const j = s.take(); const buf = []; for (let nxt; (nxt = s.curLev()) != null;) { const inner = _toConsumableArray(_cleanEmptyExprs(s.one(), !nxt.value.last || ignore)); if (nxt.value.last && !ignore || !canSkip(inner)) buf.push(inner); } switch (buf.length) { case 1: yield* reposOneArr(buf[0], i.pos); case 0: s.close(j); s.close(i); break; default: yield i; yield j; for (const k of buf) yield* k; yield s.close(j); yield s.close(i); } continue; case _core.Tag.ExpressionStatement: const inner = _toConsumableArray(_cleanEmptyExprs(s.one(), true)); if (canSkip(inner)) { if (i.pos !== _core.Tag.push) yield s.tok(i.pos, _core.Tag.Null); s.close(i); } else { yield i; yield* inner; yield s.close(i); } continue; } ignore = false; } yield i; } } return _cleanEmptyExprs(s); }, _core.removeNulls); /** console.time for generator `s` */ const time = exports.time = (0, _combinators.curry)(function* (name, s) { console.time(name); return yield* s; }); /** console.time for generator `s` */ const timeEnd = exports.timeEnd = (0, _combinators.curry)(function* (name, s) { try { return yield* s; } finally { console.timeEnd(name); } }); /** * iterators wrapper keeping a single element lookahead, which may be accessed * with `cur` method * * the iterator may be shared */ function Wrapper(cont) { this.value = void 0; this._cont = cont; this.context = []; if (cont) { const iter = this._inner = leanWrap(cont).step(); const i = iter.value; (0, _core.invariant)(!iter.done); this.done = iter.done; this.first = i; this.opts = i.value && i.value.opts || _opts; if (i.value) i.value.opts = this.opts; } else this.opts = getOpts(); this._stack = []; this._tstack = []; this.level = 0; } const AFp = Wrapper.prototype; AFp.cur = function cur() { return this._inner.value; }; AFp.step = function () { if (this._inner.done) return this.pure(); const cur = this.value; const t = this.value = this._inner.value; this._inner = this._inner.step(); const tvalue = t.value; if (t.enter) { ++this.level; const parent = tvalue.parent = cur && (cur.leave ? cur.value.parent : cur.value); if (tvalue.opts) this.opts = t.value.opts;else if (parent) this.opts = tvalue.opts = parent.opts;else tvalue.opts = this.opts; } if (t.leave) { this.opts = tvalue.opts; --this.level; } return this; }; AFp[Symbol.iterator] = function () { return this; }; AFp.toArray = function toArray() { if (Array.isArray(this._cont)) return this._cont; const res = [this.first]; result(this._inner, res); return res; }; if (Symbol.effectfulIterator) { AFp[Symbol.effectfulIterator] = function () { return this; }; // AFp.exit = function(v) { // this.done = true // this.value = v // return this // } // AFp.handle = function(e) { throw e } } AFp.pure = function (v) { this.value = v; this.done = true; return this; }; /** ES Iterator's interface `next` */ AFp.next = function () { this.step(); return { value: this.value, done: this.done }; }; /** * same as `next` but returns either next value or null if done */ AFp.take = function () { this.step(); return this.done ? void 0 : this.value; }; /** smart constructor for a token */ AFp.valCtor = function (pos, type, value) { let node = null; if (value == null) { if (type != null && !(0, _core.isSymbol)(type)) { if (type.node != null) { value = type; node = value.node; type = null; } else if (type.type != null) { node = type; value = { node }; type = null; } else if (value == null) { value = type; type = null; } } if (value == null) value = {}; } else node = value.node; if (type == null) { if (value != null && value.typeInfo != null) { type = value.typeInfo.sym; } else if (node != null && node.type != null) { type = _core.Tag[node.type]; } else { if ((0, _core.symInfo)(pos).kind === "ctrl") type = pos; } } (0, _core.invariant)(type); if (node == null) { value.node = node = type === _core.Tag.Array ? [] : type === _core.Tag.Null || type === _core.Tag.Empty ? null : {}; } if (!value.opts) value.opts = this.opts; return [pos, type, value]; }; /** * outputs tokens from parsed template * see module `toks` function for details */ AFp.toks = toks; const ctrlTok = { $: "ctrlTok" }; const ctrlTokGen = { $: "ctrlTokGen" }; const storedTok = { $: "storedTok" }; const templateTok = { $: ctrlTokGen, *run(t) { yield* t._tstack.shift(); return true; } }; const skipTag = { $: ctrlTok, run(t) { t.take(); }, t: "skip" }; const vCloseTag = { $: ctrlTok, run() {}, t: "close" }; /** outputs opening tag */ AFp.enter = function (p, t, v) { const [pos, type, value] = this.valCtor(p, t, v); (0, _core.invariant)(pos && type && value); const res = { enter: true, leave: false, pos, type, value }; this._stack.unshift({ $: storedTok, tok: { enter: false, leave: true, pos, type, value } }); return res; }; /** outputs terminal tag */ AFp.tok = function tok(p, t, v) { const [pos, type, value] = this.valCtor(p, t, v); return { enter: true, leave: true, pos, type, value }; }; /** outputs closing tag doing other actions stored in the stack */ AFp.leave = function* () { let f; while (f = this._stack.shift()) { switch (f.$) { case ctrlTok: if (f.run(this)) return; break; case ctrlTokGen: if (yield* f.run(this)) return; break; default: yield f.tok; return f.tok; } } }; /** * gets a mark (function) to close all tags opened * after the function is called */ AFp.label = function () { const pos = this._stack.length; const t = this; return function* () { const sub = t._stack.splice(0, t._stack.length - pos); for (const i of sub) { switch (i.$) { case ctrlTok: i.run(t); break; case ctrlTokGen: yield* i.run(t); break; default: yield i.tok; } } }; }; function* one(t) { const c = t.cur(); if (c == null || !c.enter) return null; const exit = t.level; c.value.stageName = t.first.value.stageName; let i; for (i of t) { yield i; if (exit >= t.level) return i; } return i; } function* sub(t) { const c = t.cur(); if (c == null || !c.enter) return null; c.value.stageName = t.first.value.stageName; const exit = t.level; let i; for (i of t) { yield i; if (exit >= t.level) { const c = t.cur(); if (c == null || !c.enter || exit > t.level) return i; } } return i; } /** gets next tag if it is in the current level or null otherwise */ AFp.curLev = function () { const v = this.cur(); if (!v || !v.enter) return null; return v; }; /** * outputs all tokens until token with position `pos` * on the current level, returns the tag if found, it doesn't * remove the tag from the original iterator */ AFp.untilPos = function* (pos) { var i; while ((i = this.curLev()) != null && i.pos !== pos) yield* one(this); return i; }; /** same as `findPos` but also outputs the found tag */ AFp.toPos = function* (pos) { const p = yield* this.findPos(pos); (0, _core.invariant)(p); yield p; return p; }; /** * - if `i` is undefined reads a token from the stream * - makes an opening tag (copied from the read one or i) and returns it * - save closing tag into a stack to output it after leaving its level */ AFp.peel = function (i) { if (i == null) i = this.take(); (0, _core.invariant)(i.enter); const res = this.enter(i.pos, i.type, i.value); this._stack.unshift(i.leave ? vCloseTag : skipTag); return res; }; /** * looks a `pos` tag in the input and returns it, * also output its peel */ AFp.peelTo = function* (pos) { (0, _core.invariant)(this._stack[0] !== vCloseTag); const i = yield* this.findPos(pos); (0, _core.invariant)(i); yield this.peel(i); return i; }; /** * same us `peel` without parameters but don't crashes * if there is nothing to peel (returns undefined) */ AFp.peelOpt = function () { const v = this.cur(); if (!v || !v.enter) return null; return this.peel(); }; /** * returns an iterator, if next tag is opening the iterator will * output all tokens until it is closed, or empty otherwise */ AFp.one = function* () { if (this._stack[0] !== vCloseTag) { return yield* one(this); } return null; }; /** * returns an iterator to traverse all tokens until exiting from * the current level */ AFp.sub = function* () { if (this._stack[0] !== vCloseTag) return yield* sub(this); return null; }; /** same as `untilPos` but takes the tag from the original stream */ AFp.findPos = function* (pos) { if (this._stack[0] !== vCloseTag) { const i = yield* this.untilPos(pos); if (i != null) this.take(); return i; } return null; }; /** peels `i` outputs it, outputs all inner */ AFp.copy = function* (i) { if (!i) i = this.take(i); yield i; if (!i.leave) { yield* this.sub(); const j = this.take(); (0, _core.invariant)(i.value === j.value); j.type = i.type; j.pos = i.pos; yield j; } }; /** copies everything till closing of open tag `i` */ AFp.tillClose = function* (i) { for (const j of this) { yield j; if (j.value == i.value) return j; } }; function* tillVal(value, iter) { for (const i of iter) { yield i; if (i.value === value) return i; } return null; } /** * like `AFp.one` but returns everything until matching value * - this won't work if some inner iterator consumes the closing tag */ function* single(iter) { const { done, value } = iter.next(); if (done) return null; yield value; if (!value.leave) yield* tillVal(value.value, iter); return value; } /** * consumes next tag if `i` is not closing one * crashes for debugging purposes if the tag is not closing tag of `i` */ AFp.close = function (i) { if (!i.leave) { const j = this.take(); (0, _core.invariant)(i.value === j.value); return j; } return null; }; /** * same as `toks` but extends formats with `$_`/'$E` identifier to mark * a position where output stream must be stopped, the position is a * result of the function, to continue emitting use `refocus` * to exit the template use `leave` * in expression statements $E focuses only the expression, while `$_` is for * the statement, in other contexts they do matches the same */ AFp.template = function* (pos, node, ...syms) { if (node.substr != null) node = toArray(toks.apply(void 0, [pos, node].concat(syms))); this._stack.unshift(templateTok); this._tstack.unshift(node); return yield* this.refocus(); }; /** emits current template stream till next `$_`/`$E` */ AFp.refocus = function* () { const arr = this._tstack[0]; while (arr.length) { const f = arr.shift(); if (f.enter) { switch (f.type) { case _core.Tag.ExpressionStatement: const n = arr[0]; if (n != null && n.type === _core.Tag.Identifier && n.value.node.name === "$_") { while (arr.length && arr.shift().value !== f.value) {} return f.pos; } break; case _core.Tag.Identifier: const name = f.value.node.name; if (name === "$_" || name === "$E") { while (arr.length && arr.shift().value !== f.value) {} return f.pos; } break; } } yield f; } throw new Error("next placeholder is not found"); }; AFp.till = function (pred) { return till(pred, this); }; /** alias of module's find */ AFp.find = function (pred) { return find(pred, this); }; /** generates error referring with `node` position */ AFp.error = function (msg, node) { if (this._name != null) msg += " during " + this._name; const e = new SyntaxError(msg); if (node) e.esOrigNode = node; if (!node || !node._loc && !node.loc) { for (const i of this) { node = i.value.node; if (node && (node.loc || node._loc)) { e.esNode = node; return e; } } } return e; }; /** alias of the module's `tillLevel` */ AFp.tillLevel = function (level) { return tillLevel(level, this); }; /** adds utility funcitons to iterable object `cont` */ function auto(cont) { return new Wrapper(cont); }