Skip to content

Commit

Permalink
Store Pos/EndPos field index/existence at grammar construction time.
Browse files Browse the repository at this point in the history
This speeds up parsing by 5-10%:

    benchmark                        old ns/op     new ns/op     delta
    BenchmarkEBNFParser-12           143589        129605        -9.74%
    BenchmarkParser-12               395397        375403        -5.06%
    BenchmarkParticipleThrift-12     202280        191766        -5.20%
    BenchmarkParser-12               7724639       7114586       -7.90%

See #108.
  • Loading branch information
alecthomas committed Nov 26, 2020
1 parent 9966e90 commit 279bd24
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 15 deletions.
6 changes: 3 additions & 3 deletions README.md
Expand Up @@ -366,10 +366,10 @@ There are a few areas where Participle can provide useful feedback to users of y

1. Errors returned by [Parser.Parse*()](https://godoc.org/github.com/alecthomas/participle#Parser.ParseReader) will be of type [Error](https://godoc.org/github.com/alecthomas/participle#Error). This will contain positional information where available.
2. Participle will make a best effort to return as much of the AST up to the error location as possible.
3. Any node in the AST containing a field `Pos lexer.Position` or `Tok lexer.Token` will be automatically
3. Any node in the AST containing a field `Pos lexer.Position` will be automatically
populated from the nearest matching token.
4. Any node in the AST containing a field `EndPos lexer.Position` or `EndTok lexer.Token` will be
automatically populated with the token at the end of the node.
4. Any node in the AST containing a field `EndPos lexer.Position` will be
automatically populated from the token at the end of the node.

These related pieces of information can be combined to provide fairly comprehensive error reporting.

Expand Down
2 changes: 1 addition & 1 deletion grammar.go
Expand Up @@ -47,7 +47,7 @@ func (g *generatorContext) parseType(t reflect.Type) (_ node, returnedError erro
if err != nil {
return nil, err
}
out := &strct{typ: t}
out := newStrct(t)
g.typeNodes[t] = out // Ensure we avoid infinite recursion.
if slexer.NumField() == 0 {
return nil, fmt.Errorf("can not parse into empty struct %s", t)
Expand Down
41 changes: 30 additions & 11 deletions nodes.go
Expand Up @@ -16,7 +16,6 @@ var (
MaxIterations = 1000000

positionType = reflect.TypeOf(lexer.Position{})
tokenType = reflect.TypeOf(lexer.Token{})
captureType = reflect.TypeOf((*Capture)(nil)).Elem()
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
parseableType = reflect.TypeOf((*Parseable)(nil)).Elem()
Expand Down Expand Up @@ -73,26 +72,46 @@ func (p *parseable) Parse(ctx *parseContext, parent reflect.Value) (out []reflec

// @@
type strct struct {
typ reflect.Type
expr node
typ reflect.Type
expr node
posFieldIndex []int
endPosFieldIndex []int
}

func newStrct(typ reflect.Type) *strct {
var (
posFieldIndex []int
endPosFieldIndex []int
)
field, ok := typ.FieldByName("Pos")
if ok && field.Type == positionType {
posFieldIndex = field.Index
}
field, ok = typ.FieldByName("EndPos")
if ok && field.Type == positionType {
endPosFieldIndex = field.Index
}
return &strct{
typ: typ,
posFieldIndex: posFieldIndex,
endPosFieldIndex: endPosFieldIndex,
}
}

func (s *strct) String() string { return stringer(s) }

func (s *strct) maybeInjectStartToken(token lexer.Token, v reflect.Value) {
if f := v.FieldByName("Pos"); f.IsValid() && f.Type() == positionType {
f.Set(reflect.ValueOf(token.Pos))
} else if f := v.FieldByName("Tok"); f.IsValid() && f.Type() == tokenType {
f.Set(reflect.ValueOf(token))
if s.posFieldIndex == nil {
return
}
v.FieldByIndex(s.posFieldIndex).Set(reflect.ValueOf(token.Pos))
}

func (s *strct) maybeInjectEndToken(token lexer.Token, v reflect.Value) {
if f := v.FieldByName("EndPos"); f.IsValid() && f.Type() == positionType {
f.Set(reflect.ValueOf(token.Pos))
} else if f := v.FieldByName("EndTok"); f.IsValid() && f.Type() == tokenType {
f.Set(reflect.ValueOf(token))
if s.endPosFieldIndex == nil {
return
}
v.FieldByIndex(s.endPosFieldIndex).Set(reflect.ValueOf(token.Pos))
}

func (s *strct) Parse(ctx *parseContext, parent reflect.Value) (out []reflect.Value, err error) {
Expand Down

0 comments on commit 279bd24

Please sign in to comment.