babel/src/tokenizer/context.js
2017-04-27 10:53:17 -04:00

107 lines
2.9 KiB
JavaScript

// @flow
// The algorithm used to determine whether a regexp can appear at a
// given point in the program is loosely based on sweet.js' approach.
// See https://github.com/mozilla/sweet.js/wiki/design
import { types as tt } from "./types";
import { lineBreak } from "../util/whitespace";
export class TokContext {
constructor(
token: string,
isExpr?: boolean,
preserveSpace?: boolean,
override?: Function,
) {
this.token = token;
this.isExpr = !!isExpr;
this.preserveSpace = !!preserveSpace;
this.override = override;
}
token: string;
isExpr: boolean;
preserveSpace: boolean;
override: ?Function;
}
export const types: {
[key: string]: TokContext;
} = {
braceStatement: new TokContext("{", false),
braceExpression: new TokContext("{", true),
templateQuasi: new TokContext("${", true),
parenStatement: new TokContext("(", false),
parenExpression: new TokContext("(", true),
template: new TokContext("`", true, true, (p) => p.readTmplToken()),
functionExpression: new TokContext("function", true)
};
// Token-specific context update code
tt.parenR.updateContext = tt.braceR.updateContext = function () {
if (this.state.context.length === 1) {
this.state.exprAllowed = true;
return;
}
const out = this.state.context.pop();
if (out === types.braceStatement && this.curContext() === types.functionExpression) {
this.state.context.pop();
this.state.exprAllowed = false;
} else if (out === types.templateQuasi) {
this.state.exprAllowed = true;
} else {
this.state.exprAllowed = !out.isExpr;
}
};
tt.name.updateContext = function (prevType) {
this.state.exprAllowed = false;
if (prevType === tt._let || prevType === tt._const || prevType === tt._var) {
if (lineBreak.test(this.input.slice(this.state.end))) {
this.state.exprAllowed = true;
}
}
};
tt.braceL.updateContext = function (prevType) {
this.state.context.push(this.braceIsBlock(prevType) ? types.braceStatement : types.braceExpression);
this.state.exprAllowed = true;
};
tt.dollarBraceL.updateContext = function () {
this.state.context.push(types.templateQuasi);
this.state.exprAllowed = true;
};
tt.parenL.updateContext = function (prevType) {
const statementParens = prevType === tt._if || prevType === tt._for ||
prevType === tt._with || prevType === tt._while;
this.state.context.push(statementParens ? types.parenStatement : types.parenExpression);
this.state.exprAllowed = true;
};
tt.incDec.updateContext = function () {
// tokExprAllowed stays unchanged
};
tt._function.updateContext = function () {
if (this.curContext() !== types.braceStatement) {
this.state.context.push(types.functionExpression);
}
this.state.exprAllowed = false;
};
tt.backQuote.updateContext = function () {
if (this.curContext() === types.template) {
this.state.context.pop();
} else {
this.state.context.push(types.template);
}
this.state.exprAllowed = false;
};