From 3b0cc79dee76244da55d2326cc4244a384d85e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Herg=C3=A8s?= Date: Sun, 9 Nov 2025 13:23:05 +0100 Subject: feat(calc)): supports factorial --- example/input.txt | 4 ++-- lib/elixir_math_parser.ex | 22 +++++++++++++++++++--- lib/main.ex | 1 + src/elixir_math_parser.yrl | 4 +++- src/elixir_math_parser_lexer.xrl | 1 + 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/example/input.txt b/example/input.txt index a32d267..7efcb9c 100644 --- a/example/input.txt +++ b/example/input.txt @@ -2,6 +2,6 @@ a = 7;; b = 4 (a + b) * 10 / 2 x = 20 -y = 3 -z = 2y +y = 2! +z = y/2 10 + 4(z / y)(x / 4) diff --git a/lib/elixir_math_parser.ex b/lib/elixir_math_parser.ex index f7f916f..863ecd8 100644 --- a/lib/elixir_math_parser.ex +++ b/lib/elixir_math_parser.ex @@ -10,9 +10,9 @@ defmodule ElixirMathParser do {:ok, value <~> 1} end - defp reduce_to_value({:var, _line, var}, state) do + defp reduce_to_value({:var, line, var}, state) do if !Map.has_key?(state, var) do - {:error, "value not found for " <> to_string(var)} + {:error, line, "value not found for " <> to_string(var)} else {:ok, state[var]} end @@ -46,9 +46,25 @@ defmodule ElixirMathParser do end end - defp evaluate_tree([{:assign, {:var, _line, lhs}, rhs} | tail], state) do + defp reduce_to_value({:factor_op, lhs}, state) do + with {:ok, op} <- reduce_to_value(lhs, state), + true <- Ratio.denominator(op) == 1, + true <- Ratio.numerator(op) >= 0 do + {:ok, factor(Ratio.numerator(op), 1)} + else + {:error, line, reason} -> {:error, line, reason} + false -> {:error, "must have a positive integer for the factorial"} + end + end + + defp factor(n, acc) when n > 0, do: factor(n - 1, acc * n) + defp factor(0, acc), do: acc + + 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} end end diff --git a/lib/main.ex b/lib/main.ex index 99c25ca..5e7cf55 100644 --- a/lib/main.ex +++ b/lib/main.ex @@ -11,6 +11,7 @@ defmodule ElixirMathParser.Main do case ElixirMathParser.process_tree(tree) do {:ok, _} -> :ok + {:error, line, reason} -> reason <> " (line " <> to_string(line) <> ")" {:error, reason} -> reason end end diff --git a/src/elixir_math_parser.yrl b/src/elixir_math_parser.yrl index 86b36c8..0808683 100644 --- a/src/elixir_math_parser.yrl +++ b/src/elixir_math_parser.yrl @@ -10,7 +10,7 @@ Terminals int var break - '+' '-' '*' '/' + '+' '-' '*' '/' '!' '=' '(' ')' . @@ -24,6 +24,7 @@ Left 300 '+'. Left 300 '-'. Left 400 '*'. Left 400 '/'. +Right 500 '!'. Left 600 '('. root -> statements : '$1'. @@ -46,6 +47,7 @@ expr -> exprs '-' exprs : {sub_op, '$1', '$3'}. expr -> exprs '*' exprs : {mul_op, '$1', '$3'}. expr -> exprs '/' exprs : {div_op, '$1', '$3'}. expr -> '(' exprs ')' : '$2'. +expr -> expr '!' : {factor_op, '$1'}. Erlang code. diff --git a/src/elixir_math_parser_lexer.xrl b/src/elixir_math_parser_lexer.xrl index facf323..5bdcbef 100644 --- a/src/elixir_math_parser_lexer.xrl +++ b/src/elixir_math_parser_lexer.xrl @@ -13,6 +13,7 @@ Rules. \= : {token, {'=', TokenLine}}. \( : {token, {'(', TokenLine}}. \) : {token, {')', TokenLine}}. +! : {token, {'!', TokenLine}}. {BREAK}+ : {token, {break, TokenLine}}. {NAME} : {token, {var, TokenLine, TokenChars}}. {INT} : {token, {int, TokenLine, TokenChars}}. -- cgit v1.2.3