Merge branch 'master' into implement-smart-pipeline-in-parser

* master: (222 commits)
  Set correct methods name
  Use toPropertyKey in the "decorate" helper
  Allow function types in type params within arrow return types (#8954)
  Fix message when plugin of a wrong type is passed (#8950)
  rename colliding let bindings with for loop init (#8937)
  edge incomplete support for arrow destructuring (babel #8349) (#8926)
  fix single-arg async arrows when retainLines=true (#8868)
  [flow] Explicit inexact objects with `...` (#8884)
  Update preset-env data (#8898)
  Treat break inside block inside loop (#8914)
  fixed "source map" formatting in comment (#8878) [skip ci]
  fix typo in contributing guidelines (#8901) [skip ci]
  fix: Expression x === 'y' && '' should not evaluate to undefined. (#8880)
  fixed an extra word
  Fixes #8865 (#8866)
  v7.1.4
  v7.1.3
  Bump Babel deps (#8770)
  flow-bin@0.82.0 (#8832)
  Insertafter jsx fix (#8833)
  ...

# Conflicts:
#	packages/babel-parser/src/tokenizer/index.js
#	packages/babel-parser/test/fixtures/experimental/class-private-properties/failure-numeric-literal/options.json
#	packages/babel-parser/test/fixtures/experimental/pipeline-operator/invalid-proposal/options.json
This commit is contained in:
mAAdhaTTah
2018-11-03 14:00:12 -04:00
1882 changed files with 21779 additions and 6653 deletions

View File

@@ -5,8 +5,10 @@ import type { PluginList } from "./plugin-utils";
// A second optional argument can be given to further configure
// the parser process. These options are recognized:
export type SourceType = "script" | "module" | "unambiguous";
export type Options = {
sourceType: "script" | "module",
sourceType: SourceType,
sourceFilename?: string,
startLine: number,
allowAwaitOutsideFunction: boolean,

View File

@@ -122,6 +122,15 @@ export default class CommentsParser extends BaseParser {
lastComment.end <= node.end
) {
if (this.state.commentPreviousNode) {
for (j = 0; j < this.state.leadingComments.length; j++) {
if (
this.state.leadingComments[j].end <
this.state.commentPreviousNode.end
) {
this.state.leadingComments.splice(j, 1);
j--;
}
}
if (this.state.leadingComments.length > 0) {
lastArg.trailingComments = this.state.leadingComments;
this.state.leadingComments = [];

View File

@@ -609,22 +609,41 @@ export default class ExpressionParser extends LValParser {
}
return node;
} else if (this.match(tt.backQuote)) {
const node = this.startNodeAt(startPos, startLoc);
node.tag = base;
node.quasi = this.parseTemplate(true);
if (state.optionalChainMember) {
this.raise(
startPos,
"Tagged Template Literals are not allowed in optionalChain",
);
}
return this.finishNode(node, "TaggedTemplateExpression");
return this.parseTaggedTemplateExpression(
startPos,
startLoc,
base,
state,
);
} else {
state.stop = true;
return base;
}
}
parseTaggedTemplateExpression(
startPos: number,
startLoc: Position,
base: N.Expression,
state: N.ParseSubscriptState,
typeArguments?: ?N.TsTypeParameterInstantiation,
): N.TaggedTemplateExpression {
const node: N.TaggedTemplateExpression = this.startNodeAt(
startPos,
startLoc,
);
node.tag = base;
node.quasi = this.parseTemplate(true);
if (typeArguments) node.typeParameters = typeArguments;
if (state.optionalChainMember) {
this.raise(
startPos,
"Tagged Template Literals are not allowed in optionalChain",
);
}
return this.finishNode(node, "TaggedTemplateExpression");
}
atPossibleAsync(base: N.Expression): boolean {
return (
!this.state.containsEsc &&
@@ -821,7 +840,12 @@ export default class ExpressionParser extends LValParser {
) {
this.next();
return this.parseFunction(node, false, false, true);
} else if (canBeArrow && id.name === "async" && this.match(tt.name)) {
} else if (
canBeArrow &&
!this.canInsertSemicolon() &&
id.name === "async" &&
this.match(tt.name)
) {
const oldYield = this.state.yieldInPossibleArrowParameters;
this.state.yieldInPossibleArrowParameters = null;
const params = [this.parseIdentifier()];
@@ -977,7 +1001,19 @@ export default class ExpressionParser extends LValParser {
if (isPrivate) {
this.expectOnePlugin(["classPrivateProperties", "classPrivateMethods"]);
const node = this.startNode();
const columnHashEnd = this.state.end;
this.next();
const columnIdentifierStart = this.state.start;
const spacesBetweenHashAndIdentifier =
columnIdentifierStart - columnHashEnd;
if (spacesBetweenHashAndIdentifier != 0) {
this.raise(
columnIdentifierStart,
"Unexpected space between # and identifier",
);
}
node.id = this.parseIdentifier(true);
return this.finishNode(node, "PrivateName");
} else {
@@ -1037,7 +1073,7 @@ export default class ExpressionParser extends LValParser {
} else if (!this.hasPlugin("importMeta")) {
this.raise(
id.start,
`Dynamic imports require a parameter: import('a.js').then`,
`Dynamic imports require a parameter: import('a.js')`,
);
}
}

View File

@@ -1,7 +1,7 @@
// @flow
import type { Options } from "../options";
import type { File } from "../types";
import type { File, JSXOpeningElement } from "../types";
import type { PluginList } from "../plugin-utils";
import { getOptions } from "../options";
import StatementParser from "./statement";
@@ -11,6 +11,11 @@ export type PluginsMap = {
};
export default class Parser extends StatementParser {
// Forward-declaration so typescript plugin can override jsx plugin
+jsxParseOpeningElementAfterName: (
node: JSXOpeningElement,
) => JSXOpeningElement;
constructor(options: ?Options, input: string) {
options = getOptions(options);
super(options, input);

View File

@@ -3,7 +3,8 @@
import * as N from "../types";
import { types as tt, type TokenType } from "../tokenizer/types";
import ExpressionParser from "./expression";
import { lineBreak } from "../util/whitespace";
import { isIdentifierChar } from "../util/identifier";
import { lineBreak, skipWhiteSpace } from "../util/whitespace";
// Reused empty array added for node fields that are always empty.
@@ -304,15 +305,7 @@ export default class StatementParser extends ExpressionParser {
}
}
if (this.eat(tt.parenL)) {
const node = this.startNodeAt(startPos, startLoc);
node.callee = expr;
node.arguments = this.parseCallExpressionArguments(tt.parenR, false);
this.toReferencedList(node.arguments);
expr = this.finishNode(node, "CallExpression");
}
node.expression = expr;
node.expression = this.parseMaybeDecoratorArguments(expr);
this.state.decoratorStack.pop();
} else {
node.expression = this.parseMaybeAssign();
@@ -320,6 +313,18 @@ export default class StatementParser extends ExpressionParser {
return this.finishNode(node, "Decorator");
}
parseMaybeDecoratorArguments(expr: N.Expression): N.Expression {
if (this.eat(tt.parenL)) {
const node = this.startNodeAtNode(expr);
node.callee = expr;
node.arguments = this.parseCallExpressionArguments(tt.parenR, false);
this.toReferencedList(node.arguments);
return this.finishNode(node, "CallExpression");
}
return expr;
}
parseBreakContinueStatement(
node: N.BreakStatement | N.ContinueStatement,
keyword: string,
@@ -1497,18 +1502,37 @@ export default class StatementParser extends ExpressionParser {
return this.finishNode(node, "ExportNamedDeclaration");
}
isAsyncFunction() {
if (!this.isContextual("async")) return false;
const { input, pos } = this.state;
skipWhiteSpace.lastIndex = pos;
const skip = skipWhiteSpace.exec(input);
if (!skip || !skip.length) return false;
const next = pos + skip[0].length;
return (
!lineBreak.test(input.slice(pos, next)) &&
input.slice(next, next + 8) === "function" &&
(next + 8 === input.length || !isIdentifierChar(input.charAt(next + 8)))
);
}
parseExportDefaultExpression(): N.Expression | N.Declaration {
const expr = this.startNode();
if (this.eat(tt._function)) {
return this.parseFunction(expr, true, false, false, true);
} else if (
this.isContextual("async") &&
this.lookahead().type === tt._function
) {
// async function declaration
this.eatContextual("async");
this.eat(tt._function);
return this.parseFunction(expr, true, false, true, true);
const isAsync = this.isAsyncFunction();
if (this.eat(tt._function) || isAsync) {
if (isAsync) {
this.eatContextual("async");
this.expect(tt._function);
}
return this.parseFunction(expr, true, false, isAsync, true);
} else if (this.match(tt._class)) {
return this.parseClass(expr, true, true);
} else if (this.match(tt.at)) {
@@ -1641,7 +1665,7 @@ export default class StatementParser extends ExpressionParser {
this.state.type.keyword === "let" ||
this.state.type.keyword === "function" ||
this.state.type.keyword === "class" ||
this.isContextual("async")
this.isAsyncFunction()
);
}

View File

@@ -41,13 +41,28 @@ export function getPluginOption(
const PIPELINE_PROPOSALS = ["minimal", "smart"];
export function validatePlugins(plugins: PluginList) {
if (
hasPlugin(plugins, "decorators") &&
hasPlugin(plugins, "decorators-legacy")
) {
throw new Error(
"Cannot use the decorators and decorators-legacy plugin together",
if (hasPlugin(plugins, "decorators")) {
if (hasPlugin(plugins, "decorators-legacy")) {
throw new Error(
"Cannot use the decorators and decorators-legacy plugin together",
);
}
const decoratorsBeforeExport = getPluginOption(
plugins,
"decorators",
"decoratorsBeforeExport",
);
if (decoratorsBeforeExport == null) {
throw new Error(
"The 'decorators' plugin requires a 'decoratorsBeforeExport' option," +
" whose value must be a boolean. If you are migrating from" +
" Babylon/Babel 6 or want to use the old decorators proposal, you" +
" should use the 'decorators-legacy' plugin instead of 'decorators'.",
);
} else if (typeof decoratorsBeforeExport !== "boolean") {
throw new Error("'decoratorsBeforeExport' must be a boolean.");
}
}
if (hasPlugin(plugins, "flow") && hasPlugin(plugins, "typescript")) {

View File

@@ -463,7 +463,13 @@ export default (superClass: Class<Parser>): Class<Parser> =>
} while (this.eat(tt.comma));
}
node.body = this.flowParseObjectType(true, false, false, isClass);
node.body = this.flowParseObjectType({
allowStatic: isClass,
allowExact: false,
allowSpread: false,
allowProto: isClass,
allowInexact: false,
});
}
flowParseInterfaceExtends(): N.FlowInterfaceExtends {
@@ -632,12 +638,15 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.state.inType = true;
this.expectRelational("<");
const oldNoAnonFunctionType = this.state.noAnonFunctionType;
this.state.noAnonFunctionType = false;
while (!this.isRelational(">")) {
node.params.push(this.flowParseType());
if (!this.isRelational(">")) {
this.expect(tt.comma);
}
}
this.state.noAnonFunctionType = oldNoAnonFunctionType;
this.expectRelational(">");
this.state.inType = oldInType;
@@ -656,7 +665,13 @@ export default (superClass: Class<Parser>): Class<Parser> =>
} while (this.eat(tt.comma));
}
node.body = this.flowParseObjectType(true, false, false, false);
node.body = this.flowParseObjectType({
allowStatic: false,
allowExact: false,
allowSpread: false,
allowProto: false,
allowInexact: false,
});
return this.finishNode(node, "InterfaceTypeAnnotation");
}
@@ -754,12 +769,19 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return this.finishNode(node, "ObjectTypeCallProperty");
}
flowParseObjectType(
flowParseObjectType({
allowStatic,
allowExact,
allowSpread,
allowProto,
allowInexact,
}: {
allowStatic: boolean,
allowExact: boolean,
allowSpread: boolean,
allowProto: boolean,
): N.FlowObjectTypeAnnotation {
allowInexact: boolean,
}): N.FlowObjectTypeAnnotation {
const oldInType = this.state.inType;
this.state.inType = true;
@@ -772,6 +794,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
let endDelim;
let exact;
let inexact = false;
if (allowExact && this.match(tt.braceBarL)) {
this.expect(tt.braceBarL);
endDelim = tt.braceBarR;
@@ -852,16 +875,21 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}
}
nodeStart.properties.push(
this.flowParseObjectTypeProperty(
node,
isStatic,
protoStart,
variance,
kind,
allowSpread,
),
const propOrInexact = this.flowParseObjectTypeProperty(
node,
isStatic,
protoStart,
variance,
kind,
allowSpread,
allowInexact,
);
if (propOrInexact === null) {
inexact = true;
} else {
nodeStart.properties.push(propOrInexact);
}
}
this.flowObjectTypeSemicolon();
@@ -869,6 +897,15 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.expect(endDelim);
/* The inexact flag should only be added on ObjectTypeAnnotations that
* are not the body of an interface, declare interface, or declare class.
* Since spreads are only allowed in objec types, checking that is
* sufficient here.
*/
if (allowSpread) {
nodeStart.inexact = inexact;
}
const out = this.finishNode(nodeStart, "ObjectTypeAnnotation");
this.state.inType = oldInType;
@@ -883,7 +920,8 @@ export default (superClass: Class<Parser>): Class<Parser> =>
variance: ?N.FlowVariance,
kind: string,
allowSpread: boolean,
): N.FlowObjectTypeProperty | N.FlowObjectTypeSpreadProperty {
allowInexact: boolean,
): (N.FlowObjectTypeProperty | N.FlowObjectTypeSpreadProperty) | null {
if (this.match(tt.ellipsis)) {
if (!allowSpread) {
this.unexpected(
@@ -901,8 +939,30 @@ export default (superClass: Class<Parser>): Class<Parser> =>
);
}
this.expect(tt.ellipsis);
node.argument = this.flowParseType();
const isInexactToken = this.eat(tt.comma) || this.eat(tt.semi);
if (this.match(tt.braceR)) {
if (allowInexact) return null;
this.unexpected(
null,
"Explicit inexact syntax is only allowed inside inexact objects",
);
}
if (this.match(tt.braceBarR)) {
this.unexpected(
null,
"Explicit inexact syntax cannot appear inside an explicit exact object type",
);
}
if (isInexactToken) {
this.unexpected(
null,
"Explicit inexact syntax must appear at the end of an inexact object",
);
}
node.argument = this.flowParseType();
return this.finishNode(node, "ObjectTypeSpreadProperty");
} else {
node.key = this.flowParseObjectPropertyKey();
@@ -1146,10 +1206,22 @@ export default (superClass: Class<Parser>): Class<Parser> =>
);
case tt.braceL:
return this.flowParseObjectType(false, false, true, false);
return this.flowParseObjectType({
allowStatic: false,
allowExact: false,
allowSpread: true,
allowProto: false,
allowInexact: true,
});
case tt.braceBarL:
return this.flowParseObjectType(false, true, true, false);
return this.flowParseObjectType({
allowStatic: false,
allowExact: true,
allowSpread: true,
allowProto: false,
allowInexact: false,
});
case tt.bracketL:
return this.flowParseTupleType();
@@ -1917,6 +1989,15 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return super.parseClassProperty(node);
}
parseClassPrivateProperty(
node: N.ClassPrivateProperty,
): N.ClassPrivateProperty {
if (this.match(tt.colon)) {
node.typeAnnotation = this.flowParseTypeAnnotation();
}
return super.parseClassPrivateProperty(node);
}
// determine whether or not we're currently in the position where a class method would appear
isClassMethod(): boolean {
return this.isRelational("<") || super.isClassMethod();

View File

@@ -358,11 +358,18 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.expect(tt.jsxTagEnd);
return this.finishNode(node, "JSXOpeningFragment");
}
node.attributes = [];
node.name = this.jsxParseElementName();
return this.jsxParseOpeningElementAfterName(node);
}
jsxParseOpeningElementAfterName(
node: N.JSXOpeningElement,
): N.JSXOpeningElement {
const attributes: N.JSXAttribute[] = [];
while (!this.match(tt.slash) && !this.match(tt.jsxTagEnd)) {
node.attributes.push(this.jsxParseAttribute());
attributes.push(this.jsxParseAttribute());
}
node.attributes = attributes;
node.selfClosing = this.eat(tt.slash);
this.expect(tt.jsxTagEnd);
return this.finishNode(node, "JSXOpeningElement");

View File

@@ -57,6 +57,8 @@ function keywordTypeFromName(
return "TSSymbolKeyword";
case "undefined":
return "TSUndefinedKeyword";
case "unknown":
return "TSUnknownKeyword";
default:
return undefined;
}
@@ -501,13 +503,32 @@ export default (superClass: Class<Parser>): Class<Parser> =>
const node: N.TsTupleType = this.startNode();
node.elementTypes = this.tsParseBracketedList(
"TupleElementTypes",
this.tsParseType.bind(this),
this.tsParseTupleElementType.bind(this),
/* bracket */ true,
/* skipFirstToken */ false,
);
return this.finishNode(node, "TSTupleType");
}
tsParseTupleElementType(): N.TsType {
// parses `...TsType[]`
if (this.match(tt.ellipsis)) {
const restNode: N.TsRestType = this.startNode();
this.next(); // skips ellipsis
restNode.typeAnnotation = this.tsParseType();
return this.finishNode(restNode, "TSRestType");
}
const type = this.tsParseType();
// parses `TsType?`
if (this.eat(tt.question)) {
const optionalTypeNode: N.TsOptionalType = this.startNodeAtNode(type);
optionalTypeNode.typeAnnotation = type;
return this.finishNode(optionalTypeNode, "TSOptionalType");
}
return type;
}
tsParseParenthesizedType(): N.TsParenthesizedType {
const node = this.startNode();
this.expect(tt.parenL);
@@ -836,14 +857,6 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return this.finishNode(node, "TSTypeAssertion");
}
tsTryParseTypeArgumentsInExpression(): ?N.TsTypeParameterInstantiation {
return this.tsTryParseAndCatch(() => {
const res = this.tsParseTypeArguments();
this.expect(tt.parenL);
return res;
});
}
tsParseHeritageClause(): $ReadOnlyArray<N.TsExpressionWithTypeArguments> {
return this.tsParseDelimitedList(
"HeritageClauseElement",
@@ -887,6 +900,16 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return this.finishNode(node, "TSTypeAliasDeclaration");
}
tsInNoContext<T>(cb: () => T): T {
const oldContext = this.state.context;
this.state.context = [oldContext[0]];
try {
return cb();
} finally {
this.state.context = oldContext;
}
}
/**
* Runs `cb` in a type context.
* This should be called one token *before* the first type token,
@@ -1241,13 +1264,19 @@ export default (superClass: Class<Parser>): Class<Parser> =>
tsParseTypeArguments(): N.TsTypeParameterInstantiation {
const node = this.startNode();
node.params = this.tsInType(() => {
this.expectRelational("<");
return this.tsParseDelimitedList(
"TypeParametersOrArguments",
this.tsParseType.bind(this),
);
});
node.params = this.tsInType(() =>
// Temporarily remove a JSX parsing context, which makes us scan different tokens.
this.tsInNoContext(() => {
this.expectRelational("<");
return this.tsParseDelimitedList(
"TypeParametersOrArguments",
this.tsParseType.bind(this),
);
}),
);
// This reads the next token after the `>` too, so do this in the enclosing context.
// But be sure not to parse a regex in the jsx expression `<C<number> />`, so set exprAllowed = false
this.state.exprAllowed = false;
this.expectRelational(">");
return this.finishNode(node, "TSTypeParameterInstantiation");
}
@@ -1358,34 +1387,53 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return this.finishNode(nonNullExpression, "TSNonNullExpression");
}
if (!noCalls && this.isRelational("<")) {
if (this.atPossibleAsync(base)) {
// Almost certainly this is a generic async function `async <T>() => ...
// But it might be a call with a type argument `async<T>();`
const asyncArrowFn = this.tsTryParseGenericAsyncArrowFunction(
startPos,
startLoc,
);
if (asyncArrowFn) {
return asyncArrowFn;
if (this.isRelational("<")) {
// tsTryParseAndCatch is expensive, so avoid if not necessary.
// There are number of things we are going to "maybe" parse, like type arguments on
// tagged template expressions. If any of them fail, walk it back and continue.
const result = this.tsTryParseAndCatch(() => {
if (!noCalls && this.atPossibleAsync(base)) {
// Almost certainly this is a generic async function `async <T>() => ...
// But it might be a call with a type argument `async<T>();`
const asyncArrowFn = this.tsTryParseGenericAsyncArrowFunction(
startPos,
startLoc,
);
if (asyncArrowFn) {
return asyncArrowFn;
}
}
}
const node: N.CallExpression = this.startNodeAt(startPos, startLoc);
node.callee = base;
const node: N.CallExpression = this.startNodeAt(startPos, startLoc);
node.callee = base;
// May be passing type arguments. But may just be the `<` operator.
const typeArguments = this.tsTryParseTypeArgumentsInExpression(); // Also eats the "("
if (typeArguments) {
// possibleAsync always false here, because we would have handled it above.
// $FlowIgnore (won't be any undefined arguments)
node.arguments = this.parseCallExpressionArguments(
tt.parenR,
/* possibleAsync */ false,
);
node.typeParameters = typeArguments;
return this.finishCallExpression(node);
}
const typeArguments = this.tsParseTypeArguments();
if (typeArguments) {
if (!noCalls && this.eat(tt.parenL)) {
// possibleAsync always false here, because we would have handled it above.
// $FlowIgnore (won't be any undefined arguments)
node.arguments = this.parseCallExpressionArguments(
tt.parenR,
/* possibleAsync */ false,
);
node.typeParameters = typeArguments;
return this.finishCallExpression(node);
} else if (this.match(tt.backQuote)) {
return this.parseTaggedTemplateExpression(
startPos,
startLoc,
base,
state,
typeArguments,
);
}
}
this.unexpected();
});
if (result) return result;
}
return super.parseSubscript(base, startPos, startLoc, noCalls, state);
@@ -1509,6 +1557,19 @@ export default (superClass: Class<Parser>): Class<Parser> =>
cls.abstract = true;
return cls;
}
// export default interface allowed in:
// https://github.com/Microsoft/TypeScript/pull/16040
if (this.state.value === "interface") {
const result = this.tsParseDeclaration(
this.startNode(),
this.state.value,
true,
);
if (result) return result;
}
return super.parseExportDefaultExpression();
}
@@ -1764,9 +1825,8 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}
parseObjPropValue(prop: N.ObjectMember, ...args): void {
if (this.isRelational("<")) {
throw new Error("TODO");
}
const typeParameters = this.tsTryParseTypeParameters();
if (typeParameters) prop.typeParameters = typeParameters;
super.parseObjPropValue(prop, ...args);
}
@@ -2006,6 +2066,22 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}
}
parseMaybeDecoratorArguments(expr: N.Expression): N.Expression {
if (this.isRelational("<")) {
const typeArguments = this.tsParseTypeArguments();
if (this.match(tt.parenL)) {
const call = super.parseMaybeDecoratorArguments(expr);
call.typeParameters = typeArguments;
return call;
}
this.unexpected(this.state.start, tt.parenL);
}
return super.parseMaybeDecoratorArguments(expr);
}
// === === === === === === === === === === === === === === === ===
// Note: All below methods are duplicates of something in flow.js.
// Not sure what the best way to combine these is.
@@ -2102,4 +2178,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// Avoid unnecessary lookahead in checking for abstract class unless needed!
return super.canHaveLeadingDecorator() || this.isAbstractClass();
}
jsxParseOpeningElementAfterName(
node: N.JSXOpeningElement,
): N.JSXOpeningElement {
const typeArguments = this.tsTryParseAndCatch(() =>
this.tsParseTypeArguments(),
);
if (typeArguments) node.typeParameters = typeArguments;
return super.jsxParseOpeningElementAfterName(node);
}
};

View File

@@ -106,7 +106,7 @@ tt.incDec.updateContext = function() {
// tokExprAllowed stays unchanged
};
tt._function.updateContext = function(prevType) {
tt._function.updateContext = tt._class.updateContext = function(prevType) {
if (this.state.exprAllowed && !this.braceIsBlock(prevType)) {
this.state.context.push(types.functionExpression);
}

View File

@@ -16,7 +16,7 @@ import {
lineBreak,
lineBreakG,
isNewLine,
nonASCIIwhitespace,
isWhitespace,
} from "../util/whitespace";
import State from "./state";
@@ -110,18 +110,6 @@ export class Token {
// ## Tokenizer
function codePointToString(code: number): string {
// UTF-16 Decoding
if (code <= 0xffff) {
return String.fromCharCode(code);
} else {
return String.fromCharCode(
((code - 0x10000) >> 10) + 0xd800,
((code - 0x10000) & 1023) + 0xdc00,
);
}
}
export default class Tokenizer extends LocationParser {
// Forward-declarations
// parser/util.js
@@ -226,7 +214,7 @@ export default class Tokenizer extends LocationParser {
if (curContext.override) {
curContext.override(this);
} else {
this.readToken(this.fullCharCodeAtPos());
this.readToken(this.input.codePointAt(this.state.pos));
}
}
@@ -240,14 +228,6 @@ export default class Tokenizer extends LocationParser {
}
}
fullCharCodeAtPos(): number {
const code = this.input.charCodeAt(this.state.pos);
if (code <= 0xd7ff || code >= 0xe000) return code;
const next = this.input.charCodeAt(this.state.pos + 1);
return (code << 10) + next - 0x35fdc00;
}
pushComment(
block: boolean,
text: string,
@@ -331,11 +311,6 @@ export default class Tokenizer extends LocationParser {
loop: while (this.state.pos < this.input.length) {
const ch = this.input.charCodeAt(this.state.pos);
switch (ch) {
case charCodes.space:
case charCodes.nonBreakingSpace:
++this.state.pos;
break;
case charCodes.carriageReturn:
if (
this.input.charCodeAt(this.state.pos + 1) === charCodes.lineFeed
@@ -367,11 +342,7 @@ export default class Tokenizer extends LocationParser {
break;
default:
if (
(ch > charCodes.backSpace && ch < charCodes.shiftOut) ||
(ch >= charCodes.oghamSpaceMark &&
nonASCIIwhitespace.test(String.fromCharCode(ch)))
) {
if (isWhitespace(ch)) {
++this.state.pos;
} else {
break loop;
@@ -455,7 +426,7 @@ export default class Tokenizer extends LocationParser {
readToken_slash(): void {
// '/'
if (this.state.exprAllowed) {
if (this.state.exprAllowed && !this.state.inType) {
++this.state.pos;
this.readRegexp();
return;
@@ -845,7 +816,7 @@ export default class Tokenizer extends LocationParser {
this.raise(
this.state.pos,
`Unexpected character '${codePointToString(code)}'`,
`Unexpected character '${String.fromCodePoint(code)}'`,
);
}
@@ -887,7 +858,7 @@ export default class Tokenizer extends LocationParser {
while (this.state.pos < this.input.length) {
const char = this.input[this.state.pos];
const charCode = this.fullCharCodeAtPos();
const charCode = this.input.codePointAt(this.state.pos);
if (VALID_REGEX_FLAGS.indexOf(char) > -1) {
if (mods.indexOf(char) > -1) {
@@ -999,7 +970,7 @@ export default class Tokenizer extends LocationParser {
}
}
if (isIdentifierStart(this.fullCharCodeAtPos())) {
if (isIdentifierStart(this.input.codePointAt(this.state.pos))) {
this.raise(this.state.pos, "Identifier directly after number");
}
@@ -1055,7 +1026,7 @@ export default class Tokenizer extends LocationParser {
}
}
if (isIdentifierStart(this.fullCharCodeAtPos())) {
if (isIdentifierStart(this.input.codePointAt(this.state.pos))) {
this.raise(this.state.pos, "Identifier directly after number");
}
@@ -1132,6 +1103,7 @@ export default class Tokenizer extends LocationParser {
(ch === charCodes.lineSeparator || ch === charCodes.paragraphSeparator)
) {
++this.state.pos;
++this.state.curLine;
} else if (isNewLine(ch)) {
this.raise(this.state.start, "Unterminated string constant");
} else {
@@ -1224,7 +1196,7 @@ export default class Tokenizer extends LocationParser {
}
case charCodes.lowercaseU: {
const code = this.readCodePoint(throwOnInvalid);
return code === null ? null : codePointToString(code);
return code === null ? null : String.fromCodePoint(code);
}
case charCodes.lowercaseT:
return "\t";
@@ -1302,7 +1274,7 @@ export default class Tokenizer extends LocationParser {
first = true,
chunkStart = this.state.pos;
while (this.state.pos < this.input.length) {
const ch = this.fullCharCodeAtPos();
const ch = this.input.codePointAt(this.state.pos);
if (isIdentifierChar(ch)) {
this.state.pos += ch <= 0xffff ? 1 : 2;
} else if (this.state.isIterator && ch === charCodes.atSign) {
@@ -1328,7 +1300,7 @@ export default class Tokenizer extends LocationParser {
}
// $FlowFixMe
word += codePointToString(esc);
word += String.fromCodePoint(esc);
chunkStart = this.state.pos;
} else {
break;

View File

@@ -66,18 +66,12 @@ export class TokenType {
}
}
class KeywordTokenType extends TokenType {
constructor(name: string, options: TokenOptions = {}) {
options.keyword = name;
super(name, options);
}
function KeywordTokenType(keyword: string, options: TokenOptions = {}) {
return new TokenType(keyword, { ...options, keyword });
}
export class BinopTokenType extends TokenType {
constructor(name: string, prec: number) {
super(name, { beforeExpr, binop: prec });
}
function BinopTokenType(name: string, binop: number) {
return new TokenType(name, { beforeExpr, binop });
}
export const types: { [name: string]: TokenType } = {

View File

@@ -1,5 +1,6 @@
// @flow
import type { SourceType } from "./options";
import type { Token } from "./tokenizer";
import type { SourceLocation } from "./util/location";
@@ -135,7 +136,7 @@ export type File = NodeBase & {
export type Program = NodeBase & {
type: "Program",
sourceType: "script" | "module",
sourceType: SourceType,
body: Array<Statement | ModuleDeclaration>, // TODO: $ReadOnlyArray
directives: $ReadOnlyArray<Directive>, // TODO: Not in spec
interpreter: InterpreterDirective | null,
@@ -397,7 +398,7 @@ export type ObjectMemberBase = NodeBase & {
decorators: $ReadOnlyArray<Decorator>,
kind?: "get" | "set" | "method",
method: boolean, // TODO: Not in spec
typeParameters?: ?TypeParameterInstantiationBase, // TODO: Not in spec
variance?: ?FlowVariance, // TODO: Not in spec
};
@@ -599,10 +600,11 @@ export type TemplateLiteral = NodeBase & {
expressions: $ReadOnlyArray<Expression>,
};
export type TaggedTmplateExpression = NodeBase & {
export type TaggedTemplateExpression = NodeBase & {
type: "TaggedTemplateExpression",
tag: Expression,
quasi: TemplateLiteral,
typeParameters?: ?TypeParameterInstantiationBase, // TODO: Not in spec
};
export type TemplateElement = NodeBase & {
@@ -742,6 +744,7 @@ export type ClassPrivateProperty = NodeBase & {
value: ?Expression, // TODO: Not in spec that this is nullable.
static: boolean,
computed: false,
typeAnnotation?: ?TypeAnnotation, // TODO: Not in spec
};
export type OptClassDeclaration = ClassBase &
@@ -846,7 +849,13 @@ export type JSXEmptyExpression = Node;
export type JSXSpreadChild = Node;
export type JSXExpressionContainer = Node;
export type JSXAttribute = Node;
export type JSXOpeningElement = Node;
export type JSXOpeningElement = NodeBase & {
type: "JSXOpeningElement",
name: JSXNamespacedName | JSXMemberExpression,
typeParameters?: ?TypeParameterInstantiationBase, // TODO: Not in spec
attributes: $ReadOnlyArray<JSXAttribute>,
selfClosing: boolean,
};
export type JSXClosingElement = Node;
export type JSXElement = Node;
export type JSXOpeningFragment = Node;
@@ -1121,6 +1130,8 @@ export type TsType =
| TsTypeLiteral
| TsArrayType
| TsTupleType
| TsOptionalType
| TsRestType
| TsUnionOrIntersectionType
| TsConditionalType
| TsInferType
@@ -1136,6 +1147,7 @@ export type TsTypeBase = NodeBase;
export type TsKeywordTypeType =
| "TSAnyKeyword"
| "TSUnknownKeyword"
| "TSNumberKeyword"
| "TSObjectKeyword"
| "TSBooleanKeyword"
@@ -1200,6 +1212,16 @@ export type TsTupleType = TsTypeBase & {
elementTypes: $ReadOnlyArray<TsType>,
};
export type TsOptionalType = TsTypeBase & {
type: "TSOptionalType",
typeAnnotation: TsType,
};
export type TsRestType = TsTypeBase & {
type: "TSRestType",
typeAnnotation: TsType,
};
export type TsUnionOrIntersectionType = TsUnionType | TsIntersectionType;
export type TsUnionOrIntersectionTypeBase = TsTypeBase & {

View File

@@ -39,16 +39,14 @@ export class SourceLocation {
// into.
export function getLineInfo(input: string, offset: number): Position {
for (let line = 1, cur = 0; ; ) {
lineBreakG.lastIndex = cur;
const match = lineBreakG.exec(input);
if (match && match.index < offset) {
++line;
cur = match.index + match[0].length;
} else {
return new Position(line, offset - cur);
}
let line = 1;
let lineStart = 0;
let match;
lineBreakG.lastIndex = 0;
while ((match = lineBreakG.exec(input)) && match.index < offset) {
line++;
lineStart = lineBreakG.lastIndex;
}
// istanbul ignore next
throw new Error("Unreachable");
return new Position(line, offset - lineStart);
}

View File

@@ -1,13 +1,55 @@
// @flow
import * as charCodes from "charcodes";
// Matches a whole line break (where CRLF is considered a single
// line break). Used to count lines.
export const lineBreak = /\r\n?|\n|\u2028|\u2029/;
export const lineBreakG = new RegExp(lineBreak.source, "g");
// https://tc39.github.io/ecma262/#sec-line-terminators
export function isNewLine(code: number): boolean {
return code === 10 || code === 13 || code === 0x2028 || code === 0x2029;
switch (code) {
case charCodes.lineFeed:
case charCodes.carriageReturn:
case charCodes.lineSeparator:
case charCodes.paragraphSeparator:
return true;
default:
return false;
}
}
export const nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;
export const skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g;
// https://tc39.github.io/ecma262/#sec-white-space
export function isWhitespace(code: number): boolean {
switch (code) {
case 0x0009: // CHARACTER TABULATION
case 0x000b: // LINE TABULATION
case 0x000c: // FORM FEED
case charCodes.space:
case charCodes.nonBreakingSpace:
case charCodes.oghamSpaceMark:
case 0x2000: // EN QUAD
case 0x2001: // EM QUAD
case 0x2002: // EN SPACE
case 0x2003: // EM SPACE
case 0x2004: // THREE-PER-EM SPACE
case 0x2005: // FOUR-PER-EM SPACE
case 0x2006: // SIX-PER-EM SPACE
case 0x2007: // FIGURE SPACE
case 0x2008: // PUNCTUATION SPACE
case 0x2009: // THIN SPACE
case 0x200a: // HAIR SPACE
case 0x202f: // NARROW NO-BREAK SPACE
case 0x205f: // MEDIUM MATHEMATICAL SPACE
case 0x3000: // IDEOGRAPHIC SPACE
case 0xfeff: // ZERO WIDTH NO-BREAK SPACE
return true;
default:
return false;
}
}