forked from alecthomas/participle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser.go
125 lines (113 loc) · 3.35 KB
/
parser.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package participle
import (
"bytes"
"errors"
"fmt"
"io"
"reflect"
"strings"
"github.com/alecthomas/participle/lexer"
)
// A Parser for a particular grammar and lexer.
type Parser struct {
root node
lex lexer.Definition
typ reflect.Type
mapper Mapper
useLookahead bool
}
// MustBuild calls Build(grammar, options...) and panics if an error occurs.
func MustBuild(grammar interface{}, options ...Option) *Parser {
parser, err := Build(grammar, options...)
if err != nil {
panic(err)
}
return parser
}
// Build constructs a parser for the given grammar.
//
// If "Lexer()" is not provided as an option, a default lexer based on text/scanner will be used. This scans typical Go-
// like tokens.
//
// See documentation for details
func Build(grammar interface{}, options ...Option) (parser *Parser, err error) {
defer recoverToError(&err)
// Configure Parser struct with defaults + options.
p := &Parser{
lex: lexer.TextScannerLexer,
mapper: identityMapper,
}
for _, option := range options {
if option == nil {
return nil, fmt.Errorf("non-nil Option passed, signature has changed; " +
"if you intended to provide a custom Lexer, try participle.Build(grammar, participle.Lexer(lexer))")
}
option(p)
}
// If we have any mapping functions, wrap the lexer.
if p.mapper != nil {
p.lex = &mappingLexerDef{p.lex, p.mapper}
}
context := newGeneratorContext(p.lex)
p.typ = reflect.TypeOf(grammar)
p.root = context.parseType(p.typ)
// TODO: Fix lookahead - see SQL example.
if p.useLookahead {
applyLookahead(p.root, map[node]bool{})
}
return p, nil
}
// Lex uses the parser's lexer to tokenise input.
func (p *Parser) Lex(r io.Reader) ([]lexer.Token, error) {
lex := p.lex.Lex(r)
return lexer.ConsumeAll(lex)
}
// Parse from r into grammar v which must be of the same type as the grammar passed to
// participle.Build().
func (p *Parser) Parse(r io.Reader, v interface{}) (err error) {
if reflect.TypeOf(v) != p.typ {
return fmt.Errorf("must parse into value of type %s not %T", p.typ, v)
}
defer recoverToError(&err)
lex := lexer.Upgrade(p.lex.Lex(r))
// If the grammar implements Parseable, use it.
if parseable, ok := v.(Parseable); ok {
return p.rootParseable(lex, parseable)
}
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr || rv.Elem().Kind() != reflect.Struct {
return errors.New("target must be a pointer to a struct")
}
pv := p.root.Parse(lex, rv.Elem())
if !lex.Peek(0).EOF() {
lexer.Panicf(lex.Peek(0).Pos, "expected %s but got %q", p.root, lex.Peek(0))
}
if pv == nil {
lexer.Panic(lex.Peek(0).Pos, "invalid syntax")
}
rv.Elem().Set(reflect.Indirect(pv[0]))
return
}
func (p *Parser) rootParseable(lex lexer.PeekingLexer, parseable Parseable) error {
err := parseable.Parse(lex)
peek := lex.Peek(0)
if err == NextMatch {
return lexer.Errorf(peek.Pos, "invalid syntax")
}
if err == nil && !peek.EOF() {
return lexer.Errorf(peek.Pos, "unexpected token %q", peek)
}
return err
}
// ParseString is a convenience around Parse().
func (p *Parser) ParseString(s string, v interface{}) error {
return p.Parse(strings.NewReader(s), v)
}
// ParseBytes is a convenience around Parse().
func (p *Parser) ParseBytes(b []byte, v interface{}) error {
return p.Parse(bytes.NewReader(b), v)
}
// String representation of the grammar.
func (p *Parser) String() string {
return dumpNode(p.root)
}