@effectful/transducers-loose
Version:
@effectful/transducers built with faster generators
321 lines (318 loc) • 10.6 kB
JavaScript
"use strict";
exports.__esModule = true;
exports.argumentsSym = exports.SymbolSym = exports.ObjectSym = exports.ArraySym = void 0;
exports.cloneSym = cloneSym;
exports.globals = exports.globalSym = exports.evalSym = void 0;
exports.namePos = namePos;
exports.newSym = newSym;
exports.parentScope = parentScope;
exports.prepare = prepare;
exports.reserved = exports.requireSym = void 0;
exports.resolve = resolve;
exports.tempVar = tempVar;
exports.undefinedSym = void 0;
exports.uniqFields = uniqFields;
var _types = require("./types");
var C = require("./core");
var M = require("@effectful/es-rt/opts/loose");
const {
next
} = C;
let symNum = 0;
function emitSym(pos) {
const res = C.tok(pos, _types.Tag.Identifier, {});
res.sym = this;
return res;
}
// String -> Sym
function newSym(name, strict = false, decl = null) {
if (!name) name = "";
const num = ++symNum;
return {
name,
orig: name,
id: `${name}_${num}`,
strict,
decl,
block: null,
func: null,
num,
type: null,
emitConstMethod: emitSym,
useFuncs: null,
hasRead: false
};
}
const undefinedSym = exports.undefinedSym = newSym("undefined", true);
const globalSym = exports.globalSym = newSym("global", true);
const argumentsSym = exports.argumentsSym = newSym("arguments", true);
const ObjectSym = exports.ObjectSym = newSym("Object", true);
const ArraySym = exports.ArraySym = newSym("Array", true);
const SymbolSym = exports.SymbolSym = newSym("Symbol", true);
const evalSym = exports.evalSym = newSym("eval", true);
const requireSym = exports.requireSym = newSym("require", true);
const globals = exports.globals = [["undefined", undefinedSym], ["global", globalSym], ["arguments", argumentsSym], ["Object", ObjectSym], ["Array", ArraySym], ["eval", evalSym], ["require", evalSym]];
function cloneSym(sym) {
const res = newSym(sym.orig);
res.decl = sym.decl;
res.block = sym.block;
res.func = sym.func;
return res;
}
function parentFunc(value) {
for (let i = value.parent; i; i = i.parent) if (i.typeInfo.funcScope) return i;
return null;
}
function prepare(root) {
let lastBlock = null;
let lastId = null;
let ids = null;
let lastIdDecl = null;
let idDecls = null;
let lastClass = null;
let classes = null;
let lastLoop = null;
let lastScope = null;
let lastFunc = null;
root.declClause = root.parentClass = root.parentLoop = null;
root.parentFunc = root.parentBlock = root.origParent = null;
const fileGlobals = root.globals = new Map(globals);
let sloppyProgram;
// TODO: traverse only needed tokens
let i = root.firstChild;
do {
const parent = i.parent;
const pti = parent.typeInfo;
i.origParent = parent;
i.parentBlock = pti.blockScope ? parent : parent.parentBlock;
const parFunc = i.parentFunc = pti.funcScope ? parent : parent.parentFunc;
const parentLoop = i.parentLoop = pti.funcScope ? null : i.parentLoop = pti.loopScope ? parent : parent.parentLoop;
i.parentMaybeScope = parentLoop || parFunc;
i.parentClass = pti.classDecl ? parent : parent.parentClass;
i.scopeNode = false;
i.scopeDecls = null;
i.root = root;
const ti = i.typeInfo;
if (i.pos === _types.Tag.id && i.type !== _types.Tag.PrivateName || i.pos === _types.Tag.param || i.pos === _types.Tag.params || i.pos === _types.Tag.local) {
i.declClause = i;
} else {
i.declClause = parent.declClause;
}
if (ti.blockScope) {
const blockDirs = new Set();
if ((i.pos === _types.Tag.body || i.pos === _types.Tag.program) && parent && parent.typeInfo.funcScope) {
i.blockDecls = parent.blockDecls;
i.blockVars = parent.blockVars;
parent.blockDirs = i.blockDirs;
parent.block = i;
} else {
i.blockDecls = new Set();
i.blockVars = new Map();
}
i.parentFunc = parentFunc(i);
i.lastBlock = lastBlock;
if (!lastBlock) root.firstBlock = i;
lastBlock = i;
i.blockDirs = blockDirs;
}
if (ti.funcScope) {
i.blockDecls = new Set();
i.funcDecls = new Set();
i.params = new Set();
i.blockVars = new Map();
i.parentFunc = parentFunc(i);
i.scopeNode = true;
i.block = null;
i.nextFunc = null;
i.scopeDecls = new Set();
if (lastFunc) lastFunc.nextFunc = i;else root.funcs = i;
if (lastScope) lastScope.nextScope = i;else root.scopes = i;
lastScope = lastFunc = i;
}
if (ti.loopScope) {
if (lastLoop) lastLoop.nextLoop = i;
lastLoop = i;
}
switch (i.type) {
case _types.Tag.Program:
sloppyProgram = i.node.sourceType === "script";
break;
case _types.Tag.ClassExpression:
case _types.Tag.ClassDeclaration:
if (lastClass) lastClass.nextClass = i;else classes = i;
lastClass = i;
break;
case _types.Tag.Identifier:
const fi = i.fieldInfo;
if (fi.declVar) {
i.nextIdDecl = null;
if (lastIdDecl) lastIdDecl.nextIdDecl = i;else idDecls = i;
lastIdDecl = i;
} else if (fi.expr || fi.lval) {
i.nextId = null;
if (lastId) lastId.nextId = i;else ids = i;
lastId = i;
}
i.sym = null;
break;
case _types.Tag.DirectiveLiteral:
i.parentBlock.blockDirs.add(i.node);
break;
case _types.Tag.ExpressionStatement:
if (i.firstChild.type === _types.Tag.StringLiteral) i.parentBlock.blockDirs.add(i.node);
}
} while ((i = next(i)) !== root);
for (let i = lastFunc; i; i = i.nextFunc) {
i.sloppy = i.parentClass == null && (i.parentFunc == null ? sloppyProgram : i.parentFunc.sloppy && !i.blockDirs.has("use strict"));
}
for (let i = idDecls; i; i = i.nextIdDecl) {
const declClause = i.declClause;
const declClauseParent = declClause.parent;
let sym,
block,
unordered = true;
switch (declClause.pos) {
case _types.Tag.local:
block = root.firstChild.firstChild;
break;
case _types.Tag.id:
switch (declClauseParent.type) {
case _types.Tag.VariableDeclarator:
const declaration = declClauseParent.parent.parent;
if (declaration.node.kind === "var") {
block = i.parentFunc;
} else {
unordered = false;
block = i.parentBlock;
}
break;
case _types.Tag.ClassExpression:
case _types.Tag.FunctionExpression:
block = declClauseParent;
break;
case _types.Tag.ClassDeclaration:
case _types.Tag.FunctionDeclaration:
block = declClauseParent.sloppy ? declClauseParent.parentFunc : declClauseParent.parentBlock;
break;
}
break;
case _types.Tag.param:
block = declClause.parentBlock;
break;
case _types.Tag.params:
block = declClause.parentFunc;
break;
}
const name = i.node.name;
if (block) {
sym = block.blockVars.get(name);
if (sym && !(sym.unordered && unordered)) throw C.error(`Identifier ${name} has already been declared`, i);
if (!sym) sym = newSym(name);
block.blockVars.set(name, sym);
sym.unordered = unordered;
sym.decl = declClause;
const func = sym.func = declClause.parentFunc;
func.funcDecls.add(sym);
if (sym.param = declClause.pos === _types.Tag.params) func.params.add(sym);
sym.block = block;
block.blockDecls.add(sym);
i.sym = sym;
}
}
for (let i = ids; i; i = i.nextId) {
const name = i.node.name;
let sym;
for (let j = i.parentBlock; j; j = j.parentBlock) {
sym = j.blockVars.get(name);
if (sym) break;
}
if (sym) {
sym.hasRead = true;
if (i.parentFunc !== sym.func) (sym.useFuncs || (sym.useFuncs = new Set())).add(i.parentFunc);
} else {
sym = fileGlobals.get(name);
if (!sym) {
sym = newSym(name, true);
fileGlobals.set(name, sym);
}
}
i.sym = sym;
}
for (let i = idDecls; i; i = i.nextIdDecl) {
if (!i.sym.useFuncs) continue;
const scope = i.parentMaybeScope;
if (!scope.scopeNode) {
scope.scopeNode = true;
scope.scopeDecls = new Set();
if (lastScope) lastScope.nextScope = scope;else root.scopes = scope;
lastScope = scope;
}
}
for (let i = lastLoop; i; i = i.nextLoop) {
if (!i.scopeNode) continue;
i.parentScope = parentScope(i);
}
for (let i = lastFunc; i; i = i.nextFunc) i.parentScope = parentScope(i);
for (let i = idDecls; i; i = i.nextIdDecl) {
const sym = i.sym;
const block = sym.block;
const scope = block.scopeNode ? block : parentScope(block);
sym.scope = scope;
scope.scopeDecls.add(sym);
i.scope = parentScope(i);
}
for (let i = ids; i; i = i.nextId) i.scope = parentScope(i);
root.ids = ids;
root.idDecls = idDecls;
root.classes = classes;
return root;
}
function parentScope(node) {
let j;
for (j = node.parentMaybeScope; j && !j.scopeNode; j = j.parentMaybeScope) {}
return j;
}
/**
* makes all `syms` to have uniq name among them only
* with prefix `pref` and postfix `postf`
*/
function uniqFields(syms, pref = "", postf = "") {
const names = new Set();
// TODO: group by field name
var loop = M.iterator(syms);
for (; !(loop = loop.step()).done;) {
const sym = loop.value;
const orig = sym.orig;
let name = `${pref}${namePos(orig, 0)}${postf}`;
for (let cnt = 0; names.has(name); cnt++, name = `${pref}${namePos(orig, cnt)}${postf}`) {}
names.add(name);
sym.name = name;
}
}
function resolve(root) {
for (let i = root.firstChild; i; i = i.nextFunc) uniqFields(i.funcDecls);
for (let i = root.ids; i; i = i.nextId) i.node.name = i.sym.name;
for (let i = root.idDecls; i; i = i.nextIdDecl) i.node.name = i.sym.name;
return root;
}
function tempVar(root, pat) {
const sym = newSym(pat);
sym.decl = null;
sym.block = sym.func = root;
root.funcDecls.add(sym);
root.blockDecls.add(sym);
return sym;
}
const nameOpts = ["a", "b", "c", "d", "e", "f", "g", "h", "k", "m", "n", "x", "y", "z"];
function namePos(n, pos) {
if (!n || !n.length) {
const len = nameOpts.length;
if (pos < len) return nameOpts[pos];
return `${nameOpts[pos % len]}${Math.floor(pos / len)}`;
}
if (n[n.length - 1] === "_") return n + (pos + 1);
if (pos === 0) return reserved.get(n) || n;
if (pos === 1) return "_" + n;
return `${n}${pos - 1}`;
}
const reserved = exports.reserved = new Map([["arguments", "args"]]);