|
Descripción general del lenguaje de programación fuente PF2026
Empecemos con un pequeño programa de ejemplo:
pf2026 Ejem1(a,b)
decl
int c;
rational d;
begin
c:= a+b;
d:= a/b;
print(int2str(a) + " + " + int2str(b) + "= " + int2str(c));
print(int2str(a) + " / " + int2str(b) + "= " + q2str(d));
print(int2str(a) + " / " + int2str(b) + "= " + q2str.decimal(d,3));
end;
Un programa en el lenguaje de programación PF2026 consta de las
siguientes partes:
Palabra clave pf2026 que indica el inicio del
programa.
Un identificador (el nombre del programa).
Opcionalmente, puede declararse una lista de variables de
entrada. Estas variables se escriben entre paréntesis y separadas
por comas, inmediatamente a continuación del nombre del
programa. Estas variables serán siempre de tipo int (el
tipo es implícito, es decir, no se indica el tipo en la declaración)
y servirán para pasarle datos al programa.
También opcionalmente, pueden declararse variables que serán
utilizadas dentro del programa. Las declaraciones de variables están
formadas por un tipo de datos seguido de una lista de identificadores
(nombres de variables). Los tipos de datos existentes en este lenguaje
de programación son int, bool, str y
rational.
Cuando se declara una variable se supondrá que inicialmente
tomará el valor false si la variable es de tipo
bool, 0 si la variable es de tipo int o
rational y "" si la variable es de tipo
str.
Cuerpo del programa, que consta de una lista de
sentencias. Comienza con la palabra clave begin y termina con
end;.
Las sentencias de un programa pueden ser de los siguientes tipos:
Sentencia de asignación: almacena en la variable indicada a la
izquierda del := el valor que se obtiene como
resultado de evaluar la expresión a la derecha del :=.
Bucle while <Exp> do <StamentList> end. La ejecución de esta sentencia comienza evaluando la expresión. Si el resultado es false no se hace nada y se pasa a ejecutar la siguiente sentencia. En caso contrario se ejecuta repetidamente la lista de sentencias contenida dentro del bucle y se vuelve a evaluar la expresión hasta que en una iteración el resultado de evaluar la expresión sea false.
Sentencia print. Esta sentencia recibe como argumento
un dato de tipo str, que imprime en pantalla, seguido de un
salto de línea.
Expresiones en el lenguaje de programación fuente
NOTA: en lo que sigue representaremos los números de tipo
rational de la siguiente forma: pe + num/den,
donde:
- pe es un número entero negativo, positivo o cero.
- num y den son dos números enteros que verifican:
a) den > num >=0; b) si num> 0, entonces el
máximo común divisor de num y den es 1; y c) si
num= 0, entonces den= 1. Puede demostrarse que todo
número racional puede descomponerse de una única forma en 3 números
enteros que cumplan con lo anterior.
Las expresiones del lenguaje de programación fuente (identificadas en
la gramática por el símbolo no terminal <Exp>) deben de
ajustarse a lo siguiente.
Expresiones sin tipo definido:
Expresiones de tipo int:
Una constante expresada en decimal.
Una suma, resta o multiplicación de expresiones
de tipo int utilizando los operadores habituales "+", "-", "*".
El opuesto de una expresión de tipo int, utilizando
el símbolo habitual "-" antes de la expresión.
Una expresión que pertenezca al lenguaje definido por la
expresión lingüística parte.entera "(" <Exp> ")", donde la
subexpresión entre paréntesis ha de ser de tipo rational. El
resultado de evaluar esta expresión será el componente pe del
dato de tipo rational que se obtendría al evaluar la
subexpresión.
Una expresión que pertenezca al lenguaje definido por la
expresión lingüística numerador "(" <Exp> ")", donde la
subexpresión entre paréntesis ha de ser de tipo rational. El
resultado de evaluar esta expresión será el componente num del
dato de tipo rational que se obtendría al evaluar la
subexpresión.
Una expresión que pertenezca al lenguaje definido por la
expresión lingüística denominador "(" <Exp> ")", donde la
subexpresión entre paréntesis ha de ser de tipo rational. El
resultado de evaluar esta expresión será el componente den del
dato de tipo rational que se obtendría al evaluar la
subexpresión.
Expresiones de tipo rational:
Una suma, resta o multiplicación cuando al menos uno de los
dos operandos sea de tipo rational y el otro sea de tipo
rational o de tipo int, utilizando los operadores
habituales "+", "-", "*".
Una división cuando ambos operandos sean de tipo
rational o de tipo int, utilizando el operador
habitual "/". Observe que la división de dos operandos de tipo
int devuelve un dato de tipo rational.
El opuesto de una expresión de tipo rational, utilizando el
símbolo habitual "-" antes de la expresión.
Expresiones de tipo str (cadenas de caracteres):
Una constante de tipo str.
Concatenación de 2 expresiones de tipo str,
utilizando el operador "+".
Una expresión que pertenezca al lenguaje definido por la
expresión lingüística int2str "(" <Exp> ")", donde la
subexpresión entre paréntesis ha de ser de tipo int. El
resultado de evaluar esta expresión será el objeto de la clase
String que se obtendría en Java al invocar el método
Integer.toString sobre el dato que se obtendría al evaluar la
subexpresión de tipo int.
Una expresión que pertenezca al lenguaje definido por la
expresión lingüística q2str "(" <Exp> ")", donde la
subexpresión entre paréntesis ha de ser de tipo rational. El
resultado de evaluar esta expresión será el String "pe +
num/den", donde pe, num y den son las
3 componentes del dato de tipo rational obtenido al evaluar
la subexpresión. Por ejemplo, el resultado de evaluar
q2str(1799/63) será "28 + 5/9".
Una expresión que pertenezca al lenguaje definido por la
expresión lingüística q2str.decimal "(" <Exp>, c ")",
donde la subexpresión entre paréntesis ha de ser de tipo
rational y el segundo argumento una constante entera positiva
o 0 (una cadena de dígitos). El resultado de evaluar esta expresión
será un dato de tipo String que represente el valor obtenido
al evaluar la subexpresión de tipo rational con tantas cifras
decimales como indique el segundo argumento, sin redondeo. Por
ejemplo, el resultado de evaluar q2str.decimal(17/63,3) será
"0.269".
Expresiones de tipo bool:
Las constantes true y false.
La conjunción o disyunción de expresiones lógicas,
utilizando los operadores and y or
respectivamente.
Una expresión lógica negada, utilizando el operador
not antes de la expresión lógica.
La igualdad entre 2 expresiones utilizando el operador
"=". Las dos expresiones comparadas deberán de ser o bien ambas de
tipo bool o bien ambas de tipo numérico (int o
rational).
La comparación entre 2 expresiones de tipo numérico
(int o rational) utilizando los operadores "<"
(menor que) o ">" (mayor que).
Reglas de precedencia
La precedencia de los operadores es, de menor a mayor:
- or
- and
- not
- =
- < y >
- + y -
- * y /
- - (opuesto)
Todos los operadores asocian por la izquierda.
Especificación del analizador léxico
El analizador léxico JLex a desarrollar debe reconocer los
siguientes tokens:
- Palabras clave: begin (token BEGIN),
and (token AND), or (token OR),
not (token NOT), pf2026 (token
PROG), decl (token DECL), while
(token WHILE), do (token DO),
print (token PRINT), int2str (token
INT2STR), parte.entera (token PENT),
numerador (token NUM), denominador (token
DEN), q2str (token Q2STR),
q2str.decimal (token Q2STRD)
y end (token END).
- Las palabras clave para los tipos básicos: int,
bool, str y rational comparten el mismo
token: TYPE.
- Signos de puntuación y operadores: ";" (token PC),
":=" (token ASIG), "<" (token MENOR),
">" (token MAYOR), "+" (token MAS),
"-" (token MENOS),
"/" (token DIV), "*" (token MUL),
"(" (token PAREN), ")" (token TESIS),
"=" (token IGUAL) y "," (token COMA).
- Identificadores (token IDENT): comienzan por una letra
(mayúscula o minúscula), seguida opcionalmente de cualquier cadena de
letras y dígitos.
- Constantes, que pueden ser de tres tipos:
- Números enteros no negativos (token CINT), es
decir, cualquier cadena de dígitos. Ejemplos: 1, 0,
2010
- Las constantes lógicas true y false
(token CLOG)
- Constantes de tipo str (cadenas de caracteres,
token CSTR). Necesariamente tienen que comenzar y terminar con
el carácter comilla (") y entre las dos comillas puede haber
cualquier cadena de caracteres que no contenga el símbolo
comilla. Adicionalmente, puede haber entre las dos comillas que
delimitan la constante una o varias comillas siempre y cada ocurrencia
del carácter comilla vaya inmediatamente precedida del carácter
\. Ejemplos: "Hola Mundo", "Ejemplo con \"
comillas \"".
El lenguaje distingue mayúsculas y minúsculas; por lo tanto mientras
begin es una palabra clave, Begin es un identificador
y BEGIN es otro identificador distinto del anterior.
Recuerde que también tiene que especificar en el fichero JLex que se
deben reconocer y consumir (sin generar token) los espacios en blanco,
tabuladores, saltos de línea y retornos de carro.
Para desarrollar el analizador léxico pedido lo único que hay que
hacer es completar este esqueleto de fichero
JLex en el que lo único que falta es definir para cada token a
reconocer, la expresión regular asociada y la acción asociada
(además debe definir al final del fichero Yylex las reglas para
consumir los espacios en blanco y para dar error léxico cuando
no se encuentre ningún patrón válido). A modo de
ejemplo, se proporciona ya definido el token para la palabra clave
and.
Como puede observarse en la definición de la regla para la palabra
clave and, lo que hay que hacer es:
- Para cada token definir la expresión regular asociada, según el
formato requerido por JLex (véase la documentación que se
proporciona).
- A continuación de la expresión regular definir la acción que se
toma cuando se reconoce dicho token. Para los tokens que no tienen
ningún dato asociado, escribiremos {return tok(sym.X, null); },
donde X es el nombre que se le ha dado al token en cuestión en el
fichero CUP.
Para los tokens que tienen un dato asociado, escribiremos
{return tok(sym.X, obj); }, donde X es el nombre que
se le ha dado al token en cuestión en el fichero CUP y obj es
el objeto Java asociado al token. Para construir el objeto Java asociado
a un token puede utilizar el método yytext(), tal y como se
explica en la documentación de JLex.
Como se explica en el manual de CUP y se ve en clase, un
fichero CUP declara una lista de tokens, donde cada token es
representado por un idendificador (por ejemplo, AND). Cuando se
genera el analizador sintáctico a partir del fichero CUP, se producen
como resultado 2 ficheros Java. Uno de ellos, que lleva por nombre
sym.java contiene una clase llamada sym en la que
simplemente se asocia un número entero a cada uno de los tokens que se
han declarado en el fichero CUP. Por lo tanto, sym.X es el
entero que utiliza la clase sym para identificar al token
X.
- En el caso de los espacios en blanco, saltos de línea, etc.,
escribiremos como acción { }, cuyo efecto es no devolver nada y
seguir consumiendo caracteres de entrada para producir el siguiente
token.
El analizador léxico deberá lanzar una excepción de tipo
LexerException, que se proporciona,
cuando se detecte un error léxico.
Definición de la sintaxis del lenguaje de programación fuente
A continuación vamos a dar la definición de la sintaxis del lenguaje
fuente por medio de una gramática independiente del contexto.
La gramática que define la sintaxis del lenguaje de programación
fuente es la siguiente:
G=<ET, EN, S, P>
ET= {COMA, PC, PAREN, TESIS, BEGIN, END, ASIG, AND, OR, NOT,
IF, THEN, ELSE, PROG, DECL, WHILE, DO, PRINT,
MAS, MENOS, MUL, Q2STR, Q2STRD, PENT, NUM, DEN,
DIV, MAYOR, MENOR, IGUAL, INT2STR, IDENT, CSTR,
CLOG, CINT, TYPE}
EN= {S, <IdentList>, <StatementList>, <Body>, <Statement>,
<Exp>, <LDecl>, <Decl>}
S= S
P= {
S-> PROG IDENT PAREN <IdentList> TESIS DECL <LDecl> <Body>
| PROG IDENT DECL <LDecl> <Body>
| PROG IDENT PAREN <IdentList> TESIS <Body>
| PROG IDENT <Body>
<LDecl>-> <Decl> PC
| <Decl> PC <LDecl>
<Decl>-> TYPE <IdentList>
<IdentList>-> IDENT
| <IdentList> COMA <IdentList>
<Body>-> BEGIN <StatementList> END PC
<StatementList>-> <Statement> PC
| <Statement> PC <StatementList>
<Statement>-> IDENT ASIG <Exp>
| PRINT PAREN <Exp> TESIS
| WHILE <Exp> DO <StatementList> END
<Exp>-> <Exp> AND <Exp>
| <Exp> OR <Exp>
| NOT <Exp>
| PAREN <Exp> TESIS
| IDENT
| CLOG
| <Exp> MAS <Exp>
| <Exp> MENOS <Exp>
| MENOS <Exp>
| <Exp> MUL <Exp>
| <Exp> DIV <Exp>
| <Exp> MAYOR <Exp>
| <Exp> IGUAL <Exp>
| <Exp> MENOR <Exp>
| PENT PAREN <Exp> TESIS
| NUM PAREN <Exp> TESIS
| DEN PAREN <Exp> TESIS
| Q2STR PAREN <Exp> TESIS
| Q2STRD PAREN <Exp> COMA CINT TESIS
| CINT
| INT2STR PAREN <Exp> TESIS
| CSTR
}
|
|