diff --git a/lib/nodes.js b/lib/nodes.js index 1877fb1b..2513aff0 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -37,7 +37,6 @@ return code; }; Base.prototype.compileClosure = function(o) { - this.tab = o.indent; o.sharedScope = o.scope; return Closure.wrap(this).compile(o); }; @@ -232,6 +231,7 @@ Expressions.prototype.compileExpression = function(node, o) { var compiledNode; this.tab = o.indent; + node.tags.front = true; compiledNode = node.compile(merge(o, { top: true })); @@ -364,8 +364,8 @@ Value.prototype.isStatement = function(o) { return this.base.isStatement(o) && !this.properties.length; }; - Value.prototype.isNumber = function() { - return this.base instanceof Literal && NUMBER.test(this.base.value); + Value.prototype.isSimpleNumber = function() { + return this.base instanceof Literal && SIMPLENUM.test(this.base.value); }; Value.prototype.cacheReference = function(o) { var base, bref, name, nref; @@ -389,6 +389,7 @@ return [base.push(name), new Value(bref || base.base, [nref || name])]; }; Value.prototype.compile = function(o) { + this.base.tags.front = this.tags.front; return !o.top || this.properties.length ? Value.__super__.compile.call(this, o) : this.base.compile(o); }; Value.prototype.compileNode = function(o) { @@ -401,7 +402,7 @@ this.base.parenthetical = true; } code = this.base.compile(o); - if (props[0] instanceof Accessor && this.isNumber() || o.top && this.base instanceof ObjectLiteral) { + if (props[0] instanceof Accessor && this.isSimpleNumber()) { code = ("(" + code + ")"); } for (_i = 0, _len = props.length; _i < _len; _i++) { @@ -540,16 +541,17 @@ return node; }; Call.prototype.compileNode = function(o) { - var _i, _j, _len, _len2, _ref2, _ref3, _ref4, _result, arg, args, left, node, rite, val; + var _i, _j, _len, _len2, _ref2, _ref3, _ref4, _ref5, _result, arg, args, left, node, rite, val; if (node = this.unfoldSoak(o)) { return node.compile(o); } + (((_ref2 = this.variable) != null) ? (_ref2.tags.front = this.tags.front) : undefined); if (this.exist) { if (val = this.variable) { if (!(val instanceof Value)) { val = new Value(val); } - _ref2 = val.cacheReference(o), left = _ref2[0], rite = _ref2[1]; + _ref3 = val.cacheReference(o), left = _ref3[0], rite = _ref3[1]; rite = new Call(rite, this.args); } else { left = new Literal(this.superReference(o)); @@ -560,16 +562,16 @@ rite = rite.compile(o); return ("(" + left + " ? undefined : " + rite + ")"); } - for (_i = 0, _len = (_ref3 = this.args).length; _i < _len; _i++) { - arg = _ref3[_i]; + for (_i = 0, _len = (_ref4 = this.args).length; _i < _len; _i++) { + arg = _ref4[_i]; if (arg instanceof Splat) { return this.compileSplat(o); } } args = (function() { _result = []; - for (_j = 0, _len2 = (_ref4 = this.args).length; _j < _len2; _j++) { - arg = _ref4[_j]; + for (_j = 0, _len2 = (_ref5 = this.args).length; _j < _len2; _j++) { + arg = _ref5[_j]; _result.push((arg.parenthetical = true) && arg.compile(o)); } return _result; @@ -803,7 +805,6 @@ })(); __extends(ObjectLiteral, Base); ObjectLiteral.prototype.children = ['properties']; - ObjectLiteral.prototype.topSensitive = YES; ObjectLiteral.prototype.compileNode = function(o) { var _i, _len, _ref2, _result, i, indent, join, lastNoncom, nonComments, obj, prop, props, top; top = del(o, 'top'); @@ -838,7 +839,7 @@ }).call(this); props = props.join(''); obj = ("{" + (props ? '\n' + props + '\n' + this.idt() : '') + "}"); - return top ? ("(" + obj + ")") : obj; + return this.tags.front ? ("(" + obj + ")") : obj; }; ObjectLiteral.prototype.assigns = function(name) { var _i, _len, _ref2, prop; @@ -1207,9 +1208,8 @@ if (this.bound) { return ("" + (utility('bind')) + "(" + func + ", " + (this.context) + ")"); } - return top ? ("(" + func + ")") : func; + return this.tags.front ? ("(" + func + ")") : func; }; - Code.prototype.topSensitive = YES; Code.prototype.traverseChildren = function(crossScope, func) { return crossScope ? Code.__super__.traverseChildren.call(this, crossScope, func) : undefined; }; @@ -1371,9 +1371,7 @@ exports.Op = (function() { Op = (function() { function Op(op, first, second, flip) { - if (first instanceof Value && first.base instanceof ObjectLiteral) { - first = new Parens(first); - } else if (op === 'new') { + if (op === 'new') { if (first instanceof Call) { return first.newInstance(); } @@ -1430,6 +1428,9 @@ return Op.__super__.toString.call(this, idt, this.constructor.name + ' ' + this.operator); }; Op.prototype.compileNode = function(o) { + if (this.second) { + this.first.tags.front = this.tags.front; + } if (this.isChainable() && this.first.unwrap() instanceof Op && this.first.unwrap().isChainable()) { return this.compileChain(o); } @@ -1960,7 +1961,7 @@ TRAILING_WHITESPACE = /[ \t]+$/gm; IDENTIFIER = /^[$A-Za-z_][$\w]*$/; NUMBER = /^0x[\da-f]+|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?$/i; - SIMPLENUM = /^-?\d+$/; + SIMPLENUM = /^[+-]?\d+$/; IS_STRING = /^['"]/; utility = function(name) { var ref; diff --git a/src/nodes.coffee b/src/nodes.coffee index a695122b..1ae77560 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -52,7 +52,6 @@ exports.Base = class Base # Statements converted into expressions via closure-wrapping share a scope # object with their parent closure, to preserve the expected lexical scope. compileClosure: (o) -> - @tab = o.indent o.sharedScope = o.scope Closure.wrap(this).compile o @@ -223,6 +222,7 @@ exports.Expressions = class Expressions extends Base # statement, ask the statement to do so. compileExpression: (node, o) -> @tab = o.indent + node.tags.front = true compiledNode = node.compile merge o, top: true if node.isStatement(o) then compiledNode else "#{@idt()}#{compiledNode};" @@ -345,8 +345,8 @@ exports.Value = class Value extends Base isStatement: (o) -> @base.isStatement(o) and not @properties.length - isNumber: -> - @base instanceof Literal and NUMBER.test @base.value + isSimpleNumber: -> + @base instanceof Literal and SIMPLENUM.test @base.value # A reference has base part (`this` value) and name part. # We cache them separately for compiling complex expressions. @@ -369,6 +369,7 @@ exports.Value = class Value extends Base # Override compile to unwrap the value when possible. compile: (o) -> + @base.tags.front = @tags.front if not o.top or @properties.length then super(o) else @base.compile(o) # We compile a value to JavaScript by compiling and joining each property. @@ -380,9 +381,7 @@ exports.Value = class Value extends Base props = @properties @base.parenthetical = yes if @parenthetical and not props.length code = @base.compile o - if props[0] instanceof Accessor and @isNumber() or - o.top and @base instanceof ObjectLiteral - code = "(#{code})" + code = "(#{code})" if props[0] instanceof Accessor and @isSimpleNumber() (code += prop.compile o) for prop in props return code @@ -493,6 +492,7 @@ exports.Call = class Call extends Base # Compile a vanilla function call. compileNode: (o) -> return node.compile o if node = @unfoldSoak o + @variable?.tags.front = @tags.front if @exist if val = @variable val = new Value val unless val instanceof Value @@ -701,8 +701,6 @@ exports.ObjectLiteral = class ObjectLiteral extends Base children: ['properties'] - topSensitive: YES - constructor: (props) -> super() @objects = @properties = props or [] @@ -727,7 +725,7 @@ exports.ObjectLiteral = class ObjectLiteral extends Base indent + prop.compile(o) + join props = props.join('') obj = "{#{ if props then '\n' + props + '\n' + @idt() else '' }}" - if top then "(#{obj})" else obj + if @tags.front then "(#{obj})" else obj assigns: (name) -> for prop in @properties when prop.assigns name then return yes @@ -1024,9 +1022,7 @@ exports.Code = class Code extends Base func = "#{open}#{ params.join(', ') }) {#{code}#{close}" o.scope.endLevel() return "#{utility 'bind'}(#{func}, #{@context})" if @bound - if top then "(#{func})" else func - - topSensitive: YES + if @tags.front then "(#{func})" else func # Short-circuit traverseChildren method to prevent it from crossing scope boundaries # unless crossScope is true @@ -1194,9 +1190,7 @@ exports.Op = class Op extends Base children: ['first', 'second'] constructor: (op, first, second, flip) -> - if first instanceof Value and first.base instanceof ObjectLiteral - first = new Parens first - else if op is 'new' + if op is 'new' return first.newInstance() if first instanceof Call first = new Parens first if first instanceof Code and first.bound super() @@ -1229,6 +1223,7 @@ exports.Op = class Op extends Base super(idt, @constructor.name + ' ' + @operator) compileNode: (o) -> + @first.tags.front = @tags.front if @second return @compileChain(o) if @isChainable() and @first.unwrap() instanceof Op and @first.unwrap().isChainable() return @compileAssignment(o) if include @ASSIGNMENT, @operator return @compileUnary(o) if @isUnary() @@ -1688,7 +1683,7 @@ TRAILING_WHITESPACE = /[ \t]+$/gm IDENTIFIER = /^[$A-Za-z_][$\w]*$/ NUMBER = /^0x[\da-f]+|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?$/i -SIMPLENUM = /^-?\d+$/ +SIMPLENUM = /^[+-]?\d+$/ # Is a literal value a string? IS_STRING = /^['"]/ diff --git a/test/test_literals.coffee b/test/test_literals.coffee index 28825271..19f6682e 100644 --- a/test/test_literals.coffee +++ b/test/test_literals.coffee @@ -236,3 +236,7 @@ result = obj.object() eq result.one, 1 eq result.two, 2 eq result.two, obj.list()[1] + + +#542: Objects leading expression statement should be parenthesized. +{f: -> ok yes }.f() + 1