#713: destructuring assignment is no longer statement and correctly returns RHS value

This commit is contained in:
satyr 2010-09-29 05:47:12 +09:00
parent b2313beaf4
commit 7450df8104
9 changed files with 206 additions and 301 deletions

View File

@ -13,9 +13,7 @@
task: function(name, description, action) {
var _ref;
if (!(action)) {
_ref = [description, action];
action = _ref[0];
description = _ref[1];
_ref = [description, action], action = _ref[0], description = _ref[1];
}
return (tasks[name] = {
name: name,

View File

@ -1,10 +1,8 @@
(function() {
var Lexer, _ref, compile, fs, lexer, parser, path;
var Lexer, compile, fs, lexer, parser, path;
path = require('path');
_ref = require('./lexer');
Lexer = _ref.Lexer;
_ref = require('./parser');
parser = _ref.parser;
Lexer = require('./lexer').Lexer;
parser = require('./parser').parser;
if (require.extensions) {
fs = require('fs');
require.extensions['.coffee'] = function(module, filename) {

View File

@ -5,11 +5,8 @@
optparse = require('./optparse');
CoffeeScript = require('./coffee-script');
helpers = require('./helpers');
_ref = require('child_process');
spawn = _ref.spawn;
exec = _ref.exec;
_ref = require('events');
EventEmitter = _ref.EventEmitter;
_ref = require('child_process'), spawn = _ref.spawn, exec = _ref.exec;
EventEmitter = require('events').EventEmitter;
helpers.extend(CoffeeScript, new EventEmitter);
global.CoffeeScript = CoffeeScript;
BANNER = 'coffee compiles CoffeeScript source files into JavaScript.\n\nUsage:\n coffee path/to/script.coffee';
@ -197,9 +194,7 @@
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
token = _ref2[_i];
_result.push((function() {
_ref3 = [token[0], token[1].toString().replace(/\n/, '\\n')];
tag = _ref3[0];
value = _ref3[1];
_ref3 = [token[0], token[1].toString().replace(/\n/, '\\n')], tag = _ref3[0], value = _ref3[1];
return "[" + (tag) + " " + (value) + "]";
})());
}

View File

@ -1,14 +1,8 @@
(function() {
var ASSIGNED, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, CONVERSIONS, HEREDOC, HEREDOC_INDENT, IDENTIFIER, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NEXT_CHARACTER, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX_END, REGEX_ESCAPE, REGEX_INTERPOLATION, REGEX_START, RESERVED, Rewriter, SHIFT, SIMPLESTR, UNARY, WHITESPACE, _ref, compact, count, include, last, starts;
var __slice = Array.prototype.slice;
_ref = require('./rewriter');
Rewriter = _ref.Rewriter;
_ref = require('./helpers');
include = _ref.include;
count = _ref.count;
starts = _ref.starts;
compact = _ref.compact;
last = _ref.last;
Rewriter = require('./rewriter').Rewriter;
_ref = require('./helpers'), include = _ref.include, count = _ref.count, starts = _ref.starts, compact = _ref.compact, last = _ref.last;
exports.Lexer = (function() {
Lexer = function() {};
Lexer.prototype.tokenize = function(code, options) {
@ -142,9 +136,7 @@
if (!(match = this.chunk.match(COMMENT))) {
return false;
}
_ref2 = match;
comment = _ref2[0];
here = _ref2[1];
_ref2 = match, comment = _ref2[0], here = _ref2[1];
this.line += count(comment, '\n');
this.i += comment.length;
if (here) {
@ -293,9 +285,7 @@
Lexer.prototype.literalToken = function() {
var _ref2, match, prev, space, spaced, tag, val, value;
if (match = this.chunk.match(OPERATOR)) {
_ref2 = match;
value = _ref2[0];
space = _ref2[1];
_ref2 = match, value = _ref2[0], space = _ref2[1];
if (CODE.test(value)) {
this.tagParameters();
}
@ -373,9 +363,7 @@
};
Lexer.prototype.sanitizeHeredoc = function(doc, options) {
var _ref2, attempt, herecomment, indent, match;
_ref2 = options;
indent = _ref2.indent;
herecomment = _ref2.herecomment;
_ref2 = options, indent = _ref2.indent, herecomment = _ref2.herecomment;
if (herecomment && !include(doc, '\n')) {
return doc;
}
@ -446,9 +434,7 @@
_ref2 = delimited;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
pair = _ref2[_i];
_ref3 = pair;
open = _ref3[0];
close = _ref3[1];
_ref3 = pair, open = _ref3[0], close = _ref3[1];
if (levels.length && starts(str, close, i) && last(levels) === pair) {
levels.pop();
i += close.length - 1;
@ -478,9 +464,7 @@
};
Lexer.prototype.interpolateString = function(str, options) {
var _len, _ref2, _ref3, end, escapeQuotes, escaped, expr, heredoc, i, idx, inner, interpolated, lexer, nested, pi, push, quote, s, tag, tok, token, tokens, value;
_ref2 = options || {};
heredoc = _ref2.heredoc;
escapeQuotes = _ref2.escapeQuotes;
_ref2 = options || {}, heredoc = _ref2.heredoc, escapeQuotes = _ref2.escapeQuotes;
quote = str.charAt(0);
if (quote !== '"' || str.length < 3) {
return this.token('STRING', str);
@ -533,14 +517,11 @@
if (interpolated) {
this.token('(', '(');
}
_ref2 = tokens;
push = _ref2.push;
push = tokens.push;
_ref2 = tokens;
for (i = 0, _len = _ref2.length; i < _len; i++) {
token = _ref2[i];
_ref3 = token;
tag = _ref3[0];
value = _ref3[1];
_ref3 = token, tag = _ref3[0], value = _ref3[1];
if (tag === 'TOKENS') {
push.apply(this.tokens, value);
} else if (tag === 'STRING' && escapeQuotes) {

View File

@ -8,18 +8,8 @@
if (typeof parent.extended === "function") parent.extended(child);
child.__super__ = parent.prototype;
};
_ref = require('./scope');
Scope = _ref.Scope;
_ref = require('./helpers');
compact = _ref.compact;
flatten = _ref.flatten;
merge = _ref.merge;
del = _ref.del;
include = _ref.include;
indexOf = _ref.indexOf;
starts = _ref.starts;
ends = _ref.ends;
last = _ref.last;
Scope = require('./scope').Scope;
_ref = require('./helpers'), compact = _ref.compact, flatten = _ref.flatten, merge = _ref.merge, del = _ref.del, include = _ref.include, indexOf = _ref.indexOf, starts = _ref.starts, ends = _ref.ends, last = _ref.last;
YES = function() {
return true;
};
@ -264,7 +254,8 @@
return this.isStatement() ? this : LiteralNode.__super__.makeReturn.call(this);
};
LiteralNode.prototype.isStatement = function() {
return this.value === 'break' || this.value === 'continue' || this.value === 'debugger';
var _ref2;
return ('break' === (_ref2 = this.value) || 'continue' === _ref2 || 'debugger' === _ref2);
};
LiteralNode.prototype.isPureStatement = LiteralNode.prototype.isStatement;
LiteralNode.prototype.isComplex = NO;
@ -355,17 +346,13 @@
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];
_ref2 = this.base.compileReference(o), this.base = _ref2[0], copy.base = _ref2[1];
}
_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];
_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) {
@ -393,9 +380,7 @@
}
}
if (hasSoak && this.isComplex()) {
_ref2 = this.cacheIndexes(o);
me = _ref2[0];
copy = _ref2[1];
_ref2 = this.cacheIndexes(o), me = _ref2[0], copy = _ref2[1];
}
if (this.parenthetical && !props.length) {
this.base.parenthetical = true;
@ -494,17 +479,13 @@
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];
_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);
} else {
_ref2 = this.variable.compileReference(o, {
precompile: true
});
this.first = _ref2[0];
this.meth = _ref2[1];
}), this.first = _ref2[0], this.meth = _ref2[1];
}
this.first = ("(typeof " + (this.first) + " === \"function\" ? ");
this.last = " : undefined)";
@ -646,17 +627,11 @@
});
_ref2 = this.from.compileReference(o, {
precompile: true
});
this.from = _ref2[0];
this.fromVar = _ref2[1];
}), this.from = _ref2[0], this.fromVar = _ref2[1];
_ref2 = this.to.compileReference(o, {
precompile: true
});
this.to = _ref2[0];
this.toVar = _ref2[1];
_ref2 = [this.fromVar.match(SIMPLENUM), this.toVar.match(SIMPLENUM)];
this.fromNum = _ref2[0];
this.toNum = _ref2[1];
}), this.to = _ref2[0], this.toVar = _ref2[1];
_ref2 = [this.fromVar.match(SIMPLENUM), this.toVar.match(SIMPLENUM)], this.fromNum = _ref2[0], this.toNum = _ref2[1];
parts = [];
if (this.from !== this.fromVar) {
parts.push(this.from);
@ -685,9 +660,7 @@
};
RangeNode.prototype.compileSimple = function(o) {
var _ref2, from, idx, step, to;
_ref2 = [+this.fromNum, +this.toNum];
from = _ref2[0];
to = _ref2[1];
_ref2 = [+this.fromNum, +this.toNum], from = _ref2[0], to = _ref2[1];
idx = del(o, 'index');
step = del(o, 'step');
step && (step = ("" + (idx) + " += " + (step.compile(o))));
@ -872,9 +845,7 @@
_ref2 = this.properties;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
prop = _ref2[_i];
_ref3 = [prop.variable, prop.value];
pvar = _ref3[0];
func = _ref3[1];
_ref3 = [prop.variable, prop.value], pvar = _ref3[0], func = _ref3[1];
if (pvar && pvar.base.value === 'constructor' && func instanceof CodeNode) {
if (func.bound) {
throw new Error("cannot define a constructor as a bound function.");
@ -937,28 +908,20 @@
AssignNode.prototype.isValue = function() {
return this.variable instanceof ValueNode;
};
AssignNode.prototype.makeReturn = function() {
if (this.isStatement()) {
return new Expressions([this, new ReturnNode(this.variable)]);
} else {
return AssignNode.__super__.makeReturn.call(this);
}
};
AssignNode.prototype.isStatement = function() {
return this.isValue() && (this.variable.isArray() || this.variable.isObject());
};
AssignNode.prototype.compileNode = function(o) {
var end, match, name, proto, stmt, top, val;
var end, isValue, match, name, proto, stmt, top, val;
if (isValue = this.isValue()) {
if (this.variable.isArray() || this.variable.isObject()) {
return this.compilePatternMatch(o);
}
if (this.variable.isSplice()) {
return this.compileSplice(o);
}
}
top = del(o, 'top');
if (this.isStatement(o)) {
return this.compilePatternMatch(o);
}
if (this.isValue() && this.variable.isSplice()) {
return this.compileSplice(o);
}
stmt = del(o, 'asStatement');
name = this.variable.compile(o);
end = this.isValue() ? this.variable.last.replace(this.LEADING_DOT, '') : name;
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) {
@ -973,7 +936,7 @@
if (this.context === 'object') {
return ("" + (name) + ": " + (val));
}
if (!(this.isValue() && (this.variable.hasProperties() || this.variable.namespaced))) {
if (!(isValue && (this.variable.hasProperties() || this.variable.namespaced))) {
o.scope.find(name);
}
val = ("" + (name) + " = " + (val));
@ -983,22 +946,41 @@
return top || this.parenthetical ? val : ("(" + (val) + ")");
};
AssignNode.prototype.compilePatternMatch = function(o) {
var _len, _ref2, _ref3, accessClass, assigns, code, i, idx, isString, obj, oindex, olength, splat, val, valVar, value;
var _len, _ref2, _ref3, accessClass, assigns, code, i, idx, isObject, obj, objects, oindex, olength, otop, splat, top, val, valVar, value;
if ((value = this.value).isStatement(o)) {
value = ClosureNode.wrap(value);
}
objects = this.variable.base.objects;
if (!(objects.length)) {
return value.compile(o);
}
if ((isObject = this.variable.isObject()) && objects.length === 1) {
if ((obj = objects[0]) instanceof AssignNode) {
_ref2 = obj, idx = _ref2.variable.base, obj = _ref2.value;
} else {
idx = obj;
}
if (!(value instanceof ValueNode)) {
value = new ValueNode(value);
}
accessClass = IDENTIFIER.test(idx.value) ? AccessorNode : IndexNode;
value.properties.push(new accessClass(idx));
return new AssignNode(obj, value).compile(o);
}
top = del(o, 'top');
otop = merge(o, {
top: true
});
valVar = o.scope.freeVariable('ref');
value = this.value.isStatement(o) ? ClosureNode.wrap(this.value) : this.value;
assigns = [("" + (this.tab) + (valVar) + " = " + (value.compile(o)) + ";")];
o.top = true;
o.asStatement = true;
assigns = [("" + (valVar) + " = " + (value.compile(o)))];
splat = false;
_ref2 = this.variable.base.objects;
_ref2 = objects;
for (i = 0, _len = _ref2.length; i < _len; i++) {
obj = _ref2[i];
idx = i;
if (this.variable.isObject()) {
if (isObject) {
if (obj instanceof AssignNode) {
_ref3 = [obj.value, obj.variable.base];
obj = _ref3[0];
idx = _ref3[1];
_ref3 = [obj.value, obj.variable.base], obj = _ref3[0], idx = _ref3[1];
} else {
idx = obj;
}
@ -1006,10 +988,9 @@
if (!(obj instanceof ValueNode || obj instanceof SplatNode)) {
throw new Error('pattern matching must use only identifiers on the left-hand side.');
}
isString = idx.value && idx.value.match(IS_STRING);
accessClass = isString || this.variable.isArray() ? IndexNode : AccessorNode;
if (obj instanceof SplatNode && !splat) {
val = literal(obj.compileValue(o, valVar, oindex = indexOf(this.variable.base.objects, obj), (olength = this.variable.base.objects.length) - oindex - 1));
accessClass = isObject && IDENTIFIER.test(idx.value) ? AccessorNode : IndexNode;
if (!splat && obj instanceof SplatNode) {
val = literal(obj.compileValue(o, valVar, oindex = indexOf(objects, obj), (olength = objects.length) - oindex - 1));
splat = true;
} else {
if (typeof idx !== 'object') {
@ -1017,10 +998,13 @@
}
val = new ValueNode(literal(valVar), [new accessClass(idx)]);
}
assigns.push(new AssignNode(obj, val).compile(o));
assigns.push(new AssignNode(obj, val).compile(otop));
}
code = assigns.join("\n");
return code;
if (!(top)) {
assigns.push(valVar);
}
code = assigns.join(', ');
return top || this.parenthetical ? code : ("(" + (code) + ")");
};
AssignNode.prototype.compileSplice = function(o) {
var from, l, name, plus, range, to, val;
@ -1076,11 +1060,8 @@
splat.trailings.push(param);
} else {
if (param.attach) {
_ref3 = param;
value = _ref3.value;
_ref3 = [literal(o.scope.freeVariable('arg')), param.splat];
param = _ref3[0];
param.splat = _ref3[1];
value = param.value;
_ref3 = [literal(o.scope.freeVariable('arg')), param.splat], param = _ref3[0], param.splat = _ref3[1];
this.body.unshift(new AssignNode(new ValueNode(literal('this'), [new AccessorNode(value)]), param));
}
if (param.splat) {
@ -1117,9 +1098,7 @@
}
return top ? ("(" + (func) + ")") : func;
};
CodeNode.prototype.topSensitive = function() {
return true;
};
CodeNode.prototype.topSensitive = YES;
CodeNode.prototype.traverseChildren = function(crossScope, func) {
return crossScope ? CodeNode.__super__.traverseChildren.call(this, crossScope, func) : null;
};
@ -1141,9 +1120,8 @@
return this.value.compile(o);
};
ParamNode.prototype.toString = function() {
var _ref2, name;
_ref2 = this;
name = _ref2.name;
var name;
name = this.name;
if (this.attach) {
name = '@' + name;
}
@ -1251,9 +1229,7 @@
this.returns = true;
return this;
};
WhileNode.prototype.topSensitive = function() {
return true;
};
WhileNode.prototype.topSensitive = YES;
WhileNode.prototype.compileNode = function(o) {
var cond, post, pre, rvar, set, top;
top = del(o, 'top') && !this.returns;
@ -1364,13 +1340,8 @@
OpNode.prototype.compileChain = function(o) {
var _ref2, first, second, shared;
shared = this.first.unwrap().second;
_ref2 = shared.compileReference(o);
this.first.second = _ref2[0];
shared = _ref2[1];
_ref2 = [this.first.compile(o), this.second.compile(o), shared.compile(o)];
first = _ref2[0];
second = _ref2[1];
shared = _ref2[2];
_ref2 = shared.compileReference(o), this.first.second = _ref2[0], shared = _ref2[1];
_ref2 = [this.first.compile(o), this.second.compile(o), shared.compile(o)], first = _ref2[0], second = _ref2[1], shared = _ref2[2];
return "(" + (first) + ") && (" + (shared) + " " + (this.operator) + " " + (second) + ")";
};
OpNode.prototype.compileAssignment = function(o) {
@ -1378,9 +1349,7 @@
_ref2 = this.first.compileReference(o, {
precompile: true,
assignment: true
});
first = _ref2[0];
firstVar = _ref2[1];
}), first = _ref2[0], firstVar = _ref2[1];
second = this.second.compile(o);
if (this.second instanceof OpNode) {
second = ("(" + (second) + ")");
@ -1395,9 +1364,7 @@
};
OpNode.prototype.compileExistence = function(o) {
var _ref2, ref, test;
_ref2 = ExistenceNode.compileTest(o, this.first);
test = _ref2[0];
ref = _ref2[1];
_ref2 = ExistenceNode.compileTest(o, this.first), test = _ref2[0], ref = _ref2[1];
return "" + (test) + " ? " + (ref) + " : " + (this.second.compile(o));
};
OpNode.prototype.compileUnary = function(o) {
@ -1428,9 +1395,7 @@
var _ref2;
_ref2 = this.object.compileReference(o, {
precompile: true
});
this.obj1 = _ref2[0];
this.obj2 = _ref2[1];
}), this.obj1 = _ref2[0], this.obj2 = _ref2[1];
return this.isArray() ? this.compileOrTest(o) : this.compileLoopTest(o);
};
InNode.prototype.compileOrTest = function(o) {
@ -1449,12 +1414,8 @@
var _ref2, i, l, prefix;
_ref2 = this.array.compileReference(o, {
precompile: true
});
this.arr1 = _ref2[0];
this.arr2 = _ref2[1];
_ref2 = [o.scope.freeVariable('i'), o.scope.freeVariable('len')];
i = _ref2[0];
l = _ref2[1];
}), this.arr1 = _ref2[0], this.arr2 = _ref2[1];
_ref2 = [o.scope.freeVariable('i'), o.scope.freeVariable('len')], i = _ref2[0], l = _ref2[1];
prefix = this.obj1 !== this.obj2 ? this.obj1 + '; ' : '';
return "(function(){ " + (prefix) + "for (var " + (i) + "=0, " + (l) + "=" + (this.arr1) + ".length; " + (i) + "<" + (l) + "; " + (i) + "++) { if (" + (this.arr2) + "[" + (i) + "] === " + (this.obj2) + ") return true; } return false; }).call(this)";
};
@ -1530,9 +1491,7 @@
var _ref2, first, second;
_ref2 = variable.compileReference(o, {
precompile: true
});
first = _ref2[0];
second = _ref2[1];
}), first = _ref2[0], second = _ref2[1];
return [("(typeof " + (first) + " !== \"undefined\" && " + (second) + " !== null)"), second];
};
return ExistenceNode;
@ -1585,9 +1544,7 @@
this.raw = !!source.raw;
this.object = !!source.object;
if (this.object) {
_ref2 = [this.index, this.name];
this.name = _ref2[0];
this.index = _ref2[1];
_ref2 = [this.index, this.name], this.name = _ref2[0], this.index = _ref2[1];
}
this.pattern = this.name instanceof ValueNode;
if (this.index instanceof ValueNode) {
@ -1743,9 +1700,7 @@
_ref2 = this.cases;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
pair = _ref2[_i];
_ref3 = pair;
conditions = _ref3[0];
block = _ref3[1];
_ref3 = pair, conditions = _ref3[0], block = _ref3[1];
exprs = block.expressions;
_ref3 = flatten([conditions]);
for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) {

View File

@ -1,8 +1,7 @@
(function() {
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, INVERSES, LINEBREAKS, Rewriter, SINGLE_CLOSERS, SINGLE_LINERS, _i, _len, _ref, _result, include, pair;
var __hasProp = Object.prototype.hasOwnProperty;
_ref = require('./helpers');
include = _ref.include;
include = require('./helpers').include;
exports.Rewriter = (function() {
Rewriter = function() {};
Rewriter.prototype.rewrite = function(tokens) {
@ -55,25 +54,21 @@
};
Rewriter.prototype.adjustComments = function() {
return this.scanTokens(function(token, i) {
var _ref2, after, before, post, prev;
var _ref, after, before, post, prev;
if (token[0] !== 'HERECOMMENT') {
return 1;
}
_ref2 = [this.tokens[i - 2], this.tokens[i - 1], this.tokens[i + 1], this.tokens[i + 2]];
before = _ref2[0];
prev = _ref2[1];
post = _ref2[2];
after = _ref2[3];
_ref = [this.tokens[i - 2], this.tokens[i - 1], this.tokens[i + 1], this.tokens[i + 2]], before = _ref[0], prev = _ref[1], post = _ref[2], after = _ref[3];
if (after && after[0] === 'INDENT') {
this.tokens.splice(i + 2, 1);
if (before && before[0] === 'OUTDENT' && post && (prev[0] === (_ref2 = post[0])) && (_ref2 === 'TERMINATOR')) {
if (before && before[0] === 'OUTDENT' && post && (prev[0] === (_ref = post[0])) && (_ref === 'TERMINATOR')) {
this.tokens.splice(i - 2, 1);
} else {
this.tokens.splice(i, 0, after);
}
} else if (prev && !('TERMINATOR' === (_ref2 = prev[0]) || 'INDENT' === _ref2 || 'OUTDENT' === _ref2)) {
} else if (prev && !('TERMINATOR' === (_ref = prev[0]) || 'INDENT' === _ref || 'OUTDENT' === _ref)) {
if (post && post[0] === 'TERMINATOR' && after && after[0] === 'OUTDENT') {
(_ref2 = this.tokens).splice.apply(_ref2, [i + 2, 0].concat(this.tokens.splice(i, 2)));
(_ref = this.tokens).splice.apply(_ref, [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]]);
}
@ -107,8 +102,8 @@
var action, condition;
if (token[0] === 'CALL_START') {
condition = function(token, i) {
var _ref2;
return ((')' === (_ref2 = token[0]) || 'CALL_END' === _ref2)) || (token[0] === 'OUTDENT' && this.tokens[i - 1][0] === ')');
var _ref;
return ((')' === (_ref = token[0]) || 'CALL_END' === _ref)) || (token[0] === 'OUTDENT' && this.tokens[i - 1][0] === ')');
};
action = function(token, i) {
var idx;
@ -125,8 +120,8 @@
var action, condition;
if (token[0] === 'INDEX_START') {
condition = function(token, i) {
var _ref2;
return (']' === (_ref2 = token[0]) || 'INDEX_END' === _ref2);
var _ref;
return (']' === (_ref = token[0]) || 'INDEX_END' === _ref);
};
action = function(token, i) {
return (token[0] = 'INDEX_END');
@ -158,15 +153,12 @@
tok.generated = true;
this.tokens.splice(idx, 0, tok);
condition = function(token, i) {
var _ref2, one, three, two;
_ref2 = this.tokens.slice(i + 1, i + 4);
one = _ref2[0];
two = _ref2[1];
three = _ref2[2];
var _ref, one, three, two;
_ref = this.tokens.slice(i + 1, i + 4), one = _ref[0], two = _ref[1], three = _ref[2];
if ((this.tag(i + 1) === 'HERECOMMENT' || this.tag(i - 1) === 'HERECOMMENT')) {
return false;
}
return ((('TERMINATOR' === (_ref2 = token[0]) || 'OUTDENT' === _ref2)) && !((two && two[0] === ':') || (one && one[0] === '@' && three && three[0] === ':'))) || (token[0] === ',' && one && (!('IDENTIFIER' === (_ref2 = one[0]) || 'STRING' === _ref2 || '@' === _ref2 || 'TERMINATOR' === _ref2 || 'OUTDENT' === _ref2)));
return ((('TERMINATOR' === (_ref = token[0]) || 'OUTDENT' === _ref)) && !((two && two[0] === ':') || (one && one[0] === '@' && three && three[0] === ':'))) || (token[0] === ',' && one && (!('IDENTIFIER' === (_ref = one[0]) || 'STRING' === _ref || '@' === _ref || 'TERMINATOR' === _ref || 'OUTDENT' === _ref)));
};
action = function(token, i) {
return this.tokens.splice(i, 0, ['}', '}', token[2]]);
@ -181,7 +173,7 @@
var classLine;
classLine = false;
return this.scanTokens(function(token, i) {
var _ref2, action, callObject, condition, idx, next, prev, seenSingle;
var _ref, action, callObject, condition, idx, next, prev, seenSingle;
if (token[0] === 'CLASS') {
classLine = true;
}
@ -199,14 +191,14 @@
if (prev && !prev.spaced && token[0] === '?') {
token.call = true;
}
if (prev && (prev.spaced && (include(IMPLICIT_FUNC, prev[0]) || prev.call) && include(IMPLICIT_CALL, token[0]) && !(token[0] === 'UNARY' && (('IN' === (_ref2 = this.tag(i + 1)) || 'OF' === _ref2 || 'INSTANCEOF' === _ref2)))) || callObject) {
if (prev && (prev.spaced && (include(IMPLICIT_FUNC, prev[0]) || prev.call) && include(IMPLICIT_CALL, token[0]) && !(token[0] === 'UNARY' && (('IN' === (_ref = this.tag(i + 1)) || 'OF' === _ref || 'INSTANCEOF' === _ref)))) || callObject) {
this.tokens.splice(i, 0, ['CALL_START', '(', token[2]]);
condition = function(token, i) {
var _ref2, post;
var _ref, post;
if (!seenSingle && token.fromThen) {
return true;
}
if (('IF' === (_ref2 = token[0]) || 'ELSE' === _ref2 || 'UNLESS' === _ref2 || '->' === _ref2 || '=>' === _ref2)) {
if (('IF' === (_ref = token[0]) || 'ELSE' === _ref || 'UNLESS' === _ref || '->' === _ref || '=>' === _ref)) {
seenSingle = true;
}
post = this.tokens[i + 1];
@ -227,20 +219,18 @@
};
Rewriter.prototype.addImplicitIndentation = function() {
return this.scanTokens(function(token, i) {
var _ref2, action, condition, indent, outdent, starter;
var _ref, action, condition, indent, outdent, starter;
if (token[0] === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
(_ref2 = this.tokens).splice.apply(_ref2, [i, 0].concat(this.indentation(token)));
(_ref = this.tokens).splice.apply(_ref, [i, 0].concat(this.indentation(token)));
return 2;
}
if (token[0] === 'CATCH' && (this.tag(i + 2) === 'TERMINATOR' || this.tag(i + 2) === 'FINALLY')) {
(_ref2 = this.tokens).splice.apply(_ref2, [i + 2, 0].concat(this.indentation(token)));
(_ref = this.tokens).splice.apply(_ref, [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')) {
starter = token[0];
_ref2 = this.indentation(token);
indent = _ref2[0];
outdent = _ref2[1];
_ref = this.indentation(token), indent = _ref[0], outdent = _ref[1];
if (starter === 'THEN') {
indent.fromThen = true;
}
@ -265,12 +255,12 @@
};
Rewriter.prototype.tagPostfixConditionals = function() {
return this.scanTokens(function(token, i) {
var _ref2, action, condition, original;
if (('IF' === (_ref2 = token[0]) || 'UNLESS' === _ref2)) {
var _ref, action, condition, original;
if (('IF' === (_ref = token[0]) || 'UNLESS' === _ref)) {
original = token;
condition = function(token, i) {
var _ref2;
return ('TERMINATOR' === (_ref2 = token[0]) || 'INDENT' === _ref2);
var _ref;
return ('TERMINATOR' === (_ref = token[0]) || 'INDENT' === _ref);
};
action = function(token, i) {
return token[0] !== 'INDENT' ? (original[0] = 'POST_' + original[0]) : null;
@ -282,17 +272,15 @@
});
};
Rewriter.prototype.ensureBalance = function(pairs) {
var _ref2, _result, key, levels, line, open, openLine, unclosed, value;
var _ref, _result, key, levels, line, open, openLine, unclosed, value;
levels = {};
openLine = {};
this.scanTokens(function(token, i) {
var _i, _len, _ref2, _ref3, close, open, pair;
_ref2 = pairs;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
pair = _ref2[_i];
_ref3 = pair;
open = _ref3[0];
close = _ref3[1];
var _i, _len, _ref, _ref2, close, open, pair;
_ref = pairs;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
pair = _ref[_i];
_ref2 = pair, open = _ref2[0], close = _ref2[1];
levels[open] || (levels[open] = 0);
if (token[0] === open) {
if (levels[open] === 0) {
@ -310,10 +298,10 @@
return 1;
});
unclosed = (function() {
_result = []; _ref2 = levels;
for (key in _ref2) {
if (!__hasProp.call(_ref2, key)) continue;
value = _ref2[key];
_result = []; _ref = levels;
for (key in _ref) {
if (!__hasProp.call(_ref, key)) continue;
value = _ref[key];
if (value > 0) {
_result.push(key);
}
@ -327,17 +315,17 @@
}
};
Rewriter.prototype.rewriteClosingParens = function() {
var _ref2, debt, key, stack, val;
var _ref, debt, key, stack, val;
stack = [];
debt = {};
_ref2 = INVERSES;
for (key in _ref2) {
if (!__hasProp.call(_ref2, key)) continue;
val = _ref2[key];
_ref = INVERSES;
for (key in _ref) {
if (!__hasProp.call(_ref, key)) continue;
val = _ref[key];
(debt[key] = 0);
}
return this.scanTokens(function(token, i) {
var _ref3, inv, match, mtag, oppos, tag;
var _ref2, inv, match, mtag, oppos, tag;
tag = token[0];
inv = INVERSES[token[0]];
if (include(EXPRESSION_START, tag)) {
@ -357,7 +345,7 @@
}
debt[mtag] += 1;
val = [oppos, mtag === 'INDENT' ? match[1] : oppos];
if ((this.tokens[(_ref3 = i + 2)] == null ? undefined : this.tokens[_ref3][0]) === mtag) {
if ((this.tokens[(_ref2 = i + 2)] == null ? undefined : this.tokens[_ref2][0]) === mtag) {
this.tokens.splice(i + 3, 0, val);
stack.push(match);
} else {

View File

@ -1,15 +1,11 @@
(function() {
var Scope, _ref, extend;
var Scope, extend;
var __hasProp = Object.prototype.hasOwnProperty;
_ref = require('./helpers');
extend = _ref.extend;
extend = require('./helpers').extend;
exports.Scope = (function() {
Scope = function(parent, expressions, method) {
var _ref2;
_ref2 = [parent, expressions, method];
this.parent = _ref2[0];
this.expressions = _ref2[1];
this.method = _ref2[2];
var _ref;
_ref = [parent, expressions, method], this.parent = _ref[0], this.expressions = _ref[1], this.method = _ref[2];
this.variables = {};
if (this.parent) {
this.garbage = this.parent.garbage;
@ -24,10 +20,10 @@
return this.garbage.push([]);
};
Scope.prototype.endLevel = function() {
var _i, _len, _ref2, _result, name;
_result = []; _ref2 = this.garbage.pop();
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
name = _ref2[_i];
var _i, _len, _ref, _result, name;
_result = []; _ref = this.garbage.pop();
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
name = _ref[_i];
if (this.variables[name] === 'var') {
_result.push(this.variables[name] = 'reuse');
}
@ -42,11 +38,11 @@
return false;
};
Scope.prototype.any = function(fn) {
var _ref2, k, v;
_ref2 = this.variables;
for (v in _ref2) {
if (!__hasProp.call(_ref2, v)) continue;
k = _ref2[v];
var _ref, k, v;
_ref = this.variables;
for (v in _ref) {
if (!__hasProp.call(_ref, v)) continue;
k = _ref[v];
if (fn(v, k)) {
return true;
}
@ -96,12 +92,12 @@
});
};
Scope.prototype.declaredVariables = function() {
var _ref2, _result, key, val;
var _ref, _result, key, val;
return (function() {
_result = []; _ref2 = this.variables;
for (key in _ref2) {
if (!__hasProp.call(_ref2, key)) continue;
val = _ref2[key];
_result = []; _ref = this.variables;
for (key in _ref) {
if (!__hasProp.call(_ref, key)) continue;
val = _ref[key];
if (val === 'var' || val === 'reuse') {
_result.push(key);
}
@ -110,11 +106,11 @@
}).call(this).sort();
};
Scope.prototype.assignedVariables = function() {
var _ref2, _result, key, val;
_result = []; _ref2 = this.variables;
for (key in _ref2) {
if (!__hasProp.call(_ref2, key)) continue;
val = _ref2[key];
var _ref, _result, key, val;
_result = []; _ref = this.variables;
for (key in _ref) {
if (!__hasProp.call(_ref, key)) continue;
val = _ref[key];
if (val.assigned) {
_result.push("" + (key) + " = " + (val.value));
}

View File

@ -247,7 +247,7 @@ exports.LiteralNode = class LiteralNode extends BaseNode
# Break and continue must be treated as pure statements -- they lose their
# meaning when wrapped in a closure.
isStatement: ->
@value is 'break' or @value is 'continue' or @value is 'debugger'
@value in ['break', 'continue', 'debugger']
isPureStatement: LiteralNode::isStatement
isComplex: NO
@ -822,26 +822,18 @@ exports.AssignNode = class AssignNode extends BaseNode
isValue: ->
@variable instanceof ValueNode
makeReturn: ->
if @isStatement()
return new Expressions [this, new ReturnNode(@variable)]
else
super()
isStatement: ->
@isValue() and (@variable.isArray() or @variable.isObject())
# Compile an assignment, delegating to `compilePatternMatch` or
# `compileSplice` if appropriate. Keep track of the name of the base object
# we've been assigned to, for correct internal references. If the variable
# has not been seen yet within the current scope, declare it.
compileNode: (o) ->
if isValue = @isValue()
return @compilePatternMatch(o) if @variable.isArray() or @variable.isObject()
return @compileSplice(o) if @variable.isSplice()
top = del o, 'top'
return @compilePatternMatch(o) if @isStatement(o)
return @compileSplice(o) if @isValue() and @variable.isSplice()
stmt = del o, 'asStatement'
name = @variable.compile(o)
end = if @isValue() then @variable.last.replace(@LEADING_DOT, '') else name
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
@ -849,7 +841,7 @@ exports.AssignNode = class AssignNode extends BaseNode
@value.proto = proto if proto
val = @value.compile o
return "#{name}: #{val}" if @context is 'object'
o.scope.find name unless @isValue() and (@variable.hasProperties() or @variable.namespaced)
o.scope.find name unless isValue and (@variable.hasProperties() or @variable.namespaced)
val = "#{name} = #{val}"
return "#{@tab}#{val};" if stmt
if top or @parenthetical then val else "(#{val})"
@ -859,37 +851,49 @@ exports.AssignNode = class AssignNode extends BaseNode
# See the [ECMAScript Harmony Wiki](http://wiki.ecmascript.org/doku.php?id=harmony:destructuring)
# for details.
compilePatternMatch: (o) ->
valVar = o.scope.freeVariable 'ref'
value = if @value.isStatement(o) then ClosureNode.wrap(@value) else @value
assigns = ["#{@tab}#{valVar} = #{ value.compile(o) };"]
o.top = true
o.asStatement = true
splat = false
for obj, i in @variable.base.objects
if (value = @value).isStatement o then value = ClosureNode.wrap value
{objects} = @variable.base
return value.compile o unless objects.length
if (isObject = @variable.isObject()) and objects.length is 1
# Unroll simplest cases: `{v} = x` -> `v = x.v`
if (obj = objects[0]) instanceof AssignNode
{variable: {base: idx}, value: obj} = obj
else
idx = obj
value = new ValueNode value unless value instanceof ValueNode
accessClass = if IDENTIFIER.test idx.value then AccessorNode else IndexNode
value.properties.push new accessClass idx
return new AssignNode(obj, value).compile o
top = del o, 'top'
otop = merge o, top: yes
valVar = o.scope.freeVariable 'ref'
assigns = ["#{valVar} = #{ value.compile o }"]
splat = false
for obj, i in objects
# A regular array pattern-match.
idx = i
if @variable.isObject()
if isObject
if obj instanceof AssignNode
# A regular object pattern-match.
[obj, idx] = [obj.value, obj.variable.base]
else
# A shorthand `{a, b, c} = val` pattern-match.
idx = obj
if not (obj instanceof ValueNode or obj instanceof SplatNode)
unless obj instanceof ValueNode or obj instanceof SplatNode
throw new Error 'pattern matching must use only identifiers on the left-hand side.'
isString = idx.value and idx.value.match IS_STRING
accessClass = if isString or @variable.isArray() then IndexNode else AccessorNode
if obj instanceof SplatNode and not splat
accessClass = if isObject and IDENTIFIER.test(idx.value) then AccessorNode else IndexNode
if not splat and obj instanceof SplatNode
val = literal obj.compileValue o, valVar,
(oindex = indexOf(@variable.base.objects, obj)),
(olength = @variable.base.objects.length) - oindex - 1
(oindex = indexOf objects, obj),
(olength = objects.length) - oindex - 1
splat = true
else
idx = literal(if splat then "#{valVar}.length - #{olength - idx}" else idx) if typeof idx isnt 'object'
val = new ValueNode(literal(valVar), [new accessClass(idx)])
assigns.push(new AssignNode(obj, val).compile(o))
code = assigns.join("\n")
code
val = new ValueNode literal(valVar), [new accessClass idx]
assigns.push new AssignNode(obj, val).compile otop
assigns.push valVar unless top
code = assigns.join ', '
if top or @parenthetical then code else "(#{code})"
# Compile the assignment from an array splice literal, using JavaScript's
# `Array#splice` method.
@ -963,8 +967,7 @@ exports.CodeNode = class CodeNode extends BaseNode
return "(#{utility 'bind'}(#{func}, #{@context}))" if @bound
if top then "(#{func})" else func
topSensitive: ->
true
topSensitive: YES
# Short-circuit traverseChildren method to prevent it from crossing scope boundaries
# unless crossScope is true
@ -1083,8 +1086,7 @@ exports.WhileNode = class WhileNode extends BaseNode
@returns = true
this
topSensitive: ->
true
topSensitive: YES
# The main difference from a JavaScript *while* is that the CoffeeScript
# *while* can be used as a part of a larger expression -- while loops may

View File

@ -4,30 +4,22 @@ b = -2
[a, b] = [b, a]
ok a is -2
ok b is -1
eq a, -2
eq b, -1
func = ->
[a, b] = [b, a]
ok func().join(' ') is '-1 -2'
eq func().join(' '), '-1 -2'
eq a, -1
eq b, -2
noop = ->
noop [a,b] = [c,d] = [1,2]
ok a is 1 and b is 2
#713
eq (onetwo = [1, 2]), [a, b] = [c, d] = onetwo
ok a is c is 1 and b is d is 2
# Array destructuring, including splats.
arr = [1, 2, 3]
[a, b, c] = arr
ok a is 1
ok b is 2
ok c is 3
[x,y...,z] = [1,2,3,4,5]
ok x is 1