UNPKG

@effectful/transducers-loose

Version:

@effectful/transducers built with faster generators

321 lines (318 loc) 10.6 kB
"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"]]);