Compare commits

..

21 Commits

Author SHA1 Message Date
e60f6ae015 Added Bracket, Where, and Token (without tests) 2025-04-11 19:03:45 -06:00
035fa7da14 Added Lazy combinator 2025-04-02 15:50:12 -06:00
82ed6b5546 Regexp: don't return error when match fails at EOF 2025-04-02 14:24:21 -06:00
4e157f7a0e Regexp: succeed on empty match at end of input 2025-04-02 14:06:12 -06:00
1892a97070 Add some TODOs 2025-04-02 13:27:16 -06:00
981edc92f7 Add Seq benchmarks 2024-11-29 23:38:38 -07:00
74237e2593 Merge branch 'master' of git.codemonkeysoftware.net:b/gigaparsec 2024-11-29 11:00:38 -07:00
17c468a3f7 Add naïve Seq series 2024-11-29 11:00:34 -07:00
ac739c0f3b Update todo list 2024-11-29 08:04:50 +00:00
b3a6bfc02e Don't be so negative, man 2024-11-29 00:55:59 -07:00
c85d0d280e Move naïve Bind and Seq into a separate package 2024-11-29 00:53:11 -07:00
fa6c15566d Fix formatting in inspiration.md 2024-11-28 22:11:05 +00:00
13d83e70ad Add missing SPDX headers 2024-10-17 16:02:29 -06:00
56536b04f6 Turn up Bind/Seq count and write a simple Bind benchmark 2024-10-17 16:01:27 -06:00
bc2f7aa911 Added Try tests 2024-10-17 08:57:45 -06:00
abef123f8a Removed unused Todo function 2024-10-16 13:52:13 -06:00
f3a37f5fb6 Test Regexp failure message 2024-10-16 13:50:14 -06:00
c22246b7de Added some Regexp tests 2024-10-16 13:32:25 -06:00
9c5e8fff0e Added Repeat tests 2024-10-16 11:58:12 -06:00
bfc9a9ae58 Test Repeat success and next state 2024-10-16 09:06:25 -06:00
59903ba151 Check source files for missing SPDX headers 2024-09-30 18:56:06 -06:00
15 changed files with 1473 additions and 45 deletions

View File

@ -1,8 +1,8 @@
# Gigaparsec
[![Go Reference](https://pkg.go.dev/badge/git.codemonkeysoftware.net/b/gigaparsec.svg)](https://pkg.go.dev/git.codemonkeysoftware.net/b/gigaparsec)
![Total garbage.](https://img.shields.io/badge/Total-garbage-red)
by Brandon Dyck <[brandon@dyck.us](mailto:brandon@dyck.us)>
Monadic parser combinators in Go
**I don't recommend using this yet. It is very unfinished and it will break.**

View File

@ -1,6 +1,9 @@
Write Repeat tests
Think about not requiring so much Pos() when making messages
Think about changing "consume" to "commit"
Rename Seq2 to Seq
Document Seq
Should MakeState be private now that there's Run?
What's Megaparsec got that we ain't got?
Add and benchmark naïve Seq
chainl
whitespace handling

418
bind.go
View File

@ -278,3 +278,421 @@ func Bind5[In, Out, T, T2, T3, T4, T5 any](
return Succeed(anyConsumed, val6, next, MessageOK(s.Pos())), nil
}
}
// Bind6 is equivalent to 6 nested calls to Bind.
func Bind6[In, Out, T, T2, T3, T4, T5, T6 any](
p Parser[In, T],
f func(T) Parser[In, T2],
f2 func(T2) Parser[In, T3],
f3 func(T3) Parser[In, T4],
f4 func(T4) Parser[In, T5],
f5 func(T5) Parser[In, T6],
f6 func(T6) Parser[In, Out],
) Parser[In, Out] {
return func(s State[In]) (Result[In, Out], error) {
var anyConsumed bool
var next = s
r, err := p(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r.Consumed()
success, val, next := r.Status()
if !success {
return Fail[In, Out](anyConsumed, r.Message()), nil
}
r2, err := f(val)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r2.Consumed()
success, val2, next := r2.Status()
if !success {
return Fail[In, Out](anyConsumed, r2.Message()), nil
}
r3, err := f2(val2)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r3.Consumed()
success, val3, next := r3.Status()
if !success {
return Fail[In, Out](anyConsumed, r3.Message()), nil
}
r4, err := f3(val3)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r4.Consumed()
success, val4, next := r4.Status()
if !success {
return Fail[In, Out](anyConsumed, r4.Message()), nil
}
r5, err := f4(val4)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r5.Consumed()
success, val5, next := r5.Status()
if !success {
return Fail[In, Out](anyConsumed, r5.Message()), nil
}
r6, err := f5(val5)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r6.Consumed()
success, val6, next := r6.Status()
if !success {
return Fail[In, Out](anyConsumed, r6.Message()), nil
}
r7, err := f6(val6)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r7.Consumed()
success, val7, next := r7.Status()
if !success {
return Fail[In, Out](anyConsumed, r7.Message()), nil
}
return Succeed(anyConsumed, val7, next, MessageOK(s.Pos())), nil
}
}
// Bind7 is equivalent to 7 nested calls to Bind.
func Bind7[In, Out, T, T2, T3, T4, T5, T6, T7 any](
p Parser[In, T],
f func(T) Parser[In, T2],
f2 func(T2) Parser[In, T3],
f3 func(T3) Parser[In, T4],
f4 func(T4) Parser[In, T5],
f5 func(T5) Parser[In, T6],
f6 func(T6) Parser[In, T7],
f7 func(T7) Parser[In, Out],
) Parser[In, Out] {
return func(s State[In]) (Result[In, Out], error) {
var anyConsumed bool
var next = s
r, err := p(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r.Consumed()
success, val, next := r.Status()
if !success {
return Fail[In, Out](anyConsumed, r.Message()), nil
}
r2, err := f(val)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r2.Consumed()
success, val2, next := r2.Status()
if !success {
return Fail[In, Out](anyConsumed, r2.Message()), nil
}
r3, err := f2(val2)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r3.Consumed()
success, val3, next := r3.Status()
if !success {
return Fail[In, Out](anyConsumed, r3.Message()), nil
}
r4, err := f3(val3)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r4.Consumed()
success, val4, next := r4.Status()
if !success {
return Fail[In, Out](anyConsumed, r4.Message()), nil
}
r5, err := f4(val4)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r5.Consumed()
success, val5, next := r5.Status()
if !success {
return Fail[In, Out](anyConsumed, r5.Message()), nil
}
r6, err := f5(val5)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r6.Consumed()
success, val6, next := r6.Status()
if !success {
return Fail[In, Out](anyConsumed, r6.Message()), nil
}
r7, err := f6(val6)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r7.Consumed()
success, val7, next := r7.Status()
if !success {
return Fail[In, Out](anyConsumed, r7.Message()), nil
}
r8, err := f7(val7)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r8.Consumed()
success, val8, next := r8.Status()
if !success {
return Fail[In, Out](anyConsumed, r8.Message()), nil
}
return Succeed(anyConsumed, val8, next, MessageOK(s.Pos())), nil
}
}
// Bind8 is equivalent to 8 nested calls to Bind.
func Bind8[In, Out, T, T2, T3, T4, T5, T6, T7, T8 any](
p Parser[In, T],
f func(T) Parser[In, T2],
f2 func(T2) Parser[In, T3],
f3 func(T3) Parser[In, T4],
f4 func(T4) Parser[In, T5],
f5 func(T5) Parser[In, T6],
f6 func(T6) Parser[In, T7],
f7 func(T7) Parser[In, T8],
f8 func(T8) Parser[In, Out],
) Parser[In, Out] {
return func(s State[In]) (Result[In, Out], error) {
var anyConsumed bool
var next = s
r, err := p(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r.Consumed()
success, val, next := r.Status()
if !success {
return Fail[In, Out](anyConsumed, r.Message()), nil
}
r2, err := f(val)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r2.Consumed()
success, val2, next := r2.Status()
if !success {
return Fail[In, Out](anyConsumed, r2.Message()), nil
}
r3, err := f2(val2)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r3.Consumed()
success, val3, next := r3.Status()
if !success {
return Fail[In, Out](anyConsumed, r3.Message()), nil
}
r4, err := f3(val3)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r4.Consumed()
success, val4, next := r4.Status()
if !success {
return Fail[In, Out](anyConsumed, r4.Message()), nil
}
r5, err := f4(val4)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r5.Consumed()
success, val5, next := r5.Status()
if !success {
return Fail[In, Out](anyConsumed, r5.Message()), nil
}
r6, err := f5(val5)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r6.Consumed()
success, val6, next := r6.Status()
if !success {
return Fail[In, Out](anyConsumed, r6.Message()), nil
}
r7, err := f6(val6)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r7.Consumed()
success, val7, next := r7.Status()
if !success {
return Fail[In, Out](anyConsumed, r7.Message()), nil
}
r8, err := f7(val7)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r8.Consumed()
success, val8, next := r8.Status()
if !success {
return Fail[In, Out](anyConsumed, r8.Message()), nil
}
r9, err := f8(val8)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r9.Consumed()
success, val9, next := r9.Status()
if !success {
return Fail[In, Out](anyConsumed, r9.Message()), nil
}
return Succeed(anyConsumed, val9, next, MessageOK(s.Pos())), nil
}
}
// Bind9 is equivalent to 9 nested calls to Bind.
func Bind9[In, Out, T, T2, T3, T4, T5, T6, T7, T8, T9 any](
p Parser[In, T],
f func(T) Parser[In, T2],
f2 func(T2) Parser[In, T3],
f3 func(T3) Parser[In, T4],
f4 func(T4) Parser[In, T5],
f5 func(T5) Parser[In, T6],
f6 func(T6) Parser[In, T7],
f7 func(T7) Parser[In, T8],
f8 func(T8) Parser[In, T9],
f9 func(T9) Parser[In, Out],
) Parser[In, Out] {
return func(s State[In]) (Result[In, Out], error) {
var anyConsumed bool
var next = s
r, err := p(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r.Consumed()
success, val, next := r.Status()
if !success {
return Fail[In, Out](anyConsumed, r.Message()), nil
}
r2, err := f(val)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r2.Consumed()
success, val2, next := r2.Status()
if !success {
return Fail[In, Out](anyConsumed, r2.Message()), nil
}
r3, err := f2(val2)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r3.Consumed()
success, val3, next := r3.Status()
if !success {
return Fail[In, Out](anyConsumed, r3.Message()), nil
}
r4, err := f3(val3)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r4.Consumed()
success, val4, next := r4.Status()
if !success {
return Fail[In, Out](anyConsumed, r4.Message()), nil
}
r5, err := f4(val4)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r5.Consumed()
success, val5, next := r5.Status()
if !success {
return Fail[In, Out](anyConsumed, r5.Message()), nil
}
r6, err := f5(val5)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r6.Consumed()
success, val6, next := r6.Status()
if !success {
return Fail[In, Out](anyConsumed, r6.Message()), nil
}
r7, err := f6(val6)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r7.Consumed()
success, val7, next := r7.Status()
if !success {
return Fail[In, Out](anyConsumed, r7.Message()), nil
}
r8, err := f7(val7)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r8.Consumed()
success, val8, next := r8.Status()
if !success {
return Fail[In, Out](anyConsumed, r8.Message()), nil
}
r9, err := f8(val8)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r9.Consumed()
success, val9, next := r9.Status()
if !success {
return Fail[In, Out](anyConsumed, r9.Message()), nil
}
r10, err := f9(val9)(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r10.Consumed()
success, val10, next := r10.Status()
if !success {
return Fail[In, Out](anyConsumed, r10.Message()), nil
}
return Succeed(anyConsumed, val10, next, MessageOK(s.Pos())), nil
}
}

16
bytes/bytes.go Normal file
View File

@ -0,0 +1,16 @@
package bytes
import (
"git.codemonkeysoftware.net/b/gigaparsec"
)
func Token[Out, WSOut any](whitespace gigaparsec.Parser[byte, WSOut]) func(p gigaparsec.Parser[byte, Out]) gigaparsec.Parser[byte, Out] {
mappedWS := gigaparsec.Map(whitespace, func(WSOut) struct{} { return struct{}{} })
var ignoreWS gigaparsec.Parser[byte, struct{}] = func(s gigaparsec.State[byte]) (gigaparsec.Result[byte, struct{}], error) {
result, err := mappedWS(s)
return result.Consume(false), err
}
return func(p gigaparsec.Parser[byte, Out]) gigaparsec.Parser[byte, Out] {
return gigaparsec.Seq2(p, gigaparsec.Repeat(0, ignoreWS), func(val Out, _ []struct{}) Out { return val })
}
}

View File

@ -69,6 +69,9 @@ func Regexp(pattern string) gigaparsec.Parser[byte, string] {
return gigaparsec.Result[byte, string]{}, fmt.Errorf("Regexp: reader error: %w", err)
}
if idx == nil {
if err == io.EOF {
return gigaparsec.Fail[byte, string](false, gigaparsec.MessageEnd(input.Pos())), nil
}
got := make([]byte, r.Count())
_, _, err = input.Read(got)
if err != nil {
@ -80,7 +83,7 @@ func Regexp(pattern string) gigaparsec.Parser[byte, string] {
// when searching a RuneReader.
dst := make([]byte, idx[1]-idx[0])
n, _, err := input.Read(dst)
if err != nil {
if err != nil && (!errors.Is(err, io.EOF) || n < uint64(len(dst))) {
return gigaparsec.Result[byte, string]{}, fmt.Errorf("Regexp: unexpected error: %w", err)
}
next := input.At(input.Pos() + n)

View File

@ -4,6 +4,7 @@ package bytes_test
import (
"bytes"
"errors"
"strings"
"testing"
@ -16,24 +17,64 @@ import (
"pgregory.net/rapid"
)
func Todo(t *testing.T) {
t.Fatalf("TODO")
}
func TestRegexp(t *testing.T) {
t.Run("only searches the beginning of input", Todo)
t.Run("position is correct after match", Todo)
t.Run("fails on unexpected error", Todo)
t.Run("returns a useful Got value", rapid.MakeCheck(func(t *rapid.T) {
alpha := rapid.SliceOfBytesMatching(`[A-Za-z]{1,100}`)
t.Run("position and value are correct after match", rapid.MakeCheck(func(t *rapid.T) {
needle := alpha.Draw(t, "needle")
input := rapid.Map(alpha, func(suffix []byte) []byte { return append(needle, suffix...) }).
Draw(t, "input")
p := pbytes.Regexp(string(needle))
result, err := p(gigaparsec.MakeState(bytes.NewReader(input)))
succeeded, val, next := result.Status()
}))
t.Run("basically works", func(t *testing.T) {
result, err := pbytes.Regexp("a")(gigaparsec.MakeState(strings.NewReader("a")))
must.NoError(t, err)
success, value, _ := result.Status()
test.True(t, success, test.Sprint(result.Message()))
test.EqOp(t, "a", value)
test.True(t, result.Consumed())
test.True(t, succeeded)
test.EqOp(t, string(needle), val)
ptest.StateIsAt(t, next, uint64(len(needle)))
}))
t.Run("only searches the beginning of input", rapid.MakeCheck(func(t *rapid.T) {
needle := alpha.Draw(t, "needle")
input := rapid.Map(alpha, func(prefix []byte) []byte { return append(prefix, needle...) }).
Filter(func(b []byte) bool { return !bytes.HasPrefix(b, needle) }).
Draw(t, "input")
p := pbytes.Regexp(string(needle))
result, err := p(gigaparsec.MakeState(bytes.NewReader(input)))
succeeded, _, _ := result.Status()
must.NoError(t, err)
test.False(t, succeeded)
}))
t.Run("fails on unexpected error", func(t *testing.T) {
expectedErr := errors.New("it broke")
p := pbytes.Regexp("nope")
result, err := p(gigaparsec.MakeState(ptest.ErrReaderAt(expectedErr)))
succeeded, _, _ := result.Status()
test.ErrorIs(t, err, expectedErr)
test.False(t, succeeded)
})
t.Run("returns a useful Got value", func(t *testing.T) {
p := pbytes.Regexp("hello")
result, err := p(gigaparsec.MakeState(strings.NewReader("hellaparsec")))
must.NoError(t, err)
test.StrContains(t, result.Message().Got(), "hella")
})
t.Run("succeeds on empty matches", func(t *testing.T) {
p := pbytes.Regexp(".*")
result, err := p(gigaparsec.MakeState(strings.NewReader("")))
succeeded, value, _ := result.Status()
must.NoError(t, err)
must.True(t, succeeded)
must.EqOp(t, "", value)
})
t.Run("fails without an error at EOF", func(t *testing.T) {
p := pbytes.Regexp("a")
result, err := p(gigaparsec.MakeState(strings.NewReader("")))
succeeded, _, _ := result.Status()
must.NoError(t, err)
must.False(t, succeeded)
})
}

View File

@ -2,7 +2,7 @@
package gigaparsec
//go:generate go run ./internal/bindgen -bindpath bind.go -seqpath seq.go -max 5 -pkg gigaparsec
//go:generate go run ./internal/bindgen -bindpath bind.go -seqpath seq.go -max 9 -pkg gigaparsec
import (
"errors"
"fmt"
@ -194,6 +194,17 @@ func (p Parser[In, Out]) Label(label string) Parser[In, Out] {
}
}
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
}
}
type ParseError Message
func (pe ParseError) Error() string {
@ -379,26 +390,40 @@ func Pipe[In, Ignore, Through any](p Parser[In, Ignore]) func(Through) Parser[In
// 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(s State[In]) (Result[In, []Out], error) {
return func(state State[In]) (Result[In, []Out], error) {
var values []Out
var consumed bool
next := s
currState := state
for {
result, err := p(next)
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, next = result.Status()
success, value, nextState := result.Status()
if !success {
if len(values) >= minCount {
return Succeed(consumed, values, next, MessageOK(s.Pos())), nil
return Succeed(consumed, values, currState, MessageOK(state.Pos())), 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)
}
}
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 })
}

90
internal/check/check.go Normal file
View File

@ -0,0 +1,90 @@
// SPDX-License-Identifier: Unlicense
package main
import (
"bufio"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"regexp"
"strings"
)
var goSpdxHeader = `// SPDX-License-Identifier: Unlicense`
var tmplSpdxHeader = `{{/* SPDX-License-Identifier: Unlicense */`
type MissingSPDXError struct {
Name string
}
func (m MissingSPDXError) Error() string {
return fmt.Sprintf("missing or incorrect SPDX header: %s", m.Name)
}
func checkFileSPDX(header string, name string) error {
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close()
r := bufio.NewReader(f)
pattern := `(?m:^` + regexp.QuoteMeta(header) + `)`
matched, err := regexp.MatchReader(pattern, r)
if err != nil {
return err
}
if !matched {
return MissingSPDXError{Name: name}
}
return nil
}
func walkSPDX(header string, extension string) error {
var errs []error
filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
if path != "." && strings.HasPrefix(path, ".") {
if d.IsDir() {
return filepath.SkipDir
}
return nil
}
if err != nil {
errs = append(errs, err)
return nil
}
if filepath.Ext(path) != extension {
return nil
}
errs = append(errs, checkFileSPDX(header, path))
return nil
})
return errors.Join(errs...)
}
func checkSPDX() error {
err := errors.Join(
walkSPDX(goSpdxHeader, ".go"),
walkSPDX(tmplSpdxHeader, ".tmpl"),
)
if err != nil {
return fmt.Errorf("Check SPDX Headers:\n%w", err)
}
return nil
}
func run() error {
return checkSPDX()
}
func main() {
err := run()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

265
naive/naive.go Normal file
View File

@ -0,0 +1,265 @@
// SPDX-License-Identifier: Unlicense
// Package naive contains naïve implementations of the Bind and Seq combinators.
// The accompanying tests include simple benchmarks comparing their performance.
package naive
import gp "git.codemonkeysoftware.net/b/gigaparsec"
func Bind2[In, Out, T, T2 any](
p gp.Parser[In, T],
f func(T) gp.Parser[In, T2],
f2 func(T2) gp.Parser[In, Out],
) gp.Parser[In, Out] {
return gp.Bind(gp.Bind(p, f), f2)
}
func Bind3[In, Out, T, T2, T3, T4, T5, T6, T7, T8, T9 any](
p gp.Parser[In, T],
f func(T) gp.Parser[In, T2],
f2 func(T2) gp.Parser[In, T3],
f3 func(T3) gp.Parser[In, Out],
) gp.Parser[In, Out] {
return gp.Bind(gp.Bind(gp.Bind(p, f), f2), f3)
}
func Bind4[In, Out, T, T2, T3, T4 any](
p gp.Parser[In, T],
f func(T) gp.Parser[In, T2],
f2 func(T2) gp.Parser[In, T3],
f3 func(T3) gp.Parser[In, T4],
f4 func(T4) gp.Parser[In, Out],
) gp.Parser[In, Out] {
return gp.Bind(gp.Bind(gp.Bind(gp.Bind(p, f), f2), f3), f4)
}
func Bind5[In, Out, T, T2, T3, T4, T5 any](
p gp.Parser[In, T],
f func(T) gp.Parser[In, T2],
f2 func(T2) gp.Parser[In, T3],
f3 func(T3) gp.Parser[In, T4],
f4 func(T4) gp.Parser[In, T5],
f5 func(T5) gp.Parser[In, Out],
) gp.Parser[In, Out] {
return gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(p, f), f2), f3), f4), f5)
}
func Bind6[In, Out, T, T2, T3, T4, T5, T6 any](
p gp.Parser[In, T],
f func(T) gp.Parser[In, T2],
f2 func(T2) gp.Parser[In, T3],
f3 func(T3) gp.Parser[In, T4],
f4 func(T4) gp.Parser[In, T5],
f5 func(T5) gp.Parser[In, T6],
f6 func(T6) gp.Parser[In, Out],
) gp.Parser[In, Out] {
return gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(p, f), f2), f3), f4), f5), f6)
}
func Bind7[In, Out, T, T2, T3, T4, T5, T6, T7 any](
p gp.Parser[In, T],
f func(T) gp.Parser[In, T2],
f2 func(T2) gp.Parser[In, T3],
f3 func(T3) gp.Parser[In, T4],
f4 func(T4) gp.Parser[In, T5],
f5 func(T5) gp.Parser[In, T6],
f6 func(T6) gp.Parser[In, T7],
f7 func(T7) gp.Parser[In, Out],
) gp.Parser[In, Out] {
return gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(p, f), f2), f3), f4), f5), f6), f7)
}
func Bind8[In, Out, T, T2, T3, T4, T5, T6, T7, T8 any](
p gp.Parser[In, T],
f func(T) gp.Parser[In, T2],
f2 func(T2) gp.Parser[In, T3],
f3 func(T3) gp.Parser[In, T4],
f4 func(T4) gp.Parser[In, T5],
f5 func(T5) gp.Parser[In, T6],
f6 func(T6) gp.Parser[In, T7],
f7 func(T7) gp.Parser[In, T8],
f8 func(T8) gp.Parser[In, Out],
) gp.Parser[In, Out] {
return gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(p, f), f2), f3), f4), f5), f6), f7), f8)
}
func Bind9[In, Out, T, T2, T3, T4, T5, T6, T7, T8, T9 any](
p gp.Parser[In, T],
f func(T) gp.Parser[In, T2],
f2 func(T2) gp.Parser[In, T3],
f3 func(T3) gp.Parser[In, T4],
f4 func(T4) gp.Parser[In, T5],
f5 func(T5) gp.Parser[In, T6],
f6 func(T6) gp.Parser[In, T7],
f7 func(T7) gp.Parser[In, T8],
f8 func(T8) gp.Parser[In, T9],
f9 func(T9) gp.Parser[In, Out],
) gp.Parser[In, Out] {
return gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(gp.Bind(p, f), f2), f3), f4), f5), f6), f7), f8), f9)
}
func Seq2[In, Out, T, T2 any](
p gp.Parser[In, T],
p2 gp.Parser[In, T2],
f func(T, T2) Out,
) gp.Parser[In, Out] {
return gp.Bind(p, func(x T) gp.Parser[In, Out] {
return gp.Bind(p2, func(x2 T2) gp.Parser[In, Out] {
return gp.Return[In](f(x, x2))
})
})
}
func Seq3[In, Out, T, T2, T3 any](
p gp.Parser[In, T],
p2 gp.Parser[In, T2],
p3 gp.Parser[In, T3],
f func(T, T2, T3) Out,
) gp.Parser[In, Out] {
return gp.Bind(p, func(x T) gp.Parser[In, Out] {
return gp.Bind(p2, func(x2 T2) gp.Parser[In, Out] {
return gp.Bind(p3, func(x3 T3) gp.Parser[In, Out] {
return gp.Return[In](f(x, x2, x3))
})
})
})
}
func Seq4[In, Out, T, T2, T3, T4 any](
p gp.Parser[In, T],
p2 gp.Parser[In, T2],
p3 gp.Parser[In, T3],
p4 gp.Parser[In, T4],
f func(T, T2, T3, T4) Out,
) gp.Parser[In, Out] {
return gp.Bind(p, func(x T) gp.Parser[In, Out] {
return gp.Bind(p2, func(x2 T2) gp.Parser[In, Out] {
return gp.Bind(p3, func(x3 T3) gp.Parser[In, Out] {
return gp.Bind(p4, func(x4 T4) gp.Parser[In, Out] {
return gp.Return[In](f(x, x2, x3, x4))
})
})
})
})
}
func Seq5[In, Out, T, T2, T3, T4, T5 any](
p gp.Parser[In, T],
p2 gp.Parser[In, T2],
p3 gp.Parser[In, T3],
p4 gp.Parser[In, T4],
p5 gp.Parser[In, T5],
f func(T, T2, T3, T4, T5) Out,
) gp.Parser[In, Out] {
return gp.Bind(p, func(x T) gp.Parser[In, Out] {
return gp.Bind(p2, func(x2 T2) gp.Parser[In, Out] {
return gp.Bind(p3, func(x3 T3) gp.Parser[In, Out] {
return gp.Bind(p4, func(x4 T4) gp.Parser[In, Out] {
return gp.Bind(p5, func(x5 T5) gp.Parser[In, Out] {
return gp.Return[In](f(x, x2, x3, x4, x5))
})
})
})
})
})
}
func Seq6[In, Out, T, T2, T3, T4, T5, T6 any](
p gp.Parser[In, T],
p2 gp.Parser[In, T2],
p3 gp.Parser[In, T3],
p4 gp.Parser[In, T4],
p5 gp.Parser[In, T5],
p6 gp.Parser[In, T6],
f func(T, T2, T3, T4, T5, T6) Out,
) gp.Parser[In, Out] {
return gp.Bind(p, func(x T) gp.Parser[In, Out] {
return gp.Bind(p2, func(x2 T2) gp.Parser[In, Out] {
return gp.Bind(p3, func(x3 T3) gp.Parser[In, Out] {
return gp.Bind(p4, func(x4 T4) gp.Parser[In, Out] {
return gp.Bind(p5, func(x5 T5) gp.Parser[In, Out] {
return gp.Bind(p6, func(x6 T6) gp.Parser[In, Out] {
return gp.Return[In](f(x, x2, x3, x4, x5, x6))
})
})
})
})
})
})
}
func Seq7[In, Out, T, T2, T3, T4, T5, T6, T7 any](
p gp.Parser[In, T],
p2 gp.Parser[In, T2],
p3 gp.Parser[In, T3],
p4 gp.Parser[In, T4],
p5 gp.Parser[In, T5],
p6 gp.Parser[In, T6],
p7 gp.Parser[In, T7],
f func(T, T2, T3, T4, T5, T6, T7) Out,
) gp.Parser[In, Out] {
return gp.Bind(p, func(x T) gp.Parser[In, Out] {
return gp.Bind(p2, func(x2 T2) gp.Parser[In, Out] {
return gp.Bind(p3, func(x3 T3) gp.Parser[In, Out] {
return gp.Bind(p4, func(x4 T4) gp.Parser[In, Out] {
return gp.Bind(p5, func(x5 T5) gp.Parser[In, Out] {
return gp.Bind(p6, func(x6 T6) gp.Parser[In, Out] {
return gp.Bind(p7, func(x7 T7) gp.Parser[In, Out] {
return gp.Return[In](f(x, x2, x3, x4, x5, x6, x7))
})
})
})
})
})
})
})
}
func Seq8[In, Out, T, T2, T3, T4, T5, T6, T7, T8 any](
p gp.Parser[In, T],
p2 gp.Parser[In, T2],
p3 gp.Parser[In, T3],
p4 gp.Parser[In, T4],
p5 gp.Parser[In, T5],
p6 gp.Parser[In, T6],
p7 gp.Parser[In, T7],
p8 gp.Parser[In, T8],
f func(T, T2, T3, T4, T5, T6, T7, T8) Out,
) gp.Parser[In, Out] {
return gp.Bind(p, func(x T) gp.Parser[In, Out] {
return gp.Bind(p2, func(x2 T2) gp.Parser[In, Out] {
return gp.Bind(p3, func(x3 T3) gp.Parser[In, Out] {
return gp.Bind(p4, func(x4 T4) gp.Parser[In, Out] {
return gp.Bind(p5, func(x5 T5) gp.Parser[In, Out] {
return gp.Bind(p6, func(x6 T6) gp.Parser[In, Out] {
return gp.Bind(p7, func(x7 T7) gp.Parser[In, Out] {
return gp.Bind(p8, func(x8 T8) gp.Parser[In, Out] {
return gp.Return[In](f(x, x2, x3, x4, x5, x6, x7, x8))
})
})
})
})
})
})
})
})
}
func Seq9[In, Out, T, T2, T3, T4, T5, T6, T7, T8, T9 any](
p gp.Parser[In, T],
p2 gp.Parser[In, T2],
p3 gp.Parser[In, T3],
p4 gp.Parser[In, T4],
p5 gp.Parser[In, T5],
p6 gp.Parser[In, T6],
p7 gp.Parser[In, T7],
p8 gp.Parser[In, T8],
p9 gp.Parser[In, T9],
f func(T, T2, T3, T4, T5, T6, T7, T8, T9) Out,
) gp.Parser[In, Out] {
return gp.Bind(p, func(x T) gp.Parser[In, Out] {
return gp.Bind(p2, func(x2 T2) gp.Parser[In, Out] {
return gp.Bind(p3, func(x3 T3) gp.Parser[In, Out] {
return gp.Bind(p4, func(x4 T4) gp.Parser[In, Out] {
return gp.Bind(p5, func(x5 T5) gp.Parser[In, Out] {
return gp.Bind(p6, func(x6 T6) gp.Parser[In, Out] {
return gp.Bind(p7, func(x7 T7) gp.Parser[In, Out] {
return gp.Bind(p8, func(x8 T8) gp.Parser[In, Out] {
return gp.Bind(p9, func(x9 T9) gp.Parser[In, Out] {
return gp.Return[In](f(x, x2, x3, x4, x5, x6, x7, x8, x9))
})
})
})
})
})
})
})
})
})
}

108
naive/naive_test.go Normal file
View File

@ -0,0 +1,108 @@
// SPDX-License-Identifier: Unlicense
package naive_test
import (
"testing"
"git.codemonkeysoftware.net/b/gigaparsec"
"git.codemonkeysoftware.net/b/gigaparsec/naive"
)
func BenchmarkBind5(b *testing.B) {
type Bind5T = func(p gigaparsec.Parser[byte, byte], f func(byte) gigaparsec.Parser[byte, byte], f2 func(byte) gigaparsec.Parser[byte, byte], f3 func(byte) gigaparsec.Parser[byte, byte], f4 func(byte) gigaparsec.Parser[byte, byte], f5 func(byte) gigaparsec.Parser[byte, byte]) gigaparsec.Parser[byte, byte]
f := func(b byte) gigaparsec.Parser[byte, byte] {
return gigaparsec.Return[byte](b + 1)
}
p := func(bind5 Bind5T) gigaparsec.Parser[byte, byte] {
// gigaparsec.Bind5()
return bind5(gigaparsec.Match(byte(0)), f, f, f, f, f)
}
input := gigaparsec.SliceReaderAt[byte]{0}
b.Run("gigaparsec.Bind5", func(b *testing.B) {
for range b.N {
gigaparsec.Run(p(gigaparsec.Bind5), input)
}
})
b.Run("naïve.Bind5", func(b *testing.B) {
for range b.N {
gigaparsec.Run(p(naive.Bind5), input)
}
})
}
func BenchmarkBind9(b *testing.B) {
type Bind9T = func(p gigaparsec.Parser[byte, byte], f func(byte) gigaparsec.Parser[byte, byte], f2 func(byte) gigaparsec.Parser[byte, byte], f3 func(byte) gigaparsec.Parser[byte, byte], f4 func(byte) gigaparsec.Parser[byte, byte], f5 func(byte) gigaparsec.Parser[byte, byte], f6 func(byte) gigaparsec.Parser[byte, byte], f7 func(byte) gigaparsec.Parser[byte, byte], f8 func(byte) gigaparsec.Parser[byte, byte], f9 func(byte) gigaparsec.Parser[byte, byte]) gigaparsec.Parser[byte, byte]
f := func(b byte) gigaparsec.Parser[byte, byte] {
return gigaparsec.Return[byte](b + 1)
}
p := func(bind5 Bind9T) gigaparsec.Parser[byte, byte] {
// gigaparsec.Bind5()
return bind5(gigaparsec.Match(byte(0)), f, f, f, f, f, f, f, f, f)
}
input := gigaparsec.SliceReaderAt[byte]{0}
b.Run("gigaparsec.Bind9", func(b *testing.B) {
for range b.N {
gigaparsec.Run(p(gigaparsec.Bind9), input)
}
})
b.Run("naive.Bind9", func(b *testing.B) {
for range b.N {
gigaparsec.Run(p(naive.Bind9), input)
}
})
}
func BenchmarkSeq5(b *testing.B) {
type P = gigaparsec.Parser[byte, byte]
type Seq5T = func(P, P, P, P, P, func(byte, byte, byte, byte, byte) byte) P
zero := gigaparsec.Return[byte, byte](0)
f := func(a, b, c, d, e byte) byte {
return a + b + c + d + e
}
p := func(seq5 Seq5T) P {
return seq5(zero, zero, zero, zero, zero, f)
}
input := gigaparsec.SliceReaderAt[byte]{0}
b.Run("gigaparsec.Seq5", func(b *testing.B) {
for range b.N {
gigaparsec.Run(p(gigaparsec.Seq5), input)
}
})
b.Run("naive.Seq5", func(b *testing.B) {
for range b.N {
gigaparsec.Run(p(naive.Seq5), input)
}
})
}
func BenchmarkSeq9(b *testing.B) {
type P = gigaparsec.Parser[byte, byte]
type Seq9T = func(P, P, P, P, P, P, P, P, P, func(byte, byte, byte, byte, byte, byte, byte, byte, byte) byte) P
zero := gigaparsec.Return[byte, byte](0)
f := func(a, b, c, d, e, f, g, h, i byte) byte {
return a + b + c + d + e + f + g + h + i
}
p := func(seq9 Seq9T) P {
return seq9(zero, zero, zero, zero, zero, zero, zero, zero, zero, f)
}
input := gigaparsec.SliceReaderAt[byte]{0}
b.Run("gigaparsec.Seq9", func(b *testing.B) {
for range b.N {
gigaparsec.Run(p(gigaparsec.Seq9), input)
}
})
b.Run("naive.Seq9", func(b *testing.B) {
for range b.N {
gigaparsec.Run(p(naive.Seq9), input)
}
})
}

View File

@ -4,6 +4,8 @@ package gigaparsec_test
import (
"bytes"
"errors"
"fmt"
"testing"
"git.codemonkeysoftware.net/b/gigaparsec"
@ -125,8 +127,32 @@ func TestSatisfy(t *testing.T) {
Todo(t)
}
func Try(t *testing.T) {
Todo(t)
func TestTry(t *testing.T) {
type R = ptest.ForcedResult
var cases = []struct{ P, TryP R }{
{P: R{Succeed: false, Consume: false}, TryP: R{Succeed: false, Consume: false}},
{P: R{Succeed: false, Consume: true}, TryP: R{Succeed: false, Consume: false}},
{P: R{Succeed: true, Consume: false}, TryP: R{Succeed: true, Consume: false}},
{P: R{Succeed: true, Consume: true}, TryP: R{Succeed: true, Consume: true}},
}
for _, c := range cases {
t.Run(fmt.Sprintf("%+v", c.P), func(t *testing.T) {
start := gigaparsec.MakeState(gigaparsec.SliceReaderAt[R]{c.P})
result, err := gigaparsec.Try(ptest.ForceResult)(start)
succeeded, _, _ := result.Status()
must.NoError(t, err)
test.EqOp(t, c.TryP.Succeed, succeeded)
test.EqOp(t, c.TryP.Consume, result.Consumed())
})
}
t.Run("fails on error", func(t *testing.T) {
expectedErr := errors.New("it broke")
p := gigaparsec.Try(gigaparsec.Match(byte(0)))
result, err := p(gigaparsec.MakeState(ptest.ErrReaderAt(expectedErr)))
succeeded, _, _ := result.Status()
test.ErrorIs(t, err, expectedErr)
test.False(t, succeeded)
})
}
func TestLabel(t *testing.T) {
@ -138,24 +164,57 @@ func TestEnd(t *testing.T) {
}
func TestRepeat(t *testing.T) {
t.Run("succeeds iff number of successes ≥ minCount", rapid.MakeCheck(func(t *rapid.T) {
const good byte = 'o'
const bad byte = 'x'
const maxParses = 100
t.Run("succeeds iff number of successes ≥ minCount", rapid.MakeCheck(func(t *rapid.T) {
minCount := rapid.IntRange(0, maxParses).Draw(t, "minCount")
successes := rapid.IntRange(0, maxParses).Draw(t, "successes")
shouldSucceed := successes >= minCount
input := append(ptest.SliceOfN(good, successes), bad)
p := gigaparsec.Repeat(minCount, gigaparsec.Match(good))
result, err := p(gigaparsec.MakeState(bytes.NewReader(input)))
input := append(ptest.SliceOfN(true, successes), false)
p := gigaparsec.Repeat(minCount, gigaparsec.Match(true))
result, err := p(gigaparsec.MakeState(gigaparsec.SliceReaderAt[bool](input)))
must.NoError(t, err)
success, _, _ := result.Status()
test.EqOp(t, successes >= minCount, success, test.Sprint("expected successes ≥ minCount"))
}))
t.Run("consumes iff at least one application consumes", Todo)
t.Run("fails on error", Todo)
t.Run("position is unchanged on failure", Todo)
t.Run("position follows last success on overall success", Todo)
success, _, next := result.Status()
test.EqOp(t, shouldSucceed, success)
if success {
test.EqOp(t, uint64(successes), next.Pos())
}
}))
t.Run("consumes iff at least one application consumes", rapid.MakeCheck(func(t *rapid.T) {
input := rapid.Map(rapid.SliceOfN(rapid.Just(ptest.ForcedResult{Succeed: true}), 0, 100),
func(ts []ptest.ForcedResult) []ptest.ForcedResult { return append(ts, ptest.ForcedResult{}) }).Draw(t, "input")
consumeAt := rapid.Ptr(rapid.IntRange(0, len(input)-1), true).Draw(t, "consumeAt")
if consumeAt != nil {
input[*consumeAt].Consume = true
}
shouldConsume := consumeAt != nil
result, err := gigaparsec.Repeat(0, ptest.ForceResult)(gigaparsec.MakeState(gigaparsec.SliceReaderAt[ptest.ForcedResult](input)))
must.NoError(t, err)
test.EqOp(t, shouldConsume, result.Consumed())
}))
t.Run("does not consume on empty input", func(t *testing.T) {
p := gigaparsec.Repeat(0, gigaparsec.Match(0))
result, err := p(gigaparsec.MakeState(gigaparsec.SliceReaderAt[int](nil)))
must.NoError(t, err)
must.False(t, result.Consumed())
})
t.Run("fails on error", func(t *testing.T) {
expectedErr := errors.New("it broke")
p := gigaparsec.Repeat(0, gigaparsec.Match(byte(0)))
result, err := p(gigaparsec.MakeState(ptest.ErrReaderAt(expectedErr)))
succeeded, _, _ := result.Status()
test.ErrorIs(t, err, expectedErr)
test.False(t, succeeded)
})
}
func TestBracket(t *testing.T) {
Todo(t)
}
func TestWhere(t *testing.T) {
Todo(t)
}

378
seq.go
View File

@ -205,3 +205,381 @@ func Seq5[In, Out, T, T2, T3, T4, T5 any](
return Succeed(anyConsumed, final, next, MessageOK(s.Pos())), nil
}
}
func Seq6[In, Out, T, T2, T3, T4, T5, T6 any](
p Parser[In, T],
p2 Parser[In, T2],
p3 Parser[In, T3],
p4 Parser[In, T4],
p5 Parser[In, T5],
p6 Parser[In, T6],
f func(T, T2, T3, T4, T5, T6) Out,
) Parser[In, Out] {
return func(s State[In]) (Result[In, Out], error) {
var anyConsumed bool
var next = s
r, err := p(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r.Consumed()
success, val, next := r.Status()
if !success {
return Fail[In, Out](anyConsumed, r.Message()), nil
}
r2, err := p2(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r2.Consumed()
success, val2, next := r2.Status()
if !success {
return Fail[In, Out](anyConsumed, r2.Message()), nil
}
r3, err := p3(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r3.Consumed()
success, val3, next := r3.Status()
if !success {
return Fail[In, Out](anyConsumed, r3.Message()), nil
}
r4, err := p4(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r4.Consumed()
success, val4, next := r4.Status()
if !success {
return Fail[In, Out](anyConsumed, r4.Message()), nil
}
r5, err := p5(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r5.Consumed()
success, val5, next := r5.Status()
if !success {
return Fail[In, Out](anyConsumed, r5.Message()), nil
}
r6, err := p6(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r6.Consumed()
success, val6, next := r6.Status()
if !success {
return Fail[In, Out](anyConsumed, r6.Message()), nil
}
final := f(val, val2, val3, val4, val5, val6)
return Succeed(anyConsumed, final, next, MessageOK(s.Pos())), nil
}
}
func Seq7[In, Out, T, T2, T3, T4, T5, T6, T7 any](
p Parser[In, T],
p2 Parser[In, T2],
p3 Parser[In, T3],
p4 Parser[In, T4],
p5 Parser[In, T5],
p6 Parser[In, T6],
p7 Parser[In, T7],
f func(T, T2, T3, T4, T5, T6, T7) Out,
) Parser[In, Out] {
return func(s State[In]) (Result[In, Out], error) {
var anyConsumed bool
var next = s
r, err := p(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r.Consumed()
success, val, next := r.Status()
if !success {
return Fail[In, Out](anyConsumed, r.Message()), nil
}
r2, err := p2(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r2.Consumed()
success, val2, next := r2.Status()
if !success {
return Fail[In, Out](anyConsumed, r2.Message()), nil
}
r3, err := p3(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r3.Consumed()
success, val3, next := r3.Status()
if !success {
return Fail[In, Out](anyConsumed, r3.Message()), nil
}
r4, err := p4(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r4.Consumed()
success, val4, next := r4.Status()
if !success {
return Fail[In, Out](anyConsumed, r4.Message()), nil
}
r5, err := p5(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r5.Consumed()
success, val5, next := r5.Status()
if !success {
return Fail[In, Out](anyConsumed, r5.Message()), nil
}
r6, err := p6(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r6.Consumed()
success, val6, next := r6.Status()
if !success {
return Fail[In, Out](anyConsumed, r6.Message()), nil
}
r7, err := p7(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r7.Consumed()
success, val7, next := r7.Status()
if !success {
return Fail[In, Out](anyConsumed, r7.Message()), nil
}
final := f(val, val2, val3, val4, val5, val6, val7)
return Succeed(anyConsumed, final, next, MessageOK(s.Pos())), nil
}
}
func Seq8[In, Out, T, T2, T3, T4, T5, T6, T7, T8 any](
p Parser[In, T],
p2 Parser[In, T2],
p3 Parser[In, T3],
p4 Parser[In, T4],
p5 Parser[In, T5],
p6 Parser[In, T6],
p7 Parser[In, T7],
p8 Parser[In, T8],
f func(T, T2, T3, T4, T5, T6, T7, T8) Out,
) Parser[In, Out] {
return func(s State[In]) (Result[In, Out], error) {
var anyConsumed bool
var next = s
r, err := p(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r.Consumed()
success, val, next := r.Status()
if !success {
return Fail[In, Out](anyConsumed, r.Message()), nil
}
r2, err := p2(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r2.Consumed()
success, val2, next := r2.Status()
if !success {
return Fail[In, Out](anyConsumed, r2.Message()), nil
}
r3, err := p3(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r3.Consumed()
success, val3, next := r3.Status()
if !success {
return Fail[In, Out](anyConsumed, r3.Message()), nil
}
r4, err := p4(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r4.Consumed()
success, val4, next := r4.Status()
if !success {
return Fail[In, Out](anyConsumed, r4.Message()), nil
}
r5, err := p5(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r5.Consumed()
success, val5, next := r5.Status()
if !success {
return Fail[In, Out](anyConsumed, r5.Message()), nil
}
r6, err := p6(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r6.Consumed()
success, val6, next := r6.Status()
if !success {
return Fail[In, Out](anyConsumed, r6.Message()), nil
}
r7, err := p7(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r7.Consumed()
success, val7, next := r7.Status()
if !success {
return Fail[In, Out](anyConsumed, r7.Message()), nil
}
r8, err := p8(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r8.Consumed()
success, val8, next := r8.Status()
if !success {
return Fail[In, Out](anyConsumed, r8.Message()), nil
}
final := f(val, val2, val3, val4, val5, val6, val7, val8)
return Succeed(anyConsumed, final, next, MessageOK(s.Pos())), nil
}
}
func Seq9[In, Out, T, T2, T3, T4, T5, T6, T7, T8, T9 any](
p Parser[In, T],
p2 Parser[In, T2],
p3 Parser[In, T3],
p4 Parser[In, T4],
p5 Parser[In, T5],
p6 Parser[In, T6],
p7 Parser[In, T7],
p8 Parser[In, T8],
p9 Parser[In, T9],
f func(T, T2, T3, T4, T5, T6, T7, T8, T9) Out,
) Parser[In, Out] {
return func(s State[In]) (Result[In, Out], error) {
var anyConsumed bool
var next = s
r, err := p(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r.Consumed()
success, val, next := r.Status()
if !success {
return Fail[In, Out](anyConsumed, r.Message()), nil
}
r2, err := p2(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r2.Consumed()
success, val2, next := r2.Status()
if !success {
return Fail[In, Out](anyConsumed, r2.Message()), nil
}
r3, err := p3(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r3.Consumed()
success, val3, next := r3.Status()
if !success {
return Fail[In, Out](anyConsumed, r3.Message()), nil
}
r4, err := p4(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r4.Consumed()
success, val4, next := r4.Status()
if !success {
return Fail[In, Out](anyConsumed, r4.Message()), nil
}
r5, err := p5(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r5.Consumed()
success, val5, next := r5.Status()
if !success {
return Fail[In, Out](anyConsumed, r5.Message()), nil
}
r6, err := p6(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r6.Consumed()
success, val6, next := r6.Status()
if !success {
return Fail[In, Out](anyConsumed, r6.Message()), nil
}
r7, err := p7(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r7.Consumed()
success, val7, next := r7.Status()
if !success {
return Fail[In, Out](anyConsumed, r7.Message()), nil
}
r8, err := p8(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r8.Consumed()
success, val8, next := r8.Status()
if !success {
return Fail[In, Out](anyConsumed, r8.Message()), nil
}
r9, err := p9(next)
if err != nil {
return Result[In, Out]{}, err
}
anyConsumed = anyConsumed || r9.Consumed()
success, val9, next := r9.Status()
if !success {
return Fail[In, Out](anyConsumed, r9.Message()), nil
}
final := f(val, val2, val3, val4, val5, val6, val7, val8, val9)
return Succeed(anyConsumed, final, next, MessageOK(s.Pos())), nil
}
}

View File

@ -1,3 +1,5 @@
// SPDX-License-Identifier: Unlicense
package gigaparsec_test
import (

View File

@ -4,6 +4,7 @@
package test
import (
"errors"
"io"
"git.codemonkeysoftware.net/b/gigaparsec"
@ -34,3 +35,22 @@ func SliceOfN[T any](value T, n int) []T {
}
return s
}
type ForcedResult struct{ Succeed, Consume bool }
func ForceResult(state gigaparsec.State[ForcedResult]) (gigaparsec.Result[ForcedResult, struct{}], error) {
buf := make([]ForcedResult, 1)
_, next, err := state.Read(buf)
if errors.Is(err, io.EOF) {
return gigaparsec.Fail[ForcedResult, struct{}](false, gigaparsec.MessageEnd(state.Pos())), nil
}
if err != nil {
return gigaparsec.Result[ForcedResult, struct{}]{}, err
}
tok := buf[0]
if tok.Succeed {
return gigaparsec.Succeed(tok.Consume, struct{}{}, next, gigaparsec.MessageOK(state.Pos())), nil
} else {
return gigaparsec.Fail[ForcedResult, struct{}](tok.Consume, gigaparsec.MakeMessage(state.Pos(), "Succeed=false", "Succeed=true")), nil
}
}