Skip to content

Commit

Permalink
Adjust parsing of exponentation expression
Browse files Browse the repository at this point in the history
  • Loading branch information
marijnh committed Feb 9, 2016
1 parent a8ce3a1 commit df8febd
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 20 deletions.
28 changes: 16 additions & 12 deletions src/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ pp.parseMaybeConditional = function(noIn, refDestructuringErrors) {

pp.parseExprOps = function(noIn, refDestructuringErrors) {
let startPos = this.start, startLoc = this.startLoc
let expr = this.parseMaybeUnary(refDestructuringErrors)
let expr = this.parseMaybeUnary(false, refDestructuringErrors)
if (this.checkExpressionErrors(refDestructuringErrors)) return expr
return this.parseExprOp(expr, startPos, startLoc, -1, noIn)
}
Expand All @@ -161,30 +161,24 @@ pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {
let op = this.type
this.next()
let startPos = this.start, startLoc = this.startLoc
node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, prec, noIn)
node.right = this.parseExprOp(this.parseMaybeUnary(false), startPos, startLoc, prec, noIn)
this.finishNode(node, (op === tt.logicalOR || op === tt.logicalAND) ? "LogicalExpression" : "BinaryExpression")
if (this.options.ecmaVersion >= 7 && op === tt.starstar)
this.checkExponentiationOperand(node.left)
return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn)
}
}
return left
}

pp.checkExponentiationOperand = function(node) {
if (node.type === "UnaryExpression" && node.operator !== "++" && node.operator !== "--")
this.raiseRecoverable(node.start, "Base operand of ** cannot use a unary expression")
}

// Parse unary operators, both prefix and postfix.

pp.parseMaybeUnary = function(refDestructuringErrors) {
pp.parseMaybeUnary = function(sawUnary, refDestructuringErrors) {
if (this.type.prefix) {
sawUnary = true
let node = this.startNode(), update = this.type === tt.incDec
node.operator = this.value
node.prefix = true
this.next()
node.argument = this.parseMaybeUnary()
node.argument = this.parseMaybeUnary(true)
this.checkExpressionErrors(refDestructuringErrors, true)
if (update) this.checkLVal(node.argument)
else if (this.strict && node.operator === "delete" &&
Expand All @@ -196,6 +190,7 @@ pp.parseMaybeUnary = function(refDestructuringErrors) {
let expr = this.parseExprSubscripts(refDestructuringErrors)
if (this.checkExpressionErrors(refDestructuringErrors)) return expr
while (this.type.postfix && !this.canInsertSemicolon()) {
sawUnary = true
let node = this.startNodeAt(startPos, startLoc)
node.operator = this.value
node.prefix = false
Expand All @@ -204,7 +199,16 @@ pp.parseMaybeUnary = function(refDestructuringErrors) {
this.next()
expr = this.finishNode(node, "UpdateExpression")
}
return expr

return !sawUnary && this.eat(tt.starstar) ? this.parseExponent(expr, startPos, startLoc, refDestructuringErrors) : expr
}

pp.parseExponent = function(expr, startPos, startLoc, refDestructuringErrors) {
let node = this.startNodeAt(startPos, startLoc)
node.operator = "**"
node.left = expr
node.right = this.parseMaybeUnary(false, refDestructuringErrors)
return this.finishNode(node, "BinaryExpression")
}

// Parse call, dot, and `[]`-subscript expressions.
Expand Down
9 changes: 9 additions & 0 deletions src/loose/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,15 @@ lp.parseMaybeUnary = function(noIn) {
this.next()
expr = this.finishNode(node, "UpdateExpression")
}

if (this.eat(tt.starstar)) {
let node = this.startNodeAt(start)
node.operator = "**"
node.left = expr
node.right = this.parseMaybeUnary(noIn)
return this.finishNode(node, "BinaryExpression")
}

return expr
}

Expand Down
2 changes: 1 addition & 1 deletion src/tokentype.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const types = {
modulo: binop("%", 10),
star: binop("*", 10),
slash: binop("/", 10),
starstar: binop("**", 11)
starstar: new TokenType("**", {beforeExpr: true})
}

// Map keyword names to token types.
Expand Down
14 changes: 7 additions & 7 deletions test/tests-es7.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,13 @@ test("3 % 5 ** 1", {
testFail("x ** y", "Unexpected token (1:3)", { ecmaVersion: 6 });

// Disallowed unary ops
testFail("delete o.p ** 2;", "Base operand of ** cannot use a unary expression (1:0)", { ecmaVersion: 7 });
testFail("void 2 ** 2;", "Base operand of ** cannot use a unary expression (1:0)", { ecmaVersion: 7 });
testFail("typeof 2 ** 2;", "Base operand of ** cannot use a unary expression (1:0)", { ecmaVersion: 7 });
testFail("~3 ** 2;", "Base operand of ** cannot use a unary expression (1:0)", { ecmaVersion: 7 });
testFail("!1 ** 2;", "Base operand of ** cannot use a unary expression (1:0)", { ecmaVersion: 7 });
testFail("-2** 2;", "Base operand of ** cannot use a unary expression (1:0)", { ecmaVersion: 7 });
testFail("+2** 2;", "Base operand of ** cannot use a unary expression (1:0)", { ecmaVersion: 7 });
testFail("delete o.p ** 2;", "Unexpected token (1:11)", { ecmaVersion: 7 });
testFail("void 2 ** 2;", "Unexpected token (1:7)", { ecmaVersion: 7 });
testFail("typeof 2 ** 2;", "Unexpected token (1:9)", { ecmaVersion: 7 });
testFail("~3 ** 2;", "Unexpected token (1:3)", { ecmaVersion: 7 });
testFail("!1 ** 2;", "Unexpected token (1:3)", { ecmaVersion: 7 });
testFail("-2** 2;", "Unexpected token (1:2)", { ecmaVersion: 7 });
testFail("+2** 2;", "Unexpected token (1:2)", { ecmaVersion: 7 });

// make sure base operand check doesn't affect other operators
test("-a * 5", {
Expand Down

0 comments on commit df8febd

Please sign in to comment.