Compare commits

...

4 Commits

Author SHA1 Message Date
5c779c4a33 Removed BufferedReaderAt 2024-09-25 17:00:29 -06:00
412707c2b5 Removed SliceCursor 2024-09-25 16:56:48 -06:00
c0603b1c30 Added a generic ReaderAt 2024-09-25 16:34:31 -06:00
e61bf67b85 More todos 2024-09-25 15:50:28 -06:00
7 changed files with 34 additions and 81 deletions

View File

@ -1,4 +1,6 @@
Write Repeat tests Write Repeat tests
Clean up cursor tests
Combine Cursor with State
Think about not requiring so much Pos() when making messages Think about not requiring so much Pos() when making messages
Rename Seq2 to Seq Rename Seq2 to Seq
Document Seq Document Seq

View File

@ -4,6 +4,7 @@ package bytes_test
import ( import (
"bytes" "bytes"
"strings"
"testing" "testing"
"git.codemonkeysoftware.net/b/gigaparsec" "git.codemonkeysoftware.net/b/gigaparsec"
@ -28,7 +29,7 @@ func TestRegexp(t *testing.T) {
})) }))
t.Run("basically works", func(t *testing.T) { t.Run("basically works", func(t *testing.T) {
result, err := pbytes.Regexp("a")(gigaparsec.MakeState(cursor.NewSlice([]byte("a")))) result, err := pbytes.Regexp("a")(gigaparsec.MakeState(cursor.NewReaderAt(strings.NewReader("a"))))
must.NoError(t, err) must.NoError(t, err)
success, value, _ := result.Status() success, value, _ := result.Status()
test.True(t, success, test.Sprint(result.Message())) test.True(t, success, test.Sprint(result.Message()))
@ -38,8 +39,8 @@ func TestRegexp(t *testing.T) {
} }
func TestRuneReader(t *testing.T) { func TestRuneReader(t *testing.T) {
var s = []byte("abcdefghijklmnopqrstuvwxyz") var s = "abcdefghijklmnopqrstuvwxyz"
rr := pbytes.NewRuneReader(cursor.NewSlice(s)) rr := pbytes.NewRuneReader(cursor.NewReaderAt(strings.NewReader(s)))
for i, b := range s { for i, b := range s {
r, n, err := rr.ReadRune() r, n, err := rr.ReadRune()
test.NoError(t, err) test.NoError(t, err)
@ -67,7 +68,7 @@ func TestMatchString(t *testing.T) {
notPrefix := func(b []byte) bool { return !bytes.HasPrefix(input, b) } notPrefix := func(b []byte) bool { return !bytes.HasPrefix(input, b) }
s := string(bgen.Filter(notPrefix).Draw(t, "s")) s := string(bgen.Filter(notPrefix).Draw(t, "s"))
result, err := pbytes.MatchString(s)(gigaparsec.MakeState(cursor.NewSlice(input))) result, err := pbytes.MatchString(s)(gigaparsec.MakeState(cursor.NewReaderAt(bytes.NewReader(input))))
test.NoError(t, err) test.NoError(t, err)
success, _, _ := result.Status() success, _, _ := result.Status()
test.False(t, success) test.False(t, success)
@ -77,7 +78,7 @@ func TestMatchString(t *testing.T) {
input := rapid.SliceOfN(rapid.Byte(), 1, 100).Draw(t, "input") input := rapid.SliceOfN(rapid.Byte(), 1, 100).Draw(t, "input")
slen := rapid.IntRange(0, len(input)).Draw(t, "slen") slen := rapid.IntRange(0, len(input)).Draw(t, "slen")
s := string(input[:slen]) s := string(input[:slen])
result, err := pbytes.MatchString(s)(gigaparsec.MakeState(cursor.NewSlice(input))) result, err := pbytes.MatchString(s)(gigaparsec.MakeState(cursor.NewReaderAt(bytes.NewReader(input))))
must.NoError(t, err) must.NoError(t, err)
success, value, next := result.Status() success, value, next := result.Status()
must.True(t, success) must.True(t, success)

View File

@ -3,6 +3,7 @@
package cursor package cursor
import ( import (
"errors"
"io" "io"
) )
@ -32,50 +33,36 @@ type Cursor[Datum any] interface {
At(pos uint64) Cursor[Datum] At(pos uint64) Cursor[Datum]
} }
type SliceCursor[Datum any] struct { type ReaderAt[T any] interface {
data []Datum ReadAt(p []T, off int64) (n int, err error)
offset uint64
} }
func NewSlice[Datum any](data []Datum) SliceCursor[Datum] { type SliceReaderAt[T any] []T
return SliceCursor[Datum]{
data: data,
offset: 0,
}
}
func (sc SliceCursor[Datum]) Read(dst []Datum) (n uint64, next Cursor[Datum], err error) { func (s SliceReaderAt[T]) ReadAt(dst []T, off int64) (n int, err error) {
if sc.offset == uint64(len(sc.data)) { if off < 0 {
return 0, sc, io.EOF return 0, errors.New("SliceReaderAt.ReadAt: negative offset")
} }
copied := copy(dst, sc.data[sc.offset:]) if off >= int64(len(s)) {
if copied < len(dst) { return 0, io.EOF
}
n = copy(dst, s[off:])
if n < len(dst) {
err = io.EOF err = io.EOF
} }
n = uint64(copied) return n, err
sc.offset += n
return n, sc, err
} }
func (sc SliceCursor[Datum]) Pos() uint64 { type ReaderAtCursor[T any] struct {
return sc.offset r ReaderAt[T]
}
func (sc SliceCursor[Datum]) At(pos uint64) Cursor[Datum] {
sc.offset = pos
return sc
}
type ReaderAtCursor struct {
r io.ReaderAt
pos uint64 pos uint64
} }
func NewReaderAt(r io.ReaderAt) ReaderAtCursor { func NewReaderAt[T any](r ReaderAt[T]) ReaderAtCursor[T] {
return ReaderAtCursor{r: r} return ReaderAtCursor[T]{r: r}
} }
func (rac ReaderAtCursor) Read(dst []byte) (uint64, Cursor[byte], error) { func (rac ReaderAtCursor[T]) Read(dst []T) (uint64, Cursor[T], error) {
n, err := rac.r.ReadAt(dst, int64(rac.pos)) n, err := rac.r.ReadAt(dst, int64(rac.pos))
if n > 0 { if n > 0 {
rac.pos += uint64(n) rac.pos += uint64(n)
@ -83,11 +70,11 @@ func (rac ReaderAtCursor) Read(dst []byte) (uint64, Cursor[byte], error) {
return uint64(n), rac, err return uint64(n), rac, err
} }
func (rac ReaderAtCursor) Pos() uint64 { func (rac ReaderAtCursor[T]) Pos() uint64 {
return rac.pos return rac.pos
} }
func (rac ReaderAtCursor) At(pos uint64) Cursor[byte] { func (rac ReaderAtCursor[T]) At(pos uint64) Cursor[T] {
rac.pos = pos rac.pos = pos
return rac return rac
} }

View File

@ -100,12 +100,8 @@ func testCursor[C cursor.Cursor[byte]](t *testing.T, makeCursor func([]byte) C)
})) }))
} }
func TestSliceCursor(t *testing.T) {
testCursor(t, cursor.NewSlice[byte])
}
func TestReaderAtCursor(t *testing.T) { func TestReaderAtCursor(t *testing.T) {
testCursor(t, func(b []byte) cursor.ReaderAtCursor { testCursor(t, func(b []byte) cursor.ReaderAtCursor[byte] {
return cursor.NewReaderAt(bytes.NewReader(b)) return cursor.NewReaderAt(bytes.NewReader(b))
}) })
t.Run("Read returns an error if the ReaderAt fails", rapid.MakeCheck(func(t *rapid.T) { t.Run("Read returns an error if the ReaderAt fails", rapid.MakeCheck(func(t *rapid.T) {

View File

@ -1,26 +0,0 @@
// SPDX-License-Identifier: Unlicense
package cursor
import (
"io"
)
// BufferedReaderAt uses a buffer to supplement an io.Reader
// with limited backward seeking.
type BufferedReaderAt struct{}
func NewBufferedReaderAt(r io.Reader, minBuffer uint64) *BufferedReaderAt {
return nil
}
// ReadAt reads bytes from the underlying reader. If the offset is after
// the end of the buffer, ReadAt will first read and ignore bytes from the
// underlying reader until it reaches the offset. If the offset is
// before the start of the buffer, ReadAt will return an error.
//
// If your parser needs unlimited lookahead, you should probably
// just read the whole input into a slice and use BytesCursor.
func (b *BufferedReaderAt) ReadAt(dst []byte, offset int64) (int, error) {
return 0, nil
}

View File

@ -1,9 +0,0 @@
// SPDX-License-Identifier: Unlicense
package cursor_test
import "testing"
func TestBufferedReader(t *testing.T) {
Todo(t)
}

View File

@ -30,7 +30,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(cursor.NewSlice(input)) start := gigaparsec.MakeState(cursor.NewReaderAt(bytes.NewReader(input)))
result, err := p(start) result, err := p(start)
must.NoError(t, err) must.NoError(t, err)
success, _, _ := result.Status() success, _, _ := result.Status()
@ -66,7 +66,7 @@ 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")
sLen := rapid.IntRange(0, len(input)).Draw(t, "sLen") sLen := rapid.IntRange(0, len(input)).Draw(t, "sLen")
s := input[:sLen] s := input[:sLen]
start := gigaparsec.MakeState(cursor.NewSlice(input)) start := gigaparsec.MakeState(cursor.NewReaderAt(bytes.NewReader(input)))
result, err := gigaparsec.MatchSlice(s)(start) result, err := gigaparsec.MatchSlice(s)(start)
must.NoError(t, err) must.NoError(t, err)
@ -108,7 +108,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(cursor.NewSlice([]byte{}))) result, err := gigaparsec.Bind(p, q)(gigaparsec.MakeState(cursor.NewReaderAt(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())
})) }))
@ -143,4 +143,6 @@ func TestRepeat(t *testing.T) {
t.Run("succeeds when number of successes is greater than minCount", Todo) t.Run("succeeds when number of successes is greater than minCount", Todo)
t.Run("consumes iff at least one application consumes", Todo) t.Run("consumes iff at least one application consumes", Todo)
t.Run("fails on error", 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)
} }