Skip to content

Instantly share code, notes, and snippets.

@GeoffreyBooth
Created September 14, 2016 03:13
Show Gist options
  • Save GeoffreyBooth/59494576acd3b06fa6c22eb2486ddfa5 to your computer and use it in GitHub Desktop.
Save GeoffreyBooth/59494576acd3b06fa6c22eb2486ddfa5 to your computer and use it in GitHub Desktop.
Error.stackTraceLimit = Infinity;
var {
Scope
} = require("./scope");
var {
isUnassignable,
JS_FORBIDDEN
} = require("./lexer");
var {
compact,
flatten,
extend,
merge,
del,
starts,
ends,
some,
addLocationDataFn,
locationDataToString,
throwSyntaxError
} = require("./helpers");
exports.extend = extend;
exports.addLocationDataFn = addLocationDataFn;
var YES = function() {
return true;
};
var NO = function() {
return false;
};
var THIS = function() {
return this;
};
var NEGATE = function() {
this.negated = !this.negated;
return this;
};
exports.CodeFragment = class CodeFragment {
constructor(parent, code) {
var ref;
this.code = ("" + (code));
this.locationData = typeof parent !== "undefined" && parent !== null ? parent.locationData : void 0;
this.type = ((typeof parent !== "undefined" && parent !== null ? (ref = parent.constructor) != null ? ref.name : void 0 : void 0)) || "unknown";
}
toString() {
return ("" + (this.code) + ((() => {
if (this.locationData) {
return ": " + locationDataToString(this.locationData);
} else {
return "";
}
})()));
}
};
var fragmentsToText = function(fragments) {
return (fragments.map(fragment => {
return fragment.code;
})).join("");
};
exports.Base = class Base {
compile(o, lvl) {
return fragmentsToText(this.compileToFragments(o, lvl));
}
compileToFragments(o, lvl) {
o = extend({}, o);
if (lvl) {
o.level = lvl;
}
var node = this.unfoldSoak(o) || this;
node.tab = o.indent;
if (o.level === LEVEL_TOP || !node.isStatement(o)) {
return node.compileNode(o);
} else {
return node.compileClosure(o);
}
}
compileClosure(o) {
var ref;
var meth;
var argumentsNode;
var jumpNode;
if (jumpNode = this.jumps()) {
jumpNode.error("cannot use a pure statement in an expression");
}
o.sharedScope = true;
var func = new Code([], Block.wrap([this]));
var args = [];
if ((argumentsNode = this.contains(isLiteralArguments)) || this.contains(isLiteralThis)) {
args = [new ThisLiteral()];
if (argumentsNode) {
meth = "apply";
args.push(new IdentifierLiteral("arguments"));
} else {
meth = "call";
}
func = new Value(func, [new Access(new PropertyName(meth))]);
}
var parts = (new Call(func, args)).compileNode(o);
if (func.isGenerator || (((ref = func.base) != null ? ref.isGenerator : void 0))) {
parts.unshift(this.makeCode("(yield* "));
parts.push(this.makeCode(")"));
}
return parts;
}
cache(o, level, isComplex) {
var sub;
var ref;
var complex = (() => {
if (typeof isComplex !== "undefined" && isComplex !== null) {
return isComplex(this);
} else {
return this.isComplex();
}
})();
if (complex) {
ref = new IdentifierLiteral(o.scope.freeVariable("ref"));
sub = new Assign(ref, this);
if (level) {
return [sub.compileToFragments(o, level), [this.makeCode(ref.value)]];
} else {
return [sub, ref];
}
} else {
ref = (() => {
if (level) {
return this.compileToFragments(o, level);
} else {
return this;
}
})();
return [ref, ref];
}
}
cacheToCodeFragments(cacheValues) {
return [fragmentsToText(cacheValues[0]), fragmentsToText(cacheValues[1])];
}
makeReturn(res) {
var me = this.unwrapAll();
if (res) {
return new Call(new Literal(((res) + ".push")), [me]);
} else {
return new Return(me);
}
}
contains(pred) {
var node = undefined;
this.traverseChildren(false, function(n) {
if (pred(n)) {
node = n;
return false;
}
});
return node;
}
lastNonComment(list) {
var i = list.length;
while (i--) {
return list[i];
}
return null;
}
toString(idt = "", name = this.constructor.name) {
var tree = "\n" + idt + name;
if (this.soak) {
tree += "?";
}
this.eachChild(function(node) {
return tree += node.toString(idt + TAB);
});
return tree;
}
eachChild(func) {
if (!this.children) {
return this;
}
for (var attr of this.children) {
if (this[attr]) {
for (var child of flatten([this[attr]])) {
if (func(child) === false) {
return this;
}
}
}
}
return this;
}
traverseChildren(crossScope, func) {
return this.eachChild(function(child) {
var recur = func(child);
if (recur !== false) {
return child.traverseChildren(crossScope, func);
}
});
}
invert() {
return new Op("!", this);
}
unwrapAll() {
var node = this;
while (node !== (node = node.unwrap())) {
continue;
}
return node;
}
updateLocationDataIfMissing(locationData) {
if (this.locationData) {
return this;
}
this.locationData = locationData;
return this.eachChild(function(child) {
return child.updateLocationDataIfMissing(locationData);
});
}
error(message) {
return throwSyntaxError(message, this.locationData);
}
makeCode(code) {
return new CodeFragment(this, code);
}
wrapInBraces(fragments) {
return [].concat(this.makeCode("("), fragments, this.makeCode(")"));
}
joinFragmentArrays(fragmentsList, joinStr) {
var answer = [];
for (var [i, fragments] of fragmentsList.entries()) {
if (i) {
answer.push(this.makeCode(joinStr));
}
answer = answer.concat(fragments);
}
return answer;
}
};
exports.Block = class Block extends Base {
constructor(nodes) {
super(...arguments);
this.expressions = compact(flatten(nodes || []));
}
push(node) {
this.expressions.push(node);
return this;
}
pop() {
return this.expressions.pop();
}
unshift(node) {
this.expressions.unshift(node);
return this;
}
unwrap() {
return (this.expressions.length === 1 ? this.expressions[0] : this);
}
isEmpty() {
return !this.expressions.length;
}
isStatement(o) {
for (var exp of this.expressions) {
if (exp.isStatement(o)) {
return true;
}
}
return false;
}
jumps(o) {
return (() => {
var jumpNode;
for (var exp of this.expressions) {
if (jumpNode = exp.jumps(o)) {
return jumpNode;
}
}
})();
}
makeReturn(res) {
var len = this.expressions.length;
while (len--) {
var expr = this.expressions[len];
if (!(expr instanceof Comment)) {
this.expressions[len] = expr.makeReturn(res);
if (expr instanceof Return && !expr.expression) {
this.expressions.splice(len, 1);
}
break;
}
}
return this;
}
compileToFragments(o = {}, level) {
if (o.scope) {
return super.compileToFragments(o, level);
} else {
return this.compileRoot(o);
}
}
compileNode(o) {
var answer;
var fragments;
this.tab = o.indent;
var top = o.level === LEVEL_TOP;
var compiledNodes = [];
for (var [index, node] of this.expressions.entries()) {
node = node.unwrapAll();
node = (node.unfoldSoak(o) || node);
if (node instanceof Block) {
compiledNodes.push(node.compileNode(o));
} else if (top) {
node.front = true;
fragments = node.compileToFragments(o);
if (!node.isStatement(o)) {
fragments.unshift(this.makeCode(("" + (this.tab))));
fragments.push(this.makeCode(";"));
}
compiledNodes.push(fragments);
} else {
compiledNodes.push(node.compileToFragments(o, LEVEL_LIST));
}
}
if (top) {
if (this.spaced) {
return [].concat(this.joinFragmentArrays(compiledNodes, "\n\n"), this.makeCode("\n"));
} else {
return this.joinFragmentArrays(compiledNodes, "\n");
}
}
if (compiledNodes.length) {
answer = this.joinFragmentArrays(compiledNodes, ", ");
} else {
answer = [this.makeCode("void 0")];
}
if (compiledNodes.length > 1 && o.level >= LEVEL_LIST) {
return this.wrapInBraces(answer);
} else {
return answer;
}
}
compileRoot(o) {
var rest;
var preludeExps;
var ref;
o.indent = (o.bare ? "" : TAB);
o.level = LEVEL_TOP;
this.spaced = true;
o.scope = new Scope(null, this, null, (ref = o.referencedVars) != null ? ref : []);
for (var name of o.locals || []) {
o.scope.parameter(name);
}
var prelude = [];
if (!o.bare) {
preludeExps = this.expressions.map((exp, i) => {
if (!(exp.unwrap() instanceof Comment)) {
break;
}
return exp;
});
rest = this.expressions.slice(preludeExps.length);
this.expressions = preludeExps;
if (preludeExps.length) {
prelude = this.compileNode(merge(o, {
indent: ""
}));
prelude.push(this.makeCode("\n"));
}
this.expressions = rest;
}
var fragments = this.compileWithDeclarations(o);
if (o.bare) {
return fragments;
}
return [].concat(
prelude,
this.makeCode("(function() {\n"),
fragments,
this.makeCode("\n}).call(this);\n")
);
}
compileWithDeclarations(o) {
var assigns;
var declars;
var spaced;
var rest;
var fragments = [];
var post = [];
for (var [i, exp] of this.expressions.entries()) {
exp = exp.unwrap();
if (!(exp instanceof Comment || exp instanceof Literal)) {
break;
}
}
o = merge(o, {
level: LEVEL_TOP
});
if (i) {
rest = this.expressions.splice(i, 9000000000);
[spaced, this.spaced] = [this.spaced, false];
[fragments, this.spaced] = [this.compileNode(o), spaced];
this.expressions = rest;
}
post = this.compileNode(o);
var {
scope
} = o;
if (scope.expressions === this) {
declars = o.scope.hasDeclarations();
assigns = scope.hasAssignments;
if (declars || assigns) {
if (i) {
fragments.push(this.makeCode("\n"));
}
fragments.push(this.makeCode(((this.tab) + "var ")));
if (declars) {
fragments.push(this.makeCode(scope.declaredVariables().join(", ")));
}
if (assigns) {
if (declars) {
fragments.push(this.makeCode((",\n" + (this.tab + TAB))));
}
fragments.push(this.makeCode(scope.assignedVariables().join((",\n" + (this.tab + TAB)))));
}
fragments.push(this.makeCode((";\n" + ((this.spaced ? "\n" : "")))));
} else if (fragments.length && post.length) {
fragments.push(this.makeCode("\n"));
}
}
return fragments.concat(post);
}
static wrap(nodes) {
if (nodes.length === 1 && nodes[0] instanceof Block) {
return nodes[0];
}
return new Block(nodes);
}
};
exports.Literal = class Literal extends Base {
constructor(value) {
super(...arguments);
this.value = value;
}
assigns(name) {
return name === this.value;
}
compileNode(o) {
return [this.makeCode(this.value)];
}
toString() {
return (" " + ((() => {
if (this.isStatement()) {
return super.toString(...arguments);
} else {
return this.constructor.name;
}
})()) + ": " + (this.value));
}
};
exports.NumberLiteral = class NumberLiteral extends Literal {};
exports.InfinityLiteral = class InfinityLiteral extends NumberLiteral {
compileNode() {
return [this.makeCode("2e308")];
}
};
exports.NaNLiteral = class NaNLiteral extends NumberLiteral {
constructor() {
super("NaN");
}
compileNode(o) {
var code = [this.makeCode("0/0")];
if (o.level >= LEVEL_OP) {
return this.wrapInBraces(code);
} else {
return code;
}
}
};
exports.StringLiteral = class StringLiteral extends Literal {};
exports.RegexLiteral = class RegexLiteral extends Literal {};
exports.PassthroughLiteral = class PassthroughLiteral extends Literal {};
exports.IdentifierLiteral = class IdentifierLiteral extends Literal {};
exports.PropertyName = class PropertyName extends Literal {};
exports.StatementLiteral = class StatementLiteral extends Literal {
jumps(o) {
if (this.value === "break" && !(((typeof o !== "undefined" && o !== null ? o.loop : void 0)) || ((typeof o !== "undefined" && o !== null ? o.block : void 0)))) {
return this;
}
if (this.value === "continue" && !((typeof o !== "undefined" && o !== null ? o.loop : void 0))) {
return this;
}
}
compileNode(o) {
return [this.makeCode(("" + (this.tab) + (this.value) + ";"))];
}
};
exports.ThisLiteral = class ThisLiteral extends Literal {
constructor() {
super("this");
}
compileNode(o) {
var ref;
var code = ((((ref = o.scope.method) != null ? ref.bound : void 0)) ? o.scope.method.context : this.value);
return [this.makeCode(code)];
}
};
exports.UndefinedLiteral = class UndefinedLiteral extends Literal {
constructor() {
super("undefined");
}
compileNode(o) {
return [this.makeCode((o.level >= LEVEL_ACCESS ? "(void 0)" : "void 0"))];
}
};
exports.NullLiteral = class NullLiteral extends Literal {
constructor() {
super("null");
}
};
exports.BooleanLiteral = class BooleanLiteral extends Literal {};
exports.Return = class Return extends Base {
constructor(expression) {
super(...arguments);
this.expression = expression;
}
compileToFragments(o, level) {
var ref;
var expr = (ref = this.expression) != null ? ref.makeReturn() : void 0;
if (expr && !(expr instanceof Return)) {
return expr.compileToFragments(o, level);
} else {
return super.compileToFragments(o, level);
}
}
compileNode(o) {
var answer = [];
answer.push(this.makeCode(this.tab + ("return" + ((this.expression ? " " : "")))));
if (this.expression) {
answer = answer.concat(this.expression.compileToFragments(o, LEVEL_PAREN));
}
answer.push(this.makeCode(";"));
return answer;
}
};
exports.YieldReturn = class YieldReturn extends Return {
compileNode(o) {
if (o.scope.parent == null) {
this.error("yield can only occur inside functions");
}
return super.compileNode(...arguments);
}
};
exports.Value = class Value extends Base {
constructor(base, props, tag) {
super(...arguments);
if (!props && base instanceof Value) {
return base;
}
this.base = base;
this.properties = props || [];
if (tag) {
this[tag] = true;
}
return this;
}
add(props) {
this.properties = this.properties.concat(props);
return this;
}
hasProperties() {
return !!this.properties.length;
}
bareLiteral(type) {
return !this.properties.length && this.base instanceof type;
}
isArray() {
return this.bareLiteral(Arr);
}
isRange() {
return this.bareLiteral(Range);
}
isComplex() {
return this.hasProperties() || this.base.isComplex();
}
isAssignable() {
return this.hasProperties() || this.base.isAssignable();
}
isNumber() {
return this.bareLiteral(NumberLiteral);
}
isString() {
return this.bareLiteral(StringLiteral);
}
isRegex() {
return this.bareLiteral(RegexLiteral);
}
isUndefined() {
return this.bareLiteral(UndefinedLiteral);
}
isNull() {
return this.bareLiteral(NullLiteral);
}
isBoolean() {
return this.bareLiteral(BooleanLiteral);
}
isAtomic() {
for (var node of this.properties.concat(this.base)) {
if (node.soak || node instanceof Call) {
return false;
}
}
return true;
}
isNotCallable() {
return this.isNumber() || this.isString() || this.isRegex() || this.isArray() || this.isRange() || this.isSplice() || this.isObject() || this.isUndefined() || this.isNull() || this.isBoolean();
}
isStatement(o) {
return !this.properties.length && this.base.isStatement(o);
}
assigns(name) {
return !this.properties.length && this.base.assigns(name);
}
jumps(o) {
return !this.properties.length && this.base.jumps(o);
}
isObject(onlyGenerated) {
if (this.properties.length) {
return false;
}
return (this.base instanceof Obj) && (!onlyGenerated || this.base.generated);
}
isSplice() {
return lastProp instanceof Slice;
}
looksStatic(className) {
var ref;
return this.base.value === className && this.properties.length === 1 && (((ref = this.properties[0].name) != null ? ref.value : void 0)) !== "prototype";
}
unwrap() {
return (this.properties.length ? this : this.base);
}
cacheReference(o) {
var name;
var nref;
var bref;
if (this.properties.length < 2 && !this.base.isComplex() && !((typeof name !== "undefined" && name !== null ? name.isComplex() : void 0))) {
return [this, this];
}
var base = new Value(this.base, this.properties.slice(0, -1));
if (base.isComplex()) {
bref = new IdentifierLiteral(o.scope.freeVariable("base"));
base = new Value(new Parens(new Assign(bref, base)));
}
if (!name) {
return [base, bref];
}
if (name.isComplex()) {
nref = new IdentifierLiteral(o.scope.freeVariable("name"));
name = new Index(new Assign(nref, name.index));
nref = new Index(nref);
}
return [base.add(name), new Value(bref || base.base, [nref || name])];
}
compileNode(o) {
this.base.front = this.front;
var props = this.properties;
var fragments = this.base.compileToFragments(o, ((() => {
if (props.length) {
return LEVEL_ACCESS;
} else {
return null;
}
})()));
if (props.length && SIMPLENUM.test(fragmentsToText(fragments))) {
fragments.push(this.makeCode("."));
}
for (var prop of props) {
fragments.push(...(prop.compileToFragments(o)));
}
return fragments;
}
unfoldSoak(o) {
return (this.unfoldedSoak != null ? this.unfoldedSoak : this.unfoldedSoak = (() => {
var ref;
var snd;
var fst;
var ifn;
if (ifn = this.base.unfoldSoak(o)) {
ifn.body.properties.push(...this.properties);
return ifn;
}
for (var [i, prop] of this.properties.entries()) {
if (prop.soak) {
prop.soak = false;
fst = new Value(this.base, this.properties.slice(0, i));
snd = new Value(this.base, this.properties.slice(i));
if (fst.isComplex()) {
ref = new IdentifierLiteral(o.scope.freeVariable("ref"));
fst = new Parens(new Assign(ref, fst));
snd.base = ref;
}
return new If(new Existence(fst), snd, {
soak: true
});
}
}
return false;
})());
}
};
exports.Comment = class Comment extends Base {
constructor(comment) {
super(...arguments);
this.comment = comment;
}
compileNode(o, level) {
var comment = this.comment.replace(/^(\s*)#(?=\s)/gm, "$1 *");
var code = ("/*" + (multident(comment, this.tab)) + ((() => {
if (comment.includes("\n")) {
return ("\n" + (this.tab));
} else {
return "";
}
})()) + " */");
if ((level || o.level) === LEVEL_TOP) {
code = o.indent + code;
}
return [this.makeCode("\n"), this.makeCode(code)];
}
};
exports.Call = class Call extends Base {
constructor(variable, args = [], soak) {
super(...arguments);
this.variable = variable;
this.args = args;
this.soak = soak;
this.isNew = false;
if (this.variable instanceof Value && this.variable.isNotCallable()) {
this.variable.error("literal is not a function");
}
}
newInstance() {
var ref;
var base = (((ref = this.variable) != null ? ref.base : void 0)) || this.variable;
if (base instanceof Call && !base.isNew) {
base.newInstance();
} else {
this.isNew = true;
}
return this;
}
unfoldSoak(o) {
var ifn;
var rite;
var left;
if (this.soak) {
if (this instanceof SuperCall) {
left = new Literal(this.superReference(o));
rite = new Value(left);
} else {
if (ifn = unfoldSoak(o, this, "variable")) {
return ifn;
}
[left, rite] = new Value(this.variable).cacheReference(o);
}
rite = new Call(rite, this.args);
rite.isNew = this.isNew;
left = new Literal(("typeof " + (left.compile(o)) + " === \"function\""));
return new If(left, new Value(rite), {
soak: true
});
}
call = this;
var list = [];
while (true) {
if (call.variable instanceof Call) {
list.push(call);
call = call.variable;
continue;
}
if (!(call.variable instanceof Value)) {
break;
}
list.push(call);
if (!((call = call.variable.base) instanceof Call)) {
break;
}
}
for (var call of list.reverse()) {
if (ifn) {
if (call.variable instanceof Call) {
call.variable = ifn;
} else {
call.variable.base = ifn;
}
}
ifn = unfoldSoak(o, call, "variable");
}
return ifn;
}
compileNode(o) {
var preface;
var ref;
((ref = this.variable) != null ? ref.front = this.front : void 0);
var compiledArray = Splat.compileSplattedArray(o, this.args, true);
if (compiledArray.length) {
return this.compileSplat(o, compiledArray);
}
var compiledArgs = [];
for (var [argIndex, arg] of this.args.entries()) {
if (argIndex) {
compiledArgs.push(this.makeCode(", "));
}
compiledArgs.push(...(arg.compileToFragments(o, LEVEL_LIST)));
}
var fragments = [];
if (this instanceof SuperCall) {
preface = this.superReference(o) + (".call(" + (this.superThis(o)));
if (compiledArgs.length) {
preface += ", ";
}
fragments.push(this.makeCode(preface));
} else {
if (this.isNew) {
fragments.push(this.makeCode("new "));
}
fragments.push(...this.variable.compileToFragments(o, LEVEL_ACCESS));
fragments.push(this.makeCode("("));
}
fragments.push(...compiledArgs);
fragments.push(this.makeCode(")"));
return fragments;
}
compileSplat(o, splatArgs) {
var fun;
var ref;
var name;
var idt;
if (this instanceof SuperCall) {
return [].concat(
this.makeCode(((this.superReference(o)) + ".apply(" + (this.superThis(o)) + ", ")),
splatArgs,
this.makeCode(")")
);
}
if (this.isNew) {
idt = this.tab + TAB;
return [].concat(this.makeCode(
("(function(func, args, ctor) {\n" + (idt) + "ctor.prototype = func.prototype;\n" + (idt) + "var child = new ctor, result = func.apply(child, args);\n" + (idt) + "return Object(result) === result ? result : child;\n" + (this.tab) + "})(")
), (this.variable.compileToFragments(o, LEVEL_LIST)), this.makeCode(", "), splatArgs, this.makeCode(", function(){})"));
}
var answer = [];
var base = new Value(this.variable);
if ((name = base.properties.pop()) && base.isComplex()) {
ref = o.scope.freeVariable("ref");
answer = answer.concat(
this.makeCode(("(" + (ref) + " = ")),
(base.compileToFragments(o, LEVEL_LIST)),
this.makeCode(")"),
name.compileToFragments(o)
);
} else {
fun = base.compileToFragments(o, LEVEL_ACCESS);
if (SIMPLENUM.test(fragmentsToText(fun))) {
fun = this.wrapInBraces(fun);
}
if (name) {
ref = fragmentsToText(fun);
fun.push(...(name.compileToFragments(o)));
} else {
ref = "null";
}
answer = answer.concat(fun);
}
return answer = answer.concat(this.makeCode((".apply(" + (ref) + ", ")), splatArgs, this.makeCode(")"));
}
};
exports.SuperCall = class SuperCall extends Call {
constructor(args) {
super(
null,
typeof args !== "undefined" && args !== null ? args : [new Splat(new IdentifierLiteral("arguments"))]
);
this.isBare = typeof args !== "undefined" && args !== null;
}
superReference(o) {
var accesses;
var nref;
var base;
var bref;
var {
klass,
name,
variable
};
var method = o.scope.namedMethod();
if (method != null ? method.klass : void 0) {
{
klass,
name,
variable
} = method;
if (klass.isComplex()) {
bref = new IdentifierLiteral(o.scope.parent.freeVariable("base"));
base = new Value(new Parens(new Assign(bref, klass)));
variable.base = base;
variable.properties.splice(0, klass.properties.length);
}
if (name.isComplex() || (name instanceof Index && name.index.isAssignable())) {
nref = new IdentifierLiteral(o.scope.parent.freeVariable("name"));
name = new Index(new Assign(nref, name.index));
variable.properties.pop();
variable.properties.push(name);
}
accesses = [new Access(new PropertyName("__super__"))];
if (method.static) {
accesses.push(new Access(new PropertyName("constructor")));
}
accesses.push((() => {
if (nref != null) {
return new Index(nref);
} else {
return name;
}
})());
return (new Value(bref != null ? bref : klass, accesses)).compile(o);
} else if (method != null ? method.ctor : void 0) {
return ((method.name) + ".__super__.constructor");
} else {
return this.error("cannot call super outside of an instance method.");
}
}
superThis(o) {
var method = o.scope.method;
return (method && !method.klass && method.context) || "this";
}
};
exports.RegexWithInterpolations = class RegexWithInterpolations extends Call {
constructor(args = []) {
super((new Value(new IdentifierLiteral("RegExp"))), args, false);
}
};
exports.Extends = class Extends extends Base {
constructor(child, parent) {
super(...arguments);
this.child = child;
this.parent = parent;
}
compileToFragments(o) {
return new Call(new Value(new Literal(utility("extend", o))), [this.child, this.parent]).compileToFragments(o);
}
};
exports.Access = class Access extends Base {
constructor(name, tag) {
super(...arguments);
this.name = name;
this.name.asKey = true;
this.soak = tag === "soak";
}
compileToFragments(o) {
var name = this.name.compileToFragments(o);
var node = this.name.unwrap();
if (node instanceof PropertyName) {
if (JS_FORBIDDEN.includes(node.value)) {
return [this.makeCode("[\""), ...name, this.makeCode("\"]")];
} else {
return [this.makeCode("."), ...name];
}
} else {
return [this.makeCode("["), ...name, this.makeCode("]")];
}
}
};
exports.Index = class Index extends Base {
constructor(index) {
super(...arguments);
this.index = index;
}
compileToFragments(o) {
return [].concat(
this.makeCode("["),
this.index.compileToFragments(o, LEVEL_PAREN),
this.makeCode("]")
);
}
isComplex() {
return this.index.isComplex();
}
};
exports.Range = class Range extends Base {
constructor(from, to, tag) {
super(...arguments);
this.from = from;
this.to = to;
this.exclusive = tag === "exclusive";
this.equals = (this.exclusive ? "" : "=");
}
compileVariables(o) {
var step;
o = merge(o, {
top: true
});
var isComplex = del(o, "isComplex");
[this.fromC, this.fromVar] = this.cacheToCodeFragments(this.from.cache(o, LEVEL_LIST, isComplex));
[this.toC, this.toVar] = this.cacheToCodeFragments(this.to.cache(o, LEVEL_LIST, isComplex));
if (step = del(o, "step")) {
[this.step, this.stepVar] = this.cacheToCodeFragments(step.cache(o, LEVEL_LIST, isComplex));
}
this.fromNum = (() => {
if (this.from.isNumber()) {
return Number(this.fromVar);
} else {
return null;
}
})();
this.toNum = (() => {
if (this.to.isNumber()) {
return Number(this.toVar);
} else {
return null;
}
})();
return this.stepNum = (() => {
if (typeof step !== "undefined" && step !== null ? step.isNumber() : void 0) {
return Number(this.stepVar);
} else {
return null;
}
})();
}
compileNode(o) {
if (!this.fromVar) {
this.compileVariables(o);
}
if (!o.index) {
return this.compileArray(o);
}
var known = this.fromNum != null && this.toNum != null;
var idx = del(o, "index");
var idxName = del(o, "name");
var namedIndex = idxName && idxName !== idx;
var varPart = ((idx) + " = " + (this.fromC));
if (this.toC !== this.toVar) {
varPart += (", " + (this.toC));
}
if (this.step !== this.stepVar) {
varPart += (", " + (this.step));
}
var [lt, gt] = [((idx) + " <" + (this.equals)), ((idx) + " >" + (this.equals))];
var condPart = (() => {
var cond;
if (this.stepNum != null) {
if (this.stepNum > 0) {
return ((lt) + " " + (this.toVar));
} else {
return ((gt) + " " + (this.toVar));
}
} else if (known) {
var [from, to] = [this.fromNum, this.toNum];
if (from <= to) {
return ((lt) + " " + (to));
} else {
return ((gt) + " " + (to));
}
} else {
cond = (() => {
if (this.stepVar) {
return ((this.stepVar) + " > 0");
} else {
return ((this.fromVar) + " <= " + (this.toVar));
}
})();
return ((cond) + " ? " + (lt) + " " + (this.toVar) + " : " + (gt) + " " + (this.toVar));
}
})();
var stepPart = (() => {
if (this.stepVar) {
return ((idx) + " += " + (this.stepVar));
} else if (known) {
if (namedIndex) {
if (from <= to) {
return ("++" + (idx));
} else {
return ("--" + (idx));
}
} else if (from <= to) {
return ((idx) + "++");
} else {
return ((idx) + "--");
}
} else if (namedIndex) {
return ((cond) + " ? ++" + (idx) + " : --" + (idx));
} else {
return ((cond) + " ? " + (idx) + "++ : " + (idx) + "--");
}
})();
if (namedIndex) {
varPart = ((idxName) + " = " + (varPart));
}
if (namedIndex) {
stepPart = ((idxName) + " = " + (stepPart));
}
return [this.makeCode(((varPart) + "; " + (condPart) + "; " + (stepPart)))];
}
compileArray(o) {
var args;
var cond;
var vars;
var body;
var range;
var known = this.fromNum != null && this.toNum != null;
if (known && Math.abs(this.fromNum - this.toNum) <= 20) {
range = (function() {
var ref;
var results = [];
for (var j = ref = this.fromNum, ref1 = this.toNum; (ref <= ref1 ? j <= ref1 : j >= ref1); (ref <= ref1 ? j++ : j--)) {
results.push(j);
}
return results;
}).apply(this);
if (this.exclusive) {
range.pop();
}
return [this.makeCode(("[" + (range.join(", ")) + "]"))];
}
var idt = this.tab + TAB;
var i = o.scope.freeVariable("i", {
single: true
});
var result = o.scope.freeVariable("results");
var pre = ("\n" + (idt) + (result) + " = [];");
if (known) {
o.index = i;
body = fragmentsToText(this.compileNode(o));
} else {
vars = ((i) + " = " + (this.fromC)) + (() => {
if (this.toC !== this.toVar) {
return (", " + (this.toC));
} else {
return "";
}
})();
cond = ((this.fromVar) + " <= " + (this.toVar));
body = ("var " + (vars) + "; " + (cond) + " ? " + (i) + " <" + (this.equals) + " " + (this.toVar) + " : " + (i) + " >" + (this.equals) + " " + (this.toVar) + "; " + (cond) + " ? " + (i) + "++ : " + (i) + "--");
}
var post = ("{ " + (result) + ".push(" + (i) + "); }\n" + (idt) + "return " + (result) + ";\n" + (o.indent));
var hasArgs = function(node) {
return typeof node !== "undefined" && node !== null ? node.contains(isLiteralArguments) : void 0;
};
if (hasArgs(this.from) || hasArgs(this.to)) {
args = ", arguments";
}
return [this.makeCode(
("(function() {" + (pre) + "\n" + (idt) + "for (" + (body) + ")" + (post) + "}).apply(this" + (args != null ? args : "") + ")")
)];
}
};
exports.Slice = class Slice extends Base {
constructor(range) {
this.range = range;
super();
}
compileNode(o) {
var toStr;
var compiledText;
var compiled;
var {
to,
from
} = this.range;
var fromCompiled = from && from.compileToFragments(o, LEVEL_PAREN) || [this.makeCode("0")];
if (to) {
compiled = to.compileToFragments(o, LEVEL_PAREN);
compiledText = fragmentsToText(compiled);
if (!(!this.range.exclusive && +compiledText === -1)) {
toStr = ", " + (() => {
if (this.range.exclusive) {
return compiledText;
} else if (to.isNumber()) {
return ("" + (+compiledText + 1));
} else {
compiled = to.compileToFragments(o, LEVEL_ACCESS);
return ("+" + (fragmentsToText(compiled)) + " + 1 || 9e9");
}
})();
}
}
return [
this.makeCode((".slice(" + (fragmentsToText(fromCompiled)) + (toStr || "") + ")"))
];
}
};
exports.Obj = class Obj extends Base {
constructor(props, generated = false) {
super(...arguments);
this.generated = generated;
this.objects = this.properties = props || [];
}
compileNode(o) {
var value;
var key;
var oref;
var props = this.properties;
if (this.generated) {
for (var node of props) {
if (node instanceof Value) {
node.error("cannot have an implicit value in an implicit object");
}
}
}
for (var [dynamicIndex, prop] of props.entries()) {
if ((prop.variable || prop).base instanceof Parens) {
break;
}
}
var hasDynamic = dynamicIndex < props.length;
var idt = o.indent += TAB;
var lastNoncom = this.lastNonComment(this.properties);
var answer = [];
if (hasDynamic) {
oref = o.scope.freeVariable("obj");
answer.push(this.makeCode(("(\n" + (idt) + (oref) + " = ")));
}
answer.push(
this.makeCode(("{" + ((props.length === 0 || dynamicIndex === 0 ? "}" : "\n"))))
);
for (var [i, prop] of props.entries()) {
if (i === dynamicIndex) {
if (i !== 0) {
answer.push(this.makeCode(("\n" + (idt) + "}")));
}
answer.push(this.makeCode(",\n"));
}
var join = (() => {
if (i === props.length - 1 || i === dynamicIndex - 1) {
return "";
} else if (prop === lastNoncom || prop instanceof Comment) {
return "\n";
} else {
return ",\n";
}
})();
var indent = (prop instanceof Comment ? "" : idt);
if (hasDynamic && i < dynamicIndex) {
indent += TAB;
}
if (prop instanceof Assign) {
if (prop.context !== "object") {
prop.operatorToken.error(("unexpected " + (prop.operatorToken.value)));
}
if (prop.variable instanceof Value && prop.variable.hasProperties()) {
prop.variable.error("invalid object key");
}
}
if (prop instanceof Value && prop.this) {
prop = new Assign(prop.properties[0].name, prop, "object");
}
if (!(prop instanceof Comment)) {
if (i < dynamicIndex) {
if (!(prop instanceof Assign)) {
prop = new Assign(prop, prop, "object");
}
(prop.variable.base || prop.variable).asKey = true;
} else {
if (prop instanceof Assign) {
key = prop.variable;
value = prop.value;
} else {
[key, value] = prop.base.cache(o);
}
prop = new Assign((new Value((new IdentifierLiteral(oref)), [new Access(key)])), value);
}
}
if (indent) {
answer.push(this.makeCode(indent));
}
answer.push(...prop.compileToFragments(o, LEVEL_TOP));
if (join) {
answer.push(this.makeCode(join));
}
}
if (hasDynamic) {
answer.push(this.makeCode((",\n" + (idt) + (oref) + "\n" + (this.tab) + ")")));
} else if (props.length !== 0) {
answer.push(this.makeCode(("\n" + (this.tab) + "}")));
}
if (this.front && !hasDynamic) {
return this.wrapInBraces(answer);
} else {
return answer;
}
}
assigns(name) {
for (var prop of this.properties) {
if (prop.assigns(name)) {
return true;
}
}
return false;
}
};
exports.Arr = class Arr extends Base {
constructor(objs) {
super(...arguments);
this.objects = objs || [];
}
compileNode(o) {
if (!this.objects.length) {
return [this.makeCode("[]")];
}
o.indent += TAB;
var answer = Splat.compileSplattedArray(o, this.objects);
if (answer.length) {
return answer;
}
answer = [];
var compiledObjs = (this.objects.map(obj => {
return obj.compileToFragments(o, LEVEL_LIST);
}));
for (var [index, fragments] of compiledObjs.entries()) {
if (index) {
answer.push(this.makeCode(", "));
}
answer.push(...fragments);
}
if (fragmentsToText(answer).indexOf("\n") >= 0) {
answer.unshift(this.makeCode(("[\n" + (o.indent))));
answer.push(this.makeCode(("\n" + (this.tab) + "]")));
} else {
answer.unshift(this.makeCode("["));
answer.push(this.makeCode("]"));
}
return answer;
}
assigns(name) {
for (var obj of this.objects) {
if (obj.assigns(name)) {
return true;
}
}
return false;
}
};
exports.Class = class Class extends Base {
constructor(variable, parent, body = new Block()) {
super(...arguments);
this.variable = variable;
this.parent = parent;
this.body = body;
this.boundFuncs = [];
this.body.classBody = true;
}
determineName() {
var message;
if (!this.variable) {
return this.defaultClassVariableName;
}
var node = (() => {
if (tail) {
return tail instanceof Access && tail.name;
} else {
return this.variable.base;
}
})();
if (!(node instanceof IdentifierLiteral || node instanceof PropertyName)) {
return this.defaultClassVariableName;
}
var name = node.value;
if (!tail) {
message = isUnassignable(name);
if (message) {
this.variable.error(message);
}
}
if (JS_FORBIDDEN.includes(name)) {
return ("_" + (name));
} else {
return name;
}
}
setContext(name) {
return this.body.traverseChildren(false, function(node) {
if (node.classBody) {
return false;
}
if (node instanceof ThisLiteral) {
return node.value = name;
} else if (node instanceof Code) {
if (node.bound) {
return node.context = name;
}
}
});
}
addBoundFunctions(o) {
for (var bvar of this.boundFuncs) {
var lhs = (new Value((new ThisLiteral()), [new Access(bvar)])).compile(o);
this.ctor.body.unshift(
new Literal(((lhs) + " = " + (utility("bind", o)) + "(" + (lhs) + ", this)"))
);
}
return;
}
addProperties(node, name, o) {
var props = node.base.properties.slice(0);
var exprs = (function() {
var acc;
var func;
var base;
var assign;
var results;
results = [];
while (assign = props.shift()) {
if (assign instanceof Assign) {
base = assign.variable.base;
delete assign.context;
func = assign.value;
if (base.value === "constructor") {
if (this.ctor) {
assign.error("cannot define more than one constructor in a class");
}
if (func.bound) {
assign.error("cannot define a constructor as a bound function");
}
if (func instanceof Code) {
assign = this.ctor = func;
} else {
this.externalCtor = o.classScope.freeVariable("ctor");
assign = new Assign(new IdentifierLiteral(this.externalCtor), func);
}
} else {
if (assign.variable["this"]) {
func["static"] = true;
} else {
acc = (base.isComplex() ? new Index(base) : new Access(base));
assign.variable = new Value(
new IdentifierLiteral(name),
[new Access(new PropertyName("prototype")), acc]
);
if (func instanceof Code && func.bound) {
this.boundFuncs.push(base);
func.bound = false;
}
}
}
}
results.push(assign);
}
return results;
}).call(this);
return compact(exprs);
}
walkBody(name, o) {
return this.traverseChildren(false, child => {
var exps;
var cont = true;
if (child instanceof Class) {
return false;
}
if (child instanceof Block) {
for (var [i, node] of (exps = child.expressions).entries()) {
if (node instanceof Assign && node.variable.looksStatic(name)) {
node.value.static = true;
} else if (node instanceof Value && node.isObject(true)) {
cont = false;
exps[i] = this.addProperties(node, name, o);
}
}
child.expressions = exps = flatten(exps);
}
return cont && !(child instanceof Class);
});
}
hoistDirectivePrologue() {
var node;
var index = 0;
var {
expressions
} = this.body;
while (((node = expressions[index]) && node instanceof Comment || node instanceof Value) && node.isString()) {
++index;
}
return this.directives = expressions.splice(0, index);
}
ensureConstructor(name) {
if (!this.ctor) {
this.ctor = new Code();
if (this.externalCtor) {
this.ctor.body.push(new Literal(((this.externalCtor) + ".apply(this, arguments)")));
} else if (this.parent) {
this.ctor.body.push(new Literal(((name) + ".__super__.constructor.apply(this, arguments)")));
}
this.ctor.body.makeReturn();
this.body.expressions.unshift(this.ctor);
}
this.ctor.ctor = this.ctor.name = name;
this.ctor.klass = null;
return this.ctor.noReturn = true;
}
compileNode(o) {
var superClass;
var argumentsNode;
var jumpNode;
if (jumpNode = this.body.jumps()) {
jumpNode.error("Class bodies cannot contain pure statements");
}
if (argumentsNode = this.body.contains(isLiteralArguments)) {
argumentsNode.error("Class bodies shouldn't reference arguments");
}
var name = this.determineName();
var lname = new IdentifierLiteral(name);
var func = new Code([], Block.wrap([this.body]));
var args = [];
o.classScope = func.makeScope(o.scope);
this.hoistDirectivePrologue();
this.setContext(name);
this.walkBody(name, o);
this.ensureConstructor(name);
this.addBoundFunctions(o);
this.body.spaced = true;
this.body.expressions.push(lname);
if (this.parent) {
superClass = new IdentifierLiteral(o.classScope.freeVariable("superClass", {
reserve: false
}));
this.body.expressions.unshift(new Extends(lname, superClass));
func.params.push(new Param(superClass));
args.push(this.parent);
}
this.body.expressions.unshift(...this.directives);
var klass = new Parens(new Call(func, args));
if (this.variable) {
klass = new Assign(this.variable, klass, null, {
this: this
});
}
return klass.compileToFragments(o);
}
};
exports.ModuleDeclaration = class ModuleDeclaration extends Base {
constructor(clause, source) {
super(...arguments);
this.clause = clause;
this.source = source;
this.checkSource();
}
checkSource() {
if (this.source != null && this.source instanceof StringWithInterpolations) {
return this.source.error(
"the name of the module to be imported from must be an uninterpolated string"
);
}
}
checkScope(o, moduleDeclarationType) {
if (o.indent.length !== 0) {
return this.error(((moduleDeclarationType) + " statements must be at top-level scope"));
}
}
};
exports.ImportDeclaration = class ImportDeclaration extends ModuleDeclaration {
compileNode(o) {
var ref;
this.checkScope(o, "import");
o.importedSymbols = [];
var code = [];
code.push(this.makeCode(((this.tab) + "import ")));
if (this.clause != null && this.clause.length !== 0) {
for (var [index, subclause] of this.clause.entries()) {
if (index !== 0) {
code.push(this.makeCode(", "));
}
code = code.concat(subclause.compileNode(o));
}
}
if ((((ref = this.source) != null ? ref.value : void 0)) != null) {
if (this.clause !== null) {
code.push(this.makeCode(" from "));
}
code.push(this.makeCode(this.source.value));
}
code.push(this.makeCode(";"));
return code;
}
};
exports.ExportDeclaration = class ExportDeclaration extends ModuleDeclaration {
compileNode(o) {
var ref;
this.checkScope(o, "export");
var code = [];
code.push(this.makeCode(((this.tab) + "export ")));
if (this instanceof ExportDefaultDeclaration) {
code.push(this.makeCode("default "));
}
if (!(this instanceof ExportDefaultDeclaration) && (this.clause instanceof Assign || this.clause instanceof Class)) {
code.push(this.makeCode("var "));
this.clause.moduleDeclaration = "export";
}
if (this.clause.body != null && this.clause.body instanceof Block) {
code = code.concat(this.clause.compileToFragments(o, LEVEL_TOP));
} else {
code = code.concat(this.clause.compileNode(o));
}
if ((((ref = this.source) != null ? ref.value : void 0)) != null) {
code.push(this.makeCode((" from " + (this.source.value))));
}
code.push(this.makeCode(";"));
return code;
}
};
exports.ExportNamedDeclaration = class ExportNamedDeclaration extends ExportDeclaration {};
exports.ExportDefaultDeclaration = class ExportDefaultDeclaration extends ExportDeclaration {};
exports.ExportAllDeclaration = class ExportAllDeclaration extends ExportDeclaration {};
exports.ModuleSpecifierList = class ModuleSpecifierList extends Base {
constructor(specifiers) {
super(...arguments);
this.specifiers = specifiers;
}
compileNode(o) {
var code = [];
o.indent += TAB;
var compiledList = (this.specifiers.map(specifier => {
return specifier.compileToFragments(o, LEVEL_LIST);
}));
if (this.specifiers.length !== 0) {
code.push(this.makeCode(("{\n" + (o.indent))));
for (var [index, fragments] of compiledList.entries()) {
if (index) {
code.push(this.makeCode((",\n" + (o.indent))));
}
code.push(...fragments);
}
code.push(this.makeCode("\n}"));
} else {
code.push(this.makeCode("{}"));
}
return code;
}
};
exports.ImportSpecifierList = class ImportSpecifierList extends ModuleSpecifierList {};
exports.ExportSpecifierList = class ExportSpecifierList extends ModuleSpecifierList {};
exports.ModuleSpecifier = class ModuleSpecifier extends Base {
constructor(original, alias, moduleDeclarationType) {
super(...arguments);
this.original = original;
this.alias = alias;
this.moduleDeclarationType = moduleDeclarationType;
this.identifier = (this.alias != null ? this.alias.value : this.original.value);
}
compileNode(o) {
o.scope.add(this.identifier, this.moduleDeclarationType);
var code = [];
code.push(this.makeCode(this.original.value));
if (this.alias != null) {
code.push(this.makeCode((" as " + (this.alias.value))));
}
return code;
}
};
exports.ImportSpecifier = class ImportSpecifier extends ModuleSpecifier {
constructor(imported, local) {
super(imported, local, "import");
}
compileNode(o) {
if (o.importedSymbols.includes(this.identifier) || o.scope.check(this.identifier)) {
this.error(("'" + (this.identifier) + "' has already been declared"));
} else {
o.importedSymbols.push(this.identifier);
}
return super.compileNode(o);
}
};
exports.ImportDefaultSpecifier = class ImportDefaultSpecifier extends ImportSpecifier {};
exports.ImportNamespaceSpecifier = class ImportNamespaceSpecifier extends ImportSpecifier {};
exports.ExportSpecifier = class ExportSpecifier extends ModuleSpecifier {
constructor(local, exported) {
super(local, exported, "export");
}
};
exports.Assign = class Assign extends Base {
constructor(variable, value, context, options = {}) {
super(...arguments);
this.variable = variable;
this.value = value;
this.context = context;
this.param = options.param, this.subpattern = options.subpattern, this.operatorToken = options.operatorToken, this.moduleDeclaration = options.moduleDeclaration, options;
}
isStatement(o) {
return ((typeof o !== "undefined" && o !== null ? o.level : void 0)) === LEVEL_TOP && this.context != null && (this.moduleDeclaration || this.context.includes("?"));
}
checkAssignability(o, varBase) {
if (Object.prototype.hasOwnProperty.call(o.scope.positions, varBase.value) && o.scope.variables[o.scope.positions[varBase.value]].type === "import") {
return varBase.error(("'" + (varBase.value) + "' is read-only"));
}
}
assigns(name) {
return this[(this.context === "object" ? "value" : "variable")].assigns(name);
}
unfoldSoak(o) {
return unfoldSoak(o, this, "variable");
}
compileNode(o) {
var varBase;
var ref1;
var ref;
var isValue;
if (isValue = this.variable instanceof Value) {
if (this.variable.isArray() || this.variable.isObject()) {
return this.compilePatternMatch(o);
}
if (this.variable.isSplice()) {
return this.compileSplice(o);
}
if (["||=", "&&=", "?="].includes(this.context)) {
return this.compileConditional(o);
}
if (["**=", "//=", "%%="].includes(this.context)) {
return this.compileSpecialMath(o);
}
}
if (this.value instanceof Code) {
if (this.value.static) {
this.value.klass = this.variable.base;
this.value.name = this.variable.properties[0];
this.value.variable = this.variable;
} else if ((((ref = this.variable.properties) != null ? ref.length : void 0)) >= 2) {
var [...properties, prototype, name] = this.variable.properties;
if ((((ref1 = prototype.name) != null ? ref1.value : void 0)) === "prototype") {
this.value.klass = new Value(this.variable.base, properties);
this.value.name = name;
this.value.variable = this.variable;
}
}
}
if (!this.context) {
varBase = this.variable.unwrapAll();
if (!varBase.isAssignable()) {
this.variable.error(("'" + (this.variable.compile(o)) + "' can't be assigned"));
}
if (!((typeof varBase.hasProperties === "function" ? varBase.hasProperties() : void 0))) {
if (this.moduleDeclaration) {
this.checkAssignability(o, varBase);
o.scope.add(varBase.value, this.moduleDeclaration);
} else if (this.param) {
o.scope.add(varBase.value, "var");
} else {
this.checkAssignability(o, varBase);
o.scope.find(varBase.value);
}
}
}
var val = this.value.compileToFragments(o, LEVEL_LIST);
if (isValue && this.variable.base instanceof Obj) {
this.variable.front = true;
}
var compiledName = this.variable.compileToFragments(o, LEVEL_LIST);
if (this.context === "object") {
if (JS_FORBIDDEN.includes(fragmentsToText(compiledName))) {
compiledName.unshift(this.makeCode("\""));
compiledName.push(this.makeCode("\""));
}
return compiledName.concat(this.makeCode(": "), val);
}
var answer = compiledName.concat(this.makeCode((" " + (this.context || "=") + " ")), val);
if (o.level <= LEVEL_LIST) {
return answer;
} else {
return this.wrapInBraces(answer);
}
}
compilePatternMatch(o) {
var {
variable: {
base: idx
},
value: obj
};
var ivar;
var rest;
var val;
var name;
var ref;
var message;
var acc;
var {
variable: {
base: idx
},
value: obj
};
var defaultValue;
var code;
var olen;
var top = o.level === LEVEL_TOP;
var {
value
} = this;
var {
objects
} = this.variable.base;
if (!(olen = objects.length)) {
code = value.compileToFragments(o);
return (() => {
if (o.level >= LEVEL_OP) {
return this.wrapInBraces(code);
} else {
return code;
}
})();
}
[obj] = objects;
if (olen === 1 && obj instanceof Expansion) {
obj.error("Destructuring assignment has no target");
}
var isObject = this.variable.isObject();
if (top && olen === 1 && !(obj instanceof Splat)) {
defaultValue = null;
if (obj instanceof Assign && obj.context === "object") {
{
variable: {
base: idx
},
value: obj
} = obj;
if (obj instanceof Assign) {
defaultValue = obj.value;
obj = obj.variable;
}
} else {
if (obj instanceof Assign) {
defaultValue = obj.value;
obj = obj.variable;
}
idx = (() => {
if (isObject) {
if (obj.this) {
return obj.properties[0].name;
} else {
return new PropertyName(obj.unwrap().value);
}
} else {
return new NumberLiteral(0);
}
})();
}
acc = idx.unwrap() instanceof PropertyName;
value = new Value(value);
value.properties.push(new ((acc ? Access : Index))(idx));
message = isUnassignable(obj.unwrap().value);
if (message) {
obj.error(message);
}
if (defaultValue) {
value = new Op("?", value, defaultValue);
}
return new Assign(obj, value, null, {
param: this.param
}).compileToFragments(o, LEVEL_TOP);
}
var vvar = value.compileToFragments(o, LEVEL_LIST);
var vvarText = fragmentsToText(vvar);
var assigns = [];
var expandedIdx = false;
if (!(value.unwrap() instanceof IdentifierLiteral) || this.variable.assigns(vvarText)) {
assigns.push([this.makeCode(((ref = o.scope.freeVariable("ref")) + " = ")), ...vvar]);
vvar = [this.makeCode(ref)];
vvarText = ref;
}
for (var [i, obj] of objects.entries()) {
idx = i;
if (!expandedIdx && obj instanceof Splat) {
name = obj.name.unwrap().value;
obj = obj.unwrap();
val = ((olen) + " <= " + (vvarText) + ".length ? " + (utility("slice", o)) + ".call(" + (vvarText) + ", " + (i));
if (rest = olen - i - 1) {
ivar = o.scope.freeVariable("i", {
single: true
});
val += (", " + (ivar) + " = " + (vvarText) + ".length - " + (rest) + ") : (" + (ivar) + " = " + (i) + ", [])");
} else {
val += ") : []";
}
val = new Literal(val);
expandedIdx = ((ivar) + "++");
} else if (!expandedIdx && obj instanceof Expansion) {
if (rest = olen - i - 1) {
if (rest === 1) {
expandedIdx = ((vvarText) + ".length - 1");
} else {
ivar = o.scope.freeVariable("i", {
single: true
});
val = new Literal(((ivar) + " = " + (vvarText) + ".length - " + (rest)));
expandedIdx = ((ivar) + "++");
assigns.push(val.compileToFragments(o, LEVEL_LIST));
}
}
continue;
} else {
if (obj instanceof Splat || obj instanceof Expansion) {
obj.error("multiple splats/expansions are disallowed in an assignment");
}
defaultValue = null;
if (obj instanceof Assign && obj.context === "object") {
{
variable: {
base: idx
},
value: obj
} = obj;
if (obj instanceof Assign) {
defaultValue = obj.value;
obj = obj.variable;
}
} else {
if (obj instanceof Assign) {
defaultValue = obj.value;
obj = obj.variable;
}
idx = (() => {
if (isObject) {
if (obj.this) {
return obj.properties[0].name;
} else {
return new PropertyName(obj.unwrap().value);
}
} else {
return new Literal(expandedIdx || idx);
}
})();
}
name = obj.unwrap().value;
acc = idx.unwrap() instanceof PropertyName;
val = new Value(new Literal(vvarText), [new ((acc ? Access : Index))(idx)]);
if (defaultValue) {
val = new Op("?", val, defaultValue);
}
}
if (name != null) {
message = isUnassignable(name);
if (message) {
obj.error(message);
}
}
assigns.push(new Assign(obj, val, null, {
param: this.param,
subpattern: true
}).compileToFragments(o, LEVEL_LIST));
}
if (!(top || this.subpattern)) {
assigns.push(vvar);
}
var fragments = this.joinFragmentArrays(assigns, ", ");
if (o.level < LEVEL_LIST) {
return fragments;
} else {
return this.wrapInBraces(fragments);
}
}
compileConditional(o) {
var fragments;
var [left, right] = this.variable.cacheReference(o);
if (!left.properties.length && left.base instanceof Literal && !(left.base instanceof ThisLiteral) && !o.scope.check(left.base.value)) {
this.variable.error(
("the variable \"" + (left.base.value) + "\" can't be assigned with " + (this.context) + " because it has not been declared before")
);
}
if (this.context.includes("?")) {
o.isExistentialEquals = true;
return new If(new Existence(left), right, {
type: "if"
}).addElse(new Assign(right, this.value, "=")).compileToFragments(o);
} else {
fragments = new Op(this.context.slice(0, -1), left, new Assign(right, this.value, "=")).compileToFragments(o);
if (o.level <= LEVEL_LIST) {
return fragments;
} else {
return this.wrapInBraces(fragments);
}
}
}
compileSpecialMath(o) {
var [left, right] = this.variable.cacheReference(o);
return new Assign(left, new Op(this.context.slice(0, -1), right, this.value)).compileToFragments(o);
}
compileSplice(o) {
var {
range: {
from,
to,
exclusive
}
} = this.variable.properties.pop();
var name = this.variable.compile(o);
if (from) {
var [fromDecl, fromRef] = this.cacheToCodeFragments(from.cache(o, LEVEL_OP));
} else {
fromDecl = fromRef = "0";
}
if (to) {
if (((from != null ? from.isNumber() : void 0)) && to.isNumber()) {
to = to.compile(o) - fromRef;
if (!exclusive) {
to += 1;
}
} else {
to = to.compile(o, LEVEL_ACCESS) + " - " + fromRef;
if (!exclusive) {
to += " + 1";
}
}
} else {
to = "9e9";
}
var [valDef, valRef] = this.value.cache(o, LEVEL_LIST);
var answer = [].concat(this.makeCode(
("[].splice.apply(" + (name) + ", [" + (fromDecl) + ", " + (to) + "].concat(")
), valDef, this.makeCode(")), "), valRef);
if (o.level > LEVEL_TOP) {
return this.wrapInBraces(answer);
} else {
return answer;
}
}
};
exports.Code = class Code extends Base {
constructor(params, body, tag) {
super(...arguments);
this.params = params || [];
this.body = body || new Block();
this.bound = tag === "boundfunc";
this.isGenerator = !!this.body.contains(function(node) {
return (node instanceof Op && node.isYield()) || node instanceof YieldReturn;
});
}
isStatement() {
return !!this.ctor;
}
makeScope(parentScope) {
return new Scope(parentScope, this.body, this);
}
compileNode(o) {
var lit;
var val;
var splats;
var boundfunc;
var wrapper;
var ref;
if (this.bound && (((ref = o.scope.method) != null ? ref.bound : void 0))) {
this.context = o.scope.method.context;
}
if (this.bound && !this.context) {
this.context = "_this";
wrapper = new Code([new Param(new IdentifierLiteral(this.context))], new Block([this]));
boundfunc = new Call(wrapper, [new ThisLiteral()]);
boundfunc.updateLocationDataIfMissing(this.locationData);
return boundfunc.compileNode(o);
}
o.scope = del(o, "classScope") || this.makeScope(o.scope);
o.scope.shared = del(o, "sharedScope");
o.indent += TAB;
delete o.bare;
delete o.isExistentialEquals;
var params = [];
var exprs = [];
for (var param of this.params) {
if (!(param instanceof Expansion)) {
o.scope.parameter(param.asReference(o));
}
}
for (var param of this.params) {
if (param.splat || param instanceof Expansion) {
for (var p of this.params) {
if (!(p instanceof Expansion) && p.name.value) {
o.scope.add(p.name.value, "var", true);
}
}
splats = new Assign(new Value(new Arr(this.params.map(p => {
return p.asReference(o);
}))), new Value(new IdentifierLiteral("arguments")));
break;
}
}
for (var param of this.params) {
if (param.isComplex()) {
val = ref = param.asReference(o);
if (param.value) {
val = new Op("?", ref, param.value);
}
exprs.push(new Assign(new Value(param.name), val, "=", {
param: true
}));
} else {
ref = param;
if (param.value) {
lit = new Literal(ref.name.value + " == null");
val = new Assign(new Value(param.name), param.value, "=");
exprs.push(new If(lit, val));
}
}
if (!splats) {
params.push(ref);
}
}
var wasEmpty = this.body.isEmpty();
if (splats) {
exprs.unshift(splats);
}
if (exprs.length) {
this.body.expressions.unshift(...exprs);
}
for (var [i, p] of params.entries()) {
params[i] = p.compileToFragments(o);
o.scope.parameter(fragmentsToText(params[i]));
}
var uniqs = [];
this.eachParamName(function(name, node) {
if (uniqs.includes(name)) {
node.error(("multiple parameters named " + (name)));
}
return uniqs.push(name);
});
if (!(wasEmpty || this.noReturn)) {
this.body.makeReturn();
}
var code = "function";
if (this.isGenerator) {
code += "*";
}
if (this.ctor) {
code += " " + this.name;
}
code += "(";
var answer = [this.makeCode(code)];
for (var [i, p] of params.entries()) {
if (i) {
answer.push(this.makeCode(", "));
}
answer.push(...p);
}
answer.push(this.makeCode(") {"));
if (!this.body.isEmpty()) {
answer = answer.concat(
this.makeCode("\n"),
this.body.compileWithDeclarations(o),
this.makeCode(("\n" + (this.tab)))
);
}
answer.push(this.makeCode("}"));
if (this.ctor) {
return [this.makeCode(this.tab), ...answer];
}
if (this.front || (o.level >= LEVEL_ACCESS)) {
return this.wrapInBraces(answer);
} else {
return answer;
}
}
eachParamName(iterator) {
return (() => {
for (var param of this.params) {
param.eachName(iterator);
}
})();
}
traverseChildren(crossScope, func) {
if (crossScope) {
return super.traverseChildren(crossScope, func);
}
}
};
exports.Param = class Param extends Base {
constructor(name, value, splat) {
super(...arguments);
var token;
this.name = name;
this.value = value;
this.splat = splat;
var message = isUnassignable(this.name.unwrapAll().value);
if (message) {
this.name.error(message);
}
if (this.name instanceof Obj && this.name.generated) {
token = this.name.objects[0].operatorToken;
token.error(("unexpected " + (token.value)));
}
}
compileToFragments(o) {
return this.name.compileToFragments(o, LEVEL_LIST);
}
asReference(o) {
var name;
if (this.reference) {
return this.reference;
}
var node = this.name;
if (node.this) {
name = node.properties[0].name.value;
if (JS_FORBIDDEN.includes(name)) {
name = ("_" + (name));
}
node = new IdentifierLiteral(o.scope.freeVariable(name));
} else if (node.isComplex()) {
node = new IdentifierLiteral(o.scope.freeVariable("arg"));
}
node = new Value(node);
if (this.splat) {
node = new Splat(node);
}
node.updateLocationDataIfMissing(this.locationData);
return this.reference = node;
}
isComplex() {
return this.name.isComplex();
}
eachName(iterator, name = this.name) {
var node;
var ref;
var atParam = function(obj) {
return iterator(("@" + (obj.properties[0].name.value)), obj);
};
if (name instanceof Literal) {
return iterator(name.value, name);
}
if (name instanceof Value) {
return atParam(name);
}
for (var obj of (ref = name.objects) != null ? ref : []) {
if (obj instanceof Assign && !(obj.context != null)) {
obj = obj.variable;
}
if (obj instanceof Assign) {
if (obj.value instanceof Assign) {
obj = obj.value;
}
this.eachName(iterator, obj.value.unwrap());
} else if (obj instanceof Splat) {
node = obj.name.unwrap();
iterator(node.value, node);
} else if (obj instanceof Value) {
if (obj.isArray() || obj.isObject()) {
this.eachName(iterator, obj.base);
} else if (obj.this) {
atParam(obj);
} else {
iterator(obj.base.value, obj.base);
}
} else if (!(obj instanceof Expansion)) {
obj.error(("illegal parameter " + (obj.compile())));
}
}
return;
}
};
exports.Splat = class Splat extends Base {
constructor(name) {
super(...arguments);
this.name = (() => {
if (name.compile) {
return name;
} else {
return new Literal(name);
}
})();
}
assigns(name) {
return this.name.assigns(name);
}
compileToFragments(o) {
return this.name.compileToFragments(o);
}
unwrap() {
return this.name;
}
static compileSplattedArray(o, list, apply) {
var concatPart;
var fragments;
var index = -1;
while ((node = list[++index]) && !(node instanceof Splat)) {
continue;
}
if (index >= list.length) {
return [];
}
if (list.length === 1) {
node = list[0];
fragments = node.compileToFragments(o, LEVEL_LIST);
if (apply) {
return fragments;
}
return [].concat(
node.makeCode(((utility("slice", o)) + ".call(")),
fragments,
node.makeCode(")")
);
}
var args = list.slice(index);
for (var [i, node] of args.entries()) {
var compiledNode = node.compileToFragments(o, LEVEL_LIST);
args[i] = (() => {
if (node instanceof Splat) {
return [].concat(
node.makeCode(((utility("slice", o)) + ".call(")),
compiledNode,
node.makeCode(")")
);
} else {
return [].concat(node.makeCode("["), compiledNode, node.makeCode("]"));
}
})();
}
if (index === 0) {
node = list[0];
concatPart = (node.joinFragmentArrays(args.slice(1), ", "));
return args[0].concat(node.makeCode(".concat("), concatPart, node.makeCode(")"));
}
var base = (list.slice(0, index).map(node => {
return node.compileToFragments(o, LEVEL_LIST);
}));
base = list[0].joinFragmentArrays(base, ", ");
concatPart = list[index].joinFragmentArrays(args, ", ");
return [].concat(
list[0].makeCode("["),
base,
list[index].makeCode("].concat("),
concatPart,
last.makeCode(")")
);
}
};
exports.Expansion = class Expansion extends Base {
compileNode(o) {
return this.error(
"Expansion must be used inside a destructuring assignment or parameter list"
);
}
asReference(o) {
return this;
}
eachName(iterator) {}
};
exports.While = class While extends Base {
constructor(condition, options) {
super(...arguments);
this.condition = (() => {
if (typeof options !== "undefined" && options !== null ? options.invert : void 0) {
return condition.invert();
} else {
return condition;
}
})();
this.guard = typeof options !== "undefined" && options !== null ? options.guard : void 0;
}
makeReturn(res) {
if (res) {
return super.makeReturn(...arguments);
} else {
this.returns = !this.jumps({
loop: true
});
return this;
}
}
addBody(body) {
this.body = body;
return this;
}
jumps() {
var jumpNode;
var {
expressions
} = this.body;
if (!expressions.length) {
return false;
}
for (var node of expressions) {
if (jumpNode = node.jumps({
loop: true
})) {
return jumpNode;
}
}
return false;
}
compileNode(o) {
var rvar;
o.indent += TAB;
var set = "";
var {
body
} = this;
if (body.isEmpty()) {
body = this.makeCode("");
} else {
if (this.returns) {
body.makeReturn(rvar = o.scope.freeVariable("results"));
set = ("" + (this.tab) + (rvar) + " = [];\n");
}
if (this.guard) {
if (body.expressions.length > 1) {
body.expressions.unshift(
new If((new Parens(this.guard)).invert(), new StatementLiteral("continue"))
);
} else if (this.guard) {
body = Block.wrap([new If(this.guard, body)]);
}
}
body = [].concat(
this.makeCode("\n"),
(body.compileToFragments(o, LEVEL_TOP)),
this.makeCode(("\n" + (this.tab)))
);
}
var answer = [].concat(
this.makeCode(set + this.tab + "while ("),
this.condition.compileToFragments(o, LEVEL_PAREN),
this.makeCode(") {"),
body,
this.makeCode("}")
);
if (this.returns) {
answer.push(this.makeCode(("\n" + (this.tab) + "return " + (rvar) + ";")));
}
return answer;
}
};
exports.Op = class Op extends Base {
constructor(op, first, second, flip) {
super(...arguments);
if (op === "in") {
return new In(first, second);
}
if (op === "do") {
return this.generateDo(first);
}
if (op === "new") {
if (first instanceof Call && !first.do && !first.isNew) {
return first.newInstance();
}
if (first instanceof Code && first.bound || first.do) {
first = new Parens(first);
}
}
this.operator = CONVERSIONS[op] || op;
this.first = first;
this.second = second;
this.flip = !!flip;
this.CONVERSIONS = {
"==": "===",
"!=": "!==",
"of": "in",
"yieldfrom": "yield*"
};
this.INVERSIONS = {
"!==": "===",
"===": "!=="
};
return this;
}
isNumber() {
return this.isUnary() && ["+", "-"].includes(this.operator) && this.first instanceof Value && this.first.isNumber();
}
isYield() {
return ["yield", "yield*"].includes(this.operator);
}
isUnary() {
return !this.second;
}
isComplex() {
return !this.isNumber();
}
isChainable() {
return ["<", ">", ">=", "<=", "===", "!=="].includes(this.operator);
}
invert() {
var fst;
var op;
var curr;
var allInvertable;
if (this.isChainable() && this.first.isChainable()) {
allInvertable = true;
curr = this;
while (curr && curr.operator) {
allInvertable &&= (curr.operator in INVERSIONS);
curr = curr.first;
}
if (!allInvertable) {
return new Parens(this).invert();
}
curr = this;
while (curr && curr.operator) {
curr.invert = !curr.invert;
curr.operator = INVERSIONS[curr.operator];
curr = curr.first;
}
return this;
} else if (op = INVERSIONS[this.operator]) {
this.operator = op;
if (this.first.unwrap() instanceof Op) {
this.first.invert();
}
return this;
} else if (this.second) {
return new Parens(this).invert();
} else if (this.operator === "!" && (fst = this.first.unwrap()) instanceof Op && ["!", "in", "instanceof"].includes(fst.operator)) {
return fst;
} else {
return new Op("!", this);
}
}
unfoldSoak(o) {
return ["++", "--", "delete"].includes(this.operator) && unfoldSoak(o, this, "first");
}
generateDo(exp) {
var ref;
var passedParams = [];
var func = (exp instanceof Assign && (ref = exp.value.unwrap()) instanceof Code ? ref : exp);
for (var param of func.params || []) {
if (param.value) {
passedParams.push(param.value);
delete param.value;
} else {
passedParams.push(param);
}
}
var call = new Call(exp, passedParams);
call.do = true;
return call;
}
compileNode(o) {
var answer;
var rhs;
var lhs;
var message;
var isChain = this.isChainable() && this.first.isChainable();
if (!isChain) {
this.first.front = this.front;
}
if (this.operator === "delete" && o.scope.check(this.first.unwrapAll().value)) {
this.error("delete operand may not be argument or var");
}
if (["--", "++"].includes(this.operator)) {
message = isUnassignable(this.first.unwrapAll().value);
if (message) {
this.first.error(message);
}
}
if (this.isYield()) {
return this.compileYield(o);
}
if (this.isUnary()) {
return this.compileUnary(o);
}
if (isChain) {
return this.compileChain(o);
}
switch (this.operator) {
case "?":
return this.compileExistence(o);
case "**":
return this.compilePower(o);
case "//":
return this.compileFloorDivision(o);
case "%%":
return this.compileModulo(o);
default:
lhs = this.first.compileToFragments(o, LEVEL_OP);
rhs = this.second.compileToFragments(o, LEVEL_OP);
answer = [].concat(lhs, this.makeCode((" " + (this.operator) + " ")), rhs);
if (o.level <= LEVEL_OP) {
return answer;
} else {
return this.wrapInBraces(answer);
}
}
}
compileChain(o) {
var shared;
[this.first.second, shared] = this.first.second.cache(o);
var fst = this.first.compileToFragments(o, LEVEL_OP);
var fragments = fst.concat(
this.makeCode((" " + ((this.invert ? "&&" : "||")) + " ")),
(shared.compileToFragments(o)),
this.makeCode((" " + (this.operator) + " ")),
(this.second.compileToFragments(o, LEVEL_OP))
);
return this.wrapInBraces(fragments);
}
compileExistence(o) {
var fst;
var ref;
if (this.first.isComplex()) {
ref = new IdentifierLiteral(o.scope.freeVariable("ref"));
fst = new Parens(new Assign(ref, this.first));
} else {
fst = this.first;
ref = fst;
}
return new If(new Existence(fst), ref, {
type: "if"
}).addElse(this.second).compileToFragments(o);
}
compileUnary(o) {
var parts = [];
var op = this.operator;
parts.push([this.makeCode(op)]);
if (op === "!" && this.first instanceof Existence) {
this.first.negated = !this.first.negated;
return this.first.compileToFragments(o);
}
if (o.level >= LEVEL_ACCESS) {
return (new Parens(this)).compileToFragments(o);
}
var plusMinus = ["+", "-"].includes(op);
if ((["new", "typeof", "delete"].includes(op) || plusMinus) && this.first instanceof Op && this.first.operator === op) {
parts.push([this.makeCode(" ")]);
}
if ((plusMinus && this.first instanceof Op) || (op === "new" && this.first.isStatement(o))) {
this.first = new Parens(this.first);
}
parts.push(this.first.compileToFragments(o, LEVEL_OP));
if (this.flip) {
parts.reverse();
}
return this.joinFragmentArrays(parts, "");
}
compileYield(o) {
var ref;
var parts = [];
var op = this.operator;
if (o.scope.parent == null) {
this.error("yield can only occur inside functions");
}
if (Object.keys(this.first).includes("expression") && !(this.first instanceof Throw)) {
if (this.first.expression != null) {
parts.push(this.first.expression.compileToFragments(o, LEVEL_OP));
}
} else {
if (o.level >= LEVEL_PAREN) {
parts.push([this.makeCode("(")]);
}
parts.push([this.makeCode(op)]);
if ((((ref = this.first.base) != null ? ref.value : void 0)) !== "") {
parts.push([this.makeCode(" ")]);
}
parts.push(this.first.compileToFragments(o, LEVEL_OP));
if (o.level >= LEVEL_PAREN) {
parts.push([this.makeCode(")")]);
}
}
return this.joinFragmentArrays(parts, "");
}
compilePower(o) {
var pow = new Value(new IdentifierLiteral("Math"), [new Access(new PropertyName("pow"))]);
return new Call(pow, [this.first, this.second]).compileToFragments(o);
}
compileFloorDivision(o) {
var floor = new Value(new IdentifierLiteral("Math"), [new Access(new PropertyName("floor"))]);
var div = new Op("/", this.first, this.second);
return new Call(floor, [div]).compileToFragments(o);
}
compileModulo(o) {
var mod = new Value(new Literal(utility("modulo", o)));
return new Call(mod, [this.first, this.second]).compileToFragments(o);
}
toString(idt) {
return super.toString(idt, this.constructor.name + " " + this.operator);
}
};
exports.In = class In extends Base {
constructor(object, array) {
super(...arguments);
this.object = object;
this.array = array;
}
compileNode(o) {
var hasSplat;
if (this.array instanceof Value && this.array.isArray() && this.array.base.objects.length) {
for (var obj of this.array.base.objects) {
if (obj instanceof Splat) {
hasSplat = true;
break;
}
}
if (!hasSplat) {
return this.compileOrTest(o);
}
}
return this.compileLoopTest(o);
}
compileOrTest(o) {
var [sub, ref] = this.object.cache(o, LEVEL_OP);
var [cmp, cnj] = (() => {
if (this.negated) {
return [" !== ", " && "];
} else {
return [" === ", " || "];
}
})();
var tests = [];
for (var [i, item] of this.array.base.objects.entries()) {
if (i) {
tests.push(this.makeCode(cnj));
}
tests = tests.concat(
((i ? ref : sub)),
this.makeCode(cmp),
item.compileToFragments(o, LEVEL_ACCESS)
);
}
if (o.level < LEVEL_OP) {
return tests;
} else {
return this.wrapInBraces(tests);
}
}
compileLoopTest(o) {
var [sub, ref] = this.object.cache(o, LEVEL_LIST);
var fragments = [].concat(
this.makeCode(utility("indexOf", o) + ".call("),
this.array.compileToFragments(o, LEVEL_LIST),
this.makeCode(", "),
ref,
this.makeCode(") " + ((this.negated ? "< 0" : ">= 0")))
);
if (fragmentsToText(sub) === fragmentsToText(ref)) {
return fragments;
}
fragments = sub.concat(this.makeCode(", "), fragments);
if (o.level < LEVEL_LIST) {
return fragments;
} else {
return this.wrapInBraces(fragments);
}
}
toString(idt) {
return super.toString(idt, this.constructor.name + ((this.negated ? "!" : "")));
}
};
exports.Try = class Try extends Base {
constructor(attempt, errorVariable, recovery, ensure) {
super(...arguments);
this.attempt = attempt;
this.errorVariable = errorVariable;
this.recovery = recovery;
this.ensure = ensure;
}
jumps(o) {
var ref;
return this.attempt.jumps(o) || (((ref = this.recovery) != null ? ref.jumps(o) : void 0));
}
makeReturn(res) {
if (this.attempt) {
this.attempt = this.attempt.makeReturn(res);
}
if (this.recovery) {
this.recovery = this.recovery.makeReturn(res);
}
return this;
}
compileNode(o) {
o.indent += TAB;
var tryPart = this.attempt.compileToFragments(o, LEVEL_TOP);
var catchPart = (() => {
var message;
var placeholder;
var generatedErrorVariableName;
if (this.recovery) {
generatedErrorVariableName = o.scope.freeVariable("error", {
reserve: false
});
placeholder = new IdentifierLiteral(generatedErrorVariableName);
if (this.errorVariable) {
message = isUnassignable(this.errorVariable.unwrapAll().value);
if (message) {
this.errorVariable.error(message);
}
this.recovery.unshift(new Assign(this.errorVariable, placeholder));
}
return [].concat(
this.makeCode(" catch ("),
placeholder.compileToFragments(o),
this.makeCode(") {\n"),
this.recovery.compileToFragments(o, LEVEL_TOP),
this.makeCode(("\n" + (this.tab) + "}"))
);
} else if (!(this.ensure || this.recovery)) {
generatedErrorVariableName = o.scope.freeVariable("error", {
reserve: false
});
return [this.makeCode((" catch (" + (generatedErrorVariableName) + ") {}"))];
} else {
return [];
}
})();
var ensurePart = (() => {
if (this.ensure) {
return ([].concat(
this.makeCode(" finally {\n"),
this.ensure.compileToFragments(o, LEVEL_TOP),
this.makeCode(("\n" + (this.tab) + "}"))
));
} else {
return [];
}
})();
return [].concat(
this.makeCode(((this.tab) + "try {\n")),
tryPart,
this.makeCode(("\n" + (this.tab) + "}")),
catchPart,
ensurePart
);
}
};
exports.Throw = class Throw extends Base {
constructor(expression) {
super(...arguments);
this.expression = expression;
}
compileNode(o) {
return [].concat(
this.makeCode(this.tab + "throw "),
this.expression.compileToFragments(o),
this.makeCode(";")
);
}
};
exports.Existence = class Existence extends Base {
constructor(expression) {
super(...arguments);
this.expression = expression;
}
compileNode(o) {
this.expression.front = this.front;
var code = this.expression.compile(o, LEVEL_OP);
if (this.expression.unwrap() instanceof IdentifierLiteral && !o.scope.check(code)) {
var [cmp, cnj] = (() => {
if (this.negated) {
return ["===", "||"];
} else {
return ["!==", "&&"];
}
})();
code = ("typeof " + (code) + " " + (cmp) + " \"undefined\" " + (cnj) + " " + (code) + " " + (cmp) + " null");
} else {
code = ((code) + " " + ((this.negated ? "==" : "!=")) + " null");
}
return [this.makeCode((() => {
if (o.level <= LEVEL_COND) {
return code;
} else {
return ("(" + (code) + ")");
}
})())];
}
};
exports.Parens = class Parens extends Base {
constructor(body) {
super(...arguments);
this.body = body;
}
unwrap() {
return this.body;
}
isComplex() {
return this.body.isComplex();
}
compileNode(o) {
var expr = this.body.unwrap();
if (expr instanceof Value && expr.isAtomic()) {
expr.front = this.front;
return expr.compileToFragments(o);
}
var fragments = expr.compileToFragments(o, LEVEL_PAREN);
var bare = o.level < LEVEL_OP && (expr instanceof Op || expr instanceof Call || (expr instanceof For && expr.returns));
if (bare) {
return fragments;
} else {
return this.wrapInBraces(fragments);
}
}
};
exports.StringWithInterpolations = class StringWithInterpolations extends Parens {};
exports.For = class For extends While {
constructor(body, source) {
super(...arguments);
this.source = source.source, this.guard = source.guard, this.step = source.step, this.name = source.name, this.index = source.index, source;
this.body = Block.wrap([body]);
this.own = !!source.own;
this.object = !!source.object;
if (this.object) {
[this.name, this.index] = [this.index, this.name];
}
if (this.index instanceof Value) {
this.index.error("index cannot be a pattern matching expression");
}
this.range = this.source instanceof Value && this.source.base instanceof Range && !this.source.properties.length;
this.pattern = this.name instanceof Value;
if (this.range && this.index) {
this.index.error("indexes do not apply to range loops");
}
if (this.range && this.pattern) {
this.name.error("cannot pattern match over range loops");
}
if (this.own && !this.object) {
this.name.error("cannot use own with for-in");
}
this.returns = false;
}
compileNode(o) {
var returnResult;
var resultPart;
var increment;
var compareDown;
var compare;
var declareDown;
var declare;
var lvar;
var down;
var namePart;
var ref;
var svar;
var forPartFragments;
var stepNum;
var rvar;
var name;
var body = Block.wrap([this.body]);
if (((typeof last !== "undefined" && last !== null ? last.jumps() : void 0)) instanceof Return) {
this.returns = false;
}
var source = (this.range ? this.source.base : this.source);
var scope = o.scope;
if (!this.pattern) {
name = this.name && (this.name.compile(o, LEVEL_LIST));
}
var index = this.index && (this.index.compile(o, LEVEL_LIST));
if (name && !this.pattern) {
scope.find(name);
}
if (index) {
scope.find(index);
}
if (this.returns) {
rvar = scope.freeVariable("results");
}
var ivar = (this.object && index) || scope.freeVariable("i", {
single: true
});
var kvar = (this.range && name) || index || ivar;
var kvarAssign = (() => {
if (kvar !== ivar) {
return ((kvar) + " = ");
} else {
return "";
}
})();
if (this.step && !this.range) {
var [step, stepVar] = this.cacheToCodeFragments(this.step.cache(o, LEVEL_LIST, isComplexOrAssignable));
if (this.step.isNumber()) {
stepNum = Number(stepVar);
}
}
if (this.pattern) {
name = ivar;
}
var varPart = "";
var guardPart = "";
var defPart = "";
var idt1 = this.tab + TAB;
if (this.range) {
forPartFragments = source.compileToFragments(merge(o, {
index: ivar,
name: name,
this: this,
isComplex: isComplexOrAssignable
}));
} else {
svar = this.source.compile(o, LEVEL_LIST);
if ((name || this.own) && !(this.source.unwrap() instanceof IdentifierLiteral)) {
defPart += ("" + (this.tab) + (ref = scope.freeVariable("ref")) + " = " + (svar) + ";\n");
svar = ref;
}
if (name && !this.pattern) {
namePart = ((name) + " = " + (svar) + "[" + (kvar) + "]");
}
if (!this.object) {
if (step !== stepVar) {
defPart += ("" + (this.tab) + (step) + ";\n");
}
down = stepNum < 0;
if (!(this.step && stepNum != null && down)) {
lvar = scope.freeVariable("len");
}
declare = ("" + (kvarAssign) + (ivar) + " = 0, " + (lvar) + " = " + (svar) + ".length");
declareDown = ("" + (kvarAssign) + (ivar) + " = " + (svar) + ".length - 1");
compare = ((ivar) + " < " + (lvar));
compareDown = ((ivar) + " >= 0");
if (this.step) {
if (stepNum != null) {
if (down) {
compare = compareDown;
declare = declareDown;
}
} else {
compare = ((stepVar) + " > 0 ? " + (compare) + " : " + (compareDown));
declare = ("(" + (stepVar) + " > 0 ? (" + (declare) + ") : " + (declareDown) + ")");
}
increment = ((ivar) + " += " + (stepVar));
} else {
increment = ("" + ((() => {
if (kvar !== ivar) {
return ("++" + (ivar));
} else {
return ((ivar) + "++");
}
})()));
}
forPartFragments = [
this.makeCode(((declare) + "; " + (compare) + "; " + (kvarAssign) + (increment)))
];
}
}
if (this.returns) {
resultPart = ("" + (this.tab) + (rvar) + " = [];\n");
returnResult = ("\n" + (this.tab) + "return " + (rvar) + ";");
body.makeReturn(rvar);
}
if (this.guard) {
if (body.expressions.length > 1) {
body.expressions.unshift(
new If((new Parens(this.guard)).invert(), new StatementLiteral("continue"))
);
} else if (this.guard) {
body = Block.wrap([new If(this.guard, body)]);
}
}
if (this.pattern) {
body.expressions.unshift(new Assign(this.name, new Literal(((svar) + "[" + (kvar) + "]"))));
}
var defPartFragments = [].concat(this.makeCode(defPart), this.pluckDirectCall(o, body));
if (namePart) {
varPart = ("\n" + (idt1) + (namePart) + ";");
}
if (this.object) {
forPartFragments = [this.makeCode(((kvar) + " in " + (svar)))];
if (this.own) {
guardPart = ("\n" + (idt1) + "if (!" + (utility("hasProp", o)) + ".call(" + (svar) + ", " + (kvar) + ")) continue;");
}
}
var bodyFragments = body.compileToFragments(merge(o, {
indent: idt1
}), LEVEL_TOP);
if (bodyFragments && (bodyFragments.length > 0)) {
bodyFragments = [].concat(this.makeCode("\n"), bodyFragments, this.makeCode("\n"));
}
return [].concat(
defPartFragments,
this.makeCode(("" + (resultPart || "") + (this.tab) + "for (")),
forPartFragments,
this.makeCode((") {" + (guardPart) + (varPart))),
bodyFragments,
this.makeCode(((this.tab) + "}" + (returnResult || "")))
);
}
pluckDirectCall(o, body) {
var ref4;
var ref3;
var ref2;
var ref1;
var defs = [];
for (var [idx, expr] of body.expressions.entries()) {
expr = expr.unwrapAll();
if (!(expr instanceof Call)) {
continue;
}
var val = (ref1 = expr.variable) != null ? ref1.unwrapAll() : void 0;
if (!((val instanceof Code) || (val instanceof Value && (((ref2 = val.base) != null ? ref2.unwrapAll() : void 0)) instanceof Code && val.properties.length === 1 && ["call", "apply"].includes((ref3 = val.properties[0].name) != null ? ref3.value : void 0)))) {
continue;
}
var fn = (((ref4 = val.base) != null ? ref4.unwrapAll() : void 0)) || val;
var ref = new IdentifierLiteral(o.scope.freeVariable("fn"));
var base = new Value(ref);
if (val.base) {
[val.base, base] = [base, val];
}
body.expressions[idx] = new Call(base, expr.args);
defs = defs.concat(
this.makeCode(this.tab),
(new Assign(ref, fn).compileToFragments(o, LEVEL_TOP)),
this.makeCode(";\n")
);
}
return defs;
}
};
exports.Switch = class Switch extends Base {
constructor(subject, cases, otherwise) {
super(...arguments);
this.subject = subject;
this.cases = cases;
this.otherwise = otherwise;
}
jumps(
o = {
block: true
}) {
var ref;
var jumpNode;
for (var [conds, block] of this.cases) {
if (jumpNode = block.jumps(o)) {
return jumpNode;
}
}
return (ref = this.otherwise) != null ? ref.jumps(o) : void 0;
}
makeReturn(res) {
var ref;
for (var pair of this.cases) {
pair[1].makeReturn(res);
}
if (res) {
this.otherwise || (this.otherwise = new Block([new Literal("void 0")]));
}
(ref = this.otherwise) != null ? ref.makeReturn(res) : void 0;
return this;
}
compileNode(o) {
var body;
var idt1 = o.indent + TAB;
var idt2 = o.indent = idt1 + TAB;
var fragments = [].concat(this.makeCode(this.tab + "switch ("), ((() => {
if (this.subject) {
return this.subject.compileToFragments(o, LEVEL_PAREN);
} else {
return this.makeCode("false");
}
})()), this.makeCode(") {\n"));
for (var [i, [conditions, block]] of this.cases.entries()) {
for (var cond of flatten([conditions])) {
if (!this.subject) {
cond = cond.invert();
}
fragments = fragments.concat(
this.makeCode(idt1 + "case "),
cond.compileToFragments(o, LEVEL_PAREN),
this.makeCode(":\n")
);
}
if ((body = block.compileToFragments(o, LEVEL_TOP)).length > 0) {
fragments = fragments.concat(body, this.makeCode("\n"));
}
if (i === this.cases.length - 1 && !this.otherwise) {
break;
}
var expr = this.lastNonComment(block.expressions);
if (expr instanceof Return || (expr instanceof Literal && expr.jumps() && expr.value !== "debugger")) {
continue;
}
fragments.push(cond.makeCode(idt2 + "break;\n"));
}
if (this.otherwise && this.otherwise.expressions.length) {
fragments.push(
this.makeCode(idt1 + "default:\n"),
...(this.otherwise.compileToFragments(o, LEVEL_TOP)),
this.makeCode("\n")
);
}
fragments.push(this.makeCode(this.tab + "}"));
return fragments;
}
};
exports.If = class If extends Base {
constructor(condition, body, options = {}) {
super(...arguments);
this.body = body;
this.condition = (() => {
if (options.type === "unless") {
return condition.invert();
} else {
return condition;
}
})();
this.elseBody = null;
this.isChain = false;
this.soak = options.soak, options;
}
bodyNode() {
var ref;
return (ref = this.body) != null ? ref.unwrap() : void 0;
}
elseBodyNode() {
var ref;
return (ref = this.elseBody) != null ? ref.unwrap() : void 0;
}
addElse(elseBody) {
if (this.isChain) {
this.elseBodyNode().addElse(elseBody);
} else {
this.isChain = elseBody instanceof If;
this.elseBody = this.ensureBlock(elseBody);
this.elseBody.updateLocationDataIfMissing(elseBody.locationData);
}
return this;
}
isStatement(o) {
var ref;
return ((typeof o !== "undefined" && o !== null ? o.level : void 0)) === LEVEL_TOP || this.bodyNode().isStatement(o) || (((ref = this.elseBodyNode()) != null ? ref.isStatement(o) : void 0));
}
jumps(o) {
var ref;
return this.body.jumps(o) || (((ref = this.elseBody) != null ? ref.jumps(o) : void 0));
}
compileNode(o) {
if (this.isStatement(o)) {
return this.compileStatement(o);
} else {
return this.compileExpression(o);
}
}
makeReturn(res) {
if (res) {
this.elseBody || (this.elseBody = new Block([new Literal("void 0")]));
}
this.body &&= new Block([this.body.makeReturn(res)]);
this.elseBody &&= new Block([this.elseBody.makeReturn(res)]);
return this;
}
ensureBlock(node) {
if (node instanceof Block) {
return node;
} else {
return new Block([node]);
}
}
compileStatement(o) {
var child = del(o, "chainChild");
var exeq = del(o, "isExistentialEquals");
if (exeq) {
return new If(this.condition.invert(), this.elseBodyNode(), {
type: "if"
}).compileToFragments(o);
}
var indent = o.indent + TAB;
var cond = this.condition.compileToFragments(o, LEVEL_PAREN);
var body = this.ensureBlock(this.body).compileToFragments(merge(o, {
indent: indent
}));
var ifPart = [].concat(
this.makeCode("if ("),
cond,
this.makeCode(") {\n"),
body,
this.makeCode(("\n" + (this.tab) + "}"))
);
if (!child) {
ifPart.unshift(this.makeCode(this.tab));
}
if (!this.elseBody) {
return ifPart;
}
var answer = ifPart.concat(this.makeCode(" else "));
if (this.isChain) {
o.chainChild = true;
answer = answer.concat(this.elseBody.unwrap().compileToFragments(o, LEVEL_TOP));
} else {
answer = answer.concat(this.makeCode("{\n"), this.elseBody.compileToFragments(merge(o, {
indent: indent
}), LEVEL_TOP), this.makeCode(("\n" + (this.tab) + "}")));
}
return answer;
}
compileExpression(o) {
var cond = this.condition.compileToFragments(o, LEVEL_COND);
var body = this.bodyNode().compileToFragments(o, LEVEL_LIST);
var alt = (() => {
if (this.elseBodyNode()) {
return this.elseBodyNode().compileToFragments(o, LEVEL_LIST);
} else {
return [this.makeCode("void 0")];
}
})();
var fragments = cond.concat(this.makeCode(" ? "), body, this.makeCode(" : "), alt);
if (o.level >= LEVEL_COND) {
return this.wrapInBraces(fragments);
} else {
return fragments;
}
}
unfoldSoak() {
return this.soak && this;
}
};
var UTILITIES = {
extend: function(o) {
return ("function(child, parent) { for (var key in parent) { if (" + (utility("hasProp", o)) + ".call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }");
},
bind: function() {
return "function(fn, me){ return function(){ return fn.apply(me, arguments); }; }";
},
indexOf: function() {
return "[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }";
},
modulo: function() {
return "function(a, b) { return (+a % (b = +b) + b) % b; }";
},
hasProp: function() {
return "{}.hasOwnProperty";
},
slice: function() {
return "[].slice";
}
};
var LEVEL_TOP = 1;
var LEVEL_PAREN = 2;
var LEVEL_LIST = 3;
var LEVEL_COND = 4;
var LEVEL_OP = 5;
var LEVEL_ACCESS = 6;
var TAB = " ";
var SIMPLENUM = /^[+-]?\d+$/;
var utility = function(name, o) {
var ref;
var {
root
} = o.scope;
if (name in root.utilities) {
return root.utilities[name];
} else {
ref = root.freeVariable(name);
root.assign(ref, UTILITIES[name](o));
return root.utilities[name] = ref;
}
};
var multident = function(code, tab) {
code = code.replace(/\n/g, "$&" + tab);
return code.replace(/\s+$/, "");
};
var isLiteralArguments = function(node) {
return node instanceof Literal && node.value === "arguments" && !node.asKey;
};
var isLiteralThis = function(node) {
return (node instanceof ThisLiteral && !node.asKey) || (node instanceof Code && node.bound) || node instanceof SuperCall;
};
var isComplexOrAssignable = function(node) {
return node.isComplex() || ((typeof node.isAssignable === "function" ? node.isAssignable() : void 0));
};
var unfoldSoak = function(o, parent, name) {
var ifn;
if (!(ifn = parent[name].unfoldSoak(o))) {
return;
}
parent[name] = ifn.body;
ifn.body = new Value(parent);
return ifn;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment