forked from markhibberd/introduction-to-fp-in-scala
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Parser.scala
406 lines (373 loc) · 10.4 KB
/
Parser.scala
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
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
package challenge
import intro._
/**
* A data type that just makes it easier to work with parse states.
*
* This is effectively just a tuple, with named accessors.
*/
case class ParseState[A](input: String, value: A)
/**
* A parser is a function from an input string,
*/
case class Parser[A](run: String => Result[ParseState[A]]) {
/**
* Return a parser with the function `f` applied to the
* output of that parser.
*
* scala> (Parser.value(5) map (x => x + 7)).run("hello")
* = Ok(ParseState(hello,12))
*
* scala> (Parser.failed[Int](NotEnoughInput) map (x => x + 7)).run("hello")
* = Fail(NotEnoughInput)
*/
def map[B](f: A => B): Parser[B] =
???
/**
* Return a parser that feeds its input into this parser, and
*
* - if that parser succeeds, apply its result to function f, and
* then run the resultant parser with the updated input
*
* - if that parser fails with an error, return a parser with
* that error
*
* scala> (Parser.value(5) flatMap (x => Parser.value(x + 7))).run("hello")
* = Ok(ParseState(hello,12))
*
* scala> (Parser.failed[Int](NotEnoughInput) flatMap (x => Parser.value(x + 7))).run("hello")
* = Fail(NotEnoughInput)
*/
def flatMap[B](f: A => Parser[B]): Parser[B] =
???
/**
* Anonymous flatMap.
*
* Return a parser that feeds its input into this parser, and
*
* - if that parser succeeds, run the next parser with the updated input
*
* - if that parser fails with an error, return a parser with that error
*
* scala> (Parser.value(5) >>> Parser.value(7)).run("hello")
* = Ok(ParseState(hello,7))
*
* scala> (Parser.failed(NotEnoughInput) >>> Parser.value(7)).run("hello")
* = Fail(NotEnoughInput)
*/
def >>>[B](parser: => Parser[B]): Parser[B] =
???
/**
* Choice.
*
* Return a parser that tries the first parser for a successful value.
*
* - if the first parser succeeds then use this parser
*
* - if the second parser succeeds then try the second parser
*
* scala> (Parser.value(5) ||| Parser.value(7)).run("hello")
* = Ok(ParseState(hello,5))
*
* scala> (Parser.failed[Int](NotEnoughInput) ||| Parser.value(7)).run("hello")
* = Ok(ParseState(hello,7))
*/
def |||(f: => Parser[A]): Parser[A] =
???
/**
* Run the parser and ignore the fail if there is any remaining output.
*
* scala> Parser.character.parseAll("a")
* = Ok('c')
*
* scala> Parser.character.parseAll("ab")
* = Fail(UnexpectedInput("b"))
*/
def parseAll(value: String): Result[A] =
???
}
object Parser {
/**
* Return a parser that always succeeds with the given value
* and consumes no input.
*
* scala> Parser.value(5).run("hello")
* = Ok(ParseState(hello, 5))
*/
def value[A](a: A): Parser[A] =
???
/**
* Return a parser that always fails with the given Error.
*
* scala> Parser.failed(NotEnoughInput).run("hello")
* = Fail(NotEnoughInput)
*
*/
def failed[A](error: Error): Parser[A] =
???
/**
* Return a parser that succeeds with a character off the input
* or fails with NotEnoughInput if the input is empty.
*
* scala> Parser.character.run("hello")
* = Ok(ParseState(ello, h))
*
* scala> Parser.character.run("")
* = Fail(NotEnoughInput)
*/
def character: Parser[Char] =
???
/**
* Return a parser that continues producing a list of values from the
* given parser.
*
* scala> Parser.list(Parser.character).run("hello")
* = Ok(ParseState(,List(h,e,l,l,o)))
*
* scala> Parser.list(Parser.character).run("")
* = Ok(ParseState(,List()))
*/
def list[A](parser: Parser[A]): Parser[List[A]] =
???
/**
* Return a parser that produces at least one value from the
* given parser then continues producing a list of values from
* the given parser (to ultimately produce a non-empty list).
*
* The returned parser fails if the input is empty.
*
* scala> Parser.list1(Parser.character).run("hello")
* = Ok(ParseState(,List(h,e,l,l,o)))
*
* scala> Parser.list1(Parser.character).run("")
* = Fail(NotEnoughInput)
*/
def list1[A](parser: Parser[A]): Parser[List[A]] =
???
/**
* Return a parser that produces a character but fails if
*
* - The input is empty, or
*
* - The character does not satisfy the given predicate
*
* scala> Parser.satisfy(c => c == 'h').run("hello")
* = Ok(ParseState(ello,h))
*/
def satisfy(pred: Char => Boolean): Parser[Char] =
???
/**
* Return a parser that produces a character but fails if
*
* - The input is empty, or
*
* - The character does not match the given character
*
* scala> Parser.is('h').run("hello")
* = Ok(ParseState(ello,h))
*/
def is(char: Char): Parser[Char] =
???
/**
* Return a parser that produces a character between '0' and '9'
* but fails if
*
* - The input is empty, or
*
* - The produced character is not a digit
*
* scala> Parser.digit.run("123hello")
* = Ok(ParseState(23hello,1))
*
* scala> Parser.digit.run("hello")
* = Fail(UnexpectedInput(h))
*/
def digit: Parser[Char] =
???
/**
* Return a parser that produces zero or a positive integer but fails if
*
* - The input is empty, or
*
* - The input does not produce a value series of digits
*
* scala> Parser.natural.run("123hello")
* = Ok(ParseState(hello, 123))
*
* scala> Parser.natural.run("hello")
* = Fail(UnexpectedInput(h))
*/
def natural: Parser[Int] =
???
/**
* Return a parser that produces a space character but fails if
*
* - The input is empty, or
*
* - The produced character is not a space
*
* scala> Parser.space.run(" hello")
* = Ok(ParseState(hello, ))
*
* scala> Parser.space.run("hello")
* = Fail(UnexpectedInput(h))
*/
def space: Parser[Char] =
???
/**
* Return a parse that produces one of more space characters
* (consuming until the first non-space) but fails if
*
* - The input is empty, or
*
* - The first produced character is not a space
*
* scala> Parser.spaces1.run(" hello")
* = Ok(ParseState(hello, ))
*
* scala> Parser.spaces1.run("hello")
* = Fail(UnexpectedInput(h))
*
*/
def spaces1: Parser[String] =
???
/**
* Return a parser that produces a lower-case character but fails if
*
* - The input is empty, or
*
* - The first produced character is not lower-case
*
* scala> Parser.lower.run("hello")
* = Ok(ParseState(ello,h))
*
* scala> Parser.lower.run("Hello")
* = Fail(UnexpectedInput(H))
*/
def lower: Parser[Char] =
???
/**
* Return a parser that produces an upper-case character but fails if
*
* - The input is empty, or
*
* - The first produced character is not upper-case
*
* scala> Parser.upper.run("Hello")
* = Ok(ParseState(ello,H))
*
* scala> Parser.upper.run("hello")
* = Fail(UnexpectedInput(h))
*/
def upper: Parser[Char] =
???
/**
* Return a parser that produces an alpha character but fails if
*
* - The input is empty, or
*
* - The first produced character is not alpha
*
* scala> Parser.alpha.run("hello")
* = Ok(ParseState(ello,h))
*
* scala> Parser.alpha.run("?hello")
* = Fail(UnexpectedInput(?))
*/
def alpha: Parser[Char] =
???
/**
* Return a parser that sequences the given list of parsers by
* producing all their results but fails on the first failing
* parser of the list.
*
* scala> Parser.sequence(List(Parser.character, Parser.character, Parser.character)).run("hello")
* = Ok(ParseState(lo,List(h, e, l)))
*
* scala> Parser.sequence(List(Parser.character, (Parser.failed(NotEnoughInput): Parser[Char]), Parser.character)).run("hello")
* = Fail(NotEnoughInput)
*/
def sequence[A](parsers: List[Parser[A]]): Parser[List[A]] =
???
/**
* Return a parser that produces the given number of values of
* the given parser. This fails if the given parser fails in the
* attempt to produce the given number of values.
*
*
* scala> Parser.thisMany(5, Parser.character).run("hello")
* = Ok(ParseState(,List(h, e, l, l, o)))
*
* scala> Parser.thisMany(6, Parser.character).run("hello")
* = Fail(NotEnoughInput)
*/
def thisMany[A](n: Int, parse: Parser[A]): Parser[List[A]] =
???
}
/**
* *Challenge* Parse a naive personel record.
*
* We have a set of personel records with a "special" format.
*
* Produce a person parser for a record.
*/
object PersonParser {
/*
* A data structure representing a person with the following attributes:
* - name: non empty string that starts with a capital letter
* - age: positive integer
* - address: non empty string that starts with a capital letter
* - phone: string of digits, dots or hyphens that must start with a
* digit and end with a hash (#)
*/
case class Person(name: String, age: Int, phone: String, address: Address)
/*
* A data structure representing an address with the following attributes:
* - number: positive integer
* - street: non empty string
*/
case class Address(number: Int, street: String)
/**
* Parse a name, which is a non-empty string that starts with a capital letter.
*/
def nameParser: Parser[String] =
???
/**
* Parse a phone number, which is a string of digits, dots or hyphens that
* starts with a digit and ends with a hash (#).
*/
def phoneParser: Parser[String] =
???
/**
* An address is a positive street number and a non empty string for the
* street name.
*/
def addressParser: Parser[Address] =
???
/**
* An person record is the following parts, each seperated by one or more spaces.
*
* <name> <age> <phone> <address>
*
* scala> PersonParser.personParser.run("Homer 39 555.123.939# 742 evergreen")
* = Ok(ParseState(,Person(Homer,39,555.123.939#,Address(742,evergreen))))
*/
def personParser: Parser[Person] =
???
/**
* Parse all records.
*
* Example usage:
*
* PersonParser.parseAll(PersonParser.Data)
*
* Hint: Use Parser.sequence
*/
def parseAll(data: List[String]): Result[List[Person]] =
???
def Data = List(
"Fred 32 123.456-1213# 301 cobblestone"
, "Barney 31 123.456.1214# 303 cobblestone"
, "Homer 39 555.123.939# 742 evergreen"
, "Flanders 39 555.123.939# 744 evergreen"
)
}