AdventOfCode/2020/elixir/day18/day18part2.exs

56 lines
1.8 KiB
Elixir

defmodule Day18Part2 do
def run do
File.stream!("input")
|> Stream.map(&tokenize/1)
|> Stream.map(&to_ast/1)
|> Stream.map(&evaluate/1)
|> Enum.sum()
|> IO.puts()
end
def tokenize(line) do
line
|> String.graphemes()
|> Stream.reject(fn char -> char in [" ", "\n"] end)
|> Enum.map(fn
symbol when symbol in ["*", "+", "(", ")"] -> symbol
num -> {:num, String.to_integer(num)}
end)
end
def to_ast(line), do: to_ast(line, nil, nil, [])
def to_ast([], ast, nil, []), do: ast
def to_ast([], ast, nil, later), do: ast_later(Enum.reverse([ast | later]), nil, nil)
def to_ast(["+" | rest], left, nil, later), do: to_ast(rest, left, "+", later)
def to_ast(["*" | rest], left, nil, later), do: to_ast(rest, nil, nil, ["*", left | later])
def to_ast(["(" | rest], nil, op, later) do
{new_left, rest} = to_ast(rest)
to_ast(rest, new_left, op, later)
end
def to_ast(["(" | rest], left, op, later) do
{new_left, rest} = to_ast(rest)
to_ast([left | rest], new_left, op, later)
end
def to_ast([")" | rest], ast, nil, later) do
ast = ast_later(Enum.reverse([ast | later]), nil, nil)
{ast, rest}
end
def to_ast([ast | rest], nil, nil, later), do: to_ast(rest, ast, nil, later)
def to_ast([right | rest], left, "+", later), do: to_ast(rest, {"+", left, right}, nil, later)
def ast_later([], ast, nil), do: ast
def ast_later(["*" | rest], left, nil), do: ast_later(rest, left, "*")
def ast_later([left | rest], nil, nil), do: ast_later(rest, left, nil)
def ast_later([right | rest], left, "*"), do: ast_later(rest, {"*", left, right}, nil)
def evaluate({:num, num}), do: num
def evaluate({"+", left, right}), do: evaluate(left) + evaluate(right)
def evaluate({"*", left, right}), do: evaluate(left) * evaluate(right)
end
Day18Part2.run()