190 likes | 319 Views
Traductores EDT con ANTLR. La gramática. t ::= f t1 t1 ::= OPMULT f t1 | OPDIV f t1 | λ f ::= | ( e ) | NUMERO. a ::= e ; a | λ e ::= t e1 e1 ::= OPSUMA t e1 | OPRESTA t e1 | λ. Las acciones semánticas. En negrita se muestran las acciones semánticas para cada producción.
E N D
La gramática • t ::= f t1 • t1 ::= OPMULT f t1 • | OPDIV f t1 • | λ • f ::= | ( e ) • | NUMERO • a ::= e ; a | λ • e ::= t e1 • e1 ::= OPSUMA t e1 | OPRESTA t e1 | λ
Las acciones semánticas En negrita se muestran las acciones semánticas para cada producción. Para esta primera producción, el resultado de la operación matemática llegará a exp, el atributo de e, y este valor se mostrará por pantalla. • a : exp=e SEMICOLON { System.out.println("\n\nReconocida expresión aritmética. Valor final: " + $exp.res); } a | ;
Las acciones semánticas El no terminal e va a devolver (subir hacia arriba) el resultado de la operación. Este resultado le vendrá dado del otro no terminal e1. Por este motivo la acción semántica iguala $eres.res (resultado de e1) a $res (valor que devuelve e). Pero para que e1 pueda realizar la parte izquierda de la operación, necesita el resultado de la parte derecha, y este vendrá dado por t. Se puede ver que e1 recibe como atributo heredado $tt.res. • e returns [int res]: tt=t eres=e1[$tt.res] {$res = $eres.res;}; En el resto de producciones se puede ver el mismo método para operar. Se calcula la parte derecha de la operación, ésta se pasa como atributo heredado a la parte izquierda, y esta parte izquierda realizará otra operación con un nuevo operando. Cuando se llegue a un caso en el que no haya más partes izquierdas (más operandos), habrá que subir el resultado hacia arriba.
Las acciones semánticas En las dos primeras producciones se realizan la suma o la resta. El valor recibido en tt (como atributo heredado) se suma/resta al siguiente operando y este resultado se le pasa a e1. • e1[int tt] returns [int res]: OPSUMA t2=t {int aux = $tt + $t2.res;} eres=e1[aux] {$res = $eres.res;} | OPRESTA t2=t {int aux = $tt - $t2.res;} eres=e1[aux] {$res = $eres.res;} | {$res = $tt;}; En esta última producción ya no hay más operandos y simplemente se devuelve el valor hacia arriba.
Las acciones semánticas Esta producción es parecida a la de e. Primero se coge el valor de f, valor que vendrá sintetizado desde abajo. Este número se le pasa a la parte izquierda de la operación. Se consigue pasándolo como atributo heredado de t1 (t1[$num1.res]). • t returns [int res]: num1=f tres=t1[$num1.res] {$res = $tres.res;}; Cuando t1 tenga el resultado, lo subirá hacia arriba asignándoselo a $res, que es el atributo que t subirá.
Las acciones semánticas En estas producciones se realizan las multiplicaciones y divisiones. Como antes el resultado de las operaciones anteriores vendrá dado, en este caso en n1. Con este valor y el siguiente operando (n2) se realizará la operación correspondiente. El valor de n2 viene sintetizado de abajo. Cuando se a operado, se pasa el valor a la parte izquierda. • t1[int n1] returns [int res]: OPMULT n2=f {int aux = $n1 * $n2.res;} tres = t1[aux] {$res = $tres.res;} | OPDIV n2=f {int aux = $n1 / $n2.res;} tres=t1[aux] {$res = $tres.res;} | {$res = $n1;} ; Cuando no hay más operandos, se sube el valor hacia arriba. Esto lo hace la última producción.
Las acciones semánticas Finalmente se llega a la producción f que es la que lee los operandos de la cadena de entrada, o la que recibe el resultado de una operación que estaba entre paréntesis. • f returns [int res = 0]: | '(' exp=e ')' {$res = $exp.res;} | num=NUMERO {$res = Integer.parseInt($num.text);} ; En el primer caso, este valor de la cadena de entrada se pasa a entero y se transmite hacia arriba a través de $res. En el segundo caso simplemente se pasa el valor de la expresión hacia arriba.
Ejemplo • (1+5*5)-8/2; • Este ejemplo tiene una operación de cada tipo: suma, resta, multiplicación, división y paréntesis. El árbol sintáctico resultante es el siguiente:
Ejemplo: Árbol sintáctico A continuación se va a mostrar el comportamiento de las acciones semánticas para resolver este ejemplo.
Ejemplo: Árbol sintáctico Se van aplicando producciones hasta que llegamos a la producción: • f returns [int res = 0]: | num=NUMERO {$res = Integer.parseInt ($num.text);} ; Entonces se coge el número de la cadena de entrada.
Ejemplo: Árbol sintáctico Como el siguiente carácter de la cadena es un “+”, t1 deriva en λ. • t1[int n1] returns [int res]: | {$res = $n1;} ; T1 ha recibido el valor de f, a través de n1. Cuando t1 calcula las operaciones inferiores (en este caso ninguna) sube el valor hacia arriba con $res.
Ejemplo: Árbol sintáctico El resultado de t se pasa heredado a e1: • e returns [int res]: tt=t eres = e1[$tt.res] Después se coge el siguiente número y se pasa de f a t1.
Ejemplo: Árbol sintáctico En esta producción se realiza una multiplicación. Se realiza antes que la suma porque tiene mayor precedencia. El resultado se pasa hacia abajo como atributo heredado. • t1[int n1] returns [int res]: OPMULT n2=f {int aux = $n1 * $n2.res;} tres=t1[aux] {$res = $tres.res;}
Ejemplo: Árbol sintáctico Como no hay más operandos t1 sube el valor que ha recibido de f hacia arriba. Lo mismo hace su antecedente t1: • t1[int n1] returns [int res]: | {$res = $n1;} ; En este momento es cuando se realiza la suma: • e1[int tt] returns [int res]: OPSUMA t2=t {int aux = $tt + $t2.res;} eres=e1[aux] {$res = $eres.res;}
Ejemplo: Árbol sintáctico e1 recibe el resultado de t, pero como no hay más operaciones en el paréntesis, sube el resultado hacia arriba: • e1[int tt] returns [int res]: | {$res = $tt;};
Ejemplo: Árbol sintáctico El proceso se va repitiendo todo el rato. Cada vez que llega un número en la cadena de entrada, este se sube hasta f. Una vez aquí se pasa como atributo heredado para que la siguiente producción haga su trabajo.
Ejemplo: Árbol sintáctico Como se ve en la imagen, el número se sube hasta f y se opera. El resultado se pasa como atributo heredado a t1: • t1[int n1] returns [int res]: | OPDIV n2=f {int aux = $n1 / $n2.res;} tres=t1[aux]{$res = $tres.res;} Como no hay más operaciones que realizar el resultado se va subiendo hacia arriba con atributos sintetizados.
Ejemplo: Árbol sintáctico “Reconocida expresión aritmética. Valor final: 22” Finalmente se realiza la última operación. Este resultado, como en todos los casos anteriores se pasa como atributo heredado hacia el hermano. Este que ya no tiene más cálculos que hacer devuelve el resultado hacia arriba. Este resultado sube hasta llegar al final y se muestra por pantalla: • a: exp=e SEMICOLON { System.out.println("\n\nReconocida expresión aritmética. Valor final: " + $exp.res); } a