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