Compare commits
No commits in common. "5c779c4a33031d0e09c72aa73141ec3874a86ebb" and "9375b51a705e99091d2ed0525e8b89526b3c9970" have entirely different histories.
5c779c4a33
...
9375b51a70
2
TODO.txt
2
TODO.txt
@ -1,6 +1,4 @@
|
|||||||
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
|
||||||
|
@ -4,7 +4,6 @@ package bytes_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.codemonkeysoftware.net/b/gigaparsec"
|
"git.codemonkeysoftware.net/b/gigaparsec"
|
||||||
@ -29,7 +28,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.NewReaderAt(strings.NewReader("a"))))
|
result, err := pbytes.Regexp("a")(gigaparsec.MakeState(cursor.NewSlice([]byte("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()))
|
||||||
@ -39,8 +38,8 @@ func TestRegexp(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRuneReader(t *testing.T) {
|
func TestRuneReader(t *testing.T) {
|
||||||
var s = "abcdefghijklmnopqrstuvwxyz"
|
var s = []byte("abcdefghijklmnopqrstuvwxyz")
|
||||||
rr := pbytes.NewRuneReader(cursor.NewReaderAt(strings.NewReader(s)))
|
rr := pbytes.NewRuneReader(cursor.NewSlice(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)
|
||||||
@ -68,7 +67,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.NewReaderAt(bytes.NewReader(input))))
|
result, err := pbytes.MatchString(s)(gigaparsec.MakeState(cursor.NewSlice(input)))
|
||||||
test.NoError(t, err)
|
test.NoError(t, err)
|
||||||
success, _, _ := result.Status()
|
success, _, _ := result.Status()
|
||||||
test.False(t, success)
|
test.False(t, success)
|
||||||
@ -78,7 +77,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.NewReaderAt(bytes.NewReader(input))))
|
result, err := pbytes.MatchString(s)(gigaparsec.MakeState(cursor.NewSlice(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)
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
package cursor
|
package cursor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,36 +32,50 @@ type Cursor[Datum any] interface {
|
|||||||
At(pos uint64) Cursor[Datum]
|
At(pos uint64) Cursor[Datum]
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReaderAt[T any] interface {
|
type SliceCursor[Datum any] struct {
|
||||||
ReadAt(p []T, off int64) (n int, err error)
|
data []Datum
|
||||||
|
offset uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type SliceReaderAt[T any] []T
|
func NewSlice[Datum any](data []Datum) SliceCursor[Datum] {
|
||||||
|
return SliceCursor[Datum]{
|
||||||
|
data: data,
|
||||||
|
offset: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s SliceReaderAt[T]) ReadAt(dst []T, off int64) (n int, err error) {
|
func (sc SliceCursor[Datum]) Read(dst []Datum) (n uint64, next Cursor[Datum], err error) {
|
||||||
if off < 0 {
|
if sc.offset == uint64(len(sc.data)) {
|
||||||
return 0, errors.New("SliceReaderAt.ReadAt: negative offset")
|
return 0, sc, io.EOF
|
||||||
}
|
}
|
||||||
if off >= int64(len(s)) {
|
copied := copy(dst, sc.data[sc.offset:])
|
||||||
return 0, io.EOF
|
if copied < len(dst) {
|
||||||
}
|
|
||||||
n = copy(dst, s[off:])
|
|
||||||
if n < len(dst) {
|
|
||||||
err = io.EOF
|
err = io.EOF
|
||||||
}
|
}
|
||||||
return n, err
|
n = uint64(copied)
|
||||||
|
sc.offset += n
|
||||||
|
return n, sc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReaderAtCursor[T any] struct {
|
func (sc SliceCursor[Datum]) Pos() uint64 {
|
||||||
r ReaderAt[T]
|
return sc.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
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[T any](r ReaderAt[T]) ReaderAtCursor[T] {
|
func NewReaderAt(r io.ReaderAt) ReaderAtCursor {
|
||||||
return ReaderAtCursor[T]{r: r}
|
return ReaderAtCursor{r: r}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rac ReaderAtCursor[T]) Read(dst []T) (uint64, Cursor[T], error) {
|
func (rac ReaderAtCursor) Read(dst []byte) (uint64, Cursor[byte], 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)
|
||||||
@ -70,11 +83,11 @@ func (rac ReaderAtCursor[T]) Read(dst []T) (uint64, Cursor[T], error) {
|
|||||||
return uint64(n), rac, err
|
return uint64(n), rac, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rac ReaderAtCursor[T]) Pos() uint64 {
|
func (rac ReaderAtCursor) Pos() uint64 {
|
||||||
return rac.pos
|
return rac.pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rac ReaderAtCursor[T]) At(pos uint64) Cursor[T] {
|
func (rac ReaderAtCursor) At(pos uint64) Cursor[byte] {
|
||||||
rac.pos = pos
|
rac.pos = pos
|
||||||
return rac
|
return rac
|
||||||
}
|
}
|
||||||
|
@ -100,8 +100,12 @@ 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[byte] {
|
testCursor(t, func(b []byte) cursor.ReaderAtCursor {
|
||||||
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) {
|
||||||
|
26
cursor/helper.go
Normal file
26
cursor/helper.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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
|
||||||
|
}
|
9
cursor/helper_test.go
Normal file
9
cursor/helper_test.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
package cursor_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestBufferedReader(t *testing.T) {
|
||||||
|
Todo(t)
|
||||||
|
}
|
@ -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.NewReaderAt(bytes.NewReader(input)))
|
start := gigaparsec.MakeState(cursor.NewSlice(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.NewReaderAt(bytes.NewReader(input)))
|
start := gigaparsec.MakeState(cursor.NewSlice(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.NewReaderAt(bytes.NewReader(nil))))
|
result, err := gigaparsec.Bind(p, q)(gigaparsec.MakeState(cursor.NewSlice([]byte{})))
|
||||||
must.NoError(t, err)
|
must.NoError(t, err)
|
||||||
must.EqOp(t, pConsume || qConsume, result.Consumed())
|
must.EqOp(t, pConsume || qConsume, result.Consumed())
|
||||||
}))
|
}))
|
||||||
@ -143,6 +143,4 @@ 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)
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user