-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Parse.swift
235 lines (199 loc) · 8.33 KB
/
Parse.swift
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
// Copyright © 2019 Brad Howes. All rights reserved.
//
// This code uses ideas presented by POINT•FREE (https://www.pointfree.co) in their discussions on parsers. The
// videos are subscriber-based (and highly recommended), but the Swift playgrounds that were used and discussed
// can be found on Github at https://github.com/pointfreeco/episode-code-samples
/**
Pseudo-namespace for general parsing utilities.
*/
public enum Parse {
/**
Utility to scan over text and capture the result.
- parameter start: the iterator to use for characters
- parameter skipWS: if true, skip over whitespace before parsing
- parameter tokenizer: closure to execute to scan/parse the text
- parameter map: closure to execute to determine if the parse was successful and to generate an `OutputType`
instance if so
- returns: optional `OutputType` instance
*/
private static func parse<OutputType>(_ start: inout Source.Iterator, skipWS: Bool,
tokenizer: (inout Source.Iterator) -> Void,
map: (String)-> OutputType?) -> OutputType? {
if skipWS { start.skip { $0.isWhitespace } }
var it = start
tokenizer(&it)
guard let value = map(start.span(to: it)) else { return nil }
start = it
return value
}
/// Parser for decimal integer values
public static let int = Parser<Int> { it in
parse(&it, skipWS: true, tokenizer: { $0.skip { $0.isNumber } }) { Int($0) }
}
//assert(int.parse(" 123") == 123)
/// Parser for floating-point values
public static let double = Parser<Double> { it in
parse(&it, skipWS: true, tokenizer: { $0.skip { $0.isNumber || $0 == "." } }) { Double($0) }
}
//assert(double.parse(" 123.456 ") == 123.456)
/// Parser for a single character -- the result is just the value of the next character from the iterator.
public static let char = Parser<Character> { it in
guard let c = it.next() else { return nil }
return c
}
/**
Obtain a parser for literal value
- parameter p: the literal value to expect
- parameter optional: if true then succeed even if the literal does not exist
- parameter skipWS: if true (default) allow for whitespace characters before literal
- returns: new parser that matches on the given literal
*/
public static func lit(_ p: String, optional: Bool = false, skipWS: Bool = true) -> Parser<String> {
.init { it in
parse(&it, skipWS: skipWS, tokenizer: { $0.skip(count: p.count) }) { $0 == p || optional ? p : nil }
}
}
/**
Obtain a parser that matches a class of characters.
- parameter skipWS: if true (default) skip whitespace characters before parsing
- parameter p: the predicate function that indicates if a character belongs in the desired class
- returns: new parser for matching character classes
*/
public static func pat(skipWS: Bool = true, while p: @escaping (Character) -> Bool) -> Parser<String> {
.init { it in
parse(&it, skipWS: skipWS, tokenizer: { $0.skip(p) }) { !$0.isEmpty ? $0 : nil }
}
}
/**
Obtain a parser that always matches without consuming any text.
- parameter a: the value to return for the parser
- returns: the new parser
*/
public static func always<A>(_ a: A) -> Parser<A> { .init { _ in a } }
//always(123).parse("testing")
/**
Obtain a parser that succeeds when the first parser in a collection of parsers succeeds (at most one).
- parameter ps: the collection of parsers
- returns: new parser
*/
public static func first<A>(_ ps: [Parser<A>]) -> Parser<A> { first { ps } }
/**
Obtain a parser that succeeds when the first parser in a collection of parsers succeeds (at most one).
- parameter ps: the collection of parsers
- returns: new parser
*/
public static func first<A>(_ ps: Parser<A>...) -> Parser<A> { first { ps } }
/**
Obtain a parser that succeeds when the first parser in a collection of parsers succeeds (at most one). This variant
obtains the collection from the given closure, thus delaying evaluation of the collection until when it is actually
needed.
- parameter ps: closure to invoke to obtain the collection to iterate over
- returns: new parser
*/
public static func first<A>(_ ps: @escaping () -> [Parser<A>]) -> Parser<A> {
.init { it -> A? in
for p in ps() {
if let match = p.scanner(&it) {
print("first: match '\(match)' remaining: '\(it.remaining)'")
return match
}
}
print("first: nil")
return nil
}
}
/**
Obtain a parser that will match zero or more times with a given parser, with matches separated by a given separator.
- parameter p: the parser to match with
- parameter s: the separator to look for
- returns: the new parser
*/
public static func any<A>(_ p: Parser<A>, separatedBy s: Parser<String>) -> Parser<[A]> {
.init { it in
var rest = it
var matches: [A] = []
while let match = p.scanner(&it) {
print("any: match '\(match)' remaining: '\(it.remaining)'")
rest = it
matches.append(match)
if s.scanner(&it) == nil {
print("any: no separator")
break
}
}
it = rest
print("any: \(matches)")
return matches
}
}
/**
Parser for an optional item. Always results in an Array[A], but it will be empty if the parsing fails.
- parameter p: the parser to run internally.
- returns: new parser
*/
public static func optional<A>(_ p: Parser<A>) -> Parser<[A]> {
.init { it in
guard let match = p.scanner(&it) else { return [] }
return [match]
}
}
}
/**
Define zip operation on two Parser instances such that parsing is successful iff both parsers match on input in
sequential order.
- parameter a: first parser to execute
- parameter b: second parser to execute
- returns: new parser
*/
public func zip<A, B>(_ a: Parser<A>, _ b: Parser<B>) -> Parser<(A, B)> {
.init { it -> (A, B)? in
let original = it
guard let matchA = a.scanner(&it) else { return nil }
guard let matchB = b.scanner(&it) else {
it = original
return nil
}
return (matchA, matchB)
}
}
/**
Define zip operation on three Parser instances such that parsing is successful iff all parsers match on input in
sequential order.
- parameter a: first parser to execute
- parameter b: second parser to execute
- parameter c: third parser to execute
- returns: new parser
*/
public func zip<A, B, C>(_ a: Parser<A>, _ b: Parser<B>, _ c: Parser<C>) -> Parser<(A, B, C)> {
zip(a, zip(b, c)).map { a, rest in (a, rest.0, rest.1) }
}
/**
Define zip operation on four Parser instances such that parsing is successful iff all parsers match on input in
sequential order.
- parameter a: first parser to execute
- parameter b: second parser to execute
- parameter c: third parser to execute
- parameter d: fourth parser to execute
- returns: new parser
*/
public func zip<A, B, C, D>(_ a: Parser<A>, _ b: Parser<B>, _ c: Parser<C>, _ d: Parser<D>) -> Parser<(A, B, C, D)> {
zip(a, zip(b, c, d)).map { a, rest in (a, rest.0, rest.1, rest.2) }
}
public func zip<A, B, C, D, E>(_ a: Parser<A>, _ b: Parser<B>, _ c: Parser<C>, _ d: Parser<D>,
_ e: Parser<E>) -> Parser<(A, B, C, D, E)> {
zip(a, zip(b, c, d, e)).map { a, rest in (a, rest.0, rest.1, rest.2, rest.3) }
}
public func zip<A, B, C, D, E, F>(_ a: Parser<A>, _ b: Parser<B>, _ c: Parser<C>, _ d: Parser<D>,
_ e: Parser<E>, _ f: Parser<F>) -> Parser<(A, B, C, D, E, F)> {
zip(a, zip(b, c, d, e, f)).map { a, rest in (a, rest.0, rest.1, rest.2, rest.3, rest.4) }
}
public func zip<A, B, C, D, E, F, G>(_ a: Parser<A>, _ b: Parser<B>, _ c: Parser<C>, _ d: Parser<D>,
_ e: Parser<E>, _ f: Parser<F>, _ g: Parser<G>) ->
Parser<(A, B, C, D, E, F, G)> {
zip(a, zip(b, c, d, e, f, g)).map { a, rest in (a, rest.0, rest.1, rest.2, rest.3, rest.4, rest.5) }
}
public func zip<A, B, C, D, E, F, G, H>(_ a: Parser<A>, _ b: Parser<B>, _ c: Parser<C>, _ d: Parser<D>,
_ e: Parser<E>, _ f: Parser<F>, _ g: Parser<G>, _ h: Parser<H>) ->
Parser<(A, B, C, D, E, F, G, H)> {
zip(a, zip(b, c, d, e, f, g, h)).map { a, rest in (a, rest.0, rest.1, rest.2, rest.3, rest.4, rest.5, rest.6) }
}