diff --git a/lib/cake.js b/lib/cake.js index 85e6f35b..d0c6ae5e 100755 --- a/lib/cake.js +++ b/lib/cake.js @@ -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, diff --git a/lib/coffee-script.js b/lib/coffee-script.js index 883864f0..15c795f1 100755 --- a/lib/coffee-script.js +++ b/lib/coffee-script.js @@ -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) { diff --git a/lib/command.js b/lib/command.js index 2c8684ed..215bbb7b 100644 --- a/lib/command.js +++ b/lib/command.js @@ -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) + "]"; })()); } diff --git a/lib/lexer.js b/lib/lexer.js index 7b433903..e319721f 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -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) { diff --git a/lib/nodes.js b/lib/nodes.js index 3a8796ce..9e6f643a 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -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++) { diff --git a/lib/rewriter.js b/lib/rewriter.js index 5fb9aef0..0f3e9b32 100644 --- a/lib/rewriter.js +++ b/lib/rewriter.js @@ -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 { diff --git a/lib/scope.js b/lib/scope.js index a3de4da7..9c2d6e32 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -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)); } diff --git a/src/nodes.coffee b/src/nodes.coffee index e24c578e..371a2f94 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -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 diff --git a/test/test_pattern_matching.coffee b/test/test_pattern_matching.coffee index 69389152..af37a20c 100644 --- a/test/test_pattern_matching.coffee +++ b/test/test_pattern_matching.coffee @@ -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