martes, octubre 13, 2009

calculadora en erlang parte (multiplicación división y modulo)

como la otra vez voy a marcar las diferencias

calc_lexer.xrl:


Definitions.

D = [0-9]
AOP = (\+|-)
MOP = (\*|/|%)
WS = ([\000-\s]|#.*)

Rules.

{AOP} : {token,{add_operator,TokenLine,list_to_atom(TokenChars)}}.
{MOP} : {token,{mul_operator,TokenLine,list_to_atom(TokenChars)}}.
{D}+ : {token,{integer,TokenLine,list_to_integer(TokenChars)}}.
{D}+\.{D}+ : {token,{float,TokenLine,list_to_float(TokenChars)}}.
{WS}+ : skip_token.

Erlang code.

----------------------------------------

nada del otro mundo, hacemos lo mismo que con los signos de adicion pero cambiando la expresion regular y el identificador.

ahora el parser:

calc_parser.yrl

Nonterminals
predicate.

Terminals
add_operator mul_operator integer float.

Rootsymbol predicate.

Left 300 add_operator.
Left 400 mul_operator.

predicate -> predicate add_operator predicate : {unwrap('$2'), '$1', '$3'}.
predicate -> predicate mul_operator predicate : {unwrap('$2'), '$1', '$3'}.

predicate -> integer : unwrap('$1').
predicate -> float : unwrap('$1').

Erlang code.

unwrap({_,_,V}) -> V.

----------------------------------

de nuevo, agregamos el identificador del nuevo simbolo no terminal, especificamos la asociatividad de la nueva operacion (tiene precedencia sobre la adicion). Y agregamos la regla para que lo transforme en una estructura de datos.

por ultimo la calculadora en si

calc.erl

-module(calc).
-export([solve/1]).

solve(String) ->
{ok, Tokens, _Endline} = calc_lexer:string(String),
{ok, Tree} = calc_parser:parse(Tokens),
matches(Tree).

matches(A) when is_number(A) -> A;
matches({'+', A, B}) -> matches(A) + matches(B);
matches({'-', A, B}) -> matches(A) - matches(B);
matches({'*', A, B}) -> matches(A) * matches(B);
matches({'/', A, B}) -> matches(A) / matches(B);
matches({'%', A, B}) -> matches(A) rem matches(B);
matches(_) -> error.

----------------------------------------------

agregamos el matching de las nuevas operaciones.

para probar un poco el codigo de build_calc:test()


test() ->
0 = calc:solve("1 + 2 - 3"),
6 = calc:solve("1 + 2 + 3"),
-4 = calc:solve("1 - 2 - 3"),
0.0 = calc:solve("1.0 + 2 - 3"),
6.0 = calc:solve("1 + 2.0 + 3"),
-4.0 = calc:solve("1 - 2.0 - 3"),
3.1 = calc:solve("1.0 + 2.1"),
7 = calc:solve("1 + 2 * 3"),
10 = calc:solve("2 * 3 + 4"),
7.1 = calc:solve("1.1 + 2 * 3"),
10.2 = calc:solve("2 * 3.1 + 4"),
1 = calc:solve("11 % 2"),
2 = calc:solve("1 + 11 % 2"),
11 = calc:solve("11 % 2 + 10"),
ok.


y por ultimo lo probamos



$ erl
Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [kernel-poll:false]

Eshell V5.6.5 (abort with ^G)
1> c(build_calc).
{ok,build_calc}
2> build_calc:build().
Old inliner: threshold=0 functions=[{yeccpars2_7_,1},
{yeccpars2_6_,1},
{yeccpars2_3_,1},
{yeccpars2_2_,1},
{yeccpars2_7_,1},
{yeccpars2_6_,1},
{yeccpars2_3_,1},
{yeccpars2_2_,1}]
ok
3> rl(calc).
ok
4> rl(calc_parser).
ok
5> rl(calc_lexer).
ok
6> build_calc:test().
ok


en la proxima agregamos expresiones anidadas (parentesis)

1 comentario:

Joac dijo...

mariano, cambia ese "divicion" por "división" que queda mas lindo!

Seguidores

Archivo del Blog