mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
CoffeeScript-in-CoffeeScript is compiling function calls
This commit is contained in:
parent
0b5b6113ee
commit
210d673ef0
4 changed files with 164 additions and 16 deletions
|
|
@ -1,5 +1,5 @@
|
|||
(function(){
|
||||
var CommentNode, Expressions, LiteralNode, Node, ReturnNode, TAB, TRAILING_WHITESPACE, ValueNode, compact, del, dup, flatten, inherit, merge, statement;
|
||||
var CallNode, CommentNode, Expressions, LiteralNode, Node, ReturnNode, TAB, TRAILING_WHITESPACE, ValueNode, any, compact, del, dup, flatten, inherit, merge, statement;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
process.mixin(require('./scope'));
|
||||
// The abstract base class for all CoffeeScript nodes.
|
||||
|
|
@ -270,6 +270,21 @@
|
|||
}).call(this);
|
||||
return dest;
|
||||
};
|
||||
// Do any of the elements in the list pass a truth test?
|
||||
any = function any(list, test) {
|
||||
var __a, __b, __c, item, result;
|
||||
result = (function() {
|
||||
__a = []; __b = list;
|
||||
for (__c = 0; __c < __b.length; __c++) {
|
||||
item = __b[__c];
|
||||
if (test(item)) {
|
||||
__a.push(true);
|
||||
}
|
||||
}
|
||||
return __a;
|
||||
}).call(this);
|
||||
return !!result.length;
|
||||
};
|
||||
// Delete a key from an object, returning the value.
|
||||
del = function del(obj, key) {
|
||||
var val;
|
||||
|
|
@ -612,4 +627,79 @@
|
|||
}
|
||||
}));
|
||||
statement(CommentNode);
|
||||
// Node for a function invocation. Takes care of converting super() calls into
|
||||
// calls against the prototype's function of the same name.
|
||||
CallNode = (exports.CallNode = inherit(Node, {
|
||||
constructor: function constructor(variable, args) {
|
||||
this.variable = variable;
|
||||
this.args = args || [];
|
||||
this.children = flatten([this.variable, this.args]);
|
||||
this.prefix = '';
|
||||
return this;
|
||||
},
|
||||
new_instance: function new_instance() {
|
||||
this.prefix = 'new ';
|
||||
return this;
|
||||
},
|
||||
push: function push(arg) {
|
||||
this.args.push(arg);
|
||||
return this.children.push(arg);
|
||||
},
|
||||
// Compile a vanilla function call.
|
||||
compile_node: function compile_node(o) {
|
||||
var __a, __b, __c, arg, args;
|
||||
if (any(this.args, function(a) {
|
||||
return a instanceof SplatNode;
|
||||
})) {
|
||||
return this.compile_splat(o);
|
||||
}
|
||||
args = ((function() {
|
||||
__a = []; __b = this.args;
|
||||
for (__c = 0; __c < __b.length; __c++) {
|
||||
arg = __b[__c];
|
||||
__a.push(arg.compile(o));
|
||||
}
|
||||
return __a;
|
||||
}).call(this)).join(', ');
|
||||
if (this.variable === 'super') {
|
||||
return this.compile_super(args, o);
|
||||
}
|
||||
return this.prefix + this.variable.compile(o) + '(' + args + ')';
|
||||
},
|
||||
// Compile a call against the superclass's implementation of the current function.
|
||||
compile_super: function compile_super(args, o) {
|
||||
var arg_part, meth, methname;
|
||||
methname = o.scope.method.name;
|
||||
arg_part = args.length ? ', ' + args : '';
|
||||
meth = o.scope.method.proto ? o.scope.method.proto + '.__superClass__.' + methname : methname + '.__superClass__.constructor';
|
||||
return meth + '.call(this' + arg_part + ')';
|
||||
},
|
||||
// Compile a function call being passed variable arguments.
|
||||
compile_splat: function compile_splat(o) {
|
||||
var __a, __b, arg, args, code, i, meth, obj;
|
||||
meth = this.variable.compile(o);
|
||||
obj = this.variable.source || 'this';
|
||||
args = (function() {
|
||||
__a = []; __b = this.args;
|
||||
for (i = 0; i < __b.length; i++) {
|
||||
arg = __b[i];
|
||||
__a.push((function() {
|
||||
code = arg.compile(o);
|
||||
code = arg instanceof SplatNode ? code : '[' + code + ']';
|
||||
return i === 0 ? code : '.concat(' + code + ')';
|
||||
}).call(this));
|
||||
}
|
||||
return __a;
|
||||
}).call(this);
|
||||
return this.prefix + meth + '.apply(' + obj + ', ' + args.join('') + ')';
|
||||
},
|
||||
// If the code generation wished to use the result of a function call
|
||||
// in multiple places, ensure that the function is only ever called once.
|
||||
compile_reference: function compile_reference(o) {
|
||||
var call, reference;
|
||||
reference = o.scope.free_variable();
|
||||
call = new ParentheticalNode(new AssignNode(reference, this));
|
||||
return [call, reference];
|
||||
}
|
||||
}));
|
||||
})();
|
||||
|
|
@ -81,7 +81,7 @@
|
|||
],
|
||||
// Assignment within an object literal (can be quoted).
|
||||
AssignObj: [o("IDENTIFIER ASSIGN Expression", function() {
|
||||
return new AssignNode(new ValueNode(yytext), $3, 'object');
|
||||
return new AssignNode(new ValueNode(new LiteralNode(yytext)), $3, 'object');
|
||||
}), o("STRING ASSIGN Expression", function() {
|
||||
return new AssignNode(new ValueNode(new LiteralNode(yytext)), $3, 'object');
|
||||
}), o("NUMBER ASSIGN Expression", function() {
|
||||
|
|
@ -189,7 +189,7 @@
|
|||
],
|
||||
// A Parameter (or ParamSplat) in a function definition.
|
||||
Param: [o("PARAM", function() {
|
||||
return yytext;
|
||||
return new LiteralNode(yytext);
|
||||
}), o("PARAM . . .", function() {
|
||||
return new SplatNode(yytext);
|
||||
})
|
||||
|
|
@ -201,7 +201,7 @@
|
|||
],
|
||||
// Expressions that can be treated as values.
|
||||
Value: [o("IDENTIFIER", function() {
|
||||
return new ValueNode(yytext);
|
||||
return new ValueNode(new LiteralNode(yytext));
|
||||
}), o("Literal", function() {
|
||||
return new ValueNode($1);
|
||||
}), o("Array", function() {
|
||||
|
|
@ -220,11 +220,11 @@
|
|||
],
|
||||
// Accessing into an object or array, through dot or index notation.
|
||||
Accessor: [o("PROPERTY_ACCESS IDENTIFIER", function() {
|
||||
return new AccessorNode(yytext);
|
||||
return new AccessorNode(new LiteralNode(yytext));
|
||||
}), o("PROTOTYPE_ACCESS IDENTIFIER", function() {
|
||||
return new AccessorNode(yytext, 'prototype');
|
||||
return new AccessorNode(new LiteralNode(yytext), 'prototype');
|
||||
}), o("SOAK_ACCESS IDENTIFIER", function() {
|
||||
return new AccessorNode(yytext, 'soak');
|
||||
return new AccessorNode(new LiteralNode(yytext), 'soak');
|
||||
}), o("Index"), o("Slice", function() {
|
||||
return new SliceNode($1);
|
||||
})
|
||||
|
|
@ -308,7 +308,7 @@
|
|||
ArgList: [o("", function() {
|
||||
return [];
|
||||
}), o("Expression", function() {
|
||||
return val;
|
||||
return [$1];
|
||||
}), o("INDENT Expression", function() {
|
||||
return [$2];
|
||||
}), o("ArgList , Expression", function() {
|
||||
|
|
|
|||
|
|
@ -72,6 +72,11 @@ merge: (src, dest) ->
|
|||
dest[key]: val for key, val of src
|
||||
dest
|
||||
|
||||
# Do any of the elements in the list pass a truth test?
|
||||
any: (list, test) ->
|
||||
result: true for item in list when test(item)
|
||||
!!result.length
|
||||
|
||||
# Delete a key from an object, returning the value.
|
||||
del: (obj, key) ->
|
||||
val: obj[key]
|
||||
|
|
@ -354,7 +359,60 @@ CommentNode: exports.CommentNode: inherit Node, {
|
|||
statement CommentNode
|
||||
|
||||
|
||||
|
||||
# Node for a function invocation. Takes care of converting super() calls into
|
||||
# calls against the prototype's function of the same name.
|
||||
CallNode: exports.CallNode: inherit Node, {
|
||||
|
||||
constructor: (variable, args) ->
|
||||
@variable: variable
|
||||
@args: args or []
|
||||
@children: flatten([@variable, @args])
|
||||
@prefix: ''
|
||||
this
|
||||
|
||||
new_instance: ->
|
||||
@prefix: 'new '
|
||||
this
|
||||
|
||||
push: (arg) ->
|
||||
@args.push(arg)
|
||||
@children.push(arg)
|
||||
|
||||
# Compile a vanilla function call.
|
||||
compile_node: (o) ->
|
||||
return @compile_splat(o) if any @args, (a) -> a instanceof SplatNode
|
||||
args: (arg.compile(o) for arg in @args).join(', ')
|
||||
return @compile_super(args, o) if @variable is 'super'
|
||||
@prefix + @variable.compile(o) + '(' + args + ')'
|
||||
|
||||
# Compile a call against the superclass's implementation of the current function.
|
||||
compile_super: (args, o) ->
|
||||
methname: o.scope.method.name
|
||||
arg_part: if args.length then ', ' + args else ''
|
||||
meth: if o.scope.method.proto
|
||||
o.scope.method.proto + '.__superClass__.' + methname
|
||||
else
|
||||
methname + '.__superClass__.constructor'
|
||||
meth + '.call(this' + arg_part + ')'
|
||||
|
||||
# Compile a function call being passed variable arguments.
|
||||
compile_splat: (o) ->
|
||||
meth: @variable.compile o
|
||||
obj: @variable.source or 'this'
|
||||
args: for arg, i in @args
|
||||
code: arg.compile o
|
||||
code: if arg instanceof SplatNode then code else '[' + code + ']'
|
||||
if i is 0 then code else '.concat(' + code + ')'
|
||||
@prefix + meth + '.apply(' + obj + ', ' + args.join('') + ')'
|
||||
|
||||
# If the code generation wished to use the result of a function call
|
||||
# in multiple places, ensure that the function is only ever called once.
|
||||
compile_reference: (o) ->
|
||||
reference: o.scope.free_variable()
|
||||
call: new ParentheticalNode(new AssignNode(reference, this))
|
||||
[call, reference]
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ grammar: {
|
|||
|
||||
# Assignment within an object literal (can be quoted).
|
||||
AssignObj: [
|
||||
o "IDENTIFIER ASSIGN Expression", -> new AssignNode(new ValueNode(yytext), $3, 'object')
|
||||
o "IDENTIFIER ASSIGN Expression", -> new AssignNode(new ValueNode(new LiteralNode(yytext)), $3, 'object')
|
||||
o "STRING ASSIGN Expression", -> new AssignNode(new ValueNode(new LiteralNode(yytext)), $3, 'object')
|
||||
o "NUMBER ASSIGN Expression", -> new AssignNode(new ValueNode(new LiteralNode(yytext)), $3, 'object')
|
||||
o "Comment"
|
||||
|
|
@ -213,7 +213,7 @@ grammar: {
|
|||
|
||||
# A Parameter (or ParamSplat) in a function definition.
|
||||
Param: [
|
||||
o "PARAM", -> yytext
|
||||
o "PARAM", -> new LiteralNode(yytext)
|
||||
o "PARAM . . .", -> new SplatNode(yytext)
|
||||
]
|
||||
|
||||
|
|
@ -224,7 +224,7 @@ grammar: {
|
|||
|
||||
# Expressions that can be treated as values.
|
||||
Value: [
|
||||
o "IDENTIFIER", -> new ValueNode(yytext)
|
||||
o "IDENTIFIER", -> new ValueNode(new LiteralNode(yytext))
|
||||
o "Literal", -> new ValueNode($1)
|
||||
o "Array", -> new ValueNode($1)
|
||||
o "Object", -> new ValueNode($1)
|
||||
|
|
@ -236,9 +236,9 @@ grammar: {
|
|||
|
||||
# Accessing into an object or array, through dot or index notation.
|
||||
Accessor: [
|
||||
o "PROPERTY_ACCESS IDENTIFIER", -> new AccessorNode(yytext)
|
||||
o "PROTOTYPE_ACCESS IDENTIFIER", -> new AccessorNode(yytext, 'prototype')
|
||||
o "SOAK_ACCESS IDENTIFIER", -> new AccessorNode(yytext, 'soak')
|
||||
o "PROPERTY_ACCESS IDENTIFIER", -> new AccessorNode(new LiteralNode(yytext))
|
||||
o "PROTOTYPE_ACCESS IDENTIFIER", -> new AccessorNode(new LiteralNode(yytext), 'prototype')
|
||||
o "SOAK_ACCESS IDENTIFIER", -> new AccessorNode(new LiteralNode(yytext), 'soak')
|
||||
o "Index"
|
||||
o "Slice", -> new SliceNode($1)
|
||||
]
|
||||
|
|
@ -311,7 +311,7 @@ grammar: {
|
|||
# A list of arguments to a method call, or as the contents of an array.
|
||||
ArgList: [
|
||||
o "", -> []
|
||||
o "Expression", -> val
|
||||
o "Expression", -> [$1]
|
||||
o "INDENT Expression", -> [$2]
|
||||
o "ArgList , Expression", -> $1.push $3
|
||||
o "ArgList TERMINATOR Expression", -> $1.push $3
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue