Files
gigaparsec/gigaparsec.go
T
b c7a743b07e
Test and check / test-and-check (push) Canceled after 0s
Apostrophes are serious business
2026-08-21 23:11:15 -06:00

451 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// SPDX-License-Identifier: Unlicense
package gigaparsec
//go:generate go run ./internal/bindgen -bindpath bind.go -seqpath seq.go -max 9 -pkg gigaparsec
import (
"errors"
"fmt"
"io"
"math"
"slices"
"strings"
)
type Result[In, Out any] struct {
consumed, success bool
value Out
next State[In]
message Message
}
func Fail[In, Out any](consumed bool, msg Message) Result[In, Out] {
return Result[In, Out]{
consumed: consumed,
success: false,
message: msg,
}
}
func Succeed[In, Out any](consumed bool, value Out, next State[In], msg Message) Result[In, Out] {
return Result[In, Out]{
success: true,
value: value,
consumed: consumed,
next: next,
message: msg,
}
}
func (r Result[In, Out]) Status() (success bool, value Out, next State[In]) {
success = r.success
if success {
value = r.value
next = r.next
}
return
}
func (r Result[In, Out]) Consumed() bool {
return r.consumed
}
func (r Result[In, Out]) Consume(consumed bool) Result[In, Out] {
r.consumed = consumed
return r
}
func (r Result[In, Out]) Message() Message {
return r.message
}
type ReaderAt[T any] interface {
ReadAt(p []T, off int64) (n int, err error)
}
type SliceReaderAt[T any] []T
func (s SliceReaderAt[T]) ReadAt(dst []T, off int64) (n int, err error) {
if off < 0 {
return 0, errors.New("SliceReaderAt.ReadAt: negative offset")
}
if off >= int64(len(s)) {
return 0, io.EOF
}
n = copy(dst, s[off:])
if n < len(dst) {
err = io.EOF
}
return n, err
}
type Message struct {
pos uint64
got string
expected []string
}
func (m Message) Pos() uint64 {
return m.pos
}
func (m Message) Got() string {
return m.got
}
func (m Message) Expected() []string {
return m.expected
}
func (m Message) expect(s string) Message {
m.expected = []string{s}
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 MakeMessage(pos uint64, got string, expected ...string) Message {
return Message{
pos: pos,
got: got,
expected: expected,
}
}
func MessageOK(pos uint64) Message { return Message{pos: pos} }
func MessageEnd(pos uint64, expected ...string) Message {
return Message{pos: pos, got: "end of input", expected: expected}
}
// MakeState creates a new State.
// Its mostly useful for testing. You probably dont need it.
func MakeState[In any](r ReaderAt[In]) State[In] {
return State[In]{r: r}
}
// State represents a position in an input source.
type State[In any] struct {
r ReaderAt[In]
pos uint64
}
// Read fills dst with data from this States position in the underlying source.
// It returns the number of data it read and a new State for the position at which
// the read ended, and an error if the read either (1) failed or (2) reached the
// end of the source before filling dst. All reads from a given State will return
// data from the same position the source.
// If the source had too few data left to fill dst, or if the States position is
// at or past the end of the source, err will be io.EOF.
func (s State[In]) Read(dst []In) (n uint64, next State[In], err error) {
if s.pos > math.MaxInt64 {
return 0, s, io.EOF
}
nread, err := s.r.ReadAt(dst, int64(s.pos))
if nread > 0 {
s.pos += uint64(nread)
}
if nread == len(dst) && err == io.EOF {
if nread == 0 {
return 0, s, io.EOF
}
return uint64(nread), s, nil
}
return uint64(nread), s, err
}
// Pos returns this States position.
func (s State[In]) Pos() uint64 {
return s.pos
}
// At returns a State pointing at pos in the same data source.
func (s State[In]) At(pos uint64) State[In] {
return State[In]{r: s.r, pos: pos}
}
func (s State[In]) Message(got string, expected ...string) Message {
return MakeMessage(s.pos, got, expected...)
}
func (s State[In]) MessageOK() Message { return MessageOK(s.pos) }
func (s State[in]) MessageEnd(expected ...string) Message {
return MessageEnd(s.pos, expected...)
}
type Parser[In, Out any] func(State[In]) (Result[In, Out], error)
// Label creates a parser identical to p, except that a failed result will
// include label as an expected parse.
func (p Parser[In, Out]) Label(label string) Parser[In, Out] {
return func(input State[In]) (Result[In, Out], error) {
result, err := p(input)
if err != nil || result.Consumed() {
return result, err
}
msg := result.Message()
if success, value, next := result.Status(); success {
return Succeed(false, value, next, msg.expect(label)), nil
}
return Fail[In, Out](false, msg.expect(label)), nil
}
}
// Where creates a parser that fails if pred does not hold.
func (p Parser[In, Out]) Where(pred func(Out) bool) Parser[In, Out] {
return func(s State[In]) (Result[In, Out], error) {
result, err := p(s)
if result.success && !pred(result.value) {
result.success = false
result.message.got = "failed Where predicate"
}
return result, err
}
}
// Map creates a parser that converts the output of p from Out1 to Out2.
func (p Parser[In, Out]) Map[Out2 any](f func(Out) Out2) Parser[In, Out2] {
return Bind(p, func(out Out) Parser[In, Out2] {
return Return[In](f(out))
})
}
// RunToResult executes a parser on r and returns the result, or an error if reading the inputs fails.
func (p Parser[In, Out]) RunToResult(r ReaderAt[In]) (Result[In, Out], error) {
start := MakeState(r)
return p(start)
}
// Run executes a parser on r and returns the output, or an an error if the parse fails.
func (p Parser[In, Out]) Run(r ReaderAt[In]) (out Out, err error) {
result, err := p.RunToResult(r)
if err != nil {
err = fmt.Errorf("Run: %w", err)
return
}
success, out, _ := result.Status()
if !success {
err = ParseError(result.Message())
return
}
return
}
type ParseError Message
func (pe ParseError) Error() string {
return Message(pe).String()
}
// Return creates a parser that always succeeds and returns value without consuming any input.
func Return[In, Out any](value Out) Parser[In, Out] {
return func(state State[In]) (Result[In, Out], error) {
return Succeed(false, value, state, state.MessageOK()), nil
}
}
// Satisfy creates a parser that attempts to read an input value for which pred returns true.
// If Satisfy succeeds, it returns the matched input value.
func Satisfy[T any](pred func(T) bool) Parser[T, T] {
return func(state State[T]) (Result[T, T], error) {
token := make([]T, 1)
n, next, err := state.Read(token)
if errors.Is(err, io.EOF) {
return Fail[T, T](false, state.MessageEnd()), nil
}
if err != nil {
return Result[T, T]{}, err
}
if n != 1 {
panic(fmt.Sprintf("expected 1 element from Read, but got %d", n))
}
if pred(token[0]) {
return Succeed(true, token[0], next, state.MessageOK()), nil
}
return Fail[T, T](false, state.Message(fmt.Sprint(token))), nil
}
}
// 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, state.MessageEnd()), nil
}
if err != nil {
return Result[T, T]{}, err
}
if token[0] == x {
return Succeed(true, token[0], next, state.MessageOK()), nil
}
return Fail[T, T](false, state.Message(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))
_, next, err := state.Read(token)
if errors.Is(err, io.EOF) {
return Fail[T, []T](false, state.MessageEnd()), nil
}
if err != nil {
return Result[T, []T]{}, err
}
if !slices.Equal(s, token) {
return Fail[T, []T](false, state.Message(fmt.Sprint(token), expected)), nil
}
return Succeed(true, token, next, state.MessageOK()), nil
}
}
func Choose[In, Out any](p Parser[In, Out], ps ...Parser[In, Out]) Parser[In, Out] {
// TODO Check this against the Parsec paper again, and simplify it.
all := append([]Parser[In, Out]{p}, ps...)
return func(input State[In]) (Result[In, Out], error) {
expecteds := make([][]string, 0, len(all))
var value Out
var got string
var failed bool
for _, q := range all {
result, err := q(input)
if err != nil {
return Result[In, Out]{}, err
}
if result.Consumed() {
return result, nil
}
var qMsg Message
msg := result.Message()
success, qValue, _ := result.Status()
if !success {
qMsg = msg
failed = true
} else {
if failed {
value = qValue
failed = false
}
qMsg = msg
}
if got == "" {
got = qMsg.got
}
}
msg := input.Message(got, slices.Concat(expecteds...)...)
if failed {
return Fail[In, Out](false, msg), nil
}
return Succeed(false, value, input, 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
// parser consumed nothing, Try will allow backing out of a complex
// parser that partially succeeded.
func Try[In, Out any](p Parser[In, Out]) Parser[In, Out] {
return func(input State[In]) (Result[In, Out], error) {
result, err := p(input)
if err != nil {
return result, err
}
success, _, _ := result.Status()
if !success {
return Fail[In, Out](false, result.Message()), nil
}
return result, nil
}
}
func end[In any](s State[In]) (Result[In, struct{}], error) {
_, _, err := s.Read([]In{})
if errors.Is(err, io.EOF) {
return Succeed(true, struct{}{}, s, s.MessageOK()), nil
}
if err != nil {
return Result[In, struct{}]{}, fmt.Errorf("End: unexpected error: %w", err)
}
return Fail[In, struct{}](false, s.Message("", "end of input")), nil
}
// End creates a parser that succeeds at the end of the input and fails otherwise.
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)
})
}
}
// Repeat applies p until p fails, and returns the collected outputs.
// It succeeds if and only if p succeeds at least minCount times.
// It consumes if and only if at least one of the applications of p consumes.
func Repeat[In, Out any](minCount int, p Parser[In, Out]) Parser[In, []Out] {
return func(state State[In]) (Result[In, []Out], error) {
var values []Out
var consumed bool
currState := state
for {
result, err := p(currState)
if err != nil {
return Result[In, []Out]{}, fmt.Errorf("AtLeastN: %w", err)
}
consumed = consumed || result.Consumed()
var value Out
var success bool
success, value, nextState := result.Status()
if !success {
if len(values) >= minCount {
return Succeed(consumed, values, currState, state.MessageOK()), nil
}
return Fail[In, []Out](consumed, result.Message()), nil
}
currState = nextState
values = append(values, value)
}
}
}
// Lazy delays creating a parser from p until the parser is called.
// This is useful for preventing recursive function calls in the
// definition of a recursive parser.
func Lazy[In, Out any](p func() Parser[In, Out]) Parser[In, Out] {
return func(s State[In]) (Result[In, Out], error) {
return p()(s)
}
}
// Bracket parses p, flanked by left and right.
func Bracket[In, Out, LOut, ROut any](left Parser[In, LOut], p Parser[In, Out], right Parser[In, ROut]) Parser[In, Out] {
return Seq3(left, p, right, func(_ LOut, val Out, _ ROut) Out { return val })
}