package gigaparsec import ( "errors" "fmt" ) // TODO Everything. See https://smunix.github.io/dev.stephendiehl.com/fun/002_parsers.html. var ErrNoParse = errors.New("no parse") type Result[T any] struct { Value T State Message } type Message struct { Pos Msg string Expected []string } type ParseError struct { Message } func (pe ParseError) Error() string { return fmt.Sprintf("parse error: %d: %s", pe.Message.Pos, pe.Message.Msg) } type Pos uint64 type State struct { Pos Input []byte } type Parser[T any] func(State) (consumed bool, reply Result[T], err error) func Return[T any](value T) Parser[T] { return func(state State) (bool, Result[T], error) { return false, Result[T]{Value: value, State: state}, nil } } func Satisfy(pred func(byte) bool) Parser[byte] { return func(state State) (bool, Result[byte], error) { if len(state.Input) == 0 { return false, Result[byte]{}, ErrNoParse } b := state.Input[0] if pred(b) { return true, Result[byte]{Value: b, State: state.Input[1:]}, nil } return false, Result[byte]{}, ErrNoParse } } func Bind[A, B any](p Parser[A], f func(A) Parser[B]) Parser[B] { return func(input State) (bool, Result[B], error) { consumed, replyA, err := p(input) if err != nil { return false, Result[B]{}, err } consumed2, replyB, err := f(replyA.Value)(replyA.Rest) return consumed || consumed2, replyB, err } } func Choose[A any](p, q Parser[A]) Parser[A] { return func(input State) (bool, Result[A], error) { consumedP, replyP, errP := p(input) if consumedP { return consumedP, replyP, errP } if errP != nil { return q(input) } consumedQ, replyQ, errQ := q(input) if consumedQ { return consumedQ, replyQ, errQ } return consumedP, replyP, errP } } func Try[A any](p Parser[A]) Parser[A] { return func(input State) (bool, Result[A], error) { consumed, reply, err := p(input) if err != nil { return false, Result[A]{}, err } return consumed, reply, err } }