Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Trace a ParseOption #238

Merged
merged 2 commits into from Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions context.go
@@ -1,7 +1,10 @@
package participle

import (
"fmt"
"io"
"reflect"
"strings"

"github.com/alecthomas/participle/v2/lexer"
)
Expand All @@ -16,6 +19,8 @@ type contextFieldSet struct {
// Context for a single parse.
type parseContext struct {
*lexer.PeekingLexer
depth int
trace io.Writer
deepestError error
deepestErrorDepth int
lookahead int
Expand Down Expand Up @@ -103,6 +108,16 @@ func (p *parseContext) Stop(err error, branch *parseContext) bool {

func (p *parseContext) hasInfiniteLookahead() bool { return p.lookahead < 0 }

func (p *parseContext) printTrace(n node) func() {
if p.trace != nil {
tok := p.PeekingLexer.Peek()
fmt.Fprintf(p.trace, "%s%q %s\n", strings.Repeat(" ", p.depth*2), tok, n.GoString())
p.depth += 1
return func() { p.depth -= 1 }
}
return func() {}
}

func maxInt(a, b int) int {
if a > b {
return a
Expand Down
3 changes: 0 additions & 3 deletions ebnf.go
Expand Up @@ -159,9 +159,6 @@ func buildEBNF(root bool, n node, seen map[node]bool, p *ebnfp, outp *[]*ebnfp)
buildEBNF(true, n.expr, seen, p, outp)
p.out += ")"

case *trace:
buildEBNF(root, n.node, seen, p, outp)

default:
panic(fmt.Sprintf("unsupported node type %T", n))
}
Expand Down
14 changes: 14 additions & 0 deletions nodes.go
Expand Up @@ -60,6 +60,7 @@ func (p *parseable) String() string { return ebnf(p) }
func (p *parseable) GoString() string { return p.t.String() }

func (p *parseable) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(p)()
rv := reflect.New(p.t)
v := rv.Interface().(Parseable)
err = v.Parse(ctx.PeekingLexer)
Expand All @@ -82,6 +83,7 @@ func (c *custom) String() string { return ebnf(c) }
func (c *custom) GoString() string { return c.typ.Name() }

func (c *custom) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(c)()
results := c.parseFn.Call([]reflect.Value{reflect.ValueOf(ctx.PeekingLexer)})
if err, _ := results[1].Interface().(error); err != nil {
if err == NextMatch {
Expand All @@ -102,6 +104,7 @@ func (u *union) String() string { return ebnf(u) }
func (u *union) GoString() string { return u.typ.Name() }

func (u *union) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(u)()
temp := disjunction{u.members}
vals, err := temp.Parse(ctx, parent)
if err != nil {
Expand Down Expand Up @@ -145,6 +148,7 @@ func (s *strct) String() string { return ebnf(s) }
func (s *strct) GoString() string { return s.typ.Name() }

func (s *strct) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(s)()
sv := reflect.New(s.typ).Elem()
start := ctx.RawCursor()
t := ctx.Peek()
Expand Down Expand Up @@ -225,6 +229,7 @@ type group struct {
func (g *group) String() string { return ebnf(g) }
func (g *group) GoString() string { return fmt.Sprintf("group{%s}", g.mode) }
func (g *group) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(g)()
// Configure min/max matches.
min := 1
max := 1
Expand Down Expand Up @@ -294,6 +299,7 @@ func (n *lookaheadGroup) String() string { return ebnf(n) }
func (n *lookaheadGroup) GoString() string { return "lookaheadGroup{}" }

func (n *lookaheadGroup) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(n)()
// Create a branch to avoid advancing the parser as any match will be discarded
branch := ctx.Branch()
out, err = n.expr.Parse(branch, parent)
Expand All @@ -315,6 +321,7 @@ func (d *disjunction) String() string { return ebnf(d) }
func (d *disjunction) GoString() string { return "disjunction{}" }

func (d *disjunction) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(d)()
var (
deepestError = 0
firstError error
Expand Down Expand Up @@ -362,6 +369,7 @@ func (s *sequence) String() string { return ebnf(s) }
func (s *sequence) GoString() string { return "sequence{}" }

func (s *sequence) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(s)()
for n := s; n != nil; n = n.next {
child, err := n.node.Parse(ctx, parent)
out = append(out, child...)
Expand Down Expand Up @@ -396,6 +404,7 @@ func (c *capture) String() string { return ebnf(c) }
func (c *capture) GoString() string { return "capture{}" }

func (c *capture) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(c)()
start := ctx.RawCursor()
v, err := c.node.Parse(ctx, parent)
if v != nil {
Expand All @@ -420,6 +429,7 @@ func (r *reference) String() string { return ebnf(r) }
func (r *reference) GoString() string { return fmt.Sprintf("reference{%s}", r.identifier) }

func (r *reference) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(r)()
token, cursor := ctx.PeekAny(func(t lexer.Token) bool {
return t.Type == r.typ
})
Expand All @@ -439,6 +449,7 @@ func (o *optional) String() string { return ebnf(o) }
func (o *optional) GoString() string { return "optional{}" }

func (o *optional) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(o)()
branch := ctx.Branch()
out, err = o.node.Parse(branch, parent)
if err != nil {
Expand Down Expand Up @@ -466,6 +477,7 @@ func (r *repetition) GoString() string { return "repetition{}" }
// Parse a repetition. Once a repetition is encountered it will always match, so grammars
// should ensure that branches are differentiated prior to the repetition.
func (r *repetition) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(r)()
i := 0
for ; i < MaxIterations; i++ {
branch := ctx.Branch()
Expand Down Expand Up @@ -505,6 +517,7 @@ func (l *literal) String() string { return ebnf(l) }
func (l *literal) GoString() string { return fmt.Sprintf("literal{%q, %q}", l.s, l.tt) }

func (l *literal) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(l)()
match := func(t lexer.Token) bool {
var equal bool
if ctx.caseInsensitive[t.Type] {
Expand All @@ -530,6 +543,7 @@ func (n *negation) String() string { return ebnf(n) }
func (n *negation) GoString() string { return "negation{}" }

func (n *negation) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
defer ctx.printTrace(n)()
// Create a branch to avoid advancing the parser, but call neither Stop nor Accept on it
// since we will discard a match.
branch := ctx.Branch()
Expand Down
8 changes: 8 additions & 0 deletions options.go
Expand Up @@ -2,6 +2,7 @@ package participle

import (
"fmt"
"io"
"reflect"

"github.com/alecthomas/participle/v2/lexer"
Expand Down Expand Up @@ -111,6 +112,13 @@ func Union[T any](members ...T) Option {
// ParseOption modifies how an individual parse is applied.
type ParseOption func(p *parseContext)

// Trace the parse to "w".
func Trace(w io.Writer) ParseOption {
return func(p *parseContext) {
p.trace = w
}
}

// AllowTrailing tokens without erroring.
//
// That is, do not error if a full parse completes but additional tokens remain.
Expand Down
4 changes: 0 additions & 4 deletions parser.go
Expand Up @@ -23,7 +23,6 @@ type customDef struct {
// A Parser for a particular grammar and lexer.
type Parser struct {
root node
trace io.Writer
lex lexer.Definition
typ reflect.Type
useLookahead int
Expand Down Expand Up @@ -114,9 +113,6 @@ func Build(grammar interface{}, options ...Option) (parser *Parser, err error) {
if err := validate(p.root); err != nil {
return nil, err
}
if p.trace != nil {
p.root = injectTrace(p.trace, 0, p.root)
}
return p, nil
}

Expand Down
4 changes: 3 additions & 1 deletion parser_test.go
Expand Up @@ -1834,8 +1834,10 @@ func TestParserWithUnion(t *testing.T) {
{`{ [ { [12] } ] }`, grammar{B: BMember2{AMember2{BMember2{AMember2{BMember1{12}}}}}}},
} {
var actual grammar
require.NoError(t, parser.ParseString("", c.src, &actual))
var trace strings.Builder
require.NoError(t, parser.ParseString("", c.src, &actual, participle.Trace(&trace)))
require.Equal(t, c.expected, actual)
require.NotEqual(t, "", trace.String())
}

require.Equal(t, strings.TrimSpace(`
Expand Down
62 changes: 0 additions & 62 deletions trace.go

This file was deleted.