Skip to content

Commit

Permalink
Make Trace a ParseOption (#238)
Browse files Browse the repository at this point in the history
  • Loading branch information
mccolljr committed Jun 14, 2022
1 parent cac0186 commit dcb4a85
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 70 deletions.
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.

0 comments on commit dcb4a85

Please sign in to comment.