aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAnhgelus Morhtuuzh <william@herges.fr>2025-11-10 17:31:41 +0100
committerAnhgelus Morhtuuzh <william@herges.fr>2025-11-10 17:31:41 +0100
commit4840f480c8f255a6cf3b4eed291a00cea76b0cac (patch)
treed9baf898c1e961bb8ee5d181c38cce93b062129b /lib
parent037094a928653ed27f1f9d5497f637af1c5380e0 (diff)
feat(calc): supports function definition and evaluation
Diffstat (limited to 'lib')
-rw-r--r--lib/elixir_math_parser.ex40
-rw-r--r--lib/math/function.ex28
-rw-r--r--lib/math/rational.ex5
3 files changed, 70 insertions, 3 deletions
diff --git a/lib/elixir_math_parser.ex b/lib/elixir_math_parser.ex
index 286829e..b7f080c 100644
--- a/lib/elixir_math_parser.ex
+++ b/lib/elixir_math_parser.ex
@@ -5,6 +5,7 @@ defmodule ElixirMathParser do
alias ElixirMathParser.Math.Rational
alias ElixirMathParser.Math.Calc
alias ElixirMathParser.Math.Conversion
+ alias ElixirMathParser.Math.Function
defp reduce_to_value({:int, _line, value}, _state) do
{:ok, Rational.new(value)}
@@ -76,11 +77,35 @@ defmodule ElixirMathParser do
end
end
+ defp reduce_to_value({:eval_func, {:var, line, var}, params}, state) do
+ if !Map.has_key?(state, var) do
+ {:error, line, "function " <> to_string(var) <> " not found"}
+ else
+ v = state[var]
+
+ # check if the mult is implicit
+ if Rational.is_rational(v) do
+ [head | _] = params
+ reduce_to_value({:mul_op, {:var, line, var}, head}, state)
+ else
+ params = Enum.map(params, fn v -> with {:ok, v} <- reduce_to_value(v, state), do: v end)
+
+ with {:ok, v} <- state[var] |> Function.eval(params) do
+ {:ok, v}
+ else
+ {:error, reason} -> {:error, line, reason}
+ {:error, line, reason} -> {:error, line, reason}
+ end
+ end
+ end
+ end
+
defp evaluate_tree([{:assign, {:var, line, lhs}, rhs} | tail], state) do
with {:ok, val} <- reduce_to_value(rhs, state) do
evaluate_tree(tail, Map.merge(state, %{lhs => val}))
else
{:error, reason} -> {:error, line, reason}
+ {:error, line, reason} -> {:error, line, reason}
end
end
@@ -92,6 +117,21 @@ defmodule ElixirMathParser do
end
end
+ defp evaluate_tree([{:assign_func, {:var, _line, name}, vars, expr} | tail], state) do
+ fun =
+ Function.new(
+ fn params, given ->
+ state =
+ Enum.reduce(given, state, fn {v, id}, acc -> Map.merge(acc, %{params[id] => v}) end)
+
+ reduce_to_value(expr, state)
+ end,
+ Enum.map(vars, fn {:var, _line, name} -> name end)
+ )
+
+ evaluate_tree(tail, Map.merge(state, %{name => fun}))
+ end
+
defp evaluate_tree([], state) do
{:ok, state}
end
diff --git a/lib/math/function.ex b/lib/math/function.ex
new file mode 100644
index 0000000..2acf2c4
--- /dev/null
+++ b/lib/math/function.ex
@@ -0,0 +1,28 @@
+defmodule ElixirMathParser.Math.Function do
+ alias ElixirMathParser.Math.Function
+
+ defstruct relation: nil, parameters: %{}
+
+ def is_function(val) when is_map(val) and is_map_key(val, :__struct__) and is_struct(val),
+ do: :erlang.map_get(:__struct__, val) == __MODULE__
+
+ def new(relation, parameters) do
+ params =
+ Enum.with_index(parameters)
+ |> Enum.reduce(%{}, fn {v, id}, acc -> Map.merge(acc, %{id => v}) end)
+
+ %Function{relation: relation, parameters: params}
+ end
+
+ def eval(fun, parameters) do
+ if Enum.count(parameters) != Enum.count(fun.parameters) do
+ {:error, "wrong count of parameters"}
+ else
+ params =
+ Enum.with_index(parameters)
+ |> Enum.reduce(%{}, fn {v, id}, acc -> Map.merge(acc, %{v => id}) end)
+
+ fun.relation.(fun.parameters, params)
+ end
+ end
+end
diff --git a/lib/math/rational.ex b/lib/math/rational.ex
index c3d94c9..e788883 100644
--- a/lib/math/rational.ex
+++ b/lib/math/rational.ex
@@ -83,9 +83,8 @@ defmodule ElixirMathParser.Math.Rational do
iex> Rational.is_rational("My quick brown fox")
false
"""
- defguard is_rational(val)
- when is_map(val) and is_map_key(val, :__struct__) and is_struct(val) and
- :erlang.map_get(:__struct__, val) == __MODULE__
+ def is_rational(val) when is_map(val) and is_map_key(val, :__struct__) and is_struct(val),
+ do: :erlang.map_get(:__struct__, val) == __MODULE__
@doc """
Creates a new Rational number.