-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathday_18.ex
64 lines (51 loc) · 1.87 KB
/
day_18.ex
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
defmodule AdventOfCode.Y2020.Day18 do
@moduledoc """
--- Day 18: Operation Order ---
Problem Link: https://adventofcode.com/2020/day/18
"""
alias AdventOfCode.Helpers.{InputReader, Transformers}
def input, do: InputReader.read_from_file(2020, 18)
def run(input \\ input()) do
{solve(parse(input, false)), solve(parse(input, true))}
end
def parse(input, p) do
for line <- Transformers.lines(input) do
line |> tokenize() |> parse_rule(p)
end
end
defp solve(expressions), do: Enum.sum(Enum.map(expressions, fn {d, _} -> eval(d) end))
@ops %{"*" => {:op, :*}, "+" => {:op, :+}, "(" => :lp, ")" => :rp, " " => :sp}
defp tokenize(expression) do
String.graphemes(expression)
|> Enum.map(&(Map.get(@ops, &1) || String.to_integer(&1)))
|> Enum.reject(&(&1 == :sp))
end
defp parse_rule({lhs, tokens}, precedence), do: parse_rule(lhs, tokens, precedence)
defp parse_rule(tokens, precedence), do: parse_rule(parse_term(tokens, precedence), precedence)
defp parse_rule(lhs, tokens, precedence) do
case tokens do
[] ->
{lhs, []}
[:rp | _] ->
{lhs, tokens}
[{:op, op} | tokens] when op == :+ or not precedence ->
{rhs, tokens} = parse_term(tokens, precedence)
parse_rule({op, [lhs, rhs]}, tokens, precedence)
[{:op, :*} | tokens] ->
{rhs, tokens} = parse_term(tokens, precedence)
{rhs, tokens} = parse_rule(rhs, tokens, precedence)
{{:*, [lhs, rhs]}, tokens}
end
end
defp parse_term(tokens, precedence) do
case tokens do
[:lp | tokens] ->
{term, [:rp | tokens]} = parse_rule(tokens, precedence)
{term, tokens}
[number | tokens] when is_number(number) ->
{number, tokens}
end
end
defp eval({op, args}), do: apply(Kernel, op, Enum.map(args, &eval/1))
defp eval(number) when is_integer(number), do: number
end