Skip to content

Especificación

Antes de comenzar a codificar una fase del compilador (tanto el análisis léxico como el resto de las fases) hay que crear siempre una especificación mediante una notación (metalenguaje) que indique claramente qué tiene que hacer dicha fase.

Hay tres razones fundamentales para esto:

  • Precisión. En la descripción mediante el lenguaje natural (que es como se ha hecho en la descripción del lenguaje) normalmente habrá ambigüedades y detalles sin aclarar.
  • Concisión. Los requisitos expresados en un metalenguaje suelen ser mucho más breves que su equivalente en lenguaje natural.
  • Facilitar la Implementación. La especificación de cada fase tendrá asociado a un método que indicará cómo generar el código a partir de dicha especificación. En algunas fases incluso fases dicho método estará ya automatizado con alguna herramienta.

Extracción de Requisitos

En primer paso es recurrir a la descripción del lenguaje y extraer, de entre todos los requisitos de la misma, aquellos que correspondan a la etapa que se esté tratando.

De entrada, se deberán contemplar todos los tokens que pueden verse en el programa de ejemplo.

c
DATA
	float price;
	int width;
	int height;
	float total;

CODE
	price = 9.95;
	total = (price - 3.0) * 1.18;
	print total;

	width = 10; height = 20;
	print 0 - width * height / 2;

En cuanto a las características del lenguaje, aquellas que serían responsabilidad del análisis léxico serían:

  • Podrán aparecer comentarios en cualquier parte del programa. Al igual que en C/C++ o Java, podrán ser de una línea (comienzan con //) o de varias líneas (comienzan con /* y terminan con */).
  • Las expresiones podrán estar formadas por literales enteros, literales reales y variables, pudiendo combinarse todos ellos mediante operadores aritméticos (+, -, * y /). Los operadores tendrán la prioridad y asociatividad habitual.
  • Se podrán agrupar expresiones mediante paréntesis.

La única parte de este documento que aporta algo nuevo es la primera (añadir comentarios) ya que en este caso todos los operadores y los paréntesis ya habían aparecido en el ejemplo.

Especificación con Expresiones Regulares

El análisis léxico debe indicar qué lexemas son válidos y a qué categoría pertenecen (tokens). Se utilizará como metalenguaje las expresiones regulares.

El primer paso es determinar qué tokens (tipos de lexemas) hay en el lenguaje. Se puede observar que en el ejemplo aparecen:

bnf
DATA REAL INT IDENT ; CODE = ( ) INT_LITERAL FLOAT_LITERAL * - / + PRINT

El segundo paso es asociar un patrón a cada uno de los tokens que indique qué lexemas pertenecen a cada uno. Cada patrón se representa con una expresión regular:

TokenPatrón
+\+
--
*\*
//
;;
((
))
==
DATADATA
CODECODE
PRINTprint
INTint
REALfloat
IDENT[a-zA-Z][a-zA-Z0-9_]*
INT_LITERAL[0-9]+
FLOAT_LITERAL[0-9]+ '.' [0-9]+

Nótese cómo el hecho de hacer la especificación ha propiciado que haya habido que plantearse cuándo una constante real es válida. La especificación en lenguaje natural no entraba, por ejemplo, en si es válido o no que no haya decimales después del punto. Si el lenguaje se está creando partiendo de cero, este es el momento de decidirlo. Si, por el contrario, el lenguaje viene impuesto, entonces es el momento de consultarlo. Algo similar ocurre en el caso de los identificadores y de su tratamiento del guion bajo (_).

Una vez concretadas estas cuestiones mediante las expresiones regulares, ya se puede pasar a la implementación