import { CannotParseError, CaretLeafParselet, CaretParser, TokensTag, } from "@caret-js/core"; import { DecimalNode } from "../nodes/decimal"; import { CharToken } from "@caret-js/core"; // This parselet looks for a sequence of `CharToken`s that form a decimal number, // meaning it has digits, optionally a single decimal point, and more digits. The digits // are optional on either side of the decimal point, but at least one digit must be present. export class DecimalParselet extends CaretLeafParselet { parse(parser: CaretParser): DecimalNode { let value = ""; let hasLeadingMinus = false; let hasDecimalPoint = false; let hasDigits = false; let consumedTokens: CharToken[] = []; // Continue consuming CharTokens that are digits or a decimal point const consumedToken = parser.consume(); this.assert( consumedToken instanceof CharToken && ((consumedToken.char >= "0" && consumedToken.char <= "9") || consumedToken.char === "." || consumedToken.char === "-"), "Expected CharToken for decimal number" ); let currentToken = consumedToken; consumedTokens.push(currentToken); while (true) { if (currentToken.char === ".") { hasDecimalPoint = true; } else if (currentToken.char >= "0" && currentToken.char <= "9") { hasDigits = true; } else if (currentToken.char === "-") { hasLeadingMinus = true; } value += currentToken.char; const nextToken = parser.peek(); if (!nextToken) break; if ( !( nextToken instanceof CharToken && ((nextToken.char >= "0" && nextToken.char <= "9") || (nextToken.char === "." && !hasDecimalPoint)) ) ) { break; } currentToken = nextToken; parser.consume(); consumedTokens.push(currentToken); } if (!hasDigits) { throw new CannotParseError(this, "Decimal must have at least one digit"); } return DecimalNode.from(value).addTag(new TokensTag(consumedTokens)); } }