@effectful/transducers
Version:
JS syntax transformation framework for @effectful/js
1,619 lines (1,548 loc) • 43 kB
JavaScript
"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);
}