-
Notifications
You must be signed in to change notification settings - Fork 1
/
seq.go
263 lines (231 loc) · 7.72 KB
/
seq.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
// Copyright (c) 2017 Opsidian Ltd.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package combinator
import (
"github.com/conflowio/parsley/ast"
"github.com/conflowio/parsley/data"
"github.com/conflowio/parsley/parser"
"github.com/conflowio/parsley/parsley"
)
// Sequence is a recursive and-type combinator
type Sequence struct {
token string
parserLookUp func(int) parsley.Parser
lenCheck func(int) bool
interpreter parsley.Interpreter
customErr error
resultHandler SeqResultHandler
}
// Seq tries to apply all parsers after each other and returns with all combinations of the results.
// The parsers should be generated by the parserLookUp function for the given index.
// When there is no parser for the given index then nil should be returned.
// The lenCheck function should return true if the longest possible match is valid
func Seq(token string, parserLookUp func(int) parsley.Parser, lenCheck func(int) bool) *Sequence {
return &Sequence{
token: token,
parserLookUp: parserLookUp,
lenCheck: lenCheck,
}
}
// Name overrides the returned error if its position is the same as the reader's position
// The error will be: "was expecting <name>"
func (s *Sequence) Name(name string) *Sequence {
s.customErr = parsley.NotFoundError(name)
return s
}
// Token sets the result token
func (s *Sequence) Token(token string) *Sequence {
s.token = token
return s
}
// HandleResult sets the result handler
// If you use a custom function, make sure to make a copy of the nodes slice
func (s *Sequence) HandleResult(resultHandler SeqResultHandler) *Sequence {
s.resultHandler = resultHandler
return s
}
// Bind binds the given interpreter
func (s *Sequence) Bind(interpreter parsley.Interpreter) *Sequence {
s.interpreter = interpreter
return s
}
// Parse parses the given input
func (s *Sequence) Parse(ctx *parsley.Context, leftRecCtx data.IntMap, pos parsley.Pos) (parsley.Node, data.IntSet, parsley.Error) {
p := &sequence{
token: s.token,
parserLookUp: s.parserLookUp,
lenCheck: s.lenCheck,
interpreter: s.interpreter,
curtailingParsers: data.EmptyIntSet,
nodes: nil,
}
if s.resultHandler != nil {
p.resultHandler = s.resultHandler
} else {
p.resultHandler = seqDefaultResultHandler(false)
}
res, cp, err := p.Parse(ctx, leftRecCtx, pos)
if err != nil && s.customErr != nil && err.Pos() == pos && parsley.IsNotFoundError(err) {
err = parsley.NewError(pos, s.customErr)
}
return res, cp, err
}
type sequence struct {
token string
parserLookUp func(i int) parsley.Parser
lenCheck func(i int) bool
interpreter parsley.Interpreter
curtailingParsers data.IntSet
result parsley.Node
err parsley.Error
nodes []parsley.Node
resultHandler SeqResultHandler
}
// Parse runs the recursive parser
func (s *sequence) Parse(ctx *parsley.Context, leftRecCtx data.IntMap, pos parsley.Pos) (parsley.Node, data.IntSet, parsley.Error) {
s.parse(0, ctx, leftRecCtx, pos, true)
if s.result == nil {
return nil, s.curtailingParsers, s.err
}
if s.err != nil {
ctx.SetError(s.err)
}
return s.result, s.curtailingParsers, nil
}
func (s *sequence) parse(depth int, ctx *parsley.Context, leftRecCtx data.IntMap, pos parsley.Pos, mergeCurtailingParsers bool) bool {
var cp data.IntSet
var res parsley.Node
var err parsley.Error
nextParser := s.parserLookUp(depth)
if nextParser != nil {
ctx.RegisterCall()
res, cp, err = nextParser.Parse(ctx, leftRecCtx, pos)
if err != nil && (s.err == nil || err.Pos() >= s.err.Pos()) {
s.err = err
}
}
if mergeCurtailingParsers {
s.curtailingParsers = s.curtailingParsers.Union(cp)
}
if res != nil {
switch rest := res.(type) {
case ast.NodeList:
for i, node := range rest {
if s.parseNext(i, node, depth, ctx, leftRecCtx, pos, mergeCurtailingParsers) {
return true
}
}
default:
if s.parseNext(0, rest, depth, ctx, leftRecCtx, pos, mergeCurtailingParsers) {
return true
}
}
}
if res == nil {
if s.lenCheck(depth) {
if depth > 0 {
s.result = ast.AppendNode(s.result, s.resultHandler.HandleResult(pos, s.token, s.nodes[0:depth], s.interpreter))
if s.nodes[depth-1] != nil && s.nodes[depth-1].Token() == parser.EOF {
return true
}
} else { // It's an empty result
s.result = ast.AppendNode(s.result, s.resultHandler.HandleResult(pos, s.token, nil, s.interpreter))
}
}
}
return false
}
func (s *sequence) parseNext(i int, node parsley.Node, depth int, ctx *parsley.Context, leftRecCtx data.IntMap, pos parsley.Pos, mergeCurtailingParsers bool) bool {
if len(s.nodes) < depth+1 {
s.nodes = append(s.nodes, node)
} else {
s.nodes[depth] = node
}
if i > 0 || node.ReaderPos() > pos {
leftRecCtx = data.EmptyIntMap
mergeCurtailingParsers = false
}
if s.parse(depth+1, ctx, leftRecCtx, node.ReaderPos(), mergeCurtailingParsers) {
return true
}
return false
}
// SeqOf tries to apply all parsers after each other and returns with all combinations of the results.
// Only matches are returned where all parsers were applied successfully.
func SeqOf(parsers ...parsley.Parser) *Sequence {
l := len(parsers)
lookup := func(i int) parsley.Parser {
if i < l {
return parsers[i]
}
return nil
}
lenCheck := func(len int) bool {
return len == l
}
return Seq("SEQ", lookup, lenCheck)
}
// SeqTry tries to apply all parsers after each other and returns with all combinations of the results.
// It needs to match the first parser at least
func SeqTry(parsers ...parsley.Parser) *Sequence {
l := len(parsers)
lookup := func(i int) parsley.Parser {
if i < l {
return parsers[i]
}
return nil
}
lenCheck := func(len int) bool {
return len > 0 && len <= l
}
return Seq("SEQ", lookup, lenCheck)
}
// SeqFirstOrAll tries to apply all parsers after each other and returns with all combinations of the results.
// If it can't match all parsers, but it can match the first one it will return with the result of the first one.
func SeqFirstOrAll(parsers ...parsley.Parser) *Sequence {
l := len(parsers)
lookup := func(i int) parsley.Parser {
if i < l {
return parsers[i]
}
return nil
}
lenCheck := func(len int) bool {
return len == 1 || len == l
}
return Seq("SEQ", lookup, lenCheck)
}
// SeqResultHandler is an interface to handle the result of a Sequence parser
// Make sure to make a copy of the nodes slice
type SeqResultHandler interface {
HandleResult(pos parsley.Pos, token string, nodes []parsley.Node, interpreter parsley.Interpreter) parsley.Node
}
// SeqResultHandlerFunc is a function to handle the result of a Sequence parser
// Make sure to make a copy of the nodes slice
type SeqResultHandlerFunc func(pos parsley.Pos, token string, nodes []parsley.Node, interpreter parsley.Interpreter) parsley.Node
func (f SeqResultHandlerFunc) HandleResult(pos parsley.Pos, token string, nodes []parsley.Node, interpreter parsley.Interpreter) parsley.Node {
return f(pos, token, nodes, interpreter)
}
// ReturnSingle returns the node, if the sequence parser only matched a single node
func ReturnSingle() SeqResultHandlerFunc {
return seqDefaultResultHandler(true)
}
func seqDefaultResultHandler(returnSingle bool) SeqResultHandlerFunc {
return func(pos parsley.Pos, token string, nodes []parsley.Node, interpreter parsley.Interpreter) parsley.Node {
l := len(nodes)
switch l {
case 0:
return ast.NewEmptyNonTerminalNode(token, pos, interpreter)
case 1:
if returnSingle {
return nodes[0]
}
}
nodesCopy := make([]parsley.Node, l)
copy(nodesCopy, nodes)
return ast.NewNonTerminalNode(token, nodesCopy, interpreter)
}
}