aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--example/input.txt12
-rw-r--r--lib/elixir_math_parser.ex40
-rw-r--r--lib/math/function.ex28
-rw-r--r--lib/math/rational.ex5
-rw-r--r--src/elixir_math_parser.yrl21
-rw-r--r--src/elixir_math_parser_lexer.xrl6
6 files changed, 99 insertions, 13 deletions
diff --git a/example/input.txt b/example/input.txt
index 9c49d56..bd668e0 100644
--- a/example/input.txt
+++ b/example/input.txt
@@ -1,9 +1,9 @@
a = 10.05;; b = .95
(a + b) * 10 / 2
-x = 2_0
-y = 2!
-z = y/2
-10 + 4(z / y)(x / 4)
-
-x^(-y)
+f: x = x + 1
+x = 2
+# is 24 + 1 = 25
+f(24)
+# must be 10
+x(5)
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.
diff --git a/src/elixir_math_parser.yrl b/src/elixir_math_parser.yrl
index 635ebd3..a48b57b 100644
--- a/src/elixir_math_parser.yrl
+++ b/src/elixir_math_parser.yrl
@@ -4,15 +4,21 @@ Nonterminals
statements
expr
exprs
+ % function specific
+ vars params
.
Terminals
int float
var
break
+ % eval
'+' '-' '*' '/' '!' '^'
- '='
'(' ')'
+ % assign
+ '='
+ % function specific
+ ':' ','
.
Rootsymbol
@@ -37,13 +43,22 @@ statements -> break : [].
statement -> var '=' exprs : {assign, '$1', '$3'}.
statement -> exprs : {eval, '$1'}.
+statement -> var ':' vars '=' exprs : {assign_func, '$1', '$3', '$5'}.
+
+vars -> var : ['$1'].
+vars -> var ',' vars : ['$1' | '$3'].
-exprs -> expr : '$1'.
+exprs -> expr : '$1'.
exprs -> expr exprs : {mul_op, '$1', '$2'}.
+params -> expr : ['$1'].
+params -> expr ',' : ['$1'].
+params -> expr ',' params : ['$1' | '$3'].
+
expr -> int : unwrap('$1').
expr -> float : unwrap('$1').
expr -> var : '$1'.
+
expr -> exprs '+' exprs : {add_op, '$1', '$3'}.
expr -> exprs '-' exprs : {sub_op, '$1', '$3'}.
expr -> exprs '*' exprs : {mul_op, '$1', '$3'}.
@@ -53,6 +68,8 @@ expr -> exprs '^' exprs : {exp_op, '$1', '$3'}.
expr -> '(' exprs ')' : '$2'.
expr -> '-' exprs : {sub_op, {int, 0, 0}, '$2'}.
+expr -> var '(' params ')' : {eval_func, '$1', '$3'}.
+
Erlang code.
% 95 is the unicode of "_"
diff --git a/src/elixir_math_parser_lexer.xrl b/src/elixir_math_parser_lexer.xrl
index 4073958..fdb661f 100644
--- a/src/elixir_math_parser_lexer.xrl
+++ b/src/elixir_math_parser_lexer.xrl
@@ -4,7 +4,7 @@ FLOAT = [0-9_]*\.[0-9]+
NAME = [a-zA-Z_][a-zA-Z0-9_]*
WHITESPACE = [\s\t\r]
COMMENT = #[^\n]*\n?
-BREAK = [\n;;]
+BREAK = [\n;;]+
Rules.
\+ : {token, {'+', TokenLine}}.
@@ -16,7 +16,9 @@ Rules.
\) : {token, {')', TokenLine}}.
! : {token, {'!', TokenLine}}.
\^ : {token, {'^', TokenLine}}.
-{BREAK}+ : {token, {break, TokenLine}}.
+\: : {token, {':', TokenLine}}.
+, : {token, {',', TokenLine}}.
+{BREAK} : {token, {break, TokenLine}}.
{NAME} : {token, {var, TokenLine, TokenChars}}.
{FLOAT} : {token, {float, TokenLine, TokenChars}}.
{INT} : {token, {int, TokenLine, TokenChars}}.