Professional Documents
Culture Documents
Capítulo 6
Tabla de símbolos
173
Tabla de símbolos
174
Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC
175
Tabla de símbolos
176
Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC
177
Tabla de símbolos
Las acciones semánticas usarán este puntero para acceder al valor de cada
variable: si el identificador está a la izquierda del token de asignación, entonces se
machacará el valor; y si forma parte de una expresión, ésta se evaluará al valor de la
variable.
Por otro lado, el atributo del token NUM sigue siendo de tipo int. Dado que
se necesitan atributos de tipos distintos (para NUM y para ID), habra que declarar un
%union en Yacc, de la forma:
178
Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC
%union {
int numero;
simbolo * ptrSimbolo;
}
y los terminales y no terminales de la forma:
%token <numero> NUM
%token <ptrSimbolo> ID
%type <numero> expr
Para finalizar, podemos optar por permitir asignaciones múltiples o no. Un
ejemplo de asignación múltiple es:
a := b := c := 16;
que asigna el valor 16 a las variables c, b y a en este orden. Para hacer esto,
consideraremos que las asignaciones vienen dadas por las siguientes reglas:
asig : ID ASIG expr
| ID ASIG asig
;
La figura 6.4 ilustra el árbol sintáctico que reconoce este ejemplo. Como
puede verse en él, el no terminal asig también debe tener asociado como atributo el
campo numero del %union, con objeto de ir propagando el valor de la expresión y
poder realizar las asignaciones a medida que se asciende en el árbol sintáctico.
La tabla de símbolos viene dada por el código siguiente, almacenado en el
fichero TabSimb.c:
1 #include <stdlib.h>
2 #include <stdio.h>
3 typedef struct _simbolo {
4 struct _simbolo * sig;
5 char nombre [20];
179
Tabla de símbolos
7 int valor;
8 } simbolo;
9 simbolo * crear() {
10 return NULL;
11 };
12 void insertar(simbolo **pT, simbolo *s) {
13 s->sig = (*pT);
14 (*pT) = s;
15 };
16 simbolo * buscar(simbolo * t, char nombre[20]){
17 while ( (t != NULL) && (strcmp(nombre, t->nombre)) )
18 t = t->sig;
19 return (t);
20 };
21 void imprimir(simbolo * t) {
22 while (t != NULL) {
23 printf("%s\n", t->nombre);
24 t = t->sig;
25 }
26 };
El programa Lex resulta sumamente sencillo. Tan sólo debe reconocer los
lexemas e insertar en la tabla de símbolos la primera vez que aparezca cada
identificador. El siguiente fichero Calcul.lex ilustra cómo hacerlo:
1 %%
2 [0-9]+ {
3 yylval.numero = atoi(yytext);
4 return NUMERO;
5 }
6 ":=" { return ASIG; }
7 "PRINT" { return PRINT; }
8 [a-zA-Z][a-zA-Z0-9]* {
9 yylval.ptrSimbolo = buscar(t,yytext);
10 if (yylval.ptrSimbolo == NULL) {
11 yylval.ptrSimbolo=(simbolo *) malloc(sizeof(simbolo));
12 strcpy(yylval.ptrSimbolo->nombre, yytext);
13 yylval.ptrSimbolo->valor=0;
14 insertar(&t, yylval.ptrSimbolo);
15 }
16 return ID;
17 }
18 [b¯& \t\n]+ {;}
19 . { return yytext[0]; }
El programa Yacc tan sólo difiere de la calculadora tradicional en los accesos
a la tabla de símbolos para obtener y alterar el valor de cada variable. El siguiente
código se supone almacenado en el fichero Calcuy.yac:
1 %{
2 #include "TabSimb.c"
180
Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC
3 simbolo * t;
4 %}
5 %union {
6 int numero;
7 simbolo * ptrSimbolo;
8 }
9 %token <numero> NUMERO
10 %token <ptrSimbolo> ID
11 %token ASIG PRINT
12 %type <numero> expr asig
13 %left '+' '-'
14 %left '*' '/'
15 %right MENOS_UNARIO
16 %%
17 prog : prog asig ';' { printf("Asignacion(es) efectuada(s).\n"); }
18 | prog PRINT expr ';' { printf("%d\n",$3); }
19 | prog error ';' { yyerrok; }
20 | /* Épsilon */
21 ;
22 asig : ID ASIG expr {
23 $$ = $3;
24 $1->valor = $3;
25 }
26 | ID ASIG asig {
27 $$ = $3;
28 $1->valor = $3;
29 }
30 ;
31 expr : expr '+' expr {$$ = $1 + $3;}
32 | expr '-' expr {$$ = $1 - $3;}
33 | expr '*' expr {$$ = $1 * $3;}
34 | expr '/' expr {$$ = $1 / $3;}
35 | '(' expr ')' {$$ = $2;}
36 | '-' expr %prec MENOS_UNARIO {$$ = - $2;}
37 | ID {$$ = $1->valor; }
38 | NUMERO {$$ = $1;}
39 ;
40 %%
41 #include "Calcul.c"
42 #include "errorlib.c"
43 void main()
44 { t = crear();
45 yyparse ();
46 imprimir(t);
47 }
181
Tabla de símbolos
tabla de símbolos será una clase Tablasimbolos que encapsula en su interior una tabla
de dispersión y proporciona al exterior tan sólo las operaciones del punto 6.4.1. Como
clave de la tabla usaremos un String (el nombre del símbolo) y como dato utilizaremos
un objeto de tipo Simbolo que, a su vez, contiene el nombre y el valor de cada variable.
El fichero Simbolo.java es:
1 class Simbolo{
2 String nombre;
3 Integer valor;
4 public Simbolo(String nombre, Integer valor){
5 this.nombre = nombre;
6 this.valor = valor;
7 }
8 }
Y TablaSimbolos.java quedaría:
1 import java.util.*;
2 public class TablaSimbolos{
3 HashMap t;
4 public TablaSimbolos(){
5 t = new HashMap();
6 }
7 public Simbolo insertar(String nombre){
8 Simbolo s = new Simbolo(nombre, new Integer(0));
9 t.put(nombre, s);
10 return s;
11 }
12 public Simbolo buscar(String nombre){
13 return (Simbolo)(t.get(nombre));
14 }
15 public void imprimir(){
16 Iterator it = t.values().iterator();
17 while(it.hasNext()){
18 Simbolo s = (Simbolo)it.next();
19 System.out.println(s.nombre + ": "+ s.valor);
20 }
21 }
22 }
La tabla de símbolos debería ser vista tanto por el analizador sintáctico como
por el léxico, con objeto de poder acceder a sus elementos. Aunque en este ejemplo no
es estrictamente necesario, haremos que la tabla de símbolos sea creada por el
programa principal como dato propio del analizador sintáctico (ya que estamos en un
análisis dirigido por sintaxis) y se pasará al analizador léxico como un parámetro más
en el momento de su construcción. De esta manera el fichero Calcul.jflex quedaría:
1 import java_cup.runtime.*;
2 import java.io.*;
3 %%
4 %{
182
Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC
183
Tabla de símbolos
18 :};
19 terminal PUNTOYCOMA, MAS, MENOS, POR, ENTRE;
20 terminal UMENOS, LPAREN, RPAREN, PRINT, ASIG;
21 terminal Simbolo ID;
22 terminal Integer NUMERO;
23 non terminal listaExpr;
24 non terminal Integer expr, asig;
25 precedence left MAS, MENOS;
26 precedence left POR, ENTRE;
27 precedence left UMENOS;
28 listaExpr ::= listaExpr asig PUNTOYCOMA
29 {: System.out.println("Asignacion(es) efectuada(s)."); :}
30 | listaExpr PRINT expr:e PUNTOYCOMA
31 {: System.out.println("= " + e); :}
32 | listaExpr error PUNTOYCOMA
33 | /* Epsilon */
34 ;
35 asig ::= ID:s ASIG expr:e {:
36 RESULT = e;
37 s.valor = e;
38 :}
39 | ID:s ASIG asig:e {:
40 RESULT = e;
41 s.valor = e;
42 :}
43 ;
44 expr ::= expr:e1 MAS expr:e2
45 {: RESULT = new Integer(e1.intValue() + e2.intValue()); :}
46 | expr:e1 MENOS expr:e2
47 {: RESULT = new Integer(e1.intValue() - e2.intValue()); :}
48 | expr:e1 POR expr:e2
49 {: RESULT = new Integer(e1.intValue() * e2.intValue()); :}
50 | expr:e1 ENTRE expr:e2
51 {: RESULT = new Integer(e1.intValue() / e2.intValue()); :}
52 | ID:s {: RESULT = s.valor; :}
53 | NUMERO:n {: RESULT = n; :}
54 | MENOS expr:e %prec UMENOS
55 {: RESULT = new Integer(0 - e.intValue()); :}
56 | LPAREN expr:e RPAREN {: RESULT = e; :}
57 ;
184
Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC
185
Tabla de símbolos
33 | error ";"
34 */
35 void sentFinalizada():{
36 int resultado;
37 Vector v = new Vector();
38 Simbolo s;
39 }{
40 try{
41 <PRINT> resultado=expr() <PUNTOYCOMA>
42 { System.out.println("="+resultado); }
43 |
44 ( LOOKAHEAD(2)
45 s=id() ":=" { v.add(s); } )+ resultado=expr() <PUNTOYCOMA>
46 {
47 Integer valor = new Integer(resultado);
48 Iterator it = v.iterator();
49 while(it.hasNext())
50 ((Simbolo)it.next()).valor = valor;
51 System.out.println("Asignacion(es) efectuada(s).");
52 }
53 }catch(ParseException x){
54 System.out.println(x.toString());
55 Token t;
56 do {
57 t = getNextToken();
58 } while (t.kind != PUNTOYCOMA);
59 }
60 }
61 /*
62 expr ::= term ( ( "+" | "-" ) term )*
63 */
64 int expr():{
65 int acum1=0,
66 acum2=0;
67 }{
68 acum1=term() ( ("+" acum2=term() {acum1+=acum2;} )
69 | ("-" acum2=term() {acum1-=acum2;} )
70 )*
71 { return acum1; }
72 }
73 /*
74 term ::= fact ( ( "*" | "/" ) fact )*
75 */
76 int term():{
77 int acum1=0,
78 acum2=0;
79 }{
80 acum1=fact() ( ("*" acum2=fact() {acum1*=acum2;} )
81 | ("/" acum2=fact() {acum1/=acum2;} )
186
Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC
82 )*
83 { return acum1; }
84 }
85 /*
86 fact ::= (“-“)* ( ID | NUMERO | "(" expr ")" )
87 */
88 int fact():{
89 int acum=0,
90 signo=1;
91 Simbolo s;
92 }{
93 ("-" { signo *= -1; } )*
94 ( s=id() { acum = s.valor.intValue(); }
95 | acum=numero()
96 | "(" acum=expr() ")"
97 )
98 { return signo*acum; }
99 }
100 Simbolo id():{}{
101 <ID> {
102 Simbolo s;
103 if ((s = tabla.buscar(token.image)) == null)
104 s = tabla.insertar(token.image);
105 return s;
106 }
107 }
108 int numero():{}{
109 <NUMERO> { return Integer.parseInt(token.image); }
110 }
111 SKIP : {
112 <ILEGAL: (~[])> { System.out.println("Carácter: "+image+" no esperado.");}
113 }
Como se ha comentado anteriormente, la necesidad de realizar todas las
asignaciones de golpe en una asignación múltiple se podría evitar introduciendo la
regla:
/*
asig ::= ( ID “:=” )+ expr
*/
int asig():{
int resultado;
Simbolo s;
}{
( LOOKAHEAD(4)
s=id() “:=” resultado=asig() { s.valor = new Integer(resultado); }
| s=id() “:=” resultado=expr() { s.valor = new Integer(resultado); }
)
{ return resultado; }
}
187
Tabla de símbolos
Y la regla
id() ":=" )+ expr() <PUNTOYCOMA>
se sustituye por
asig() <PUNTOYCOMA> { System.out.println("Asignacion(es) efectuada(s)."); }
188