Documente Academic
Documente Profesional
Documente Cultură
Prctica de Procesadores
del Lenguaje II
Enunciado de la prctica y especificacin del
lenguaje cUNED (v1.0)
1 Introduccin ........................................................................................................................... 4
2 Descripcin del lenguaje ........................................................................................................ 4
2.1 Aspectos Lxicos ............................................................................................................. 4
2
3.2.2 Formato de entrega............................................................................................... 33
3.2.3.5 Dificultades..................................................................................................... 36
4 Herramientas ....................................................................................................................... 37
4.1 JFlex .............................................................................................................................. 37
3
1 Introduccin
En este documento se define la prctica de la asignatura Procesadores del Lenguaje II
correspondiente al curso 2016-2017. El objetivo de la prctica es realizar un compilador del
lenguaje cUNED, con el que se trabaj en la prctica de la asignatura Procesadores del
Lenguaje I del curso 2015-2016, que es una variacin del conocido lenguaje de programacin
C.
Primero se presenta una descripcin del lenguaje elegido y las caractersticas especiales que
tiene. A continuacin se indicar el trabajo a realizar por los alumnos en diferentes fases, junto
con las herramientas a utilizar para su realizacin.
Desde el punto de vista lxico, un programa es una secuencia ordenada de TOKENS. Un TOKEN
es una entidad lxica indivisible que tiene un sentido nico dentro del lenguaje. En trminos
generales es posible distinguir diferentes tipos de TOKENS: operadores aritmticos,
relacionales y lgicos, delimitadores como los parntesis o los corchetes, identificadores
utilizados para nombrar variables, constantes, tipos definidos por el usuario, nombres de
procedimientos, o las palabras reservadas del lenguaje son algunos ejemplos significativos. A
lo largo de esta seccin describiremos en detalle cada uno de estos tipos junto con otros
elementos que deben ser tratados por la fase de anlisis lxico del compilador.
2.1.1 Comentarios
Un comentario es una secuencia de caracteres que se encuentra encerrada entre los
delimitadores de principio de comentario y final de comentario: /* y */, respectivamente.
Todos los caracteres encerrados dentro de un comentario deben ser ignorados por el
analizador lxico. En este sentido su procesamiento no debe generar TOKENS que se
comuniquen a las fases posteriores del compilador. En el caso de este compilador s es posible
4
realizar anidamiento de comentarios. De esta manera, dentro de un comentario pueden
aparecer los delimitadores de comentario /* y */ para acotar el comentario anidado. El
analizador lxico deber gestionar apropiadamente el anidamiento de comentarios para
garantizar que los delimitadores de principio y fin de comentario estn adecuadamente
balanceados. En caso de que esto no se produzca el proceso de anlisis debe finalizar
emitiendo un mensaje de error lxico. Algunos ejemplos de comentarios correctos e
incorrectos son los siguientes:
/* Comentario Anidado 1
*/
*/
5
nicamente para poder escribir mensajes de texto por pantalla mediante la instruccin
printc()(ver ms adelante), pero no es necesario tratarlas en ningn otro contexto.
Es decir, no se crearn variables de este tipo. No se tendrn en cuenta el tratamiento
de caracteres especiales dentro de la cadena ni tampoco secuencias de escape.
2.1.3 Identificadores
Un identificador consiste, desde el punto de vista lxico, en una secuencia ordenada de
caracteres y dgitos que comienzan obligatoriamente por una letra. Los identificadores se usan
para nombrar entidades del programa tales como las constantes, los tipos definidos por el
usuario, las variables o los subprogramas definidos por el programador. El lenguaje s es
sensible a las maysculas (case sensitive), lo que significa que dos identificadores compuestos
de los mismos caracteres y que difieran nicamente en el uso de maysculas o minsculas se
consideran diferentes. Por ejemplo, Abc y ABC son identificadores diferentes. La longitud de
los identificadores no est restringida, pero el alumno es libre de hacerlo en caso de que lo
considere necesario.
A continuacin se muestra una tabla con las palabras reservadas del lenguaje as como una
breve descripcin aclarativa de las mismas. Su uso se ver en ms profundidad en los
siguientes apartados.
6
de lnea al final
2.1.5 Delimitadores
El lenguaje define una coleccin de delimitadores que se utilizan en diferentes contextos. A
continuacin ofrecemos una relacin de cada uno de ellos:
DELIMITADOR DESCRIPCIN
/* */ Delimitadores de comentario
7
2.1.6 Operadores
Existen diferentes tipos de operadores que son utilizados para construir expresiones por
combinacin de otras ms sencillas como se discutir ms adelante. En concreto podemos
distinguir los siguientes tipos:
+ (suma aritmtica)
Operadores aritmticos
- (resta aritmtica)
< (menor)
Operadores relacionales
> (mayor)
== (igual)
!= (distinto)
= (asignacin)
Operadores de asignacin
Obsrvese que, como se advirti con anterioridad, no se consideran los operadores unarios +
y , de forma que los literales numricos que aparezcan en los programas sern siempre sin
signo (positivos). As, los nmeros negativos no aparecern en el lenguaje fuente, pero s
pueden surgir en tiempo de ejecucin como resultado de evaluar una expresin aritmtica de
resultado negativo, como por ejemplo 1 - 4.
8
2.2.1 Estructura de un programa y mbitos de visibilidad
Desde el punto de vista sintctico, un programa en cUNED es un fichero de cdigo fuente con
extensin .cuned que contiene una coleccin de declaraciones. En el cdigo fuente del listado
2 se muestra un esquema general de la estructura de un programa cUNED.
Como puede apreciarse un programa en cUNED comienza con la declaracin opcional de las
constantes simblicas que se van a utilizar en el programa. En caso de existir, stas solamente
pueden ir al inicio del programa. A continuacin deben declararse primero los tipos y despus
las variables que se utilizarn globalmente en todo el programa. Un programa se completa con
una coleccin ordenada de declaracin de funciones. A este respecto existe una restriccin
importante:
Toda funcin que sea invocada por otra funcin debe ser declarada previamente
dentro del cdigo fuente del programa. Es decir, si g invoca a f, f debe declararse antes
que g.
Existe una funcin principal, denominada main, con una cabecera fija y bien definida
cuyo cdigo constituye el punto de arranque del programa y que debe ser declarada
obligatoriamente siempre al final del programa. La funcin main se explicar ms
detalladamente a lo largo de este documento.
9
En esta prctica las constantes simblicas representan literales de valor entero y positivo por
lo que son entidades de tipo entero (consulte la descripcin de tipos en la seccin 2.2.3.1 de
este documento). La sintaxis para su declaracin es la siguiente:
Donde nombre es el nombre simblico que recibe la constante definida dentro del programa
y valor un valor constante literal de tipo entero positivo (nmero sin signo) de manera que
cada vez que ste aparece referenciado dentro del cdigo se sustituye por su valor establecido
en la declaracin. La clusula #define no marca el inicio de la seccin de declaraciones de
constantes, sino que se usa en cada declaracin de constante simblica. Para cada constante
declarada es necesario utilizar esta clusula, terminando obligatoriamente en ;. Por convenio
el nombre de la constante suele expresarse en maysculas, lo que ayuda a la hora de leer el
cdigo final, pero no es obligatorio y pueden declararse tambin en minsculas.
#define FALSE 0;
#define TRUE 1;
#define LUNES 1;
10
El tipo entero se representa con la palabra reservada int. La aplicacin de este tipo aparece
en distintos contextos sintcticos como en la declaracin de variables, la declaracin de
parmetros y tipo de retorno de funciones o la definicin de tipos compuestos.
int a;
int a=3;
int a [5][2];
struct Punto {
int x;
int y;
printi (x);
11
2.2.3.2 Tipos Estructurados
Los tipos estructurados (tambin llamados tipos definidos por el usuario o tipos compuestos)
permiten al programador definir estructuras de datos complejas y establecerlas como un tipo
ms del lenguaje. Estas estructuras reciben un nombre (identificador) que sirve para
referenciarlas posteriormente en la declaracin de variables, parmetros, campos de registros,
tipos base de matrices, etc. Por eso, es preciso declararlos siempre antes que los elementos
donde aparecen referidos.
En lo que respecta a la equivalencia de tipos se considera que dos tipos son equivalentes
nicamente si tienen el mismo nombre. Por ejemplo dos variables sern del mismo tipo si su
tipificacin hace referencia a una misma declaracin de tipo (o tipo primitivo),
independientemente de la estructura interna de los mismos. Es decir, la tipificacin en cUNED
es nominal
Tipo Matriz
Una matriz es una estructura de datos bidimensional que sirve para almacenar una secuencia
ordenada de valores de tipo entero. Es decir, en cUNED slo pueden declararse matrices de
dos dimensiones y cuyo tipo base sea entero. El tamao de una matriz limita el nmero de
elementos que pueden ser almacenados en la misma en tiempo de ejecucin. ste se define
en su declaracin a travs de dos constantes (literales o simblicas) conocidas en tiempo de
compilacin. Esto significa que el tamao de cada dimensin nunca puede ser una expresin.
La sintaxis de declaracin para matrices es la siguiente:
#const MAXROW 7;
#const MAXCOL 7;
int matriz1[MAXROW][MAXCOL];
int matriz2[3][2];
int matriz3[MAXROW][3];
12
Como se ha descrito, en cUNED no se pueden definir vectores ni arrays multidimensionales. A
todos los efectos un vector se podra declarar asignando un tamao 0 al nmero de filas o
columnas de una matriz. El acceso a los datos de una matriz es una expresin y como tal se
explicar ms adelante.
Tipo Registro
Un registro es una estructura de datos compuesta que permite agrupar varios elementos de
distinto tipo. Para definir un registro se utiliza la palabra reservada struct seguida de una lista
de declaraciones de campos encerradas entre llaves. Cada declaracin de campos comienza
por un identificador de tipo (primitivo o compuesto) seguido de una lista de identificadores de
campos separadas por comas y finalizadas en punto y coma. Concretamente, la declaracin de
un registro sigue la siguiente sintaxis:
struct nombre {
...
Donde nombre ser el identificador del tipo registro, y campoij, son los nombres de los
distintos campos del registro. Los tipos de los campos slo pueden ser enteros o registros. No
es posible declarar matrices como campos de un registro. Para declarar un registro cuyos
campos son registros es preciso declarar primero los registros de los campos y a continuacin
declarar el registro compuesto de otros registros. El nivel de anidacin de registros no puede
ser mayor que dos. Es decir, no puede existir un registro que contenga un campo de tipo
registro y que ste, a su vez, contenga un campo de tipo registro. A continuacin se muestran
varios ejemplos de uso de registros.
struct Tpersona {
int dni;
int edad;
struct Tfecha {
struct Tcita {
Tpersona empleado;
Tfecha fecha;
13
int urgente;
struct TCitaDoble {
TCita cita;
int a;
int a,b;
int a=1, b;
14
matriz1 m3[1][2]=2; /* ERROR. La asignacin en lnea solo est
El uso de funciones con y sin valor de retorno se aplica en contextos sintcticos diferentes. Las
funciones con valor de retorno se emplean en el contexto de una expresin mientras que las
funciones sin valor de retorno se consideran sentencias. En las secciones 2.2.6.1 (Invocacin de
funciones) y 2.2.6.2 (Sentencias de invocacin a una funcin void) abordaremos la invocacin
de funciones en esos dos casos. Por ahora nos ocuparemos de describir la sintaxis de su
declaracin:
return expresin;
En esta definicin los corchetes indican opcionalidad. Como puede apreciarse, la declaracin
de la funcin comienza por tipo-retorno que indica el tipo de retorno de la funcin. ste
debe ser necesariamente un tipo primitivo (int o void). A esto le sigue el nombre de la funcin
y despus, opcionalmente, una lista de parmetros con nombre separados por comas. Si la
funcin no tiene parmetros, al nombre de la funcin le siguen parntesis vacios (). Ntese
que cada declaracin de parmetro debe ir emparejado con su tipo correspondiente.
Una sentencia relevante dentro del cuerpo de la funcin es la sentencia return. Esta
instruccin indica un punto de salida de la funcin y provoca que el flujo de control de
ejecucin pase de la funcin llamada a la siguiente instruccin dentro de la funcin llamante.
Ntese que una funcin debe tener una instruccin de retorno por cada punto de salida. La
sentencia return puede venir seguida de una expresin que indique el valor de retorno. El
15
tipo de esta expresin debe coincidir, naturalmente, con el tipo de retorno especificado en la
cabecera de la funcin. En casos como ste, la sentencia return evala la expresin y
devuelve su valor al contexto del programa llamante.
Cuando la funcin ha sido declarada con void como tipo de retorno, la sentencia return es
opcional y, en caso de aparecer, no viene acompaada de ninguna expresin (en otro caso
sera un error). Es importante para realizar la evaluacin por parte del equipo docente que el
compilador funcione tanto si existe return como si no en las funciones declaradas con void.
void nada () {
return x + y;
printi (x);
return;
void saluda () {
int devuelve2 () {
return 2;
Algunos de los errores en funciones son de tipo sintctico, como por ejemplo construir mal la
cabecera. Existen, sin embargo, otro tipo de errores que deben ser comprobados en otras
fases. Por ejemplo, la comprobacin de la existencia de return se realiza dentro del anlisis
semntico.
void main () {
16
[<< declaracin de tipos locales >>]
Notese que se trata de una funcin sin valor de retorno ni parmetros y cuyo cuerpo es
estructuralmente idntico a la de cualquier funcin sin valor de retorno. Cuando se ejecuta el
programa, se ejecuta el contenido de la funcin main.
En este caso el compilador realiza una copia del argumento a otra zona de memoria para que
el subprograma pueda trabajar con l sin modificar el valor del argumento tras la ejecucin de
la invocacin. Los parmetros pasados por valor no pueden ser matrices ni registros enteros.
En este caso el parmetro slo podr ser una referencia (no una expresin) y el compilador
transmite al subprograma la direccin de memoria donde est almacenado el parmetro
actual, de forma que las modificaciones que se hagan dentro de ste tendrn efecto sobre el
argumento una vez terminada la ejecucin del mismo. El paso de parmetros por referencia se
utiliza para pasar argumentos de salida o de entrada / salida. Para indicar que un parmetro se
pasa por referencia debe precederse su nombre con el carcter & en la declaracin de la
funcin. Se considera un error de compilacin pasar expresiones como argumentos actuales a
una funcin donde se ha declarado un parmetro formal de salida o entrada/salida. En el
listado 10 se incluyen ejemplos de funciones que utilizan paso por valor y por referencia:
x=x+1;
return x;
17
void decrementa (int &x) { /* Paso por referencia */
x=x-1;
void main () {
int a=1;
incrementa (a);
a = incrementa (a);
decrementa (a);
return;
2.2.6.1 Expresiones
Una expresin es una construccin del lenguaje que devuelve un valor de retorno al contexto
sintctico del programa donde se evalu la expresin. Las expresiones no deben aparecer de
forma aislada en el cdigo. Es decir, han de estar incluidas como parte de una sentencia all
donde se espere una expresin. Desde un punto de vista conceptual es posible clasificar las
expresiones de un programa en cUNED en varios grupos:
Expresiones aritmticas
Las expresiones aritmticas son aquellas cuyo cmputo devuelve un valor de tipo entero (int)
al contexto de evaluacin. Puede afirmarse que son expresiones aritmticas las constantes
literales de tipo entero, las constantes simblicas de tipo entero, los identificadores (variables
o parmetros) de tipo entero y las funciones que tienen declarado un valor de retorno de tipo
entero. Asimismo, tambin son expresiones aritmticas la suma y resta de dos expresiones
aritmticas.
Expresiones lgicas
Las expresiones lgicas son aquellas cuyo cmputo devuelve un valor de tipo lgico al contexto
de evaluacin. En nuestro caso no existe un tipo primitivo para tipificar expresiones de verdad
(cierto / falso). En su lugar, se utiliza una convencin para codificar numricamente valores
18
lgicos de verdad o falsedad. En concreto ha de suponerse que cuando una expresin
aritmtica se evala en un contexto lgico (como en el caso de la expresin condicional de un
bucle while) y devuelve un valor igual a 0 este resultado se debe interpretar con el significado
de falso, mientras que si el resultado es distinto de 0 la interpretacin ser de cierto. A partir
de ahora, llamaremos expresin lgica a toda expresin aritmtica que es evaluada en un
contexto lgico. Asimismo tambin son expresiones lgicas la conjuncin && y disyuncin
|| de dos expresiones aritmticas. Se incluyen una serie de operadores relacionales que
permite comparar expresiones aritmticas y lgicas entre s. El resultado de esa comparacin
es tambin una expresin lgica. Es decir, la comparacin con los operadores >, <, == o != de
dos expresiones aritmticas o lgicas es tambin una expresin lgica. Para el desarrollo de la
prctica se supondr que si al realizar una operacin lgica su valor es verdadero se ha de
devolver el valor 1.
Expresiones de asignacin
Las expresiones de asignacin sirven para asignar un valor a una variable, elemento de una
matriz o campo de un registro. Para ello se escribe primero una referencia a alguno de estos
elementos seguido del operador de asignacin = y a su derecha una expresin. El compilador
deber comprobar en primer lugar la compatibilidad entre el tipo de la expresin a la derecha
del operador de asignacin con el de la referencia a la izquierda. El operador de asignacin no
es asociativo. Es decir no es posible escribir construcciones sintcticas del estilo a = b = c.
Tampoco es posible hacer asignaciones a estructuras (matrices o registros) de forma directa.
La sintaxis de la expresin de asignacin es a:
ref = expresion;
Donde ref es una referencia a una posicin de memoria (variable, parmetro, campo de
registro o elemento de una matriz) y expresin una expresin que le da valor. El siguiente
listado muestra algunos ejemplos de uso de sentencias de asignacin:
i = 3 + 7;
m[i][j] = 10;
distinto = 3!=4;
cita1.urgente = 0;
a = 2 + suma(2,2);
asignacin */
19
asignacin (=), pero adicionalmente devuelve el valor del resultado de la asignacin al contexto
sintctico donde esta instruccin se ejecuta. Esto permite que las asignaciones puedan
escribirse en lugares donde tpicamente se esperara una expresin. Por ejemplo, en el
fragmento de sentencia condicional if (a=3>1) se evala primero el valor de la expresin 3>1.
Como el resultado es verdadero, el valor que devuelve al contexto es 1 (cierto) y ese valor es
asignado a la variable a y simultneamente devuelto al contexto de la sentencia condicional
que, al ser un valor distinto de 0 provoca que se entre a ejecutar el cuerpo del condicional.
int x;
TCita cita1;
TCita cita2;
cita1.urgente = 1;
cita1.empleado.dni = 1234;
cita1.empleado.edad = 23;
cita1.fecha.dia = 2;
cita1.fecha.mes = 11;
cita1.fecha.anyo = 2016;
x = cita1.empleado.edad;
cita2.empleado.edad = x;
cita2.urgente = cita2.urgente;
20
aritmticas para acceder a un elemento de una matriz, la comprobacin de que no supere al
ndice de la declaracin se debera hacer en tiempo de ejecucin. Para simplificar el desarrollo
de la prctica, no se tiene que detectar este tipo de errores. En caso de acceder mediante
constantes enteras literales, s es posible realizar esa comprobacin en la fase de anlisis
semntico, y de hecho se debe de hacer en la prctica.
matrizVector m1;
matrizDos m2;
m1[0][0]=1;
m1[0][1]=2;
m1[0][2]=3;
m2[1][0]=5;
m2[0][0]=m1[0][2];
m2[0][1]=0;
Invocacin de funciones
Para llamar a una funcin ha de escribirse su identificador indicando entre parntesis los
parmetros actuales de la llamada, que deben coincidir en nmero, tipo y orden con los
definidos en la declaracin de la funcin. Los parmetros pueden ser referencias a variables,
campos de registros o elementos de matriz o expresiones. El uso de los parntesis es siempre
obligatorio. As, si una funcin no tiene argumentos, en su llamada es necesario colocar unos
parntesis vacios () tras su identificador.
Ntese que las funciones que devuelven un tipo void no son expresiones. Por tanto, si se
utilizan en el lugar de una expresin debe generarse un error. Esta comprobacin se realiza en
el anlisis semntico. Posteriormente, en la seccin 2.2.6.2 (Sentencias de invocacin a una
funcin void) abordaremos este tipo de sentencias.
X = resta(a, b);
21
y = suma(a, resta(b, c));
a = funcion1();
Evaluacin de expresiones
El modelo de evaluacin de expresiones lgicas de cUNED es en cortocircuito o modo de
evaluacin perezosa. Este modo de evaluacin prescribe que: 1) si en la evaluacin de una
conjuncin (&&) de dos expresiones la primera de las evaluadas es falsa, la otra no debe
evaluarse y 2) si en la evaluacin de una disyuncin (||) de dos expresiones la primera de las
evaluadas es verdadera, la otra no debe evaluarse.
Precedencia Asociatividad
. ( )
Izquierda
[]
Derecha
+ - Izquierda
== != Izquierda
&& Izquierda
|| Izquierda
= Derecha
Para alterar el orden de evaluacin de las operaciones en una expresin aritmtica o lgica
prescrita por las reglas de prelacin se puede hacer uso de los parntesis. Como puede
apreciarse los parntesis son los operadores de mayor precedencia. Esto implica que toda
expresin aritmtica encerrada entre parntesis es tambin una expresin aritmtica. En el
siguiente listado se exponen algunos ejemplos sobre precedencia y asociatividad y cmo la
parentizacin puede alterar la misma.
22
Listado 11. Ejemplos de precedencia y asociatividad en expresiones
2 + (2 3) /* Devuelve 1 */
(2 + 2) 3 /* Devuelve 1 */
2.2.6.2 Sentencias
El lenguaje propuesto dispone de una serie de sentencias que sirven a distintos propsitos
dentro de un programa. Las sentencias se escriben dentro del cuerpo de las funciones
declaradas en el mismo (ya sea la funcin principal o funciones declaradas por el usuario). En
efecto, tal como se describi en la seccin 2.2.5, tras la declaracin de tipos y variables
comienza una secuencia de sentencias que constituyen el cdigo de la funcin. A lo largo de
esta seccin describiremos todos los tipos de sentencias existentes en cUNED a excepcin de
la sentencia return que ya fue explicada en la seccin 2.2.5.
Bloques de sentencias
Un bloque de sentencias es una coleccin de declaraciones y sentencias encerradas entre los
delimitadores de llaves { y } . La sintaxis de un bloque de sentencias es la siguiente:
Como puede apreciarse, dentro de un bloque aparece inicialmente una coleccin opcional de
declaraciones de tipos definidas por el usuario. Seguidamente aparece una coleccin opcional
de declaraciones de variables y finalmente una coleccin de sentencias tambin opcional. A
todos los efectos, un bloque de sentencias se comporta como una sentencia ordinaria. Esto
implica varias cosas. En primer lugar que los bloques de sentencia pueden aparecer unos
dentro de otros sin ninguna restriccin en cuanto al nivel de anidamiento. En segundo lugar,
que en cualquier punto, a lo largo de este documento, donde indiquemos el uso de una
sentencia puede aparecer perfectamente un bloque de sentencias en su lugar (a no ser que se
indique expresamente lo contrario). No obstante, la nica diferencia sintctica entre un bloque
de sentencias y una sentencia es que el primero no lleva un punto y coma ; tras la llave }
mientras que las sentencias simples obligatoriamente si lo llevan.
23
declarar nuevos bloques anidados, en los bloques hijos se vern las declaraciones locales y las
realizadas en cada uno de los bloques de la jerarqua de anidamiento. El listado 16 muestra un
ejemplo de uso de bloques anidados.
int x = 0;
void f () {
int a=1, b = 1;
{ int a=2, c = 2;
{ int d = 3
Esta estructura sintctica articula una coleccin anidada de mbitos de visibilidad. En este
sentido podemos distinguir entre: 1) el mbito de visibilidad global asociado a las
declaraciones realizadas al inicio del programa, que es visible por todas las funciones
24
declaradas; 2) el mbito de visibilidad asociado a cada funcin y 3) los mbitos de visibilidad
asociados a bloques annimos declarados dentro del cuerpo de cada funcin. Cuando
hacemos referencia a una variable desde cualquier punto del programa podemos caer en uno
de los 3 casos siguientes:
Referencias globales. Las referencias globales son aquellas referencias a variables que
han sido declaradas en el mbito global del programa.
Referencias locales. Las referencias locales son aquellas variables que se encuentran
declaradas dentro del mbito local donde se realiza la referencia (vase ejemplo del
listado 16).
if (expresinLogica)
<< sentencia o bloque de sentencias 1 >>
[else
<< sentencia o bloque de sentencias 2 >>]
Los corchetes en este caso indican una parte opcional, es decir, puede aparecer o no.
expresionLogica es una expresin lgica, es decir aritmtica, que se evala para
comprobar si debe ejecutarse la sentencia o bloques de sentencias 1 o 2. En concreto si el
resultado de dicha expresin es cierta, es decir, es distinto de 0, se ejecuta el bloque de
sentencias 1. Si existe else y la condicin es falsa (0), se ejecuta el bloque de sentencias 2.
Es necesario que expresionLogica est siempre entre parntesis.
Este tipo de construcciones puede anidarse con otras construcciones de tipo if-then-else o con
otros tipos de sentencias de control de flujo que estudiaremos a continuacin. Hay que
destacar que esta sentencia es inherentemente ambigua debido al problema del else
ambiguo. Esta ambigedad se da cuando se declaran varios if de forma consecutiva y un
nico else al final, ya que no queda claro con que sentencia if debe asociarse el else. Se asume
para resolverla que el else viene siempre asociado al if sintcticamente ms cercano, forzando
una mxima precedencia al else. El listado 17 ilustra varios ejemplos de uso.
if (a==b)
a=a+1;
25
/* Una sola sentencia, pero usando un bloque */
if (a==b) {
a=a+1;
/* varias sentencias */
if (a==b) {
a=a+1;
b=b+1;
/* uso de else */
if (esCierto==1)
valor=1;
else
valor=0;
if (esCierto==1){
valor=1;
a=a+1;
else
valor=0;
if (esCierto==1){
valor=1;
a=a+1;
else {
26
valor=0;
a=a-1;
if (esCierto==1){
valor=1;
else {
valor=0;
while (expresionLogica)
La sentencia while permite que otras estructuras de control formen parte del bloque de
sentencias a iterar de forma que se creen anidamientos. En el listado 18 se muestran unos
ejemplos de uso.
while (a<5){
a=a+1;
escribeEnt (a);
27
while (a<5)
a=a+1;
while (a<5) {
a=a+1;
Hay que hacer notar que no es posible declarar la variable indice dentro de la sentencia
for. La declaracin de variables ha de ser anterior a la de las sentencias. La estructura for
permite que otras estructuras de control formen parte del bloque de sentencia a iterar de
forma que se creen anidamientos, de forma anloga a lo explicado en la sentencia while. El
listado 19 muestra unos ejemplos de sentencia for.
for (a=0;a<5;a=a+1)
b=b+1;
x=2;
/* con bloque */
for (a=0;a<5;a=a+1) {
printi(a);
b=b+1;
28
}
En caso de no recibir ningn parmetro no escribira nada por pantalla, pero no sera
un error. Este procedimiento slo admite un nico parmetro. Indicar que se debe
generar un salto de lnea despus de mostrar el resultado por pantalla (se considerar
un error en la prctica no hacerlo).
29
texto entre comillas y se debe generar un salto de lnea despus de mostrar el
resultado por pantalla (se considerar un error en la prctica no hacerlo).
printi (x+1);
printi (cita1.urgente);
printi (a<b);
printc (hola);
especiales*/
Como mnimo se exige que el compilador indique el tipo de error: lxico, sintctico o
semntico. Los errores lxicos y sintcticos los genera la estructura proporcionada. Por lo
dems, se valorarn intentos de aportar ms informacin sobre la naturaleza del error
semntico. Por ejemplo, los errores motivados por la comprobacin explcita de tipos de
acuerdo al sistema de tipos del lenguaje constituyen errores de carcter semntico. Otros
ejemplos de errores semnticos son: identificador duplicado, tipo errneo de variable, variable
no declarada, subprograma no definido, campo de registro inexistente, campo de registro
duplicado, demasiados parmetros en llamada a subprograma, etc.:
30
3 Descripcin del trabajo
En esta seccin se describe el trabajo que ha de realizar el alumno. La prctica es un trabajo
amplio que exige tiempo y dedicacin. De cara a cumplir los plazos de entrega, recomendamos
avanzar constantemente sin dejar todo el trabajo para el final. Se debe abordar etapa por
etapa, pero hay que saber que todas las etapas estn ntimamente ligadas entre si, de forma
que es complicado separar unas de otras. De hecho, es muy frecuente tener que revisar en un
punto decisiones tomadas en partes anteriores.
Adems, se proporciona una implementacin del analizador lxico (fichero scanner.flex) y del
analizador sintctico (fichero parser.cup) de la que se puede partir. No es obligatorio usar estas
implementaciones. De hecho, la implementacin dada del anlisis lxico y sintctico se puede
modificar para adaptarla al desarrollo de la prctica que realice cada alumno. Para cada
especificacin del lenguaje (ver siguiente apartado con la divisin del trabajo) se proporciona
una de estas implementaciones.
Es responsabilidad del alumno visitar con asiduidad el tabln de anuncios y el foro del Curso
Virtual, donde se publicarn posibles modificaciones a este y otros documentos y recursos.
Cada alumno deber implementar solamente una de las dos especificaciones. La especificacin
que debe realizar depende de su nmero de DNI. Asi:
El compilador debe respetar la descripcin del lenguaje que se hace a lo largo de esta seccin.
Incorporar caractersticas no contempladas no slo no se valorar, sino que se considerar un
error y puede suponer un suspenso en la prctica.
31
A continuacin se detallan las funcionalidades que incorporan cada una de las especificaciones
A y B. Para cada funcionalidad la "X" indica que esa caracterstica debe implementarse
mientras que el "-" indica que no debe implementarse.
FUNCIONALIDAD A B
Registros X -
Por referencia - X
Operadores aritmticos + - X
- X -
> X -
== X -
!= - X
|| X -
for X -
3.2 Entregas
Antes de empezar, nos remitimos al documento Normas de la asignatura que podr
encontrar en el entorno virtual para ms informacin sobre este tema. Es fundamental que el
alumno conozca en todo momento las normas indicadas en dicho documento. Por tanto en
este apartado se explicar nicamente el contenido que se espera en la entrega.
32
Septiembre 11 de septiembre de 2017
Para entregar su prctica el alumno debe acceder a la seccin Entrega de Trabajos del Curso
Virtual (los enlaces a cada entrega se activan automticamente unos meses antes). Si una vez
entregada desea corregir algo y entregar una nueva versin, puede hacerlo hasta la fecha
lmite. Los profesores no tendrn acceso a los trabajos hasta dicha fecha, y por tanto no
realizarn correcciones o evaluaciones de la prctica antes de tener todos los trabajos. En
ningn caso se enviarn las prcticas por correo electrnico a los profesores.
Puesto que la compilacin y ejecucin de las prcticas de los alumnos se realiza de forma
automatizada, el alumno debe respetar las normas de entrega indicadas en el enunciado de la
prctica.
Se recuerda que es necesario superar una sesin de control obligatoria a lo largo del curso
para aprobar la prctica y la asignatura. En la sesin presencial obligatoria el tutor comprobar
que el alumno ha realizado:
- Anlisis semntico
o comprobacin de tipos
- Cdigo intermedio
(Ejemplo: a-gonzalez1.zip)
33
Dicho archivo contendr la estructura de directorios que se proporcionar en las directrices de
implementacin. Esta estructura debe estar en la raz del fichero zip. No se debe de incluir
dentro de otro directorio, del tipo, por ejemplo: pdl, practica, arquitectura, etc.
En cuanto a la memoria, ser un breve documento llamado "memoria" con extensin .pdf y
situado en el directorio correspondiente de la estructura dada.
Portada obligatoria
4. Indicaciones especiales
En cada apartado habr que incluir nicamente comentarios relevantes sobre cada parte y no
texto de relleno ni descripciones tericas, de forma que la extensin de la memoria est
comprendida aproximadamente entre 2 y 5 hojas. En caso de que la memoria no concuerde
con las decisiones tomadas en la implementacin de cada alumno la prctica puede ser
considerada suspensa.
Es importante tener claro que la entrega debe de contener todo el proceso de compilacin.
Es decir, han de incluirse las siguientes fases: anlisis lxico, anlisis sintctico, anlisis
semntico, generacin de cdigo intermedio y cdigo final. Para las dos primeras etapas
(anlisis lxico y sintctico) se puede usar la implementacin proporcionada, o se puede
realizar una implementacin de las mismas.
34
estructura disponible en tiempo de compilacin que almacena informacin sobre los nombres
definidos por el usuario en el programa fuente, llamados smbolos en este contexto terico.
Dependiendo del tipo de smbolo encontrado por el compilador durante el procesamiento del
cdigo fuente (constante, variable, funcin), los datos que deben ser almacenados por la TS
sern diferentes. Por ejemplo:
Otra estructura importante es la tabla de tipos (TT), cuya responsabilidad es mantener una
definicin computacional de todos los tipos (primitivos y compuestos) que estn accesibles por
el programador dentro de un programa fuente. Las entradas de la TS mantienen referencias a
esta tabla para tipificar sus elementos (variables, constantes, funciones y procedimientos).
35
nuestro caso ENS2001). Este proceso requiere convertir cada operador en su equivalente en
ENS2001 y en convertir las referencias simblicas de los operandos en direcciones fsicas
reales. Ntese que en funcin del mbito de las mismas (variables globales, locales,
parmetros, temporales, etc.) el modo de direccionamiento puede ser diferente.
Una vez obtenido el cdigo final, ste puede probarse utilizando la herramienta ENS2001 que
permite interpretar el cdigo generado. As podremos ejecutar un programa compilado con
nuestro compilador y probar su funcionamiento.
3.2.3.5 Dificultades
Es muy importante dedicar tiempo a entender el proceso completo de compilacin y
ejecucin, especialmente a la hora de distinguir dos conceptos fundamentales: tiempo de
compilacin y tiempo de ejecucin. La diferencia es que en tiempo de compilacin tenemos a
nuestra disposicin la tabla de smbolos que nos dice, por ejemplo, en qu direccin de
memoria est una determinada variable. Sin embargo, al ejecutar el programa ya no tenemos
ningn apoyo ms que el propio cdigo generado, de forma que el cdigo debe contener toda
la informacin necesaria para que el programa funcione correctamente. Esto resulta
especialmente complicado a la hora de manejar llamadas a funciones, especialmente si son
llamadas recursivas. Para mantener esta informacin en tiempo de ejecucin se reserva un
espacio en memoria llamado registro de activacin, que almacena elementos como la
direccin de retorno, el valor devuelto, los parmetros y las variables locales.
36
4 Herramientas
Para el desarrollo del compilador se utilizan herramientas de apoyo que simplifican
enormemente el trabajo. En concreto, se utilizarn las indicadas en los siguientes apartados.
Para cada una de ellas se incluye su pgina web e informacin relacionada. En el curso virtual
de la asignatura pueden encontrarse una versin de todas estas herramientas junto con
manuales y ejemplos bsicos.
4.1 JFlex
Se usa para especificar analizadores lxicos. Para ello se utilizan reglas que definen expresiones
regulares como patrones en que encajar los caracteres que se van leyendo del archivo fuente,
obteniendo tokens.
4.2 Cup
Esta herramienta permite especificar gramticas formales facilitando el anlisis sintctico para
obtener un analizador ascendente de tipo LALR. Adems Cup tambin permite asociar acciones
java entre los elementos de la parte derecha de la gramtica de forma que stas se vayan
ejecutando a medida que se va construyendo el rbol de anlisis sintctico. Estas acciones
permitirn implementar las fases de anlisis semntico y generacin de cdigo intermedio.
4.4 Ant
Ant es una herramienta muy til para automatizar la compilacin y ejecucin de programas
escritos en java. La generacin de un compilador utilizando JFlex y Cup se realiza mediante una
serie de llamadas a clases java y al compilador de java. Ant permite evitar situaciones
habituales en las que, debido a configuraciones particulares del proceso de compilacin, la
prctica slo funciona en el ordenador del alumno.
37
5 Ayuda e informacin de contacto
Es fundamental y obligatorio que el alumno consulte regularmente el Tabln de Anuncios y el
foro de la asignatura, accesible desde el Curso Virtual para los alumnos matriculados. En caso
de producirse errores en el enunciado o cambios en las fechas siempre se avisar a travs de
este medio. El alumno es, por tanto, responsable de mantenerse informado.
Tambin debe estudiarse bien el documento que contiene las normas y el calendario de la
asignatura, incluido en el Curso Virtual.
Se recomienda tambin la utilizacin de los foros como medio de comunicacin entre alumnos
y de estos con el Equipo Docente. Se habilitarn diferentes foros para cada parte de la
prctica. Se ruega elegir cuidadosamente a qu foro dirigir el mensaje. Esto facilitar que la
respuesta, bien por otros compaeros o por el Equipo Docente, sea ms eficiente.
Esto no significa que la prctica pueda hacerse en comn. Por tanto, no debe compartirse
cdigo. Se utilizar un programa de deteccin de copias al corregir la prctica. La deteccin de
prcticas copiadas supondr el SUSPENSO en todo el curso (junio y septiembre) para todos los
implicados.
38
Anexo A. Programa de ejemplo completo
El siguiente listado muestra un ejemplo de un programa complejo en cUNED que contiene
varios ejemplos de todas las estructuras sintcticas que se han definido en este documento.
/* constantes simblicas */
#define FALSE 0;
#define TRUE 1;
/* tipos globales */
struct Tpersona {
int dni;
int edad;
struct Tfecha {
/* variables globales */
Tpersona persona1;
/* funciones */
printi (x);
if (edad>MAYORDEEDAD){
return TRUE;
39
else{
return FALSE;
int debug;
escribe(dni);
debug=1;
/* nuevo bloque */
int x;
if (debug=1){
x=edad;
escribe(x);
/* Funcin principal */
/* tipos locales*/
struct Tfecha {
struct Tcita {
Tpersona empleado;
40
Tfecha fecha;
int urgente;
/* variables locales*/
Tfecha f;
int numeroCitas;
int urge=1;
/* sentencias y bloques*/
persona1.dni=1234;
persona1.edad=23;
if (mayorDeEdad(persona1.edad)){
int edad;
edad = persona1.edad;
printc("Persona:");
imprimePersona(persona.dni, edad);
printc("Mayor de edad");
f.dia=1;
f.mes=12:
f.anyo=2016;
c1.empleado=persona1;
c1.fecha=f;
c1.urgente=urge;
41