Watch
1
0
Fork
You've already forked advent-of-code
0

Day 18 Part 1

This commit is contained in:
Jeremy Kaplan 2020-12-17 22:03:59 -08:00
commit beba85f7ef

148
2020/day18/day18.ex Normal file
View file

@ -0,0 +1,148 @@
defmodule NewMath do
defmodule Integer do
@enforce_keys [:value]
defstruct [:value]
end
defmodule Plus do
@enforce_keys [:lexpr, :rexpr]
defstruct [:lexpr, :rexpr]
end
defmodule Times do
@enforce_keys [:lexpr, :rexpr]
defstruct [:lexpr, :rexpr]
end
defmodule Group do
@enforce_keys [:expr]
defstruct [:expr]
end
defmodule Expr do
@enforce_keys [:expr]
defstruct [:expr]
end
def tokenize(text) do
scan(String.codepoints(text), [])
end
defp scan([], tokens) do
Enum.reverse(tokens)
end
defp scan(text = [first | rest], tokens) do
case first do
"(" ->
scan(rest, [:lparen | tokens])
")" ->
scan(rest, [:rparen | tokens])
"*" ->
scan(rest, [:times | tokens])
"+" ->
scan(rest, [:plus | tokens])
" " ->
scan(rest, tokens)
_ ->
{digits, extra} = Enum.split_while(text, fn char -> String.match?(char, ~r/\d/) end)
scan(extra, [{:integer, String.to_integer(Enum.join(digits))} | tokens])
end
end
def parse(tokens) do
[%Expr{expr: expr}] = parse_iter(tokens, [])
expr
end
defp parse_iter([], stack) do
stack
end
defp parse_iter(tokens, stack) do
{rest, new_stack} = shift(tokens, stack)
reduced = reduce(new_stack)
parse_iter(rest, reduced)
end
defp shift([token | rest], stack) do
gram =
case token do
{:integer, value} -> %Expr{expr: %Integer{value: value}}
_ -> token
end
{rest, [gram | stack]}
end
defp reduce(stack) do
case stack do
[:rparen, %Expr{expr: expr}, :lparen | rest] ->
new_expr = %Expr{expr: expr}
reduce([new_expr | rest])
[%Expr{expr: rexpr}, :plus, %Expr{expr: lexpr} | rest] ->
new_expr = %Expr{expr: %Plus{lexpr: lexpr, rexpr: rexpr}}
reduce([new_expr | rest])
[%Expr{expr: rexpr}, :times, %Expr{expr: lexpr} | rest] ->
new_expr = %Expr{expr: %Times{lexpr: lexpr, rexpr: rexpr}}
reduce([new_expr | rest])
_ ->
stack
end
end
def evaluate(%Plus{lexpr: lexpr, rexpr: rexpr}) do
evaluate(lexpr) + evaluate(rexpr)
end
def evaluate(%Times{lexpr: lexpr, rexpr: rexpr}) do
evaluate(lexpr) * evaluate(rexpr)
end
def evaluate(%Integer{value: value}) do
value
end
end
defmodule Day18 do
defp read_input do
Path.expand('input', Path.dirname(__ENV__.file))
|> File.read!()
end
defp parse_expressions(text) do
String.split(text, "\n", trim: true)
|> Enum.map(&parse_expression/1)
end
defp parse_expression(line) do
NewMath.tokenize(line)
|> NewMath.parse()
end
defp test_input do
"""
2 * 3 + (4 * 5)
5 + (8 * 3 + 9 + 3 * 4 * 3)
5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))
((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2
"""
end
def part1 do
read_input()
|> parse_expressions()
|> Enum.map(&NewMath.evaluate/1)
|> Enum.sum()
end
end
Day18.part1() |> IO.inspect()