PRACTICA 2
ANALISIS LEXICO: LEX
Programa en Modula-2 ----------------------------------------------------> Genera código de 3 direcciones
ANALISIS SINTACTICO: YACC
%{
/* SECCION DE DECLARACIONES */
/* Lex copia todo lo incluido entre %{ y %} directamente al fichero generado en C, así que aquí solo se debe escribir cualquier código válido en C. */
#include "tablasim.h"
#include "y.tab.h" /*Fichero generado por el programa Yacc cuando se ejecuta sobre " pl96.y" y que contiene los códigos de todos los tokens.*/
#include "main.h"
%}
%%
/* SECCIÓN DE REGLAS: Cada regla se compone de 2 partes: un patrón y una acción separadas por un espacio en blanco. El lexer que genera Lex ejecutará la acción cuando reconozca el patrón de entre lo que toma de la entrada. */
\. { return '.'; } /* Cuando encuentra un "." en la cadena de entrada devuelve el código ASCII del "." al parser .*/
\, { return ','; }
\: { return ':'; }
\; { return ';'; }
\( { return '('; }
\) { return ')'; }
\+ { return '+'; }
\* { return '*'; }
\/ { return '/'; }
DIV { return DIV; }
\- { return '-'; }
\{ { return '{'; }
\} { return '}'; }
\< { return LT; }
\<\= { return LE; }
\> { return GT; }
\>\= { return GE; }
\= { return EQ; }
\<\> { return NE; }
\:\= { return ASIG; }
VAR { return VAR; }
INTEGER { return INTEGER; }
REAL { return REAL; }
CHAR { return CHAR; }
AND { return AND; }
OR { return OR; }
NOT { return NOT; }
BEGIN { return BEGIN; }
END { return END; }
REPEAT { return REPEAT; }
UNTIL { return UNTIL; }
WHILE { return WHILE; }
DO { return DO; }
FOR { return FOR; }
BY { return BY; }
TO { return TO; }
IF { return IF; }
THEN { return THEN; }
ELSEIF { return ELSEIF; }
ELSE { return ELSE; }
[a-zA-Z][a-zA-Z0-9]* { /* NOTA 1: */ strcpy(yylval.codigo,yytext); return IDENT; }
0|[1-9][0-9]* {/* NOTA 2: */ strcpy(yylval.codigo,yytext); return ENTERO; }
\n numlinea++; {/* Cuenta el nº de líneas del fichero de entrada*/; }
[\ \t] ;
. { /* Si entra por aquí, es que en el fichero de entrada hay algo escrito que la gramática no admite. Entonces copia en "st" un mensaje que pasará a la función error, la cual lo sacará por pantalla. */
char st[80];
sprintf(st,"ERROR LÉXICO Carácter inesperado: <%s>",yytext);
yyerror(st);
}
%%
/* SECCION DE RUTINAS DE USUARIO: Aquí se escribe cualquier código legal en C. El programa Lex copiará esta parte al final de fichero que genera. */
/* No hay ninguna rutina*/
NOTA 1: La variable yylval es de tipo integer en ella se devuelve el código del token al parser, pero cuando se quiere devolver además del código del token el valor de éste, bien sea numérico o una cadena, el lexer debe almacenar ese valor en yylval.campo donde "campo" es el tipo de la unión del token a devolver.
NOTA 2: En "yylval.codigo" se almacena el valor del número que ha identificado el lexer. El campo "código" es un array de caracteres, esto indica que con el nº copiado en yylval no podremos hacer operaciones aritméticas, pero como no necesitamos hacer nada de esto, nos da igual.
NOTA 3: La variable yytext es un array de caracteres donde se almacena el texto que ha encajado con el patrón.
NOTA 4: A menos que las acciones contengan alguna sentencia RETURN, yylex() no devolverá nada al parser, sino que seguirá procesando la entrada.
#include "main.h"
char *nombreprograma; /* Vble donde se almacena el nombre del fichero de entrada.*/
int numlinea = 1; /* Inicializa la vble numlinea a 1 (se empieza a contar desde 1 el nº de líneas) */
/* MAIN: Se encarga de llamar a la rutina yyparse() hasta que se agota el fichero de entrada en cuyo caso yylex() le devuelve un 0 indicando fin de fichero. */
main (int argc, char *argv[])
{
void cerrarficheros ();
int abrirficheros ();
nombreprograma = argv[0];
if (abrirficheros (argc, argv))
yyparse();
cerrarficheros ();
}
/* ABRIRFICHEROS: Abre los ficheros de entrada y salida. Devuelve 0 en caso de error. La sintaxis para llamar al analizador es: PL96 nomfich_entrada [nomfich_salida] */
abrirficheros (int argc, char *argv[])
{
if (argc > 3) { printf ("Demasiados parametros.\n"); return (0); }
if (argc > 1) yyin = fopen (*(argv+1), "r");
if (yyin == NULL) { printf ("Fichero no encontrado.\n"); return (0); }
if (argc > 2) yyout = fopen (*(argv+2), "w");
return (1);
}
/* CERRARFICHEROS: Cierra los ficheros de entrada y salida. */
void cerrarficheros ()
{
fclose (yyin);
fclose (yyout);
return;
}
/* YYERROR: Saca por pantalla el nº de línea, el nombre del programa donde se cometió el error, así como que lo produjo. */
yyerror(char *s)
{
fprintf(stderr,"%s(%d): %s \n",nombreprograma,numlinea,s);
}
/* Asigna a cada una de las posibles instrucciones ( int instr, de la función gc) un número para que el código de la función gc al hacer la llamada sea más fácil. */
#define CTD_ASIG 1 /* Significa código 3 direcciones para la asignación.*/
#define CTD_SUMA 2
#define CTD_RESTA 3
#define CTD_PROD 4
#define CTD_DIV 5
#define CTD_MENOS 6
#define CTD_GOTO 7
#define CTD_LABEL 8
#define CTD_IFMENOR 9
#define CTD_IFIGUAL 10
void gc(int instr, char* arg1, char* arg2, char* result);
#include "gc.h"
#include "main.h"
/* GC: Generador de código de tres direcciones. */
void gc(int instr, char* arg1, char* arg2, char* result)
{
switch (instr)
{
case CTD_ASIG: { /* DEVUELVE: a=b */
fprintf(yyout,"\t%s = %s;\n",result,arg1);
break;
}
case CTD_SUMA: { /* DEVUELVE: a= b + c. */
fprintf(yyout,"\t%s = %s + %s;\n",result,arg1,arg2);
break;
}
case CTD_RESTA:{ /* DEVUELVE: a= b - c. */
fprintf(yyout,"\t%s = %s - %s;\n",result,arg1,arg2);
break;
}
case CTD_PROD: { /* DEVUELVE: a= b * c. */
fprintf(yyout,"\t%s = %s * %s;\n",result,arg1,arg2);
break;
}
case CTD_DIV: { /* DEVUELVE: a= b / c. */
fprintf(yyout,"\t%s = %s / %s;\n",result,arg1,arg2);
break;
}
case CTD_MENOS:{ /* DEVUELVE: a= - b */
fprintf(yyout,"\t%s = -%s;\n",result,arg1);
break;
}
case CTD_GOTO: { /* DEVUELVE: goto L. */
fprintf(yyout,"\tgoto %s;\n",result);
break;
}
case CTD_LABEL:{ /* DEVUELVE: Label L. */
fprintf(yyout,"label %s:\n",result);
break;
}
case CTD_IFMENOR: { /* DEVUELVE: if (a< b) goto L. */
fprintf(yyout,"\tif (%s < %s) goto %s;\n",arg1,arg2,result);
break;
}
case CTD_IFIGUAL: { /* DEVUELVE: if (a== b) goto L. */
fprintf(yyout,"\tif (%s == %s) goto %s;\n",arg1,arg2,result);
break;
}
default: {
fprintf(yyout,"ERROR CTD DESCONOCIDO\n",arg1,arg2,result);
}
} /* switch */
}
#define MAXID 80 /* longitud maxima de los identificadores */
#define RESET 0
#define NEW 1
/* Definición de los Tipos que puede tomar una variable . */
#define TIPOENTERO 1
#define TIPOREAL 2
#define TIPOCARACTER 3
/* Estructura de la Tabla de Simbolos que utilizaremos para almacenar las variables declaradas en el programa en modula-2 junto con su tipo. */
typedef int TIPO; /* estructura de datos TIPO */
typedef struct tabla
{
char id[MAXID]; /* nombre del simbolo */
TIPO tipo; /* tipo (codificado) */
struct tabla *sig;
} TABLA ;
/* Funciones para manejar la Tabla De Simbolos. */
extern void listartabla();
extern TABLA * apuntar(char *simbolo, TIPO tipo);
extern TABLA * buscar(char *simbolo);
#include <string.h>
#include <stdlib.h>
#include "tablasim.h"
/* Declaración de una variable global "tablasim". (implementacion de lista no ordenada) */
TABLA *tablasim = NULL;
/* LISTARTABLA: Recorre la lista enlazada de simbolos, imprimiendo en pantalla el nombre y el tipo de todas las variables declaradas. */
void listartabla()
{
TABLA *i;
printf ("\n----------- Listado de la tabla de simbolos ---------------\n\n");
for(i=tablasim; i != NULL ; i = i->sig)
{
switch (i->tipo)
{
case TIPOENTERO: printf("%s (ENTERO)\n",i->id); break;
case TIPOREAL: printf("%s (REAL)\n",i->id); break;
case TIPOCARACTER: printf("%s (CARACTER)\n",i->id); break;
}
}
printf ("\n-----------------------------------------------------------\n\n");
}
/* BUSCAR: Dado un símbolo busca si éste ya ha sido instalado en la tabla de simbolos, si es así devuelve un puntero al simbolo, sino devuelve NULL. */
TABLA *buscar(char *simb)
{
TABLA *i;
for(i=tablasim; i != NULL; i=i->sig)
if (strcmp(i->id,simb) == 0 )
return i;
return (TABLA*) NULL;
}
/* APUNTAR: Instala un símbolo y su tipo en la Tabla de Simbolos, y devuelve un puntero a este nuevo símbolo, que los instalará al principio de la estructura de la lista enlazada. */
TABLA *apuntar(char *simb, TIPO argtipo)
{
TABLA *pt; /* indice */
pt = (TABLA *) malloc(sizeof(TABLA));
sprintf(pt->id,simb);
pt->tipo = argtipo;
pt->sig = tablasim;
tablasim = pt;
return pt;
}
%{
/* SECCION DE DECLARACIONES. */
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include "main.h"
#include "gc.h"
#include "tablasim.h"
%}
/* La UNION sirve para definir los posibles tipos de símbolos. El contenido de la unión se copia tal cual en el fichero yy.tab.h como contenido de una unión en C, a través de la cual se define el tipo YYSTYPE como un typedef; quedando algo así:
typedef unión {
.......................
.........................
} YYSTYPE
extern YYSTYPE yylval;
Ahora ya se puede utilizar en el lexer la variable yylval.*/
%union
{
TIPO tipo; /* (codificado) */
char código[MAXID]; /* literal (lexema) */
struct {
char inicio[10];
char fin[10];
} etq_control;
struct {
char true[10];
char false[10];
} etq_condicion;
};
/* Tras la unión viene la definición de todos los token que el parser espera recibir del lexer. Así:
%token INTEGER = indica que INTEGER es un token que el parser espera recibir.
Utilizando también %token podemos indicarle al parser qué símbolos utilizan que tipos de valores de la unión.
Esto se hace poniendo el nombre del campo necesario de la unión entre ángulos en la línea que está en la sección de declaraciones donde se define el símbolo: p.ej:
%token <código> IDENT =
indica que el token IDENT es del tipo código, es decir, un array de caracteres. Que en este caso servirá para guardar el nombre del identificador. */
/* La declaracion %type establece el tipo para los no terminales, los cuales sino es así no necesitan ser declarados.*/
%type <tipo> tipo declaracion
%type <código> exp by
%type <etq_condicion> cond
%token INTEGER REAL CHAR
%token ASIG
%token EQ NE LT LE GT GE /* comparaciones */
%token <código> IDENT
%token <código> ENTERO
%token VAR BEGIN END
%token <etq_control> IF ELSEIF
%token THEN ELSE
%token <etq_control> WHILE
%token DO
%token <etq_control> REPEAT
%token UNTIL
%token <etq_control> FOR
%token TO BY /* DO */
/* La gramática aquí definida utiliza operadores: ‘+’ ‘*’ ‘/’ ‘-’ ; para solucionar los problemas de ambigüedad que presentan las reglas que utilizan estos operadores hay que establecer una PRECEDENCIA y una ASOCIATIVIDAD entre los operadores:
PRECEDENCIA: Controla que operadores deben ejecutarse primero en una expresión.
ASOCIATIVIDAD: Controla como debe asociar si a la dcha. o a la izqda. para operadores con la misma
precedencia.
Así:
%left ‘+’ ‘-’
%left ‘*’
%left ‘/’
La 1ª línea indicaría que "+" y "-" son asociativos por la izquierda (en caso de que aparezcan en una expresión sumas y restas así se asociarán) y que están en el más bajo nivel de precedencia, (tanto la suma como la resta tienen la misma precedencia). Y el operador "/" tiene la mayor precedencia.
MENOSUNARIO: es un pseudo-token que utilizaremos para el menos unario, que no tiene asociatividad y que tiene la más alta precedencia. */
%left OR
%left AND
%left NOT
%left '+'
%left '-'
%left '*'
%left '/'
%left DIV
%left MOD
%nonassoc MENOSUNARIO
/* Yacc asigna a cada regla la precedencia del token que tenga más a la derecha en la parte derecha de ésta. Si la regla contiene tokens que no tienen precedencia asignada, la regla no tendrá precedencia. Cuando Yacc encuentra un conflicto SHIFT/REDUCE debido a una gramática ambigua, consulta la tabla de precedencias, y si todas las reglas envueltas en el conflicto incluyen un token con precedencia asignada, usará precedencia para resolver el conflicto.*/
/* Una de las reglas asociadas al no terminal "exp" es:
exp : ‘-’ exp %prec MENOSUNARIO
El único operador que esta regla incluye es "-" el cual tiene la precedencia más baja, pero nosotros queremos que el operador menos unario tenga la precedencia más alta que la de la multiplicación, así %prec le dice a Yacc que use la precedencia de MENOSUNARIO para esta regla.
NOTAR que el símbolo ‘-’ se utiliza tanto como operador binario como unario, y que sólo cuando es operador unario queremos que tenga la mayor precedencia, de ahí que se declare con la menor precedencia para que ésta se utilice en las restas binarias, y se modifica en la regla de operador menos unario asignándole la mayor precedencia. */
%%
/* SECCION DE REGLAS: Aquí se declara la gramática, sus reglas. */
/* La tabla de simbolos la utilizaremos en Yacc para ir instalando las variables que hay declaradas en el fichero de entrada al analizador. Cuando una variable vaya a ser utilizada se comprobará primero que ha sido declarada antes, lo cual se traduce a que ya esté instalada en la tabla de simbolos, si no es así se lanzará un mensaje de error.*/
programa: VAR list_decl BEGIN {listartabla();} list_sent END '.'
;
list_decl: list_decl declaracion
| /* vacio */
;
/* Cuando encuentra una variable, mira si ya está en la Tabla de Símbolos si es así declara que la variable está duplicada, sino la instala. En declaración nos llevamos el tipo de la variable.*/
declaracion: IDENT ':' tipo ';'
{
$$ = $3;
if ( !buscar($1) )
apuntar($1,$3);
else
yyerror("AVISO Variable duplicada");
}
| IDENT ',' declaracion
{
$$ = $3;
if ( !buscar($1) )
apuntar($1,$3);
else
yyerror("AVISO Variable duplicada");
}
;
tipo: INTEGER { $$ = TIPOENTERO; }
| REAL { $$ = TIPOREAL; }
| CHAR { $$ = TIPOCARACTER; }
;
list_sent: list_sent ';' sentencia
| sentencia
;
sentencia: /* sentencia vacia */
/* Si la variable IDENT ha sido declarada en la sección VAR entonces se le podrá asignar una expresión, para comprobar que esto es cierto busca en la TDS si se encuentra, si es así podrá realizar las pertinentes operaciones, si no devolverá un error.*/
| IDENT ASIG exp
{
if (buscar($1))
gc(CTD_ASIG,$3,"",$1);
else
yyerror("ERROR Variable no declarada");
}
| WHILE
{
sprintf($1.inicio,nuevaetiqueta());
gc(CTD_LABEL,"","",$1.inicio);
}
'(' cond ')'
{
gc(CTD_LABEL,"","",$4.true);
}
DO list_sent END
{
gc(CTD_GOTO,"","",$1.inicio);
gc(CTD_LABEL,"","",$4.false);
}
| REPEAT
{
strcpy($1.inicio,nuevaetiqueta());
gc(CTD_LABEL,"","",$1.inicio);
}
list_sent UNTIL '(' cond ')'
{
gc(CTD_LABEL,"","",$6.false);
gc(CTD_GOTO,"","",$1.inicio);
gc(CTD_LABEL,"","",$6.true);
}
| FOR IDENT ASIG exp
{
if (buscar($2))
{
gc(CTD_ASIG,$4,"",$2);
strcpy($1.inicio,nuevaetiqueta());
strcpy($1.fin,nuevaetiqueta());
gc(CTD_LABEL,"","",$1.inicio);
}
else
yyerror("ERROR Variable no declarada");
}
TO exp
{
gc(CTD_IFMENOR,$7,$2,$1.fin);
}
by list_sent END
{
gc(CTD_SUMA,$2,$9,$2);
gc(CTD_GOTO,"","",$1.inicio);
gc(CTD_LABEL,"","",$1.fin);
}
| IF '(' cond ')' THEN
{
strcpy($1.fin,nuevaetiqueta());
gc(CTD_LABEL,"","",$3.true);
}
list_sent
{
gc(CTD_GOTO,"","",$1.fin);
gc(CTD_LABEL,"","",$3.false);
}
elseif END
{
gc(CTD_LABEL,"","",$1.fin);
}
;
elseif: ELSEIF '(' cond ')' THEN
{
strcpy($1.fin,nuevaetiqueta());
gc(CTD_LABEL,"","",$3.true);
}
list_sent
{
gc(CTD_GOTO,"","",$1.fin);
gc(CTD_LABEL,"","",$3.false);
}
elseif
{
gc(CTD_LABEL,"","",$1.fin);
}
| ELSE list_sent
| /* vacio */
;
by: /* vacio */
{
strcpy($$,"1");
}
| BY ENTERO
{
strcpy($$,$2);
}
;
exp: ENTERO
{
strcpy($$,$1);
}
/* Antes de pasarle a EXP el nombre que tenga asociado IDENT comprueba si el identificador está en la tabla de simbolos, sino no podrá trabajar con ella. */
| IDENT
{
if (buscar($1))
strcpy($$,$1);
else
yyerror("ERROR Variable no declarada");
}
/*NOTA: No hace comprobación de tipo cuando realiza operaciones aritméticas.*/
| exp '+' exp {
strcpy($$,nuevatemp());
gc(CTD_SUMA,$1,$3,$$);
}
| exp '-' exp {
strcpy($$,nuevatemp());
gc(CTD_RESTA,$1,$3,$$);
}
| exp '*' exp {
strcpy($$,nuevatemp());
gc(CTD_PROD,$1,$3,$$);
}
| exp '/' exp {
strcpy($$,nuevatemp());
gc(CTD_DIV,$1,$3,$$);
}
| exp DIV exp {
strcpy($$,nuevatemp());
gc(CTD_DIV,$1,$3,$$);
}
| exp MOD exp
{
char *t1, *t2;
t1 = nuevatemp();
t2 = nuevatemp();
strcpy($$,nuevatemp());
gc(CTD_DIV,$1,$3,t1);
gc(CTD_PROD,t1,$3,t2);
gc(CTD_RESTA,$1,t2,$$);
}
| '-' exp %prec MENOSUNARIO
{
strcpy($$,nuevatemp());
gc(CTD_MENOS,$2,"",$$);
}
| '(' exp ')'
{ strcpy($$,$2); }
;
cond: exp LT exp { /* < */
strcpy($$.true,nuevaetiqueta());
strcpy($$.false,nuevaetiqueta());
gc(CTD_IFMENOR,$1,$3,$$.true);
gc(CTD_GOTO,"","",$$.false);
}
| exp LE exp { /* <= */
strcpy($$.true,nuevaetiqueta());
strcpy($$.false,nuevaetiqueta());
gc(CTD_IFMENOR,$3,$1,$$.false);
gc(CTD_GOTO,"","",$$.true);
}
| exp GT exp { /* > */
strcpy($$.true,nuevaetiqueta());
strcpy($$.false,nuevaetiqueta());
gc(CTD_IFMENOR,$3,$1,$$.true);
gc(CTD_GOTO,"","",$$.false);
}
| exp GE exp
{ /* >= */
strcpy($$.true,nuevaetiqueta());
strcpy($$.false,nuevaetiqueta());
gc(CTD_IFMENOR,$1,$3,$$.false);
gc(CTD_GOTO,"","",$$.true);
}
| exp EQ exp { /* = */
strcpy($$.true,nuevaetiqueta());
strcpy($$.false,nuevaetiqueta());
gc(CTD_IFIGUAL,$1,$3,$$.true);
gc(CTD_GOTO,"","",$$.false);
}
| exp NE exp { /* <> */
strcpy($$.true,nuevaetiqueta());
strcpy($$.false,nuevaetiqueta());
gc(CTD_IFIGUAL,$1,$3,$$.false);
gc(CTD_GOTO,"","",$$.true);
}
| cond AND {
gc(CTD_LABEL,"","",$1.true);
}
cond {
strcpy($$.true,$4.true);
strcpy($$.false,$4.false);
gc(CTD_LABEL,"","",$1.false);
gc(CTD_GOTO,"","",$4.false);
}
| cond OR {
gc(CTD_LABEL,"","",$1.false);
}
cond {
strcpy($$.true,$4.true);
strcpy($$.false,$4.false);
gc(CTD_LABEL,"","",$1.true);
}
| NOT cond {
strcpy($$.true,$2.false);
strcpy($$.false,$2.true);
}
| '(' cond ')' {
strcpy($$.true,$2.true);
strcpy($$.false,$2.false);
}
;
%%
/* SECCIÓN DE RUTINAS DE USUARIO. */
/* NUEVATEMP: Devuelve el nombre de una nueva variable temporal. */
char* nuevatemp()
{
static contador = 0;
static char* st;
st = (char *) malloc(32);
sprintf(st,"_temp%d_",contador++);
return st;
}
/* NUEVAETIQUETA: Devuelve el nombre de una nueva etiqueta. */
char* nuevaetiqueta()
{
static contador = 0;
static char* st;
st = (char *) malloc(32);
sprintf(st,"_L%d_",contador++);
return st;
}
reglas de PL96.Y:
exp : exp GE exp
{
strcpy($$.true,nuevaetiqueta()); ® Crea una nueva etiqueta a donde saltar cuando la
condicion sea verdadera.
strcpy($$.false,nuevaetiqueta()); ® Crea una nueva etiqueta a donde saltar cuando la
condicion no se cumpla.
gc(CTD_IFMENOR,$1,$3,$$.false); ® IF ($1 < $3) goto $$.false
gc(CTD_GOTO,"","",$$.true); ® goto $$.true
}
exp: expa MOD expb /* MOD es un operador que no existe en Codigo tres direcciones. */
{
char *t1, *t2;
t1 = nuevatemp(); ® Crea un nueva variable temporal.
t2 = nuevatemp(); ® Crea otra nueva variable temporal.
strcpy($$,nuevatemp()); ® Asigna a $$ una nueva temporal
gc(CTD_DIV,$1,$3,t1); ® t1 := expa / expb
gc(CTD_PROD,t1,$3,t2); ® t2:= t1 * expb
gc(CTD_RESTA,$1,t2,$$); ® $$ := expa - t2
}
sentencia: WHILE
{
sprintf($1.inicio,nuevaetiqueta()); ® Crea una nueva etiqueta $1.inicio
gc(CTD_LABEL,"","",$1.inicio); ® label $1.inicio
}
'(' cond ')' ® IF (cond) goto LV
goto LF
{
gc(CTD_LABEL,"","",$4.true); ® label $4.true
}
DO list_sent END ® Escribirá la lista de sentencias.
{
gc(CTD_GOTO,"","",$1.inicio); ® goto $1.inicio
gc(CTD_LABEL,"","",$4.false); ® label $4.false
}
sentencia: IF ‘(‘ cond ‘)’ THEN ® if (cond) goto $3.true
goto $3.false
{ strcpy($1.fin,nuevaetiqueta()); ® Label $3.true
gc(CTD_LABEL,"","",$3.true);
}
list_sent ® list_sent
{
gc(CTD_GOTO,"","",$1.fin); ® Goto $1.fin
gc(CTD_LABEL,"","",$3.false); ® Label $3.false
}
elseif END ® Una vez reducida la regla elseif
{
gc(CTD_LABEL,"","",$1.fin); ® Label $1.fin
elseif: ELSEIF ‘(‘ cond ‘)’ THEN ® if (cond) goto $3.true
goto $3.false
{
strcpy($1.fin,nuevaetiqueta()); ® Crea una nueva etiqueta que asocia al token
ELSEIF
gc(CTD_LABEL,"","",$3.true); ® Label $3.true
}
list_sent ® Escribe la lista de sentencias que haya
{
gc(CTD_GOTO,"",""$1.fin); ® Goto $1.fin
gc(CTD_LABEL,"","",$3.false); ® Label $3.false
}
elseif ® Una vez reducida la regla elseif
{
gc(CTD_LABEL,"","",$1.fin); ® Label $1.fin
}
sentencia: REPEAT
{
sprintf($1.inicio,nuevaetiqueta()); ® Crea una nueva etiqueta asociada al
token REPEAT
gc(CTD_LABEL,"","",$1.inicio); ® Label $1.inicio
}
list_sent UNTIL '(' cond ')' ® Escribira la lista de sentencias
if (cond) goto $6.true
goto $6.false
{
gc(CTD_LABEL,"","",$6.false); ® Label $6.false
gc(CTD_GOTO,"","",$1.inicio); ® Goto $1.inicio
gc(CTD_LABEL,"","",$6.true); ® Label $6.true
}
sentencia: FOR IDENT ASIG exp
{
if (buscar($2)) ® Busca en la tabla de
simbolos el identificador
{ Está entonces:
gc(CTD_ASIG,$4,"",$2); ® ident = exp
strcpy($1.inicio,nuevaetiqueta()); ® Crea una nueva etiqueta
strcpy($1.fin,nuevaetiqueta()); ® Crea una nueva etiqueta
gc(CTD_LABEL,"","",$1.inicio); ® Label $1.inicio
}
else
yyerror("ERROR Variable no declarada");® La vble no ha sido
} declarada con lo cual no
se le puede asignar un valor
TO exp
{
gc(CTD_IFMENOR,$7,$2,$1.fin); ® if exp < ident goto $1.fin
}
by list_sent END
{
gc(CTD_SUMA,$2,$9,$2); ® ident = (by) + ident
gc(CTD_GOTO,"","",$1.inicio); ® Goto $1.inicio
gc(CTD_LABEL,"","",$1.fin); ® Label $1.fin
}
by: /* vacio */
{
strcpy($$,"1"); ® Si despues del by no va ninguna expresion se supone que el
contador se va decrementando de 1 en 1.
}
| BY ENTERO
{
strcpy($$,$2); ® Si despued del by va un entero entonces el contador del for se
irá decrementando tantas unidades como indique el entero.
El valor del entero lo copiamos al no terminal by para poder
utilizarlo en la regla FOR, donde aparece el no terminal by.
}
declaracion: IDENT ':' tipo ';'
{
$$ = $3; ® Al no terminal declaracion se le pasa el tipo del IDENT
if ( !buscar($1) ) ® Si el identificador no se encuentra en la T.D.S ya, entonces
apuntar($1,$3); ® Se instala en ésta.
else ® Si no se avisa que la vble está duplicada
yyerror("AVISO Variable duplicada");
}
| IDENT ',' declaracion ® Como en una línea se pueden declarar varios
{ identificadores del mismo tipo, se ha contem-
$$ = $3; plado esta posibilidad, y por ello el no terminal declaracion lleva asociado el tipo de
if ( !buscar($1) ) identificador, para que en este caso sepamos
apuntar($1,$3); de que tipo es IDENT.
else
yyerror("AVISO Variable duplicada");
}
tipo: INTEGER { $$ = TIPOENTERO; } ® El no terminal tipo llevará asociado como
| REAL { $$ = TIPOREAL; } valor el tipo
| CHAR { $$ = TIPOCARACTER; }