Fix #4877: Exponentiation operators (#4881)

* Passthrough exponentiation operator; remove tests that are invalid JavaScript

* Treat **= as a passthrough assignment

* Get tests passing in Node 6

* Improve scoping

* Move exponentiation tests into their own file, now that it's filtered out by Cakefile

* Restore original test
This commit is contained in:
Geoffrey Booth 2018-03-30 00:47:40 -07:00 committed by GitHub
parent e5aa758dda
commit 195a46ab77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 27 additions and 41 deletions

View File

@ -3318,11 +3318,11 @@
if ((ref1 = this.context) === '||=' || ref1 === '&&=' || ref1 === '?=') {
return this.compileConditional(o);
}
if ((ref2 = this.context) === '**=' || ref2 === '//=' || ref2 === '%%=') {
if ((ref2 = this.context) === '//=' || ref2 === '%%=') {
return this.compileSpecialMath(o);
}
}
if (!this.context) {
if (!this.context || this.context === '**=') {
varBase = this.variable.unwrapAll();
if (!varBase.isAssignable()) {
this.variable.error(`'${this.variable.compile(o)}' can't be assigned`);
@ -3791,7 +3791,7 @@
}
}
// Convert special math assignment operators like `a **= b` to the equivalent
// Convert special math assignment operators like `a //= b` to the equivalent
// extended form `a = a ** b` and then compiles that.
compileSpecialMath(o) {
var left, right;
@ -4800,8 +4800,6 @@
switch (this.operator) {
case '?':
return this.compileExistence(o, this.second.isDefaultValue);
case '**':
return this.compilePower(o);
case '//':
return this.compileFloorDivision(o);
case '%%':
@ -4903,13 +4901,6 @@
return this.joinFragmentArrays(parts, '');
}
compilePower(o) {
var pow;
// Make a Math.pow call
pow = new Value(new IdentifierLiteral('Math'), [new Access(new PropertyName('pow'))]);
return new Call(pow, [this.first, this.second]).compileToFragments(o);
}
compileFloorDivision(o) {
var div, floor, second;
floor = new Value(new IdentifierLiteral('Math'), [new Access(new PropertyName('floor'))]);

View File

@ -2222,9 +2222,9 @@ exports.Assign = class Assign extends Base
return @compileSplice o if @variable.isSplice()
return @compileConditional o if @context in ['||=', '&&=', '?=']
return @compileSpecialMath o if @context in ['**=', '//=', '%%=']
return @compileSpecialMath o if @context in ['//=', '%%=']
unless @context
if not @context or @context is '**='
varBase = @variable.unwrapAll()
unless varBase.isAssignable()
@variable.error "'#{@variable.compile o}' can't be assigned"
@ -2548,7 +2548,7 @@ exports.Assign = class Assign extends Base
fragments = new Op(@context[...-1], left, new Assign(right, @value, '=')).compileToFragments o
if o.level <= LEVEL_LIST then fragments else @wrapInParentheses fragments
# Convert special math assignment operators like `a **= b` to the equivalent
# Convert special math assignment operators like `a //= b` to the equivalent
# extended form `a = a ** b` and then compiles that.
compileSpecialMath: (o) ->
[left, right] = @variable.cacheReference o
@ -3253,7 +3253,6 @@ exports.Op = class Op extends Base
return @compileChain o if isChain
switch @operator
when '?' then @compileExistence o, @second.isDefaultValue
when '**' then @compilePower o
when '//' then @compileFloorDivision o
when '%%' then @compileModulo o
else
@ -3320,11 +3319,6 @@ exports.Op = class Op extends Base
parts.push [@makeCode ")"] if o.level >= LEVEL_PAREN
@joinFragmentArrays parts, ''
compilePower: (o) ->
# Make a Math.pow call
pow = new Value new IdentifierLiteral('Math'), [new Access new PropertyName 'pow']
new Call(pow, [@first, @second]).compileToFragments o
compileFloorDivision: (o) ->
floor = new Value new IdentifierLiteral('Math'), [new Access new PropertyName 'floor']
second = if @second.shouldCache() then new Parens @second else @second

View File

@ -0,0 +1,19 @@
# The `**` and `**=` operators are only supported in Node 7.5+, so the tests
# for these exponentiation operators are split out into their own file to be
# loaded only by supported runtimes.
test "exponentiation operator", ->
eq 27, 3 ** 3
test "exponentiation operator has higher precedence than other maths operators", ->
eq 55, 1 + 3 ** 3 * 2
eq -4, -2 ** 2
eq 0, (!2) ** 2
test "exponentiation operator is right associative", ->
eq 2, 2 ** 1 ** 3
test "exponentiation operator compound assignment", ->
a = 2
a **= 3
eq 8, a

View File

@ -168,7 +168,7 @@ test "`throw` can be yielded", ->
throws -> x.next()
test "symbolic operators has precedence over the `yield`", ->
symbolic = '+ - * / << >> & | || && ** ^ // or and'.split ' '
symbolic = '+ - * / << >> & | || && ^ // or and'.split ' '
compound = ("#{op}=" for op in symbolic)
relations = '< > == != <= >= is isnt'.split ' '

View File

@ -139,7 +139,7 @@ eq 'multiline nested "interpolations" work', """multiline #{
eq 'function(){}', "#{->}".replace /\s/g, ''
ok /^a[\s\S]+b$/.test "a#{=>}b"
ok /^a[\s\S]+b$/.test "a#{ (x) -> x ** 2 }b"
ok /^a[\s\S]+b$/.test "a#{ (x) -> x %% 2 }b"
# Regular Expression Interpolation

View File

@ -305,24 +305,6 @@ test "#2508: Existential access of the prototype", ->
eq NonExistent?::nothing, undefined
ok Object?::toString
test "power operator", ->
eq 27, 3 ** 3
test "power operator has higher precedence than other maths operators", ->
eq 55, 1 + 3 ** 3 * 2
eq -4, -2 ** 2
eq false, !2 ** 2
eq 0, (!2) ** 2
eq -2, ~1 ** 5
test "power operator is right associative", ->
eq 2, 2 ** 1 ** 3
test "power operator compound assignment", ->
a = 2
a **= 3
eq 8, a
test "floor division operator", ->
eq 2, 7 // 3
eq -3, -7 // 3