Issue #1356 ... range comprehension optimization when a step is present.

This commit is contained in:
Jeremy Ashkenas
2011-05-15 17:05:05 -04:00
parent 9e32a5bfa1
commit c056c93e19
2 changed files with 64 additions and 74 deletions

View File

@@ -68,9 +68,9 @@
}
};
Base.prototype.compileLoopReference = function(o, name) {
var src, tmp, _ref2;
var src, tmp;
src = tmp = this.compile(o, LEVEL_LIST);
if (!((-Infinity < (_ref2 = +src) && _ref2 < Infinity) || IDENTIFIER.test(src) && o.scope.check(src, true))) {
if (!((-Infinity < +src && +src < Infinity) || IDENTIFIER.test(src) && o.scope.check(src, true))) {
src = "" + (tmp = o.scope.freeVariable(name)) + " = " + src;
}
return [src, tmp];
@@ -783,60 +783,39 @@
this.equals = this.exclusive ? '' : '=';
}
Range.prototype.compileVariables = function(o) {
var parts, _ref2, _ref3, _ref4;
var step, _ref2, _ref3, _ref4, _ref5;
o = merge(o, {
top: true
});
_ref2 = this.from.cache(o, LEVEL_LIST), this.from = _ref2[0], this.fromVar = _ref2[1];
_ref3 = this.to.cache(o, LEVEL_LIST), this.to = _ref3[0], this.toVar = _ref3[1];
_ref4 = [this.fromVar.match(SIMPLENUM), this.toVar.match(SIMPLENUM)], this.fromNum = _ref4[0], this.toNum = _ref4[1];
parts = [];
if (this.from !== this.fromVar) {
parts.push(this.from);
if (step = del(o, 'step')) {
_ref4 = step.cache(o, LEVEL_LIST), this.step = _ref4[0], this.stepVar = _ref4[1];
}
if (this.to !== this.toVar) {
return parts.push(this.to);
_ref5 = [this.fromVar.match(SIMPLENUM), this.toVar.match(SIMPLENUM)], this.fromNum = _ref5[0], this.toNum = _ref5[1];
if (this.stepVar) {
return this.stepNum = this.stepVar.match(SIMPLENUM);
}
};
Range.prototype.compileNode = function(o) {
var cond, condPart, idx, step, stepPart, stepvar, varPart;
this.compileVariables(o);
var cond, condPart, from, idx, known, stepPart, to, varPart, _ref2;
if (!this.fromVar) {
this.compileVariables(o);
}
if (!o.index) {
return this.compileArray(o);
}
if (this.fromNum && this.toNum) {
return this.compileSimple(o);
}
known = this.fromNum && this.toNum;
idx = del(o, 'index');
step = del(o, 'step');
if (step) {
stepvar = o.scope.freeVariable("step");
varPart = "" + idx + " = " + this.from;
if (this.to !== this.toVar) {
varPart += ", " + this.to;
}
varPart = ("" + idx + " = " + this.from) + (this.to !== this.toVar ? ", " + this.to : '') + (step ? ", " + stepvar + " = " + (step.compile(o)) : '');
cond = "" + this.fromVar + " <= " + this.toVar;
condPart = "" + cond + " ? " + idx + " <" + this.equals + " " + this.toVar + " : " + idx + " >" + this.equals + " " + this.toVar;
stepPart = step ? "" + idx + " += " + stepvar : "" + cond + " ? " + idx + "++ : " + idx + "--";
return "" + varPart + "; " + condPart + "; " + stepPart;
};
Range.prototype.compileSimple = function(o) {
var condPart, from, idx, step, stepPart, stepvar, to, varPart, _ref2;
_ref2 = [+this.fromNum, +this.toNum], from = _ref2[0], to = _ref2[1];
idx = del(o, 'index');
step = del(o, 'step');
if (step) {
stepvar = o.scope.freeVariable("step");
}
varPart = "" + idx + " = " + from;
if (step) {
varPart += ", " + stepvar + " = " + (step.compile(o));
}
condPart = from <= to ? "" + idx + " <" + this.equals + " " + to : "" + idx + " >" + this.equals + " " + to;
if (step) {
stepPart = "" + idx + " += " + stepvar;
}
if (!step) {
stepPart = (from <= to ? "" + idx + "++" : "" + idx + "--");
if (this.step !== this.stepVar) {
varPart += ", " + this.step;
}
condPart = this.stepNum ? condPart = +this.stepNum > 0 ? "" + idx + " <" + this.equals + " " + this.toVar : "" + idx + " >" + this.equals + " " + this.toVar : known ? ((_ref2 = [+this.fromNum, +this.toNum], from = _ref2[0], to = _ref2[1], _ref2), condPart = from <= to ? "" + idx + " <" + this.equals + " " + to : "" + idx + " >" + this.equals + " " + to) : (cond = "" + this.fromVar + " <= " + this.toVar, condPart = "" + cond + " ? " + idx + " <" + this.equals + " " + this.toVar + " : " + idx + " >" + this.equals + " " + this.toVar);
stepPart = this.stepVar ? "" + idx + " += " + this.stepVar : known ? from <= to ? "" + idx + "++" : "" + idx + "--" : "" + cond + " ? " + idx + "++ : " + idx + "--";
return "" + varPart + "; " + condPart + "; " + stepPart;
};
Range.prototype.compileArray = function(o) {
@@ -858,7 +837,7 @@
pre = "\n" + idt + result + " = [];";
if (this.fromNum && this.toNum) {
o.index = i;
body = this.compileSimple(o);
body = this.compileNode(o);
} else {
vars = ("" + i + " = " + this.from) + (this.to !== this.toVar ? ", " + this.to : '');
cond = "" + this.fromVar + " <= " + this.toVar;
@@ -1628,6 +1607,10 @@
Op.prototype.isUnary = function() {
return !this.second;
};
Op.prototype.isComplex = function() {
var _ref2;
return !(this.isUnary() && ((_ref2 = this.operator) === '+' || _ref2 === '-')) || this.first.isComplex();
};
Op.prototype.isChainable = function() {
var _ref2;
return (_ref2 = this.operator) === '<' || _ref2 === '>' || _ref2 === '>=' || _ref2 === '<=' || _ref2 === '===' || _ref2 === '!==';

View File

@@ -632,41 +632,47 @@ exports.Range = class Range extends Base
# 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.
compileVariables: (o) ->
o = merge(o, top: true)
[@from, @fromVar] = @from.cache o, LEVEL_LIST
[@to, @toVar] = @to.cache o, LEVEL_LIST
[@fromNum, @toNum] = [@fromVar.match(SIMPLENUM), @toVar.match(SIMPLENUM)]
parts = []
parts.push @from if @from isnt @fromVar
parts.push @to if @to isnt @toVar
o = merge o, top: true
[@from, @fromVar] = @from.cache o, LEVEL_LIST
[@to, @toVar] = @to.cache o, LEVEL_LIST
[@step, @stepVar] = step.cache o, LEVEL_LIST if step = del o, 'step'
[@fromNum, @toNum] = [@fromVar.match(SIMPLENUM), @toVar.match(SIMPLENUM)]
@stepNum = @stepVar.match(SIMPLENUM) if @stepVar
# When compiled normally, the range returns the contents of the *for loop*
# needed to iterate over the values in the range. Used by comprehensions.
compileNode: (o) ->
@compileVariables o
@compileVariables o unless @fromVar
return @compileArray(o) unless o.index
return @compileSimple(o) if @fromNum and @toNum
# Set up endpoints.
known = @fromNum and @toNum
idx = del o, 'index'
step = del o, 'step'
stepvar = o.scope.freeVariable "step" if step
varPart = "#{idx} = #{@from}" + ( if @to isnt @toVar then ", #{@to}" else '' ) + if step then ", #{stepvar} = #{step.compile(o)}" else ''
cond = "#{@fromVar} <= #{@toVar}"
condPart = "#{cond} ? #{idx} <#{@equals} #{@toVar} : #{idx} >#{@equals} #{@toVar}"
stepPart = if step then "#{idx} += #{stepvar}" else "#{cond} ? #{idx}++ : #{idx}--"
"#{varPart}; #{condPart}; #{stepPart}"
# Compile a simple range comprehension, with integers.
compileSimple: (o) ->
[from, to] = [+@fromNum, +@toNum]
idx = del o, 'index'
step = del o, 'step'
stepvar = o.scope.freeVariable "step" if step
varPart = "#{idx} = #{from}"
varPart += ", #{stepvar} = #{step.compile(o)}" if step
condPart = if from <= to then "#{idx} <#{@equals} #{to}" else "#{idx} >#{@equals} #{to}"
stepPart = "#{idx} += #{stepvar}" if step
stepPart = ( if from <= to then "#{idx}++" else "#{idx}--" ) if not step
varPart = "#{idx} = #{@from}"
varPart += ", #{@to}" if @to isnt @toVar
varPart += ", #{@step}" if @step isnt @stepVar
# Generate the condition.
condPart = if @stepNum
condPart = if +@stepNum > 0 then "#{idx} <#{@equals} #{@toVar}" else "#{idx} >#{@equals} #{@toVar}"
else if known
[from, to] = [+@fromNum, +@toNum]
condPart = if from <= to then "#{idx} <#{@equals} #{to}" else "#{idx} >#{@equals} #{to}"
else
cond = "#{@fromVar} <= #{@toVar}"
condPart = "#{cond} ? #{idx} <#{@equals} #{@toVar} : #{idx} >#{@equals} #{@toVar}"
# Generate the step.
stepPart = if @stepVar
"#{idx} += #{@stepVar}"
else if known
if from <= to then "#{idx}++" else "#{idx}--"
else
"#{cond} ? #{idx}++ : #{idx}--"
# The final loop body.
"#{varPart}; #{condPart}; #{stepPart}"
# When used as a value, expand the range into the equivalent array.
compileArray: (o) ->
@@ -680,7 +686,7 @@ exports.Range = class Range extends Base
pre = "\n#{idt}#{result} = [];"
if @fromNum and @toNum
o.index = i
body = @compileSimple o
body = @compileNode o
else
vars = "#{i} = #{@from}" + if @to isnt @toVar then ", #{@to}" else ''
cond = "#{@fromVar} <= #{@toVar}"
@@ -1222,9 +1228,7 @@ exports.While = class While extends Base
# Simple Arithmetic and logical operations. Performs some conversion from
# CoffeeScript operations into their JavaScript equivalents.
exports.Op = class Op extends Base
constructor: (op, first, second, flip, @isExistentialEquals ) ->
return new In first, second if op is 'in'
if op is 'do'
@@ -1257,6 +1261,9 @@ exports.Op = class Op extends Base
isUnary: ->
not @second
isComplex: ->
not (@isUnary() and (@operator in ['+', '-'])) or @first.isComplex()
# Am I capable of
# [Python-style comparison chaining](http://docs.python.org/reference/expressions.html#notin)?