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

Self-compiler: can now compile functions.

This commit is contained in:
Jeremy Ashkenas 2010-02-09 21:31:07 -05:00
parent c466537a26
commit ae4f6309e8
5 changed files with 202 additions and 88 deletions

View file

@ -1,5 +1,5 @@
(function(){
var AccessorNode, ArrayNode, AssignNode, CallNode, CommentNode, Expressions, ExtendsNode, IndexNode, LiteralNode, Node, ObjectNode, RangeNode, ReturnNode, SliceNode, TAB, TRAILING_WHITESPACE, ThisNode, ValueNode, any, compact, del, dup, flatten, inherit, merge, statement;
var AccessorNode, ArrayNode, AssignNode, CallNode, ClosureNode, CodeNode, CommentNode, Expressions, ExtendsNode, IndexNode, LiteralNode, Node, ObjectNode, PushNode, RangeNode, ReturnNode, SliceNode, TAB, TRAILING_WHITESPACE, ThisNode, 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.
@ -366,7 +366,7 @@
is_last: function is_last(node) {
var l;
l = this.expressions.length;
this.last_index = this.last_index || this.expressions[l - 1] instanceof CommentNode ? -2 : -1;
this.last_index = this.last_index || this.expressions[l - 1] instanceof CommentNode ? 2 : 1;
return node === this.expressions[l - this.last_index];
},
compile: function compile(o) {
@ -435,7 +435,7 @@
}));
}
// Otherwise, we can just return the value of the expression.
return this.idt() + 'return ' + node.compile(o);
return this.idt() + 'return ' + node.compile(o) + ';';
}
}));
// Wrap up a node as an Expressions, unless it already is one.
@ -817,6 +817,29 @@
return '[' + objects + ending;
}
}));
// A faux-node that is never created by the grammar, but is used during
// code generation to generate a quick "array.push(value)" tree of nodes.
PushNode = (exports.PushNode = {
wrap: function wrap(array, expressions) {
var expr;
expr = expressions.unwrap();
if (expr.is_statement_only() || expr.contains(function(n) {
return n.is_statement_only();
})) {
return expressions;
}
return Expressions.wrap(new CallNode(new ValueNode(new LiteralNode(array), [new AccessorNode(new LiteralNode('push'))]), [expr]));
}
});
// A faux-node used to wrap an expressions body in a closure.
ClosureNode = (exports.ClosureNode = {
wrap: function wrap(expressions, statement) {
var call, func;
func = new ParentheticalNode(new CodeNode([], Expressions.wrap(expressions)));
call = new CallNode(new ValueNode(func, new AccessorNode(new LiteralNode('call'))), [new LiteralNode('this')]);
return statement ? Expressions.wrap(call) : call;
}
});
// Setting the value of a local variable, or the value of an object property.
AssignNode = (exports.AssignNode = inherit(Node, {
// Keep the identifier regex in sync with the Lexer.
@ -912,4 +935,49 @@
return name + '.splice.apply(' + name + ', [' + from + ', ' + to + '].concat(' + this.value.compile(o) + '))';
}
}));
// A function definition. The only node that creates a new Scope.
// A CodeNode does not have any children -- they're within the new scope.
CodeNode = (exports.CodeNode = inherit(Node, {
constructor: function constructor(params, body, tag) {
this.params = params;
this.body = body;
this.bound = tag === 'boundfunc';
return this;
},
compile_node: function compile_node(o) {
var __a, __b, code, func, inner, name_part, param, shared_scope, splat, top;
shared_scope = del(o, 'shared_scope');
top = del(o, 'top');
o.scope = shared_scope || new Scope(o.scope, this.body, this);
o.returns = true;
o.top = true;
o.indent = this.idt(this.bound ? 2 : 1);
del(o, 'no_wrap');
del(o, 'globals');
if (this.params[this.params.length - 1] instanceof SplatNode) {
splat = this.params.pop();
splat.index = this.params.length;
this.body.unshift(splat);
}
__a = this.params;
for (__b = 0; __b < __a.length; __b++) {
param = __a[__b];
(o.scope.parameter(param));
}
code = this.body.expressions.length ? '\n' + this.body.compile_with_declarations(o) + '\n' : '';
name_part = this.name ? ' ' + this.name : '';
func = 'function' + (this.bound ? '' : name_part) + '(' + this.params.join(', ') + ') {' + code + this.idt(this.bound ? 1 : 0) + '}';
if (top && !this.bound) {
func = '(' + func + ')';
}
if (!(this.bound)) {
return func;
}
inner = '(function' + name_part + '() {\n' + this.idt(2) + 'return __func.apply(__this, arguments);\n' + this.idt(1) + '});';
return '(function(__this) {\n' + this.idt(1) + 'var __func = ' + func + ';\n' + this.idt(1) + 'return ' + inner + '\n' + this.idt() + '})(this)';
},
top_sensitive: function top_sensitive() {
return true;
}
}));
})();