2019-12-07 19:26:41 +00:00
|
|
|
defmodule ExpressionEvaluator do
|
|
|
|
import NimbleParsec
|
|
|
|
|
|
|
|
number = integer(min: 1)
|
|
|
|
|
2019-12-08 00:00:46 +00:00
|
|
|
variable = ascii_string([?a..?z, ?A..?Z, ?_], min: 1)
|
2019-12-07 19:26:41 +00:00
|
|
|
|
|
|
|
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))
|
|
|
|
|
2019-12-08 00:00:46 +00:00
|
|
|
def eval(expr, vars \\ %{})
|
|
|
|
|
|
|
|
def eval(expr, vars) when is_map(vars) do
|
|
|
|
eval(expr, fn name ->
|
|
|
|
Map.fetch!(vars, name)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
def eval(expr, get_var) when is_function(get_var) do
|
2019-12-07 19:26:41 +00:00
|
|
|
{:ok, expr, _, _, _, _} = parse(expr)
|
2019-12-08 00:00:46 +00:00
|
|
|
do_eval(expr, get_var)
|
2019-12-07 19:26:41 +00:00
|
|
|
end
|
|
|
|
|
2019-12-08 00:00:46 +00:00
|
|
|
def do_eval([{op, [a, b]}], get_var) do
|
|
|
|
do_op(op, do_eval_single(a, get_var), do_eval_single(b, get_var))
|
2019-12-07 19:26:41 +00:00
|
|
|
end
|
|
|
|
|
2019-12-08 00:00:46 +00:00
|
|
|
def do_eval([it], get_var) do
|
|
|
|
do_eval_single(it, get_var)
|
2019-12-07 19:26:41 +00:00
|
|
|
end
|
|
|
|
|
2019-12-08 00:00:46 +00:00
|
|
|
def do_eval_single({:neg, [it]}, get_var) do
|
|
|
|
-1 * do_eval_single(it, get_var)
|
2019-12-07 19:26:41 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def do_eval_single(it, _) when is_integer(it) do
|
|
|
|
it
|
|
|
|
end
|
|
|
|
|
2019-12-08 00:00:46 +00:00
|
|
|
def do_eval_single(it, get_var) when is_binary(it) do
|
|
|
|
get_var.(it)
|
2019-12-07 19:26:41 +00:00
|
|
|
end
|
|
|
|
|
2019-12-08 00:00:46 +00:00
|
|
|
def do_eval_single(it, get_var) do
|
|
|
|
do_eval([it], get_var)
|
2019-12-07 19:26:41 +00:00
|
|
|
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
|