From e2a6f292a2cf1ecf7ee9d8ccf003e1ecc7cc4784 Mon Sep 17 00:00:00 2001 From: satyr Date: Wed, 20 Oct 2010 19:53:41 +0900 Subject: [PATCH] nodes: refactored to reduce parens --- lib/lexer.js | 4 +- lib/nodes.js | 109 +++++++++++--------------- src/nodes.coffee | 198 +++++++++++++++++++++++------------------------ 3 files changed, 147 insertions(+), 164 deletions(-) diff --git a/lib/lexer.js b/lib/lexer.js index fea785dd..d24d816c 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -413,7 +413,7 @@ if (!herecomment) { while (match = HEREDOC_INDENT.exec(doc)) { attempt = match[1]; - if (indent === null || (0 < (_ref2 = attempt.length)) && (_ref2 < indent.length)) { + if (indent === null || 0 < (_ref2 = attempt.length) && _ref2 < indent.length) { indent = attempt; } } @@ -528,7 +528,7 @@ i += expr.length; pi = i + 1; } - if ((i > pi) && (pi < str.length)) { + if (i > pi && pi < str.length) { tokens.push(['TO_BE_STRING', str.slice(pi)]); } if (regex) { diff --git a/lib/nodes.js b/lib/nodes.js index 4d10d293..ced9e67a 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -41,7 +41,7 @@ Base.prototype.compileClosure = function(o) { o.sharedScope = o.scope; if (this.containsPureStatement()) { - throw new Error('cannot include a pure statement in an expression.'); + throw SyntaxError('cannot include a pure statement in an expression.'); } return Closure.wrap(this).compile(o); }; @@ -109,11 +109,10 @@ return '\n' + idt + klass + children; }; Base.prototype.eachChild = function(func) { - var _i, _j, _len, _len2, _ref2, _ref3, _result, attr, child; + var _i, _j, _len, _len2, _ref2, _ref3, attr, child; if (!this.children) { return; } - _result = []; for (_i = 0, _len = (_ref2 = this.children).length; _i < _len; _i++) { attr = _ref2[_i]; if (this[attr]) { @@ -125,7 +124,7 @@ } } } - return _result; + return this; }; Base.prototype.collectChildren = function() { var nodes; @@ -292,14 +291,14 @@ return Return; })(); __extends(Return, Base); + Return.prototype.children = ['expression']; Return.prototype.isStatement = YES; Return.prototype.isPureStatement = YES; - Return.prototype.children = ['expression']; Return.prototype.makeReturn = THIS; Return.prototype.compile = function(o) { var _ref2, expr; expr = (((_ref2 = this.expression) != null) ? _ref2.makeReturn() : undefined); - if (expr && (!(expr instanceof Return))) { + if (expr && !(expr instanceof Return)) { return expr.compile(o); } return Return.__super__.compile.call(this, o); @@ -319,11 +318,10 @@ })(); exports.Value = (function() { Value = (function() { - function Value(_arg, _arg2, tag) { - this.properties = _arg2; + function Value(_arg, props, tag) { this.base = _arg; Value.__super__.constructor.call(this); - this.properties || (this.properties = []); + this.properties = props || []; if (tag) { this.tags[tag] = true; } @@ -485,11 +483,11 @@ var method, name; method = o.scope.method; if (!method) { - throw Error("cannot call super outside of a function."); + throw SyntaxError('cannot call super outside of a function.'); } name = method.name; if (!name) { - throw Error("cannot call super on an anonymous function."); + throw SyntaxError('cannot call super on an anonymous function.'); } return method.klass ? ("" + (method.klass) + ".__super__." + name) : ("" + name + ".__super__.constructor"); }; @@ -606,9 +604,7 @@ __extends(Extends, Base); Extends.prototype.children = ['child', 'parent']; Extends.prototype.compileNode = function(o) { - var ref; - ref = new Value(new Literal(utility('extends'))); - return (new Call(ref, [this.child, this.parent])).compile(o); + return new Call(new Value(new Literal(utility('extends'))), [this.child, this.parent]).compile(o); }; return Extends; })(); @@ -626,10 +622,9 @@ __extends(Accessor, Base); Accessor.prototype.children = ['name']; Accessor.prototype.compileNode = function(o) { - var name, namePart; + var name; name = this.name.compile(o); - namePart = name.match(IS_STRING) ? ("[" + name + "]") : ("." + name); - return this.prototype + namePart; + return this.prototype + (IS_STRING.test(name) ? ("[" + name + "]") : ("." + name)); }; Accessor.prototype.isComplex = NO; return Accessor; @@ -646,10 +641,7 @@ __extends(Index, Base); Index.prototype.children = ['index']; Index.prototype.compileNode = function(o) { - var idx, prefix; - idx = this.index.compile(o); - prefix = this.proto ? '.prototype' : ''; - return "" + prefix + "[" + idx + "]"; + return "" + (this.proto ? '.prototype' : '') + "[" + (this.index.compile(o)) + "]"; }; Index.prototype.isComplex = function() { return this.index.isComplex(); @@ -828,10 +820,9 @@ })(); exports.ArrayLiteral = (function() { ArrayLiteral = (function() { - function ArrayLiteral(_arg) { - this.objects = _arg; + function ArrayLiteral(objs) { ArrayLiteral.__super__.constructor.call(this); - this.objects || (this.objects = []); + this.objects = objs || []; return this; }; return ArrayLiteral; @@ -873,12 +864,11 @@ })(); exports.Class = (function() { Class = (function() { - function Class(variable, _arg, _arg2) { - this.properties = _arg2; + function Class(variable, _arg, props) { this.parent = _arg; Class.__super__.constructor.call(this); this.variable = variable === '__temp__' ? new Literal(variable) : variable; - this.properties || (this.properties = []); + this.properties = props || []; this.returns = false; return this; }; @@ -922,7 +912,7 @@ func = new Code([], new Expressions([apply])); } if (func.bound) { - throw new Error("cannot define a constructor as a bound function."); + throw SyntaxError('cannot define a constructor as a bound function.'); } func.name = className; func.body.push(new Return(new Literal('this'))); @@ -955,7 +945,7 @@ } props.push(prop); } - constructor.className = className.match(/[\w\d\$_]+$/); + constructor.className = className.match(/[$\w]+$/); if (me) { constructor.body.unshift(new Literal("" + me + " = this")); } @@ -1067,7 +1057,7 @@ } } if (!(obj instanceof Value || obj instanceof Splat)) { - throw new Error('pattern matching must use only identifiers on the left-hand side.'); + throw SyntaxError('pattern matching must use only identifiers on the left-hand side.'); } accessClass = isObject && IDENTIFIER.test(idx.value) ? Accessor : Index; if (!splat && obj instanceof Splat) { @@ -1177,7 +1167,7 @@ } for (_i = 0, _len2 = params.length; _i < _len2; _i++) { param = params[_i]; - (o.scope.parameter(param)); + o.scope.parameter(param); } comm = this.comment ? this.comment.compile(o) + '\n' : ''; if (this.className) { @@ -1311,6 +1301,7 @@ })(); __extends(While, Base); While.prototype.children = ['condition', 'guard', 'body']; + While.prototype.topSensitive = YES; While.prototype.isStatement = YES; While.prototype.addBody = function(body) { this.body = body; @@ -1320,7 +1311,6 @@ this.returns = true; return this; }; - While.prototype.topSensitive = YES; While.prototype.compileNode = function(o) { var cond, post, pre, rvar, set, top; top = del(o, 'top') && !this.returns; @@ -1398,9 +1388,9 @@ return (_ref2 = this.operator, __indexOf.call(this.CHAINABLE, _ref2) >= 0); }; Op.prototype.invert = function() { - var _ref2; - if (((_ref2 = this.operator) === '===' || _ref2 === '!==')) { - this.operator = this.INVERSIONS[this.operator]; + var op; + if (op = this.INVERSIONS[this.operator]) { + this.operator = op; return this; } else return this.second ? new Parens(this).invert() : Op.__super__.invert.call(this); }; @@ -1425,11 +1415,9 @@ return "" + (this.first.compile(o)) + " " + (this.operator) + " " + (this.second.compile(o)); }; Op.prototype.compileChain = function(o) { - var _ref2, _ref3, first, second, shared; - shared = this.first.unwrap().second; - _ref2 = shared.compileReference(o), this.first.second = _ref2[0], shared = _ref2[1]; - _ref3 = [this.first.compile(o), this.second.compile(o), shared.compile(o)], first = _ref3[0], second = _ref3[1], shared = _ref3[2]; - return "(" + first + ") && (" + shared + " " + (this.operator) + " " + second + ")"; + var _ref2, shared; + _ref2 = this.first.unwrap().second.compileReference(o), this.first.second = _ref2[0], shared = _ref2[1]; + return "" + (this.first.compile(o)) + " && " + (shared.compile(o)) + " " + (this.operator) + " " + (this.second.compile(o)); }; Op.prototype.compileExistence = function(o) { var fst, ref; @@ -1580,13 +1568,13 @@ })(); __extends(Parens, Base); Parens.prototype.children = ['expression']; + Parens.prototype.topSensitive = YES; Parens.prototype.isStatement = function(o) { return this.expression.isStatement(o); }; Parens.prototype.isComplex = function() { return this.expression.isComplex(); }; - Parens.prototype.topSensitive = YES; Parens.prototype.makeReturn = function() { return this.expression.makeReturn(); }; @@ -1619,10 +1607,10 @@ if (this.object) { _ref2 = [this.index, this.name], this.name = _ref2[0], this.index = _ref2[1]; } - this.pattern = this.name instanceof Value; if (this.index instanceof Value) { - throw new Error('index cannot be a pattern matching expression'); + throw SyntaxError('index cannot be a pattern matching expression'); } + this.pattern = this.name instanceof Value; this.returns = false; return this; }; @@ -1630,8 +1618,8 @@ })(); __extends(For, Base); For.prototype.children = ['body', 'source', 'guard']; - For.prototype.isStatement = YES; For.prototype.topSensitive = YES; + For.prototype.isStatement = YES; For.prototype.makeReturn = function() { this.returns = true; return this; @@ -1771,8 +1759,6 @@ this.cases = _arg2; this.subject = _arg; Switch.__super__.constructor.call(this); - this.tags.subjectless = !this.subject; - this.subject || (this.subject = new Literal('true')); return this; }; return Switch; @@ -1792,28 +1778,27 @@ return this; }; Switch.prototype.compileNode = function(o) { - var _i, _j, _len, _len2, _ref2, _ref3, block, code, condition, conditions, exprs, idt, pair; - idt = (o.indent = this.idt(2)); + var _i, _j, _len, _len2, _ref2, _ref3, _ref4, _ref5, block, code, condition, conditions, idt1, idt2; + idt1 = this.idt(1); + idt2 = (o.indent = this.idt(2)); o.top = true; - code = ("" + (this.tab) + "switch (" + (this.subject.compile(o)) + ") {"); - for (_i = 0, _len = (_ref2 = this.cases).length; _i < _len; _i++) { - pair = _ref2[_i]; - conditions = pair[0], block = pair[1]; - exprs = block.expressions; - for (_j = 0, _len2 = (_ref3 = flatten([conditions])).length; _j < _len2; _j++) { - condition = _ref3[_j]; - if (this.tags.subjectless) { - condition = new Op('!!', new Parens(condition)); + code = ("" + (this.tab) + "switch (" + ((((_ref2 = this.subject) != null) ? _ref2.compile(o) : undefined) || true) + ") {"); + for (_i = 0, _len = (_ref3 = this.cases).length; _i < _len; _i++) { + _ref4 = _ref3[_i], conditions = _ref4[0], block = _ref4[1]; + for (_j = 0, _len2 = (_ref5 = flatten([conditions])).length; _j < _len2; _j++) { + condition = _ref5[_j]; + if (!this.subject) { + condition = condition.invert().invert(); } - code += ("\n" + (this.idt(1)) + "case " + (condition.compile(o)) + ":"); + code += ("\n" + idt1 + "case " + (condition.compile(o)) + ":"); } code += ("\n" + (block.compile(o))); - if (!(last(exprs) instanceof Return)) { - code += ("\n" + idt + "break;"); + if (!(last(block.expressions) instanceof Return)) { + code += ("\n" + idt2 + "break;"); } } if (this.otherwise) { - code += ("\n" + (this.idt(1)) + "default:\n" + (this.otherwise.compile(o))); + code += ("\n" + idt1 + "default:\n" + (this.otherwise.compile(o))); } code += ("\n" + (this.tab) + "}"); return code; @@ -1965,7 +1950,7 @@ TAB = ' '; TRAILING_WHITESPACE = /[ \t]+$/gm; IDENTIFIER = /^[$A-Za-z_][$\w]*$/; - NUMBER = /^0x[\da-f]+|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?$/i; + NUMBER = /^0x[\da-f]+$|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?$/i; SIMPLENUM = /^[+-]?\d+$/; IS_STRING = /^['"]/; utility = function(name) { diff --git a/src/nodes.coffee b/src/nodes.coffee index 66ccfc51..c1445811 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -52,7 +52,7 @@ exports.Base = class Base # object with their parent closure, to preserve the expected lexical scope. compileClosure: (o) -> o.sharedScope = o.scope - throw new Error 'cannot include a pure statement in an expression.' if @containsPureStatement() + throw SyntaxError 'cannot include a pure statement in an expression.' if @containsPureStatement() Closure.wrap(this).compile o # If the code generation wishes to use the result of a complex expression @@ -115,6 +115,7 @@ exports.Base = class Base for attr in @children when this[attr] for child in flatten [this[attr]] return if func(child) is false + this collectChildren: -> nodes = [] @@ -151,7 +152,8 @@ exports.Base = class Base # `if`, `switch`, or `try`, and so on... exports.Expressions = class Expressions extends Base - children: ['expressions'] + children: ['expressions'] + isStatement: YES constructor: (nodes) -> @@ -212,7 +214,7 @@ exports.Expressions = class Expressions extends Base if o.scope.hasAssignments this code = "#{@tab}var #{ o.scope.compiledAssignments().replace /\n/g, '$&' + @tab };\n#{code}" if not o.globals and o.scope.hasDeclarations this - code = "#{@tab}var #{o.scope.compiledDeclarations()};\n#{code}" + code = "#{@tab}var #{ o.scope.compiledDeclarations() };\n#{code}" code # Compiles a single expression within the expressions body. If we need to @@ -257,9 +259,9 @@ exports.Literal = class Literal extends Base assigns: (name) -> name is @value compileNode: (o) -> - idt = if @isStatement(o) then @idt() else '' - end = if @isStatement(o) then ';' else '' - val = if @isReserved() then "\"#{@value}\"" else @value + idt = if @isStatement o then @idt() else '' + end = if @isStatement o then ';' else '' + val = if @isReserved() then "\"#{@value}\"" else @value idt + val + end toString: -> ' "' + @value + '"' @@ -270,9 +272,10 @@ exports.Literal = class Literal extends Base # make sense. exports.Return = class Return extends Base - isStatement: YES - isPureStatement: YES - children: ['expression'] + children: ['expression'] + + isStatement : YES + isPureStatement: YES constructor: (@expression) -> super() @@ -281,7 +284,7 @@ exports.Return = class Return extends Base compile: (o) -> expr = @expression?.makeReturn() - return expr.compile o if expr and (expr not instanceof Return) + return expr.compile o if expr and expr not instanceof Return super o compileNode: (o) -> @@ -300,9 +303,9 @@ exports.Value = class Value extends Base children: ['base', 'properties'] # A **Value** has a base and a list of property accesses. - constructor: (@base, @properties, tag) -> + constructor: (@base, props, tag) -> super() - @properties or= [] + @properties = props or [] @tags[tag] = yes if tag # Add a property access to the list. @@ -333,7 +336,6 @@ exports.Value = class Value extends 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. unwrap: -> @@ -429,7 +431,7 @@ exports.Call = class Call extends Base @isNew = false @isSuper = variable is 'super' @variable = if @isSuper then null else variable - @args or= [] + @args or= [] compileSplatArguments: (o) -> Splat.compileSplattedArray @args, o @@ -445,9 +447,9 @@ exports.Call = class Call extends Base # Grab the reference to the superclass' implementation of the current method. superReference: (o) -> {method} = o.scope - throw Error "cannot call super outside of a function." unless method + throw SyntaxError 'cannot call super outside of a function.' unless method {name} = method - throw Error "cannot call super on an anonymous function." unless name + throw SyntaxError 'cannot call super on an anonymous function.' unless name if method.klass "#{method.klass}.__super__.#{name}" else @@ -542,8 +544,7 @@ exports.Extends = class Extends extends Base # Hooks one constructor into another's prototype chain. compileNode: (o) -> - ref = new Value new Literal utility 'extends' - (new Call ref, [@child, @parent]).compile o + new Call(new Value(new Literal utility 'extends'), [@child, @parent]).compile o #### Accessor @@ -556,12 +557,11 @@ exports.Accessor = class Accessor extends Base constructor: (@name, tag) -> super() @prototype = if tag is 'prototype' then '.prototype' else '' - @soakNode = tag is 'soak' + @soakNode = tag is 'soak' compileNode: (o) -> name = @name.compile o - namePart = if name.match(IS_STRING) then "[#{name}]" else ".#{name}" - @prototype + namePart + @prototype + if IS_STRING.test(name) then "[#{name}]" else ".#{name}" isComplex: NO @@ -576,9 +576,7 @@ exports.Index = class Index extends Base super() compileNode: (o) -> - idx = @index.compile o - prefix = if @proto then '.prototype' else '' - "#{prefix}[#{idx}]" + "#{ if @proto then '.prototype' else '' }[#{ @index.compile o }]" isComplex: -> @index.isComplex() @@ -716,9 +714,9 @@ exports.ArrayLiteral = class ArrayLiteral extends Base children: ['objects'] - constructor: (@objects) -> + constructor: (objs) -> super() - @objects or= [] + @objects = objs or [] compileSplatLiteral: (o) -> Splat.compileSplattedArray @objects, o @@ -752,15 +750,16 @@ exports.ArrayLiteral = class ArrayLiteral extends Base # The CoffeeScript class definition. exports.Class = class Class extends Base - children: ['variable', 'parent', 'properties'] + children: ['variable', 'parent', 'properties'] + isStatement: YES # Initialize a **Class** with its name, an optional superclass, and a # list of prototype property assignments. - constructor: (variable, @parent, @properties) -> + constructor: (variable, @parent, props) -> super() - @variable = if variable is '__temp__' then new Literal variable else variable - @properties or= [] + @variable = if variable is '__temp__' then new Literal variable else variable + @properties = props or [] @returns = false makeReturn: -> @@ -782,9 +781,9 @@ exports.Class = class Class extends Base if @parent applied = new Value @parent, [new Accessor new Literal 'apply'] - constructor = new Code([], new Expressions([ + constructor = new Code [], new Expressions [ new Call applied, [new Literal('this'), new Literal('arguments')] - ])) + ] else constructor = new Code [], new Expressions [new Return new Literal 'this'] @@ -794,9 +793,10 @@ exports.Class = class Class extends Base if func not instanceof Code [func, ref] = func.compileReference o props.push func if func isnt ref - apply = new Call(new Value(ref, [new Accessor new Literal 'apply']), [new Literal('this'), new Literal('arguments')]) + apply = new Call new Value(ref, [new Accessor new Literal 'apply']), + [new Literal('this'), new Literal('arguments')] func = new Code [], new Expressions([apply]) - throw new Error "cannot define a constructor as a bound function." if func.bound + throw SyntaxError 'cannot define a constructor as a bound function.' if func.bound func.name = className func.body.push new Return new Literal 'this' variable = new Value variable @@ -809,18 +809,18 @@ exports.Class = class Class extends Base func.context = className else func.bound = false - constScope or= new Scope(o.scope, constructor.body, constructor) + constScope or= new Scope o.scope, constructor.body, constructor me or= constScope.freeVariable 'this' - pname = pvar.compile(o) + pname = pvar.compile o constructor.body.push new Return new Literal 'this' if constructor.body.empty() constructor.body.unshift new Literal "this.#{pname} = function(){ return #{className}.prototype.#{pname}.apply(#{me}, arguments); }" if pvar access = if prop.context is 'this' then pvar.base.properties[0] else new Accessor(pvar, 'prototype') val = new Value variable, [access] - prop = new Assign(val, func) + prop = new Assign val, func props.push prop - constructor.className = className.match /[\w\d\$_]+$/ + constructor.className = className.match /[$\w]+$/ constructor.body.unshift new Literal "#{me} = this" if me construct = @idt() + new Assign(variable, constructor).compile(merge o, sharedScope: constScope) + ';' props = if !props.empty() then '\n' + props.compile(o) else '' @@ -841,11 +841,11 @@ exports.Assign = class Assign extends Base children: ['variable', 'value'] + topSensitive: YES + constructor: (@variable, @value, @context) -> super() - topSensitive: YES - # 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 @@ -911,7 +911,7 @@ exports.Assign = class Assign extends Base # A shorthand `{a, b, @c} = val` pattern-match. idx = if obj.tags.this then obj.properties[0].name else obj unless obj instanceof Value or obj instanceof Splat - throw new Error 'pattern matching must use only identifiers on the left-hand side.' + throw SyntaxError 'pattern matching must use only identifiers on the left-hand side.' accessClass = if isObject and IDENTIFIER.test(idx.value) then Accessor else Index if not splat and obj instanceof Splat val = new Literal obj.compileValue o, valVar, i, olength - i - 1 @@ -972,7 +972,7 @@ exports.Code = class Code extends Base top = del o, 'top' o.scope = sharedScope or new Scope(o.scope, @body, this) o.top = true - o.indent = @idt(1) + o.indent = @idt 1 empty = @body.expressions.length is 0 delete o.bare delete o.globals @@ -998,9 +998,9 @@ exports.Code = class Code extends Base else params.push param o.scope.startLevel() - params = (param.compile(o) for param in params) + params = param.compile(o) for param in params @body.makeReturn() unless empty or @noReturn - (o.scope.parameter(param)) for param in params + o.scope.parameter param for param in params comm = if @comment then @comment.compile(o) + '\n' else '' o.indent = @idt 2 if @className code = if @body.expressions.length then "\n#{ @body.compileWithDeclarations(o) }\n" else '' @@ -1052,12 +1052,12 @@ exports.Splat = class Splat extends Base assigns: (name) -> @name.assigns name compileNode: (o) -> - if @index? then @compileParam(o) else @name.compile(o) + if @index? then @compileParam o else @name.compile o # Compiling a parameter splat means recovering the parameters that succeed # the splat in the parameter list, by slicing the arguments object. compileParam: (o) -> - name = @name.compile(o) + name = @name.compile o o.scope.find name end = '' if @trailings.length @@ -1107,8 +1107,10 @@ exports.Splat = class Splat extends Base # flexibility or more speed than a comprehension can provide. exports.While = class While extends Base - children: ['condition', 'guard', 'body'] - isStatement: YES + children: ['condition', 'guard', 'body'] + + topSensitive: YES + isStatement : YES constructor: (condition, opts) -> super() @@ -1123,8 +1125,6 @@ exports.While = class While extends Base @returns = true this - 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 # return an array containing the computed result of each iteration. @@ -1140,9 +1140,9 @@ exports.While = class While extends Base set = "#{@tab}#{rvar} = [];\n" @body = Push.wrap(rvar, @body) if @body pre = "#{set}#{@tab}while (#{cond})" - @body = Expressions.wrap([new If(@guard, @body)]) if @guard + @body = Expressions.wrap [new If @guard, @body] if @guard if @returns - post = '\n' + new Return(new Literal rvar).compile(merge(o, indent: @idt())) + post = '\n' + new Return(new Literal rvar).compile merge o, indent: @idt() else post = '' "#{pre} {\n#{ @body.compile(o) }\n#{@tab}}#{post}" @@ -1182,21 +1182,22 @@ exports.Op = class Op extends Base first = new Parens first if first instanceof Code and first.bound super() @operator = @CONVERSIONS[op] or op - (@first = first ).tags.operation = yes - (@second = second).tags.operation = yes if second + (@first = first ).tags.operation = yes + (@second = second).tags.operation = yes if second @flip = !!flip isUnary: -> not @second - isComplex: -> @operator isnt '!' or @first.isComplex() + isComplex: -> + @operator isnt '!' or @first.isComplex() isChainable: -> @operator in @CHAINABLE invert: -> - if @operator in ['===', '!=='] - @operator = @INVERSIONS[@operator] + if op = @INVERSIONS[@operator] + @operator = op this else if @second new Parens(this).invert() @@ -1204,7 +1205,7 @@ exports.Op = class Op extends Base super() toString: (idt) -> - super(idt, @constructor.name + ' ' + @operator) + super idt, @constructor.name + ' ' + @operator compileNode: (o) -> if @isUnary() @@ -1221,10 +1222,8 @@ exports.Op = class Op extends Base # bin/coffee -e "puts 50 < 65 > 10" # true compileChain: (o) -> - shared = @first.unwrap().second - [@first.second, shared] = shared.compileReference o - [first, second, shared] = [@first.compile(o), @second.compile(o), shared.compile(o)] - "(#{first}) && (#{shared} #{@operator} #{second})" + [@first.second, shared] = @first.unwrap().second.compileReference o + "#{ @first.compile o } && #{ shared.compile o } #{@operator} #{ @second.compile o }" compileExistence: (o) -> if @first.isComplex() @@ -1253,13 +1252,13 @@ exports.In = class In extends Base @array instanceof Value and @array.isArray() compileNode: (o) -> - if @isArray() then @compileOrTest(o) else @compileLoopTest(o) + if @isArray() then @compileOrTest o else @compileLoopTest o compileOrTest: (o) -> [obj1, obj2] = @object.compileReference o, precompile: yes tests = for item, i in @array.base.objects - "#{if i then obj2 else obj1} === #{item.compile(o)}" - "(#{tests.join(' || ')})" + "#{ if i then obj2 else obj1 } === #{ item.compile o }" + "(#{ tests.join ' || ' })" compileLoopTest: (o) -> [obj1, obj2] = @object.compileReference merge(o, top: yes), precompile: yes @@ -1272,14 +1271,15 @@ exports.In = class In extends Base # A classic *try/catch/finally* block. exports.Try = class Try extends Base - children: ['attempt', 'recovery', 'ensure'] + children: ['attempt', 'recovery', 'ensure'] + isStatement: YES constructor: (@attempt, @error, @recovery, @ensure) -> super() makeReturn: -> - @attempt = @attempt.makeReturn() if @attempt + @attempt = @attempt .makeReturn() if @attempt @recovery = @recovery.makeReturn() if @recovery this @@ -1289,11 +1289,11 @@ exports.Try = class Try extends Base o.indent = @idt 1 o.top = true attemptPart = @attempt.compile(o) - errorPart = if @error then " (#{ @error.compile(o) }) " else ' ' + errorPart = if @error then " (#{ @error.compile o }) " else ' ' catchPart = if @recovery - " catch#{errorPart}{\n#{ @recovery.compile(o) }\n#{@tab}}" + " catch#{errorPart}{\n#{ @recovery.compile o }\n#{@tab}}" else unless @ensure or @recovery then ' catch (_e) {}' else '' - finallyPart = (@ensure or '') and ' finally {\n' + @ensure.compile(merge(o)) + "\n#{@tab}}" + finallyPart = (@ensure or '') and ' finally {\n' + @ensure.compile(merge o) + "\n#{@tab}}" "#{@tab}try {\n#{attemptPart}\n#{@tab}}#{catchPart}#{finallyPart}" #### Throw @@ -1301,7 +1301,8 @@ exports.Try = class Try extends Base # Simple node to throw an exception. exports.Throw = class Throw extends Base - children: ['expression'] + children: ['expression'] + isStatement: YES constructor: (@expression) -> @@ -1311,7 +1312,7 @@ exports.Throw = class Throw extends Base makeReturn: THIS compileNode: (o) -> - "#{@tab}throw #{@expression.compile(o)};" + "#{@tab}throw #{ @expression.compile o };" #### Existence @@ -1344,23 +1345,23 @@ exports.Parens = class Parens extends Base children: ['expression'] + topSensitive: YES + constructor: (@expression) -> super() isStatement: (o) -> - @expression.isStatement(o) + @expression.isStatement o isComplex: -> @expression.isComplex() - topSensitive: YES - makeReturn: -> @expression.makeReturn() compileNode: (o) -> top = del o, 'top' @expression.parenthetical = true - code = @expression.compile(o) + code = @expression.compile o return code if top and @expression.isPureStatement o if @parenthetical or @isStatement o return if top then @tab + code + ';' else code @@ -1377,8 +1378,10 @@ exports.Parens = class Parens extends Base # you can map and filter in a single pass. exports.For = class For extends Base - children: ['body', 'source', 'guard'] - isStatement: YES + children: ['body', 'source', 'guard'] + + topSensitive: YES + isStatement : YES constructor: (@body, source, @name, @index) -> super() @@ -1386,18 +1389,16 @@ exports.For = class For extends Base @raw = !!source.raw @object = !!source.object [@name, @index] = [@index, @name] if @object + throw SyntaxError 'index cannot be a pattern matching expression' if @index instanceof Value @pattern = @name instanceof Value - throw new Error('index cannot be a pattern matching expression') if @index instanceof Value @returns = false - topSensitive: YES - makeReturn: -> @returns = true this compileReturnValue: (val, o) -> - return '\n' + new Return(new Literal val).compile(o) if @returns + return '\n' + new Return(new Literal val).compile o if @returns return '\n' + val if val '' @@ -1422,7 +1423,7 @@ exports.For = class For extends Base varPart = '' guardPart = '' unstepPart = '' - body = Expressions.wrap([@body]) + body = Expressions.wrap [@body] idt1 = @idt 1 if range forPart = source.compile merge o, {index: ivar, @step} @@ -1442,10 +1443,10 @@ exports.For = class For extends Base stepPart = if @step then "#{ivar} += #{ @step.compile(o) }" else "#{ivar}++" forPart = "#{ivar} = 0, #{lvar} = #{sourcePart}.length; #{ivar} < #{lvar}; #{stepPart}" resultPart = if rvar then "#{@tab}#{rvar} = [];\n" else '' - returnResult = @compileReturnValue(rvar, o) - body = Push.wrap(rvar, body) unless topLevel + returnResult = @compileReturnValue rvar, o + body = Push.wrap rvar, body unless topLevel if @guard - body = Expressions.wrap([new If(@guard, body)]) + body = Expressions.wrap [new If @guard, body] if codeInBody body.unshift new Literal "var #{name} = #{ivar}" if range body.unshift new Literal "var #{namePart}" if namePart @@ -1485,8 +1486,6 @@ exports.Switch = class Switch extends Base constructor: (@subject, @cases, @otherwise) -> super() - @tags.subjectless = !@subject - @subject or= new Literal 'true' makeReturn: -> pair[1].makeReturn() for pair in @cases @@ -1494,19 +1493,18 @@ exports.Switch = class Switch extends Base this compileNode: (o) -> - idt = o.indent = @idt 2 + idt1 = @idt 1 + idt2 = o.indent = @idt 2 o.top = yes - code = "#{ @tab }switch (#{ @subject.compile o }) {" - for pair in @cases - [conditions, block] = pair - exprs = block.expressions + code = "#{ @tab }switch (#{ @subject?.compile(o) or true }) {" + for [conditions, block] in @cases for condition in flatten [conditions] - condition = new Op '!!', new Parens condition if @tags.subjectless - code += "\n#{ @idt(1) }case #{ condition.compile o }:" + condition = condition.invert().invert() unless @subject + code += "\n#{ idt1 }case #{ condition.compile o }:" code += "\n#{ block.compile o }" - code += "\n#{ idt }break;" unless last(exprs) instanceof Return + code += "\n#{ idt2 }break;" unless last(block.expressions) instanceof Return if @otherwise - code += "\n#{ @idt(1) }default:\n#{ @otherwise.compile o }" + code += "\n#{ idt1 }default:\n#{ @otherwise.compile o }" code += "\n#{ @tab }}" code @@ -1556,8 +1554,8 @@ exports.If = class If extends Base makeReturn: -> if @isStatement() - @body and= @ensureExpressions(@body.makeReturn()) - @elseBody and= @ensureExpressions(@elseBody.makeReturn()) + @body and= @ensureExpressions @body.makeReturn() + @elseBody and= @ensureExpressions @elseBody.makeReturn() this else new Return this @@ -1687,7 +1685,7 @@ TAB = ' ' TRAILING_WHITESPACE = /[ \t]+$/gm IDENTIFIER = /^[$A-Za-z_][$\w]*$/ -NUMBER = /^0x[\da-f]+|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?$/i +NUMBER = /^0x[\da-f]+$|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?$/i SIMPLENUM = /^[+-]?\d+$/ # Is a literal value a string?