Added Pipe and Match parsers

This commit is contained in:
2024-09-17 20:10:19 -06:00
parent 4fa3c2a466
commit edff0f8ca0
2 changed files with 62 additions and 7 deletions

View File

@ -6,6 +6,7 @@ import (
"fmt"
"io"
"slices"
"strings"
"git.codemonkeysoftware.net/b/gigaparsec/cursor"
)
@ -78,14 +79,17 @@ type Message struct {
expected []string
}
// TODO rename
func (m Message) PosMethod() uint64 {
return m.pos
}
// TODO rename
func (m Message) GotMethod() string {
return m.got
}
// TODO rename
func (m Message) ExpectedMethod() []string {
return m.expected
}
@ -95,6 +99,23 @@ func (m Message) expect(s string) Message {
return m
}
func (m Message) String() string {
s := fmt.Sprintf("bad parse at %d", m.pos)
if m.got != "" || len(m.expected) > 0 {
s += ":"
if m.got != "" {
s += fmt.Sprintf(" got %v", m.got)
if len(m.expected) > 0 {
s += ","
}
}
if len(m.expected) > 0 {
s += fmt.Sprintf(" expected %v", strings.Join(m.expected, " or "))
}
}
return s
}
func MessageOK(pos uint64) Message { return Message{pos: pos} }
func MessageEnd(pos uint64) Message { return Message{pos: pos, got: "end of input"} }
@ -171,9 +192,29 @@ func Satisfy[T any](pred func(T) bool) Parser[T, T] {
}
}
// Slice creates a parser that attempts to read the contents of s from the input.
// If Slice succeeds, it returns a copy of the matched input values.
func Slice[T comparable](s []T) Parser[T, []T] {
// Match creates a parser that attempts to read an input value equal to x.
// If Match succeeds, it returns the matched input value.
func Match[T comparable](x T) Parser[T, T] {
expected := fmt.Sprint(x)
return func(state State[T]) (Result[T, T], error) {
token := make([]T, 1)
_, next, err := state.Read(token)
if errors.Is(err, io.EOF) {
return Fail[T, T](false, MessageEnd(state.Pos())), nil
}
if err != nil {
return Result[T, T]{}, err
}
if token[0] == x {
return Succeed(true, token[0], next, MessageOK(state.Pos())), nil
}
return Fail[T, T](false, MakeMessage(state.Pos(), fmt.Sprint(token), expected)), nil
}
}
// MatchSlice creates a parser that attempts to read the contents of s from the input.
// If MatchSlice succeeds, it returns a copy of the matched input values.
func MatchSlice[T comparable](s []T) Parser[T, []T] {
expected := fmt.Sprint(s)
return func(state State[T]) (Result[T, []T], error) {
token := make([]T, len(s))
@ -271,3 +312,11 @@ func end[In any](s State[In]) (Result[In, struct{}], error) {
func End[In any]() Parser[In, struct{}] {
return end
}
func Pipe[In, Ignore, Through any](p Parser[In, Ignore]) func(Through) Parser[In, Through] {
return func(t Through) Parser[In, Through] {
return Bind(p, func(Ignore) Parser[In, Through] {
return Return[In](t)
})
}
}