UNPKG

buntis

Version:

A 100% compliant, self-hosted typescript parser that emits an ESTree-compatible abstract syntax tree

289 lines (241 loc) 9.2 kB
import { CharTypes, CharFlags, isIdentifierStart } from './charClassifier'; import { Token } from '../token'; import { Chars } from '../chars'; import { ParserState, Context } from '../common'; import { advance, toHex } from './common'; import { report, Errors } from '../errors'; export function scanNumber( parser: ParserState, context: Context, nonOctalDecimalInteger: 0 | 1, isFloat: 0 | 1 ): Token { let char = parser.nextCodePoint; let decimalValue = 0; let value: string | number = 0; if (isFloat) { decimalValue = scanDecimalDigits(parser, context, char); if (decimalValue < 0) return Token.Error; value = '.' + decimalValue; } else { let digit = 9; let allowSeparator: 0 | 1 = 0; if (nonOctalDecimalInteger === 0) { while (digit >= 0 && ((char >= Chars.Zero && char <= Chars.Nine) || char == Chars.Underscore)) { if (char === Chars.Underscore) { char = advance(parser); if (char === Chars.Underscore) { report(parser, context, Errors.ContinuousNumericSeparator, /* early */ 1); return Token.Error; } allowSeparator = 1; continue; } allowSeparator = 0; value = 10 * (value as number) + (char - Chars.Zero); char = advance(parser); --digit; } if (allowSeparator) { report(parser, context, Errors.TrailingNumericSeparator, /* early */ 1); return Token.Error; } if (digit >= 0 && char !== Chars.Period && !isIdentifierStart(char)) { parser.tokenValue = value; return Token.NumericLiteral; } } decimalValue = scanDecimalDigits(parser, context, char); if (decimalValue < 0) return Token.Error; (value as any) += decimalValue; char = parser.nextCodePoint; if (char === Chars.Period) { char = advance(parser); if (char === Chars.Underscore) { report(parser, context, Errors.Unexpected, /* early */ 1); return Token.Error; } decimalValue = scanDecimalDigits(parser, context, char); if (decimalValue < 0) return Token.Error; (value as any) += '.' + decimalValue; isFloat = 1; } } char = parser.nextCodePoint; const { index: end } = parser; let isBigInt: 0 | 1 = 0; if (char === Chars.LowerN) { if (isFloat || nonOctalDecimalInteger) { report(parser, context, Errors.InvalidBigIntLiteral, /* early */ 1); return Token.Error; } char = advance(parser); isBigInt = 1; } else if ((char | 32) == Chars.LowerE) { char = advance(parser); // '-', '+' if (CharTypes[char] & CharFlags.Exponent) char = advance(parser); const { index } = parser; // Exponential notation must contain at least one digit if ((CharTypes[char] & CharFlags.Decimal) === 0) { report(parser, context, Errors.MissingExponent, /* early */ 0); return Token.Error; } // Consume exponential digits decimalValue = scanDecimalDigits(parser, context, char); if (decimalValue < 0) return Token.Error; value += parser.source.substring(end, index) + decimalValue; char = parser.nextCodePoint; } // The source character immediately following a numeric literal must // not be an identifier start or a decimal digit if ((parser.index < parser.length && char >= Chars.Zero && char <= Chars.Nine) || isIdentifierStart(char)) { report(parser, context, Errors.IDStartAfterNumber, /* early */ 0); return Token.Error; } parser.tokenValue = nonOctalDecimalInteger ? parseFloat(parser.source.slice(parser.startPos, parser.index)) : +value; return isBigInt ? Token.BigIntLiteral : Token.NumericLiteral; } export function scanDecimalDigits(parser: ParserState, context: Context, char: number): any { let allowSeparator: 0 | 1 = 0; let start = parser.index; let value = ''; while ((char >= Chars.Zero && char <= Chars.Nine) || char == Chars.Underscore) { if (char === Chars.Underscore) { const { index } = parser; char = advance(parser); if (char === Chars.Underscore) { report(parser, context, Errors.ContinuousNumericSeparator, /* early */ 1); return -1; } allowSeparator = 1; value += parser.source.substring(start, index); start = parser.index; continue; } allowSeparator = 0; char = advance(parser); } if (allowSeparator) { report(parser, context, Errors.TrailingNumericSeparator, /* early */ 1); return -1; } return (value += parser.source.substring(start, parser.index)); } export function scanLeadingZero(parser: ParserState, context: Context, char: number): any { let nonOctalDecimalInteger: 0 | 1 = 0; let allowSeparator: 0 | 1 = 0; let digits = 0; let value = 0; char = advance(parser); // Hex if ((char | 32) === Chars.LowerX) { char = advance(parser); // skips 'X', 'x' while (CharTypes[char] & (CharFlags.Hex | CharFlags.Underscore)) { if (char === Chars.Underscore) { if (!allowSeparator) { report(parser, context, Errors.ContinuousNumericSeparator, /* early */ 1); return Token.Error; } allowSeparator = 0; char = advance(parser); continue; } allowSeparator = 1; value = value * 0x10 + toHex(char); digits++; char = advance(parser); } if (digits < 1 || !allowSeparator) { report(parser, context, digits < 1 ? Errors.MissingHexDigits : Errors.TrailingNumericSeparator, /* early */ 0); return Token.Error; } // Octal } else if ((char | 32) === Chars.LowerO) { char = advance(parser); // skips 'X', 'x' while ((char >= Chars.Zero && char <= Chars.Seven) || char == Chars.Underscore) { if (char === Chars.Underscore) { if (!allowSeparator) { report(parser, context, Errors.ContinuousNumericSeparator, /* early */ 1); return Token.Error; } allowSeparator = 0; char = advance(parser); continue; } allowSeparator = 1; value = value * 8 + (char - Chars.Zero); digits++; char = advance(parser); } if (digits < 1 || !allowSeparator) { report(parser, context, digits < 1 ? Errors.MissingOctalDigits : Errors.TrailingNumericSeparator, /* early */ 1); return Token.Error; } } else if ((char | 32) === Chars.LowerB) { char = advance(parser); // skips 'B', 'b' while ((char >= Chars.Zero && char <= Chars.One) || char == Chars.Underscore) { if (char === Chars.Underscore) { if (!allowSeparator) { report(parser, context, Errors.ContinuousNumericSeparator, /* early */ 1); return Token.Error; } allowSeparator = 0; char = advance(parser); continue; } allowSeparator = 1; value = value * 2 + (char - Chars.Zero); digits++; char = advance(parser); } if (digits < 1 || !allowSeparator) { report(parser, context, digits < 1 ? Errors.MissingBinaryDigits : Errors.TrailingNumericSeparator, /* early */ 1); return Token.Error; } } else if (char >= Chars.Zero && char <= Chars.Nine) { // Octal integer literals are not permitted in strict mode code if (context & Context.Strict) { report(parser, context, Errors.StrictOctalEscape, /* early */ 1); return Token.Error; } while (char >= Chars.Zero && char <= Chars.Nine) { if (char >= Chars.Eight) { nonOctalDecimalInteger = 1; } value = value * 8 + (char - Chars.Zero); advance(parser); char = parser.nextCodePoint; } if (char === Chars.Underscore) { report(parser, context, Errors.SeparatorInZeroPrefixedNumber, /* early */ 1); return Token.Error; } if (char === Chars.LowerN) { report(parser, context, Errors.InvalidBigIntLiteral, /* early */ 1); return Token.Error; } if (nonOctalDecimalInteger) { // Use the decimal scanner for the rest of the number. return scanNumber(parser, context, nonOctalDecimalInteger, /* isFloat */ 0); } } else if (char === Chars.Underscore) { report(parser, context, Errors.SeparatorInZeroPrefixedNumber, /* early */ 1); return Token.Error; } else { return scanNumber(parser, context, nonOctalDecimalInteger, /* isFloat */ 0); } let isBigInt = 0; if (char === Chars.LowerN) { char = advance(parser); isBigInt = 1; } // The source character immediately following a numeric literal must // not be an identifier start or a decimal digit if ((parser.index < parser.length && char >= Chars.Zero && char <= Chars.Nine) || isIdentifierStart(char)) { report(parser, context, Errors.IDStartAfterNumber, /* early */ 0); return Token.Error; } parser.tokenValue = value; return isBigInt ? Token.BigIntLiteral : Token.NumericLiteral; }