AoC19/lib/intcode/expression_evaluator.ex

99 lines
2.1 KiB
Elixir

defmodule ExpressionEvaluator do
import NimbleParsec
number = integer(min: 1)
variable = ascii_string([?a..?z, ?_], min: 1)
whitespace = ascii_string([?\s], min: 1)
factor =
choice([
ignore(ascii_char([?(]))
|> concat(parsec(:expr))
|> ignore(ascii_char([?)])),
number,
variable,
ignore(ascii_char([?-]))
|> concat(number)
|> tag(:neg),
ignore(ascii_char([?-]))
|> concat(variable)
|> tag(:neg)
])
defcombinatorp(
:term,
choice([
factor
|> optional(ignore(whitespace))
|> ignore(ascii_char([?*]))
|> optional(ignore(whitespace))
|> concat(parsec(:term))
|> tag(:mul),
factor
|> optional(ignore(whitespace))
|> ignore(ascii_char([?/]))
|> optional(ignore(whitespace))
|> concat(parsec(:term))
|> tag(:div),
factor
])
)
defcombinatorp(
:expr,
choice([
parsec(:term)
|> optional(ignore(whitespace))
|> ignore(ascii_char([?+]))
|> optional(ignore(whitespace))
|> concat(parsec(:expr))
|> tag(:add),
parsec(:term)
|> optional(ignore(whitespace))
|> ignore(ascii_char([?-]))
|> optional(ignore(whitespace))
|> concat(parsec(:expr))
|> tag(:sub),
parsec(:term)
])
)
defparsec(:parse, parsec(:expr))
def eval(expr, vars \\ %{}) do
{:ok, expr, _, _, _, _} = parse(expr)
do_eval(expr, vars)
end
def do_eval([{op, [a, b]}], vars) do
do_op(op, do_eval_single(a, vars), do_eval_single(b, vars))
end
def do_eval([it], vars) do
do_eval_single(it, vars)
end
def do_eval_single({:neg, [it]}, vars) do
-1 * do_eval_single(it, vars)
end
def do_eval_single(it, _) when is_integer(it) do
it
end
def do_eval_single(it, vars) when is_binary(it) do
Map.fetch!(vars, it)
end
def do_eval_single(it, vars) do
do_eval([it], vars)
end
def do_op(:add, a, b), do: a + b
def do_op(:sub, a, b), do: a - b
def do_op(:mul, a, b), do: a * b
def do_op(:div, a, b), do: floor(a / b)
end