diff --git a/toml/_parser.ts b/toml/_parser.ts index 1e857eb76f29..8256f5e79ab8 100644 --- a/toml/_parser.ts +++ b/toml/_parser.ts @@ -197,8 +197,13 @@ export function deepAssignWithTable(target: Record, table: { // Parser combinators and generators // --------------------------------- -function or(parsers: ParserComponent[]): ParserComponent { - return (scanner: Scanner): ParseResult => { +// deno-lint-ignore no-explicit-any +function or[]>( + parsers: T, +): ParserComponent< + ReturnType extends ParseResult ? R : Failure +> { + return (scanner: Scanner) => { for (const parse of parsers) { const result = parse(scanner); if (result.ok) return result; @@ -499,22 +504,41 @@ export function multilineLiteralString( return success(acc.join("")); } -const symbolPairs: [string, unknown][] = [ - ["true", true], - ["false", false], +const BOOLEAN_REGEXP = /(?:true|false)\b/y; +export function boolean(scanner: Scanner): ParseResult { + scanner.skipWhitespaces(); + const match = scanner.match(BOOLEAN_REGEXP); + if (!match) return failure(); + const string = match[0]; + scanner.next(string.length); + const value = string === "true"; + return success(value); +} + +const INFINITY_MAP = new Map([ ["inf", Infinity], ["+inf", Infinity], ["-inf", -Infinity], - ["nan", NaN], - ["+nan", NaN], - ["-nan", NaN], -]; -export function symbols(scanner: Scanner): ParseResult { +]); +const INFINITY_REGEXP = /[+-]?inf\b/y; +export function infinity(scanner: Scanner): ParseResult { scanner.skipWhitespaces(); - const found = symbolPairs.find(([str]) => scanner.startsWith(str)); - if (!found) return failure(); - const [str, value] = found; - scanner.next(str.length); + const match = scanner.match(INFINITY_REGEXP); + if (!match) return failure(); + const string = match[0]; + scanner.next(string.length); + const value = INFINITY_MAP.get(string)!; + return success(value); +} + +const NAN_REGEXP = /[+-]?nan\b/y; +export function nan(scanner: Scanner): ParseResult { + scanner.skipWhitespaces(); + const match = scanner.match(NAN_REGEXP); + if (!match) return failure(); + const string = match[0]; + scanner.next(string.length); + const value = NaN; return success(value); } @@ -652,7 +676,9 @@ export const value = or([ multilineLiteralString, basicString, literalString, - symbols, + boolean, + infinity, + nan, dateTime, localTime, binary, diff --git a/toml/parse_test.ts b/toml/parse_test.ts index 797562d4846f..361c780874a2 100644 --- a/toml/parse_test.ts +++ b/toml/parse_test.ts @@ -5,22 +5,24 @@ import { bareKey, basicString, binary, + boolean, dateTime, deepAssignWithTable, dottedKey, float, hex, + infinity, inlineTable, integer, literalString, localTime, multilineBasicString, multilineLiteralString, + nan, octal, pair, parserFactory, Scanner, - symbols, table, value, } from "./_parser.ts"; @@ -181,14 +183,38 @@ Violets are\\tblue'''`), }); Deno.test({ - name: "parse() handles symbols", + name: "parse() handles boolean", fn() { - const parse = parserFactory(symbols); + const parse = parserFactory(boolean); assertEquals(parse("true"), true); - assertEquals(parse("nan"), NaN); + assertEquals(parse("false"), false); + assertThrows(() => parse("truetrue")); + assertThrows(() => parse("false ")); + }, +}); + +Deno.test({ + name: "parse() handles infinity", + fn() { + const parse = parserFactory(infinity); assertEquals(parse("inf"), Infinity); - assertThrows(() => parse("")); - assertThrows(() => parse("_")); + assertEquals(parse("+inf"), Infinity); + assertEquals(parse("-inf"), -Infinity); + assertThrows(() => parse("infinf")); + assertThrows(() => parse("+inf ")); + assertThrows(() => parse("-inf_")); + }, +}); +Deno.test({ + name: "parse() handles nan", + fn() { + const parse = parserFactory(nan); + assertEquals(parse("nan"), NaN); + assertEquals(parse("+nan"), NaN); + assertEquals(parse("-nan"), NaN); + assertThrows(() => parse("nannan")); + assertThrows(() => parse("+nan ")); + assertThrows(() => parse("-nan_")); }, });