Created
September 14, 2016 03:13
-
-
Save GeoffreyBooth/59494576acd3b06fa6c22eb2486ddfa5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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