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

Day 18 Part 2

This commit is contained in:
Jeremy Kaplan 2020-12-17 23:21:21 -08:00
commit 339fda2f90

View file

@ -112,6 +112,125 @@ defmodule NewMath do
end
end
defmodule NextMath 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
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
# Grammar
#
# Expr = Product
# Atom = '(' Expr ')' | integer
# Sum = Atom ( '+' Atom )*
# Product = Sum ( '*' Sum )*
def parse(tokens) do
{expr, []} = parse_expr(tokens)
expr
end
defp parse_expr(tokens) do
parse_product(tokens)
end
defp parse_product(tokens) do
{lexpr, remaining} = parse_sum(tokens)
parse_product_iter(remaining, lexpr)
end
defp parse_product_iter(tokens, lexpr) do
case tokens do
[:times | rest] ->
{rexpr, leftover} = parse_sum(rest)
product = %Times{lexpr: lexpr, rexpr: rexpr}
parse_product_iter(leftover, product)
_ ->
{lexpr, tokens}
end
end
defp parse_sum(tokens) do
{lexpr, remaining} = parse_atom(tokens)
parse_sum_iter(remaining, lexpr)
end
defp parse_sum_iter(tokens, lexpr) do
case tokens do
[:plus | rest] ->
{rexpr, leftover} = parse_atom(rest)
sum = %Plus{lexpr: lexpr, rexpr: rexpr}
parse_sum_iter(leftover, sum)
_ ->
{lexpr, tokens}
end
end
defp parse_atom([:lparen | tokens]) do
{expr, [:rparen | rest]} = parse_expr(tokens)
{expr, rest}
end
defp parse_atom([{:integer, value} | tokens]) do
{%Integer{value: value}, tokens}
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))
@ -130,6 +249,7 @@ defmodule Day18 do
defp test_input do
"""
1 + (2 * 3) + (4 * (5 + 6))
2 * 3 + (4 * 5)
5 + (8 * 3 + 9 + 3 * 4 * 3)
5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))
@ -143,6 +263,19 @@ defmodule Day18 do
|> Enum.map(&NewMath.evaluate/1)
|> Enum.sum()
end
def part2 do
read_input()
|> String.split("\n", trim: true)
|> Enum.map(fn line ->
line
|> NextMath.tokenize()
|> NextMath.parse()
end)
|> Enum.map(&NextMath.evaluate/1)
|> Enum.sum()
end
end
Day18.part1() |> IO.inspect()
Day18.part2() |> IO.inspect()