diff options
Diffstat (limited to 'node_modules/meriyah/src/lexer/identifier.ts')
-rw-r--r-- | node_modules/meriyah/src/lexer/identifier.ts | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/node_modules/meriyah/src/lexer/identifier.ts b/node_modules/meriyah/src/lexer/identifier.ts new file mode 100644 index 0000000..ec8a7e4 --- /dev/null +++ b/node_modules/meriyah/src/lexer/identifier.ts @@ -0,0 +1,177 @@ +import { ParserState, Context } from '../common'; +import { Token, descKeywordTable } from '../token'; +import { Chars } from '../chars'; +import { advanceChar, consumeMultiUnitCodePoint, fromCodePoint, toHex } from './common'; +import { CharTypes, CharFlags, isIdentifierPart, isIdentifierStart, isIdPart } from './charClassifier'; +import { report, reportScannerError, Errors } from '../errors'; + +/** + * Scans identifier + * + * @param parser Parser object + * @param context Context masks + */ +export function scanIdentifier(parser: ParserState, context: Context, isValidAsKeyword: 0 | 1): Token { + while (isIdPart[advanceChar(parser)]) {} + parser.tokenValue = parser.source.slice(parser.tokenPos, parser.index); + + return parser.currentChar !== Chars.Backslash && parser.currentChar < 0x7e + ? descKeywordTable[parser.tokenValue] || Token.Identifier + : scanIdentifierSlowCase(parser, context, 0, isValidAsKeyword); +} + +/** + * Scans unicode identifier + * + * @param parser Parser object + * @param context Context masks + */ +export function scanUnicodeIdentifier(parser: ParserState, context: Context): Token { + const cookedChar = scanIdentifierUnicodeEscape(parser); + if (!isIdentifierPart(cookedChar)) report(parser, Errors.InvalidUnicodeEscapeSequence); + parser.tokenValue = fromCodePoint(cookedChar); + return scanIdentifierSlowCase(parser, context, /* hasEscape */ 1, CharTypes[cookedChar] & CharFlags.KeywordCandidate); +} + +/** + * Scans identifier slow case + * + * @param parser Parser object + * @param context Context masks + * @param hasEscape True if contains a unicode sequence + * @param isValidAsKeyword + */ +export function scanIdentifierSlowCase( + parser: ParserState, + context: Context, + hasEscape: 0 | 1, + isValidAsKeyword: number +): Token { + let start = parser.index; + + while (parser.index < parser.end) { + if (parser.currentChar === Chars.Backslash) { + parser.tokenValue += parser.source.slice(start, parser.index); + hasEscape = 1; + const code = scanIdentifierUnicodeEscape(parser); + if (!isIdentifierPart(code)) report(parser, Errors.InvalidUnicodeEscapeSequence); + isValidAsKeyword = isValidAsKeyword && CharTypes[code] & CharFlags.KeywordCandidate; + parser.tokenValue += fromCodePoint(code); + start = parser.index; + } else if (isIdentifierPart(parser.currentChar) || consumeMultiUnitCodePoint(parser, parser.currentChar)) { + advanceChar(parser); + } else { + break; + } + } + + if (parser.index <= parser.end) { + parser.tokenValue += parser.source.slice(start, parser.index); + } + + const length = parser.tokenValue.length; + + if (isValidAsKeyword && length >= 2 && length <= 11) { + const token: Token | undefined = descKeywordTable[parser.tokenValue]; + if (token === void 0) return Token.Identifier; + if (!hasEscape) return token; + + if (context & Context.Strict) { + return token === Token.AwaitKeyword && (context & (Context.Module | Context.InAwaitContext)) === 0 + ? token + : token === Token.StaticKeyword + ? Token.EscapedFutureReserved + : (token & Token.FutureReserved) === Token.FutureReserved + ? Token.EscapedFutureReserved + : Token.EscapedReserved; + } + if ( + context & Context.AllowEscapedKeyword && + (context & Context.InGlobal) === 0 && + (token & Token.Reserved) === Token.Reserved + ) + return token; + if (token === Token.YieldKeyword) { + return context & Context.AllowEscapedKeyword + ? Token.AnyIdentifier + : context & Context.InYieldContext + ? Token.EscapedReserved + : token; + } + + return token === Token.AsyncKeyword && context & Context.AllowEscapedKeyword + ? Token.AnyIdentifier + : (token & Token.FutureReserved) === Token.FutureReserved + ? token + : token === Token.AwaitKeyword && (context & Context.InAwaitContext) === 0 + ? token + : Token.EscapedReserved; + } + return Token.Identifier; +} + +/** + * Scans private name + * + * @param parser Parser object + */ +export function scanPrivateIdentifier(parser: ParserState): Token { + if (!isIdentifierStart(advanceChar(parser))) report(parser, Errors.MissingPrivateIdentifier); + return Token.PrivateField; +} + +/** + * Scans unicode identifier + * + * @param parser Parser object + */ +export function scanIdentifierUnicodeEscape(parser: ParserState): number { + // Check for Unicode escape of the form '\uXXXX' + // and return code point value if valid Unicode escape is found. + if (parser.source.charCodeAt(parser.index + 1) !== Chars.LowerU) { + report(parser, Errors.InvalidUnicodeEscapeSequence); + } + parser.currentChar = parser.source.charCodeAt((parser.index += 2)); + return scanUnicodeEscape(parser); +} + +/** + * Scans unicode escape value + * + * @param parser Parser object + */ +export function scanUnicodeEscape(parser: ParserState): number { + // Accept both \uxxxx and \u{xxxxxx} + let codePoint = 0; + const char = parser.currentChar; + // First handle a delimited Unicode escape, e.g. \u{1F4A9} + if (char === Chars.LeftBrace) { + const begin = parser.index - 2; + while (CharTypes[advanceChar(parser)] & CharFlags.Hex) { + codePoint = (codePoint << 4) | toHex(parser.currentChar); + if (codePoint > Chars.NonBMPMax) reportScannerError(begin, parser.line, parser.index + 1, Errors.UnicodeOverflow); + } + + // At least 4 characters have to be read + if ((parser.currentChar as number) !== Chars.RightBrace) { + reportScannerError(begin, parser.line, parser.index - 1, Errors.InvalidHexEscapeSequence); + } + advanceChar(parser); // consumes '}' + return codePoint; + } + + if ((CharTypes[char] & CharFlags.Hex) === 0) report(parser, Errors.InvalidHexEscapeSequence); // first one is mandatory + + const char2 = parser.source.charCodeAt(parser.index + 1); + if ((CharTypes[char2] & CharFlags.Hex) === 0) report(parser, Errors.InvalidHexEscapeSequence); + const char3 = parser.source.charCodeAt(parser.index + 2); + if ((CharTypes[char3] & CharFlags.Hex) === 0) report(parser, Errors.InvalidHexEscapeSequence); + const char4 = parser.source.charCodeAt(parser.index + 3); + if ((CharTypes[char4] & CharFlags.Hex) === 0) report(parser, Errors.InvalidHexEscapeSequence); + + codePoint = (toHex(char) << 12) | (toHex(char2) << 8) | (toHex(char3) << 4) | toHex(char4); + + parser.currentChar = parser.source.charCodeAt((parser.index += 4)); + + return codePoint; +} |