Move Map and Run onto Parser, and document stuff

This commit is contained in:
b
2026-08-20 23:14:00 -06:00
parent 6b201fbb69
commit 22a6eb7335
5 changed files with 27 additions and 24 deletions
+2 -2
View File
@@ -9,7 +9,7 @@ import (
// Token wraps a parser to discard all whitespace after the parsed input. // Token wraps a parser to discard all whitespace after the parsed input.
// whitespace is a parser that will consume non-empty whitespace. // whitespace is a parser that will consume non-empty whitespace.
func Token[Out any](whitespace gigaparsec.Parser[byte, struct{}]) func(p gigaparsec.Parser[byte, Out]) gigaparsec.Parser[byte, Out] { func Token[Out any](whitespace gigaparsec.Parser[byte, struct{}]) func(p gigaparsec.Parser[byte, Out]) gigaparsec.Parser[byte, Out] {
mappedWS := gigaparsec.Map(whitespace, func(struct{}) struct{} { return struct{}{} }) mappedWS := whitespace.Map(func(struct{}) struct{} { return struct{}{} })
var ignoreWS gigaparsec.Parser[byte, struct{}] = func(s gigaparsec.State[byte]) (gigaparsec.Result[byte, struct{}], error) { var ignoreWS gigaparsec.Parser[byte, struct{}] = func(s gigaparsec.State[byte]) (gigaparsec.Result[byte, struct{}], error) {
result, err := mappedWS(s) result, err := mappedWS(s)
return result.Consume(false), err return result.Consume(false), err
@@ -25,5 +25,5 @@ func isWhitespaceASCII(b byte) bool {
// WhitespaceASCII consumes one ASCII whitespace character: space, tab, LF, CR, vertical tab, or form feed. // WhitespaceASCII consumes one ASCII whitespace character: space, tab, LF, CR, vertical tab, or form feed.
func WhitespaceASCII() gigaparsec.Parser[byte, struct{}] { func WhitespaceASCII() gigaparsec.Parser[byte, struct{}] {
return gigaparsec.Map(gigaparsec.Satisfy(isWhitespaceASCII), func(byte) struct{} { return struct{}{} }) return gigaparsec.Satisfy(isWhitespaceASCII).Map(func(byte) struct{} { return struct{}{} })
} }
+1 -1
View File
@@ -21,7 +21,7 @@ func ExampleToken() {
) )
tokenize := gigaparsec.Repeat(0, bytes.Token[string](bytes.WhitespaceASCII())(ptokens)) tokenize := gigaparsec.Repeat(0, bytes.Token[string](bytes.WhitespaceASCII())(ptokens))
tokens, _ := gigaparsec.Run(tokenize, strings.NewReader("(alpha + 42) -5 ")) tokens, _ := tokenize.Run(strings.NewReader("(alpha + 42) -5 "))
for _, token := range tokens { for _, token := range tokens {
fmt.Println(token) fmt.Println(token)
} }
+15 -12
View File
@@ -194,6 +194,7 @@ func (p Parser[In, Out]) Label(label string) Parser[In, Out] {
} }
} }
// Where creates a parser that fails if pred does not hold.
func (p Parser[In, Out]) Where(pred func(Out) bool) 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) { return func(s State[In]) (Result[In, Out], error) {
result, err := p(s) result, err := p(s)
@@ -205,13 +206,15 @@ func (p Parser[In, Out]) Where(pred func(Out) bool) Parser[In, Out] {
} }
} }
type ParseError Message // Map creates a parser that converts the output of p from Out1 to Out2.
func (p Parser[In, Out]) Map[Out2 any](f func(Out) Out2) Parser[In, Out2] {
func (pe ParseError) Error() string { return Bind(p, func(out Out) Parser[In, Out2] {
return Message(pe).String() return Return[In](f(out))
})
} }
func Run[In, Out any](p Parser[In, Out], r ReaderAt[In]) (out Out, err error) { // Run executes a parser on r and returns the result.
func (p Parser[In, Out]) Run(r ReaderAt[In]) (out Out, err error) {
start := MakeState(r) start := MakeState(r)
result, err := p(start) result, err := p(start)
if err != nil { if err != nil {
@@ -226,6 +229,12 @@ func Run[In, Out any](p Parser[In, Out], r ReaderAt[In]) (out Out, err error) {
return return
} }
type ParseError Message
func (pe ParseError) Error() string {
return Message(pe).String()
}
// Return creates a parser that always succeeds and returns value without consuming any input. // Return creates a parser that always succeeds and returns value without consuming any input.
func Return[In, Out any](value Out) Parser[In, Out] { func Return[In, Out any](value Out) Parser[In, Out] {
return func(state State[In]) (Result[In, Out], error) { return func(state State[In]) (Result[In, Out], error) {
@@ -355,13 +364,6 @@ func Try[In, Out any](p Parser[In, Out]) Parser[In, Out] {
} }
} }
// Map creates a parser that converts the output of p from Out1 to Out2.
func Map[In, Out1, Out2 any](p Parser[In, Out1], f func(Out1) Out2) Parser[In, Out2] {
return Bind(p, func(out Out1) Parser[In, Out2] {
return Return[In](f(out))
})
}
func end[In any](s State[In]) (Result[In, struct{}], error) { func end[In any](s State[In]) (Result[In, struct{}], error) {
_, _, err := s.Read([]In{}) _, _, err := s.Read([]In{})
if errors.Is(err, io.EOF) { if errors.Is(err, io.EOF) {
@@ -424,6 +426,7 @@ func Lazy[In, Out any](p func() Parser[In, Out]) Parser[In, Out] {
} }
} }
// Bracket parses p, flanked by left and right.
func Bracket[In, Out, LOut, ROut any](left Parser[In, LOut], p Parser[In, Out], right Parser[In, ROut]) Parser[In, Out] { 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 }) return Seq3(left, p, right, func(_ LOut, val Out, _ ROut) Out { return val })
} }
+1 -1
View File
@@ -1,6 +1,6 @@
module git.codemonkeysoftware.net/b/gigaparsec module git.codemonkeysoftware.net/b/gigaparsec
go 1.23 go 1.27
require ( require (
github.com/shoenig/test v1.11.0 github.com/shoenig/test v1.11.0
+8 -8
View File
@@ -22,13 +22,13 @@ func BenchmarkBind5(b *testing.B) {
input := gigaparsec.SliceReaderAt[byte]{0} input := gigaparsec.SliceReaderAt[byte]{0}
b.Run("gigaparsec.Bind5", func(b *testing.B) { b.Run("gigaparsec.Bind5", func(b *testing.B) {
for range b.N { for range b.N {
gigaparsec.Run(p(gigaparsec.Bind5), input) p(gigaparsec.Bind5).Run(input)
} }
}) })
b.Run("naïve.Bind5", func(b *testing.B) { b.Run("naïve.Bind5", func(b *testing.B) {
for range b.N { for range b.N {
gigaparsec.Run(p(naive.Bind5), input) p(naive.Bind5).Run(input)
} }
}) })
} }
@@ -46,13 +46,13 @@ func BenchmarkBind9(b *testing.B) {
input := gigaparsec.SliceReaderAt[byte]{0} input := gigaparsec.SliceReaderAt[byte]{0}
b.Run("gigaparsec.Bind9", func(b *testing.B) { b.Run("gigaparsec.Bind9", func(b *testing.B) {
for range b.N { for range b.N {
gigaparsec.Run(p(gigaparsec.Bind9), input) p(gigaparsec.Bind9).Run(input)
} }
}) })
b.Run("naive.Bind9", func(b *testing.B) { b.Run("naive.Bind9", func(b *testing.B) {
for range b.N { for range b.N {
gigaparsec.Run(p(naive.Bind9), input) p(naive.Bind9).Run(input)
} }
}) })
} }
@@ -72,12 +72,12 @@ func BenchmarkSeq5(b *testing.B) {
input := gigaparsec.SliceReaderAt[byte]{0} input := gigaparsec.SliceReaderAt[byte]{0}
b.Run("gigaparsec.Seq5", func(b *testing.B) { b.Run("gigaparsec.Seq5", func(b *testing.B) {
for range b.N { for range b.N {
gigaparsec.Run(p(gigaparsec.Seq5), input) p(gigaparsec.Seq5).Run(input)
} }
}) })
b.Run("naive.Seq5", func(b *testing.B) { b.Run("naive.Seq5", func(b *testing.B) {
for range b.N { for range b.N {
gigaparsec.Run(p(naive.Seq5), input) p(naive.Seq5).Run(input)
} }
}) })
} }
@@ -97,12 +97,12 @@ func BenchmarkSeq9(b *testing.B) {
input := gigaparsec.SliceReaderAt[byte]{0} input := gigaparsec.SliceReaderAt[byte]{0}
b.Run("gigaparsec.Seq9", func(b *testing.B) { b.Run("gigaparsec.Seq9", func(b *testing.B) {
for range b.N { for range b.N {
gigaparsec.Run(p(gigaparsec.Seq9), input) p(gigaparsec.Seq9).Run(input)
} }
}) })
b.Run("naive.Seq9", func(b *testing.B) { b.Run("naive.Seq9", func(b *testing.B) {
for range b.N { for range b.N {
gigaparsec.Run(p(naive.Seq9), input) p(naive.Seq9).Run(input)
} }
}) })
} }