UNPKG

@effectful/transducers

Version:

JS syntax transformation framework for @effectful/js

405 lines (402 loc) 14.4 kB
"use strict"; exports.__esModule = true; exports.default = void 0; var _core = require("../core"); var Kit = require("../kit"); var Scope = require("../scope"); var RT = require("../rt"); 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 path = require("path"); const globals = Scope.newSym("g"); /** calculates captured vars dependencies */ function calcClosCapt(si) { const sa = Kit.toArray(si); const s = Kit.auto(sa); const closureSym = s.first.value.closureSym = Scope.newSym("closure"); s.first.value.ctxSym = globals; closureSym.global = true; const rt = s.first.value.rt; if (!s.opts.noRT) { const fs = require("fs"); rt.inlineSyms.push({ syms: [closureSym], content: fs.readFileSync(path.join(__dirname, "closConvRT.js"), "utf-8") }); } function walk(root, sw) { const decls = root.clDecls = []; root.closSym = Scope.newSym(root.node.id && root.node.id.name || "fn"); if (!root.ctxSym) root.ctxSym = Scope.newSym("loc"); // thisSym const closDeps = root.closDepsSet = new Set(); function id(i) { const si = i.value.sym; if (si != null) { if (si === Scope.argumentsSym && !root.argumentsSym) { root.argumentsSym = Scope.newSym("args"); return; } if (i.value.decl) decls.push(si); if (si.declScope) { if (si.declScope !== root) { (si.refScopes || (si.refScopes = new Set())).add(root); for (let c = root; c && c !== si.declScope; c = c.parScope) c.closDepsSet.add(si.declScope); } } } } for (const i of sw) { if (i.enter) { switch (i.type) { case _core.Tag.FunctionDeclaration: id(s.cur()); Kit.skip(s.one()); case _core.Tag.FunctionExpression: walk(i.value, s.sub()); break; case _core.Tag.Identifier: id(i); break; } } } root.closDeps = _toConsumableArray(closDeps).sort((a, b) => a.closSym.num - b.closSym.num); } walk(s.first.value, s); return sa; } /** replaces function calls, to call method */ function replaceCalls(si) { const s = Kit.auto(si); // globals.global = true // if (s.opts.closImplicitCall) // return s const callMethod = !s.opts.closImplicitCall; const noConstrs = s.opts.noClosConstrs; const noThis = s.opts.noClosThis; return walk(s, s.first.value.clDecls); function* walk(sw, decls) { for (const i of sw) { if (i.enter) { switch (i.type) { case _core.Tag.FunctionDeclaration: case _core.Tag.FunctionExpression: yield i; yield* walk(s.sub(), i.value.clDecls); continue; case _core.Tag.NewExpression: if (noConstrs) break; case _core.Tag.CallExpression: i.value.callRedir = true; if (!callMethod) break; const j = s.curLev(); const lab = s.label(); const constr = i.type === _core.Tag.NewExpression; yield s.peel(Kit.setType(i, _core.Tag.CallExpression)); yield s.enter(_core.Tag.callee, _core.Tag.MemberExpression); let sym; if (j.type === _core.Tag.MemberExpression && !noThis) { s.take(); yield s.enter(_core.Tag.object, j.type, j.value); const k = s.curLev(); if (k.type === _core.Tag.Identifier || constr) { yield* s.one(); sym = k.value.sym; } else { decls.push(sym = Scope.newSym("temp")); yield* s.template(k.pos, "=$I = $_", sym); yield* Kit.reposOne(walk(s.one(), decls), _core.Tag.right); yield* s.leave(); } yield* walk(s.one(), decls); yield* s.leave(); s.close(j); } else { sym = Scope.undefinedSym; yield* Kit.reposOne(walk(s.one()), _core.Tag.object); } yield s.tok(_core.Tag.property, _core.Tag.Identifier, { node: { name: constr ? "constr" : "call" } }); yield* s.leave(); yield s.peel(); if (!constr && !noThis) yield s.tok(_core.Tag.push, _core.Tag.Identifier, { sym }); yield* walk(s.sub(), decls); yield* s.leave(); yield* lab(); continue; } } yield i; } } } function* functToObj(si) { const s = Kit.auto(si); const hoisted = []; const closureSym = s.first.value.closureSym; const dpref = s.opts.closDepPrefix || ""; const dpost = s.opts.closDepPostfix || ""; const noThis = s.opts.noClosThis; function* walk(sw, root, blockHoisted) { const selfSym = root.selfSym = !noThis && Scope.newSym("self"); function* func(i, pos) { const sym = i.value.closSym; yield* s.template(pos, "=new $I($_)", { funcVal: i.value }, sym); for (const j of i.value.closDeps) { if (j === root) yield s.tok(_core.Tag.push, _core.Tag.Identifier, { sym: root.ctxSym });else yield* s.toks(_core.Tag.push, `=this.${dpref}${j.closSym.name}${dpost}`, { origSym: j.closSym }); } yield* s.leave(); const objArr = Kit.toArray(obj(i.value, sym)); objArr[0].value.closConstr = i.value; hoisted.push(objArr); s.close(i); } for (const i of sw) { if (i.enter) { switch (i.type) { case _core.Tag.BlockStatement: yield i; yield* s.peelTo(_core.Tag.body); const hoisted = []; const buf = Kit.toArray(walk(s.sub(), root, hoisted)); for (const j of hoisted) yield* j; yield* buf; yield* s.leave(); continue; case _core.Tag.ThisExpression: if (!selfSym) throw new Error("`this` is not supported with `noClosThis:true`"); yield s.tok(i.pos, _core.Tag.Identifier, { sym: selfSym }); s.close(i); continue; case _core.Tag.VariableDeclaration: const vlab = s.label(); let pos = i.pos; if (i.pos === _core.Tag.left) { const j = s.take(); const k = s.take(); yield s.tok(i.pos, _core.Tag.Identifier, { sym: s.curLev().value.sym }); Kit.skip(s.sub()); s.close(k); s.close(j); s.close(i); continue; } if (pos === _core.Tag.push) { yield s.enter(_core.Tag.push, _core.Tag.ExpressionStatement); pos = _core.Tag.expression; } let sym; yield s.enter(pos, _core.Tag.SequenceExpression); yield s.enter(_core.Tag.expressions, _core.Tag.Array); for (const j of s.sub()) { if (j.enter) { for (const j of s.sub()) { const id = Kit.toArray(s.one()); sym = id[0].value.sym; if (s.curLev() != null) { yield* s.template(_core.Tag.push, "=$I = $_", sym); yield* Kit.reposOne(walk(s.one(), root, blockHoisted), _core.Tag.right); yield* s.leave(); } s.close(j); } } } yield* vlab(); s.close(i); continue; case _core.Tag.FunctionDeclaration: blockHoisted.push(Kit.toArray(function* (i) { yield* s.template(i.pos, "$I = $_", s.curLev().value.sym); yield* func(i, _core.Tag.right); yield* s.leave(); }(i))); continue; case _core.Tag.FunctionExpression: yield* func(i, i.pos); continue; } } yield i; } } function* obj(fun, sym) { const lab = s.label(); yield* s.template(_core.Tag.push, "*function $1($_){$_} $2($1, $_)", sym, closureSym); const copies = []; for (const j of fun.closDeps) { const sym = Scope.newSym(j.closSym.orig); copies.push(sym); sym.copyOf = j.closSym; yield s.tok(_core.Tag.push, _core.Tag.Identifier, { sym }); } yield* s.refocus(); for (const j of fun.closDeps) { const copySym = copies.shift(); yield* s.template(_core.Tag.push, `$E = $I`, copySym); yield* s.toks(_core.Tag.left, `=this.${dpref}${j.closSym.name}${dpost}`, { origSym: j.closSym, copySym }); yield* s.leave(); } yield* s.refocus(); yield s.enter(_core.Tag.push, _core.Tag.FunctionExpression, fun); if (s.cur().pos === _core.Tag.id) Kit.skip(s.one()); if (fun.node) fun.node.id = null; yield* walk(s.sub(), fun); yield* lab(); } yield* Kit.fileBody(s); const buf = Kit.toArray(walk(s, s.first.value, hoisted)); for (const i of hoisted) yield* i; yield* buf; yield* s; } function* injectLocObjs(si) { const s = Kit.auto(si); for (const i of s) { yield i; if (i.enter) { switch (i.type) { case _core.Tag.File: case _core.Tag.FunctionExpression: case _core.Tag.FunctionDeclaration: if (i.value.ctxSym && i.value.clDecls.some(i => i.refScopes && i.refScopes.size)) { yield* Kit.fileBody(s); yield* s.toks(_core.Tag.push, `var $I = {}`, i.value.ctxSym); } } } } } function substIds(si) { const s = Kit.auto(si); const vpref = s.opts.closVarPrefix || ""; const vpost = s.opts.closVarPostfix || ""; const dpref = s.opts.closDepPrefix || ""; const dpost = s.opts.closDepPostfix || ""; function* emitDecls({ clDecls: decls, argumentsSym, ctxSym }) { const locs = []; const params = []; for (const i of decls) { if (i.param) { if (i.refScopes) params.push(i); } else if (!i.refScopes) locs.push(i); } yield* s.till(i => i.pos === _core.Tag.body && i.type === _core.Tag.Array); const lab = s.label(); if (locs.length || argumentsSym) { yield s.enter(_core.Tag.push, _core.Tag.VariableDeclaration, { node: { kind: "var" } }); yield s.enter(_core.Tag.declarations, _core.Tag.Array); if (argumentsSym) { yield s.enter(_core.Tag.push, _core.Tag.VariableDeclarator); yield s.tok(_core.Tag.id, _core.Tag.Identifier, { sym: argumentsSym }); yield* s.toks(_core.Tag.init, "=Array.from(arguments).slice(1)"); yield* s.leave(); } for (const sym of locs) { yield s.enter(_core.Tag.push, _core.Tag.VariableDeclarator); yield s.tok(_core.Tag.id, _core.Tag.Identifier, { sym }); yield* s.leave(); } yield* lab(); } if (params.length) { yield s.enter(_core.Tag.push, _core.Tag.ExpressionStatement); yield s.enter(_core.Tag.expression, _core.Tag.SequenceExpression); yield s.enter(_core.Tag.expressions, _core.Tag.Array); for (const i of params) { yield* s.template(_core.Tag.push, `=$E = $I`, { clParam: i }, i); yield* s.toks(_core.Tag.left, `=$1.${vpref}${i.name}${vpost}`, { origSym: i }, ctxSym); yield* s.leave(); } yield* lab(); } } function* walk(root) { for (const i of s.sub()) { if (i.enter) { switch (i.type) { case _core.Tag.FunctionExpression: yield i; yield* s.till(i => i.pos === _core.Tag.params); if (i.value.selfSym) yield s.tok(_core.Tag.push, _core.Tag.Identifier, { sym: i.value.selfSym }); yield* emitDecls(i.value); yield* walk(i.value); continue; case _core.Tag.File: yield i; yield* emitDecls(i.value); continue; case _core.Tag.Identifier: const { sym } = i.value; if (sym) { if (sym === Scope.argumentsSym) { i.value.sym = root.argumentsSym; } else if (sym.refScopes) { if (root === sym.declScope) yield* s.toks(i.pos, `=$I.${vpref}${sym.name}${vpost}`, { origSym: sym }, root.ctxSym);else { yield* s.template(i.pos, `=$E.${vpref}${sym.name}${vpost}`, { origSym: sym }); yield* s.toks(_core.Tag.object, `=this.${dpref}${sym.declScope.closSym.orig}${dpost}`, { origSym: sym.declScope.closSym }); yield* s.leave(); } s.close(i); continue; } } break; } } yield i; } } return walk(s.first.value); } var _default = exports.default = Kit.pipe(Scope.prepare, RT.init, calcClosCapt, replaceCalls, functToObj, Kit.toArray, substIds, injectLocObjs, RT.inline, Scope.resolve);