Escuela Técnica Superior de Ingenieros Informáticos

Universidad Politécnica de Madrid

Traductores de Lenguajes

Estás aquí:

Inicio > Práctica > Lenguaje

Introducción a Boreal para la Práctica de "Traductores de Lenguajes"

Curso 2024/2025


Contenido:


Generalidades

Boreal es un lenguaje de programación ideado en 2024 a partir de los lenguajes Pascal, Ada y BASIC.

Este resumen presenta las principales características del lenguaje Boreal que son las que hay que utilizar para la práctica de la asignatura. No hay que considerar elementos del lenguaje no mencionados en este resumen y se deben considerar los elementos y características tal como aparecen aquí descritos. Entre corchetes se dan indicaciones sobre la obligatoriedad u opcionalidad de algunas partes del lenguaje en cuanto a su implementación.

Boreal es un lenguaje en el que no se diferencian las minúsculas y las mayúsculas (no es case sensitive).

Boreal es un lenguaje de formato libre, es decir, que se admiten espacios, tabuladores, saltos de línea y comentarios en cualquier parte del código.

Las palabras clave que tiene el lenguaje son reservadas (aunque en la descripción del lenguaje en este documento se indiquen principalmente en mayúsculas, se recuerda que se pueden escribir indistintamente en mayúsculas y minúsculas). Cada grupo de prácticas sólo ha de tener en cuenta las palabras asignadas a su grupo.

Boreal es un lenguaje con estructura de bloques y, por tanto, maneja los conceptos de identificadores globales y locales. Los identificadores globales pueden ser utilizados desde cualquier subprograma definido con posterioridad. Los identificadores declarados en el interior de un subprograma son locales a dicho subprograma.

En Boreal es obligatorio declarar todos los identificadores antes de que se utilicen. Además, hay que realizar la implementación considerando que es un lenguaje con recursividad, por lo que cualquier subprograma puede ser recursivo. El lenguaje no permite la definición de subprogramas anidados.

Estructura de un Programa

Debe considerarse que un programa en Boreal estará compuesto por un único fichero que puede tener declaraciones de variables globales y declaración de subprogramas (en este orden).

En este enlace se muestra un ejemplo de un fichero válido según el lenguaje descrito en esta asignatura.

Programa Principal

El programa principal (por donde comenzará la ejecución del programa) estará identificado por un subprograma especial identificado como PROGRAM con un nombre definido por el usuario, que no tiene parámetros ni devuelve nada.

Por tanto, la ejecución comenzaría por la primera sentencia que se encuentre en el PROGRAM y proseguiría secuencialmente hasta el final ejecutando todas sus sentencias. Hay que tener en cuenta que los otros subprogramas se ejecutan únicamente cuando son invocados. Ejemplo:

PROGRAM nombre; 
VAR declaración de variables
BEGIN
  sentencias
END;

Comentarios

En Boreal hay solamente un tipo de comentario, que es un comentario de bloque: Se utilizan el carácter { para abrir el comentario, y } para cerrarlo. En su interior puede aparecer cualquier cantidad de caracteres imprimibles, incluyendo los saltos de línea. No se permiten comentarios anidados. Los comentarios pueden ocupar más de una línea y pueden ir colocados en cualquier parte del código:

{ ¡Comentario con apertura y cierre! 
  (Y ocupa 2 líneas.)}

Constantes

El lenguaje dispone de varios tipos de constantes [implementación obligatoria de las constantes enteras y cadenas]:

Enteras

Para representar las constantes enteras se utilizan los dígitos decimales. Por ejemplo: 378.

Los números enteros se tienen que poder representar con una palabra (16 bits, incluido el signo), por lo que el máximo entero válido será el 32767.

Cadenas de Caracteres

Las constantes cadena van encerradas entre comillas simples ('¡Hola, mundo!'). Se utiliza internamente el carácter nulo (cuyo código ASCII es 0) como carácter de fin de cadena. Puede aparecer cualquier carácter imprimible en la cadena (excepto los saltos de línea).

Una cadena no puede contener más de 64 caracteres (incluyendo el nulo de fin de cadena).

Lógicas

En Boreal existen dos constantes lógicas para representar verdadero y falso: TRUE y FALSE [es opcional implementar las constantes lógicas, que son palabras reservadas].

Operadores

Este lenguaje presenta un conjunto de operadores con los que escribir distintas expresiones. Además, se pueden utilizar los paréntesis para agrupar subexpresiones [es obligatorio implementar los paréntesis]. Las expresiones pueden tener varios operadores, varios operandos, paréntesis... Las expresiones se pueden utilizar en multitud de construcciones del lenguaje: asignaciones, condiciones, parámetros de un subprograma, instrucciones de salida, instrucciones de retorno... Las expresiones se tienen que evaluar completamente para obtener un resultado.

Operadores Aritméticos

Son los operadores que permiten realizar la suma, resta, producto, división y módulo: +, -, *, / y MOD [obligatorio implementar al menos uno y dos como máximo]. Se aplican sobre datos enteros, proporcionando un resultado entero (en el caso de la división, redondeando el valor si es necesario).

También existen los operadores más y menos unarios: +, - [implementación opcional]. Estos operadores se pueden utilizar delante de una constante entera, una variable o una expresión.

Operadores de Relación

Son los operadores que permiten realizar las comparaciones de igual, distinto, menor, mayor, menor o igual, mayor o igual: =, <>, <, >, <= y >= [obligatorio implementar al menos uno de los operadores y dos como máximo]. Se aplican sobre datos enteros y proporcionan un resultado lógico.

Operadores Lógicos

Representan las operaciones de conjunción, disyunción, disyunción exclusiva y negación: AND, OR, XOR y NOT [obligatorio implementar al menos uno y dos como máximo]. Se aplican sobre datos lógicos y devuelven un resultado lógico.

Operador Potencia

Representa la operación de potencia (**), es decir, un número elevado a otro número (por ejemplo, 2**8, y**x). Se aplica sobre datos enteros y devuelve un resultado entero [implementación obligatoria para los grupos que les corresponda].

Operador de Concatenación

Representa la operación de concatenación de cadenas (+), es decir, devuelve una cadena formada por la unión de dos cadenas. Se aplica sobre cadenas y devuelve una cadena [implementación obligatoria para los grupos que les corresponda].

Operador de Pertenencia

Representa la pertenencia a una lista de valores (IN), es decir, devuelve cierto si el valor aparece en la lista y falso en caso contrario. La lista está formada por paréntesis con varias expresiones separadas por comas en su interior. Su notación es Expresión IN ListaExpresiones (la lista debe tener al menos una expresión). Por ejemplo, a IN (2, b, fun(9), c-(d/2)). Se aplica sobre enteros y devuelve un lógico [implementación obligatoria para los grupos que les corresponda].

Operadores Máximo y Mínimo

Obtiene el valor máximo (MAX) o mínimo (MIN) de una lista de valores enteros. Devuelve, respectivamente, el mayor o el menor valor numérico de los elementos de una lista. La lista está formada por paréntesis con varias expresiones separadas por comas en su interior. Su notación es MAX ListaExpresiones (la lista debe tener al menos una expresión). Por ejemplo, MIN (2, b, fun(9), c-(d/2)). Se aplican sobre enteros y devuelve un entero [implementación obligatoria para los grupos que les corresponda].

Precedencia de Operadores

En la tabla siguiente se muestra la precedencia de los operadores con el siguiente significado: los operadores del mismo grupo tienen la misma precedencia y, conforme se desciende por la tabla, la precedencia aumenta. La asociatividad de cada grupo de operadores se indica también en la tabla. En cualquier caso, el uso de paréntesis permite alterar el orden de evaluación de las expresiones [es obligatorio para todos los grupos tener en cuenta la precedencia y asociatividad de los operadores utilizados, así como el uso de paréntesis].

Precedencias de los Operadores
Más información

Tabla con la relación de todos los operadores de Boreal con su significado, orden de prioridad (ascendente) y relación de asociatividad. La primera columna muestra los operadores, la segunda su significado y la tercera la asociatividad.

Operadores Significado Asociatividad
OR
XOR
O lógico
O exclusivo
Izquierda a derecha
AND Y lógico Izquierda a derecha
=
<>
>
>=
<
<=
Igual
Distinto
Mayor
Mayor o igual
Menor
Menor o igual
Pertenece
Izquierda a derecha
+
-
Suma/Concatenación
Resta
Izquierda a derecha
*
/
MOD
Producto
División
Módulo
Izquierda a derecha
** Potencia Izquierda a derecha
NOT
+
-
Negación lógica
Positivo
Negativo
Derecha a izquierda
MAX
MIN
IN
Máximo
Mínimo
Izquierda a derecha

Identificadores

Los nombres de identificadores están formados por letras y dígitos, siendo el primero siempre una letra, teniendo en cuenta que el número máximo de caracteres permitido en el nombre de un identificador es de 32. Ejemplos: a, a3, A3, SueldoDeTrabajador, z99...

Como ya se ha dicho, el lenguaje no diferencia minúsculas y mayúsculas, por lo que los nombres a3 y A3 son exactamente el mismo identificador.

Declaraciones

El lenguaje Boreal exige declaración previa de los identificadores que se utilicen. No se admite la redeclaración de identificadores en un mismo ámbito. Las declaraciones de un bloque van todas juntas al principio y antes del cuerpo del bloque.

Para realizar una declaración explícita de variables, se coloca la palabra VAR seguida de las declaraciones de todas las variables. La declaración de una variable se indica poniendo el nombre de la variable, dos puntos, el tipo (que deberá ser entero, lógico o cadena) y punto y coma.

Por tanto, la declaración de variables sería:

VAR nombreVar1: Tipo;
    nombreVar2: Tipo;

Cuando se declara una variable, se realiza una inicialización por omisión basándose en su tipo: 0 si es entera, falso si es lógica y la cadena vacía ('') si es cadena.

El ámbito de una variable será global si se declara fuera de cualquier subprograma, y será local si se declara dentro de un subprograma. Cualquier otro bloque (por ejemplo, los usados por las sentencias de control) no define un ámbito nuevo. Por otro lado, el nombre de los subprogramas siempre estará en el ámbito global.

Variables globales

Las variables globales se declaran siempre fuera de los subprogramas, al principio del fichero y antes de comenzar la declaración de los subprogramas.

Cuando se declara una variable fuera de los subprogramas, la variable será global y visible hasta el final del programa. Por tanto, todos los subprogramas podrán usar todas las variables globales.

Variables locales

Las variables locales se declaran siempre dentro de los subprogramas.

Cuando se declara una variable en un subprograma, la variable será local y visible hasta el final del subprograma.

Hay que tener en cuenta que si una variable local tiene el mismo nombre que una variable global, la variable local "oculta" a la global y, por tanto, en ese subprograma se podrá usar esa variable local pero no la global.

Subprogramas

Los subprogramas se declaran siempre fuera de otros subprogramas. La notación de estas declaraciones se muestra detalladamente en la sección de subprogramas.

Tipos de Datos

El lenguaje dispone de distintos tipos de datos básicos. Se deben considerar sólo los siguientes tipos de datos: entero, lógico y cadena.

El tipo entero se refiere a un número entero que debe ocupar un tamaño de 1 palabra (16 bits). Se representa con la palabra INTEGER.

El tipo lógico se refiere a un valor lógico. Se representa con la palabra BOOLEAN. El tipo lógico se almacena como un entero, por lo que ocupa también un tamaño de 1 palabra (16 bits). Las expresiones relacionales y lógicas devuelven un valor lógico.

El tipo cadena se refiere a una secuencia de caracteres. Se representa mediante STRING y una variable de tipo cadena ocupa 64 palabras (128 bytes), es decir, un máximo de 64 caracteres.

El lenguaje no tiene conversiones automáticas entre tipos.

Ejemplos:

Var i:  Integer;  { variable entera }
    st: String;   { variable cadena }
    b:  Boolean;  { variable lógica }
    c:  integer;  { variable entera }
{...}
c:= 66 + i;       { i y c son enteras; c valdrá 66, pues i vale 0 }
i:= 22;
b:= i <> c + 1;   { i y c+1 son enteros; b valdrá verdadero }
c:= c + i;        { c valdrá 88 }
i:= b + i;        { Error: no se puede sumar un lógico con un entero }
b:= not i;        { Error: el operador de negación solo puede aplicarse a lógicos }

Instrucciones de Entrada/Salida

Las instrucciones de entrada/salida disponibles en el lenguaje son tres. Su uso tiene la sintaxis de una sentencia.

La instrucción write (ListaExpresiones) recibe como parámetros una lista de expresiones separadas por comas. Evalúa cada una de las expresiones e imprime el resultado por pantalla, uno a continuación del otro. Las expresiones de ListaExpresiones pueden ser de tipo cadena o entero. Se pueden omitir los paréntesis y la lista, y entonces no imprime nada.

La instrucción writeln (ListaExpresiones) funciona exactamente igual que la anterior, salvo que tras imprimir el valor de la última expresión, se imprime un salto de línea. Se pueden omitir los paréntesis y la lista, y entonces solamente imprimiría el salto de línea.

Por ejemplo:

c:= 21; 
write ('Precio: ', 100, 'Euros.'+' Con IVA: ', 50 * 2 + c);
{imprime: “Precio: 100Euros. Con IVA: 121”}
a:= 'Adiós';
writeln ('Hola', a); {igual a write('Hola',a);writeln;}
{imprime “HolaAdiós” y un salto de línea}

La instrucción read (ListaVariables), que recibe una lista (no vacía) de variables enteras o cadena, lee números enteros o cadenas del teclado, almacenándose cada valor leído en cada una de las variables recibidas, respectivamente. Las variables tienen que ser del mismo tipo que el dato introducido por teclado. Por ejemplo:

var a: integer;
    c: string;
{...}
writeln('Pon tu nombre');
read (a, c);        { lee un número y una cadena }
writeln (a * a);    { imprime el cuadrado del número leído }
write ('Hola, ', c);{ imprime las cadenas }

Sentencias

El lenguaje Boreal dispone de las sentencias que aparecen a continuación [opcional u obligatoriamente, según se indique en cada caso]. Además de las sentencias aquí indicadas, también se consideran sentencias en este lenguaje las instrucciones de entrada/salida (write, writeln y read).

Sentencias de Asignación

Existe una sentencia de asignación en Boreal, que se construye mediante la operación de asignación := [es obligatorio implementar la sentencia de asignación por todos los grupos]. Su sintaxis general es la siguiente: variable := expresión;. Esta sentencia asigna a la variable el resultado de evaluar la expresión:

i:= 8 + 6;

Como ya se ha indicado, no hay conversiones entre tipos, por lo que tanto la variable como la expresión han de ser del mismo tipo.

var i: integer; { i es una variable entera }
    cad: string;
{...}
i := 123;
write (i);      { imprime el valor entero 123 }
cad:= 'hola';
write (cad);    { imprime el valor cadena “hola” }
i:= i > 88; { Error: no se puede asignar un lógico a un entero }

Sentencia de Llamada a Subprograma

Esta sentencia permite invocar la ejecución de un subprograma que debe estar previamente definido [implementación obligatoria].

La llamada a un subprograma de tipo procedimiento se realiza mediante su nombre seguido de los parámetros actuales (separados por comas) entre paréntesis (si no tiene parámetros, no hay que poner los paréntesis). Los parámetros pueden ser cualquier expresión válida en el lenguaje:

p1 (5);         {llamada a un subprograma con un argumento entero} 
p2;             {llamada sin parámetros a un subprograma} 
p3 ('', i - 8); {llamada con dos argumentos a un subprograma}

Los parámetros actuales en la llamada tienen que coincidir en número y tipo con los parámetros formales de la declaración del subprograma.

Si un subprograma devuelve un valor (es una función), la llamada a dicha función deberá estar siempre dentro de una expresión:

if (fun1 (9)) {llamada a una función lógica con un argumento entero} 
c:= b + fun2(b, fun3<>8); {llamada con 2 argumentos a una función entera, 
        siendo fun3, una llamada a una función entera sin argumentos}
write (fun2 (c, true));  {se imprime el valor devuelto por fun2}

Sentencia de Retorno de un Subprograma

Boreal dispone de la sentencia RETURN para finalizar la ejecución de un subprograma y volver al punto desde el que fue llamado [implementación obligatoria]. El uso de la instrucción de retorno varía dependiendo de si el subprograma es de tipo función (devuelve un valor) o de tipo procedimiento (no devuelve nada).

Un subprograma finalizará su ejecución cuando se ejecute una instrucción RETURN o al llegar al final del cuerpo deL subprograma (es decir, no es obligatorio que aparezca la instrucción RETURN).

Si es un procedimiento (no tiene tipo devuelto), sus sentencias RETURN no deberán contener una expresión.

Si es una función con tipo devuelto, sus sentencias RETURN deberán contener una expresión. El tipo de la expresión retornada deberá coincidir con el tipo devuelto de la función.

No es necesario que todos los subprogramas tengan instrucción de retorno ni tampoco hay ninguna restricción a la cantidad o ubicación de las instrucciones de retorno dentro de los subprogramas.

FUNCTION SumaAlCuadrado  (a: integer; b: integer): integer;
BEGIN
  j:= a + b;
  return j * j;
  { La función finaliza y devuelve el valor entero de la expresión }
END;
PROCEDURE pro (x: integer);
Begin
  if (x > (362/2)) return; { finaliza, si se cumple la condición }
  x:= SumaAlCuadrado (x - 1, x);
   { x contendrá el valor devuelto por la función: (x+x-1)^2 }
  write (SumaAlCuadrado (x, x));
End;  { finaliza la ejecución si antes no se ejecutó el return }

Sentencia Condicional sencilla

Selecciona la ejecución de una sentencia, dependiendo del valor correspondiente de una condición de tipo lógico [implementación obligatoria para todos los grupos]:

IF condición THEN sentencia

Si la condición (que puede ser cualquier expresión lógica) se evalúa como cierta, se ejecuta la sentencia que puede ser cualquier sentencia simple del lenguaje, es decir, asignación, instrucción de entrada/salida, llamada a procedimiento o retorno [también exit si el grupo tiene la sentencia loop]; en caso contrario, se finaliza su ejecución:

If a > b Then c:= b;
if (fin) then write('adiós');

Sentencia Condicional compuesta

Selecciona la ejecución de una de las secuencias de sentencias que encierra, dependiendo del valor correspondiente de una condición de tipo lógico. Tiene dos formatos [implementación obligatoria de ambos para los grupos que les corresponda]:

  • IF condición THEN
    BEGIN
       cuerpo1
    END;
  • IF condición THEN
    BEGIN
       cuerpo1
    END;
    ELSE 
    BEGIN
       cuerpo2
    END;

Si la condición (que puede ser cualquier expresión lógica) se evalúa como cierta, se ejecuta el cuerpo1; en caso contrario, se ejecuta el cuerpo2 (si el programador lo ha escrito). Cada uno de estos cuerpos contendrán cero, una o más sentencias, siempre en el bloque BEGIN-END.

if a > b then
begin
  c:= b;
end;
else 
begin
  c:= a;
  if fin then
  begin writeln('adiós'); end;
end;

Sentencia Repetitiva while

Esta sentencia permite repetir la ejecución de unas sentencias basándose en el resultado de una expresión lógica [implementación obligatoria para algunos grupos]. La sintaxis es:

WHILE condición DO
BEGIN
   cuerpo
END;

Se evalúa la condición (que puede ser cualquier expresión lógica) y, si resulta ser cierta, se ejecuta el cuerpo (que será un bloque de sentencias, que puede estar vacío). Este proceso se repite mientras que la condición sea verdadera:

while n <= 10 do
begin
    n:= n + 1;
    writeln (n);
end; { mientras que n sea menor o igual que 10...}

Sentencia Repetitiva repeat until

Esta sentencia permite repetir la ejecución de las sentencias del bucle hasta que se cumpla una condición [implementación obligatoria para algunos grupos]. La sintaxis es:

repeat 
   cuerpo
UNTIL condición;

En esta instrucción se ha de colocar un bloque de sentencias (puede estar vacío). Primero, se ejecuta el cuerpo; seguidamente, se evalúa la condición (que puede ser cualquier expresión lógica) y, si resulta ser falsa, se ejecuta de nuevo el cuerpo. Este proceso se repite hasta que la condición sea verdadera:

repeat
  c:= a;
  a:= a+1;
  c:= c*b;
until not (a < b);

Sentencia Repetitiva loop

Esta sentencia permite repetir la ejecución de las sentencias del bucle hasta que se cumpla una condición [implementación obligatoria para algunos grupos]. La sintaxis es:

LOOP 
   cuerpo
END;

Dentro del cuerpo (que puede contener cualquier cantidad de sentencias) tendrá que aparecer, una vez y solo una vez, una sentencia especial EXIT WHEN condición.

Se ejecuta el cuerpo iterativamente hasta que la condición de la sentencia EXIT sea cierta; en ese momento, se abandona el bucle:

loop 
  n:= n + 1;
  exit when n > 32766; { sale cuando n sea mayor que 32766 }
  write (n);
end;

Sentencia Repetitiva for

Esta sentencia FOR permite ejecutar un bucle un número determinado de veces mientras varía un índice [implementación obligatoria para algunos grupos]. La sintaxis es:

for VariableÍndice := expresión1 to expresión2 do
BEGIN
   cuerpo
END;

La variable índice y las dos expresiones deben ser enteras. El cuerpo (con cero, una o más sentencias) está en un bloque BEGIN-END.

El funcionamiento de este bucle es como sigue:

  1. Se evalúan las dos expresiones
  2. Se inicializa la VariableÍndice con el valor de la expresión1
  3. Si la VariableÍndice es mayor al valor obtenido al evaluar la expresión2 en el paso 1, se abandona la ejecución del bucle
  4. Se ejecuta el cuerpo
  5. Se incrementa en una unidad la VariableÍndice
  6. Se vuelve al paso 3.
read (a);
for i:= 1 to a + 1 do
begin
  writeln (i, '^2=', i * i);
end;

Sentencia de Selección Múltiple

Esta sentencia selecciona y ejecuta unas sentencias basándose en el resultado de una expresión [implementación obligatoria para algunos grupos]. La sintaxis de la sentencia de selección múltiple es:

CASE expresión OF
  valor1: BEGIN cuerpo1 END;
  valor2: BEGIN cuerpo2 END;
  { ... }
  OTHERWISE: BEGIN cuerpon END;
END;

Su funcionamiento es como sigue: Se evalúa la expresión (que debe ser de tipo entero), se busca el valor que coincida con el resultado (los valores tienen que ser constantes enteras) y se ejecuta su cuerpo asociado (cada cuerpo puede estar formado por cero, una o más sentencias en un bloque BEGIN-END). Una vez ejecutado el cuerpo correspondiente, se abandona la sentencia de selección. Si no se encuentra el valor, se ejecutan las sentencias asociadas a OTHERWISE, si el programador la ha incluido [otherwise es opcional para la implementación]:

case dia of
   1: begin writeln ('lunes');    end;
   2: begin writeln ('martes');   end; 
   3: begin writeln ('miércoles');end;
   4: begin writeln ('jueves');   end;
   5: begin writeln('viernes');write('¡Llega el finde!');end;
   otherwise: begin write ('Fiesta'); end;
end;

Subprogramas

Es necesario definir cada subprograma antes de poder utilizarlo. Hay tres tipos de subprogramas: el programa principal, los subprogramas procedimiento (que no devuelven ningún valor) y los subprogramas función (que devuelven un valor).

La definición de un subprograma incluye el tipo de subprograma, el nombre, los parámetros con sus tipos (entre paréntesis, si existen), terminando con el tipo de retorno si es una función.

La lista de parámetros (que puede estar vacía y, en este caso, se omiten los paréntesis) consta del nombre de cada parámetro formal, dos puntos y su tipo. Si hay más de un argumento, se separan por puntos y comas. Los parámetros pueden pasarse por valor o por referencia:

Los subprogramas pueden recibir como parámetros cualquiera de los tipos del lenguaje (entero, lógico o cadena). La sintaxis de la ListaDeArgumentos es:

([VAR] nombre1: Tipo1; [VAR] nombre2: Tipo2...)

Tras esta cabecera pueden ir las declaraciones de variables locales (si las hay, tienen que ir precedidas con una palabra VAR) y un bloque (BEGIN-END) con el cuerpo de la función (que puede contener cualquier cantidad de sentencias del lenguaje).

Boreal admite recursividad. Todos los grupos de prácticas han de considerarla en su implementación. Cualquier subprograma puede ser recursivo, es decir, puede llamarse a sí mismo.

El lenguaje Boreal no permite la definición de subprogramas anidados. Esto implica que dentro de un subprograma no se puede declarar otro subprograma.

Dentro de un subprograma se tiene acceso a las variables globales, a sus argumentos y a sus variables locales. Si en un subprograma se declara una variable local o un argumento con el mismo nombre que un identificador global, este último no es accesible desde dicho subprograma.

Programa Principal

Todo programa debe tener un subprograma (y solo uno) denominado programa principal (por donde comenzará la ejecución del programa). El programa principal no lleva parámetros ni devuelve nada y no puede ser llamado explícitamente desde el programa (la llamda se produce automáticamente al ejecutar el programa). Pueden declararse variables locales (si no se declara ninguna no se pone VAR) y debe contener un bloque. El programa principal puede tener alguna instrucción return en su cuerpo, pero no es obligatorio que sea la última instrucción.

Su sintaxis es:

PROGRAM NombreProgramaPrincipal;
VAR
   declaración de variables
BEGIN
   sentencias
END;

Procedimientos

Se pueden declarar subprogramas de tipo procedimiento. Los procedimientos pueden llevar parámetros pero no devuelven nada. Por tanto, solamente pueden ser llamados como una sentencia, y no pueden aparecer en ninguna expresión. Pueden declararse variables locales (si no se declara ninguna no se pone VAR) y debe contener un bloque. No es obligatorio que un procedimiento tenga una instrucción return en su cuerpo ni que tenga que ser la última instrucción. [Implementación de los procedimientos a los grupos que les corresponda, junto al paso de parámetros por valor y por referencia.]

Su sintaxis es:

PROCEDURE NombreProcedimiento ListaDeArgumentos;
VAR
   declaración de variables
BEGIN
   sentencias
END;

Funciones

Se pueden declarar subprogramas de tipo función. Las funciones pueden llevar parámetros y tienen que devolver un valor de uno de los tipos del lenguaje. Por tanto, solamente pueden ser llamados desde una expresión que recoja el valor retornado, no pudiendo ser llamados como una sentencia. Pueden declararse variables locales (si no se declara ninguna no se pone VAR) y debe contener un bloque. [Implementación de las funciones a los grupos que les corresponda.]

El tipo de retorno de la función se determina según el Tipo que aparezca en su declaración. En caso de que alguna de instrucción return de una función tenga una expresión de un tipo distinto al declarado, será un error. No es obligatorio que una función tenga una instrucción return expresión en su cuerpo ni que tenga que ser la última instrucción. Si una función termina sin ejecutar una instrucción return expresión, devolverá un valor indeterminado.

Su sintaxis es:

FUNCTION NombreFunción ListaDeArgumentos : TipoRetorno;
VAR
   declaración de variables
BEGIN
   sentencias
END;

A continuación se muestran algunos ejemplos de subprogramas:

var x: integer;  { global }
procedure Hola;  { procedimiento sin parámetros }
var s: string;
begin
    s:= 'Hello';
    writeln (s + '!');
end;
function factorial (x: integer): integer;
   { se define la función recursiva con un parámetro, 
     que oculta a la variable global de igual nombre }
begin
  hola;
  if (x > 1) then
    return x * factorial (x - 1);
  return 1;
end;	{ la función devuelve un entero }
function Suma (aux: integer; fin: integer; b: boolean): integer;
  {se define la función entera Suma que recibe dos enteros y un lógico}
  {usa la variable global x} 
begin
	if (b) then
	begin
		for x:= 1 to fin do
		begin
		  x:= x + 2;
		  aux:= aux + factorial (aux - 1);
		end;
	end;
    return aux;
end;	{ la función devuelve un lógico }
procedure Imprime (a: integer);
begin
    writeln ('Resultado= ', a);
    return;	{ esta instrucción se podría omitir }
end;	{ la función no devuelve nada }
procedure Leer (var a: integer; x: integer);
  {parámetro a por referencia, obligatorio solo para algunos grupos}
  {parámetro x por valor, obligatorio para todos los grupos}
begin
    read (a); {se modifica el parámetro actual de la llamada a leer}
    a:= a - 1 + x;
end;
program ejemplo;	{programa principal}
var tres:integer;
begin
	tres:= 3;
    leer (x, 3/tres);
    Imprime (factorial (Suma (x, tres, true)));  
    { se llama a los tres subprogramas }
end;

Más Información sobre el Lenguaje

Hay que tener en cuenta que para esta práctica tan solo hay que incorporar el funcionamiento del lenguaje Boreal que se ha explicado aquí, necesario para implementar las partes comunes a todos los grupos, así como las partes obligatorias de cada grupo. Por tanto, no se deben incluir otras características, variantes y elementos que puedan tener otros lenguajes.

Aquí se tiene un ejemplo de un fichero que incorpora la mayoría de las funcionalidades descritas en esta página, a modo de ejemplo adicional para facilitar su compresión.

Se puede encontrar más información sobre la construcción de Compiladores en la página de referencias, en los libros que hay en la Biblioteca de la Escuela (se puede usar el buscador de la UPM), así como en cualquier libro, manual o documentación de Compiladores (buscando en librerías como Amazon).