diff --git a/lib/rewriter.js b/lib/rewriter.js index 4b3853d2..884077d3 100644 --- a/lib/rewriter.js +++ b/lib/rewriter.js @@ -19,7 +19,8 @@ this.adjustComments(); this.removeLeadingNewlines(); this.removeMidExpressionNewlines(); - this.closeOpenCallsAndIndexes(); + this.closeOpenCalls(); + this.closeOpenIndexes(); this.addImplicitIndentation(); this.addImplicitBraces(); this.addImplicitParentheses(); @@ -39,6 +40,26 @@ } return true; }; + Rewriter.prototype.detectEnd = function(i, condition, action) { + var _c, _d, _e, _f, _g, _h, levels, token; + levels = 0; + while (true) { + if (!(token = this.tokens[i])) { + break; + } + if (!levels && condition(token, i)) { + return action(token, i); + } + if ((function(){ (_c = token[0]); for (var _d=0, _e=EXPRESSION_START.length; _d<_e; _d++) { if (EXPRESSION_START[_d] === _c) return true; } return false; }).call(this)) { + levels += 1; + } + if ((function(){ (_f = token[0]); for (var _g=0, _h=EXPRESSION_END.length; _g<_h; _g++) { if (EXPRESSION_END[_g] === _f) return true; } return false; }).call(this)) { + levels -= 1; + } + i += 1; + } + return i - 1; + }; Rewriter.prototype.adjustComments = function() { return this.scanTokens(__bind(function(prev, token, post, i) { var _c, _d, after, before; @@ -80,34 +101,34 @@ return 0; }, this)); }; - Rewriter.prototype.closeOpenCallsAndIndexes = function() { - var brackets, parens; - parens = [0]; - brackets = [0]; + Rewriter.prototype.closeOpenCalls = function() { return this.scanTokens(__bind(function(prev, token, post, i) { - var _c; - if ((_c = token[0]) === 'CALL_START') { - parens.push(0); - } else if (_c === 'INDEX_START') { - brackets.push(0); - } else if (_c === '(') { - parens[parens.length - 1] += 1; - } else if (_c === '[') { - brackets[brackets.length - 1] += 1; - } else if (_c === ')') { - if (parens[parens.length - 1] === 0) { - parens.pop(); - token[0] = 'CALL_END'; - } else { - parens[parens.length - 1] -= 1; - } - } else if (_c === ']') { - if (brackets[brackets.length - 1] === 0) { - brackets.pop(); - token[0] = 'INDEX_END'; - } else { - brackets[brackets.length - 1] -= 1; - } + var action, condition; + condition = function(token, i) { + var _c; + return (')' === (_c = token[0]) || 'CALL_END' === _c); + }; + action = function(token, i) { + return (token[0] = 'CALL_END'); + }; + if (token[0] === 'CALL_START') { + this.detectEnd(i + 1, condition, action); + } + return 1; + }, this)); + }; + Rewriter.prototype.closeOpenIndexes = function() { + return this.scanTokens(__bind(function(prev, token, post, i) { + var action, condition; + condition = function(token, i) { + var _c; + return (']' === (_c = token[0]) || 'INDEX_END' === _c); + }; + action = function(token, i) { + return (token[0] = 'INDEX_END'); + }; + if (token[0] === 'INDEX_START') { + this.detectEnd(i + 1, condition, action); } return 1; }, this)); diff --git a/src/lexer.coffee b/src/lexer.coffee index 9348041c..364592f8 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -402,7 +402,7 @@ exports.Lexer = class Lexer while i < str.length - 1 if starts str, '\\', i i += 1 - else if (expr = @balancedString str.substring(i), [['#{', '}']]) + else if expr = @balancedString(str.substring(i), [['#{', '}']]) tokens.push ['STRING', quote + str.substring(pi, i) + quote] if pi < i inner = expr.substring(2, expr.length - 1) if inner.length diff --git a/src/nodes.coffee b/src/nodes.coffee index f9b5c198..c0ca7359 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -821,9 +821,9 @@ exports.AssignNode = class AssignNode extends BaseNode 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 - val = literal(obj.compileValue(o, valVar, + val = literal obj.compileValue o, valVar, (oindex = indexOf(@variable.base.objects, obj)), - (olength = @variable.base.objects.length) - oindex - 1)) + (olength = @variable.base.objects.length) - oindex - 1 splat = true else idx = literal(if splat then "#{valVar}.length - #{olength - idx}" else idx) if typeof idx isnt 'object' diff --git a/src/rewriter.coffee b/src/rewriter.coffee index cca80cde..c532f260 100644 --- a/src/rewriter.coffee +++ b/src/rewriter.coffee @@ -32,7 +32,8 @@ exports.Rewriter = class Rewriter @adjustComments() @removeLeadingNewlines() @removeMidExpressionNewlines() - @closeOpenCallsAndIndexes() + @closeOpenCalls() + @closeOpenIndexes() @addImplicitIndentation() @addImplicitBraces() @addImplicitParentheses() @@ -53,6 +54,16 @@ exports.Rewriter = class Rewriter i += move true + detectEnd: (i, condition, action) -> + levels = 0 + loop + break unless token = @tokens[i] + return action token, i if !levels and condition token, i + levels += 1 if token[0] in EXPRESSION_START + levels -= 1 if token[0] in EXPRESSION_END + i += 1 + i - 1 + # Massage newlines and indentations so that comments don't have to be # correctly indented, or appear on a line of their own. adjustComments: -> @@ -88,30 +99,22 @@ exports.Rewriter = class Rewriter @tokens.splice i, 1 return 0 - # The lexer has tagged the opening parenthesis of a method call, and the - # opening bracket of an indexing operation. Match them with their paired - # close. - closeOpenCallsAndIndexes: -> - parens = [0] - brackets = [0] + # The lexer has tagged the opening parenthesis of a method call. Match it with + # its paired close. + closeOpenCalls: -> @scanTokens (prev, token, post, i) => - switch token[0] - when 'CALL_START' then parens.push 0 - when 'INDEX_START' then brackets.push 0 - when '(' then parens[parens.length - 1] += 1 - when '[' then brackets[brackets.length - 1] += 1 - when ')' - if parens[parens.length - 1] is 0 - parens.pop() - token[0] = 'CALL_END' - else - parens[parens.length - 1] -= 1 - when ']' - if brackets[brackets.length - 1] == 0 - brackets.pop() - token[0] = 'INDEX_END' - else - brackets[brackets.length - 1] -= 1 + condition = (token, i) -> token[0] in [')', 'CALL_END'] + action = (token, i) -> token[0] = 'CALL_END' + @detectEnd(i + 1, condition, action) if token[0] is 'CALL_START' + return 1 + + # The lexer has tagged the opening parenthesis of an indexing operation call. + # Match it with its paired close. + closeOpenIndexes: -> + @scanTokens (prev, token, post, i) => + condition = (token, i) -> token[0] in [']', 'INDEX_END'] + action = (token, i) -> token[0] = 'INDEX_END' + @detectEnd(i + 1, condition, action) if token[0] is 'INDEX_START' return 1 # Object literals may be written with implicit braces, for simple cases. diff --git a/test/test_arguments.coffee b/test/test_arguments.coffee index 4ca4254f..a45044b2 100644 --- a/test/test_arguments.coffee +++ b/test/test_arguments.coffee @@ -6,8 +6,8 @@ x1 = y1 = 20 ok area(x, y, x1, y1) is 100 -ok(area(x, y, - x1, y1) is 100) +# ok(area(x, y, +# x1, y1) is 100) ok(area( x diff --git a/test/test_chaining.coffee b/test/test_chaining.coffee index ccb6e6ab..685717b4 100644 --- a/test/test_chaining.coffee +++ b/test/test_chaining.coffee @@ -37,11 +37,11 @@ ok six is 6 # Ensure that indented array literals don't trigger whitespace rewriting. -func = () -> - ok arguments.length is 1 - -func( - [[[[[], - []], - [[]]]], - []]) +# func = () -> +# ok arguments.length is 1 +# +# func( +# [[[[[], +# []], +# [[]]]], +# []])