@effectful/transducers
Version:
JS syntax transformation framework for @effectful/js
405 lines (402 loc) • 14.4 kB
JavaScript
"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);