Jump To …
browser.coffee cake.coffee coffee-script.coffee command.coffee grammar.coffee helpers.coffee index.coffee lexer.coffee nodes.coffee optparse.coffee repl.coffee rewriter.coffee scope.coffee

# nodes.coffee

#

`nodes.coffee` contains all of the node classes for the syntax tree. Most nodes are created as the result of actions in the grammar, but some are created by other nodes as a method of code generation. To convert the syntax tree into a string of JavaScript code, call `compile()` on the root.

#

Set up for both Node.js and the browser, by including the Scope class and the helper functions.

    if process?
      Scope =   require('./scope').Scope
      helpers = require('./helpers').helpers
    else
      this.exports = this
      helpers =      this.helpers
      Scope =        this.Scope

#

Import the helpers we plan to use.

    {compact, flatten, merge, del, include, indexOf, starts, ends} = helpers

#

### BaseNode

#

The BaseNode is the abstract base class for all nodes in the syntax tree. Each subclass implements the `compileNode` method, which performs the code generation for that node. To compile a node to JavaScript, call `compile` on it, which wraps `compileNode` in some generic extra smarts, to know when the generated code needs to be wrapped up in a closure. An options hash is passed and cloned throughout, containing information about the environment from higher in the tree (such as if a returned value is being requested by the surrounding function), information about the current scope, and indentation level.

    exports.BaseNode = class BaseNode

      constructor: ->
        @tags = {}

#

Common logic for determining whether to wrap this node in a closure before compiling it, or to compile directly. We need to wrap if this node is a statement, and it's not a pureStatement, and we're not at the top level of a block (which would be unnecessary), and we haven't already been asked to return the result (because statements know how to return results).

If a Node is topSensitive, that means that it needs to compile differently depending on whether it's being used as part of a larger expression, or is a top-level statement within the function body.

      compile: (o) ->
        @options = merge o or {}
        @tab     = o.indent
        del @options, 'chainRoot' unless this instanceof AccessorNode or this instanceof IndexNode
        top     = if @topSensitive() then @options.top else del @options, 'top'
        closure = @isStatement(o) and not @isPureStatement() and not top and
                  not @options.asStatement and this not instanceof CommentNode and
                  not @containsPureStatement()
        if closure then @compileClosure(@options) else @compileNode(@options)

#

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
        ClosureNode.wrap(this).compile o

#

If the code generation wishes to use the result of a complex expression in multiple places, ensure that the expression is only ever evaluated once, by assigning it to a temporary variable.

      compileReference: (o, options) ->
        options or= {}
        pair = if not (@containsType(CallNode) or
                      (this instanceof ValueNode and (not (@base instanceof LiteralNode) or @hasProperties())))
          [this, this]
        else if this instanceof ValueNode and options.assignment
          this.cacheIndexes(o)
        else
          reference = literal o.scope.freeVariable()
          compiled  = new AssignNode reference, this
          [compiled, reference]
        return [pair[0].compile(o), pair[1].compile(o)] if options.precompile
        pair

#

Convenience method to grab the current indentation level, plus tabbing in.

      idt: (tabs) ->
        idt = @tab or ''
        num = (tabs or 0) + 1
        idt += TAB while num -= 1
        idt

#

Construct a node that returns the current node's result. Note that this is overridden for smarter behavior for many statement nodes (eg IfNode, ForNode)...

      makeReturn: ->
        new ReturnNode this

#

Does this node, or any of its children, contain a node of a certain kind? Recursively traverses down the children of the nodes, yielding to a block and returning true when the block finds a match. `contains` does not cross scope boundaries.

      contains: (block) ->
        contains = false
        @traverseChildren false, (node) ->
          if block(node)
            contains = true
            return false
        contains

#

Is this node of a certain type, or does it contain the type?

      containsType: (type) ->
        this instanceof type or @contains (n) -> n instanceof type

#

Convenience for the most common use of contains. Does the node contain a pure statement?

      containsPureStatement: ->
        @isPureStatement() or @contains (n) -> n.isPureStatement and n.isPureStatement()

#

Perform an in-order traversal of the AST. Crosses scope boundaries.

      traverse: (block) -> @traverseChildren true, block

#

`toString` representation of the node, for inspecting the parse tree. This is what `coffee --nodes` prints out.

      toString: (idt, override) ->
        idt or= ''
        children = (child.toString idt + TAB for child in @collectChildren()).join('')
        '\n' + idt + (override or @class) + children

      eachChild: (func) ->
        return unless @children
        for attr in @children when this[attr]
          for child in flatten [this[attr]]
            return if func(child) is false

      collectChildren: ->
        nodes = []
        @eachChild (node) -> nodes.push node
        nodes

      traverseChildren: (crossScope, func) ->
        @eachChild (child) ->
          func.apply(this, arguments)
          child.traverseChildren(crossScope, func) if child instanceof BaseNode

#

Default implementations of the common node properties and methods. Nodes will override these with custom logic, if needed.

      class:    'BaseNode'
      children: []

      unwrap          : -> this
      isStatement     : -> no
      isPureStatement : -> no
      topSensitive    : -> no

#

### Expressions

#

The expressions body is the list of expressions that forms the body of an indented block of code -- the implementation of a function, a clause in an `if`, `switch`, or `try`, and so on...

    exports.Expressions = class Expressions extends BaseNode

      class:        'Expressions'
      children:     ['expressions']
      isStatement:  -> yes

      constructor: (nodes) ->
        super()
        @expressions = compact flatten nodes or []

#

Tack an expression on to the end of this expression list.

      push: (node) ->
        @expressions.push(node)
        this

#

Add an expression at the beginning of this expression list.

      unshift: (node) ->
        @expressions.unshift(node)
        this

#

If this Expressions consists of just a single node, unwrap it by pulling it back out.

      unwrap: ->
        if @expressions.length is 1 then @expressions[0] else this

#

Is this an empty block of code?

      empty: ->
        @expressions.length is 0

#

An Expressions node does not return its entire body, rather it ensures that the final expression is returned.

      makeReturn: ->
        idx  = @expressions.length - 1
        last = @expressions[idx]
        last = @expressions[idx -= 1] if last instanceof CommentNode
        return this if not last or last instanceof ReturnNode
        @expressions[idx] = last.makeReturn()
        this

#

An Expressions is the only node that can serve as the root.

      compile: (o) ->
        o or= {}
        if o.scope then super(o) else @compileRoot(o)

      compileNode: (o) ->
        (@compileExpression(node, merge(o)) for node in @expressions).join("\n")

#

If we happen to be the top-level Expressions, wrap everything in a safety closure, unless requested not to. It would be better not to generate them in the first place, but for now, clean up obvious double-parentheses.

      compileRoot: (o) ->
        o.indent  = @tab = if o.noWrap then '' else TAB
        o.scope   = new Scope(null, this, null)
        code      = @compileWithDeclarations(o)
        code      = code.replace(TRAILING_WHITESPACE, '')
        if o.noWrap then code else "(function() {\n#{code}\n})();\n"

#

Compile the expressions body for the contents of a function, with declarations of all inner variables pushed up to the top.

      compileWithDeclarations: (o) ->
        code = @compileNode(o)
        code = "#{@tab}var #{o.scope.compiledAssignments()};\n#{code}"  if o.scope.hasAssignments(this)
        code = "#{@tab}var #{o.scope.compiledDeclarations()};\n#{code}" if not o.globals and o.scope.hasDeclarations(this)
        code

#

Compiles a single expression within the expressions body. If we need to return the result, and it's an expression, simply return it. If it's a statement, ask the statement to do so.

      compileExpression: (node, o) ->
        @tab = o.indent
        compiledNode = node.compile merge o, top: true
        if node.isStatement(o) then compiledNode else "#{@idt()}#{compiledNode};"

#

Wrap up the given nodes as an Expressions, unless it already happens to be one.

    Expressions.wrap = (nodes) ->
      return nodes[0] if nodes.length is 1 and nodes[0] instanceof Expressions
      new Expressions(nodes)

#

### LiteralNode

#

Literals are static values that can be passed through directly into JavaScript without translation, such as: strings, numbers, `true`, `false`, `null`...

    exports.LiteralNode = class LiteralNode extends BaseNode

      class: 'LiteralNode'

      constructor: (@value) ->
        super()

      makeReturn: ->
        if @isStatement() then this else super()

#

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'
      isPureStatement: LiteralNode::isStatement

      compileNode: (o) ->
        idt = if @isStatement(o) then @idt() else ''
        end = if @isStatement(o) then ';' else ''
        idt + @value + end

      toString: (idt) ->
        '"' + @value + '"'

#

### ReturnNode

#

A `return` is a pureStatement -- wrapping it in a closure wouldn't make sense.

    exports.ReturnNode = class ReturnNode extends BaseNode

      class:            'ReturnNode'
      isStatement:      -> yes
      isPureStatement:  -> yes
      children:         ['expression']

      constructor: (@expression) ->
        super()

      makeReturn: ->
        this

      compile: (o) ->
        expr = @expression.makeReturn()
        return expr.compile o unless expr instanceof ReturnNode
        super o

      compileNode: (o) ->
        o.asStatement = true if @expression.isStatement(o)
        "#{@tab}return #{@expression.compile(o)};"

#

### ValueNode

#

A value, variable or literal or parenthesized, indexed or dotted into, or vanilla.

    exports.ValueNode = class ValueNode extends BaseNode

      class:     'ValueNode'
      children: ['base', 'properties']

#

A ValueNode has a base and a list of property accesses.

      constructor: (@base, @properties) ->
        super()
        @properties or= []

#

Add a property access to the list.

      push: (prop) ->
        @properties.push(prop)
        this

      hasProperties: ->
        !!@properties.length

#

Some boolean checks for the benefit of other nodes.

      isArray: ->
        @base instanceof ArrayNode and not @hasProperties()

      isObject: ->
        @base instanceof ObjectNode and not @hasProperties()

      isSplice: ->
        @hasProperties() and @properties[@properties.length - 1] instanceof SliceNode

      makeReturn: ->
        if @hasProperties() then super() else @base.makeReturn()

#

The value can be unwrapped as its inner node, if there are no attached properties.

      unwrap: ->
        if @properties.length then this else @base

#

Values are considered to be statements if their base is a statement.

      isStatement: (o) ->
        @base.isStatement and @base.isStatement(o) and not @hasProperties()

      isNumber: ->
        @base instanceof LiteralNode and @base.value.match NUMBER

#

If the value node has indexes containing function calls, and the value node needs to be used twice, in compound assignment ... then we need to cache the value of the indexes.

      cacheIndexes: (o) ->
        copy = new ValueNode @base, @properties[0..]
        if @base instanceof CallNode
          [@base, copy.base] = @base.compileReference o
        for prop, i in copy.properties
          if prop instanceof IndexNode and prop.contains((n) -> n instanceof CallNode)
            [index, indexVar] = prop.index.compileReference o
            this.properties[i] = new IndexNode index
            copy.properties[i] = new IndexNode indexVar
        [this, copy]

#

Override compile to unwrap the value when possible.

      compile: (o) ->
        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. Things get much more insteresting if the chain of properties has soak operators `?.` interspersed. Then we have to take care not to accidentally evaluate a anything twice when building the soak chain.

      compileNode: (o) ->
        only        = del o, 'onlyFirst'
        op          = @tags.operation
        props       = if only then @properties[0...@properties.length - 1] else @properties
        o.chainRoot or= this
        for prop in props
          hasSoak = yes if prop.soakNode
        if hasSoak and @containsType CallNode
          [me, copy] = @cacheIndexes o
        @base.parenthetical = yes if @parenthetical and not props.length
        baseline    = @base.compile o
        baseline    = "(#{baseline})" if @hasProperties() and (@base instanceof ObjectNode or @isNumber())
        complete    = @last = baseline

        for prop, i in props
          @source = baseline
          if prop.soakNode
            if @base.containsType(CallNode) and i is 0
              temp = o.scope.freeVariable()
              complete = "(#{ baseline = temp } = (#{complete}))"
            complete = if i is 0
              "(typeof #{complete} === \"undefined\" || #{baseline} === null) ? undefined : "
            else
              "#{complete} == null ? undefined : "
            complete += (baseline += prop.compile(o))
          else
            part = prop.compile(o)
            if hasSoak and prop.containsType CallNode
              baseline += copy.properties[i].compile o
            else
              baseline += part
            complete += part
            @last = part

        if op and @wrapped then "(#{complete})" else complete

#

### CommentNode

#

CoffeeScript passes through block comments as JavaScript block comments at the same position.

    exports.CommentNode = class CommentNode extends BaseNode

      class: 'CommentNode'
      isStatement: -> yes

      constructor: (@comment) ->
        super()

      makeReturn: ->
        this

      compileNode: (o) ->
        @tab + '/*' + @comment.replace(/\r?\n/g, '\n' + @tab) + '*/'

#

### CallNode

#

Node for a function invocation. Takes care of converting `super()` calls into calls against the prototype's function of the same name.

    exports.CallNode = class CallNode extends BaseNode

      class:     'CallNode'
      children: ['variable', 'args']

      constructor: (variable, @args, @exist) ->
        super()
        @isNew    = false
        @isSuper  = variable is 'super'
        @variable = if @isSuper then null else variable
        @args     or= []
        @first = @last = ''
        @compileSplatArguments = (o) ->
          SplatNode.compileSplattedArray.call(this, @args, o)

#

Tag this invocation as creating a new instance.

      newInstance: ->
        @isNew = true
        this

      prefix: ->
        if @isNew then 'new ' else ''

#

Grab the reference to the superclass' implementation of the current method.

      superReference: (o) ->
        throw new Error "cannot call super outside of a function" unless o.scope.method
        methname = o.scope.method.name
        meth = if o.scope.method.proto
          "#{o.scope.method.proto}.__super__.#{methname}"
        else if methname
          "#{methname}.__super__.constructor"
        else throw new Error "cannot call super on an anonymous function."

#

Compile a vanilla function call.

      compileNode: (o) ->
        o.chainRoot = this unless o.chainRoot
        op = @tags.operation
        if @exist
          [@first, @meth] = @variable.compileReference o, precompile: yes
          @first = "(typeof #{@first} === \"function\" ? "
          @last  = " : undefined)"
        else if @variable then @meth = @variable.compile o
        for arg in @args when arg instanceof SplatNode
          code = @compileSplat(o)
        if not code
          args = for arg in @args
            arg.parenthetical = true
            arg.compile o
          code = if @isSuper
            @compileSuper(args.join(', '), o)
          else
            "#{@first}#{@prefix()}#{@meth}(#{ args.join(', ') })#{@last}"
        if op and @variable and @variable.wrapped then "(#{code})" else code

#

`super()` is converted into a call against the superclass's implementation of the current function.

      compileSuper: (args, o) ->
        "#{@superReference(o)}.call(this#{ if args.length then ', ' else '' }#{args})"

#

If you call a function with a splat, it's converted into a JavaScript `.apply()` call to allow an array of arguments to be passed. If it's a constructor, then things get real tricky. We have to inject an inner constructor in order to be able to pass the varargs.

      compileSplat: (o) ->
        meth = @meth or @superReference(o)
        obj  = @variable and @variable.source or 'this'
        if obj.match(/\(/)
          temp = o.scope.freeVariable()
          obj  = temp
          meth = "(#{temp} = #{ @variable.source })#{ @variable.last }"
        if @isNew
          mentionsArgs = no
          for arg in @args
            arg.contains (n) -> mentionsArgs or= n instanceof LiteralNode and (n.value is 'arguments')
          utility 'extends'
          a = o.scope.freeVariable()
          b = o.scope.freeVariable()
          c = o.scope.freeVariable()
          """

    #DIVIDER
          """
        else
          "#{@first}#{@prefix()}#{meth}.apply(#{obj}, #{ @compileSplatArguments(o) })#{@last}"

#

{@first}(function() { {@idt(1)}var ctor = function(){}; {@idt(1)}__extends(ctor, #{a} = #{meth}); {@idt(1)}return typeof (#{b} = #{a}.apply(#{c} = new ctor, #{ @compileSplatArguments(o) })) === "object" ? #{b} : #{c}; {@tab}}).#{ if mentionsArgs then 'apply(this, arguments)' else 'call(this)'}#{@last}

#

### ExtendsNode

    exports.ExtendsNode = class ExtendsNode extends BaseNode

      class:     'ExtendsNode'
      children: ['child', 'parent']

      constructor: (@child, @parent) ->
        super()

#

Node to extend an object's prototype with an ancestor object. After `goog.inherits` from the Closure Library.

      compileNode: (o) ->
        ref =  new ValueNode literal utility 'extends'
        (new CallNode ref, [@child, @parent]).compile o

#

Hooks one constructor into another's prototype chain.

#

### AccessorNode

    exports.AccessorNode = class AccessorNode extends BaseNode

      class:     'AccessorNode'
      children: ['name']

      constructor: (@name, tag) ->
        super()
        @prototype = if tag is 'prototype' then '.prototype' else ''
        @soakNode = tag is 'soak'

      compileNode: (o) ->
        name = @name.compile o
        o.chainRoot.wrapped or= @soakNode
        namePart = if name.match(IS_STRING) then "[#{name}]" else ".#{name}"
        @prototype + namePart

#

A `.` accessor into a property of a value, or the `::` shorthand for an accessor into the object's prototype.

#

### IndexNode

    exports.IndexNode = class IndexNode extends BaseNode

      class:     'IndexNode'
      children: ['index']

      constructor: (@index) ->
        super()

      compileNode: (o) ->
        o.chainRoot.wrapped or= @soakNode
        idx = @index.compile o
        prefix = if @proto then '.prototype' else ''
        "#{prefix}[#{idx}]"

#

A `[ ... ]` indexed accessor into an array or object.

#

### RangeNode

    exports.RangeNode = class RangeNode extends BaseNode

      class:     'RangeNode'
      children: ['from', 'to']

      constructor: (@from, @to, tag) ->
        super()
        @exclusive = tag is 'exclusive'
        @equals = if @exclusive then '' else '='

#

A range literal. Ranges can be used to extract portions (slices) of arrays, to specify a range for comprehensions, or as a value, to be expanded into the corresponding array of integers at runtime.

      compileVariables: (o) ->
        o = merge(o, top: true)
        [@from, @fromVar] =  @from.compileReference o, precompile: yes
        [@to, @toVar] =      @to.compileReference o, precompile: yes
        [@fromNum, @toNum] = [@fromVar.match(SIMPLENUM), @toVar.match(SIMPLENUM)]
        parts = []
        parts.push @from if @from isnt @fromVar
        parts.push @to if @to isnt @toVar
        if parts.length then "#{parts.join('; ')}; " else ''

#

Compiles the range's source variables -- where it starts and where it ends. But only if they need to be cached to avoid double evaluation.

      compileNode: (o) ->
        return    @compileArray(o)  unless o.index
        return    @compileSimple(o) if @fromNum and @toNum
        idx      = del o, 'index'
        step     = del o, 'step'
        vars     = "#{idx} = #{@fromVar}"
        intro    = "(#{@fromVar} <= #{@toVar} ? #{idx}"
        compare  = "#{intro} <#{@equals} #{@toVar} : #{idx} >#{@equals} #{@toVar})"
        stepPart = if step then step.compile(o) else '1'
        incr     = if step then "#{idx} += #{stepPart}" else "#{intro} += #{stepPart} : #{idx} -= #{stepPart})"
        "#{vars}; #{compare}; #{incr}"

#

When compiled normally, the range returns the contents of the for loop needed to iterate over the values in the range. Used by comprehensions.

      compileSimple: (o) ->
        [from, to] = [parseInt(@fromNum, 10), parseInt(@toNum, 10)]
        idx        = del o, 'index'
        step       = del o, 'step'
        step       and= "#{idx} += #{step.compile(o)}"
        if from <= to
          "#{idx} = #{from}; #{idx} <#{@equals} #{to}; #{step or "#{idx}++"}"
        else
          "#{idx} = #{from}; #{idx} >#{@equals} #{to}; #{step or "#{idx}--"}"

#

Compile a simple range comprehension, with integers.

      compileArray: (o) ->
        idt    = @idt 1
        vars   = @compileVariables merge o, indent: idt
        if @fromNum and @toNum and Math.abs(+@fromNum - +@toNum) <= 20
          range = [+@fromNum..+@toNum]
          range.pop() if @exclusive
          return "[#{ range.join(', ') }]"
        i = o.scope.freeVariable()
        result = o.scope.freeVariable()
        pre    = "\n#{idt}#{result} = []; #{vars}"
        if @fromNum and @toNum
          o.index = i
          body = @compileSimple o
        else
          clause = "#{@fromVar} <= #{@toVar} ?"
          body   = "var #{i} = #{@fromVar}; #{clause} #{i} <#{@equals} #{@toVar} : #{i} >#{@equals} #{@toVar}; #{clause} #{i} += 1 : #{i} -= 1"
        post   = "{ #{result}.push(#{i}); }\n#{idt}return #{result};\n#{o.indent}"
        "(function() {#{pre}\n#{idt}for (#{body})#{post}}).call(this)"

#

When used as a value, expand the range into the equivalent array.

#

### SliceNode

    exports.SliceNode = class SliceNode extends BaseNode

      class:     'SliceNode'
      children: ['range']

      constructor: (@range) ->
        super()

      compileNode: (o) ->
        from  =  if @range.from then @range.from.compile(o) else '0'
        to    =  if @range.to then @range.to.compile(o) else ''
        to    += if not to or @range.exclusive then '' else ' + 1'
        to    =  ', ' + to if to
        ".slice(#{from}#{to})"

#

An array slice literal. Unlike JavaScript's `Array#slice`, the second parameter specifies the index of the end of the slice, just as the first parameter is the index of the beginning.

#

### ObjectNode

    exports.ObjectNode = class ObjectNode extends BaseNode

      class:     'ObjectNode'
      children: ['properties']

      topSensitive: -> true

      constructor: (props) ->
        super()
        @objects = @properties = props or []

      compileNode: (o) ->
        top = del o, 'top'
        o.indent = @idt 1
        nonComments = prop for prop in @properties when (prop not instanceof CommentNode)
        lastNoncom =  nonComments[nonComments.length - 1]
        props = for prop, i in @properties
          join   = ",\n"
          join   = "\n" if (prop is lastNoncom) or (prop instanceof CommentNode)
          join   = '' if i is @properties.length - 1
          indent = if prop instanceof CommentNode then '' else @idt 1
          prop   = new AssignNode prop, prop, 'object' unless prop instanceof AssignNode or prop instanceof CommentNode
          indent + prop.compile(o) + join
        props = props.join('')
        obj   = '{' + (if props then '\n' + props + '\n' + @idt() else '') + '}'
        if top then "(#{obj})" else obj

#

An object literal, nothing fancy.

#

### ArrayNode

    exports.ArrayNode = class ArrayNode extends BaseNode

      class:     'ArrayNode'
      children: ['objects']

      constructor: (@objects) ->
        super()
        @objects or= []
        @compileSplatLiteral = (o) ->
          SplatNode.compileSplattedArray.call(this, @objects, o)

      compileNode: (o) ->
        o.indent = @idt 1
        objects = []
        for obj, i in @objects
          code = obj.compile(o)
          if obj instanceof SplatNode
            return @compileSplatLiteral o
          else if obj instanceof CommentNode
            objects.push "\n#{code}\n#{o.indent}"
          else if i is @objects.length - 1
            objects.push code
          else
            objects.push "#{code}, "
        objects = objects.join('')
        if indexOf(objects, '\n') >= 0
          "[\n#{@idt(1)}#{objects}\n#{@tab}]"
        else
          "[#{objects}]"

#

An array literal.

#

### ClassNode

    exports.ClassNode = class ClassNode extends BaseNode

      class:        'ClassNode'
      children:     ['variable', 'parent', 'properties']
      isStatement:  -> yes

#

The CoffeeScript class definition.

      constructor: (@variable, @parent, @properties) ->
        super()
        @properties or= []
        @returns    = false

      makeReturn: ->
        @returns = true
        this

#

Initialize a ClassNode with its name, an optional superclass, and a list of prototype property assignments.

      compileNode: (o) ->
        @variable  = literal o.scope.freeVariable() if @variable is '__temp__'
        extension  = @parent and new ExtendsNode(@variable, @parent)
        props      = new Expressions
        o.top      = true
        me         = null
        className  = @variable.compile o
        constScope = null

        if @parent
          applied = new ValueNode(@parent, [new AccessorNode(literal('apply'))])
          constructor = new CodeNode([], new Expressions([
            new CallNode(applied, [literal('this'), literal('arguments')])
          ]))
        else
          constructor = new CodeNode

        for prop in @properties
          [pvar, func] = [prop.variable, prop.value]
          if pvar and pvar.base.value is 'constructor' and func instanceof CodeNode
            throw new Error "cannot define a constructor as a bound function." if func.bound
            func.name = className
            func.body.push new ReturnNode literal 'this'
            @variable = new ValueNode @variable
            @variable.namespaced = include func.name, '.'
            constructor = func
            continue
          if func instanceof CodeNode and func.bound
            if prop.context is 'this'
              func.context = className
            else
              func.bound = false
              constScope or= new Scope(o.scope, constructor.body, constructor)
              me or= constScope.freeVariable()
              pname = pvar.compile(o)
              constructor.body.push    new ReturnNode literal 'this' if constructor.body.empty()
              constructor.body.unshift 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 AccessorNode(pvar, 'prototype')
            val    = new ValueNode(@variable, [access])
            prop   = new AssignNode(val, func)
          props.push prop

        constructor.body.unshift literal "#{me} = this" if me
        construct = @idt() + (new AssignNode(@variable, constructor)).compile(merge o, {sharedScope: constScope}) + ';'
        props     = if !props.empty() then '\n' + props.compile(o)                     else ''
        extension = if extension      then '\n' + @idt() + extension.compile(o) + ';'  else ''
        returns   = if @returns       then '\n' + new ReturnNode(@variable).compile(o) else ''
        construct + extension + props + returns

#

Instead of generating the JavaScript string directly, we build up the equivalent syntax tree and compile that, in pieces. You can see the constructor, property assignments, and inheritance getting built out below.

#

### AssignNode

    exports.AssignNode = class AssignNode extends BaseNode

#

The AssignNode is used to assign a local variable to value, or to set the property of an object -- including within object literals.

      PROTO_ASSIGN: /^(\S+)\.prototype/
      LEADING_DOT:  /^\.(prototype\.)?/

      class:     'AssignNode'
      children: ['variable', 'value']

      constructor: (@variable, @value, @context) ->
        super()

      topSensitive: ->
        true

      isValue: ->
        @variable instanceof ValueNode

      makeReturn: ->
        if @isStatement()
          return new Expressions [this, new ReturnNode(@variable)]
        else
          super()

      isStatement: ->
        @isValue() and (@variable.isArray() or @variable.isObject())

#

Matchers for detecting prototype assignments.

      compileNode: (o) ->
        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)
        last   = if @isValue() then @variable.last.replace(@LEADING_DOT, '') else name
        match  = name.match(@PROTO_ASSIGN)
        proto  = match and match[1]
        if @value instanceof CodeNode
          @value.name =  last  if last.match(IDENTIFIER)
          @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)
        val = "#{name} = #{val}"
        return "#{@tab}#{val};" if stmt
        if top or @parenthetical then val else "(#{val})"

#

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.

      compilePatternMatch: (o) ->
        valVar        = o.scope.freeVariable()
        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

#

Brief implementation of recursive pattern matching, when assigning array or object literals to a value. Peeks at their properties to assign inner names. See the ECMAScript Harmony Wiki for details.

          idx = i
          if @variable.isObject()
            if obj instanceof AssignNode

#

A regular array pattern-match.

              [obj, idx] = [obj.value, obj.variable.base]
            else

#

A regular object pattern-match.

              idx = obj
          if not (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
            val = literal obj.compileValue o, valVar,
              (oindex = indexOf(@variable.base.objects, obj)),
              (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'
            val = new ValueNode(literal(valVar), [new accessClass(idx)])
          assigns.push(new AssignNode(obj, val).compile(o))
        code = assigns.join("\n")
        code

#

A shorthand `{a, b, c} = val` pattern-match.

      compileSplice: (o) ->
        name  = @variable.compile merge o, onlyFirst: true
        l     = @variable.properties.length
        range = @variable.properties[l - 1].range
        plus  = if range.exclusive then '' else ' + 1'
        from  = if range.from then range.from.compile(o) else '0'
        to    = if range.to then range.to.compile(o) + ' - ' + from + plus else "#{name}.length"
        val   = @value.compile(o)
        "#{name}.splice.apply(#{name}, [#{from}, #{to}].concat(#{val}))"

#

Compile the assignment from an array splice literal, using JavaScript's `Array#splice` method.

#

### CodeNode

    exports.CodeNode = class CodeNode extends BaseNode

      class:     'CodeNode'
      children: ['params', 'body']

      constructor: (@params, @body, tag) ->
        super()
        @params   or= []
        @body     or= new Expressions
        @bound    = tag is 'boundfunc'
        @context  = 'this' if @bound

#

A function definition. This is the only node that creates a new Scope. When for the purposes of walking the contents of a function body, the CodeNode has no children -- they're within the inner scope.

      compileNode: (o) ->
        sharedScope = del o, 'sharedScope'
        top         = del o, 'top'
        o.scope     = sharedScope or new Scope(o.scope, @body, this)
        o.top       = true
        o.indent    = @idt(1)
        empty       = @body.expressions.length is 0
        del o, 'noWrap'
        del o, 'globals'
        splat = undefined
        params = []
        for param, i in @params
          if splat
            if param.attach
              param.assign = new AssignNode new ValueNode literal('this'), [new AccessorNode param.value]
              @body.expressions.splice splat.index + 1, 0, param.assign
            splat.trailings.push param
          else
            if param.attach
              {value} = param
              [param, param.splat] = [literal(o.scope.freeVariable()), param.splat]
              @body.unshift new AssignNode new ValueNode(literal('this'), [new AccessorNode value]), param
            if param.splat
              splat           = new SplatNode param.value
              splat.index     = i
              splat.trailings = []
              splat.arglength = @params.length
              @body.unshift(splat)
            else
              params.push param
        params = (param.compile(o) for param in params)
        @body.makeReturn() unless empty
        (o.scope.parameter(param)) for param in params
        code = if @body.expressions.length then "\n#{ @body.compileWithDeclarations(o) }\n" else ''
        func = "function(#{ params.join(', ') }) {#{code}#{ code and @tab }}"
        return "#{utility('bind')}(#{func}, #{@context})" if @bound
        if top then "(#{func})" else func

      topSensitive: ->
        true

#

Compilation creates a new scope unless explicitly asked to share with the outer scope. Handles splat parameters in the parameter list by peeking at the JavaScript `arguments` objects. If the function is bound with the `=>` arrow, generates a wrapper that saves the current value of `this` through a closure.

      traverseChildren: (crossScope, func) -> super(crossScope, func) if crossScope

      toString: (idt) ->
        idt or= ''
        children = (child.toString(idt + TAB) for child in @collectChildren()).join('')
        '\n' + idt + children

#

Short-circuit traverseChildren method to prevent it from crossing scope boundaries unless crossScope is true

#

### ParamNode

    exports.ParamNode = class ParamNode extends BaseNode

      class:    'ParamNode'
      children: ['name']

      constructor: (@name, @attach, @splat) ->
        super()
        @value = literal @name

      compileNode: (o) ->
        @value.compile o

      toString: (idt) ->
        if @attach then (literal '@' + @name).toString idt else @value.toString idt

#

A parameter in a function definition. Beyond a typical Javascript parameter, these parameters can also attach themselves to the context of the function, as well as be a splat, gathering up a group of parameters into an array.

#

### SplatNode

    exports.SplatNode = class SplatNode extends BaseNode

      class:     'SplatNode'
      children: ['name']

      constructor: (name) ->
        super()
        name = literal(name) unless name.compile
        @name = name

      compileNode: (o) ->
        if @index? then @compileParam(o) else @name.compile(o)

#

A splat, either as a parameter to a function, an argument to a call, or as part of a destructuring assignment.

      compileParam: (o) ->
        name = @name.compile(o)
        o.scope.find name
        end = ''
        if @trailings.length
          len = o.scope.freeVariable()
          o.scope.assign len, "arguments.length"
          variadic = o.scope.freeVariable()
          o.scope.assign variadic, len + ' >= ' + @arglength
          end = if @trailings.length then ", #{len} - #{@trailings.length}"
          for trailing, idx in @trailings
            if trailing.attach
              assign        = trailing.assign
              trailing      = literal o.scope.freeVariable()
              assign.value  = trailing
            pos = @trailings.length - idx
            o.scope.assign(trailing.compile(o), "arguments[#{variadic} ? #{len} - #{pos} : #{@index + idx}]")
        "#{name} = #{utility('slice')}.call(arguments, #{@index}#{end})"

#

Compiling a parameter splat means recovering the parameters that succeed the splat in the parameter list, by slicing the arguments object.

      compileValue: (o, name, index, trailings) ->
        trail = if trailings then ", #{name}.length - #{trailings}" else ''
        "#{utility 'slice'}.call(#{name}, #{index}#{trail})"

#

A compiling a splat as a destructuring assignment means slicing arguments from the right-hand-side's corresponding array.

      @compileSplattedArray: (list, o) ->
        args = []
        for arg, i in list
          code = arg.compile o
          prev = args[last = args.length - 1]
          if arg not instanceof SplatNode
            if prev and starts(prev, '[') and ends(prev, ']')
              args[last] = "#{prev.substr(0, prev.length - 1)}, #{code}]"
              continue
            else if prev and starts(prev, '.concat([') and ends(prev, '])')
              args[last] = "#{prev.substr(0, prev.length - 2)}, #{code}])"
              continue
            else
              code = "[#{code}]"
          args.push(if i is 0 then code else ".concat(#{code})")
        args.join('')

#

Utility function that converts arbitrary number of elements, mixed with splats, to a proper array

#

### WhileNode

    exports.WhileNode = class WhileNode extends BaseNode

      class:         'WhileNode'
      children:     ['condition', 'guard', 'body']
      isStatement: -> yes

      constructor: (condition, opts) ->
        super()
        if opts and opts.invert
          condition = new ParentheticalNode condition if condition instanceof OpNode
          condition = new OpNode('!', condition)
        @condition  = condition
        @guard = opts and opts.guard

      addBody: (body) ->
        @body = body
        this

      makeReturn: ->
        @returns = true
        this

      topSensitive: ->
        true

#

A while loop, the only sort of low-level loop exposed by CoffeeScript. From it, all other loops can be manufactured. Useful in cases where you need more flexibility or more speed than a comprehension can provide.

      compileNode: (o) ->
        top      =  del(o, 'top') and not @returns
        o.indent =  @idt 1
        o.top    =  true
        @condition.parenthetical = yes
        cond     =  @condition.compile(o)
        set      =  ''
        unless top
          rvar  = o.scope.freeVariable()
          set   = "#{@tab}#{rvar} = [];\n"
          @body = PushNode.wrap(rvar, @body) if @body
        pre     = "#{set}#{@tab}while (#{cond})"
        @body   = Expressions.wrap([new IfNode(@guard, @body)]) if @guard
        if @returns
          post = '\n' + new ReturnNode(literal(rvar)).compile(merge(o, indent: @idt()))
        else
          post = ''
        "#{pre} {\n#{ @body.compile(o) }\n#{@tab}}#{post}"

#

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.

#

### OpNode

    exports.OpNode = class OpNode extends BaseNode

#

Simple Arithmetic and logical operations. Performs some conversion from CoffeeScript operations into their JavaScript equivalents.

      CONVERSIONS:
        '==': '==='
        '!=': '!=='

#

The map of conversions from CoffeeScript to JavaScript symbols.

      INVERSIONS:
        '!==': '==='
        '===': '!=='

#

The map of invertible operators.

      CHAINABLE:        ['<', '>', '>=', '<=', '===', '!==']

#

The list of operators for which we perform Python-style comparison chaining.

      ASSIGNMENT:       ['||=', '&&=', '?=']

#

Our assignment operators that have no JavaScript equivalent.

      PREFIX_OPERATORS: ['typeof', 'delete']

      class:     'OpNode'
      children: ['first', 'second']

      constructor: (@operator, @first, @second, flip) ->
        super()
        @operator = @CONVERSIONS[@operator] or @operator
        @flip     = !!flip
        if @first instanceof ValueNode and @first.base instanceof ObjectNode
          @first = new ParentheticalNode @first
        @first.tags.operation = yes
        @second.tags.operation = yes if @second

      isUnary: ->
        not @second

      isInvertible: ->
        (@operator in ['===', '!==']) and
          not (@first instanceof OpNode) and not (@second instanceof OpNode)

      isMutator: ->
        ends(@operator, '=') and not (@operator in ['===', '!=='])

      isChainable: ->
        include(@CHAINABLE, @operator)

      invert: ->
        @operator = @INVERSIONS[@operator]

      toString: (idt) ->
        super(idt, @class + ' ' + @operator)

      compileNode: (o) ->
        return @compileChain(o)      if @isChainable() and @first.unwrap() instanceof OpNode and @first.unwrap().isChainable()
        return @compileAssignment(o) if indexOf(@ASSIGNMENT, @operator) >= 0
        return @compileUnary(o)      if @isUnary()
        return @compileExistence(o)  if @operator is '?'
        @first  = new ParentheticalNode(@first)  if @first instanceof OpNode and @first.isMutator()
        @second = new ParentheticalNode(@second) if @second instanceof OpNode and @second.isMutator()
        [@first.compile(o), @operator, @second.compile(o)].join ' '

#

Operators must come before their operands with a space.

      compileChain: (o) ->
        shared = @first.unwrap().second
        [@first.second, shared] = shared.compileReference(o) if shared.containsType CallNode
        [first, second, shared] = [@first.compile(o), @second.compile(o), shared.compile(o)]
        "(#{first}) && (#{shared} #{@operator} #{second})"

#

Mimic Python's chained comparisons when multiple comparison operators are used sequentially. For example:

    bin/coffee -e "puts 50 < 65 > 10"
    true

      compileAssignment: (o) ->
        [first, firstVar] = @first.compileReference o, precompile: yes, assignment: yes
        second = @second.compile o
        second = "(#{second})" if @second instanceof OpNode
        o.scope.find(first) if first.match(IDENTIFIER)
        return "#{first} = #{ ExistenceNode.compileTest(o, literal(firstVar))[0] } ? #{firstVar} : #{second}" if @operator is '?='
        "#{first} #{ @operator.substr(0, 2) } (#{firstVar} = #{second})"

#

When compiling a conditional assignment, take care to ensure that the operands are only evaluated once, even though we have to reference them more than once.

      compileExistence: (o) ->
        [test, ref] = ExistenceNode.compileTest(o, @first)
        "#{test} ? #{ref} : #{ @second.compile(o) }"

#

If this is an existence operator, we delegate to `ExistenceNode.compileTest` to give us the safe references for the variables.

      compileUnary: (o) ->
        space = if indexOf(@PREFIX_OPERATORS, @operator) >= 0 then ' ' else ''
        parts = [@operator, space, @first.compile(o)]
        parts = parts.reverse() if @flip
        parts.join('')

#

Compile a unary OpNode.

    exports.InNode = class InNode extends BaseNode

      class:    'InNode'
      children: ['object', 'array']

      constructor: (@object, @array) ->
        super()

      isArray: ->
        @array instanceof ValueNode and @array.isArray()

      compileNode: (o) ->
        [@obj1, @obj2] = @object.compileReference o, precompile: yes
        if @isArray() then @compileOrTest(o) else @compileLoopTest(o)

      compileOrTest: (o) ->
        tests = for item, i in @array.base.objects
          "#{item.compile(o)} === #{if i then @obj2 else @obj1}"
        "(#{tests.join(' || ')})"

      compileLoopTest: (o) ->
        [@arr1, @arr2] = @array.compileReference o, precompile: yes
        [i, l] = [o.scope.freeVariable(), o.scope.freeVariable()]
        prefix = if @obj1 isnt @obj2 then @obj1 + '; ' else ''
        "(function(){ #{prefix}for (var #{i}=0, #{l}=#{@arr1}.length; #{i}<#{l}; #{i}++) { if (#{@arr2}[#{i}] === #{@obj2}) return true; } return false; }).call(this)"

#

### InNode

#

### TryNode

    exports.TryNode = class TryNode extends BaseNode

      class:        'TryNode'
      children:     ['attempt', 'recovery', 'ensure']
      isStatement:  -> yes

      constructor: (@attempt, @error, @recovery, @ensure) ->
        super()

      makeReturn: ->
        @attempt  = @attempt.makeReturn() if @attempt
        @recovery = @recovery.makeReturn() if @recovery
        this

#

A classic try/catch/finally block.

      compileNode: (o) ->
        o.indent    = @idt 1
        o.top       = true
        attemptPart = @attempt.compile(o)
        errorPart   = if @error then " (#{ @error.compile(o) }) " else ' '
        catchPart   = if @recovery then " catch#{errorPart}{\n#{ @recovery.compile(o) }\n#{@tab}}" else ''
        finallyPart = (@ensure or '') and ' finally {\n' + @ensure.compile(merge(o)) + "\n#{@tab}}"
        "#{@tab}try {\n#{attemptPart}\n#{@tab}}#{catchPart}#{finallyPart}"

#

Compilation is more or less as you would expect -- the finally clause is optional, the catch is not.

#

### ThrowNode

    exports.ThrowNode = class ThrowNode extends BaseNode

      class:         'ThrowNode'
      children:     ['expression']
      isStatement: -> yes

      constructor: (@expression) ->
        super()

#

Simple node to throw an exception.

      makeReturn: ->
        return this

      compileNode: (o) ->
        "#{@tab}throw #{@expression.compile(o)};"

#

A ThrowNode is already a return, of sorts...

#

### ExistenceNode

    exports.ExistenceNode = class ExistenceNode extends BaseNode

      class:     'ExistenceNode'
      children: ['expression']

      constructor: (@expression) ->
        super()

      compileNode: (o) ->
        test = ExistenceNode.compileTest(o, @expression)[0]
        if @parenthetical then test.substring(1, test.length - 1) else test

#

Checks a variable for existence -- not null and not undefined. This is similar to `.nil?` in Ruby, and avoids having to consult a JavaScript truth table.

      @compileTest: (o, variable) ->
        [first, second] = variable.compileReference o, precompile: yes
        ["(typeof #{first} !== \"undefined\" && #{second} !== null)", second]

#

The meat of the ExistenceNode is in this static `compileTest` method because other nodes like to check the existence of their variables as well. Be careful not to double-evaluate anything.

#

### ParentheticalNode

    exports.ParentheticalNode = class ParentheticalNode extends BaseNode

      class:     'ParentheticalNode'
      children: ['expression']

      constructor: (@expression) ->
        super()

      isStatement: (o) ->
        @expression.isStatement(o)

      makeReturn: ->
        @expression.makeReturn()

      topSensitive: ->
        yes

      compileNode: (o) ->
        top  = del o, 'top'
        @expression.parenthetical = true
        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
        "(#{code})"

#

An extra set of parentheses, specified explicitly in the source. At one time we tried to clean up the results by detecting and removing redundant parentheses, but no longer -- you can put in as many as you please.

Parentheses are a good way to force any statement to become an expression.

#

### ForNode

    exports.ForNode = class ForNode extends BaseNode

      class:         'ForNode'
      children:     ['body', 'source', 'guard']
      isStatement: -> yes

      constructor: (@body, source, @name, @index) ->
        super()
        @index  or= null
        @source = source.source
        @guard  = source.guard
        @step   = source.step
        @raw    = !!source.raw
        @object = !!source.object
        [@name, @index] = [@index, @name] if @object
        @pattern = @name instanceof ValueNode
        throw new Error('index cannot be a pattern matching expression') if @index instanceof ValueNode
        @returns = false

      topSensitive: ->
        true

      makeReturn: ->
        @returns = true
        this

      compileReturnValue: (val, o) ->
        return '\n' + new ReturnNode(literal(val)).compile(o) if @returns
        return '\n' + val if val
        ''

#

CoffeeScript's replacement for the for loop is our array and object comprehensions, that compile into for loops here. They also act as an expression, able to return the result of each filtered iteration.

Unlike Python array comprehensions, they can be multi-line, and you can pass the current index of the loop as a second parameter. Unlike Ruby blocks, you can map and filter in a single pass.

      compileNode: (o) ->
        topLevel      = del(o, 'top') and not @returns
        range         = @source instanceof ValueNode and @source.base instanceof RangeNode and not @source.properties.length
        source        = if range then @source.base else @source
        codeInBody    = @body.contains (n) -> n instanceof CodeNode
        scope         = o.scope
        name          = (@name and @name.compile(o)) or scope.freeVariable()
        index         = @index and @index.compile o
        scope.find(name,  immediate: yes) if name and not @pattern and (range or not codeInBody)
        scope.find(index, immediate: yes) if index
        rvar          = scope.freeVariable() unless topLevel
        ivar          = if codeInBody then scope.freeVariable() else if range then name else index or scope.freeVariable()
        varPart       = ''
        guardPart     = ''
        body          = Expressions.wrap([@body])
        if range
          sourcePart  = source.compileVariables(o)
          forPart     = source.compile merge o, index: ivar, step: @step
        else
          svar        = scope.freeVariable()
          sourcePart  = "#{svar} = #{ @source.compile(o) };"
          if @pattern
            namePart  = new AssignNode(@name, literal("#{svar}[#{ivar}]")).compile(merge o, {indent: @idt(1), top: true}) + '\n'
          else
            namePart  = "#{name} = #{svar}[#{ivar}]" if name
          unless @object
            lvar      = scope.freeVariable()
            stepPart  = if @step then "#{ivar} += #{ @step.compile(o) }" else "#{ivar}++"
            forPart   = "#{ivar} = 0, #{lvar} = #{svar}.length; #{ivar} < #{lvar}; #{stepPart}"
        sourcePart    = (if rvar then "#{rvar} = []; " else '') + sourcePart
        sourcePart    = if sourcePart then "#{@tab}#{sourcePart}\n#{@tab}" else @tab
        returnResult  = @compileReturnValue(rvar, o)

        body          = PushNode.wrap(rvar, body) unless topLevel
        if @guard
          body        = Expressions.wrap([new IfNode(@guard, body)])
        if codeInBody
          body.unshift  literal "var #{name} = #{ivar}" if range
          body.unshift  literal "var #{namePart}" if namePart
          body.unshift  literal "var #{index} = #{ivar}" if index
          body        = ClosureNode.wrap(body, true)
        else
          varPart     = (namePart or '') and (if @pattern then namePart else "#{@idt(1)}#{namePart};\n")
        if @object
          forPart     = "#{ivar} in #{svar}"
          guardPart   = "\n#{@idt(1)}if (!#{utility('hasProp')}.call(#{svar}, #{ivar})) continue;" unless @raw
        body          = body.compile(merge(o, {indent: @idt(1), top: true}))
        vars          = if range then name else "#{name}, #{ivar}"
        "#{sourcePart}for (#{forPart}) {#{guardPart}\n#{varPart}#{body}\n#{@tab}}#{returnResult}"

#

Welcome to the hairiest method in all of CoffeeScript. Handles the inner loop, filtering, stepping, and result saving for array, object, and range comprehensions. Some of the generated code can be shared in common, and some cannot.

#

### SwitchNode

    exports.SwitchNode = class SwitchNode extends BaseNode

      class:     'SwitchNode'
      children: ['subject', 'cases', 'otherwise']

      isStatement: -> yes

      constructor: (@subject, @cases, @otherwise) ->
        super()
        @tags.subjectless = !@subject
        @subject or= literal 'true'

      makeReturn: ->
        pair[1].makeReturn() for pair in @cases
        @otherwise.makeReturn() if @otherwise
        this

      compileNode: (o) ->
        idt = o.indent = @idt 1
        o.top = yes
        code = "#{ @tab }switch (#{ @subject.compile o }) {"
        for pair in @cases
          [conditions, block] = pair
          exprs = block.expressions
          for condition in flatten [conditions]
            condition = new OpNode '!!', new ParentheticalNode condition if @tags.subjectless
            code += "\n#{ @tab }case #{ condition.compile o }:"
          code += "\n#{ block.compile o }"
          code += "\n#{ idt }break;" unless exprs[exprs.length - 1] instanceof ReturnNode
        if @otherwise
          code += "\n#{ @tab }default:\n#{ @otherwise.compile o }"
        code += "\n#{ @tab }}"
        code

#

A JavaScript switch statement. Converts into a returnable expression on-demand.

#

### IfNode

    exports.IfNode = class IfNode extends BaseNode

      class:     'IfNode'
      children: ['condition', 'body', 'elseBody', 'assigner']

      topSensitive: -> true

      constructor: (@condition, @body, @tags) ->
        @tags or= {}
        if @tags.invert
          if @condition instanceof OpNode and @condition.isInvertible()
            @condition.invert()
          else
            @condition = new OpNode '!', new ParentheticalNode @condition
        @elseBody = null
        @isChain  = false

      bodyNode: -> @body?.unwrap()
      elseBodyNode: -> @elseBody?.unwrap()

      forceStatement: ->
        @tags.statement = true
        this

#

If/else statements. Acts as an expression by pushing down requested returns to the last line of each clause.

Single-expression IfNodes are compiled into ternary operators if possible, because ternaries are already proper expressions, and don't need conversion.

      addElse: (elseBody, statement) ->
        if @isChain
          @elseBodyNode().addElse elseBody, statement
        else
          @isChain = elseBody instanceof IfNode
          @elseBody = @ensureExpressions elseBody
        this

#

Rewrite a chain of IfNodes to add a default case as the final else.

      isStatement: (o) ->
        @statement or= !!((o and o.top) or @tags.statement or @bodyNode().isStatement(o) or (@elseBody and @elseBodyNode().isStatement(o)))

      compileCondition: (o) ->
        conditions = flatten [@condition]
        conditions[0].parenthetical = yes if conditions.length is 1
        (cond.compile(o) for cond in conditions).join(' || ')

      compileNode: (o) ->
        if @isStatement(o) then @compileStatement(o) else @compileTernary(o)

      makeReturn: ->
        if @isStatement()
          @body     and= @ensureExpressions(@body.makeReturn())
          @elseBody and= @ensureExpressions(@elseBody.makeReturn())
          this
        else
          new ReturnNode this

      ensureExpressions: (node) ->
        if node instanceof Expressions then node else new Expressions [node]

#

The IfNode only compiles into a statement if either of its bodies needs to be a statement. Otherwise a ternary is safe.

      compileStatement: (o) ->
        top      = del o, 'top'
        child    = del o, 'chainChild'
        condO    = merge o
        o.indent = @idt 1
        o.top    = true
        ifDent   = if child or (top and not @isStatement(o)) then '' else @idt()
        comDent  = if child then @idt() else ''
        body     = @body.compile(o)
        ifPart   = "#{ifDent}if (#{ @compileCondition(condO) }) {\n#{body}\n#{@tab}}"
        return ifPart unless @elseBody
        elsePart = if @isChain
          ' else ' + @elseBodyNode().compile(merge(o, {indent: @idt(), chainChild: true}))
        else
          " else {\n#{ @elseBody.compile(o) }\n#{@tab}}"
        "#{ifPart}#{elsePart}"

#

Compile the IfNode as a regular if-else statement. Flattened chains force inner else bodies into statement form.

      compileTernary: (o) ->
        @bodyNode().tags.operation = @condition.tags.operation = yes
        @elseBodyNode().tags.operation = yes if @elseBody
        ifPart      = @condition.compile(o) + ' ? ' + @bodyNode().compile(o)
        elsePart    = if @elseBody then @elseBodyNode().compile(o) else 'null'
        code        = "#{ifPart} : #{elsePart}"
        if @tags.operation then "(#{code})" else code

#

Compile the IfNode as a ternary operator.

#

## Faux-Nodes

#

### PushNode

    PushNode = exports.PushNode =
      wrap: (array, expressions) ->
        expr = expressions.unwrap()
        return expressions if expr.isPureStatement() or expr.containsPureStatement()
        Expressions.wrap([new CallNode(
          new ValueNode(literal(array), [new AccessorNode(literal('push'))]), [expr]
        )])

#

Faux-nodes are never created by the grammar, but are used during code generation to generate other combinations of nodes. The PushNode creates the tree for `array.push(value)`, which is helpful for recording the result arrays from comprehensions.

#

### ClosureNode

    ClosureNode = exports.ClosureNode =

#

A faux-node used to wrap an expressions body in a closure.

      wrap: (expressions, statement) ->
        return expressions if expressions.containsPureStatement()
        func = new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions])))
        args = []
        mentionsArgs = expressions.contains (n) ->
          n instanceof LiteralNode and (n.value is 'arguments')
        mentionsThis = expressions.contains (n) ->
          (n instanceof LiteralNode and (n.value is 'this')) or
          (n instanceof CodeNode and n.bound)
        if mentionsArgs or mentionsThis
          meth = literal(if mentionsArgs then 'apply' else 'call')
          args = [literal('this')]
          args.push literal 'arguments' if mentionsArgs
          func = new ValueNode func, [new AccessorNode(meth)]
        call = new CallNode(func, args)
        if statement then Expressions.wrap([call]) else call

#

Wrap the expressions body, unless it contains a pure statement, in which case, no dice. If the body mentions `this` or `arguments`, then make sure that the closure wrapper preserves the original values.

    UTILITIES =

#

## Utility Functions

      extends:  """
                function(child, parent) {
                    var ctor = function(){};
                    ctor.prototype = parent.prototype;
                    child.prototype = new ctor();
                    child.prototype.constructor = child;
                    if (typeof parent.extended === "function") parent.extended(child);
                    child.__super__ = parent.prototype;
                  }
                """

#

Correctly set up a prototype chain for inheritance, including a reference to the superclass for `super()` calls. See: goog.inherits.

      bind: """
            function(func, context) {
                return function(){ return func.apply(context, arguments); };
              }
            """

#

Create a function bound to the current value of "this".

      hasProp: 'Object.prototype.hasOwnProperty'
      slice:   'Array.prototype.slice'

#

Shortcuts to speed up the lookup time for native functions.

#

## Constants

    TAB = '  '

#

Tabs are two spaces for pretty printing.

    TRAILING_WHITESPACE = /[ \t]+$/gm

#

Trim out all trailing whitespace, so that the generated code plays nice with Git.

    IDENTIFIER = /^[a-zA-Z\$_](\w|\$)*$/
    NUMBER     = /^(((\b0(x|X)[0-9a-fA-F]+)|((\b[0-9]+(\.[0-9]+)?|\.[0-9]+)(e[+\-]?[0-9]+)?)))\b$/i
    SIMPLENUM  = /^-?\d+$/

#

Keep these identifier regexes in sync with the Lexer.

    IS_STRING = /^['"]/

#

Is a literal value a string?

#

## Utility Functions

    literal = (name) ->
      new LiteralNode(name)

#

Handy helper for a generating LiteralNode.

    utility = (name) ->
      ref = "__#{name}"
      Scope.root.assign ref, UTILITIES[name]
      ref

#

Helper for ensuring that utility functions are assigned at the top level.

    undefined