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:
mariano, cambia ese "divicion" por "división" que queda mas lindo!
Publicar un comentario