From c056c93e19f4b8d55f06371b38efe9926744d484 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Sun, 15 May 2011 17:05:05 -0400 Subject: [PATCH] Issue #1356 ... range comprehension optimization when a step is present. --- lib/nodes.js | 67 +++++++++++++++++---------------------------- src/nodes.coffee | 71 ++++++++++++++++++++++++++---------------------- 2 files changed, 64 insertions(+), 74 deletions(-) diff --git a/lib/nodes.js b/lib/nodes.js index b40895b8f7..1fb007db9d 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -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); - } - idx = del(o, 'index'); - step = del(o, 'step'); - if (step) { - stepvar = o.scope.freeVariable("step"); - } - 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]; + known = this.fromNum && this.toNum; 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; + varPart = "" + idx + " = " + this.from; + if (this.to !== this.toVar) { + varPart += ", " + this.to; } - 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 === '!=='; diff --git a/src/nodes.coffee b/src/nodes.coffee index 327523a223..da30533361 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -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)?