Use a lot less MakeState
Test and check / test-and-check (push) Canceled after 0s

This commit is contained in:
b
2026-08-21 23:08:51 -06:00
parent 17b2a0eee5
commit 9b25034a01
4 changed files with 26 additions and 25 deletions
-2
View File
@@ -1,5 +1,3 @@
Think about changing "consume" to "commit" Think about changing "consume" to "commit"
Should MakeState be private now that there's Run?
I need to be able to test combinators at a lower level than Run.
What's Megaparsec got that we ain't got? What's Megaparsec got that we ain't got?
chainl chainl
+6 -6
View File
@@ -25,7 +25,7 @@ func TestRegexp(t *testing.T) {
Draw(t, "input") Draw(t, "input")
p := pbytes.Regexp(string(needle)) p := pbytes.Regexp(string(needle))
result, err := p(gigaparsec.MakeState(bytes.NewReader(input))) result, err := p.RunToResult(bytes.NewReader(input))
succeeded, val, next := result.Status() succeeded, val, next := result.Status()
must.NoError(t, err) must.NoError(t, err)
@@ -40,7 +40,7 @@ func TestRegexp(t *testing.T) {
Draw(t, "input") Draw(t, "input")
p := pbytes.Regexp(string(needle)) p := pbytes.Regexp(string(needle))
result, err := p(gigaparsec.MakeState(bytes.NewReader(input))) result, err := p.RunToResult(bytes.NewReader(input))
succeeded, _, _ := result.Status() succeeded, _, _ := result.Status()
must.NoError(t, err) must.NoError(t, err)
@@ -49,21 +49,21 @@ func TestRegexp(t *testing.T) {
t.Run("fails on unexpected error", func(t *testing.T) { t.Run("fails on unexpected error", func(t *testing.T) {
expectedErr := errors.New("it broke") expectedErr := errors.New("it broke")
p := pbytes.Regexp("nope") p := pbytes.Regexp("nope")
result, err := p(gigaparsec.MakeState(ptest.ErrReaderAt(expectedErr))) result, err := p.RunToResult(ptest.ErrReaderAt(expectedErr))
succeeded, _, _ := result.Status() succeeded, _, _ := result.Status()
test.ErrorIs(t, err, expectedErr) test.ErrorIs(t, err, expectedErr)
test.False(t, succeeded) test.False(t, succeeded)
}) })
t.Run("returns a useful Got value", func(t *testing.T) { t.Run("returns a useful Got value", func(t *testing.T) {
p := pbytes.Regexp("hello") p := pbytes.Regexp("hello")
result, err := p(gigaparsec.MakeState(strings.NewReader("hellaparsec"))) result, err := p.RunToResult(strings.NewReader("hellaparsec"))
must.NoError(t, err) must.NoError(t, err)
test.StrContains(t, result.Message().Got(), "hella") test.StrContains(t, result.Message().Got(), "hella")
}) })
t.Run("succeeds on empty matches", func(t *testing.T) { t.Run("succeeds on empty matches", func(t *testing.T) {
p := pbytes.Regexp(".*") p := pbytes.Regexp(".*")
result, err := p(gigaparsec.MakeState(strings.NewReader(""))) result, err := p.RunToResult(strings.NewReader(""))
succeeded, value, _ := result.Status() succeeded, value, _ := result.Status()
must.NoError(t, err) must.NoError(t, err)
must.True(t, succeeded) must.True(t, succeeded)
@@ -71,7 +71,7 @@ func TestRegexp(t *testing.T) {
}) })
t.Run("fails without an error at EOF", func(t *testing.T) { t.Run("fails without an error at EOF", func(t *testing.T) {
p := pbytes.Regexp("a") p := pbytes.Regexp("a")
result, err := p(gigaparsec.MakeState(strings.NewReader(""))) result, err := p.RunToResult(strings.NewReader(""))
succeeded, _, _ := result.Status() succeeded, _, _ := result.Status()
must.NoError(t, err) must.NoError(t, err)
must.False(t, succeeded) must.False(t, succeeded)
+10 -4
View File
@@ -133,7 +133,8 @@ func MessageEnd(pos uint64, expected ...string) Message {
return Message{pos: pos, got: "end of input", expected: expected} return Message{pos: pos, got: "end of input", expected: expected}
} }
// MakeState creates a new State // MakeState creates a new State.
// It's mostly useful for testing. You probably don't need it.
func MakeState[In any](r ReaderAt[In]) State[In] { func MakeState[In any](r ReaderAt[In]) State[In] {
return State[In]{r: r} return State[In]{r: r}
} }
@@ -225,10 +226,15 @@ func (p Parser[In, Out]) Map[Out2 any](f func(Out) Out2) Parser[In, Out2] {
}) })
} }
// Run executes a parser on r and returns the result. // RunToResult executes a parser on r and returns the result, or an error if reading the inputs fails.
func (p Parser[In, Out]) Run(r ReaderAt[In]) (out Out, err error) { func (p Parser[In, Out]) RunToResult(r ReaderAt[In]) (Result[In, Out], error) {
start := MakeState(r) start := MakeState(r)
result, err := p(start) 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 { if err != nil {
err = fmt.Errorf("Run: %w", err) err = fmt.Errorf("Run: %w", err)
return return
+10 -13
View File
@@ -31,8 +31,7 @@ func hasPrefix(prefix []byte) func([]byte) bool {
func TestSlice(t *testing.T) { func TestSlice(t *testing.T) {
assertParseFails := func(t rapid.TB, input []byte, p gigaparsec.Parser[byte, []byte]) { assertParseFails := func(t rapid.TB, input []byte, p gigaparsec.Parser[byte, []byte]) {
t.Helper() t.Helper()
start := gigaparsec.MakeState(bytes.NewReader(input)) result, err := p.RunToResult(bytes.NewReader(input))
result, err := p(start)
must.NoError(t, err) must.NoError(t, err)
success, _, _ := result.Status() success, _, _ := result.Status()
test.False(t, success) test.False(t, success)
@@ -58,7 +57,7 @@ func TestSlice(t *testing.T) {
expectedErr := generator.Error().Draw(t, "expectedErr") expectedErr := generator.Error().Draw(t, "expectedErr")
r := ptest.ErrReaderAt(expectedErr) r := ptest.ErrReaderAt(expectedErr)
s := rapid.SliceOfN(rapid.Byte(), 0, 100).Draw(t, "s") s := rapid.SliceOfN(rapid.Byte(), 0, 100).Draw(t, "s")
result, err := gigaparsec.MatchSlice(s)(gigaparsec.MakeState(r)) result, err := gigaparsec.MatchSlice(s).RunToResult(r)
test.ErrorIs(t, err, expectedErr) test.ErrorIs(t, err, expectedErr)
success, _, _ := result.Status() success, _, _ := result.Status()
test.False(t, success) test.False(t, success)
@@ -67,9 +66,8 @@ func TestSlice(t *testing.T) {
input := rapid.SliceOfN(rapid.Byte(), 1, -1).Draw(t, "input") input := rapid.SliceOfN(rapid.Byte(), 1, -1).Draw(t, "input")
s := rapid.Map(rapid.IntRange(0, len(input)), s := rapid.Map(rapid.IntRange(0, len(input)),
func(n int) []byte { return input[:n] }).Draw(t, "s") func(n int) []byte { return input[:n] }).Draw(t, "s")
start := gigaparsec.MakeState(bytes.NewReader(input))
result, err := gigaparsec.MatchSlice(s)(start) result, err := gigaparsec.MatchSlice(s).RunToResult(bytes.NewReader(input))
must.NoError(t, err) must.NoError(t, err)
success, value, next := result.Status() success, value, next := result.Status()
test.True(t, success) test.True(t, success)
@@ -109,7 +107,7 @@ func TestBind(t *testing.T) {
p := makeParser(pConsume) p := makeParser(pConsume)
q := func(struct{}) gigaparsec.Parser[byte, struct{}] { return makeParser(qConsume) } q := func(struct{}) gigaparsec.Parser[byte, struct{}] { return makeParser(qConsume) }
result, err := gigaparsec.Bind(p, q)(gigaparsec.MakeState(bytes.NewReader(nil))) result, err := gigaparsec.Bind(p, q).RunToResult(bytes.NewReader(nil))
must.NoError(t, err) must.NoError(t, err)
must.EqOp(t, pConsume || qConsume, result.Consumed()) must.EqOp(t, pConsume || qConsume, result.Consumed())
})) }))
@@ -137,8 +135,7 @@ func TestTry(t *testing.T) {
} }
for _, c := range cases { for _, c := range cases {
t.Run(fmt.Sprintf("%+v", c.P), func(t *testing.T) { 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).RunToResult(gigaparsec.SliceReaderAt[R]{c.P})
result, err := gigaparsec.Try(ptest.ForceResult)(start)
succeeded, _, _ := result.Status() succeeded, _, _ := result.Status()
must.NoError(t, err) must.NoError(t, err)
test.EqOp(t, c.TryP.Succeed, succeeded) test.EqOp(t, c.TryP.Succeed, succeeded)
@@ -148,7 +145,7 @@ func TestTry(t *testing.T) {
t.Run("fails on error", func(t *testing.T) { t.Run("fails on error", func(t *testing.T) {
expectedErr := errors.New("it broke") expectedErr := errors.New("it broke")
p := gigaparsec.Try(gigaparsec.Match(byte(0))) p := gigaparsec.Try(gigaparsec.Match(byte(0)))
result, err := p(gigaparsec.MakeState(ptest.ErrReaderAt(expectedErr))) result, err := p.RunToResult(ptest.ErrReaderAt(expectedErr))
succeeded, _, _ := result.Status() succeeded, _, _ := result.Status()
test.ErrorIs(t, err, expectedErr) test.ErrorIs(t, err, expectedErr)
test.False(t, succeeded) test.False(t, succeeded)
@@ -172,7 +169,7 @@ func TestRepeat(t *testing.T) {
input := append(ptest.SliceOfN(true, successes), false) input := append(ptest.SliceOfN(true, successes), false)
p := gigaparsec.Repeat(minCount, gigaparsec.Match(true)) p := gigaparsec.Repeat(minCount, gigaparsec.Match(true))
result, err := p(gigaparsec.MakeState(gigaparsec.SliceReaderAt[bool](input))) result, err := p.RunToResult(gigaparsec.SliceReaderAt[bool](input))
must.NoError(t, err) must.NoError(t, err)
success, _, next := result.Status() success, _, next := result.Status()
@@ -190,21 +187,21 @@ func TestRepeat(t *testing.T) {
} }
shouldConsume := consumeAt != nil shouldConsume := consumeAt != nil
result, err := gigaparsec.Repeat(0, ptest.ForceResult)(gigaparsec.MakeState(gigaparsec.SliceReaderAt[ptest.ForcedResult](input))) result, err := gigaparsec.Repeat(0, ptest.ForceResult).RunToResult(gigaparsec.SliceReaderAt[ptest.ForcedResult](input))
must.NoError(t, err) must.NoError(t, err)
test.EqOp(t, shouldConsume, result.Consumed()) test.EqOp(t, shouldConsume, result.Consumed())
})) }))
t.Run("does not consume on empty input", func(t *testing.T) { t.Run("does not consume on empty input", func(t *testing.T) {
p := gigaparsec.Repeat(0, gigaparsec.Match(0)) p := gigaparsec.Repeat(0, gigaparsec.Match(0))
result, err := p(gigaparsec.MakeState(gigaparsec.SliceReaderAt[int](nil))) result, err := p.RunToResult(gigaparsec.SliceReaderAt[int](nil))
must.NoError(t, err) must.NoError(t, err)
must.False(t, result.Consumed()) must.False(t, result.Consumed())
}) })
t.Run("fails on error", func(t *testing.T) { t.Run("fails on error", func(t *testing.T) {
expectedErr := errors.New("it broke") expectedErr := errors.New("it broke")
p := gigaparsec.Repeat(0, gigaparsec.Match(byte(0))) p := gigaparsec.Repeat(0, gigaparsec.Match(byte(0)))
result, err := p(gigaparsec.MakeState(ptest.ErrReaderAt(expectedErr))) result, err := p.RunToResult(ptest.ErrReaderAt(expectedErr))
succeeded, _, _ := result.Status() succeeded, _, _ := result.Status()
test.ErrorIs(t, err, expectedErr) test.ErrorIs(t, err, expectedErr)
test.False(t, succeeded) test.False(t, succeeded)