diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 8b651143ec0d..60bceba8149f 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -23,6 +23,7 @@ import * as N from "../types"; import LValParser from "./lval"; import { reservedWords } from "../util/identifier"; import type { Pos, Position } from "../util/location"; +import * as charCodes from "charcodes"; export default class ExpressionParser extends LValParser { // Forward-declaration: defined in statement.js @@ -718,6 +719,10 @@ export default class ExpressionParser extends LValParser { // or `{}`. parseExprAtom(refShorthandDefaultPos?: ?Pos): N.Expression { + // If a division operator appears in an expression position, the + // tokenizer got confused, and we force it to read a regexp instead. + if (this.state.type === tt.slash) this.readRegexp(); + const canBeArrow = this.state.potentialArrowAt === this.state.start; let node; @@ -965,9 +970,11 @@ export default class ExpressionParser extends LValParser { parseFunctionExpression(): N.FunctionExpression | N.MetaProperty { const node = this.startNode(); - // We do not do parseIdentifier here because of perf but more importantly - // because parseIdentifier will remove an item from the expression stack - // if function or class is parsed as identifier (in objects e.g.). + // We do not do parseIdentifier here because when parseFunctionExpression + // is called we already know that the current token is a "name" with the value "function" + // This will improve perf a tiny little bit as we do not do validation but more importantly + // here is that parseIdentifier will remove an item from the expression stack + // if "function" or "class" is parsed as identifier (in objects e.g.), which should not happen here. let meta = this.startNode(); this.next(); meta = this.createIdentifier(meta, "function"); @@ -1906,7 +1913,7 @@ export default class ExpressionParser extends LValParser { if ( (name === "class" || name === "function") && (this.state.lastTokEnd !== this.state.lastTokStart + 1 || - this.input.charCodeAt(this.state.lastTokStart) !== 46) + this.input.charCodeAt(this.state.lastTokStart) !== charCodes.dot) ) { this.state.context.pop(); } diff --git a/packages/babel-parser/src/plugins/flow.js b/packages/babel-parser/src/plugins/flow.js index 617a115579bc..bff354249893 100644 --- a/packages/babel-parser/src/plugins/flow.js +++ b/packages/babel-parser/src/plugins/flow.js @@ -6,6 +6,7 @@ import * as N from "../types"; import type { Options } from "../options"; import type { Pos, Position } from "../util/location"; import type State from "../tokenizer/state"; +import { types as tc } from "../tokenizer/context"; import * as charCodes from "charcodes"; import { isIteratorStart } from "../util/identifier"; @@ -2392,7 +2393,10 @@ export default (superClass: Class): Class => refNeedsArrowPos?: ?Pos, ): N.Expression { let jsxError = null; - if (tt.jsxTagStart && this.match(tt.jsxTagStart)) { + if ( + this.hasPlugin("jsx") && + (this.match(tt.jsxTagStart) || this.isRelational("<")) + ) { const state = this.state.clone(); try { return super.parseMaybeAssign( @@ -2408,7 +2412,10 @@ export default (superClass: Class): Class => // Remove `tc.j_expr` and `tc.j_oTag` from context added // by parsing `jsxTagStart` to stop the JSX plugin from // messing with the tokens - this.state.context.length -= 2; + const cLength = this.state.context.length; + if (this.state.context[cLength - 1] === tc.j_oTag) { + this.state.context.length -= 2; + } jsxError = err; } else { diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index d0179db3ee63..0271aeffed9d 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -506,6 +506,15 @@ export default (superClass: Class): Class => return this.parseLiteral(this.state.value, "JSXText"); } else if (this.match(tt.jsxTagStart)) { return this.jsxParseElement(); + } else if ( + this.isRelational("<") && + this.state.input.charCodeAt(this.state.pos) !== + charCodes.exclamationMark + ) { + // In case we encounter an lt token here it will always be the start of + // jsx as the lt sign is not allowed in places that expect an expression + this.finishToken(tt.jsxTagStart); + return this.jsxParseElement(); } else { return super.parseExprAtom(refShortHandDefaultPos); } diff --git a/packages/babel-parser/src/tokenizer/context.js b/packages/babel-parser/src/tokenizer/context.js index 2e30a4f2b768..a224f801b262 100644 --- a/packages/babel-parser/src/tokenizer/context.js +++ b/packages/babel-parser/src/tokenizer/context.js @@ -40,20 +40,6 @@ export const types: { template: new TokContext("`", true, true, p => p.readTmplToken()), functionExpression: new TokContext("function", true), functionStatement: new TokContext("function", false), - functionExpressionGenerator: new TokContext( - "function", - true, - false, - null, - true, - ), - functionStatementGenerator: new TokContext( - "function", - false, - false, - null, - true, - ), }; // Token-specific context update code @@ -72,39 +58,18 @@ tt.parenR.updateContext = tt.braceR.updateContext = function() { this.state.exprAllowed = !out.isExpr; }; -tt.star.updateContext = function(prevType) { - if (prevType === tt._function) { - const index = this.state.context.length - 1; - if (this.state.context[index] === types.functionExpression) { - this.state.context[index] = types.functionExpressionGenerator; - } else { - this.state.context[index] = types.functionStatementGenerator; - } - } - this.state.exprAllowed = true; -}; - tt.name.updateContext = function(prevType) { let allowed = false; if (prevType !== tt.dot) { if ( (this.state.value === "of" && !this.state.exprAllowed) || - (this.state.value === "yield" && this.inGeneratorContext()) + (this.state.value === "yield" && this.state.inGenerator) ) { allowed = true; } } this.state.exprAllowed = allowed; - if (prevType === tt._let || prevType === tt._const || prevType === tt._var) { - if ( - lineBreak.test( - this.input.slice(this.state.end, this.lookahead(true).start), - ) - ) { - this.state.exprAllowed = true; - } - } if (this.state.isIterator) { this.state.isIterator = false; } diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 8eeda2cb2c92..33a272e7dcf0 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -1378,14 +1378,6 @@ export default class Tokenizer extends LocationParser { return !this.state.exprAllowed; } - inGeneratorContext() { - for (let i = this.state.context.length - 1; i >= 1; i--) { - const context = this.state.context[i]; - if (context.token === "function") return context.generator; - } - return false; - } - updateContext(prevType: TokenType): void { const type = this.state.type; let update;