I’m (still) learning Erlang. This is my solution with the given operators and integer numbers. I’m quite satisfied:
-module( calc ). -export( [ calc/1 ] ). -include_lib("eunit/include/eunit.hrl"). % expr := term [+|- term]* % term := factor [*|/ factor]* % factor := number % number := -?digit+ skip_ws( [ $\ | Rest ] ) -> skip_ws (Rest ); skip_ws( [ $\t | Rest ] ) -> skip_ws (Rest ); skip_ws( Rest ) -> Rest. parse_number( [ $- | S ] ) -> parse_number( S, undef, -1 ); parse_number( S ) -> parse_number( S, undef, 1 ). parse_number( [ C | Rest ], Value, Sign ) when C >= $0, C =< $9 -> NewValue = case Value of undef -> 0; _ -> Value end * 10 + ( C - $0 ), parse_number( Rest, NewValue, Sign ); parse_number( Rest, Value, Sign ) -> case Value of undef -> { invalid, "" }; _ -> { Sign * Value, Rest } end. parse_factor( S ) -> parse_number( S ). parse_term( S ) -> parse_term( S, 1, $* ). parse_term( S, Value, Op ) -> { F1, R1 } = parse_factor( S ), NewValue = case Op of $* -> Value * F1; $/ -> Value / F1 end, R2 = skip_ws( R1 ), case R2 of [ $* | R3 ] -> parse_term( skip_ws( R3 ), NewValue, $* ); [ $/ | R3 ] -> parse_term( skip_ws( R3 ), NewValue, $/ ); _ -> { NewValue, R2 } end. parse_expr( S ) -> parse_expr( S, 0, $+ ). parse_expr( S, Value, Op ) -> { F1, R1 } = parse_term( S ), NewValue = case Op of $+ -> Value + F1; $- -> Value - F1 end, R2 = skip_ws( R1 ), case R2 of [ $+ | R3 ] -> parse_expr( skip_ws( R3 ), NewValue, $+ ); [ $- | R3 ] -> parse_expr( skip_ws( R3 ), NewValue, $- ); _ -> { NewValue, R2 } end. calc( S ) -> { Result, [] } = parse_expr( skip_ws( S ) ), Result. % TESTS skip_ws_test_() -> [ ?_assertEqual( "", skip_ws("") ), ?_assertEqual( "", skip_ws(" ") ), ?_assertEqual( "", skip_ws(" ") ), ?_assertEqual( "", skip_ws( [ $\t ] ) ), ?_assertEqual( "ASDF", skip_ws(" ASDF") ), ?_assertEqual( "AS DF ", skip_ws( [ $\ , $\t, $\ , $\ , $A, $S, $\ , $D, $F, $\ ] ) ) ]. parse_number_test_() -> [ ?_assertEqual( { 0, "" }, parse_number("0") ), ?_assertEqual( { 2, "" }, parse_number("2") ), ?_assertEqual( { 42, "" }, parse_number("42") ), ?_assertEqual( { -1, "" }, parse_number("-1") ), ?_assertEqual( { -145, "" }, parse_number("-145") ), ?_assertEqual( { -145, " some stuff" }, parse_number("-145 some stuff") ), ?_assertEqual( { invalid, "" }, parse_number("asdf") ), ?_assertEqual( { invalid, "" }, parse_number("-") ), ?_assertEqual( { invalid, "" }, parse_number("-stuff") ), ?_assertEqual( { invalid, "" }, parse_number("- stuff") ) ]. parse_term_test_() -> [ ?_assertEqual( { 6, "" }, parse_term("3 * 2") ), ?_assertEqual( { -6, "" }, parse_term("-3 * 2") ), ?_assertEqual( { -6, "" }, parse_term("3 * -2") ), ?_assertEqual( { 6, "" }, parse_term("-3 * -2") ) ]. parse_expr_test_() -> [ ?_assertEqual( { 5, "" }, parse_expr("3 + 2") ), ?_assertEqual( { 1, "" }, parse_expr("3 - 2") ), ?_assertEqual( { 1, "" }, parse_expr("3 + -2") ), ?_assertEqual( { -1, "" }, parse_expr("-3 - -2") ) ]. calc_test_() -> [ ?_assertEqual( 0, calc("0") ), ?_assertEqual( 2, calc("2") ), ?_assertEqual( -3, calc("-3") ), ?_assertEqual( 128, calc("128 ") ), ?_assertEqual( 128, calc(" 128 ") ), ?_assertEqual( 14, calc("2 + 3 * 4") ), ?_assertEqual( 9.0, calc("6 / 2 * 3") ), ?_assertEqual( 1, calc("-1 - -2") ), ?_assertEqual( 11, calc("3 * -2 + 17") ) ].
To run it:
% erl 1> c(calc). {ok,calc} 2> calc:test(). All 33 tests passed. ok 3> calc:calc("2 + 3 * 4"). 14 4> calc:calc("8 / 2 * 4"). 16.0
Then I extended it with:
125e-2 = 1.25
^
You can find it in this Gist, and try it:
% erl 1> c(calc). {ok,calc} 2> calc:test(). All 57 tests passed. ok 3> calc:calc("1.5 * 2"). 3.0 4> calc:calc("12e4 * 2e-3"). 240.0 5> calc:calc("3 ^ 4"). 81.0 6> calc:calc("2 ^ 0.5"). 1.4142135623730951 7> calc:calc("2 + ( 3 * 4 )"). 14.0 8> calc:calc("8 / 2 * (2 + 2)"). 16.0 % OBVIOUSLY! 9> calc:calc("3^(1/2)"). 1.7320508075688772
parse_number got a bit out of hand, but I find the rest quite elegant. I like Erlang.
parse_number
Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink.
Hide child comments as well
Confirm
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
I’m (still) learning Erlang. This is my solution with the given operators and integer numbers. I’m quite satisfied:
To run it:
Then I extended it with:
125e-2 = 1.25
);^
;You can find it in this Gist, and try it:
parse_number
got a bit out of hand, but I find the rest quite elegant. I like Erlang.