mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-05-03 03:00:14 -04:00
reworking Stan's nodes.coffee type/children work a bit for issue 386
This commit is contained in:
249
lib/nodes.js
249
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();
|
||||
};
|
||||
|
||||
160
src/nodes.coffee
160
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
|
||||
|
||||
Reference in New Issue
Block a user