1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

nodes: re{name,formatt}ed things

This commit is contained in:
satyr 2010-10-07 09:25:00 +09:00
parent 69b901a5b6
commit f90f1ef8e0
2 changed files with 127 additions and 124 deletions

View file

@ -89,13 +89,13 @@
return contains; return contains;
}; };
BaseNode.prototype.containsType = function(type) { BaseNode.prototype.containsType = function(type) {
return this instanceof type || this.contains(function(n) { return this instanceof type || this.contains(function(node) {
return n instanceof type; return node instanceof type;
}); });
}; };
BaseNode.prototype.containsPureStatement = function() { BaseNode.prototype.containsPureStatement = function() {
return this.isPureStatement() || this.contains(function(n) { return this.isPureStatement() || this.contains(function(node) {
return (typeof n.isPureStatement !== "function" ? undefined : n.isPureStatement()); return node.isPureStatement();
}); });
}; };
BaseNode.prototype.traverse = function(block) { BaseNode.prototype.traverse = function(block) {
@ -222,7 +222,7 @@
var code; var code;
code = this.compileNode(o); code = this.compileNode(o);
if (o.scope.hasAssignments(this)) { if (o.scope.hasAssignments(this)) {
code = ("" + (this.tab) + "var " + (o.scope.compiledAssignments()) + ";\n" + code); code = ("" + (this.tab) + "var " + (o.scope.compiledAssignments().replace(/\n/g, '$&' + this.tab)) + ";\n" + code);
} }
if (!o.globals && o.scope.hasDeclarations(this)) { if (!o.globals && o.scope.hasDeclarations(this)) {
code = ("" + (this.tab) + "var " + (o.scope.compiledDeclarations()) + ";\n" + code); code = ("" + (this.tab) + "var " + (o.scope.compiledDeclarations()) + ";\n" + code);
@ -567,7 +567,7 @@
return "" + (this.superReference(o)) + ".call(this" + (args.length ? ', ' : '') + args + ")"; return "" + (this.superReference(o)) + ".call(this" + (args.length ? ', ' : '') + args + ")";
}; };
CallNode.prototype.compileSplat = function(o) { CallNode.prototype.compileSplat = function(o) {
var _i, _len, _ref2, a, arg, argvar, b, base, c, call, fun, idt, name, ref, splatargs; var _i, _len, _ref2, arg, argvar, base, call, ctor, fun, idt, name, ref, result, splatargs;
splatargs = this.compileSplatArguments(o); splatargs = this.compileSplatArguments(o);
if (this.isSuper) { if (this.isSuper) {
return ("" + (this.superReference(o)) + ".apply(this, " + splatargs + ")"); return ("" + (this.superReference(o)) + ".apply(this, " + splatargs + ")");
@ -588,8 +588,8 @@
return ("" + fun + ".apply(" + ref + ", " + splatargs + ")"); return ("" + fun + ".apply(" + ref + ", " + splatargs + ")");
} }
call = 'call(this)'; call = 'call(this)';
argvar = function(n) { argvar = function(node) {
return n instanceof LiteralNode && n.value === 'arguments'; return node instanceof LiteralNode && node.value === 'arguments';
}; };
_ref2 = this.args; _ref2 = this.args;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) { for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
@ -599,10 +599,10 @@
break; break;
} }
} }
a = o.scope.freeVariable('ctor'); ctor = o.scope.freeVariable('ctor');
b = o.scope.freeVariable('ref'); ref = o.scope.freeVariable('ref');
c = o.scope.freeVariable('result'); result = o.scope.freeVariable('result');
return "(function() {\n" + (idt = this.idt(1)) + "var ctor = function() {};\n" + idt + (utility('extends')) + "(ctor, " + a + " = " + (this.variable.compile(o)) + ");\n" + idt + "return typeof (" + c + " = " + a + ".apply(" + b + " = new ctor, " + splatargs + ")) === \"object\" ? " + c + " : " + b + ";\n" + (this.tab) + "})." + call; return "(function() {\n" + (idt = this.idt(1)) + "var ctor = function() {};\n" + idt + (utility('extends')) + "(ctor, " + ctor + " = " + (this.variable.compile(o)) + ");\n" + idt + "return typeof (" + result + " = " + ctor + ".apply(" + ref + " = new ctor, " + splatargs + ")) === \"object\" ? " + result + " : " + ref + ";\n" + (this.tab) + "})." + call;
}; };
return CallNode; return CallNode;
})(); })();
@ -873,11 +873,11 @@
})(); })();
exports.ClassNode = (function() { exports.ClassNode = (function() {
ClassNode = (function() { ClassNode = (function() {
return function ClassNode(_arg, _arg2, _arg3) { return function ClassNode(variable, _arg, _arg2) {
this.properties = _arg3; this.properties = _arg2;
this.parent = _arg2; this.parent = _arg;
this.variable = _arg;
ClassNode.__super__.constructor.call(this); ClassNode.__super__.constructor.call(this);
this.variable = variable === '__temp__' ? literal(variable) : variable;
this.properties || (this.properties = []); this.properties || (this.properties = []);
this.returns = false; this.returns = false;
return this; return this;
@ -891,15 +891,16 @@
return this; return this;
}; };
ClassNode.prototype.compileNode = function(o) { ClassNode.prototype.compileNode = function(o) {
var _i, _len, _ref2, _ref3, access, applied, className, constScope, construct, constructor, extension, func, me, pname, prop, props, pvar, returns, val; var _i, _len, _ref2, _ref3, access, applied, className, constScope, construct, constructor, extension, func, me, pname, prop, props, pvar, returns, val, variable;
if (this.variable === '__temp__') { variable = this.variable;
this.variable = literal(o.scope.freeVariable('ctor')); if (variable.value === '__temp__') {
variable = literal(o.scope.freeVariable('ctor'));
} }
extension = this.parent && new ExtendsNode(this.variable, this.parent); extension = this.parent && new ExtendsNode(variable, this.parent);
props = new Expressions; props = new Expressions;
o.top = true; o.top = true;
me = null; me = null;
className = this.variable.compile(o); className = variable.compile(o);
constScope = null; constScope = null;
if (this.parent) { if (this.parent) {
applied = new ValueNode(this.parent, [new AccessorNode(literal('apply'))]); applied = new ValueNode(this.parent, [new AccessorNode(literal('apply'))]);
@ -917,8 +918,8 @@
} }
func.name = className; func.name = className;
func.body.push(new ReturnNode(literal('this'))); func.body.push(new ReturnNode(literal('this')));
this.variable = new ValueNode(this.variable); variable = new ValueNode(variable);
this.variable.namespaced = include(func.name, '.'); variable.namespaced = include(func.name, '.');
constructor = func; constructor = func;
continue; continue;
} }
@ -938,7 +939,7 @@
} }
if (pvar) { if (pvar) {
access = prop.context === 'this' ? pvar.base.properties[0] : new AccessorNode(pvar, 'prototype'); access = prop.context === 'this' ? pvar.base.properties[0] : new AccessorNode(pvar, 'prototype');
val = new ValueNode(this.variable, [access]); val = new ValueNode(variable, [access]);
prop = new AssignNode(val, func); prop = new AssignNode(val, func);
} }
props.push(prop); props.push(prop);
@ -947,12 +948,12 @@
if (me) { if (me) {
constructor.body.unshift(literal("" + me + " = this")); constructor.body.unshift(literal("" + me + " = this"));
} }
construct = this.idt() + (new AssignNode(this.variable, constructor)).compile(merge(o, { construct = this.idt() + new AssignNode(variable, constructor).compile(merge(o, {
sharedScope: constScope sharedScope: constScope
})) + ';'; })) + ';';
props = !props.empty() ? '\n' + props.compile(o) : ''; props = !props.empty() ? '\n' + props.compile(o) : '';
extension = extension ? '\n' + this.idt() + extension.compile(o) + ';' : ''; extension = extension ? '\n' + this.idt() + extension.compile(o) + ';' : '';
returns = this.returns ? '\n' + new ReturnNode(this.variable).compile(o) : ''; returns = this.returns ? '\n' + new ReturnNode(variable).compile(o) : '';
return construct + extension + props + returns; return construct + extension + props + returns;
}; };
return ClassNode; return ClassNode;
@ -1640,8 +1641,8 @@
topLevel = del(o, 'top') && !this.returns; topLevel = del(o, 'top') && !this.returns;
range = this.source instanceof ValueNode && this.source.base instanceof RangeNode && !this.source.properties.length; range = this.source instanceof ValueNode && this.source.base instanceof RangeNode && !this.source.properties.length;
source = range ? this.source.base : this.source; source = range ? this.source.base : this.source;
codeInBody = this.body.contains(function(n) { codeInBody = this.body.contains(function(node) {
return n instanceof CodeNode; return node instanceof CodeNode;
}); });
scope = o.scope; scope = o.scope;
name = this.name && this.name.compile(o); name = this.name && this.name.compile(o);
@ -1849,7 +1850,7 @@
})().join(' || '); })().join(' || ');
}; };
IfNode.prototype.compileNode = function(o) { IfNode.prototype.compileNode = function(o) {
return this.isStatement(o) ? this.compileStatement(o) : this.compileTernary(o); return this.isStatement(o) ? this.compileStatement(o) : this.compileExpression(o);
}; };
IfNode.prototype.makeReturn = function() { IfNode.prototype.makeReturn = function() {
if (this.isStatement()) { if (this.isStatement()) {
@ -1883,7 +1884,7 @@
})) : (" else {\n" + (this.elseBody.compile(o)) + "\n" + (this.tab) + "}"); })) : (" else {\n" + (this.elseBody.compile(o)) + "\n" + (this.tab) + "}");
return "" + ifPart + elsePart; return "" + ifPart + elsePart;
}; };
IfNode.prototype.compileTernary = function(o) { IfNode.prototype.compileExpression = function(o) {
var code, elsePart, ifPart; var code, elsePart, ifPart;
this.bodyNode().tags.operation = (this.condition.tags.operation = true); this.bodyNode().tags.operation = (this.condition.tags.operation = true);
if (this.elseBody) { if (this.elseBody) {
@ -1896,31 +1897,23 @@
}; };
return IfNode; return IfNode;
})(); })();
PushNode = (exports.PushNode = { PushNode = {
wrap: function(array, expressions) { wrap: function(name, expressions) {
var expr; if (expressions.empty() || expressions.containsPureStatement()) {
expr = expressions.unwrap();
if (expr.isPureStatement() || expr.containsPureStatement()) {
return expressions; return expressions;
} }
return Expressions.wrap([new CallNode(new ValueNode(literal(array), [new AccessorNode(literal('push'))]), [expr])]); return Expressions.wrap([new CallNode(new ValueNode(literal(name), [new AccessorNode(literal('push'))]), [expressions.unwrap()])]);
} }
}); };
ClosureNode = (exports.ClosureNode = { ClosureNode = {
wrap: function(expressions, statement) { wrap: function(expressions, statement) {
var args, call, func, mentionsArgs, mentionsThis, meth; var args, call, func, mentionsArgs, meth;
if (expressions.containsPureStatement()) { if (expressions.containsPureStatement()) {
return expressions; return expressions;
} }
func = new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions]))); func = new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions])));
args = []; args = [];
mentionsArgs = expressions.contains(function(n) { if ((mentionsArgs = expressions.contains(this.literalArgs)) || (expressions.contains(this.literalThis))) {
return n instanceof LiteralNode && (n.value === 'arguments');
});
mentionsThis = expressions.contains(function(n) {
return (n instanceof LiteralNode && (n.value === 'this')) || (n instanceof CodeNode && n.bound);
});
if (mentionsArgs || mentionsThis) {
meth = literal(mentionsArgs ? 'apply' : 'call'); meth = literal(mentionsArgs ? 'apply' : 'call');
args = [literal('this')]; args = [literal('this')];
if (mentionsArgs) { if (mentionsArgs) {
@ -1930,11 +1923,17 @@
} }
call = new CallNode(func, args); call = new CallNode(func, args);
return statement ? Expressions.wrap([call]) : call; return statement ? Expressions.wrap([call]) : call;
},
literalArgs: function(node) {
return node instanceof LiteralNode && node.value === 'arguments';
},
literalThis: function(node) {
return node instanceof LiteralNode && node.value === 'this' || node instanceof CodeNode && node.bound;
} }
}); };
UTILITIES = { UTILITIES = {
"extends": "function(child, parent) {\n var ctor = function(){};\n ctor.prototype = parent.prototype;\n child.prototype = new ctor();\n child.prototype.constructor = child;\n if (typeof parent.extended === \"function\") parent.extended(child);\n child.__super__ = parent.prototype;\n }", "extends": 'function(child, parent) {\n var ctor = function() {};\n ctor.prototype = parent.prototype;\n child.prototype = new ctor();\n child.prototype.constructor = child;\n if (typeof parent.extended === "function") parent.extended(child);\n child.__super__ = parent.prototype;\n}',
bind: "function(func, context) {\n return function(){ return func.apply(context, arguments); };\n }", bind: 'function(func, context) {\n return function() { return func.apply(context, arguments); };\n}',
hasProp: 'Object.prototype.hasOwnProperty', hasProp: 'Object.prototype.hasOwnProperty',
slice: 'Array.prototype.slice' slice: 'Array.prototype.slice'
}; };

View file

@ -98,12 +98,12 @@ exports.BaseNode = class BaseNode
# Is this node of a certain type, or does it contain the type? # Is this node of a certain type, or does it contain the type?
containsType: (type) -> containsType: (type) ->
this instanceof type or @contains (n) -> n instanceof type this instanceof type or @contains (node) -> node instanceof type
# Convenience for the most common use of contains. Does the node contain # Convenience for the most common use of contains. Does the node contain
# a pure statement? # a pure statement?
containsPureStatement: -> containsPureStatement: ->
@isPureStatement() or @contains (n) -> n.isPureStatement?() @isPureStatement() or @contains (node) -> node.isPureStatement()
# Perform an in-order traversal of the AST. Crosses scope boundaries. # Perform an in-order traversal of the AST. Crosses scope boundaries.
traverse: (block) -> @traverseChildren true, block traverse: (block) -> @traverseChildren true, block
@ -209,8 +209,14 @@ exports.Expressions = class Expressions extends BaseNode
# declarations of all inner variables pushed up to the top. # declarations of all inner variables pushed up to the top.
compileWithDeclarations: (o) -> compileWithDeclarations: (o) ->
code = @compileNode(o) code = @compileNode(o)
code = "#{@tab}var #{o.scope.compiledAssignments()};\n#{code}" if o.scope.hasAssignments(this) code = """
code = "#{@tab}var #{o.scope.compiledDeclarations()};\n#{code}" if not o.globals and o.scope.hasDeclarations(this) #{@tab}var #{ o.scope.compiledAssignments().replace /\n/g, '$&' + @tab };
#{code}
""" if o.scope.hasAssignments this
code = """
#{@tab}var #{o.scope.compiledDeclarations()};
#{code}
""" if not o.globals and o.scope.hasDeclarations this
code code
# Compiles a single expression within the expressions body. If we need to # Compiles a single expression within the expressions body. If we need to
@ -522,18 +528,18 @@ exports.CallNode = class CallNode extends BaseNode
fun += name.compile o if name fun += name.compile o if name
return "#{fun}.apply(#{ref}, #{splatargs})" return "#{fun}.apply(#{ref}, #{splatargs})"
call = 'call(this)' call = 'call(this)'
argvar = (n) -> n instanceof LiteralNode and n.value is 'arguments' argvar = (node) -> node instanceof LiteralNode and node.value is 'arguments'
for arg in @args when arg.contains argvar for arg in @args when arg.contains argvar
call = 'apply(this, arguments)' call = 'apply(this, arguments)'
break break
a = o.scope.freeVariable 'ctor' ctor = o.scope.freeVariable 'ctor'
b = o.scope.freeVariable 'ref' ref = o.scope.freeVariable 'ref'
c = o.scope.freeVariable 'result' result = o.scope.freeVariable 'result'
""" """
(function() { (function() {
#{idt = @idt 1}var ctor = function() {}; #{idt = @idt 1}var ctor = function() {};
#{idt}#{utility 'extends'}(ctor, #{a} = #{ @variable.compile o }); #{idt}#{utility 'extends'}(ctor, #{ctor} = #{ @variable.compile o });
#{idt}return typeof (#{c} = #{a}.apply(#{b} = new ctor, #{splatargs})) === "object" ? #{c} : #{b}; #{idt}return typeof (#{result} = #{ctor}.apply(#{ref} = new ctor, #{splatargs})) === "object" ? #{result} : #{ref};
#{@tab}}).#{call} #{@tab}}).#{call}
""" """
@ -756,8 +762,9 @@ exports.ClassNode = class ClassNode extends BaseNode
# Initialize a **ClassNode** with its name, an optional superclass, and a # Initialize a **ClassNode** with its name, an optional superclass, and a
# list of prototype property assignments. # list of prototype property assignments.
constructor: (@variable, @parent, @properties) -> constructor: (variable, @parent, @properties) ->
super() super()
@variable = if variable is '__temp__' then literal variable else variable
@properties or= [] @properties or= []
@returns = false @returns = false
@ -769,12 +776,13 @@ exports.ClassNode = class ClassNode extends BaseNode
# equivalent syntax tree and compile that, in pieces. You can see the # equivalent syntax tree and compile that, in pieces. You can see the
# constructor, property assignments, and inheritance getting built out below. # constructor, property assignments, and inheritance getting built out below.
compileNode: (o) -> compileNode: (o) ->
@variable = literal o.scope.freeVariable 'ctor' if @variable is '__temp__' {variable} = this
extension = @parent and new ExtendsNode(@variable, @parent) variable = literal o.scope.freeVariable 'ctor' if variable.value is '__temp__'
extension = @parent and new ExtendsNode variable, @parent
props = new Expressions props = new Expressions
o.top = true o.top = true
me = null me = null
className = @variable.compile o className = variable.compile o
constScope = null constScope = null
if @parent if @parent
@ -791,8 +799,8 @@ exports.ClassNode = class ClassNode extends BaseNode
throw new Error "cannot define a constructor as a bound function." if func.bound throw new Error "cannot define a constructor as a bound function." if func.bound
func.name = className func.name = className
func.body.push new ReturnNode literal 'this' func.body.push new ReturnNode literal 'this'
@variable = new ValueNode @variable variable = new ValueNode variable
@variable.namespaced = include func.name, '.' variable.namespaced = include func.name, '.'
constructor = func constructor = func
continue continue
if func instanceof CodeNode and func.bound if func instanceof CodeNode and func.bound
@ -807,16 +815,16 @@ exports.ClassNode = class ClassNode extends BaseNode
constructor.body.unshift literal "this.#{pname} = function(){ return #{className}.prototype.#{pname}.apply(#{me}, arguments); }" constructor.body.unshift literal "this.#{pname} = function(){ return #{className}.prototype.#{pname}.apply(#{me}, arguments); }"
if pvar if pvar
access = if prop.context is 'this' then pvar.base.properties[0] else new AccessorNode(pvar, 'prototype') access = if prop.context is 'this' then pvar.base.properties[0] else new AccessorNode(pvar, 'prototype')
val = new ValueNode(@variable, [access]) val = new ValueNode variable, [access]
prop = new AssignNode(val, func) prop = new AssignNode(val, func)
props.push prop props.push prop
constructor.className = className.match /[\w\d\$_]+$/ constructor.className = className.match /[\w\d\$_]+$/
constructor.body.unshift literal "#{me} = this" if me constructor.body.unshift literal "#{me} = this" if me
construct = @idt() + (new AssignNode(@variable, constructor)).compile(merge o, {sharedScope: constScope}) + ';' construct = @idt() + new AssignNode(variable, constructor).compile(merge o, sharedScope: constScope) + ';'
props = if !props.empty() then '\n' + props.compile(o) else '' props = if !props.empty() then '\n' + props.compile(o) else ''
extension = if extension then '\n' + @idt() + extension.compile(o) + ';' else '' extension = if extension then '\n' + @idt() + extension.compile(o) + ';' else ''
returns = if @returns then '\n' + new ReturnNode(@variable).compile(o) else '' returns = if @returns then '\n' + new ReturnNode(variable).compile(o) else ''
construct + extension + props + returns construct + extension + props + returns
#### AssignNode #### AssignNode
@ -1401,7 +1409,7 @@ exports.ForNode = class ForNode extends BaseNode
topLevel = del(o, 'top') and not @returns topLevel = del(o, 'top') and not @returns
range = @source instanceof ValueNode and @source.base instanceof RangeNode and not @source.properties.length range = @source instanceof ValueNode and @source.base instanceof RangeNode and not @source.properties.length
source = if range then @source.base else @source source = if range then @source.base else @source
codeInBody = @body.contains (n) -> n instanceof CodeNode codeInBody = @body.contains (node) -> node instanceof CodeNode
scope = o.scope scope = o.scope
name = @name and @name.compile o name = @name and @name.compile o
index = @index and @index.compile o index = @index and @index.compile o
@ -1498,7 +1506,7 @@ exports.SwitchNode = class SwitchNode extends BaseNode
# *If/else* statements. Acts as an expression by pushing down requested returns # *If/else* statements. Acts as an expression by pushing down requested returns
# to the last line of each clause. # to the last line of each clause.
# #
# Single-expression **IfNodes** are compiled into ternary operators if possible, # Single-expression **IfNodes** are compiled into conditional operators if possible,
# because ternaries are already proper expressions, and don't need conversion. # because ternaries are already proper expressions, and don't need conversion.
exports.IfNode = class IfNode extends BaseNode exports.IfNode = class IfNode extends BaseNode
@ -1529,7 +1537,7 @@ exports.IfNode = class IfNode extends BaseNode
this this
# The **IfNode** only compiles into a statement if either of its bodies needs # The **IfNode** only compiles into a statement if either of its bodies needs
# to be a statement. Otherwise a ternary is safe. # to be a statement. Otherwise a conditional operator is safe.
isStatement: (o) -> isStatement: (o) ->
@statement or= !!((o and o.top) or @bodyNode().isStatement(o) or (@elseBody and @elseBodyNode().isStatement(o))) @statement or= !!((o and o.top) or @bodyNode().isStatement(o) or (@elseBody and @elseBodyNode().isStatement(o)))
@ -1539,7 +1547,7 @@ exports.IfNode = class IfNode extends BaseNode
(cond.compile(o) for cond in conditions).join(' || ') (cond.compile(o) for cond in conditions).join(' || ')
compileNode: (o) -> compileNode: (o) ->
if @isStatement(o) then @compileStatement(o) else @compileTernary(o) if @isStatement o then @compileStatement o else @compileExpression o
makeReturn: -> makeReturn: ->
if @isStatement() if @isStatement()
@ -1571,8 +1579,8 @@ exports.IfNode = class IfNode extends BaseNode
" else {\n#{ @elseBody.compile(o) }\n#{@tab}}" " else {\n#{ @elseBody.compile(o) }\n#{@tab}}"
"#{ifPart}#{elsePart}" "#{ifPart}#{elsePart}"
# Compile the IfNode as a ternary operator. # Compile the IfNode as a conditional operator.
compileTernary: (o) -> compileExpression: (o) ->
@bodyNode().tags.operation = @condition.tags.operation = yes @bodyNode().tags.operation = @condition.tags.operation = yes
@elseBodyNode().tags.operation = yes if @elseBody @elseBodyNode().tags.operation = yes if @elseBody
ifPart = @condition.compile(o) + ' ? ' + @bodyNode().compile(o) ifPart = @condition.compile(o) + ' ? ' + @bodyNode().compile(o)
@ -1582,55 +1590,55 @@ exports.IfNode = class IfNode extends BaseNode
# Faux-Nodes # Faux-Nodes
# ---------- # ----------
# Faux-nodes are never created by the grammar, but are used during code
# generation to generate other combinations of nodes.
#### PushNode #### PushNode
# Faux-nodes are never created by the grammar, but are used during code # The **PushNode** creates the tree for `array.push(value)`,
# generation to generate other combinations of nodes. The **PushNode** creates # which is helpful for recording the result arrays from comprehensions.
# the tree for `array.push(value)`, which is helpful for recording the result PushNode =
# arrays from comprehensions. wrap: (name, expressions) ->
PushNode = exports.PushNode = return expressions if expressions.empty() or expressions.containsPureStatement()
wrap: (array, expressions) -> Expressions.wrap [new CallNode(
expr = expressions.unwrap() new ValueNode literal(name), [new AccessorNode literal 'push']
return expressions if expr.isPureStatement() or expr.containsPureStatement() [expressions.unwrap()]
Expressions.wrap([new CallNode( )]
new ValueNode(literal(array), [new AccessorNode(literal('push'))]), [expr]
)])
#### ClosureNode #### ClosureNode
# A faux-node used to wrap an expressions body in a closure. # A faux-node used to wrap an expressions body in a closure.
ClosureNode = exports.ClosureNode = ClosureNode =
# Wrap the expressions body, unless it contains a pure statement, # Wrap the expressions body, unless it contains a pure statement,
# in which case, no dice. If the body mentions `this` or `arguments`, # in which case, no dice. If the body mentions `this` or `arguments`,
# then make sure that the closure wrapper preserves the original values. # then make sure that the closure wrapper preserves the original values.
wrap: (expressions, statement) -> wrap: (expressions, statement) ->
return expressions if expressions.containsPureStatement() return expressions if expressions.containsPureStatement()
func = new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions]))) func = new ParentheticalNode new CodeNode [], Expressions.wrap [expressions]
args = [] args = []
mentionsArgs = expressions.contains (n) -> if (mentionsArgs = expressions.contains @literalArgs) or
n instanceof LiteralNode and (n.value is 'arguments') ( expressions.contains @literalThis)
mentionsThis = expressions.contains (n) -> meth = literal if mentionsArgs then 'apply' else 'call'
(n instanceof LiteralNode and (n.value is 'this')) or args = [literal 'this']
(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 args.push literal 'arguments' if mentionsArgs
func = new ValueNode func, [new AccessorNode(meth)] func = new ValueNode func, [new AccessorNode meth]
call = new CallNode(func, args) call = new CallNode func, args
if statement then Expressions.wrap([call]) else call if statement then Expressions.wrap [call] else call
# Utility Functions literalArgs: (node) -> node instanceof LiteralNode and node.value is 'arguments'
# ----------------- literalThis: (node) -> node instanceof LiteralNode and node.value is 'this' or
node instanceof CodeNode and node.bound
# Constants
# ---------
UTILITIES = UTILITIES =
# Correctly set up a prototype chain for inheritance, including a reference # Correctly set up a prototype chain for inheritance, including a reference
# to the superclass for `super()` calls. See: # to the superclass for `super()` calls. See:
# [goog.inherits](http://closure-library.googlecode.com/svn/docs/closureGoogBase.js.source.html#line1206). # [goog.inherits](http://closure-library.googlecode.com/svn/docs/closureGoogBase.js.source.html#line1206).
extends: """ extends: '''
function(child, parent) { function(child, parent) {
var ctor = function() {}; var ctor = function() {};
ctor.prototype = parent.prototype; ctor.prototype = parent.prototype;
@ -1639,22 +1647,19 @@ UTILITIES =
if (typeof parent.extended === "function") parent.extended(child); if (typeof parent.extended === "function") parent.extended(child);
child.__super__ = parent.prototype; child.__super__ = parent.prototype;
} }
""" '''
# Create a function bound to the current value of "this". # Create a function bound to the current value of "this".
bind: """ bind: '''
function(func, context) { function(func, context) {
return function() { return func.apply(context, arguments); }; return function() { return func.apply(context, arguments); };
} }
""" '''
# Shortcuts to speed up the lookup time for native functions. # Shortcuts to speed up the lookup time for native functions.
hasProp: 'Object.prototype.hasOwnProperty' hasProp: 'Object.prototype.hasOwnProperty'
slice: 'Array.prototype.slice' slice: 'Array.prototype.slice'
# Constants
# ---------
# Tabs are two spaces for pretty printing. # Tabs are two spaces for pretty printing.
TAB = ' ' TAB = ' '
@ -1672,9 +1677,8 @@ IS_STRING = /^['"]/
# Utility Functions # Utility Functions
# ----------------- # -----------------
# Handy helper for a generating LiteralNode. # Handy helper for generating a LiteralNode.
literal = (name) -> literal = (name) -> new LiteralNode name
new LiteralNode(name)
# Helper for ensuring that utility functions are assigned at the top level. # Helper for ensuring that utility functions are assigned at the top level.
utility = (name) -> utility = (name) ->