forked from prataprc/goparsec
-
Notifications
You must be signed in to change notification settings - Fork 0
/
parsec.go
269 lines (255 loc) · 8.16 KB
/
parsec.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
264
265
266
267
268
269
// Copyright (c) 2013 Goparsec AUTHORS. All rights reserved.
// Use of this source code is governed by LICENSE file.
package parsec
import "fmt"
// ParsecNode for parsers return input text as parsed nodes.
type ParsecNode interface{}
// Parser function parses input text encapsulated by Scanner, higher
// order parsers are constructed using combinators.
type Parser func(Scanner) (ParsecNode, Scanner)
// Nodify callback function to construct custom ParsecNode. Even when
// combinators like And, OrdChoice, Many etc.. can match input string,
// it is still possible to fail them via nodify callback function, by
// returning nil. This very useful in cases when,
// * lookahead matching is required.
// * an exceptional cases for regex pattern.
//
// Note that some combinators like KLEENE shall not interpret the return
// value from Nodify callback.
type Nodify func([]ParsecNode) ParsecNode
// And combinator accepts a list of `Parser`, or reference to a
// parser, that must match the input string, atleast until the
// last Parser argument. Return a parser function that can further be
// used to construct higher-level parsers.
//
// If all parser matches, a list of ParsecNode, where each
// ParsecNode is constructed by matching parser, will be passed
// as argument to Nodify callback. Even if one of the input
// parser function fails, And will fail without consuming the input.
func And(callb Nodify, parsers ...interface{}) Parser {
return func(s Scanner) (ParsecNode, Scanner) {
var ns = make([]ParsecNode, 0, len(parsers))
var n ParsecNode
news := s.Clone()
for _, parser := range parsers {
n, news = doParse(parser, news)
if n == nil {
return nil, s
}
ns = append(ns, n)
}
if node := docallback(callb, ns); node != nil {
return node, news
}
return nil, s
}
}
// OrdChoice combinator accepts a list of `Parser`, or
// reference to a parser, where atleast one of the parser
// must match the input string. Return a parser function
// that can further be used to construct higher level parsers.
//
// The first matching parser function's output is passed
// as argument to Nodify callback, that is []ParsecNode argument
// will just have one element in it. If none of the parsers
// match the input, then OrdChoice will fail without consuming
// any input.
func OrdChoice(callb Nodify, parsers ...interface{}) Parser {
return func(s Scanner) (ParsecNode, Scanner) {
for _, parser := range parsers {
if n, news := doParse(parser, s.Clone()); n != nil {
if node := docallback(callb, []ParsecNode{n}); node != nil {
return node, news
}
}
}
return nil, s
}
}
// Kleene combinator accepts two parsers, or reference to
// parsers, namely opScan and sepScan, where opScan parser
// will be used to match input string and contruct ParsecNode,
// and sepScan parser will be used to match input string
// and ignore the matched string. If sepScan parser is not
// supplied, then opScan parser will be applied on the input
// until it fails.
//
// The process of matching opScan parser and sepScan parser
// will continue in a loop until either one of them fails on
// the input stream.
//
// For every successful match of opScan, the returned
// ParsecNode from matching parser will be accumulated and
// passed as argument to Nodify callback. If there is not a
// single match for opScan, then []ParsecNode of ZERO length
// will be passed as argument to Nodify callback. Kleene
// combinator will never fail.
func Kleene(callb Nodify, parsers ...interface{}) Parser {
var opScan, sepScan interface{}
switch l := len(parsers); l {
case 1:
opScan = parsers[0]
case 2:
opScan, sepScan = parsers[0], parsers[1]
default:
panic(fmt.Errorf("kleene parser doesn't accept %v parsers", l))
}
return func(s Scanner) (ParsecNode, Scanner) {
var n ParsecNode
ns := make([]ParsecNode, 0)
news := s.Clone()
for {
if n, news = doParse(opScan, news); n == nil {
break
}
ns = append(ns, n)
if sepScan != nil {
if n, news = doParse(sepScan, news); n == nil {
break
}
}
}
return docallback(callb, ns), news
}
}
// Many combinator accepts two parsers, or reference to
// parsers, namely opScan and sepScan, where opScan parser
// will be used to match input string and contruct ParsecNode,
// and sepScan parser will be used to match input string and
// ignore the matched string. If sepScan parser is not
// supplied, then opScan parser will be applied on the input
// until it fails.
//
// The process of matching opScan parser and sepScan parser
// will continue in a loop until either one of them fails on
// the input stream.
//
// The difference between `Many` combinator and `Kleene`
// combinator is that there shall atleast be one match of opScan.
//
// For every successful match of opScan, the returned
// ParsecNode from matching parser will be accumulated and
// passed as argument to Nodify callback. If there is not a
// single match for opScan, then Many will fail without
// consuming the input.
func Many(callb Nodify, parsers ...interface{}) Parser {
var opScan, sepScan interface{}
switch l := len(parsers); l {
case 1:
opScan = parsers[0]
case 2:
opScan, sepScan = parsers[0], parsers[1]
default:
panic(fmt.Errorf("many parser doesn't accept %v parsers", l))
}
return func(s Scanner) (ParsecNode, Scanner) {
var n ParsecNode
ns := make([]ParsecNode, 0)
news := s.Clone()
for {
if n, news = doParse(opScan, news); n == nil {
break
}
ns = append(ns, n)
if sepScan != nil {
if n, news = doParse(sepScan, news); n == nil {
break
}
}
}
if len(ns) > 0 {
if node := docallback(callb, ns); node != nil {
return node, news
}
}
return nil, s
}
}
// ManyUntil combinator accepts three parsers, or references to
// parsers, namely opScan, sepScan and untilScan, where opScan parser
// will be used to match input string and contruct ParsecNode,
// and sepScan parser will be used to match input string and
// ignore the matched string. If sepScan parser is not
// supplied, then opScan parser will be applied on the input
// until it fails.
//
// The process of matching opScan parser and sepScan parser
// will continue in a loop until either one of them fails on
// the input stream or untilScan matches.
//
// For every successful match of opScan, the returned
// ParsecNode from matching parser will be accumulated and
// passed as argument to Nodify callback. If there is not a
// single match for opScan, then ManyUntil will fail without
// consuming the input.
func ManyUntil(callb Nodify, parsers ...interface{}) Parser {
var opScan, sepScan, untilScan interface{}
switch l := len(parsers); l {
case 2:
opScan, untilScan = parsers[0], parsers[1]
case 3:
opScan, sepScan, untilScan = parsers[0], parsers[1], parsers[2]
default:
panic(fmt.Errorf("ManyUntil parser doesn't accept %v parsers", l))
}
return func(s Scanner) (ParsecNode, Scanner) {
var n ParsecNode
var e ParsecNode
ns := make([]ParsecNode, 0)
news := s.Clone()
for {
if e, _ = doParse(untilScan, news.Clone()); e != nil {
break
}
if n, news = doParse(opScan, news); n == nil {
break
}
ns = append(ns, n)
if sepScan != nil {
if n, news = doParse(sepScan, news); n == nil {
break
}
}
}
if len(ns) > 0 {
if node := docallback(callb, ns); node != nil {
return node, news
}
}
return nil, s
}
}
// Maybe combinator accepts a single parser, or reference to
// a parser, and tries to match the input stream with it. If
// parser fails to match the input, returns MaybeNone.
func Maybe(callb Nodify, parser interface{}) Parser {
return func(s Scanner) (ParsecNode, Scanner) {
n, news := doParse(parser, s.Clone())
if n == nil {
return MaybeNone("missing"), s
}
if node := docallback(callb, []ParsecNode{n}); node != nil {
return node, news
}
return MaybeNone("missing"), s
}
}
//----------------
// Local functions
//----------------
func doParse(parser interface{}, s Scanner) (ParsecNode, Scanner) {
switch p := parser.(type) {
case Parser:
return p(s)
case *Parser:
return (*p)(s)
default:
panic(fmt.Errorf("type of parser `%T` not supported", parser))
}
}
func docallback(callb Nodify, ns []ParsecNode) ParsecNode {
if callb != nil {
return callb(ns)
}
return ns
}