AoC19/lib/intcode/expression_evaluator.ex

94 lines
1.9 KiB
Elixir

defmodule ExpressionEvaluator do
def eval(expr, vars) do
expr
|> String.to_charlist()
|> tokenize()
|> run(vars)
# |> parse()
# |> run(vars)
end
@digits ?0..?9
@alpha ?a..?z
@operators [?+, ?-, ?*]
def do_tokenize([first | _] = str) when first in @digits do
{digits, rest} = Enum.split_while(str, &(&1 in @digits))
num = digits |> to_string() |> String.to_integer()
{{:number, num}, rest}
end
def do_tokenize([?( | rest]) do
{:lparen, rest}
end
def do_tokenize([?) | rest]) do
{:rparen, rest}
end
def do_tokenize([op | rest]) when op in @operators do
atom = [op] |> to_string() |> String.to_atom()
{{:operator, atom}, rest}
end
def do_tokenize([first | _] = str) when first in @alpha do
{var, rest} = Enum.split_while(str, &(&1 in @alpha))
{{:variable, var}, rest}
end
def tokenize([]), do: []
def tokenize(str) do
{token, rest} =
str
|> Enum.drop_while(&(&1 == ?\s))
|> do_tokenize()
[token | tokenize(rest)]
end
# def to_rpn([{:number, _} = num | rest]) do
# [num | to_rpn(rest)]
# end
#
# def to_rpn([{:operator, _} = op | rest]) do
# [op | to_rpn(rest)]
# end
#
# def to_rpn
# def parse(tokens) do
# {expr, rest} = do_parse(tokens)
# [expr |
# end
def run([:lparen | _] = tokens, vars) do
paren_tokens =
tokens
|> Enum.drop(1)
|> Enum.reduce_while({1, []}, fn token, {count, paren_tokens} ->
count =
case token do
:lparen -> count + 1
:rparen -> count - 1
_ -> count
end
if count == 0 do
{:halt, Enum.reverse(paren_tokens)}
else
{:cont, {count, [token | paren_tokens]}}
end
end)
# paren_expr = run(paren_tokens)
rest_tokens = Enum.drop(tokens, length(paren_tokens))
paren_res = run(paren_tokens, vars)
run([{:number, paren_res} | rest_tokens])
end
end