diff --git a/generator/generateTokenTypes.ts b/generator/generateTokenTypes.ts index bb8562f1..a506724f 100644 --- a/generator/generateTokenTypes.ts +++ b/generator/generateTokenTypes.ts @@ -87,22 +87,22 @@ const types = { tilde: new TokenType("~", {prefix}), pipeline: new BinopTokenType("|>", 0), nullishCoalescing: new BinopTokenType("??", 1), - logicalOR: new BinopTokenType("||", 1), - logicalAND: new BinopTokenType("&&", 2), - bitwiseOR: new BinopTokenType("|", 3), - bitwiseXOR: new BinopTokenType("^", 4), - bitwiseAND: new BinopTokenType("&", 5), - equality: new BinopTokenType("==/!=", 6), - lessThan: new BinopTokenType("<", 7), - greaterThan: new BinopTokenType(">", 7), - relationalOrEqual: new BinopTokenType("<=/>=", 7), - bitShift: new BinopTokenType("<>", 8), - plus: new TokenType("+", {binop: 9, prefix}), - minus: new TokenType("-", {binop: 9, prefix}), - modulo: new BinopTokenType("%", 10), - star: new BinopTokenType("*", 10), - slash: new BinopTokenType("/", 10), - exponent: new TokenType("**", {binop: 11, rightAssociative: true}), + logicalOR: new BinopTokenType("||", 2), + logicalAND: new BinopTokenType("&&", 3), + bitwiseOR: new BinopTokenType("|", 4), + bitwiseXOR: new BinopTokenType("^", 5), + bitwiseAND: new BinopTokenType("&", 6), + equality: new BinopTokenType("==/!=", 7), + lessThan: new BinopTokenType("<", 8), + greaterThan: new BinopTokenType(">", 8), + relationalOrEqual: new BinopTokenType("<=/>=", 8), + bitShift: new BinopTokenType("<>", 9), + plus: new TokenType("+", {binop: 10, prefix}), + minus: new TokenType("-", {binop: 10, prefix}), + modulo: new BinopTokenType("%", 11), + star: new BinopTokenType("*", 11), + slash: new BinopTokenType("/", 11), + exponent: new TokenType("**", {binop: 12, rightAssociative: true}), jsxName: new TokenType("jsxName"), jsxText: new TokenType("jsxText"), @@ -144,8 +144,8 @@ const types = { _null: new KeywordTokenType("null"), _true: new KeywordTokenType("true"), _false: new KeywordTokenType("false"), - _in: new KeywordTokenType("in", {binop: 7}), - _instanceof: new KeywordTokenType("instanceof", {binop: 7}), + _in: new KeywordTokenType("in", {binop: 8}), + _instanceof: new KeywordTokenType("instanceof", {binop: 8}), _typeof: new KeywordTokenType("typeof", {prefix}), _void: new KeywordTokenType("void", {prefix}), _delete: new KeywordTokenType("delete", {prefix}), diff --git a/src/parser/plugins/flow.ts b/src/parser/plugins/flow.ts index 6996ae4f..707e9537 100644 --- a/src/parser/plugins/flow.ts +++ b/src/parser/plugins/flow.ts @@ -113,8 +113,8 @@ function flowParseDeclare(): void { flowParseDeclareFunction(); } else if (match(tt._var)) { flowParseDeclareVariable(); - } else if (isContextual(ContextualKeyword._module)) { - if (lookaheadType() === tt.dot) { + } else if (eatContextual(ContextualKeyword._module)) { + if (eat(tt.dot)) { flowParseDeclareModuleExports(); } else { flowParseDeclareModule(); @@ -139,8 +139,6 @@ function flowParseDeclareVariable(): void { } function flowParseDeclareModule(): void { - next(); - if (match(tt.string)) { parseExprAtom(); } else { @@ -193,8 +191,6 @@ function flowParseDeclareExportDeclaration(): void { } function flowParseDeclareModuleExports(): void { - expectContextual(ContextualKeyword._module); - expect(tt.dot); expectContextual(ContextualKeyword._exports); flowParseTypeAnnotation(); semicolon(); diff --git a/src/parser/plugins/jsx/index.ts b/src/parser/plugins/jsx/index.ts index 9b38c0a9..fee7f34e 100644 --- a/src/parser/plugins/jsx/index.ts +++ b/src/parser/plugins/jsx/index.ts @@ -3,7 +3,6 @@ import { finishToken, getTokenFromCode, IdentifierRole, - lookaheadType, match, next, skipSpace, @@ -116,6 +115,7 @@ function jsxParseElementName(): void { function jsxParseAttributeValue(): void { switch (state.type) { case tt.braceL: + next(); jsxParseExpressionContainer(); nextJSXTagToken(); return; @@ -138,18 +138,16 @@ function jsxParseEmptyExpression(): void { // Do nothing. } -// Parse JSX spread child -// Does not parse the last token. +// Parse JSX spread child, after already processing the { +// Does not parse the closing } function jsxParseSpreadChild(): void { - expect(tt.braceL); expect(tt.ellipsis); parseExpression(); } -// Parses JSX expression enclosed into curly brackets. -// Does not parse the last token. +// Parses JSX expression enclosed into curly brackets, after already processing the { +// Does not parse the closing } function jsxParseExpressionContainer(): void { - next(); if (match(tt.braceR)) { jsxParseEmptyExpression(); } else { @@ -231,7 +229,8 @@ function jsxParseElementAt(): void { break; case tt.braceL: - if (lookaheadType() === tt.ellipsis) { + next(); + if (match(tt.ellipsis)) { jsxParseSpreadChild(); nextJSXExprToken(); } else { diff --git a/src/parser/plugins/typescript.ts b/src/parser/plugins/typescript.ts index 6414de43..615d2665 100644 --- a/src/parser/plugins/typescript.ts +++ b/src/parser/plugins/typescript.ts @@ -226,15 +226,7 @@ function tsParseTypeMemberSemicolon(): void { } } -enum SignatureMemberKind { - TSCallSignatureDeclaration, - TSConstructSignatureDeclaration, -} - -function tsParseSignatureMember(kind: SignatureMemberKind): void { - if (kind === SignatureMemberKind.TSConstructSignatureDeclaration) { - expect(tt._new); - } +function tsParseSignatureMember(): void { tsFillSignature(tt.colon); tsParseTypeMemberSemicolon(); } @@ -267,7 +259,6 @@ function tsTryParseIndexSignature(): boolean { } function tsParsePropertyOrMethodSignature(isReadonly: boolean): void { - parsePropertyName(-1 /* Types don't need context IDs. */); eat(tt.question); if (!isReadonly && (match(tt.parenL) || match(tt.lessThan))) { @@ -281,11 +272,18 @@ function tsParsePropertyOrMethodSignature(isReadonly: boolean): void { function tsParseTypeMember(): void { if (match(tt.parenL) || match(tt.lessThan)) { - tsParseSignatureMember(SignatureMemberKind.TSCallSignatureDeclaration); + // call signature + tsParseSignatureMember(); return; } - if (match(tt._new) && tsIsStartOfConstructSignature()) { - tsParseSignatureMember(SignatureMemberKind.TSConstructSignatureDeclaration); + if (match(tt._new)) { + next(); + if (match(tt.parenL) || match(tt.lessThan)) { + // constructor signature + tsParseSignatureMember(); + } else { + tsParsePropertyOrMethodSignature(false); + } return; } const readonly = !!tsParseModifier([ContextualKeyword._readonly]); @@ -294,14 +292,10 @@ function tsParseTypeMember(): void { if (found) { return; } + parsePropertyName(-1 /* Types don't need context IDs. */); tsParsePropertyOrMethodSignature(readonly); } -function tsIsStartOfConstructSignature(): boolean { - const lookahead = lookaheadType(); - return lookahead === tt.parenL || lookahead === tt.lessThan; -} - function tsParseTypeLiteral(): void { tsParseObjectTypeMembers(); } diff --git a/src/parser/tokenizer/types.ts b/src/parser/tokenizer/types.ts index 68e0c3b3..134656b2 100644 --- a/src/parser/tokenizer/types.ts +++ b/src/parser/tokenizer/types.ts @@ -47,22 +47,22 @@ export enum TokenType { tilde = 17024, // ~ prefix pipeline = 17409, // |> prec:1 nullishCoalescing = 17922, // ?? prec:2 - logicalOR = 18434, // || prec:2 - logicalAND = 18947, // && prec:3 - bitwiseOR = 19460, // | prec:4 - bitwiseXOR = 19973, // ^ prec:5 - bitwiseAND = 20486, // & prec:6 - equality = 20999, // ==/!= prec:7 - lessThan = 21512, // < prec:8 - greaterThan = 22024, // > prec:8 - relationalOrEqual = 22536, // <=/>= prec:8 - bitShift = 23049, // <> prec:9 - plus = 23690, // + prec:10 prefix - minus = 24202, // - prec:10 prefix - modulo = 24587, // % prec:11 - star = 25099, // * prec:11 - slash = 25611, // / prec:11 - exponent = 26188, // ** prec:12 rightAssociative + logicalOR = 18435, // || prec:3 + logicalAND = 18948, // && prec:4 + bitwiseOR = 19461, // | prec:5 + bitwiseXOR = 19974, // ^ prec:6 + bitwiseAND = 20487, // & prec:7 + equality = 21000, // ==/!= prec:8 + lessThan = 21513, // < prec:9 + greaterThan = 22025, // > prec:9 + relationalOrEqual = 22537, // <=/>= prec:9 + bitShift = 23050, // <> prec:10 + plus = 23691, // + prec:11 prefix + minus = 24203, // - prec:11 prefix + modulo = 24588, // % prec:12 + star = 25100, // * prec:12 + slash = 25612, // / prec:12 + exponent = 26189, // ** prec:13 rightAssociative jsxName = 26624, // jsxName jsxText = 27136, // jsxText jsxTagStart = 27648, // jsxTagStart @@ -101,8 +101,8 @@ export enum TokenType { _null = 44560, // null keyword _true = 45072, // true keyword _false = 45584, // false keyword - _in = 46104, // in prec:8 keyword - _instanceof = 46616, // instanceof prec:8 keyword + _in = 46105, // in prec:9 keyword + _instanceof = 46617, // instanceof prec:9 keyword _typeof = 47248, // typeof keyword prefix _void = 47760, // void keyword prefix _delete = 48272, // delete keyword prefix diff --git a/src/parser/traverser/expression.ts b/src/parser/traverser/expression.ts index 7d248628..7cd4a636 100644 --- a/src/parser/traverser/expression.ts +++ b/src/parser/traverser/expression.ts @@ -399,6 +399,13 @@ function parseNoCallExpr(): void { // or `{}`. // Returns true if the parsed expression was an arrow function. export function parseExprAtom(): boolean { + if (eat(tt.modulo)) { + // V8 intrinsic expression. Just parse the identifier, and the function invocation is parsed + // naturally. + parseIdentifier(); + return false; + } + if (match(tt.jsxText)) { parseLiteral(); return false; @@ -429,11 +436,13 @@ export function parseExprAtom(): boolean { return false; case tt._import: - if (lookaheadType() === tt.dot) { - parseImportMetaProperty(); - return false; - } next(); + if (match(tt.dot)) { + // import.meta + state.tokens[state.tokens.length - 1].type = tt.name; + next(); + parseIdentifier(); + } return false; case tt.name: { @@ -546,22 +555,11 @@ function parseFunctionExpression(): void { parseIdentifier(); if (eat(tt.dot)) { // function.sent - parseMetaProperty(); + parseIdentifier(); } parseFunction(functionStart, false); } -function parseMetaProperty(): void { - parseIdentifier(); -} - -function parseImportMetaProperty(): void { - parseIdentifier(); - expect(tt.dot); - // import.meta - parseMetaProperty(); -} - export function parseLiteral(): void { next(); } @@ -652,7 +650,7 @@ function parseNew(): void { expect(tt._new); if (eat(tt.dot)) { // new.target - parseMetaProperty(); + parseIdentifier(); return; } parseNoCallExpr(); diff --git a/test/imports-test.ts b/test/imports-test.ts index cde32ebc..a76a2f91 100644 --- a/test/imports-test.ts +++ b/test/imports-test.ts @@ -1284,6 +1284,18 @@ module.exports = exports.default; ); }); + it("leaves import.meta alone", () => { + assertResult( + ` + console.log(import.meta); + `, + `"use strict"; + console.log(import.meta); + `, + {transforms: ["imports"]}, + ); + }); + it("implements basic live bindings", () => { assertMultiFileOutput( { diff --git a/test/sucrase-test.ts b/test/sucrase-test.ts index c51efdd0..80e67df0 100644 --- a/test/sucrase-test.ts +++ b/test/sucrase-test.ts @@ -847,4 +847,16 @@ describe("sucrase", () => { {transforms: []}, ); }); + + it("handles V8 intrinsic syntax", () => { + assertResult( + ` + %DebugPrint(foo) + `, + ` + %DebugPrint(foo) + `, + {transforms: []}, + ); + }); }); diff --git a/test/tokens-test.ts b/test/tokens-test.ts index bc4dd80c..3e979c65 100644 --- a/test/tokens-test.ts +++ b/test/tokens-test.ts @@ -303,4 +303,20 @@ describe("tokens", () => { ], ); }); + + it("properly parses import.meta", () => { + assertTokens( + ` + f = import.meta + `, + [ + {type: tt.name}, + {type: tt.eq}, + {type: tt.name}, + {type: tt.dot}, + {type: tt.name}, + {type: tt.eof}, + ], + ); + }); });