From 12587d8295f3aee4d9c133805d231e46aba64c4f Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 31 May 2010 10:36:50 -0400 Subject: [PATCH] reworking Stan's nodes.coffee type/children work a bit for issue 386 --- lib/nodes.js | 249 ++++++++++++++++++----------------------------- src/nodes.coffee | 160 +++++++++++++++++++----------- 2 files changed, 200 insertions(+), 209 deletions(-) diff --git a/lib/nodes.js b/lib/nodes.js index 06ac9d94..84b1b865 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -44,30 +44,7 @@ // being requested by the surrounding function), information about the current // scope, and indentation level. exports.BaseNode = (function() { - BaseNode = function(o) { - o = o || {}; - if (o.name) { - this.constructor_name = o.name; - } - if (o.children) { - this.children_attributes = o.children; - } - if (o.is_statement) { - (this.is_statement = function() { - return true; - }); - } - if (o.is_pure_statement) { - (this.is_pure_statement = function() { - return true; - }); - } - return this; - }; - // If `is_statement` is passed, marks the node as a JavaScript *statement*, - // or as a *pure_statement* if `is_pure_statement`. Statements must be - // wrapped in a closure when used as an expression, and nodes tagged as - // *pure_statement* cannot be closure-wrapped without losing their meaning. + BaseNode = function() { }; // 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 *pure_statement*, and we're not at @@ -164,8 +141,8 @@ BaseNode.prototype.toString = function(idt) { var _b, _c, _d, _e, child; idt = idt || ''; - return '\n' + idt + this.constructor_name + (function() { - _b = []; _d = this.children(); + return '\n' + idt + this.type + (function() { + _b = []; _d = this.collect_children(); for (_c = 0, _e = _d.length; _c < _e; _c++) { child = _d[_c]; _b.push(child.toString(idt + TAB)); @@ -173,20 +150,12 @@ return _b; }).call(this).join(''); }; - BaseNode.prototype.children = function() { - var nodes; - nodes = []; - this.each_child(function(node) { - return nodes.push(node); - }); - return nodes; - }; BaseNode.prototype.each_child = function(func) { var _b, _c, _d, _e, _f, _g, attr, child; - if (!(this.children_attributes)) { + if (!(this.children)) { return null; } - _c = this.children_attributes; + _c = this.children; for (_b = 0, _d = _c.length; _b < _d; _b++) { attr = _c[_b]; if (this[attr]) { @@ -200,6 +169,14 @@ } } }; + BaseNode.prototype.collect_children = function() { + var nodes; + nodes = []; + this.each_child(function(node) { + return nodes.push(node); + }); + return nodes; + }; BaseNode.prototype.traverse_children = function(cross_scope, func) { return this.each_child(function(child) { func.apply(this, arguments); @@ -210,8 +187,8 @@ }; // Default implementations of the common node properties and methods. Nodes // will override these with custom logic, if needed. - BaseNode.prototype.constructor_name = 'BaseNode'; - BaseNode.prototype.children_attributes = []; + BaseNode.prototype.type = 'BaseNode'; + BaseNode.prototype.children = []; BaseNode.prototype.unwrap = function() { return this; }; @@ -232,15 +209,15 @@ // `if`, `switch`, or `try`, and so on... exports.Expressions = (function() { Expressions = function(nodes) { - Expressions.__superClass__.constructor.call(this, { - name: 'Expressions', - is_statement: true, - children: ['expressions'] - }); this.expressions = compact(flatten(nodes || [])); return this; }; __extends(Expressions, BaseNode); + Expressions.prototype.type = 'Expressions'; + Expressions.prototype.children = ['expressions']; + Expressions.prototype.is_statement = function() { + return true; + }; // Tack an expression on to the end of this expression list. Expressions.prototype.push = function(node) { this.expressions.push(node); @@ -359,13 +336,11 @@ // `true`, `false`, `null`... exports.LiteralNode = (function() { LiteralNode = function(value) { - LiteralNode.__superClass__.constructor.call(this, { - name: 'LiteralNode' - }); this.value = value; return this; }; __extends(LiteralNode, BaseNode); + LiteralNode.prototype.type = 'LiteralNode'; // Break and continue must be treated as pure statements -- they lose their // meaning when wrapped in a closure. LiteralNode.prototype.is_statement = function() { @@ -388,16 +363,18 @@ // make sense. exports.ReturnNode = (function() { ReturnNode = function(expression) { - ReturnNode.__superClass__.constructor.call(this, { - name: 'ReturnNode', - is_statement: true, - is_pure_statement: true, - children: ['expression'] - }); this.expression = expression; return this; }; __extends(ReturnNode, BaseNode); + ReturnNode.prototype.type = 'ReturnNode'; + ReturnNode.prototype.is_statement = function() { + return true; + }; + ReturnNode.prototype.is_pure_statement = function() { + return true; + }; + ReturnNode.prototype.children = ['expression']; ReturnNode.prototype.top_sensitive = function() { return true; }; @@ -420,16 +397,14 @@ // or vanilla. exports.ValueNode = (function() { ValueNode = function(base, properties) { - ValueNode.__superClass__.constructor.call(this, { - name: 'ValueNode', - children: ['base', 'properties'] - }); this.base = base; this.properties = (properties || []); return this; }; __extends(ValueNode, BaseNode); ValueNode.prototype.SOAK = " == undefined ? undefined : "; + ValueNode.prototype.type = 'ValueNode'; + ValueNode.prototype.children = ['base', 'properties']; // A **ValueNode** has a base and a list of property accesses. // Add a property access to the list. ValueNode.prototype.push = function(prop) { @@ -528,22 +503,22 @@ // CoffeeScript passes through comments as JavaScript comments at the // same position. exports.CommentNode = (function() { - CommentNode = function(lines, type) { - CommentNode.__superClass__.constructor.call(this, { - name: 'CommentNode', - is_statement: true - }); + CommentNode = function(lines, kind) { this.lines = lines; - this.type = type; + this.kind = kind; return this; }; __extends(CommentNode, BaseNode); + CommentNode.prototype.type = 'CommentNode'; + CommentNode.prototype.is_statement = function() { + return true; + }; CommentNode.prototype.make_return = function() { return this; }; CommentNode.prototype.compile_node = function(o) { var sep; - if (this.type === 'herecomment') { + if (this.kind === 'herecomment') { sep = '\n' + this.tab; return "" + this.tab + "/*" + sep + (this.lines.join(sep)) + "\n" + this.tab + "*/"; } else { @@ -557,10 +532,6 @@ // calls against the prototype's function of the same name. exports.CallNode = (function() { CallNode = function(variable, args) { - CallNode.__superClass__.constructor.call(this, { - name: 'CallNode', - children: ['variable', 'args'] - }); this.is_new = false; this.is_super = variable === 'super'; this.variable = this.is_super ? null : variable; @@ -569,6 +540,8 @@ return this; }; __extends(CallNode, BaseNode); + CallNode.prototype.type = 'CallNode'; + CallNode.prototype.children = ['variable', 'args']; // Tag this invocation as creating a new instance. CallNode.prototype.new_instance = function() { this.is_new = true; @@ -650,10 +623,6 @@ // Underscore's `bind` functions. exports.CurryNode = (function() { CurryNode = function(meth, args) { - CurryNode.__superClass__.constructor.call(this, { - name: 'CurryNode', - children: ['meth', 'context', 'args'] - }); this.meth = meth; this.context = args[0]; this.args = (args.slice(1) || []); @@ -661,6 +630,8 @@ return this; }; __extends(CurryNode, CallNode); + CurryNode.prototype.type = 'CurryNode'; + CurryNode.prototype.children = ['meth', 'context', 'args']; CurryNode.prototype.arguments = function(o) { var _b, _c, _d, arg; _c = this.args; @@ -686,15 +657,13 @@ // [Closure Library](http://closure-library.googlecode.com/svn/docs/closure_goog_base.js.html). exports.ExtendsNode = (function() { ExtendsNode = function(child, parent) { - ExtendsNode.__superClass__.constructor.call(this, { - name: 'ExtendsNode', - children: ['child', 'parent'] - }); this.child = child; this.parent = parent; return this; }; __extends(ExtendsNode, BaseNode); + ExtendsNode.prototype.type = 'ExtendsNode'; + ExtendsNode.prototype.children = ['child', 'parent']; // Hooks one constructor into another's prototype chain. ExtendsNode.prototype.compile_node = function(o) { var ref; @@ -708,16 +677,14 @@ // an accessor into the object's prototype. exports.AccessorNode = (function() { AccessorNode = function(name, tag) { - AccessorNode.__superClass__.constructor.call(this, { - name: 'AccessorNode', - children: ['name'] - }); this.name = name; this.prototype = tag === 'prototype'; this.soak_node = tag === 'soak'; return this; }; __extends(AccessorNode, BaseNode); + AccessorNode.prototype.type = 'AccessorNode'; + AccessorNode.prototype.children = ['name']; AccessorNode.prototype.compile_node = function(o) { var proto_part; o.chain_root.wrapped = o.chain_root.wrapped || this.soak_node; @@ -730,15 +697,13 @@ // A `[ ... ]` indexed accessor into an array or object. exports.IndexNode = (function() { IndexNode = function(index, tag) { - IndexNode.__superClass__.constructor.call(this, { - name: 'IndexNode', - children: ['index'] - }); this.index = index; this.soak_node = tag === 'soak'; return this; }; __extends(IndexNode, BaseNode); + IndexNode.prototype.type = 'IndexNode'; + IndexNode.prototype.children = ['index']; IndexNode.prototype.compile_node = function(o) { var idx; o.chain_root.wrapped = o.chain_root.wrapped || this.soak_node; @@ -753,16 +718,14 @@ // corresponding array of integers at runtime. exports.RangeNode = (function() { RangeNode = function(from, to, exclusive) { - RangeNode.__superClass__.constructor.call(this, { - name: 'RangeNode', - children: ['from', 'to'] - }); this.from = from; this.to = to; this.exclusive = !!exclusive; return this; }; __extends(RangeNode, BaseNode); + RangeNode.prototype.type = 'RangeNode'; + RangeNode.prototype.children = ['from', 'to']; // Compiles the range's source variables -- where it starts and where it ends. RangeNode.prototype.compile_variables = function(o) { var _b, _c, from, to; @@ -814,14 +777,12 @@ // is the index of the beginning. exports.SliceNode = (function() { SliceNode = function(range) { - SliceNode.__superClass__.constructor.call(this, { - name: 'SliceNode', - children: ['range'] - }); this.range = range; return this; }; __extends(SliceNode, BaseNode); + SliceNode.prototype.type = 'SliceNode'; + SliceNode.prototype.children = ['range']; SliceNode.prototype.compile_node = function(o) { var from, plus_part, to; from = this.range.from.compile(o); @@ -835,14 +796,12 @@ // An object literal, nothing fancy. exports.ObjectNode = (function() { ObjectNode = function(props) { - ObjectNode.__superClass__.constructor.call(this, { - name: 'ObjectNode', - children: ['properties'] - }); this.objects = (this.properties = props || []); return this; }; __extends(ObjectNode, BaseNode); + ObjectNode.prototype.type = 'ObjectNode'; + ObjectNode.prototype.children = ['properties']; // All the mucking about with commas is to make sure that CommentNodes and // AssignNodes get interleaved correctly, with no trailing commas or // commas affixed to comments. @@ -889,15 +848,13 @@ // An array literal. exports.ArrayNode = (function() { ArrayNode = function(objects) { - ArrayNode.__superClass__.constructor.call(this, { - name: 'ArrayNode', - children: ['objects'] - }); this.objects = objects || []; this.compile_splat_literal = __bind(SplatNode.compile_mixed_array, this, [this.objects]); return this; }; __extends(ArrayNode, BaseNode); + ArrayNode.prototype.type = 'ArrayNode'; + ArrayNode.prototype.children = ['objects']; ArrayNode.prototype.compile_node = function(o) { var _b, _c, code, i, obj, objects; o.indent = this.idt(1); @@ -929,11 +886,6 @@ // The CoffeeScript class definition. exports.ClassNode = (function() { ClassNode = function(variable, parent, props) { - ClassNode.__superClass__.constructor.call(this, { - name: 'ClassNode', - is_statement: true, - children: ['variable', 'parent', 'properties'] - }); this.variable = variable; this.parent = parent; this.properties = props || []; @@ -941,6 +893,11 @@ return this; }; __extends(ClassNode, BaseNode); + ClassNode.prototype.type = 'ClassNode'; + ClassNode.prototype.children = ['variable', 'parent', 'properties']; + ClassNode.prototype.is_statement = function() { + return true; + }; // Initialize a **ClassNode** with its name, an optional superclass, and a // list of prototype property assignments. ClassNode.prototype.make_return = function() { @@ -998,10 +955,6 @@ // property of an object -- including within object literals. exports.AssignNode = (function() { AssignNode = function(variable, value, context) { - AssignNode.__superClass__.constructor.call(this, { - name: 'AssignNode', - children: ['variable', 'value'] - }); this.variable = variable; this.value = value; this.context = context; @@ -1011,6 +964,8 @@ // Matchers for detecting prototype assignments. AssignNode.prototype.PROTO_ASSIGN = /^(\S+)\.prototype/; AssignNode.prototype.LEADING_DOT = /^\.(prototype\.)?/; + AssignNode.prototype.type = 'AssignNode'; + AssignNode.prototype.children = ['variable', 'value']; AssignNode.prototype.top_sensitive = function() { return true; }; @@ -1136,16 +1091,14 @@ // has no *children* -- they're within the inner scope. exports.CodeNode = (function() { CodeNode = function(params, body, tag) { - CodeNode.__superClass__.constructor.call(this, { - name: 'CodeNode', - children: ['params', 'body'] - }); this.params = params || []; this.body = body || new Expressions(); this.bound = tag === 'boundfunc'; return this; }; __extends(CodeNode, BaseNode); + CodeNode.prototype.type = 'CodeNode'; + CodeNode.prototype.children = ['params', 'body']; // 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 `=>` @@ -1219,7 +1172,7 @@ var _b, _c, _d, _e, child, children; idt = idt || ''; children = (function() { - _b = []; _d = this.children(); + _b = []; _d = this.collect_children(); for (_c = 0, _e = _d.length; _c < _e; _c++) { child = _d[_c]; _b.push(child.toString(idt + TAB)); @@ -1235,10 +1188,6 @@ // or as part of a destructuring assignment. exports.SplatNode = (function() { SplatNode = function(name) { - SplatNode.__superClass__.constructor.call(this, { - name: 'SplatNode', - children: ['name'] - }); if (!(name.compile)) { name = literal(name); } @@ -1246,6 +1195,8 @@ return this; }; __extends(SplatNode, BaseNode); + SplatNode.prototype.type = 'SplatNode'; + SplatNode.prototype.children = ['name']; SplatNode.prototype.compile_node = function(o) { var _b; if ((typeof (_b = this.index) !== "undefined" && _b !== null)) { @@ -1314,11 +1265,6 @@ // flexibility or more speed than a comprehension can provide. exports.WhileNode = (function() { WhileNode = function(condition, opts) { - WhileNode.__superClass__.constructor.call(this, { - name: 'WhileNode', - is_statement: true, - children: ['condition', 'guard', 'body'] - }); if (opts && opts.invert) { if (condition instanceof OpNode) { condition = new ParentheticalNode(condition); @@ -1330,6 +1276,11 @@ return this; }; __extends(WhileNode, BaseNode); + WhileNode.prototype.type = 'WhileNode'; + WhileNode.prototype.children = ['condition', 'guard', 'body']; + WhileNode.prototype.is_statement = function() { + return true; + }; WhileNode.prototype.add_body = function(body) { this.body = body; return this; @@ -1374,10 +1325,6 @@ // CoffeeScript operations into their JavaScript equivalents. exports.OpNode = (function() { OpNode = function(operator, first, second, flip) { - OpNode.__superClass__.constructor.call(this, { - name: ("OpNode " + operator), - children: ['first', 'second'] - }); this.first = first; this.second = second; this.operator = this.CONVERSIONS[operator] || operator; @@ -1397,6 +1344,8 @@ OpNode.prototype.ASSIGNMENT = ['||=', '&&=', '?=']; // Operators must come before their operands with a space. OpNode.prototype.PREFIX_OPERATORS = ['typeof', 'delete']; + OpNode.prototype.type = 'OpNode'; + OpNode.prototype.children = ['first', 'second']; OpNode.prototype.is_unary = function() { return !this.second; }; @@ -1479,11 +1428,6 @@ // A classic *try/catch/finally* block. exports.TryNode = (function() { TryNode = function(attempt, error, recovery, ensure) { - TryNode.__superClass__.constructor.call(this, { - name: 'TryNode', - is_statement: true, - children: ['attempt', 'recovery', 'ensure'] - }); this.attempt = attempt; this.recovery = recovery; this.ensure = ensure; @@ -1491,6 +1435,11 @@ return this; }; __extends(TryNode, BaseNode); + TryNode.prototype.type = 'TryNode'; + TryNode.prototype.children = ['attempt', 'recovery', 'ensure']; + TryNode.prototype.is_statement = function() { + return true; + }; TryNode.prototype.make_return = function() { if (this.attempt) { this.attempt = this.attempt.make_return(); @@ -1518,15 +1467,15 @@ // Simple node to throw an exception. exports.ThrowNode = (function() { ThrowNode = function(expression) { - ThrowNode.__superClass__.constructor.call(this, { - name: 'ThrowNode', - is_statement: true, - children: ['expression'] - }); this.expression = expression; return this; }; __extends(ThrowNode, BaseNode); + ThrowNode.prototype.type = 'ThrowNode'; + ThrowNode.prototype.children = ['expression']; + ThrowNode.prototype.is_statement = function() { + return true; + }; // A **ThrowNode** is already a return, of sorts... ThrowNode.prototype.make_return = function() { return this; @@ -1542,14 +1491,12 @@ // table. exports.ExistenceNode = (function() { ExistenceNode = function(expression) { - ExistenceNode.__superClass__.constructor.call(this, { - name: 'ExistenceNode', - children: ['expression'] - }); this.expression = expression; return this; }; __extends(ExistenceNode, BaseNode); + ExistenceNode.prototype.type = 'ExistenceNode'; + ExistenceNode.prototype.children = ['expression']; ExistenceNode.prototype.compile_node = function(o) { return ExistenceNode.compile_test(o, this.expression); }; @@ -1580,14 +1527,12 @@ // Parentheses are a good way to force any statement to become an expression. exports.ParentheticalNode = (function() { ParentheticalNode = function(expression) { - ParentheticalNode.__superClass__.constructor.call(this, { - name: 'ParentheticalNode', - children: ['expression'] - }); this.expression = expression; return this; }; __extends(ParentheticalNode, BaseNode); + ParentheticalNode.prototype.type = 'ParentheticalNode'; + ParentheticalNode.prototype.children = ['expression']; ParentheticalNode.prototype.is_statement = function() { return this.expression.is_statement(); }; @@ -1622,11 +1567,6 @@ exports.ForNode = (function() { ForNode = function(body, source, name, index) { var _b; - ForNode.__superClass__.constructor.call(this, { - name: 'ForNode', - is_statement: true, - children: ['body', 'source', 'guard'] - }); this.body = body; this.name = name; this.index = index || null; @@ -1647,6 +1587,11 @@ return this; }; __extends(ForNode, BaseNode); + ForNode.prototype.type = 'ForNode'; + ForNode.prototype.children = ['body', 'source', 'guard']; + ForNode.prototype.is_statement = function() { + return true; + }; ForNode.prototype.top_sensitive = function() { return true; }; @@ -1742,10 +1687,6 @@ // because ternaries are already proper expressions, and don't need conversion. exports.IfNode = (function() { IfNode = function(condition, body, tags) { - IfNode.__superClass__.constructor.call(this, { - name: 'IfNode', - children: ['condition', 'switch_subject', 'body', 'else_body', 'assigner'] - }); this.condition = condition; this.body = body; this.else_body = null; @@ -1757,6 +1698,8 @@ return this; }; __extends(IfNode, BaseNode); + IfNode.prototype.type = 'IfNode'; + IfNode.prototype.children = ['condition', 'switch_subject', 'body', 'else_body', 'assigner']; IfNode.prototype.body_node = function() { return this.body == undefined ? undefined : this.body.unwrap(); }; diff --git a/src/nodes.coffee b/src/nodes.coffee index 29e02d80..5468695f 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -29,17 +29,6 @@ else # scope, and indentation level. exports.BaseNode: class BaseNode - # If `is_statement` is passed, marks the node as a JavaScript *statement*, - # or as a *pure_statement* if `is_pure_statement`. Statements must be - # wrapped in a closure when used as an expression, and nodes tagged as - # *pure_statement* cannot be closure-wrapped without losing their meaning. - constructor: (o) -> - o: or {} - @constructor_name: o.name if o.name - @children_attributes: o.children if o.children - (@is_statement: -> true) if o.is_statement - (@is_pure_statement: -> true) if o.is_pure_statement - # 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 *pure_statement*, and we're not at @@ -118,19 +107,19 @@ exports.BaseNode: class BaseNode # This is what `coffee --nodes` prints out. toString: (idt) -> idt: or '' - '\n' + idt + @constructor_name + (child.toString(idt + TAB) for child in @children()).join('') + '\n' + idt + @type + (child.toString(idt + TAB) for child in @collect_children()).join('') - children: -> + each_child: (func) -> + return unless @children + for attr in @children when this[attr] + for child in flatten [this[attr]] + return if func(child) is false + + collect_children: -> nodes: [] @each_child (node) -> nodes.push node nodes - each_child: (func) -> - return unless @children_attributes - for attr in @children_attributes when this[attr] - for child in flatten [this[attr]] - return if func(child) is false - traverse_children: (cross_scope, func) -> @each_child (child) -> func.apply(this, arguments) @@ -138,13 +127,13 @@ exports.BaseNode: class BaseNode # Default implementations of the common node properties and methods. Nodes # will override these with custom logic, if needed. - constructor_name: 'BaseNode' - children_attributes: [] + type: 'BaseNode' + children: [] - unwrap: -> this - is_statement: -> false - is_pure_statement: -> false - top_sensitive: -> false + unwrap: -> this + is_statement: -> no + is_pure_statement: -> no + top_sensitive: -> no #### Expressions @@ -153,8 +142,11 @@ exports.BaseNode: class BaseNode # `if`, `switch`, or `try`, and so on... exports.Expressions: class Expressions extends BaseNode + type: 'Expressions' + children: ['expressions'] + is_statement: -> yes + constructor: (nodes) -> - super {name: 'Expressions', is_statement: yes, children: ['expressions']} @expressions: compact flatten nodes or [] # Tack an expression on to the end of this expression list. @@ -232,8 +224,9 @@ Expressions.wrap: (nodes) -> # `true`, `false`, `null`... exports.LiteralNode: class LiteralNode extends BaseNode + type: 'LiteralNode' + constructor: (value) -> - super {name: 'LiteralNode'} @value: value # Break and continue must be treated as pure statements -- they lose their @@ -256,8 +249,12 @@ exports.LiteralNode: class LiteralNode extends BaseNode # make sense. exports.ReturnNode: class ReturnNode extends BaseNode + type: 'ReturnNode' + is_statement: -> yes + is_pure_statement: -> yes + children: ['expression'] + constructor: (expression) -> - super {name: 'ReturnNode', is_statement: yes, is_pure_statement: yes, children: ['expression']} @expression: expression top_sensitive: -> @@ -276,11 +273,13 @@ exports.ReturnNode: class ReturnNode extends BaseNode # or vanilla. exports.ValueNode: class ValueNode extends BaseNode - SOAK: " == undefined ? undefined : " + SOAK: " == undefined ? undefined : " + + type: 'ValueNode' + children: ['base', 'properties'] # A **ValueNode** has a base and a list of property accesses. constructor: (base, properties) -> - super {name: 'ValueNode', children: ['base', 'properties']} @base: base @properties: (properties or []) @@ -357,16 +356,18 @@ exports.ValueNode: class ValueNode extends BaseNode # same position. exports.CommentNode: class CommentNode extends BaseNode - constructor: (lines, type) -> - super {name: 'CommentNode', is_statement: yes} + type: 'CommentNode' + is_statement: -> yes + + constructor: (lines, kind) -> @lines: lines - @type: type + @kind: kind make_return: -> this compile_node: (o) -> - if @type is 'herecomment' + if @kind is 'herecomment' sep: '\n' + @tab "$@tab/*$sep${ @lines.join(sep) }\n$@tab*/" else @@ -378,8 +379,10 @@ exports.CommentNode: class CommentNode extends BaseNode # calls against the prototype's function of the same name. exports.CallNode: class CallNode extends BaseNode + type: 'CallNode' + children: ['variable', 'args'] + constructor: (variable, args) -> - super {name: 'CallNode', children: ['variable', 'args']} @is_new: false @is_super: variable is 'super' @variable: if @is_super then null else variable @@ -437,8 +440,10 @@ exports.CallNode: class CallNode extends BaseNode # Underscore's `bind` functions. exports.CurryNode: class CurryNode extends CallNode + type: 'CurryNode' + children: ['meth', 'context', 'args'] + constructor: (meth, args) -> - super {name: 'CurryNode', children: ['meth', 'context', 'args']} @meth: meth @context: args[0] @args: (args.slice(1) or []) @@ -461,8 +466,10 @@ exports.CurryNode: class CurryNode extends CallNode # [Closure Library](http://closure-library.googlecode.com/svn/docs/closure_goog_base.js.html). exports.ExtendsNode: class ExtendsNode extends BaseNode + type: 'ExtendsNode' + children: ['child', 'parent'] + constructor: (child, parent) -> - super {name: 'ExtendsNode', children: ['child', 'parent']} @child: child @parent: parent @@ -477,8 +484,10 @@ exports.ExtendsNode: class ExtendsNode extends BaseNode # an accessor into the object's prototype. exports.AccessorNode: class AccessorNode extends BaseNode + type: 'AccessorNode' + children: ['name'] + constructor: (name, tag) -> - super {name: 'AccessorNode', children: ['name']} @name: name @prototype: tag is 'prototype' @soak_node: tag is 'soak' @@ -493,8 +502,10 @@ exports.AccessorNode: class AccessorNode extends BaseNode # A `[ ... ]` indexed accessor into an array or object. exports.IndexNode: class IndexNode extends BaseNode + type: 'IndexNode' + children: ['index'] + constructor: (index, tag) -> - super {name: 'IndexNode', children: ['index']} @index: index @soak_node: tag is 'soak' @@ -510,8 +521,10 @@ exports.IndexNode: class IndexNode extends BaseNode # corresponding array of integers at runtime. exports.RangeNode: class RangeNode extends BaseNode + type: 'RangeNode' + children: ['from', 'to'] + constructor: (from, to, exclusive) -> - super {name: 'RangeNode', children: ['from', 'to']} @from: from @to: to @exclusive: !!exclusive @@ -553,8 +566,10 @@ exports.RangeNode: class RangeNode extends BaseNode # is the index of the beginning. exports.SliceNode: class SliceNode extends BaseNode + type: 'SliceNode' + children: ['range'] + constructor: (range) -> - super {name: 'SliceNode', children: ['range']} @range: range compile_node: (o) -> @@ -568,8 +583,10 @@ exports.SliceNode: class SliceNode extends BaseNode # An object literal, nothing fancy. exports.ObjectNode: class ObjectNode extends BaseNode + type: 'ObjectNode' + children: ['properties'] + constructor: (props) -> - super {name: 'ObjectNode', children: ['properties']} @objects: @properties: props or [] # All the mucking about with commas is to make sure that CommentNodes and @@ -595,8 +612,10 @@ exports.ObjectNode: class ObjectNode extends BaseNode # An array literal. exports.ArrayNode: class ArrayNode extends BaseNode + type: 'ArrayNode' + children: ['objects'] + constructor: (objects) -> - super {name: 'ArrayNode', children: ['objects']} @objects: objects or [] @compile_splat_literal: SplatNode.compile_mixed_array <- @, @objects @@ -624,10 +643,13 @@ exports.ArrayNode: class ArrayNode extends BaseNode # The CoffeeScript class definition. exports.ClassNode: class ClassNode extends BaseNode + type: 'ClassNode' + children: ['variable', 'parent', 'properties'] + is_statement: -> yes + # Initialize a **ClassNode** with its name, an optional superclass, and a # list of prototype property assignments. constructor: (variable, parent, props) -> - super {name: 'ClassNode', is_statement: yes, children: ['variable', 'parent', 'properties']} @variable: variable @parent: parent @properties: props or [] @@ -686,8 +708,10 @@ exports.AssignNode: class AssignNode extends BaseNode PROTO_ASSIGN: /^(\S+)\.prototype/ LEADING_DOT: /^\.(prototype\.)?/ + type: 'AssignNode' + children: ['variable', 'value'] + constructor: (variable, value, context) -> - super {name: 'AssignNode', children: ['variable', 'value']} @variable: variable @value: value @context: context @@ -783,8 +807,10 @@ exports.AssignNode: class AssignNode extends BaseNode # has no *children* -- they're within the inner scope. exports.CodeNode: class CodeNode extends BaseNode + type: 'CodeNode' + children: ['params', 'body'] + constructor: (params, body, tag) -> - super {name: 'CodeNode', children: ['params', 'body']} @params: params or [] @body: body or new Expressions() @bound: tag is 'boundfunc' @@ -837,7 +863,7 @@ exports.CodeNode: class CodeNode extends BaseNode toString: (idt) -> idt: or '' - children: (child.toString(idt + TAB) for child in @children()).join('') + children: (child.toString(idt + TAB) for child in @collect_children()).join('') "\n$idt$children" #### SplatNode @@ -846,8 +872,10 @@ exports.CodeNode: class CodeNode extends BaseNode # or as part of a destructuring assignment. exports.SplatNode: class SplatNode extends BaseNode + type: 'SplatNode' + children: ['name'] + constructor: (name) -> - super {name: 'SplatNode', children: ['name']} name: literal(name) unless name.compile @name: name @@ -902,8 +930,11 @@ exports.SplatNode: class SplatNode extends BaseNode # flexibility or more speed than a comprehension can provide. exports.WhileNode: class WhileNode extends BaseNode + type: 'WhileNode' + children: ['condition', 'guard', 'body'] + is_statement: -> yes + constructor: (condition, opts) -> - super {name: 'WhileNode', is_statement: yes, children: ['condition', 'guard', 'body']} if opts and opts.invert condition: new ParentheticalNode condition if condition instanceof OpNode condition: new OpNode('!', condition) @@ -964,8 +995,10 @@ exports.OpNode: class OpNode extends BaseNode # Operators must come before their operands with a space. PREFIX_OPERATORS: ['typeof', 'delete'] + type: 'OpNode' + children: ['first', 'second'] + constructor: (operator, first, second, flip) -> - super {name: "OpNode $operator", children: ['first', 'second']} @first: first @second: second @operator: @CONVERSIONS[operator] or operator @@ -1024,8 +1057,11 @@ exports.OpNode: class OpNode extends BaseNode # A classic *try/catch/finally* block. exports.TryNode: class TryNode extends BaseNode + type: 'TryNode' + children: ['attempt', 'recovery', 'ensure'] + is_statement: -> yes + constructor: (attempt, error, recovery, ensure) -> - super {name: 'TryNode', is_statement: true, children: ['attempt', 'recovery', 'ensure']} @attempt: attempt @recovery: recovery @ensure: ensure @@ -1052,8 +1088,11 @@ exports.TryNode: class TryNode extends BaseNode # Simple node to throw an exception. exports.ThrowNode: class ThrowNode extends BaseNode + type: 'ThrowNode' + children: ['expression'] + is_statement: -> yes + constructor: (expression) -> - super {name: 'ThrowNode', is_statement: true, children: ['expression']} @expression: expression # A **ThrowNode** is already a return, of sorts... @@ -1070,8 +1109,10 @@ exports.ThrowNode: class ThrowNode extends BaseNode # table. exports.ExistenceNode: class ExistenceNode extends BaseNode + type: 'ExistenceNode' + children: ['expression'] + constructor: (expression) -> - super {name: 'ExistenceNode', children: ['expression']} @expression: expression compile_node: (o) -> @@ -1096,8 +1137,10 @@ exports.ExistenceNode: class ExistenceNode extends BaseNode # Parentheses are a good way to force any statement to become an expression. exports.ParentheticalNode: class ParentheticalNode extends BaseNode + type: 'ParentheticalNode' + children: ['expression'] + constructor: (expression) -> - super {name: 'ParentheticalNode', children: ['expression']} @expression: expression is_statement: -> @@ -1124,8 +1167,11 @@ exports.ParentheticalNode: class ParentheticalNode extends BaseNode # you can map and filter in a single pass. exports.ForNode: class ForNode extends BaseNode + type: 'ForNode' + children: ['body', 'source', 'guard'] + is_statement: -> yes + constructor: (body, source, name, index) -> - super {name: 'ForNode', is_statement: yes, children: ['body', 'source', 'guard']} @body: body @name: name @index: index or null @@ -1205,8 +1251,10 @@ exports.ForNode: class ForNode extends BaseNode # because ternaries are already proper expressions, and don't need conversion. exports.IfNode: class IfNode extends BaseNode + type: 'IfNode' + children: ['condition', 'switch_subject', 'body', 'else_body', 'assigner'] + constructor: (condition, body, tags) -> - super {name: 'IfNode', children: ['condition', 'switch_subject', 'body', 'else_body', 'assigner']} @condition: condition @body: body @else_body: null