1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

#733: streamlined soak compilations and improved reference cachings

This commit is contained in:
satyr 2010-10-02 07:17:35 +09:00
parent 54f162e523
commit 341f511bbd
9 changed files with 459 additions and 409 deletions

View file

@ -206,7 +206,7 @@
var o;
optionParser = new optparse.OptionParser(SWITCHES, BANNER);
o = (opts = optionParser.parse(process.argv.slice(2, process.argv.length)));
o.compile || (o.compile = (!!o.output));
o.compile || (o.compile = !!o.output);
o.run = !(o.compile || o.print || o.lint);
o.print = !!(o.print || (o.eval || o.stdio && o.compile));
return (sources = o.arguments);

View file

@ -294,7 +294,7 @@
}
this.i += value.length;
prev = last(this.tokens);
spaced = prev == null ? undefined : prev.spaced;
spaced = ((prev != null) ? prev.spaced : null);
tag = value;
if (value === '=') {
if (include(JS_FORBIDDEN, val = this.value())) {

View file

@ -26,7 +26,7 @@
};
BaseNode.prototype.compile = function(o) {
var closure, code, top;
this.options = merge(o || {});
this.options = o ? merge(o) : {};
this.tab = o.indent;
if (!(this instanceof AccessorNode || this instanceof IndexNode)) {
del(this.options, 'chainRoot');
@ -48,21 +48,22 @@
return ClosureNode.wrap(this).compile(o);
};
BaseNode.prototype.compileReference = function(o, options) {
var compiled, pair, reference;
options || (options = {});
var _len, _ref2, compiled, i, node, pair, reference;
pair = (function() {
if (!this.isComplex()) {
if (!(this.isComplex())) {
return [this, this];
} else if (this instanceof ValueNode && options.assignment) {
return this.cacheIndexes(o);
} else {
reference = literal(o.scope.freeVariable('ref'));
compiled = new AssignNode(reference, this);
return [compiled, reference];
}
}).call(this);
if (options.precompile) {
return [pair[0].compile(o), pair[1].compile(o)];
if (((options != null) ? options.precompile : null)) {
_ref2 = pair;
for (i = 0, _len = _ref2.length; i < _len; i++) {
node = _ref2[i];
(pair[i] = node.compile(o));
}
}
return pair;
};
@ -96,7 +97,7 @@
};
BaseNode.prototype.containsPureStatement = function() {
return this.isPureStatement() || this.contains(function(n) {
return n.isPureStatement && n.isPureStatement();
return (typeof n.isPureStatement !== "function" ? undefined : n.isPureStatement());
});
};
BaseNode.prototype.traverse = function(block) {
@ -146,8 +147,10 @@
};
BaseNode.prototype.traverseChildren = function(crossScope, func) {
return this.eachChild(function(child) {
func.apply(this, arguments);
return child instanceof BaseNode ? child.traverseChildren(crossScope, func) : null;
if (func(child) === false) {
return false;
}
return child instanceof BaseNode && (crossScope || !(child instanceof CodeNode)) ? child.traverseChildren(crossScope, func) : null;
});
};
BaseNode.prototype["class"] = 'BaseNode';
@ -319,10 +322,10 @@
return !!this.properties.length;
};
ValueNode.prototype.isArray = function() {
return this.base instanceof ArrayNode && !this.hasProperties();
return this.base instanceof ArrayNode && !this.properties.length;
};
ValueNode.prototype.isObject = function() {
return this.base instanceof ObjectNode && !this.hasProperties();
return this.base instanceof ObjectNode && !this.properties.length;
};
ValueNode.prototype.isSplice = function() {
return last(this.properties) instanceof SliceNode;
@ -331,7 +334,7 @@
return this.base.isComplex() || this.hasProperties();
};
ValueNode.prototype.makeReturn = function() {
return this.hasProperties() ? ValueNode.__super__.makeReturn.call(this) : this.base.makeReturn();
return this.properties.length ? ValueNode.__super__.makeReturn.call(this) : this.base.makeReturn();
};
ValueNode.prototype.unwrap = function() {
return this.properties.length ? this : this.base;
@ -342,76 +345,95 @@
ValueNode.prototype.isNumber = function() {
return this.base instanceof LiteralNode && NUMBER.test(this.base.value);
};
ValueNode.prototype.cacheIndexes = function(o) {
var _len, _ref2, _ref3, copy, first, i, index, indexVar, prop;
copy = new ValueNode(this.base, this.properties.slice(0));
if (this.base.isComplex()) {
_ref2 = this.base.compileReference(o), this.base = _ref2[0], copy.base = _ref2[1];
ValueNode.prototype.cacheReference = function(o) {
var base, bref, name, nref;
name = last(this.properties);
if (!this.base.isComplex() && this.properties.length < 2 && !((name != null) ? name.isComplex() : null)) {
return [this, this];
}
_ref2 = copy.properties;
for (i = 0, _len = _ref2.length; i < _len; i++) {
prop = _ref2[i];
if (prop instanceof IndexNode && prop.index.isComplex()) {
_ref3 = prop.index.compileReference(o), index = _ref3[0], indexVar = _ref3[1];
this.properties[i] = (first = new IndexNode(index));
copy.properties[i] = new IndexNode(indexVar);
if (prop.soakNode) {
first.soakNode = true;
}
}
base = new ValueNode(this.base, this.properties.slice(0, -1));
if (base.isComplex()) {
bref = literal(o.scope.freeVariable('base'));
base = new ValueNode(new ParentheticalNode(new AssignNode(bref, base)));
}
return [this, copy];
if (!(name)) {
return [base, bref];
}
if (name.isComplex()) {
nref = literal(o.scope.freeVariable('name'));
name = new IndexNode(new AssignNode(nref, name.index));
nref = new IndexNode(nref);
}
return [base.push(name), new ValueNode(bref || base.base, [nref || name])];
};
ValueNode.prototype.compile = function(o) {
return !o.top || this.properties.length ? ValueNode.__super__.compile.call(this, o) : this.base.compile(o);
};
ValueNode.prototype.compileNode = function(o) {
var _i, _len, _ref2, baseline, complete, copy, hasSoak, i, me, only, op, part, prop, props, temp;
only = del(o, 'onlyFirst');
op = this.tags.operation;
props = only ? this.properties.slice(0, -1) : this.properties;
var _i, _len, _ref2, code, ex, prop, props;
if (ex = this.unfoldSoak(o)) {
return ex.compile(o);
}
props = this.properties;
o.chainRoot || (o.chainRoot = this);
_ref2 = props;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
prop = _ref2[_i];
if (prop.soakNode) {
hasSoak = true;
break;
}
}
if (hasSoak && this.isComplex()) {
_ref2 = this.cacheIndexes(o), me = _ref2[0], copy = _ref2[1];
}
if (this.parenthetical && !props.length) {
this.base.parenthetical = true;
}
baseline = this.base.compile(o);
if (this.hasProperties() && (this.base instanceof ObjectNode || this.isNumber())) {
baseline = ("(" + (baseline) + ")");
code = this.base.compile(o);
if (props[0] instanceof AccessorNode && this.isNumber() || o.top && this.base instanceof ObjectNode) {
code = ("(" + (code) + ")");
}
complete = (this.last = baseline);
_ref2 = props;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
prop = _ref2[_i];
(code += prop.compile(o));
}
return code;
};
ValueNode.prototype.unfoldSoak = function(o) {
var _len, _ref2, fst, i, ifn, prop, ref, snd;
if (this.base.soakNode) {
Array.prototype.push.apply(this.base.body.properties, this.properties);
return this.base;
}
_ref2 = this.properties;
for (i = 0, _len = _ref2.length; i < _len; i++) {
prop = _ref2[i];
this.source = baseline;
if (prop.soakNode) {
if (i === 0 && this.base.isComplex()) {
temp = o.scope.freeVariable('ref');
complete = ("(" + (baseline = temp) + " = (" + (complete) + "))");
prop.soakNode = false;
fst = new ValueNode(this.base, this.properties.slice(0, i));
snd = new ValueNode(this.base, this.properties.slice(i));
if (fst.isComplex()) {
ref = literal(o.scope.freeVariable('ref'));
fst = new ParentheticalNode(new AssignNode(ref, fst));
snd.base = ref;
}
complete = i === 0 && !o.scope.check(complete) ? ("(typeof " + (complete) + " === \"undefined\" || " + (baseline) + " === null)") : ("" + (complete) + " == null");
complete += ' ? undefined : ' + (baseline += prop.compile(o));
} else {
part = prop.compile(o);
baseline += (hasSoak && prop.isComplex() ? copy.properties[i].compile(o) : part);
complete += part;
this.last = part;
ifn = new IfNode(new ExistenceNode(fst), snd, {
operation: true
});
ifn.soakNode = true;
return ifn;
}
}
return op && this.wrapped ? ("(" + (complete) + ")") : complete;
return null;
};
ValueNode.unfoldSoak = function(o, parent, name) {
var ifnode, node;
node = parent[name];
if (node instanceof IfNode && node.soakNode) {
ifnode = node;
} else if (node instanceof ValueNode) {
ifnode = node.unfoldSoak(o);
}
if (!(ifnode)) {
return null;
}
parent[name] = ifnode.body;
ifnode.body = new ValueNode(parent);
return ifnode;
};
return ValueNode;
})();
}).call(this);
exports.CommentNode = (function() {
CommentNode = function(_arg) {
this.comment = _arg;
@ -436,15 +458,14 @@
this.isSuper = variable === 'super';
this.variable = this.isSuper ? null : variable;
this.args || (this.args = []);
this.first = (this.last = '');
this.compileSplatArguments = function(o) {
return SplatNode.compileSplattedArray.call(this, this.args, o);
};
return this;
};
__extends(CallNode, BaseNode);
CallNode.prototype["class"] = 'CallNode';
CallNode.prototype.children = ['variable', 'args'];
CallNode.prototype.compileSplatArguments = function(o) {
return SplatNode.compileSplattedArray(this.args, o);
};
CallNode.prototype.newInstance = function() {
this.isNew = true;
return this;
@ -453,97 +474,128 @@
return this.isNew ? 'new ' : '';
};
CallNode.prototype.superReference = function(o) {
var meth, methname;
if (!(o.scope.method)) {
throw new Error("cannot call super outside of a function");
var method, name;
method = o.scope.method;
if (!(method)) {
throw Error("cannot call super outside of a function");
}
methname = o.scope.method.name;
return (meth = (function() {
if (o.scope.method.proto) {
return "" + (o.scope.method.proto) + ".__super__." + (methname);
} else if (methname) {
return "" + (methname) + ".__super__.constructor";
} else {
throw new Error("cannot call super on an anonymous function.");
name = method.name;
if (!(name)) {
throw Error("cannot call super on an anonymous function.");
}
return method.klass ? ("" + (method.klass) + ".__super__." + (name)) : ("" + (name) + ".__super__.constructor");
};
CallNode.prototype.unfoldSoak = function(o) {
var _i, _len, _ref2, call, list, node;
call = this;
list = [];
while (true) {
if (call.variable instanceof CallNode) {
list.push(call);
call = call.variable;
continue;
}
})());
if (!(call.variable instanceof ValueNode)) {
break;
}
list.push(call);
if (!((call = call.variable.base) instanceof CallNode)) {
break;
}
}
_ref2 = list.reverse();
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
call = _ref2[_i];
if (node) {
if (call.variable instanceof CallNode) {
call.variable = node;
} else {
call.variable.base = node;
}
}
node = ValueNode.unfoldSoak(o, call, 'variable');
}
return node;
};
CallNode.prototype.compileNode = function(o) {
var _i, _len, _ref2, _result, arg, args, code, first, meth, methodAccessor, op;
if (!(o.chainRoot)) {
o.chainRoot = this;
var _i, _len, _ref2, _result, arg, args, left, node, rite, val;
if (node = this.unfoldSoak(o)) {
return node.compile(o);
}
op = this.tags.operation;
o.chainRoot || (o.chainRoot = this);
if (this.exist) {
if (this.variable instanceof ValueNode && last(this.variable.properties) instanceof AccessorNode) {
methodAccessor = this.variable.properties.pop();
_ref2 = this.variable.compileReference(o), first = _ref2[0], meth = _ref2[1];
this.first = new ValueNode(first, [methodAccessor]).compile(o);
this.meth = new ValueNode(meth, [methodAccessor]).compile(o);
if (val = this.variable) {
if (!(val instanceof ValueNode)) {
val = new ValueNode(val);
}
_ref2 = val.cacheReference(o), left = _ref2[0], rite = _ref2[1];
rite = new CallNode(rite, this.args);
} else {
_ref2 = this.variable.compileReference(o, {
precompile: true
}), this.first = _ref2[0], this.meth = _ref2[1];
left = literal(this.superReference(o));
rite = new CallNode(new ValueNode(left), this.args);
rite.isNew = this.isNew;
}
this.first = ("(typeof " + (this.first) + " === \"function\" ? ");
this.last = " : undefined)";
} else if (this.variable) {
this.meth = this.variable.compile(o);
left = ("typeof " + (left.compile(o)) + " !== \"function\"");
rite = rite.compile(o);
return ("(" + (left) + " ? undefined : " + (rite) + ")");
}
_ref2 = this.args;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
arg = _ref2[_i];
if (arg instanceof SplatNode) {
code = this.compileSplat(o);
return this.compileSplat(o);
}
}
if (!code) {
args = (function() {
_result = []; _ref2 = this.args;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
arg = _ref2[_i];
_result.push((function() {
arg.parenthetical = true;
return arg.compile(o);
})());
}
return _result;
}).call(this);
code = this.isSuper ? this.compileSuper(args.join(', '), o) : ("" + (this.first) + (this.prefix()) + (this.meth) + "(" + (args.join(', ')) + ")" + (this.last));
}
return op && this.variable && this.variable.wrapped ? ("(" + (code) + ")") : code;
args = (function() {
_result = []; _ref2 = this.args;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
arg = _ref2[_i];
_result.push((arg.parenthetical = true) && arg.compile(o));
}
return _result;
}).call(this).join(', ');
return this.isSuper ? this.compileSuper(args, o) : ("" + (this.prefix()) + (this.variable.compile(o)) + "(" + (args) + ")");
};
CallNode.prototype.compileSuper = function(args, o) {
return "" + (this.superReference(o)) + ".call(this" + (args.length ? ', ' : '') + (args) + ")";
};
CallNode.prototype.compileSplat = function(o) {
var _i, _len, _ref2, a, b, c, mentionsArgs, meth, obj, temp;
meth = this.meth || this.superReference(o);
obj = this.variable && this.variable.source || 'this';
if (!(IDENTIFIER.test(obj) || NUMBER.test(obj))) {
temp = o.scope.freeVariable('ref');
obj = temp;
meth = ("(" + (temp) + " = " + (this.variable.source) + ")" + (this.variable.last));
var _i, _len, _ref2, a, arg, argvar, b, base, c, call, fun, idt, name, ref, splatargs;
splatargs = this.compileSplatArguments(o);
if (this.isSuper) {
return ("" + (this.superReference(o)) + ".apply(this, " + (splatargs) + ")");
}
if (this.isNew) {
mentionsArgs = false;
_ref2 = this.args;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
(function() {
var arg = _ref2[_i];
return arg.contains(function(n) {
return mentionsArgs || (mentionsArgs = (n instanceof LiteralNode && (n.value === 'arguments')));
});
})();
if (!(this.isNew)) {
if (!((base = this.variable) instanceof ValueNode)) {
base = new ValueNode(base);
}
utility('extends');
a = o.scope.freeVariable('ctor');
b = o.scope.freeVariable('ref');
c = o.scope.freeVariable('result');
return "" + (this.first) + "(function() {\n" + (this.idt(1)) + "var ctor = function(){};\n" + (this.idt(1)) + "__extends(ctor, " + (a) + " = " + (meth) + ");\n" + (this.idt(1)) + "return typeof (" + (c) + " = " + (a) + ".apply(" + (b) + " = new ctor, " + (this.compileSplatArguments(o)) + ")) === \"object\" ? " + (c) + " : " + (b) + ";\n" + (this.tab) + "})." + (mentionsArgs ? 'apply(this, arguments)' : 'call(this)') + (this.last);
} else {
return "" + (this.first) + (meth) + ".apply(" + (obj) + ", " + (this.compileSplatArguments(o)) + ")" + (this.last);
if ((name = base.properties.pop()) && base.isComplex()) {
ref = o.scope.freeVariable('this');
fun = ("(" + (ref) + " = " + (base.compile(o)) + ")" + (name.compile(o)));
} else {
fun = (ref = base.compile(o));
if (name) {
fun += name.compile(o);
}
}
return ("" + (fun) + ".apply(" + (ref) + ", " + (splatargs) + ")");
}
call = 'call(this)';
argvar = function(n) {
return n instanceof LiteralNode && n.value === 'arguments';
};
_ref2 = this.args;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
arg = _ref2[_i];
if (arg.contains(argvar)) {
call = 'apply(this, arguments)';
break;
}
}
a = o.scope.freeVariable('ctor');
b = o.scope.freeVariable('ref');
c = o.scope.freeVariable('result');
return "(function() {\n" + (idt = this.idt(1)) + "var ctor = function() {};\n" + (idt) + (utility('extends')) + "(ctor, " + (a) + " = " + (this.variable.compile(o)) + ");\n" + (idt) + "return typeof (" + (c) + " = " + (a) + ".apply(" + (b) + " = new ctor, " + (splatargs) + ")) === \"object\" ? " + (c) + " : " + (b) + ";\n" + (this.tab) + "})." + (call);
};
return CallNode;
})();
@ -576,9 +628,9 @@
AccessorNode.prototype["class"] = 'AccessorNode';
AccessorNode.prototype.children = ['name'];
AccessorNode.prototype.compileNode = function(o) {
var name, namePart;
var _base, name, namePart;
name = this.name.compile(o);
o.chainRoot.wrapped || (o.chainRoot.wrapped = this.soakNode);
(_base = o.chainRoot).wrapped || (_base.wrapped = this.soakNode);
namePart = name.match(IS_STRING) ? ("[" + (name) + "]") : ("." + (name));
return this.prototype + namePart;
};
@ -595,8 +647,8 @@
IndexNode.prototype["class"] = 'IndexNode';
IndexNode.prototype.children = ['index'];
IndexNode.prototype.compileNode = function(o) {
var idx, prefix;
o.chainRoot.wrapped || (o.chainRoot.wrapped = this.soakNode);
var _base, idx, prefix;
(_base = o.chainRoot).wrapped || (_base.wrapped = this.soakNode);
idx = this.index.compile(o);
prefix = this.proto ? '.prototype' : '';
return "" + (prefix) + "[" + (idx) + "]";
@ -774,14 +826,14 @@
this.objects = _arg;
ArrayNode.__super__.constructor.call(this);
this.objects || (this.objects = []);
this.compileSplatLiteral = function(o) {
return SplatNode.compileSplattedArray.call(this, this.objects, o);
};
return this;
};
__extends(ArrayNode, BaseNode);
ArrayNode.prototype["class"] = 'ArrayNode';
ArrayNode.prototype.children = ['objects'];
ArrayNode.prototype.compileSplatLiteral = function(o) {
return SplatNode.compileSplattedArray(this.objects, o);
};
ArrayNode.prototype.compileNode = function(o) {
var _len, _ref2, code, i, obj, objects;
o.indent = this.idt(1);
@ -898,8 +950,7 @@
return this;
};
__extends(AssignNode, BaseNode);
AssignNode.prototype.PROTO_ASSIGN = /^(\S+)\.prototype/;
AssignNode.prototype.LEADING_DOT = /^\.(?:prototype\.)?/;
AssignNode.prototype.METHOD_DEF = /^(?:(\S+)\.prototype\.)?([$A-Za-z_][$\w]*)$/;
AssignNode.prototype["class"] = 'AssignNode';
AssignNode.prototype.children = ['variable', 'value'];
AssignNode.prototype.topSensitive = YES;
@ -907,7 +958,7 @@
return this.variable instanceof ValueNode;
};
AssignNode.prototype.compileNode = function(o) {
var end, isValue, match, name, proto, stmt, top, val;
var isValue, match, name, node, stmt, top, val;
if (isValue = this.isValue()) {
if (this.variable.isArray() || this.variable.isObject()) {
return this.compilePatternMatch(o);
@ -915,20 +966,16 @@
if (this.variable.isSplice()) {
return this.compileSplice(o);
}
if (node = ValueNode.unfoldSoak(o, this, 'variable')) {
return node.compile(o);
}
}
top = del(o, 'top');
stmt = del(o, 'asStatement');
name = this.variable.compile(o);
end = isValue ? this.variable.last.replace(this.LEADING_DOT, '') : name;
match = name.match(this.PROTO_ASSIGN);
proto = match && match[1];
if (this.value instanceof CodeNode) {
if (IDENTIFIER.test(end)) {
this.value.name = end;
}
if (proto) {
this.value.proto = proto;
}
if (this.value instanceof CodeNode && (match = this.METHOD_DEF.exec(name))) {
this.value.name = match[2];
this.value.klass = match[1];
}
val = this.value.compile(o);
if (this.context === 'object') {
@ -1005,17 +1052,15 @@
return top || this.parenthetical ? code : ("(" + (code) + ")");
};
AssignNode.prototype.compileSplice = function(o) {
var from, l, name, plus, range, to, val;
name = this.variable.compile(merge(o, {
onlyFirst: true
}));
l = this.variable.properties.length;
range = this.variable.properties[l - 1].range;
var from, name, plus, range, ref, to, val;
range = this.variable.properties.pop().range;
name = this.variable.compile(o);
plus = range.exclusive ? '' : ' + 1';
from = range.from ? range.from.compile(o) : '0';
to = range.to ? range.to.compile(o) + ' - ' + from + plus : ("" + (name) + ".length");
ref = o.scope.freeVariable('ref');
val = this.value.compile(o);
return "[].splice.apply(" + (name) + ", [" + (from) + ", " + (to) + "].concat(" + (val) + "))";
return "([].splice.apply(" + (name) + ", [" + (from) + ", " + (to) + "].concat(" + (ref) + " = " + (val) + ")), " + (ref) + ")";
};
return AssignNode;
})();
@ -1025,7 +1070,7 @@
this.params = _arg;
CodeNode.__super__.constructor.call(this);
this.params || (this.params = []);
this.body || (this.body = (new Expressions));
this.body || (this.body = new Expressions);
this.bound = tag === 'boundfunc';
if (this.bound) {
this.context = 'this';
@ -1143,8 +1188,7 @@
SplatNode.prototype["class"] = 'SplatNode';
SplatNode.prototype.children = ['name'];
SplatNode.prototype.compileNode = function(o) {
var _ref2;
return (typeof (_ref2 = this.index) !== "undefined" && _ref2 !== null) ? this.compileParam(o) : this.name.compile(o);
return (this.index != null) ? this.compileParam(o) : this.name.compile(o);
};
SplatNode.prototype.compileParam = function(o) {
var _len, _ref2, assign, end, idx, len, name, pos, trailing, variadic;
@ -1205,14 +1249,14 @@
exports.WhileNode = (function() {
WhileNode = function(condition, opts) {
WhileNode.__super__.constructor.call(this);
if (opts == null ? undefined : opts.invert) {
if (((opts != null) ? opts.invert : null)) {
if (condition instanceof OpNode) {
condition = new ParentheticalNode(condition);
}
condition = new OpNode('!', condition);
}
this.condition = condition;
this.guard = opts == null ? undefined : opts.guard;
this.guard = ((opts != null) ? opts.guard : null);
return this;
};
__extends(WhileNode, BaseNode);
@ -1315,6 +1359,10 @@
return OpNode.__super__.toString.call(this, idt, this["class"] + ' ' + this.operator);
};
OpNode.prototype.compileNode = function(o) {
var node;
if (node = ValueNode.unfoldSoak(o, this, 'first')) {
return node.compile(o);
}
if (this.isChainable() && this.first.unwrap() instanceof OpNode && this.first.unwrap().isChainable()) {
return this.compileChain(o);
}
@ -1343,7 +1391,10 @@
return "(" + (first) + ") && (" + (shared) + " " + (this.operator) + " " + (second) + ")";
};
OpNode.prototype.compileAssignment = function(o) {
var _ref2, first, firstVar, second;
var _ref2, first, firstVar, left, rite, second;
_ref2 = this.first.cacheReference(o), left = _ref2[0], rite = _ref2[1];
rite = new AssignNode(rite, this.second);
return new OpNode(this.operator.slice(0, -1), left, rite).compile(o);
_ref2 = this.first.compileReference(o, {
precompile: true,
assignment: true
@ -1361,9 +1412,15 @@
return "" + (first) + " " + (this.operator.substr(0, 2)) + " (" + (firstVar) + " = " + (second) + ")";
};
OpNode.prototype.compileExistence = function(o) {
var _ref2, ref, test;
_ref2 = ExistenceNode.compileTest(o, this.first), test = _ref2[0], ref = _ref2[1];
return "" + (test) + " ? " + (ref) + " : " + (this.second.compile(o));
var fst, ref;
if (this.first.isComplex()) {
ref = o.scope.freeVariable('ref');
fst = new ParentheticalNode(new AssignNode(literal(ref), this.first));
} else {
fst = this.first;
ref = fst.compile(o);
}
return new ExistenceNode(fst).compile(o) + (" ? " + (ref) + " : " + (this.second.compile(o)));
};
OpNode.prototype.compileUnary = function(o) {
var parts, space;
@ -1479,20 +1536,13 @@
ExistenceNode.prototype["class"] = 'ExistenceNode';
ExistenceNode.prototype.children = ['expression'];
ExistenceNode.prototype.compileNode = function(o) {
var test;
test = ExistenceNode.compileTest(o, this.expression)[0];
return this.parenthetical ? test.slice(1, -1) : test;
};
ExistenceNode.compileTest = function(o, variable) {
var _ref2, first, second;
_ref2 = variable.compileReference(o, {
precompile: true
}), first = _ref2[0], second = _ref2[1];
first = first === second && o.scope.check(first) ? ("(" + (first) + " != null)") : ("(typeof " + (first) + " !== \"undefined\" && " + (second) + " !== null)");
return [first, second];
var code;
code = this.expression.compile(o);
code = IDENTIFIER.test(code) && !o.scope.check(code) ? ("typeof " + (code) + " !== \"undefined\" && " + (code) + " !== null") : ("" + (code) + " != null");
return this.parenthetical ? code : ("(" + (code) + ")");
};
return ExistenceNode;
}).call(this);
})();
exports.ParentheticalNode = (function() {
ParentheticalNode = function(_arg) {
this.expression = _arg;
@ -1742,10 +1792,12 @@
IfNode.prototype.children = ['condition', 'body', 'elseBody', 'assigner'];
IfNode.prototype.topSensitive = YES;
IfNode.prototype.bodyNode = function() {
return this.body == null ? undefined : this.body.unwrap();
var _ref2;
return (((_ref2 = this.body) != null) ? _ref2.unwrap() : null);
};
IfNode.prototype.elseBodyNode = function() {
return this.elseBody == null ? undefined : this.elseBody.unwrap();
var _ref2;
return (((_ref2 = this.elseBody) != null) ? _ref2.unwrap() : null);
};
IfNode.prototype.addElse = function(elseBody, statement) {
if (this.isChain) {
@ -1757,7 +1809,7 @@
return this;
};
IfNode.prototype.isStatement = function(o) {
return this.statement || (this.statement = (!!((o && o.top) || this.bodyNode().isStatement(o) || (this.elseBody && this.elseBodyNode().isStatement(o)))));
return this.statement || (this.statement = !!((o && o.top) || this.bodyNode().isStatement(o) || (this.elseBody && this.elseBodyNode().isStatement(o))));
};
IfNode.prototype.compileCondition = function(o) {
var _i, _len, _ref2, _result, cond, conditions;

View file

@ -68,7 +68,7 @@
if (tuple.length < 3) {
tuple.unshift(null);
}
return buildRule.apply(this, tuple);
return buildRule.apply(buildRule, tuple);
})());
}
return _result;

View file

@ -54,7 +54,7 @@
};
Rewriter.prototype.adjustComments = function() {
return this.scanTokens(function(token, i) {
var _ref, after, before, post, prev;
var _ref, _this, after, before, post, prev;
if (token[0] !== 'HERECOMMENT') {
return 1;
}
@ -68,7 +68,7 @@
}
} else if (prev && !('TERMINATOR' === (_ref = prev[0]) || 'INDENT' === _ref || 'OUTDENT' === _ref)) {
if (post && post[0] === 'TERMINATOR' && after && after[0] === 'OUTDENT') {
(_ref = this.tokens).splice.apply(_ref, [i + 2, 0].concat(this.tokens.splice(i, 2)));
(_this = this.tokens).splice.apply(_this, [i + 2, 0].concat(this.tokens.splice(i, 2)));
if (this.tokens[i + 2][0] !== 'TERMINATOR') {
this.tokens.splice(i + 2, 0, ['TERMINATOR', "\n", prev[2]]);
}
@ -219,13 +219,13 @@
};
Rewriter.prototype.addImplicitIndentation = function() {
return this.scanTokens(function(token, i) {
var _ref, action, condition, indent, outdent, starter;
var _ref, _this, action, condition, indent, outdent, starter;
if (token[0] === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
(_ref = this.tokens).splice.apply(_ref, [i, 0].concat(this.indentation(token)));
(_this = this.tokens).splice.apply(_this, [i, 0].concat(this.indentation(token)));
return 2;
}
if (token[0] === 'CATCH' && (this.tag(i + 2) === 'TERMINATOR' || this.tag(i + 2) === 'FINALLY')) {
(_ref = this.tokens).splice.apply(_ref, [i + 2, 0].concat(this.indentation(token)));
(_this = this.tokens).splice.apply(_this, [i + 2, 0].concat(this.indentation(token)));
return 4;
}
if (include(SINGLE_LINERS, token[0]) && this.tag(i + 1) !== 'INDENT' && !(token[0] === 'ELSE' && this.tag(i + 1) === 'IF')) {
@ -345,7 +345,7 @@
}
debt[mtag] += 1;
val = [oppos, mtag === 'INDENT' ? match[1] : oppos];
if ((this.tokens[(_ref2 = i + 2)] == null ? undefined : this.tokens[_ref2][0]) === mtag) {
if ((((_ref2 = this.tokens[i + 2]) != null) ? _ref2[0] === mtag : null)) {
this.tokens.splice(i + 3, 0, val);
stack.push(match);
} else {

View file

@ -40,7 +40,7 @@ exports.BaseNode = class BaseNode
# depending on whether it's being used as part of a larger expression, or is a
# top-level statement within the function body.
compile: (o) ->
@options = merge o or {}
@options = if o then merge o else {}
@tab = o.indent
del @options, 'chainRoot' unless this instanceof AccessorNode or this instanceof IndexNode
top = if @topSensitive() then @options.top else del @options, 'top'
@ -63,16 +63,13 @@ exports.BaseNode = class BaseNode
# in multiple places, ensure that the expression is only ever evaluated once,
# by assigning it to a temporary variable.
compileReference: (o, options) ->
options or= {}
pair = if not @isComplex()
pair = unless @isComplex()
[this, this]
else if this instanceof ValueNode and options.assignment
this.cacheIndexes(o)
else
reference = literal o.scope.freeVariable 'ref'
compiled = new AssignNode reference, this
[compiled, reference]
return [pair[0].compile(o), pair[1].compile(o)] if options.precompile
(pair[i] = node.compile o) for node, i in pair if options?.precompile
pair
# Convenience method to grab the current indentation level, plus tabbing in.
@ -107,7 +104,7 @@ exports.BaseNode = class BaseNode
# Convenience for the most common use of contains. Does the node contain
# a pure statement?
containsPureStatement: ->
@isPureStatement() or @contains (n) -> n.isPureStatement and n.isPureStatement()
@isPureStatement() or @contains (n) -> n.isPureStatement?()
# Perform an in-order traversal of the AST. Crosses scope boundaries.
traverse: (block) -> @traverseChildren true, block
@ -133,8 +130,10 @@ exports.BaseNode = class BaseNode
traverseChildren: (crossScope, func) ->
@eachChild (child) ->
func.apply(this, arguments)
child.traverseChildren(crossScope, func) if child instanceof BaseNode
return false if func(child) is false
if child instanceof BaseNode and
(crossScope or child not instanceof CodeNode)
child.traverseChildren crossScope, func
# Default implementations of the common node properties and methods. Nodes
# will override these with custom logic, if needed.
@ -311,10 +310,10 @@ exports.ValueNode = class ValueNode extends BaseNode
# Some boolean checks for the benefit of other nodes.
isArray: ->
@base instanceof ArrayNode and not @hasProperties()
@base instanceof ArrayNode and not @properties.length
isObject: ->
@base instanceof ObjectNode and not @hasProperties()
@base instanceof ObjectNode and not @properties.length
isSplice: ->
last(@properties) instanceof SliceNode
@ -323,7 +322,8 @@ exports.ValueNode = class ValueNode extends BaseNode
@base.isComplex() or @hasProperties()
makeReturn: ->
if @hasProperties() then super() else @base.makeReturn()
if @properties.length then super() else @base.makeReturn()
# The value can be unwrapped as its inner node, if there are no attached
# properties.
@ -337,20 +337,24 @@ exports.ValueNode = class ValueNode extends BaseNode
isNumber: ->
@base instanceof LiteralNode and NUMBER.test @base.value
# If the value node has indexes containing function calls, and the value node
# needs to be used twice, in compound assignment ... then we need to cache
# the value of the indexes.
cacheIndexes: (o) ->
copy = new ValueNode @base, @properties[0..]
if @base.isComplex()
[@base, copy.base] = @base.compileReference o
for prop, i in copy.properties
if prop instanceof IndexNode and prop.index.isComplex()
[index, indexVar] = prop.index.compileReference o
this.properties[i] = first = new IndexNode index
copy.properties[i] = new IndexNode indexVar
first.soakNode = yes if prop.soakNode
[this, copy]
# A reference has base part (`this` value) and name part.
# We cache them separately for compiling complex expressions.
# `a()[b()] ?= c` -> `(_base = a())[_name = b()] ? _base[_name] = c`
cacheReference: (o) ->
name = last @properties
if not @base.isComplex() and @properties.length < 2 and
not name?.isComplex()
return [this, this] # `a` `a.b`
base = new ValueNode @base, @properties.slice 0, -1
if base.isComplex() # `a().b`
bref = literal o.scope.freeVariable 'base'
base = new ValueNode new ParentheticalNode new AssignNode bref, base
return [base, bref] unless name # `a()`
if name.isComplex() # `a[b()]`
nref = literal o.scope.freeVariable 'name'
name = new IndexNode new AssignNode nref, name.index
nref = new IndexNode nref
[base.push(name), new ValueNode(bref or base.base, [nref or name])]
# Override compile to unwrap the value when possible.
compile: (o) ->
@ -361,41 +365,46 @@ exports.ValueNode = class ValueNode extends BaseNode
# operators `?.` interspersed. Then we have to take care not to accidentally
# evaluate a anything twice when building the soak chain.
compileNode: (o) ->
only = del o, 'onlyFirst'
op = @tags.operation
props = if only then @properties[0...-1] else @properties
return ex.compile o if ex = @unfoldSoak o
props = @properties
o.chainRoot or= this
for prop in props when prop.soakNode
hasSoak = yes
break
if hasSoak and @isComplex()
[me, copy] = @cacheIndexes o
@base.parenthetical = yes if @parenthetical and not props.length
baseline = @base.compile o
baseline = "(#{baseline})" if @hasProperties() and (@base instanceof ObjectNode or @isNumber())
complete = @last = baseline
code = @base.compile o
if props[0] instanceof AccessorNode and @isNumber() or
o.top and @base instanceof ObjectNode
code = "(#{code})"
(code += prop.compile o) for prop in props
return code
for prop, i in props
@source = baseline
if prop.soakNode
if i is 0 and @base.isComplex()
temp = o.scope.freeVariable 'ref'
complete = "(#{ baseline = temp } = (#{complete}))"
complete = if i is 0 and not o.scope.check complete
"(typeof #{complete} === \"undefined\" || #{baseline} === null)"
else
"#{complete} == null"
complete += ' ? undefined : ' + baseline += prop.compile o
else
part = prop.compile(o)
baseline += if hasSoak and prop.isComplex()
copy.properties[i].compile o
else
part
complete += part
@last = part
# Unfold a soak into an `IfNode`: `a?.b` -> `a.b if a?`
unfoldSoak: (o) ->
if @base.soakNode
Array::push.apply @base.body.properties, @properties
return @base
for prop, i in @properties when prop.soakNode
prop.soakNode = off
fst = new ValueNode @base, @properties.slice 0, i
snd = new ValueNode @base, @properties.slice i
if fst.isComplex()
ref = literal o.scope.freeVariable 'ref'
fst = new ParentheticalNode new AssignNode ref, fst
snd.base = ref
ifn = new IfNode new ExistenceNode(fst), snd, operation: yes
ifn.soakNode = on
return ifn
null
if op and @wrapped then "(#{complete})" else complete
# Unfold a node's child if soak, then tuck the node under created `IfNode`
@unfoldSoak: (o, parent, name) ->
node = parent[name]
if node instanceof IfNode and node.soakNode
ifnode = node
else if node instanceof ValueNode
ifnode = node.unfoldSoak o
return unless ifnode
parent[name] = ifnode.body
ifnode.body = new ValueNode parent
ifnode
#### CommentNode
@ -429,9 +438,9 @@ exports.CallNode = class CallNode extends BaseNode
@isSuper = variable is 'super'
@variable = if @isSuper then null else variable
@args or= []
@first = @last = ''
@compileSplatArguments = (o) ->
SplatNode.compileSplattedArray.call(this, @args, o)
compileSplatArguments: (o) ->
SplatNode.compileSplattedArray @args, o
# Tag this invocation as creating a new instance.
newInstance: ->
@ -443,42 +452,58 @@ exports.CallNode = class CallNode extends BaseNode
# Grab the reference to the superclass' implementation of the current method.
superReference: (o) ->
throw new Error "cannot call super outside of a function" unless o.scope.method
methname = o.scope.method.name
meth = if o.scope.method.proto
"#{o.scope.method.proto}.__super__.#{methname}"
else if methname
"#{methname}.__super__.constructor"
else throw new Error "cannot call super on an anonymous function."
{method} = o.scope
throw Error "cannot call super outside of a function" unless method
{name} = method
throw Error "cannot call super on an anonymous function." unless name
if method.klass
"#{method.klass}.__super__.#{name}"
else
"#{name}.__super__.constructor"
unfoldSoak: (o) ->
call = this
list = []
loop
if call.variable instanceof CallNode
list.push call
call = call.variable
continue
break unless call.variable instanceof ValueNode
list.push call
break unless (call = call.variable.base) instanceof CallNode
for call in list.reverse()
if node
if call.variable instanceof CallNode
call.variable = node
else
call.variable.base = node
node = ValueNode.unfoldSoak o, call, 'variable'
node
# Compile a vanilla function call.
compileNode: (o) ->
o.chainRoot = this unless o.chainRoot
op = @tags.operation
return node.compile o if node = @unfoldSoak o
o.chainRoot or= this
if @exist
if @variable instanceof ValueNode and
last(@variable.properties) instanceof AccessorNode
methodAccessor = @variable.properties.pop()
[first, meth] = @variable.compileReference o
@first = new ValueNode(first, [methodAccessor]).compile o
@meth = new ValueNode(meth, [methodAccessor]).compile o
if val = @variable
val = new ValueNode val unless val instanceof ValueNode
[left, rite] = val.cacheReference o
rite = new CallNode rite, @args
else
[@first, @meth] = @variable.compileReference o, precompile: yes
@first = "(typeof #{@first} === \"function\" ? "
@last = " : undefined)"
else if @variable
@meth = @variable.compile o
left = literal @superReference o
rite = new CallNode new ValueNode(left), @args
rite.isNew = @isNew
left = "typeof #{ left.compile o } !== \"function\""
rite = rite.compile o
return "(#{left} ? undefined : #{rite})"
for arg in @args when arg instanceof SplatNode
code = @compileSplat(o)
if not code
args = for arg in @args
arg.parenthetical = true
arg.compile o
code = if @isSuper
@compileSuper(args.join(', '), o)
else
"#{@first}#{@prefix()}#{@meth}(#{ args.join(', ') })#{@last}"
if op and @variable and @variable.wrapped then "(#{code})" else code
return @compileSplat o
args = ((arg.parenthetical = on) and arg.compile o for arg in @args).join ', '
if @isSuper
@compileSuper args, o
else
"#{@prefix()}#{@variable.compile o}(#{args})"
# `super()` is converted into a call against the superclass's implementation
# of the current function.
@ -490,29 +515,32 @@ exports.CallNode = class CallNode extends BaseNode
# If it's a constructor, then things get real tricky. We have to inject an
# inner constructor in order to be able to pass the varargs.
compileSplat: (o) ->
meth = @meth or @superReference(o)
obj = @variable and @variable.source or 'this'
unless IDENTIFIER.test(obj) or NUMBER.test(obj)
temp = o.scope.freeVariable 'ref'
obj = temp
meth = "(#{temp} = #{ @variable.source })#{ @variable.last }"
if @isNew
mentionsArgs = no
for arg in @args
arg.contains (n) -> mentionsArgs or= n instanceof LiteralNode and (n.value is 'arguments')
utility 'extends'
a = o.scope.freeVariable 'ctor'
b = o.scope.freeVariable 'ref'
c = o.scope.freeVariable 'result'
"""
#{@first}(function() {
#{@idt(1)}var ctor = function(){};
#{@idt(1)}__extends(ctor, #{a} = #{meth});
#{@idt(1)}return typeof (#{c} = #{a}.apply(#{b} = new ctor, #{ @compileSplatArguments(o) })) === "object" ? #{c} : #{b};
#{@tab}}).#{ if mentionsArgs then 'apply(this, arguments)' else 'call(this)'}#{@last}
"""
else
"#{@first}#{meth}.apply(#{obj}, #{ @compileSplatArguments(o) })#{@last}"
splatargs = @compileSplatArguments o
return "#{ @superReference o }.apply(this, #{splatargs})" if @isSuper
unless @isNew
base = new ValueNode base unless (base = @variable) instanceof ValueNode
if (name = base.properties.pop()) and base.isComplex()
ref = o.scope.freeVariable 'this'
fun = "(#{ref} = #{ base.compile o })#{ name.compile o }"
else
fun = ref = base.compile o
fun += name.compile o if name
return "#{fun}.apply(#{ref}, #{splatargs})"
call = 'call(this)'
argvar = (n) -> n instanceof LiteralNode and n.value is 'arguments'
for arg in @args when arg.contains argvar
call = 'apply(this, arguments)'
break
a = o.scope.freeVariable 'ctor'
b = o.scope.freeVariable 'ref'
c = o.scope.freeVariable 'result'
"""
(function() {
#{idt = @idt 1}var ctor = function() {};
#{idt}#{utility 'extends'}(ctor, #{a} = #{ @variable.compile o });
#{idt}return typeof (#{c} = #{a}.apply(#{b} = new ctor, #{splatargs})) === "object" ? #{c} : #{b};
#{@tab}}).#{call}
"""
#### ExtendsNode
@ -706,8 +734,9 @@ exports.ArrayNode = class ArrayNode extends BaseNode
constructor: (@objects) ->
super()
@objects or= []
@compileSplatLiteral = (o) ->
SplatNode.compileSplattedArray.call(this, @objects, o)
compileSplatLiteral: (o) ->
SplatNode.compileSplattedArray @objects, o
compileNode: (o) ->
o.indent = @idt 1
@ -807,9 +836,8 @@ exports.ClassNode = class ClassNode extends BaseNode
# property of an object -- including within object literals.
exports.AssignNode = class AssignNode extends BaseNode
# Matchers for detecting prototype assignments.
PROTO_ASSIGN: /^(\S+)\.prototype/
LEADING_DOT: /^\.(?:prototype\.)?/
# Matchers for detecting class/method names
METHOD_DEF: /^(?:(\S+)\.prototype\.)?([$A-Za-z_][$\w]*)$/
class: 'AssignNode'
children: ['variable', 'value']
@ -830,15 +858,13 @@ exports.AssignNode = class AssignNode extends BaseNode
if isValue = @isValue()
return @compilePatternMatch(o) if @variable.isArray() or @variable.isObject()
return @compileSplice(o) if @variable.isSplice()
return node.compile o if node = ValueNode.unfoldSoak o, this, 'variable'
top = del o, 'top'
stmt = del o, 'asStatement'
name = @variable.compile(o)
end = if isValue then @variable.last.replace(@LEADING_DOT, '') else name
match = name.match(@PROTO_ASSIGN)
proto = match and match[1]
if @value instanceof CodeNode
@value.name = end if IDENTIFIER.test end
@value.proto = proto if proto
if @value instanceof CodeNode and match = @METHOD_DEF.exec name
@value.name = match[2]
@value.klass = match[1]
val = @value.compile o
return "#{name}: #{val}" if @context is 'object'
o.scope.find name unless isValue and (@variable.hasProperties() or @variable.namespaced)
@ -898,14 +924,14 @@ exports.AssignNode = class AssignNode extends BaseNode
# Compile the assignment from an array splice literal, using JavaScript's
# `Array#splice` method.
compileSplice: (o) ->
name = @variable.compile merge o, onlyFirst: true
l = @variable.properties.length
range = @variable.properties[l - 1].range
{range} = @variable.properties.pop()
name = @variable.compile o
plus = if range.exclusive then '' else ' + 1'
from = if range.from then range.from.compile(o) else '0'
to = if range.to then range.to.compile(o) + ' - ' + from + plus else "#{name}.length"
ref = o.scope.freeVariable 'ref'
val = @value.compile(o)
"[].splice.apply(#{name}, [#{from}, #{to}].concat(#{val}))"
"([].splice.apply(#{name}, [#{from}, #{to}].concat(#{ref} = #{val})), #{ref})"
#### CodeNode
@ -1172,6 +1198,7 @@ exports.OpNode = class OpNode extends BaseNode
super(idt, @class + ' ' + @operator)
compileNode: (o) ->
return node.compile o if node = ValueNode.unfoldSoak o, this, 'first'
return @compileChain(o) if @isChainable() and @first.unwrap() instanceof OpNode and @first.unwrap().isChainable()
return @compileAssignment(o) if indexOf(@ASSIGNMENT, @operator) >= 0
return @compileUnary(o) if @isUnary()
@ -1195,6 +1222,10 @@ exports.OpNode = class OpNode extends BaseNode
# operands are only evaluated once, even though we have to reference them
# more than once.
compileAssignment: (o) ->
[left, rite] = @first.cacheReference o
rite = new AssignNode rite, @second
return new OpNode(@operator.slice(0, -1), left, rite).compile o
[first, firstVar] = @first.compileReference o, precompile: yes, assignment: yes
second = @second.compile o
second = "(#{second})" if @second instanceof OpNode
@ -1202,11 +1233,14 @@ exports.OpNode = class OpNode extends BaseNode
return "#{first} = #{ ExistenceNode.compileTest(o, literal(firstVar))[0] } ? #{firstVar} : #{second}" if @operator is '?='
"#{first} #{ @operator.substr(0, 2) } (#{firstVar} = #{second})"
# If this is an existence operator, we delegate to `ExistenceNode.compileTest`
# to give us the safe references for the variables.
compileExistence: (o) ->
[test, ref] = ExistenceNode.compileTest(o, @first)
"#{test} ? #{ref} : #{ @second.compile(o) }"
if @first.isComplex()
ref = o.scope.freeVariable 'ref'
fst = new ParentheticalNode new AssignNode literal(ref), @first
else
fst = @first
ref = fst.compile o
new ExistenceNode(fst).compile(o) + " ? #{ref} : #{ @second.compile o }"
# Compile a unary **OpNode**.
compileUnary: (o) ->
@ -1302,19 +1336,12 @@ exports.ExistenceNode = class ExistenceNode extends BaseNode
super()
compileNode: (o) ->
test = ExistenceNode.compileTest(o, @expression)[0]
if @parenthetical then test.slice 1, -1 else test
# The meat of the **ExistenceNode** is in this static `compileTest` method
# because other nodes like to check the existence of their variables as well.
# Be careful not to double-evaluate anything.
@compileTest: (o, variable) ->
[first, second] = variable.compileReference o, precompile: yes
first = if first is second and o.scope.check first
"(#{first} != null)"
code = @expression.compile o
code = if IDENTIFIER.test(code) and not o.scope.check code
"typeof #{code} !== \"undefined\" && #{code} !== null"
else
"(typeof #{first} !== \"undefined\" && #{second} !== null)"
[first, second]
"#{code} != null"
if @parenthetical then code else "(#{code})"
#### ParentheticalNode
@ -1413,8 +1440,8 @@ exports.ForNode = class ForNode extends BaseNode
sourcePart = source.compileVariables(o)
forPart = source.compile merge o, index: ivar, step: @step
else
svar = scope.freeVariable 'ref'
sourcePart = "#{svar} = #{ @source.compile(o) };"
svar = scope.freeVariable 'ref'
sourcePart = "#{svar} = #{ @source.compile(o) };"
if @pattern
namePart = new AssignNode(@name, literal("#{svar}[#{ivar}]")).compile(merge o, {indent: @idt(1), top: true, keepLevel: yes}) + '\n'
else

View file

@ -1,45 +1,26 @@
num = 10
num -= 5
ok num is 5
num = -3
ok num is -3
num = +3
ok num is 3
eq num, 5
num *= 10
ok num is 30
eq num, 50
num /= 10
eq num, 5
ok num is 3
num %= 3
eq num, 2
val = false
val ||= 'value'
val ||= 'eulav'
eq val, 'value'
ok val is 'value'
val &&= 'rehto'
val &&= 'other'
ok val is 'other'
eq val, 'other'
val = null
val ?= 'value'
ok val is 'value'
val = 6
val = -(10)
ok val is -10
val -= (10)
ok val is -20
val ?= 'eulav'
eq val, 'value'

View file

@ -37,45 +37,33 @@ ok(if getNextNode()? then true else false)
# Existence chains, soaking up undefined properties:
obj = {
obj =
prop: "hello"
}
ok obj?.prop is "hello"
ok obj?['prop'] is "hello"
ok obj.prop?.length is 5
ok obj?['prop']?['length'] is 5
ok obj?.prop?.non?.existent?.property is undefined
ok obj?['non']?['existent'].property is undefined
eq obj?.prop, "hello"
eq obj?['prop'], "hello"
eq obj.prop?.length, 5
eq obj?.prop?['length'], 5
eq obj?.prop?.non?.existent?.property, null
# Soaks and caches method calls as well.
arr = ["--", "----"]
ok arr.pop()?.length is 4
ok arr.pop()?.length is 2
ok arr.pop()?.length is undefined
ok arr[0]?.length is undefined
ok arr.pop()?.length?.non?.existent()?.property is undefined
eq arr.pop()?.length, 4
eq arr.pop()?.length, 2
eq arr.pop()?.length, null
eq arr.pop()?.length?.non?.existent()?.property, null
# Soaks method calls safely.
value = undefined
result = value?.toString().toLowerCase()
ok result is undefined
value = null
eq value?.toString().toLowerCase(), null
value = 10
result = value?.toString().toLowerCase()
eq value?.toString().toLowerCase(), '10'
ok result is '10'
ok(process.exit.nothing?.property() or 101)
eq process.exit.nothing?.property() or 101, 101
counter = 0
func = ->
@ -89,9 +77,8 @@ ok obj[func()]()[func()]()[func()]()?.value is 25
ok counter is 3
# Soaks inner values.
ident = (obj) -> obj
ok ident(non?.existent().method()) is undefined
eq ident(non?.existent().method()), null, 'soaks inner values'
# Soaks constructor invocations.
@ -104,14 +91,10 @@ ok (new Foo())?.bar is 'bat'
ok a is 1
# Safely existence test on soaks.
result = not value?.property?
ok result
ok not value?.property?, 'safely checks existence on soaks'
# Safely calls values off of non-existent variables.
result = nothing?.value
ok result is undefined
eq nothing?.value, null, 'safely calls values off of non-existent variables'
# Assign to the result of an exsitential operation with a minus.
@ -146,6 +129,14 @@ ok maybe_close(plus1, 41)?() is 42
ok (maybe_close plus1, 41)?() is 42
ok (maybe_close 'string', 41)?() is undefined
ok 2?(3) is undefined
eq 2?(3), undefined
ok calendar?[Date()] is undefined
#726
eq calendar?[Date()], null
#733
a = b: {c: null}
eq a.b?.c?(), undefined
a.b?.c or= (it) -> it
eq a.b?.c?(1), 1
eq a.b?.c?([2, 3]...), 2

View file

@ -49,7 +49,7 @@ medalists contenders..., 'Tim', 'Moe', 'Jim'
ok last is 'Jim'
obj = {
obj =
name: 'moe'
accessor: (args...) ->
[@name].concat(args).join(' ')
@ -58,7 +58,6 @@ obj = {
@accessor(args...)
index: 0
0: {method: -> this is obj[0]}
}
ok obj.getNames() is 'moe jane ted'
ok obj[obj.index++].method([]...), 'should cache base value'