diff --git a/src/expression.js b/src/expression.js index b820a6bf64..086a01c58a 100755 --- a/src/expression.js +++ b/src/expression.js @@ -214,7 +214,12 @@ pp.parseExprSubscripts = function(refShorthandDefaultPos) { } pp.parseSubscripts = function(base, start, noCalls) { - if (this.eat(tt.dot)) { + if (this.eat(tt.doubleColon)) { + let node = this.startNodeAt(start) + node.object = base + node.callee = this.parseNoCallExpr() + return this.parseSubscripts(this.finishNode(node, "BindExpression"), start, noCalls) + } else if (this.eat(tt.dot)) { let node = this.startNodeAt(start) node.object = base node.property = this.parseIdent(true) @@ -240,6 +245,13 @@ pp.parseSubscripts = function(base, start, noCalls) { } return base } +// Parse a no-call expression (like argument of `new` or `::` operators). + +pp.parseNoCallExpr = function() { + let start = this.markPosition() + return this.parseSubscripts(this.parseExprAtom(), start, true) +} + // Parse an atomic expression — either a single token that is an // expression, an expression started by a keyword like `function` or // `new`, or an expression wrapped in punctuation like `()`, `[]`, @@ -363,6 +375,15 @@ pp.parseExprAtom = function(refShorthandDefaultPos) { case tt.backQuote: return this.parseTemplate() + case tt.doubleColon: + node = this.startNode() + this.next() + node.object = null + let callee = node.callee = this.parseNoCallExpr() + if (callee.type !== "MemberExpression") + this.raise(callee.start, "Binding should be performed on object property.") + return this.finishNode(node, "BindExpression") + default: this.unexpected() } @@ -472,8 +493,7 @@ pp.parseNew = function() { this.raise(node.property.start, "The only valid meta property for new is new.target") return this.finishNode(node, "MetaProperty") } - let start = this.markPosition() - node.callee = this.parseSubscripts(this.parseExprAtom(), start, true) + node.callee = this.parseNoCallExpr() if (this.eat(tt.parenL)) node.arguments = this.parseExprList( tt.parenR, this.options.features["es7.trailingFunctionCommas"] diff --git a/src/tokenize.js b/src/tokenize.js index e99fc74080..fc9d706bb8 100755 --- a/src/tokenize.js +++ b/src/tokenize.js @@ -320,7 +320,13 @@ pp.getTokenFromCode = function(code) { case 93: ++this.pos; return this.finishToken(tt.bracketR) case 123: ++this.pos; return this.finishToken(tt.braceL) case 125: ++this.pos; return this.finishToken(tt.braceR) - case 58: ++this.pos; return this.finishToken(tt.colon) + + case 58: + if (this.options.features["es7.functionBind"] && this.input.charCodeAt(this.pos + 1) === 58) + return this.finishOp(tt.doubleColon, 2) + ++this.pos + return this.finishToken(tt.colon) + case 63: ++this.pos; return this.finishToken(tt.question) case 64: ++this.pos; return this.finishToken(tt.at) diff --git a/src/tokentype.js b/src/tokentype.js index 609e508583..05d1d53186 100755 --- a/src/tokentype.js +++ b/src/tokentype.js @@ -54,6 +54,7 @@ export const types = { comma: new TokenType(",", beforeExpr), semi: new TokenType(";", beforeExpr), colon: new TokenType(":", beforeExpr), + doubleColon: new TokenType("::", beforeExpr), dot: new TokenType("."), question: new TokenType("?", beforeExpr), arrow: new TokenType("=>", beforeExpr),