Propagate errors through Choose

This commit is contained in:
Brandon Dyck 2024-09-02 21:14:46 -06:00
parent b3b90b7e6b
commit 183e4db928

View File

@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"io"
"slices"
"git.codemonkeysoftware.net/b/gigaparsec/cursor"
)
@ -99,6 +100,52 @@ func Choose[In, Out any](p, q Parser[In, Out]) Parser[In, Out] {
}
}
func ChooseMany[In, Out any](p Parser[In, Out], ps ...Parser[In, Out]) Parser[In, Out] {
all := append([]Parser[In, Out]{p}, ps...)
return func(input State[In]) (bool, Result[In, Out], error) {
expecteds := make([][]string, 0, len(all))
var value Out
var got string
var gotGot bool
var failed bool
for _, q := range all {
consumed, result, err := q(input)
if consumed {
return consumed, result, err
}
var qMsg Message
if err != nil {
var parseErr ParseError
if !errors.As(err, &parseErr) {
// It broke. Give up.
return consumed, result, err
}
failed = failed && true
qMsg = Message(parseErr)
} else {
if failed {
value = result.Value
failed = false
}
qMsg = result.Message
}
if !gotGot {
got = qMsg.Got
gotGot = true
}
}
msg := Message{Pos: input.Pos(), Got: got, Expected: slices.Concat(expecteds...)}
if failed {
return false, Result[In, Out]{}, ParseError(msg)
}
return false, Result[In, Out]{
Value: value,
State: input,
Message: msg,
}, nil
}
}
// Try behaves identically to p, except that if p returns an error,
// Try will pretend that no input was consumed. This allows infinite
// lookahead: Since Choose only calls another parser when the previous