diff options
Diffstat (limited to 'aoc-2020-gleam/src/util/parser.gleam')
-rw-r--r-- | aoc-2020-gleam/src/util/parser.gleam | 82 |
1 files changed, 50 insertions, 32 deletions
diff --git a/aoc-2020-gleam/src/util/parser.gleam b/aoc-2020-gleam/src/util/parser.gleam index c16dc60..321e459 100644 --- a/aoc-2020-gleam/src/util/parser.gleam +++ b/aoc-2020-gleam/src/util/parser.gleam @@ -9,7 +9,7 @@ import ext/intx const eof: String = "end of input" -fn quoted(grapheme: String) -> String { +fn quot(grapheme: String) -> String { "'" <> grapheme <> "'" } @@ -59,8 +59,8 @@ pub fn grapheme_literal(expected: String) -> Parser(String) { Parser(fn(input) { case run(any_grapheme(), on: input) { Ok(#(value, _)) as ok if value == expected -> ok - Ok(#(value, _)) -> Error(InvalidInput(quoted(expected), found: value)) - Error(_) -> Error(InvalidInput(quoted(expected), found: eof)) + Ok(#(value, _)) -> Error(InvalidInput(quot(expected), found: quot(value))) + Error(_) -> Error(InvalidInput(quot(expected), found: eof)) } }) } @@ -83,6 +83,15 @@ pub fn then_skip(first: Parser(a), second: Parser(b)) -> Parser(a) { |> map(with: pair.first) } +pub fn then_third(two: Parser(#(a, b)), third: Parser(c)) -> Parser(#(a, b, c)) { + two + |> then(third) + |> map(with: fn(tuple) { + let #(#(p0, p1), p2) = tuple + #(p0, p1, p2) + }) +} + pub fn or(first: Parser(a), else second: Parser(a)) -> Parser(a) { Parser(fn(input) { first @@ -94,7 +103,7 @@ pub fn or(first: Parser(a), else second: Parser(a)) -> Parser(a) { pub fn any(of parsers: List(Parser(a))) -> Parser(a) { parsers |> list.reduce(with: or) - |> result.unwrap(or: failing(InvalidParser)) + |> result.unwrap(or: failing(with: InvalidParser)) } pub fn digit() -> Parser(String) { @@ -113,66 +122,67 @@ pub fn map(parser: Parser(a), with mapper: fn(a) -> b) -> Parser(b) { }) } -fn succeeding(value: a) -> Parser(a) { - Parser(fn(input) { Ok(#(value, input)) }) +pub fn map2(parser: Parser(#(a, b)), with mapper: fn(a, b) -> c) -> Parser(c) { + parser + |> map(with: fn(args) { mapper(args.0, args.1) }) } -fn failing(error: ParseError) -> Parser(a) { - Parser(fn(_) { Error(error) }) +pub fn map3( + parser: Parser(#(a, b, c)), + with mapper: fn(a, b, c) -> d, +) -> Parser(d) { + parser + |> map(with: fn(args) { mapper(args.0, args.1, args.2) }) } -fn apply(fn_parser: Parser(fn(a) -> b), value_parser: Parser(a)) -> Parser(b) { - fn_parser - |> then(value_parser) - |> map(fn(pair) { - let #(f, x) = pair - f(x) - }) +fn succeeding(with value: a) -> Parser(a) { + Parser(fn(input) { Ok(#(value, input)) }) +} + +fn failing(with error: ParseError) -> Parser(a) { + Parser(fn(_) { Error(error) }) } -fn lift2(func: fn(a, b) -> c) -> fn(Parser(a), Parser(b)) -> Parser(c) { +fn lift2(function: fn(a, b) -> c) -> fn(Parser(a), Parser(b)) -> Parser(c) { fn(x_parser, y_parser) { - func - |> function.curry2 + function |> succeeding - |> apply(x_parser) - |> apply(y_parser) + |> then(x_parser) + |> then_third(y_parser) + |> map3(with: fn(f, x, y) { f(x, y) }) } } pub fn sequence(of parsers: List(Parser(a))) -> Parser(List(a)) { - let prepend_parser = lift2(list.prepend) + let prepend_parser = lift2(fn(x, xs) { [x, ..xs] }) case parsers { - [] -> succeeding([]) + [] -> succeeding(with: []) [head, ..tail] -> tail |> sequence - |> prepend_parser(head) + |> prepend_parser(head, _) } } -fn parse_zero_or_more( - input: String, - with parser: Parser(a), -) -> #(List(a), String) { +fn do_zero_or_more(input: String, with parser: Parser(a)) -> #(List(a), String) { case run(parser, on: input) { Ok(#(value, rest)) -> { - let #(previous, rest) = parse_zero_or_more(rest, with: parser) + let #(previous, rest) = do_zero_or_more(rest, with: parser) #([value, ..previous], rest) } Error(_) -> #([], input) } } -pub fn many(parser: Parser(a)) -> Parser(List(a)) { - Parser(fn(input) { Ok(parse_zero_or_more(input, with: parser)) }) +pub fn many(of parser: Parser(a)) -> Parser(List(a)) { + Parser(fn(input) { Ok(do_zero_or_more(input, with: parser)) }) } -pub fn many1(parser: Parser(a)) -> Parser(List(a)) { +pub fn many1(of parser: Parser(a)) -> Parser(List(a)) { Parser(fn(input) { use parsed <- result.then(run(parser, on: input)) let #(value, rest) = parsed - let #(previous, rest) = parse_zero_or_more(rest, with: parser) + let #(previous, rest) = do_zero_or_more(rest, with: parser) Ok(#([value, ..previous], rest)) }) } @@ -188,3 +198,11 @@ pub fn any_string() -> Parser(String) { |> many |> map(with: string.concat) } + +pub fn string_literal(expected: String) -> Parser(String) { + expected + |> string.to_graphemes() + |> list.map(with: grapheme_literal) + |> sequence + |> map(with: string.concat) +} |