cleaner generated code for range comprehensions, but you have to use an explicit step to go downwards. Forked the code path for cleaner range literals as well.

This commit is contained in:
Jeremy Ashkenas 2010-05-31 19:38:45 -04:00
parent 300c711af1
commit 7402aea9f2
5 changed files with 89 additions and 74 deletions

View File

@ -78,15 +78,15 @@
};
// Display the list of Cake tasks in a format similar to `rake -T`
print_tasks = function() {
var _a, _b, _c, _d, desc, i, name, spaces, task;
var _a, _b, desc, i, name, spaces, task;
puts('');
_a = tasks;
for (name in _a) { if (__hasProp.call(_a, name)) {
task = _a[name];
spaces = 20 - name.length;
spaces = spaces > 0 ? (function() {
_b = []; _c = 0; _d = spaces;
for (i = _c; (_c <= _d ? i <= _d : i >= _d); (_c <= _d ? i += 1 : i -= 1)) {
_b = [];
for (i = 0; i <= spaces; i += 1) {
_b.push(' ');
}
return _b;

View File

@ -1,5 +1,5 @@
(function(){
var AccessorNode, ArrayNode, AssignNode, BaseNode, CallNode, ClassNode, ClosureNode, CodeNode, CommentNode, CurryNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IS_STRING, IfNode, IndexNode, LiteralNode, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, Scope, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, UTILITIES, ValueNode, WhileNode, _a, compact, del, flatten, helpers, include, index_of, literal, merge, utility;
var AccessorNode, ArrayNode, AssignNode, BaseNode, CallNode, ClassNode, ClosureNode, CodeNode, CommentNode, CurryNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IS_STRING, IfNode, IndexNode, LiteralNode, ObjectNode, OpNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, Scope, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, UTILITIES, ValueNode, WhileNode, _a, compact, del, flatten, helpers, include, index_of, literal, merge, starts, utility;
var __extends = function(child, parent) {
var ctor = function(){ };
ctor.prototype = parent.prototype;
@ -33,6 +33,7 @@
del = _a.del;
include = _a.include;
index_of = _a.index_of;
starts = _a.starts;
//### BaseNode
// The **BaseNode** is the abstract base class for all nodes in the syntax tree.
// Each subclass implements the `compile_node` method, which performs the
@ -82,8 +83,11 @@
// If the code generation wishes to use the result of a complex expression
// in multiple places, ensure that the expression is only ever evaluated once,
// by assigning it to a temporary variable.
BaseNode.prototype.compile_reference = function(o) {
BaseNode.prototype.compile_reference = function(o, only_if_necessary) {
var compiled, reference;
if (only_if_necessary && !(this instanceof CallNode || this instanceof ValueNode && (!(this.base instanceof LiteralNode) || this.has_properties()))) {
return [this, this];
}
reference = literal(o.scope.free_variable());
compiled = new AssignNode(reference, this);
return [compiled, reference];
@ -727,47 +731,59 @@
RangeNode.prototype.type = 'RangeNode';
RangeNode.prototype.children = ['from', 'to'];
// Compiles the range's source variables -- where it starts and where it ends.
// But only if they need to be cached to avoid double evaluation.
RangeNode.prototype.compile_variables = function(o) {
var _b, _c, from, to;
this.tab = o.indent;
_b = [o.scope.free_variable(), o.scope.free_variable()];
this.from_var = _b[0];
this.to_var = _b[1];
_c = [this.from.compile(o), this.to.compile(o)];
from = _c[0];
to = _c[1];
return "" + this.from_var + " = " + from + "; " + this.to_var + " = " + to + ";\n" + this.tab;
var _b, _c, parts, tail;
_b = this.from.compile_reference(o, true);
this.from = _b[0];
this.from_var = _b[1];
_c = this.to.compile_reference(o, true);
this.to = _c[0];
this.to_var = _c[1];
parts = [];
if (this.from !== this.from_var) {
parts.push(this.from.compile(o));
}
if (this.to !== this.to_var) {
parts.push(this.to.compile(o));
}
tail = ("\n" + o.indent);
if (parts.length) {
return "" + (parts.join('; ')) + ";" + tail;
} else {
return tail;
}
};
// When compiled normally, the range returns the contents of the *for loop*
// needed to iterate over the values in the range. Used by comprehensions.
RangeNode.prototype.compile_node = function(o) {
var compare, equals, idx, incr, intro, step, vars;
var equals, idx, op, step, vars;
if (!(o.index)) {
return this.compile_array(o);
}
idx = del(o, 'index');
step = del(o, 'step');
vars = ("" + idx + " = " + this.from_var);
vars = ("" + idx + " = " + (this.from_var.compile(o)));
step = step ? step.compile(o) : '1';
equals = this.exclusive ? '' : '=';
intro = ("(" + this.from_var + " <= " + this.to_var + " ? " + idx);
compare = ("" + intro + " <" + equals + " " + this.to_var + " : " + idx + " >" + equals + " " + this.to_var + ")");
incr = ("" + intro + " += " + step + " : " + idx + " -= " + step + ")");
return "" + vars + "; " + compare + "; " + incr;
op = starts(step, '-') ? (">" + equals) : ("<" + equals);
return "" + vars + "; " + (idx) + " " + op + " " + (this.to_var.compile(o)) + "; " + idx + " += " + step;
};
// When used as a value, expand the range into the equivalent array. In the
// future, the code this generates should probably be cleaned up by handwriting
// it instead of wrapping nodes.
// When used as a value, expand the range into the equivalent array.
RangeNode.prototype.compile_array = function(o) {
var arr, body, name;
name = o.scope.free_variable();
body = Expressions.wrap([literal(name)]);
arr = Expressions.wrap([
new ForNode(body, {
source: (new ValueNode(this))
}, literal(name))
]);
return (new ParentheticalNode(new CallNode(new CodeNode([], arr.make_return())))).compile(o);
var body, clause, equals, from, idt, post, pre, to, vars;
idt = this.idt(1);
vars = this.compile_variables(merge(o, {
indent: idt
}));
equals = this.exclusive ? '' : '=';
from = this.from_var.compile(o);
to = this.to_var.compile(o);
clause = ("" + from + " <= " + to + " ?");
pre = ("\n" + (idt) + "a = [];" + (vars));
body = ("var i = " + from + "; (" + clause + " i <" + equals + " " + to + " : i >" + equals + " " + to + "); (" + clause + " i += 1 : i -= 1)");
post = ("a.push(i);\n" + (idt) + "return a;\n" + o.indent);
return "(function(){" + (pre) + "for (" + body + ") " + post + "}).call(this)";
};
return RangeNode;
})();
@ -1504,19 +1520,11 @@
// because other nodes like to check the existence of their variables as well.
// Be careful not to double-evaluate anything.
ExistenceNode.compile_test = function(o, variable) {
var _b, _c, _d, first, second;
_b = [variable, variable];
var _b, first, second;
_b = variable.compile_reference(o, true);
first = _b[0];
second = _b[1];
if (variable instanceof CallNode || (variable instanceof ValueNode && variable.has_properties())) {
_c = variable.compile_reference(o);
first = _c[0];
second = _c[1];
}
_d = [first.compile(o), second.compile(o)];
first = _d[0];
second = _d[1];
return "(typeof " + first + " !== \"undefined\" && " + second + " !== null)";
return "(typeof " + (first.compile(o)) + " !== \"undefined\" && " + (second.compile(o)) + " !== null)";
};
return ExistenceNode;
}).call(this);

View File

@ -48,7 +48,7 @@
// Return the help text for this **OptionParser**, listing and describing all
// of the valid options, for `--help` and such.
OptionParser.prototype.help = function() {
var _a, _b, _c, _d, _e, _f, i, let_part, lines, rule, spaces;
var _a, _b, _c, _d, i, let_part, lines, rule, spaces;
lines = ['Available options:'];
if (this.banner) {
lines.unshift(("" + this.banner + "\n"));
@ -58,8 +58,8 @@
rule = _b[_a];
spaces = 15 - rule.long_flag.length;
spaces = spaces > 0 ? (function() {
_d = []; _e = 0; _f = spaces;
for (i = _e; (_e <= _f ? i <= _f : i >= _f); (_e <= _f ? i += 1 : i -= 1)) {
_d = [];
for (i = 0; i <= spaces; i += 1) {
_d.push(' ');
}
return _d;

View File

@ -146,9 +146,9 @@
var close_calls, stack;
stack = [0];
close_calls = __bind(function(i) {
var _c, _d, size, tmp;
_c = 0; _d = stack[stack.length - 1];
for (tmp = _c; (_c <= _d ? tmp < _d : tmp > _d); (_c <= _d ? tmp += 1 : tmp -= 1)) {
var _c, size, tmp;
(_c = stack[stack.length - 1]);
for (tmp = 0; tmp < _c; tmp += 1) {
this.tokens.splice(i, 0, ['CALL_END', ')', this.tokens[i][2]]);
}
size = stack[stack.length - 1] + 1;
@ -156,7 +156,7 @@
return size;
}, this);
return this.scan_tokens(__bind(function(prev, token, post, i) {
var j, nx, open, size, tag;
var _c, j, nx, open, size, tag;
tag = token[0];
if (tag === 'OUTDENT') {
stack[stack.length - 2] += stack.pop();
@ -181,7 +181,7 @@
}
if (open && !token.generated && (!post || include(IMPLICIT_END, tag))) {
j = 1;
while ((typeof (nx = this.tokens[i + j]) !== "undefined" && (nx = this.tokens[i + j]) !== null) && include(IMPLICIT_END, nx[0])) {
while ((typeof (_c = (nx = this.tokens[i + j])) !== "undefined" && _c !== null) && include(IMPLICIT_END, nx[0])) {
j++;
}
if ((typeof nx !== "undefined" && nx !== null) && nx[0] === ',') {

View File

@ -14,7 +14,7 @@ else
Scope: this.Scope
# Import the helpers we plan to use.
{compact, flatten, merge, del, include, index_of}: helpers
{compact, flatten, merge, del, include, index_of, starts}: helpers
#### BaseNode
@ -61,7 +61,11 @@ exports.BaseNode: class BaseNode
# If the code generation wishes to use the result of a complex expression
# in multiple places, ensure that the expression is only ever evaluated once,
# by assigning it to a temporary variable.
compile_reference: (o) ->
compile_reference: (o, only_if_necessary) ->
if only_if_necessary and not
(this instanceof CallNode or
this instanceof ValueNode and (not (@base instanceof LiteralNode) or @has_properties()))
return [this, this]
reference: literal o.scope.free_variable()
compiled: new AssignNode reference, this
[compiled, reference]
@ -530,11 +534,15 @@ exports.RangeNode: class RangeNode extends BaseNode
@exclusive: !!exclusive
# Compiles the range's source variables -- where it starts and where it ends.
# But only if they need to be cached to avoid double evaluation.
compile_variables: (o) ->
@tab: o.indent
[@from_var, @to_var]: [o.scope.free_variable(), o.scope.free_variable()]
[from, to]: [@from.compile(o), @to.compile(o)]
"$@from_var = $from; $@to_var = $to;\n$@tab"
[@from, @from_var]: @from.compile_reference o, true
[@to, @to_var]: @to.compile_reference o, true
parts: []
parts.push @from.compile o if @from isnt @from_var
parts.push @to.compile o if @to isnt @to_var
tail: "\n$o.indent"
if parts.length then "${parts.join('; ')};$tail" else tail
# When compiled normally, the range returns the contents of the *for loop*
# needed to iterate over the values in the range. Used by comprehensions.
@ -542,22 +550,24 @@ exports.RangeNode: class RangeNode extends BaseNode
return @compile_array(o) unless o.index
idx: del o, 'index'
step: del o, 'step'
vars: "$idx = $@from_var"
vars: "$idx = ${@from_var.compile(o)}"
step: if step then step.compile(o) else '1'
equals: if @exclusive then '' else '='
intro: "($@from_var <= $@to_var ? $idx"
compare: "$intro <$equals $@to_var : $idx >$equals $@to_var)"
incr: "$intro += $step : $idx -= $step)"
"$vars; $compare; $incr"
op: if starts(step, '-') then ">$equals" else "<$equals"
"$vars; ${idx} $op ${@to_var.compile(o)}; $idx += $step"
# When used as a value, expand the range into the equivalent array. In the
# future, the code this generates should probably be cleaned up by handwriting
# it instead of wrapping nodes.
# When used as a value, expand the range into the equivalent array.
compile_array: (o) ->
name: o.scope.free_variable()
body: Expressions.wrap([literal(name)])
arr: Expressions.wrap([new ForNode(body, {source: (new ValueNode(this))}, literal(name))])
(new ParentheticalNode(new CallNode(new CodeNode([], arr.make_return())))).compile(o)
idt: @idt 1
vars: @compile_variables(merge(o, {indent: idt}))
equals: if @exclusive then '' else '='
from: @from_var.compile o
to: @to_var.compile o
clause: "$from <= $to ?"
pre: "\n${idt}a = [];${vars}"
body: "var i = $from; ($clause i <$equals $to : i >$equals $to); ($clause i += 1 : i -= 1)"
post: "a.push(i);\n${idt}return a;\n$o.indent"
"(function(){${pre}for ($body) $post}).call(this)"
#### SliceNode
@ -1122,11 +1132,8 @@ exports.ExistenceNode: class ExistenceNode extends BaseNode
# because other nodes like to check the existence of their variables as well.
# Be careful not to double-evaluate anything.
@compile_test: (o, variable) ->
[first, second]: [variable, variable]
if variable instanceof CallNode or (variable instanceof ValueNode and variable.has_properties())
[first, second]: variable.compile_reference(o)
[first, second]: [first.compile(o), second.compile(o)]
"(typeof $first !== \"undefined\" && $second !== null)"
[first, second]: variable.compile_reference o, true
"(typeof ${first.compile(o)} !== \"undefined\" && ${second.compile(o)} !== null)"
#### ParentheticalNode