diff --git a/gigaparsec.go b/gigaparsec.go index 46afbd4..5762d41 100644 --- a/gigaparsec.go +++ b/gigaparsec.go @@ -9,8 +9,6 @@ import ( "git.codemonkeysoftware.net/b/gigaparsec/cursor" ) -var ErrNoParse = errors.New("no parse") - type Result[In, Out any] struct { Value Out State[In] @@ -33,7 +31,22 @@ func (pe ParseError) Error() string { return fmt.Sprintf("parse error: %d: %s", pe.Pos, pe.Got) } -type State[T any] cursor.Cursor[T] +type State[T any] struct { + cursor cursor.Cursor[T] +} + +func (s State[T]) Cursor() cursor.Cursor[T] { + return s.cursor +} + +func (s State[T]) Read(dst []T) (n uint64, next State[T], err error) { + n, c, err := s.cursor.Read(dst) + return n, State[T]{cursor: c}, err +} + +func (s State[T]) Pos() uint64 { + return s.cursor.Pos() +} type Parser[In, Out any] func(State[In]) (consumed bool, reply Result[In, Out], err error) @@ -54,6 +67,9 @@ func Satisfy[T any](pred func(T) bool) Parser[T, T] { if errors.Is(err, io.EOF) { return false, Result[T, T]{}, ParseError(MessageEnd(state.Pos())) } + if err != nil { + return false, Result[T, T]{}, err + } if n != 1 { panic(fmt.Sprintf("expected 1 element from Read, but got %d", n)) } @@ -71,6 +87,32 @@ func Satisfy[T any](pred func(T) bool) Parser[T, T] { } } +func Slice[T comparable](s []T) Parser[T, []T] { + expected := fmt.Sprint(s) + return func(state State[T]) (consumed bool, reply Result[T, []T], err error) { + token := make([]T, len(s)) + _, next, err := state.Read(token) + if errors.Is(err, io.EOF) { + return false, Result[T, []T]{}, ParseError(MessageEnd(state.Pos())) + } + if err != nil { + return false, Result[T, []T]{}, err + } + if !slices.Equal(s, token) { + return false, Result[T, []T]{}, ParseError{ + Pos: state.Pos(), + Got: fmt.Sprint(token), + Expected: []string{expected}, + } + } + return true, Result[T, []T]{ + Value: token, + State: next, + Message: MessageOK(state.Pos()), + }, nil + } +} + func Bind[In, A, B any](p Parser[In, A], f func(A) Parser[In, B]) Parser[In, B] { return func(input State[In]) (bool, Result[In, B], error) { consumed, resultA, err := p(input)