A world-class math input for the web
at main 66 lines 2.1 kB view raw
1import { 2 CannotParseError, 3 CaretLeafParselet, 4 CaretParser, 5 TokensTag, 6} from "@caret-js/core"; 7import { DecimalNode } from "../nodes/decimal"; 8import { CharToken } from "@caret-js/core"; 9 10// This parselet looks for a sequence of `CharToken`s that form a decimal number, 11// meaning it has digits, optionally a single decimal point, and more digits. The digits 12// are optional on either side of the decimal point, but at least one digit must be present. 13 14export class DecimalParselet extends CaretLeafParselet { 15 parse(parser: CaretParser): DecimalNode { 16 let value = ""; 17 let hasLeadingMinus = false; 18 let hasDecimalPoint = false; 19 let hasDigits = false; 20 let consumedTokens: CharToken[] = []; 21 22 // Continue consuming CharTokens that are digits or a decimal point 23 const consumedToken = parser.consume(); 24 this.assert( 25 consumedToken instanceof CharToken && 26 ((consumedToken.char >= "0" && consumedToken.char <= "9") || 27 consumedToken.char === "." || 28 consumedToken.char === "-"), 29 "Expected CharToken for decimal number" 30 ); 31 32 let currentToken = consumedToken; 33 consumedTokens.push(currentToken); 34 35 while (true) { 36 if (currentToken.char === ".") { 37 hasDecimalPoint = true; 38 } else if (currentToken.char >= "0" && currentToken.char <= "9") { 39 hasDigits = true; 40 } else if (currentToken.char === "-") { 41 hasLeadingMinus = true; 42 } 43 value += currentToken.char; 44 const nextToken = parser.peek(); 45 if (!nextToken) break; 46 if ( 47 !( 48 nextToken instanceof CharToken && 49 ((nextToken.char >= "0" && nextToken.char <= "9") || 50 (nextToken.char === "." && !hasDecimalPoint)) 51 ) 52 ) { 53 break; 54 } 55 currentToken = nextToken; 56 parser.consume(); 57 consumedTokens.push(currentToken); 58 } 59 60 if (!hasDigits) { 61 throw new CannotParseError(this, "Decimal must have at least one digit"); 62 } 63 64 return DecimalNode.from(value).addTag(new TokensTag(consumedTokens)); 65 } 66}