diff --git a/README.md b/README.md index f87390dc..0cb01531 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/grammar.go b/grammar.go index 451b52de..d3984e57 100644 --- a/grammar.go +++ b/grammar.go @@ -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) diff --git a/nodes.go b/nodes.go index a297ebaf..6f8edc04 100644 --- a/nodes.go +++ b/nodes.go @@ -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() @@ -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) {