Compare commits

...

5 Commits

6 changed files with 70 additions and 2 deletions

View File

@ -4,4 +4,6 @@ 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
Add and benchmark naïve Seq
chainl
whitespace handling

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

@ -61,6 +61,21 @@ func TestRegexp(t *testing.T) {
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)
})
}
func TestRuneReader(t *testing.T) {

View File

@ -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 {
@ -403,3 +414,16 @@ func Repeat[In, Out any](minCount int, p Parser[In, Out]) Parser[In, []Out] {
}
}
}
// 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 })
}

View File

@ -210,3 +210,11 @@ func TestRepeat(t *testing.T) {
test.False(t, succeeded)
})
}
func TestBracket(t *testing.T) {
Todo(t)
}
func TestWhere(t *testing.T) {
Todo(t)
}