Implement choice parser

This commit is contained in:
Florian Schroedl
2022-01-20 17:00:00 +01:00
parent dfb4baa8c8
commit 383a234921

View File

@@ -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')]),
])