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

now self-compiling array/object/range comprehensions.

This commit is contained in:
Jeremy Ashkenas 2010-02-10 20:19:59 -05:00
parent 0c2a13b468
commit 13d3b3a3ce
3 changed files with 209 additions and 26 deletions

View file

@ -1,5 +1,5 @@
(function(){
var AccessorNode, ArrayNode, AssignNode, CallNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, IDENTIFIER, IndexNode, LiteralNode, Node, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThisNode, ThrowNode, TryNode, ValueNode, WhileNode, any, compact, del, dup, flatten, inherit, merge, statement;
var AccessorNode, ArrayNode, AssignNode, CallNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IndexNode, LiteralNode, Node, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThisNode, ThrowNode, TryNode, ValueNode, WhileNode, 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.
@ -144,9 +144,6 @@
this.name = this.constructor.name;
return this.values = arguments;
};
exports.Expressions.wrap = function wrap(values) {
return this.values = values;
};
// Some helper functions
// Tabs are two spaces for pretty printing.
TAB = ' ';
@ -205,15 +202,24 @@
};
// Merge objects.
merge = function merge(src, dest) {
var __a, key, val;
var __a, __b, fresh, key, val;
fresh = {
};
__a = src;
for (key in __a) {
val = __a[key];
if (__hasProp.call(__a, key)) {
((dest[key] = val));
((fresh[key] = val));
}
}
return dest;
__b = dest;
for (key in __b) {
val = __b[key];
if (__hasProp.call(__b, key)) {
((fresh[key] = val));
}
}
return fresh;
};
// Do any of the elements in the list pass a truth test?
any = function any(list, test) {
@ -721,10 +727,11 @@
// TODO: This generates pretty ugly code ... shrink it.
compile_array: function compile_array(o) {
var arr, body;
body = Expressions.wrap(new LiteralNode('i'));
arr = Expressions.wrap(new ForNode(body, {
source: (new ValueNode(this))
}, 'i'));
body = Expressions.wrap([new LiteralNode('i')]);
arr = Expressions.wrap([new ForNode(body, {
source: (new ValueNode(this))
}, 'i')
]);
return (new ParentheticalNode(new CallNode(new CodeNode([], arr)))).compile(o);
}
}));
@ -830,16 +837,16 @@
})) {
return expressions;
}
return Expressions.wrap(new CallNode(new ValueNode(new LiteralNode(array), [new AccessorNode(new LiteralNode('push'))]), [expr]));
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)));
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;
return statement ? Expressions.wrap([call]) : call;
}
});
// Setting the value of a local variable, or the value of an object property.
@ -1183,4 +1190,104 @@
return '(' + code + ')';
}
}));
// The replacement for the for loop is an array comprehension (that compiles)
// into a for loop. Also acts as an expression, able to return the result
// of the comprehenion. Unlike Python array comprehensions, it's able to pass
// the current index of the loop as a second parameter.
ForNode = (exports.ForNode = inherit(Node, {
constructor: function constructor(body, source, name, index) {
var __a;
this.body = body;
this.name = name;
this.index = index || null;
this.source = source.source;
this.filter = source.filter;
this.step = source.step;
this.object = !!source.object;
if (this.object) {
__a = [this.index, this.name];
this.name = __a[0];
this.index = __a[1];
}
this.children = [this.body, this.source, this.filter];
return this;
},
top_sensitive: function top_sensitive() {
return true;
},
compile_node: function compile_node(o) {
var body, body_dent, call, for_part, index, index_found, index_var, ivar, name, name_found, range, return_result, rvar, scope, set_result, source, source_part, step_part, svar, top_level, var_part, vars;
top_level = del(o, 'top') && !o.returns;
range = this.source instanceof ValueNode && this.source.base instanceof RangeNode && !this.source.properties.length;
source = range ? this.source.base : this.source;
scope = o.scope;
name = this.name && this.name.compile(o);
index = this.index && this.index.compile(o);
name_found = name && scope.find(name);
index_found = index && scope.find(index);
body_dent = this.idt(1);
if (!(top_level)) {
rvar = scope.free_variable();
}
svar = scope.free_variable();
ivar = range ? name : index || scope.free_variable();
var_part = '';
body = Expressions.wrap([this.body]);
if (range) {
index_var = scope.free_variable();
source_part = source.compile_variables(o);
for_part = index_var + '=0, ' + source.compile(merge(o, {
index: ivar,
step: this.step
})) + ', ' + index_var + '++';
} else {
index_var = null;
source_part = svar + ' = ' + this.source.compile(o) + ';\n' + this.idt();
step_part = this.step ? ivar + ' += ' + this.step.compile(o) : ivar + '++';
for_part = this.object ? ivar + ' in ' + svar : ivar + ' = 0; ' + ivar + ' < ' + svar + '.length; ' + step_part;
if (name) {
var_part = body_dent + name + ' = ' + svar + '[' + ivar + '];\n';
}
}
set_result = rvar ? this.idt() + rvar + ' = []; ' : this.idt();
return_result = rvar || '';
if (top_level && this.contains(function(n) {
return n instanceof CodeNode;
})) {
body = ClosureNode.wrap(body, true);
}
if (!(top_level)) {
body = PushNode.wrap(rvar, body);
}
if (o.returns) {
return_result = 'return ' + return_result;
del(o, 'returns');
if (this.filter) {
body = new IfNode(this.filter, body, null, {
statement: true
});
}
} else if (this.filter) {
body = Expressions.wrap([new IfNode(this.filter, body)]);
}
if (this.object) {
o.scope.assign('__hasProp', 'Object.prototype.hasOwnProperty', true);
call = new CallNode(new ValueNode(new LiteralNode('__hasProp'), [new AccessorNode(new LiteralNode('call'))]), [new LiteralNode(svar), new LiteralNode(ivar)]);
body = Expressions.wrap([new IfNode(call, Expressions.wrap([body]), null, {
statement: true
})
]);
}
if (!(top_level)) {
return_result = '\n' + this.idt() + return_result + ';';
}
body = body.compile(merge(o, {
indent: body_dent,
top: true
}));
vars = range ? name : name + ', ' + ivar;
return set_result + source_part + 'for (' + for_part + ') {\n' + var_part + body + '\n' + this.idt() + '}\n' + this.idt() + return_result;
}
}));
statement(ForNode);
})();