refactor: extract tt.lt and tt.gt from tt.relation (#13892)

This commit is contained in:
Huáng Jùnliàng 2021-11-01 21:15:42 -04:00 committed by GitHub
parent 05af38cff2
commit 1fa759f989
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 143 additions and 137 deletions

View File

@ -1211,23 +1211,24 @@ export default class ExpressionParser extends LValParser {
if (pipeProposal) { if (pipeProposal) {
return this.parseTopicReference(pipeProposal); return this.parseTopicReference(pipeProposal);
} else {
throw this.unexpected();
} }
} }
// fall through case tt.lt: {
case tt.relational: { const lookaheadCh = this.input.codePointAt(this.nextTokenStart());
if (this.state.value === "<") { if (
const lookaheadCh = this.input.codePointAt(this.nextTokenStart()); isIdentifierStart(lookaheadCh) || // Element/Type Parameter <foo>
if ( lookaheadCh === charCodes.greaterThan // Fragment <>
isIdentifierStart(lookaheadCh) || // Element/Type Parameter <foo> ) {
lookaheadCh === charCodes.greaterThan // Fragment <> this.expectOnePlugin(["jsx", "flow", "typescript"]);
) { break;
this.expectOnePlugin(["jsx", "flow", "typescript"]); } else {
} throw this.unexpected();
} }
} }
// fall through
default: default:
if (tokenIsIdentifier(type)) { if (tokenIsIdentifier(type)) {
if ( if (

View File

@ -50,22 +50,6 @@ export default class UtilParser extends Tokenizer {
extra[key] = val; extra[key] = val;
} }
// TODO
isRelational(op: "<" | ">"): boolean {
return this.match(tt.relational) && this.state.value === op;
}
// TODO
expectRelational(op: "<" | ">"): void {
if (this.isRelational(op)) {
this.next();
} else {
this.unexpected(null, tt.relational);
}
}
// Tests whether parsed token is a contextual keyword. // Tests whether parsed token is a contextual keyword.
isContextual(token: TokenType): boolean { isContextual(token: TokenType): boolean {

View File

@ -316,7 +316,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
const typeNode = this.startNode(); const typeNode = this.startNode();
const typeContainer = this.startNode(); const typeContainer = this.startNode();
if (this.isRelational("<")) { if (this.match(tt.lt)) {
typeNode.typeParameters = this.flowParseTypeParameterDeclaration(); typeNode.typeParameters = this.flowParseTypeParameterDeclaration();
} else { } else {
typeNode.typeParameters = null; typeNode.typeParameters = null;
@ -600,7 +600,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
node.id.start, node.id.start,
); );
if (this.isRelational("<")) { if (this.match(tt.lt)) {
node.typeParameters = this.flowParseTypeParameterDeclaration(); node.typeParameters = this.flowParseTypeParameterDeclaration();
} else { } else {
node.typeParameters = null; node.typeParameters = null;
@ -643,7 +643,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
const node = this.startNode(); const node = this.startNode();
node.id = this.flowParseQualifiedTypeIdentifier(); node.id = this.flowParseQualifiedTypeIdentifier();
if (this.isRelational("<")) { if (this.match(tt.lt)) {
node.typeParameters = this.flowParseTypeParameterInstantiation(); node.typeParameters = this.flowParseTypeParameterInstantiation();
} else { } else {
node.typeParameters = null; node.typeParameters = null;
@ -692,7 +692,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
); );
this.scope.declareName(node.id.name, BIND_LEXICAL, node.id.start); this.scope.declareName(node.id.name, BIND_LEXICAL, node.id.start);
if (this.isRelational("<")) { if (this.match(tt.lt)) {
node.typeParameters = this.flowParseTypeParameterDeclaration(); node.typeParameters = this.flowParseTypeParameterDeclaration();
} else { } else {
node.typeParameters = null; node.typeParameters = null;
@ -715,7 +715,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
); );
this.scope.declareName(node.id.name, BIND_LEXICAL, node.id.start); this.scope.declareName(node.id.name, BIND_LEXICAL, node.id.start);
if (this.isRelational("<")) { if (this.match(tt.lt)) {
node.typeParameters = this.flowParseTypeParameterDeclaration(); node.typeParameters = this.flowParseTypeParameterDeclaration();
} else { } else {
node.typeParameters = null; node.typeParameters = null;
@ -770,7 +770,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.state.inType = true; this.state.inType = true;
// istanbul ignore else: this condition is already checked at all call sites // istanbul ignore else: this condition is already checked at all call sites
if (this.isRelational("<") || this.match(tt.jsxTagStart)) { if (this.match(tt.lt) || this.match(tt.jsxTagStart)) {
this.next(); this.next();
} else { } else {
this.unexpected(); this.unexpected();
@ -787,11 +787,11 @@ export default (superClass: Class<Parser>): Class<Parser> =>
defaultRequired = true; defaultRequired = true;
} }
if (!this.isRelational(">")) { if (!this.match(tt.gt)) {
this.expect(tt.comma); this.expect(tt.comma);
} }
} while (!this.isRelational(">")); } while (!this.match(tt.gt));
this.expectRelational(">"); this.expect(tt.gt);
this.state.inType = oldInType; this.state.inType = oldInType;
@ -805,17 +805,17 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.state.inType = true; this.state.inType = true;
this.expectRelational("<"); this.expect(tt.lt);
const oldNoAnonFunctionType = this.state.noAnonFunctionType; const oldNoAnonFunctionType = this.state.noAnonFunctionType;
this.state.noAnonFunctionType = false; this.state.noAnonFunctionType = false;
while (!this.isRelational(">")) { while (!this.match(tt.gt)) {
node.params.push(this.flowParseType()); node.params.push(this.flowParseType());
if (!this.isRelational(">")) { if (!this.match(tt.gt)) {
this.expect(tt.comma); this.expect(tt.comma);
} }
} }
this.state.noAnonFunctionType = oldNoAnonFunctionType; this.state.noAnonFunctionType = oldNoAnonFunctionType;
this.expectRelational(">"); this.expect(tt.gt);
this.state.inType = oldInType; this.state.inType = oldInType;
@ -829,14 +829,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.state.inType = true; this.state.inType = true;
this.expectRelational("<"); this.expect(tt.lt);
while (!this.isRelational(">")) { while (!this.match(tt.gt)) {
node.params.push(this.flowParseTypeOrImplicitInstantiation()); node.params.push(this.flowParseTypeOrImplicitInstantiation());
if (!this.isRelational(">")) { if (!this.match(tt.gt)) {
this.expect(tt.comma); this.expect(tt.comma);
} }
} }
this.expectRelational(">"); this.expect(tt.gt);
this.state.inType = oldInType; this.state.inType = oldInType;
@ -902,7 +902,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
node.id = this.flowParseObjectPropertyKey(); node.id = this.flowParseObjectPropertyKey();
this.expect(tt.bracketR); this.expect(tt.bracketR);
this.expect(tt.bracketR); this.expect(tt.bracketR);
if (this.isRelational("<") || this.match(tt.parenL)) { if (this.match(tt.lt) || this.match(tt.parenL)) {
node.method = true; node.method = true;
node.optional = false; node.optional = false;
node.value = this.flowParseObjectTypeMethodish( node.value = this.flowParseObjectTypeMethodish(
@ -926,7 +926,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
node.typeParameters = null; node.typeParameters = null;
node.this = null; node.this = null;
if (this.isRelational("<")) { if (this.match(tt.lt)) {
node.typeParameters = this.flowParseTypeParameterDeclaration(); node.typeParameters = this.flowParseTypeParameterDeclaration();
} }
@ -1047,7 +1047,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.flowParseObjectTypeIndexer(node, isStatic, variance), this.flowParseObjectTypeIndexer(node, isStatic, variance),
); );
} }
} else if (this.match(tt.parenL) || this.isRelational("<")) { } else if (this.match(tt.parenL) || this.match(tt.lt)) {
if (protoStart != null) { if (protoStart != null) {
this.unexpected(protoStart); this.unexpected(protoStart);
} }
@ -1169,7 +1169,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
node.kind = kind; node.kind = kind;
let optional = false; let optional = false;
if (this.isRelational("<") || this.match(tt.parenL)) { if (this.match(tt.lt) || this.match(tt.parenL)) {
// This is a method property // This is a method property
node.method = true; node.method = true;
@ -1287,7 +1287,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
node.typeParameters = null; node.typeParameters = null;
node.id = this.flowParseQualifiedTypeIdentifier(startPos, startLoc, id); node.id = this.flowParseQualifiedTypeIdentifier(startPos, startLoc, id);
if (this.isRelational("<")) { if (this.match(tt.lt)) {
node.typeParameters = this.flowParseTypeParameterInstantiation(); node.typeParameters = this.flowParseTypeParameterInstantiation();
} }
@ -1453,23 +1453,20 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.state.noAnonFunctionType = oldNoAnonFunctionType; this.state.noAnonFunctionType = oldNoAnonFunctionType;
return type; return type;
case tt.relational: case tt.lt:
if (this.state.value === "<") { node.typeParameters = this.flowParseTypeParameterDeclaration();
node.typeParameters = this.flowParseTypeParameterDeclaration(); this.expect(tt.parenL);
this.expect(tt.parenL); tmp = this.flowParseFunctionTypeParams();
tmp = this.flowParseFunctionTypeParams(); node.params = tmp.params;
node.params = tmp.params; node.rest = tmp.rest;
node.rest = tmp.rest; node.this = tmp._this;
node.this = tmp._this; this.expect(tt.parenR);
this.expect(tt.parenR);
this.expect(tt.arrow); this.expect(tt.arrow);
node.returnType = this.flowParseType(); node.returnType = this.flowParseType();
return this.finishNode(node, "FunctionTypeAnnotation"); return this.finishNode(node, "FunctionTypeAnnotation");
}
break;
case tt.parenL: case tt.parenL:
this.next(); this.next();
@ -2178,7 +2175,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
parseClassId(node: N.Class, isStatement: boolean, optionalId: ?boolean) { parseClassId(node: N.Class, isStatement: boolean, optionalId: ?boolean) {
super.parseClassId(node, isStatement, optionalId); super.parseClassId(node, isStatement, optionalId);
if (this.isRelational("<")) { if (this.match(tt.lt)) {
node.typeParameters = this.flowParseTypeParameterDeclaration(); node.typeParameters = this.flowParseTypeParameterDeclaration();
} }
} }
@ -2241,7 +2238,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.state.inType && this.state.inType &&
(code === charCodes.greaterThan || code === charCodes.lessThan) (code === charCodes.greaterThan || code === charCodes.lessThan)
) { ) {
return this.finishOp(tt.relational, 1); return this.finishOp(code === charCodes.greaterThan ? tt.gt : tt.lt, 1);
} else if (this.state.inType && code === charCodes.questionMark) { } else if (this.state.inType && code === charCodes.questionMark) {
if (next === charCodes.dot) { if (next === charCodes.dot) {
return this.finishOp(tt.questionDot, 2); return this.finishOp(tt.questionDot, 2);
@ -2369,7 +2366,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// determine whether or not we're currently in the position where a class method would appear // determine whether or not we're currently in the position where a class method would appear
isClassMethod(): boolean { isClassMethod(): boolean {
return this.isRelational("<") || super.isClassMethod(); return this.match(tt.lt) || super.isClassMethod();
} }
// determine whether or not we're currently in the position where a class property would appear // determine whether or not we're currently in the position where a class property would appear
@ -2394,7 +2391,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.unexpected((method: $FlowFixMe).variance.start); this.unexpected((method: $FlowFixMe).variance.start);
} }
delete (method: $FlowFixMe).variance; delete (method: $FlowFixMe).variance;
if (this.isRelational("<")) { if (this.match(tt.lt)) {
method.typeParameters = this.flowParseTypeParameterDeclaration(); method.typeParameters = this.flowParseTypeParameterDeclaration();
} }
@ -2436,7 +2433,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.unexpected((method: $FlowFixMe).variance.start); this.unexpected((method: $FlowFixMe).variance.start);
} }
delete (method: $FlowFixMe).variance; delete (method: $FlowFixMe).variance;
if (this.isRelational("<")) { if (this.match(tt.lt)) {
method.typeParameters = this.flowParseTypeParameterDeclaration(); method.typeParameters = this.flowParseTypeParameterDeclaration();
} }
@ -2446,7 +2443,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// parse a the super class type parameters and implements // parse a the super class type parameters and implements
parseClassSuper(node: N.Class): void { parseClassSuper(node: N.Class): void {
super.parseClassSuper(node); super.parseClassSuper(node);
if (node.superClass && this.isRelational("<")) { if (node.superClass && this.match(tt.lt)) {
node.superTypeParameters = this.flowParseTypeParameterInstantiation(); node.superTypeParameters = this.flowParseTypeParameterInstantiation();
} }
if (this.isContextual(tt._implements)) { if (this.isContextual(tt._implements)) {
@ -2455,7 +2452,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
do { do {
const node = this.startNode(); const node = this.startNode();
node.id = this.flowParseRestrictedIdentifier(/*liberal*/ true); node.id = this.flowParseRestrictedIdentifier(/*liberal*/ true);
if (this.isRelational("<")) { if (this.match(tt.lt)) {
node.typeParameters = this.flowParseTypeParameterInstantiation(); node.typeParameters = this.flowParseTypeParameterInstantiation();
} else { } else {
node.typeParameters = null; node.typeParameters = null;
@ -2508,7 +2505,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
let typeParameters; let typeParameters;
// method shorthand // method shorthand
if (this.isRelational("<") && !isAccessor) { if (this.match(tt.lt) && !isAccessor) {
typeParameters = this.flowParseTypeParameterDeclaration(); typeParameters = this.flowParseTypeParameterDeclaration();
if (!this.match(tt.parenL)) this.unexpected(); if (!this.match(tt.parenL)) this.unexpected();
} }
@ -2740,7 +2737,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
parseFunctionParams(node: N.Function, allowModifiers?: boolean): void { parseFunctionParams(node: N.Function, allowModifiers?: boolean): void {
// $FlowFixMe // $FlowFixMe
const kind = node.kind; const kind = node.kind;
if (kind !== "get" && kind !== "set" && this.isRelational("<")) { if (kind !== "get" && kind !== "set" && this.match(tt.lt)) {
node.typeParameters = this.flowParseTypeParameterDeclaration(); node.typeParameters = this.flowParseTypeParameterDeclaration();
} }
super.parseFunctionParams(node, allowModifiers); super.parseFunctionParams(node, allowModifiers);
@ -2798,7 +2795,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
if ( if (
this.hasPlugin("jsx") && this.hasPlugin("jsx") &&
(this.match(tt.jsxTagStart) || this.isRelational("<")) (this.match(tt.jsxTagStart) || this.match(tt.lt))
) { ) {
state = this.state.clone(); state = this.state.clone();
@ -2823,7 +2820,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
} }
} }
if (jsx?.error || this.isRelational("<")) { if (jsx?.error || this.match(tt.lt)) {
state = state || this.state.clone(); state = state || this.state.clone();
let typeParameters; let typeParameters;
@ -3020,7 +3017,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
} else if ( } else if (
base.type === "Identifier" && base.type === "Identifier" &&
base.name === "async" && base.name === "async" &&
this.isRelational("<") this.match(tt.lt)
) { ) {
const state = this.state.clone(); const state = this.state.clone();
const arrow = this.tryParse( const arrow = this.tryParse(
@ -3081,11 +3078,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
node.arguments = this.parseCallExpressionArguments(tt.parenR, false); node.arguments = this.parseCallExpressionArguments(tt.parenR, false);
node.optional = true; node.optional = true;
return this.finishCallExpression(node, /* optional */ true); return this.finishCallExpression(node, /* optional */ true);
} else if ( } else if (!noCalls && this.shouldParseTypes() && this.match(tt.lt)) {
!noCalls &&
this.shouldParseTypes() &&
this.isRelational("<")
) {
const node = this.startNodeAt(startPos, startLoc); const node = this.startNodeAt(startPos, startLoc);
node.callee = base; node.callee = base;
@ -3118,7 +3111,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
parseNewArguments(node: N.NewExpression): void { parseNewArguments(node: N.NewExpression): void {
let targs = null; let targs = null;
if (this.shouldParseTypes() && this.isRelational("<")) { if (this.shouldParseTypes() && this.match(tt.lt)) {
targs = this.tryParse(() => targs = this.tryParse(() =>
this.flowParseTypeParameterInstantiationCallOrNew(), this.flowParseTypeParameterInstantiationCallOrNew(),
).node; ).node;
@ -3665,7 +3658,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return this.finishNode(node, "EnumDeclaration"); return this.finishNode(node, "EnumDeclaration");
} }
// check if the next token is a tt.relation("<") // check if the next token is a tt.lt
isLookaheadToken_lt(): boolean { isLookaheadToken_lt(): boolean {
const next = this.nextTokenStart(); const next = this.nextTokenStart();
if (this.input.charCodeAt(next) === charCodes.lessThan) { if (this.input.charCodeAt(next) === charCodes.lessThan) {

View File

@ -520,7 +520,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
node.closingElement = closingElement; node.closingElement = closingElement;
} }
node.children = children; node.children = children;
if (this.isRelational("<")) { if (this.match(tt.lt)) {
throw this.raise( throw this.raise(
this.state.start, this.state.start,
JsxErrors.UnwrappedAdjacentJSXElements, JsxErrors.UnwrappedAdjacentJSXElements,
@ -551,7 +551,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
} else if (this.match(tt.jsxTagStart)) { } else if (this.match(tt.jsxTagStart)) {
return this.jsxParseElement(); return this.jsxParseElement();
} else if ( } else if (
this.isRelational("<") && this.match(tt.lt) &&
this.input.charCodeAt(this.state.pos) !== charCodes.exclamationMark this.input.charCodeAt(this.state.pos) !== charCodes.exclamationMark
) { ) {
// In case we encounter an lt token here it will always be the start of // In case we encounter an lt token here it will always be the start of

View File

@ -351,7 +351,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
case "TupleElementTypes": case "TupleElementTypes":
return this.match(tt.bracketR); return this.match(tt.bracketR);
case "TypeParametersOrArguments": case "TypeParametersOrArguments":
return this.isRelational(">"); return this.match(tt.gt);
} }
throw new Error("Unreachable"); throw new Error("Unreachable");
@ -440,7 +440,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
if (bracket) { if (bracket) {
this.expect(tt.bracketL); this.expect(tt.bracketL);
} else { } else {
this.expectRelational("<"); this.expect(tt.lt);
} }
} }
@ -453,7 +453,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
if (bracket) { if (bracket) {
this.expect(tt.bracketR); this.expect(tt.bracketR);
} else { } else {
this.expectRelational(">"); this.expect(tt.gt);
} }
return result; return result;
@ -474,7 +474,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
if (this.eat(tt.dot)) { if (this.eat(tt.dot)) {
node.qualifier = this.tsParseEntityName(/* allowReservedWords */ true); node.qualifier = this.tsParseEntityName(/* allowReservedWords */ true);
} }
if (this.isRelational("<")) { if (this.match(tt.lt)) {
node.typeParameters = this.tsParseTypeArguments(); node.typeParameters = this.tsParseTypeArguments();
} }
return this.finishNode(node, "TSImportType"); return this.finishNode(node, "TSImportType");
@ -494,7 +494,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
tsParseTypeReference(): N.TsTypeReference { tsParseTypeReference(): N.TsTypeReference {
const node: N.TsTypeReference = this.startNode(); const node: N.TsTypeReference = this.startNode();
node.typeName = this.tsParseEntityName(/* allowReservedWords */ false); node.typeName = this.tsParseEntityName(/* allowReservedWords */ false);
if (!this.hasPrecedingLineBreak() && this.isRelational("<")) { if (!this.hasPrecedingLineBreak() && this.match(tt.lt)) {
node.typeParameters = this.tsParseTypeArguments(); node.typeParameters = this.tsParseTypeArguments();
} }
return this.finishNode(node, "TSTypeReference"); return this.finishNode(node, "TSTypeReference");
@ -535,7 +535,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
} }
tsTryParseTypeParameters(): ?N.TsTypeParameterDeclaration { tsTryParseTypeParameters(): ?N.TsTypeParameterDeclaration {
if (this.isRelational("<")) { if (this.match(tt.lt)) {
return this.tsParseTypeParameters(); return this.tsParseTypeParameters();
} }
} }
@ -543,7 +543,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
tsParseTypeParameters() { tsParseTypeParameters() {
const node: N.TsTypeParameterDeclaration = this.startNode(); const node: N.TsTypeParameterDeclaration = this.startNode();
if (this.isRelational("<") || this.match(tt.jsxTagStart)) { if (this.match(tt.lt) || this.match(tt.jsxTagStart)) {
this.next(); this.next();
} else { } else {
this.unexpected(); this.unexpected();
@ -672,12 +672,12 @@ export default (superClass: Class<Parser>): Class<Parser> =>
if (this.eat(tt.question)) node.optional = true; if (this.eat(tt.question)) node.optional = true;
const nodeAny: any = node; const nodeAny: any = node;
if (this.match(tt.parenL) || this.isRelational("<")) { if (this.match(tt.parenL) || this.match(tt.lt)) {
if (readonly) { if (readonly) {
this.raise(node.start, TSErrors.ReadonlyForMethodSignature); this.raise(node.start, TSErrors.ReadonlyForMethodSignature);
} }
const method: N.TsMethodSignature = nodeAny; const method: N.TsMethodSignature = nodeAny;
if (method.kind && this.isRelational("<")) { if (method.kind && this.match(tt.lt)) {
this.raise(this.state.pos, TSErrors.AccesorCannotHaveTypeParameters); this.raise(this.state.pos, TSErrors.AccesorCannotHaveTypeParameters);
} }
this.tsFillSignature(tt.colon, method); this.tsFillSignature(tt.colon, method);
@ -742,14 +742,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
tsParseTypeMember(): N.TsTypeElement { tsParseTypeMember(): N.TsTypeElement {
const node: any = this.startNode(); const node: any = this.startNode();
if (this.match(tt.parenL) || this.isRelational("<")) { if (this.match(tt.parenL) || this.match(tt.lt)) {
return this.tsParseSignatureMember("TSCallSignatureDeclaration", node); return this.tsParseSignatureMember("TSCallSignatureDeclaration", node);
} }
if (this.match(tt._new)) { if (this.match(tt._new)) {
const id: N.Identifier = this.startNode(); const id: N.Identifier = this.startNode();
this.next(); this.next();
if (this.match(tt.parenL) || this.isRelational("<")) { if (this.match(tt.parenL) || this.match(tt.lt)) {
return this.tsParseSignatureMember( return this.tsParseSignatureMember(
"TSConstructSignatureDeclaration", "TSConstructSignatureDeclaration",
node, node,
@ -1199,7 +1199,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
} }
tsIsStartOfFunctionType() { tsIsStartOfFunctionType() {
if (this.isRelational("<")) { if (this.match(tt.lt)) {
return true; return true;
} }
return ( return (
@ -1441,7 +1441,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
const node: N.TsTypeAssertion = this.startNode(); const node: N.TsTypeAssertion = this.startNode();
const _const = this.tsTryNextParseConstantContext(); const _const = this.tsTryNextParseConstantContext();
node.typeAnnotation = _const || this.tsNextThenParseType(); node.typeAnnotation = _const || this.tsNextThenParseType();
this.expectRelational(">"); this.expect(tt.gt);
node.expression = this.parseMaybeUnary(); node.expression = this.parseMaybeUnary();
return this.finishNode(node, "TSTypeAssertion"); return this.finishNode(node, "TSTypeAssertion");
} }
@ -1468,7 +1468,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// Note: TS uses parseLeftHandSideExpressionOrHigher, // Note: TS uses parseLeftHandSideExpressionOrHigher,
// then has grammar errors later if it's not an EntityName. // then has grammar errors later if it's not an EntityName.
node.expression = this.tsParseEntityName(/* allowReservedWords */ false); node.expression = this.tsParseEntityName(/* allowReservedWords */ false);
if (this.isRelational("<")) { if (this.match(tt.lt)) {
node.typeParameters = this.tsParseTypeArguments(); node.typeParameters = this.tsParseTypeArguments();
} }
@ -1917,7 +1917,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
startPos: number, startPos: number,
startLoc: Position, startLoc: Position,
): ?N.ArrowFunctionExpression { ): ?N.ArrowFunctionExpression {
if (!this.isRelational("<")) { if (!this.match(tt.lt)) {
return undefined; return undefined;
} }
@ -1955,7 +1955,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
node.params = this.tsInType(() => node.params = this.tsInType(() =>
// Temporarily remove a JSX parsing context, which makes us scan different tokens. // Temporarily remove a JSX parsing context, which makes us scan different tokens.
this.tsInNoContext(() => { this.tsInNoContext(() => {
this.expectRelational("<"); this.expect(tt.lt);
return this.tsParseDelimitedList( return this.tsParseDelimitedList(
"TypeParametersOrArguments", "TypeParametersOrArguments",
this.tsParseType.bind(this), this.tsParseType.bind(this),
@ -1965,7 +1965,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
if (node.params.length === 0) { if (node.params.length === 0) {
this.raise(node.start, TSErrors.EmptyTypeArguments); this.raise(node.start, TSErrors.EmptyTypeArguments);
} }
this.expectRelational(">"); this.expect(tt.gt);
return this.finishNode(node, "TSTypeParameterInstantiation"); return this.finishNode(node, "TSTypeParameterInstantiation");
} }
@ -2149,7 +2149,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.next(); this.next();
} }
if (this.isRelational("<")) { if (this.match(tt.lt)) {
let missingParenErrorPos; let missingParenErrorPos;
// tsTryParseAndCatch is expensive, so avoid if not necessary. // tsTryParseAndCatch is expensive, so avoid if not necessary.
// There are number of things we are going to "maybe" parse, like type arguments on // There are number of things we are going to "maybe" parse, like type arguments on
@ -2222,7 +2222,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
} }
parseNewArguments(node: N.NewExpression): void { parseNewArguments(node: N.NewExpression): void {
if (this.isRelational("<")) { if (this.match(tt.lt)) {
// tsTryParseAndCatch is expensive, so avoid if not necessary. // tsTryParseAndCatch is expensive, so avoid if not necessary.
// 99% certain this is `new C<T>();`. But may be `new C < T;`, which is also legal. // 99% certain this is `new C<T>();`. But may be `new C < T;`, which is also legal.
const typeParameters = this.tsTryParseAndCatch(() => { const typeParameters = this.tsTryParseAndCatch(() => {
@ -2803,7 +2803,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
parseClassSuper(node: N.Class): void { parseClassSuper(node: N.Class): void {
super.parseClassSuper(node); super.parseClassSuper(node);
if (node.superClass && this.isRelational("<")) { if (node.superClass && this.match(tt.lt)) {
node.superTypeParameters = this.tsParseTypeArguments(); node.superTypeParameters = this.tsParseTypeArguments();
} }
if (this.eatContextual(tt._implements)) { if (this.eatContextual(tt._implements)) {
@ -2861,7 +2861,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
if ( if (
this.hasPlugin("jsx") && this.hasPlugin("jsx") &&
(this.match(tt.jsxTagStart) || this.isRelational("<")) (this.match(tt.jsxTagStart) || this.match(tt.lt))
) { ) {
// Prefer to parse JSX if possible. But may be an arrow fn. // Prefer to parse JSX if possible. But may be an arrow fn.
state = this.state.clone(); state = this.state.clone();
@ -2883,7 +2883,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
} }
} }
if (!jsx?.error && !this.isRelational("<")) { if (!jsx?.error && !this.match(tt.lt)) {
return super.parseMaybeAssign(...args); return super.parseMaybeAssign(...args);
} }
@ -2973,7 +2973,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// Handle type assertions // Handle type assertions
parseMaybeUnary(refExpressionErrors?: ?ExpressionErrors): N.Expression { parseMaybeUnary(refExpressionErrors?: ?ExpressionErrors): N.Expression {
if (!this.hasPlugin("jsx") && this.isRelational("<")) { if (!this.hasPlugin("jsx") && this.match(tt.lt)) {
return this.tsParseTypeAssertion(); return this.tsParseTypeAssertion();
} else { } else {
return super.parseMaybeUnary(refExpressionErrors); return super.parseMaybeUnary(refExpressionErrors);
@ -3114,7 +3114,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
} }
parseMaybeDecoratorArguments(expr: N.Expression): N.Expression { parseMaybeDecoratorArguments(expr: N.Expression): N.Expression {
if (this.isRelational("<")) { if (this.match(tt.lt)) {
const typeArguments = this.tsParseTypeArguments(); const typeArguments = this.tsParseTypeArguments();
if (this.match(tt.parenL)) { if (this.match(tt.parenL)) {
@ -3147,7 +3147,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// === === === === === === === === === === === === === === === === // === === === === === === === === === === === === === === === ===
isClassMethod(): boolean { isClassMethod(): boolean {
return this.isRelational("<") || super.isClassMethod(); return this.match(tt.lt) || super.isClassMethod();
} }
isClassProperty(): boolean { isClassProperty(): boolean {
@ -3175,24 +3175,26 @@ export default (superClass: Class<Parser>): Class<Parser> =>
// ensure that inside types, we bypass the jsx parser plugin // ensure that inside types, we bypass the jsx parser plugin
getTokenFromCode(code: number): void { getTokenFromCode(code: number): void {
if ( if (this.state.inType) {
this.state.inType && if (code === charCodes.greaterThan) {
(code === charCodes.greaterThan || code === charCodes.lessThan) return this.finishOp(tt.gt, 1);
) { }
return this.finishOp(tt.relational, 1); if (code === charCodes.lessThan) {
} else { return this.finishOp(tt.lt, 1);
return super.getTokenFromCode(code); }
} }
return super.getTokenFromCode(code);
} }
// used after we have finished parsing types // used after we have finished parsing types
reScan_lt_gt() { reScan_lt_gt() {
if (this.match(tt.relational)) { const { type } = this.state;
const code = this.input.charCodeAt(this.state.start); if (type === tt.lt) {
if (code === charCodes.lessThan || code === charCodes.greaterThan) { this.state.pos -= 1;
this.state.pos -= 1; this.readToken_lt();
this.readToken_lt_gt(code); } else if (type === tt.gt) {
} this.state.pos -= 1;
this.readToken_gt();
} }
} }
@ -3248,7 +3250,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
jsxParseOpeningElementAfterName( jsxParseOpeningElementAfterName(
node: N.JSXOpeningElement, node: N.JSXOpeningElement,
): N.JSXOpeningElement { ): N.JSXOpeningElement {
if (this.isRelational("<")) { if (this.match(tt.lt)) {
const typeArguments = this.tsTryParseAndCatch(() => const typeArguments = this.tsTryParseAndCatch(() =>
this.tsParseTypeArguments(), this.tsParseTypeArguments(),
); );

View File

@ -727,18 +727,38 @@ export default class Tokenizer extends ParserErrors {
} }
} }
readToken_lt_gt(code: number): void { readToken_lt(): void {
// '<>' // '<'
const next = this.input.charCodeAt(this.state.pos + 1); const { pos } = this.state;
let size = 1; const next = this.input.charCodeAt(pos + 1);
if (next === code) { if (next === charCodes.lessThan) {
size = if (this.input.charCodeAt(pos + 2) === charCodes.equalsTo) {
code === charCodes.greaterThan && this.finishOp(tt.assign, 3);
this.input.charCodeAt(this.state.pos + 2) === charCodes.greaterThan return;
? 3 }
: 2; this.finishOp(tt.bitShift, 2);
if (this.input.charCodeAt(this.state.pos + size) === charCodes.equalsTo) { return;
}
if (next === charCodes.equalsTo) {
// <=
this.finishOp(tt.relational, 2);
return;
}
this.finishOp(tt.lt, 1);
}
readToken_gt(): void {
// '>'
const { pos } = this.state;
const next = this.input.charCodeAt(pos + 1);
if (next === charCodes.greaterThan) {
const size =
this.input.charCodeAt(pos + 2) === charCodes.greaterThan ? 3 : 2;
if (this.input.charCodeAt(pos + size) === charCodes.equalsTo) {
this.finishOp(tt.assign, size + 1); this.finishOp(tt.assign, size + 1);
return; return;
} }
@ -748,10 +768,11 @@ export default class Tokenizer extends ParserErrors {
if (next === charCodes.equalsTo) { if (next === charCodes.equalsTo) {
// <= | >= // <= | >=
size = 2; this.finishOp(tt.relational, 2);
return;
} }
this.finishOp(tt.relational, size); this.finishOp(tt.gt, 1);
} }
readToken_eq_excl(code: number): void { readToken_eq_excl(code: number): void {
@ -963,8 +984,11 @@ export default class Tokenizer extends ParserErrors {
return; return;
case charCodes.lessThan: case charCodes.lessThan:
this.readToken_lt();
return;
case charCodes.greaterThan: case charCodes.greaterThan:
this.readToken_lt_gt(code); this.readToken_gt();
return; return;
case charCodes.equalsTo: case charCodes.equalsTo:

View File

@ -200,6 +200,8 @@ export const tt: { [name: string]: TokenType } = {
bitwiseXOR: createBinop("^", 4), bitwiseXOR: createBinop("^", 4),
bitwiseAND: createBinop("&", 5), bitwiseAND: createBinop("&", 5),
equality: createBinop("==/!=/===/!==", 6), equality: createBinop("==/!=/===/!==", 6),
lt: createBinop("</>/<=/>=", 7),
gt: createBinop("</>/<=/>=", 7),
relational: createBinop("</>/<=/>=", 7), relational: createBinop("</>/<=/>=", 7),
bitShift: createBinop("<</>>/>>>", 8), bitShift: createBinop("<</>>/>>>", 8),
plusMin: createToken("+/-", { beforeExpr, binop: 9, prefix, startsExpr }), plusMin: createToken("+/-", { beforeExpr, binop: 9, prefix, startsExpr }),