diff --git a/src/test.nim b/src/test.nim index 12c3987..c7723bf 100644 --- a/src/test.nim +++ b/src/test.nim @@ -24,6 +24,7 @@ type tokens: seq[Token] ParseErrorKind = enum + choiceMismatchErr charMismatchErr endOfStringErr ParserError = ref object @@ -96,6 +97,13 @@ proc `$`*(x: ParserError): string = &"""Parsing Error (Character Mismatch Error): {original} +{errSpace}^ Expected '{expected}' but got '{unexpected}'""" + of choiceMismatchErr(expected: @expected, parser: @parser, index: @index, unexpected: @unexpected): + let original = parser.state.stream + let errSpace = " ".repeat(max(0, index)) + + &"""Parsing Error (Character Mismatch Error): +{original} {errSpace}^ Expected '{expected}' but got '{unexpected}'""" else: "ParseError" @@ -204,6 +212,33 @@ func manyUntil(acceptFn: Parser -> ParserResult, stopFn: Parser -> ParserResult) func anyUntil(stopFn: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} = manyUntil(ch(AllChars), stopFn) +func choice(parsers: seq[Parser -> ParserResult]): (Parser -> ParserResult) {.inline.} = + return proc(parser: Parser): ParserResult = + var errors: seq[ParserResult] = newSeq[ParserResult]() + var found = Nothing[ParserResult]() + + for fn in parsers: + let fnResult: ParserResult = fn(parser) + + if fnResult.isOk(): + found = fnResult.just + break + else: + errors = errors & fnResult + + return found + .fold( + proc(): ParserResult = + let prettyErrors = errors.map((x: ParserResult) => x.error().expected) + err(ParserError( + kind: choiceMismatchErr, + expected: &"Choice ({prettyErrors})", + unexpected: errors[0].error().unexpected, + parser: parser, + )), + proc(x: ParserResult): ParserResult = x, + ) + proc parseSeq(parser: ParserResult, xs: seq[Parser -> ParserResult]): ParserResult = xs.foldl(a.flatMap(b), parser) @@ -240,10 +275,8 @@ when isMainModule: # xs => xs.foldl(a & b.value, "") # ) - - - echo initParser("_FOO___BAR").parseSeq(@[ - anyUntil(endOfStream), + echo initParser("BB").parseSeq(@[ + choice(@[ch(Digits), ch('B')]), ])