diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 4a45ee2dc1e0..7327fadcf045 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -19,7 +19,6 @@ // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser import { types as tt, type TokenType } from "../tokenizer/types"; -import { types as ct } from "../tokenizer/context"; import * as N from "../types"; import LValParser from "./lval"; import { @@ -2325,19 +2324,6 @@ export default class ExpressionParser extends LValParser { name = this.state.value; } else if (type.keyword) { name = type.keyword; - - // `class` and `function` keywords push function-type token context into this.context. - // But there is no chance to pop the context if the keyword is consumed - // as an identifier such as a property name. - if (type === tt._class || type === tt._function) { - const curContext = this.curContext(); - if ( - curContext === ct.functionStatement || - curContext === ct.functionExpression - ) { - this.state.context.pop(); - } - } } else { throw this.unexpected(); } diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 099db9c8ca41..3bf6f4082d7e 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -2847,9 +2847,10 @@ export default (superClass: Class): Class => // by parsing `jsxTagStart` to stop the JSX plugin from // messing with the tokens const { context } = this.state; - if (context[context.length - 1] === tc.j_oTag) { + const curContext = context[context.length - 1]; + if (curContext === tc.j_oTag) { context.length -= 2; - } else if (context[context.length - 1] === tc.j_expr) { + } else if (curContext === tc.j_expr) { context.length -= 1; } } diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index ca06064682dc..df3dbcbd0663 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -55,8 +55,10 @@ tt.jsxTagStart = new TokenType("jsxTagStart", { startsExpr: true }); tt.jsxTagEnd = new TokenType("jsxTagEnd"); tt.jsxTagStart.updateContext = context => { - context.push(tc.j_expr); // treat as beginning of JSX expression - context.push(tc.j_oTag); // start opening tag context + context.push( + tc.j_expr, // treat as beginning of JSX expression + tc.j_oTag, // start opening tag context + ); }; function isFragment(object: ?N.JSXElement): boolean { @@ -616,17 +618,10 @@ export default (superClass: Class): Class => updateContext(prevType: TokenType): void { super.updateContext(prevType); const { context, type } = this.state; - if (type === tt.braceL) { - const curContext = context[context.length - 1]; - if (curContext === tc.j_oTag) { - context.push(tc.brace); - } else if (curContext === tc.j_expr) { - context.push(tc.templateQuasi); - } - this.state.exprAllowed = true; - } else if (type === tt.slash && prevType === tt.jsxTagStart) { - context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore - context.push(tc.j_cTag); // reconsider as closing tag context + if (type === tt.slash && prevType === tt.jsxTagStart) { + // do not consider JSX expr -> JSX open tag -> ... anymore + // reconsider as closing tag context + context.splice(-2, 2, tc.j_cTag); this.state.exprAllowed = false; } else if (type === tt.jsxTagEnd) { const out = context.pop(); diff --git a/packages/babel-parser/src/tokenizer/context.js b/packages/babel-parser/src/tokenizer/context.js index 4543b2e19aa3..47aee4df879b 100644 --- a/packages/babel-parser/src/tokenizer/context.js +++ b/packages/babel-parser/src/tokenizer/context.js @@ -1,8 +1,7 @@ // @flow -// The token context is used to track whether `}` matches -// a template quasi `${` or other tokens containing `{`: -// namely tt.braceL `{` and tt.braceHashL `#{` +// The token context is used to track whether the apostrophe "`" +// starts or ends a string template import { types as tt } from "./types"; @@ -20,7 +19,6 @@ export const types: { [key: string]: TokContext, } = { brace: new TokContext("{"), - templateQuasi: new TokContext("${"), template: new TokContext("`", true), }; @@ -35,19 +33,22 @@ export const types: { // `this.prodParam` still has `[Yield]` production because it is not yet updated tt.braceR.updateContext = context => { - if (context.length > 1) { - context.pop(); - } + context.pop(); }; // we don't need to update context for tt.braceBarL because we do not pop context for tt.braceBarR -tt.braceL.updateContext = tt.braceHashL.updateContext = context => { - context.push(types.brace); -}; - -tt.dollarBraceL.updateContext = context => { - context.push(types.templateQuasi); -}; +// ideally only dollarBraceL "${" needs a non-template context +// in order to indicate that the last "`" in `${`" starts a new string template +// inside a template element within outer string template. +// but when we popped such context in `}`, we lost track of whether this +// `}` matches a `${` or other tokens matching `}`, so we have to push +// such context in every token that `}` will match. +tt.braceL.updateContext = + tt.braceHashL.updateContext = + tt.dollarBraceL.updateContext = + context => { + context.push(types.brace); + }; tt.backQuote.updateContext = context => { if (context[context.length - 1] === types.template) { diff --git a/packages/babel-parser/src/tokenizer/state.js b/packages/babel-parser/src/tokenizer/state.js index a8c8f80e4a65..c6775ede56eb 100644 --- a/packages/babel-parser/src/tokenizer/state.js +++ b/packages/babel-parser/src/tokenizer/state.js @@ -127,10 +127,10 @@ export default class State { lastTokStart: number = 0; lastTokEnd: number = 0; - // The context stack is used to superficially track syntactic - // context to predict whether a regular expression is allowed in a - // given position. + // The context stack is used to track whether the apostrophe "`" starts + // or ends a string template context: Array = [ct.brace]; + // Used to track whether a JSX element is allowed to form exprAllowed: boolean = true; // Used to signal to callers of `readWord1` whether the word