diff --git a/bind.go b/bind.go new file mode 100644 index 0000000..3f9af6e --- /dev/null +++ b/bind.go @@ -0,0 +1,304 @@ +// GENERATED FILE. DO NOT EDIT. + +package gigaparsec + +func Bind[In, Out, T any]( + p Parser[In, T], + f func(T) Parser[In, Out], +) Parser[In, Out] { + return func(s State[In]) (Result[In, Out], error) { + var anyConsumed bool + var failed bool + var msg Message + var next = s + + r, err := p(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r.Consumed() + failed, _, msg = r.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val, next, _ := r.Succeeded() + + r2, err := f(val)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r2.Consumed() + failed, _, msg = r2.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val2, next, _ := r2.Succeeded() + + return Succeed(anyConsumed, val2, next, MessageOK(s.Pos())), nil + } +} + +func Bind2[In, Out, T, T2 any]( + p Parser[In, T], + f func(T) Parser[In, T2], + f2 func(T2) Parser[In, Out], +) Parser[In, Out] { + return func(s State[In]) (Result[In, Out], error) { + var anyConsumed bool + var failed bool + var msg Message + var next = s + + r, err := p(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r.Consumed() + failed, _, msg = r.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val, next, _ := r.Succeeded() + + r2, err := f(val)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r2.Consumed() + failed, _, msg = r2.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val2, next, _ := r2.Succeeded() + + r3, err := f2(val2)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r3.Consumed() + failed, _, msg = r3.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val3, next, _ := r3.Succeeded() + + return Succeed(anyConsumed, val3, next, MessageOK(s.Pos())), nil + } +} + +func Bind3[In, Out, T, T2, T3 any]( + p Parser[In, T], + f func(T) Parser[In, T2], + f2 func(T2) Parser[In, T3], + f3 func(T3) Parser[In, Out], +) Parser[In, Out] { + return func(s State[In]) (Result[In, Out], error) { + var anyConsumed bool + var failed bool + var msg Message + var next = s + + r, err := p(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r.Consumed() + failed, _, msg = r.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val, next, _ := r.Succeeded() + + r2, err := f(val)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r2.Consumed() + failed, _, msg = r2.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val2, next, _ := r2.Succeeded() + + r3, err := f2(val2)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r3.Consumed() + failed, _, msg = r3.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val3, next, _ := r3.Succeeded() + + r4, err := f3(val3)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r4.Consumed() + failed, _, msg = r4.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val4, next, _ := r4.Succeeded() + + return Succeed(anyConsumed, val4, next, MessageOK(s.Pos())), nil + } +} + +func Bind4[In, Out, T, T2, T3, T4 any]( + p Parser[In, T], + f func(T) Parser[In, T2], + f2 func(T2) Parser[In, T3], + f3 func(T3) Parser[In, T4], + f4 func(T4) Parser[In, Out], +) Parser[In, Out] { + return func(s State[In]) (Result[In, Out], error) { + var anyConsumed bool + var failed bool + var msg Message + var next = s + + r, err := p(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r.Consumed() + failed, _, msg = r.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val, next, _ := r.Succeeded() + + r2, err := f(val)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r2.Consumed() + failed, _, msg = r2.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val2, next, _ := r2.Succeeded() + + r3, err := f2(val2)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r3.Consumed() + failed, _, msg = r3.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val3, next, _ := r3.Succeeded() + + r4, err := f3(val3)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r4.Consumed() + failed, _, msg = r4.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val4, next, _ := r4.Succeeded() + + r5, err := f4(val4)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r5.Consumed() + failed, _, msg = r5.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val5, next, _ := r5.Succeeded() + + return Succeed(anyConsumed, val5, next, MessageOK(s.Pos())), nil + } +} + +func Bind5[In, Out, T, T2, T3, T4, T5 any]( + p Parser[In, T], + f func(T) Parser[In, T2], + f2 func(T2) Parser[In, T3], + f3 func(T3) Parser[In, T4], + f4 func(T4) Parser[In, T5], + f5 func(T5) Parser[In, Out], +) Parser[In, Out] { + return func(s State[In]) (Result[In, Out], error) { + var anyConsumed bool + var failed bool + var msg Message + var next = s + + r, err := p(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r.Consumed() + failed, _, msg = r.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val, next, _ := r.Succeeded() + + r2, err := f(val)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r2.Consumed() + failed, _, msg = r2.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val2, next, _ := r2.Succeeded() + + r3, err := f2(val2)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r3.Consumed() + failed, _, msg = r3.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val3, next, _ := r3.Succeeded() + + r4, err := f3(val3)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r4.Consumed() + failed, _, msg = r4.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val4, next, _ := r4.Succeeded() + + r5, err := f4(val4)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r5.Consumed() + failed, _, msg = r5.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val5, next, _ := r5.Succeeded() + + r6, err := f5(val5)(next) + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r6.Consumed() + failed, _, msg = r6.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val6, next, _ := r6.Succeeded() + + return Succeed(anyConsumed, val6, next, MessageOK(s.Pos())), nil + } +} + diff --git a/gigaparsec.go b/gigaparsec.go index 051f174..f77e0df 100644 --- a/gigaparsec.go +++ b/gigaparsec.go @@ -1,5 +1,6 @@ package gigaparsec +//go:generate go run ./internal/bindgen -output bind.go -max 5 -pkg gigaparsec import ( "errors" "fmt" @@ -183,25 +184,6 @@ func Slice[T comparable](s []T) Parser[T, []T] { } } -func Bind[In, A, B any](p Parser[In, A], f func(A) Parser[In, B]) Parser[In, B] { - return func(input State[In]) (Result[In, B], error) { - resultA, err := p(input) - if err != nil { - return Result[In, B]{}, err - } - if ok, consumed, msg := resultA.Failed(); ok { - return Fail[In, B](consumed, msg), nil - } - - _, consumedA, valueA, next, _ := resultA.Succeeded() - resultB, err := f(valueA)(next) - if err != nil { - return Result[In, B]{}, err - } - return resultB.Consume(consumedA || resultB.Consumed()), nil - } -} - func Choose[In, Out any](p Parser[In, Out], ps ...Parser[In, Out]) Parser[In, Out] { // TODO Check this against the Parsec paper again, and simplify it. all := append([]Parser[In, Out]{p}, ps...) diff --git a/internal/bindgen/bind.go.tmpl b/internal/bindgen/bind.go.tmpl new file mode 100644 index 0000000..b87a525 --- /dev/null +++ b/internal/bindgen/bind.go.tmpl @@ -0,0 +1,35 @@ +{{define "fparams" -}} +{{with $max := .}}{{range .Count}} f{{.}} func(T{{.}}) Parser[In, {{if eq . $max}}Out{{else}}T{{.Next}}{{end}}], +{{end}}{{end}}{{end -}} + +{{define "func" -}} +func Bind{{.}}[In, Out{{range .Count}}, T{{.}}{{end}} any]( + p Parser[In, T], +{{template "fparams" .}}) Parser[In, Out] { + return func(s State[In]) (Result[In, Out], error) { + var anyConsumed bool + var failed bool + var msg Message + var next = s +{{range .Next.Count}} + r{{.}}, err := {{if eq . 1}}p(next){{else}}f{{.Prev}}(val{{.Prev}})(next){{end}} + if err != nil { + return Result[In, Out]{}, err + } + anyConsumed = anyConsumed || r{{.}}.Consumed() + failed, _, msg = r{{.}}.Failed() + if failed { + return Fail[In, Out](anyConsumed, msg), nil + } + _, _, val{{.}}, next, _ := r{{.}}.Succeeded() +{{end}} + return Succeed(anyConsumed, val{{.Next}}, next, MessageOK(s.Pos())), nil + } +}{{end -}} + +// GENERATED FILE. DO NOT EDIT. + +package {{.Package}} +{{range .Count}} +{{template "func" .}} +{{end}} diff --git a/internal/bindgen/bindgen.go b/internal/bindgen/bindgen.go new file mode 100644 index 0000000..239f99b --- /dev/null +++ b/internal/bindgen/bindgen.go @@ -0,0 +1,110 @@ +package main + +import ( + _ "embed" + "errors" + "flag" + "fmt" + "os" + "strconv" + "text/template" +) + +//go:embed bind.go.tmpl +var bind string + +var tmpl *template.Template + +func main() { + err := run() + if err != nil { + fmt.Fprint(os.Stderr, err) + os.Exit(1) + } +} + +func run() error { + outputPath := flag.String("output", "", "output file path") + maxBindLen := flag.Int("max", 0, "max bind length") + pkg := flag.String("pkg", "", "output package") + flag.Parse() + + if *outputPath == "" { + return errors.New("output required") + } + if *maxBindLen == 0 { + return errors.New("maxbind required") + } + if *pkg == "" { + return errors.New("pkg required") + } + + tmpl = template.New("") + _, err := tmpl.New("bind").Parse(bind) + if err != nil { + return fmt.Errorf("failed to parse bind: %w", err) + } + + f, err := os.OpenFile(*outputPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777) + if err != nil { + return err + } + defer f.Close() + + data := File{ + Package: *pkg, + Counter: Counter(*maxBindLen), + } + return tmpl.ExecuteTemplate(f, "bind", data) +} + +type File struct { + Package string + Counter +} + +// func (f File) Funcs() []Func { +// counters := f.Max.Count() +// funcs := make([]Func, len(counters)) +// for i, counter := range counters { +// funcs[i] = Func{ +// Max: f.Max, +// Length: counter, +// } +// } +// return funcs +// } + +type Func struct { + Max Counter + Length Counter +} + +type Counter int + +func (c Counter) String() string { + if c == 1 { + return "" + } + return strconv.Itoa(int(c)) +} + +func (c Counter) Int() int { + return int(c) +} + +func (c Counter) Next() Counter { + return c + 1 +} + +func (c Counter) Prev() Counter { + return c - 1 +} + +func (c Counter) Count() []Counter { + a := make([]Counter, c) + for i := range a { + a[i] = Counter(i + 1) + } + return a +} diff --git a/seqproto.go b/seqproto.go index fdc4632..f984e02 100644 --- a/seqproto.go +++ b/seqproto.go @@ -1,3 +1,5 @@ +//go:build ignore + package gigaparsec func Seq3[In, Out, T1, T2, T3 any](