-module(calc).
-export([eval/1]).
%% num := [-] 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
num(Input) ->
num(Input, []).
num([Current|Input], Num) when ($0 =< Current) and ($9 >= Current) ->
num(Input, Num ++ [Current]);
num([Head|_], "-") ->
{error, {unexpected, list_to_atom([Head])}};
num(Input, Num) ->
{ok, {Input, list_to_integer(Num)}}.
%% bracket := [-] ( '(' sum '}' | num )
bracket([$-|Input]) ->
case bracket(Input) of
{ok, {Tail, Result}} ->
{ok, {Tail, -1 * Result}};
{error, Why} ->
{error, Why}
end;
bracket([$(|Input]) ->
case sum(Input) of
{ok, {Tail, Result}} ->
bracketCheck(Tail, Result);
{error, Why} ->
{error, Why}
end;
bracket(Input) ->
num(Input).
bracketCheck([$)|Tail], Result) ->
{ok, {Tail, Result}};
bracketCheck(_, _) ->
{error, missing_bracket}.
%% factor := num [ ^ factor ]
factor(Input) ->
case bracket(Input) of
{ok, {Tail, Number2}} ->
factorExtended(Tail, Number2);
{error, Why} ->
{error, Why}
end.
factor([Head|Input], Number1) ->
case bracket([Head|Input]) of
{ok, {Tail, Number2}} ->
Result = math:pow(Number1, Number2),
factorExtended(Tail, Result);
{error, Why} ->
{error, Why}
end;
factor([], _) ->
{error, unexpected_end_of_input}.
factorExtended([$ |Tail], Result) ->
factorExtended(Tail, Result);
factorExtended([$^|Tail], Result) ->
factor(Tail, Result);
factorExtended(Tail, Result) ->
{ok, {Tail, Result}}.
%% term := factor [ ( * | / ) term ]
term(Input) ->
case factor(Input) of
{ok, {Tail, Result}} ->
termExtended(Tail, Result);
{error, Why} ->
{error, Why}
end.
term([Head|Input], Number1, Operator) ->
case factor([Head|Input]) of
{ok, {Tail, Number2}} ->
case calc(Number1, Operator, Number2) of
{ok, Result} ->
termExtended(Tail, Result);
{error, Why} ->
{error, Why}
end;
{error, Why} ->
{error, Why}
end;
term([], _, _) ->
{error, unexpected_end_of_input}.
termExtended([$ |Tail], Result) ->
termExtended(Tail, Result);
termExtended([$*|Tail], Result) ->
term(Tail, Result, $*);
termExtended([$/|Tail], Result) ->
term(Tail, Result, $/);
termExtended(Tail, Result) ->
{ok, {Tail, Result}}.
%% sum := term [ ( + | - ) sum ]
sum([$+|Input]) ->
sum(Input);
sum(Input) ->
case term(Input) of
{ok, {Tail, Result}} ->
sumExtended(Tail, Result);
{error, Why} ->
{error, Why}
end.
sum([Head|Input], Number1, Operator) ->
case term([Head|Input]) of
{ok, {Tail, Number2}} ->
case calc(Number1, Operator, Number2) of
{ok, Result} ->
sumExtended(Tail, Result);
{error, Why} ->
{error, Why}
end;
{error, Why} ->
{error, Why}
end;
sum([], _, _) ->
{error, unexpected_end_of_input}.
sumExtended([$ |Tail], Result) ->
sumExtended(Tail, Result);
sumExtended([$+|Tail], Result) ->
sum(Tail, Result, $+);
sumExtended([$-|Tail], Result) ->
sum(Tail, Result, $-);
sumExtended(Tail, Result) ->
{ok, {Tail, Result}}.
%% helper function to do the calculations
calc(Number1, $+, Number2) ->
{ok, Number1 + Number2};
calc(Number1, $-, Number2) ->
{ok, Number1 - Number2};
calc(Number1, $*, Number2) ->
{ok, Number1 * Number2};
calc(Number1, $/, Number2) when (Number2 /= 0) ->
{ok, Number1 / Number2};
calc(_, $/, _) ->
{error, division_zero};
calc(_,_,_) ->
{error, invalid_operator}.
eval(Formular) ->
case sum([X || X <- Formular, X /= $ ]) of
{ok, {Tail, Result}} ->
evalCheck(Tail, Result);
{error, Why} ->
{error, Why}
end.
evalCheck([], Result) ->
{ok, Result};
evalCheck(Tail, _) ->
{error, {unexpected_garbage_at_end_of_input, Tail}}.