next up previous contents index practicapracticaPP2moodleLHPmoodlepserratacpanmodulospauseperlgoogleetsiiullpcgull
Sig: Ejercicio: El orden de Sup: La Estructura de los Ant: Práctica: Fases de un Err: Si hallas una errata ...


Análisis Léxico

Comenzaremos con la parte mas sencilla del compilador: el analizador léxico. Habitualmente el término ``análisis léxico'' se refiere al tratamiento de la entrada que produce como salida la lista de tokens. Un token hace alusión a las unidades mas simples que tiene significado. Habitualmente un token o lexema queda descrito por una expresión regular. Léxico viene del griego lexis, que significa ``palabra''. Perl es, sobra decirlo, una herramienta eficaz para encontrar en que lugar de la cadena se produce el emparejamiento. Sin embargo, en el análisis léxico, el problema es encontrar la subcadena a partir de la última posición ``casada'' que casa con una de las expresiones regulares que definen los lexemas del lenguaje dado.

La estructura general del analizador léxico consiste en un bucle en el que se va recorriendo la entrada, buscando por uno de los patrones/lexemas del lenguaje y añadiéndolo al final de la lista de terminales.

Una iteración del bucle tiene la forma de una secuencia de condicionales en las que se va comprobando si la entrada casa con cada una de las expresiones regulares que definen los terminales del lenguaje.

     ...
     if (m|\G\s*(\d+)|gc) {
       push @tokens, 'NUM', $1;
     } 
     elsif (m|\G\s*([a-z_]\w*)\b|igc) {
       push @tokens, 'ID', $1;
     } 
     ...

Una expresión como m|\G\s*(\d+)|gc) es una expresión regular. Es conveniente que en este punto repase la introducción a las expresiones regulares en la sección 1.5.

Nótese que, puesto que no se menciona sobre que variable se hace el binding (no se usa ninguno de los operadores de binding =~ y !~) se entiende que es sobre la variable por defecto $_ sobre la que se hace el casamiento.

El ancla \G casa con el punto en la cadena en el que terminó el último emparejamiento.

La expresión regular describiendo el patrón de interés se pone entre paréntesis para usar la estrategia de los paréntesis con memoria (véanse las secciones 4.3 y 4.4).

La opción c y la opción g son especialmente útiles para la construcción del analizador. Como lo usamos en un contexto escalar, la opción g itera sobre la cadena, devolviendo cierto cada vez que casa, y falso cuando deja de casar. Se puede averiguar la posicion del emparejamiento utilizando la función pos. (véase la sección 4.12 para mas información sobre la opción g).

La opción /c afecta a las operaciones de emparejamiento con /g en un contexto escalar. Normalmente, cuando una búsqueda global escalar tiene lugar y no ocurre casamiento, la posición de comienzo de búsqueda es reestablecida al comienzo de la cadena. La opción /c hace que la posición inicial de emparejamiento permanezca donde la dejó el último emparejamiento con éxito y no se vaya al comienzo. Al combinar esto con el ancla \G, la cuál casa con el final del último casamiento, obtenemos que la combinación

m{\G\s*(...)}gc

logra el efecto deseado: digamos que si la primera expresión regular en la cadena elsif fracasa, la posición de búsqueda no es inicializada de nuevo gracias a la opción c y el ancla \G sigue recordando donde terminó el ultimo casamiento.

Por último, la opción i permite ignorar el tipo de letra (mayúsculas o minúsculas).

Repase la sección 4.9 para ver algunas de las opciones mas usadas.

 1 package Lexical::Analysis;
 2 sub scanner {
 3   local $_ = shift;
 4   { # Con el redo del final hacemos un bucle "infinito"
 5     if (m|\G\s*(\d+)|gc) {
 6       push @tokens, 'NUM', $1;
 7     } 
 8     elsif (m|\G\s*int\b|igc) {
 9       push @tokens, 'INT', 'INT';
10     } 
11     elsif (m|\G\s*string\b|igc) {
12       push @tokens, 'STRING', 'STRING';
13     } 
14     elsif (m|\G\s*p\b|igc) {
15       push @tokens, 'P', 'P'; # P para imprimir
16     } 
17     elsif (m|\G\s*([a-z_]\w*)\b|igc) {
18       push @tokens, 'ID', $1;
19     } 
20     elsif (m|\G\s*\"([^"]*)\"|igc) {
21       push @tokens, 'STR', $1;
22     } 
23     elsif (m|\G\s*([+*()=;,])|gc) {
24       push @tokens, 'PUN', $1;
25     }
26     elsif (/\G\s*(.)/gc) {
27       Error::fatal "Caracter invalido: $1\n";
28     }
29     else {
30       last;
31     }
32     redo;
33   }
34 }

Para completar el analizador solo quedan declarar las variables usadas y las subrutinas de manejo de errores:

######## global scope variables
our @tokens = ();
our $errorflag = 0;

package Error;

sub error($) {
  my $msg = shift;
  if (!$errorflag) {
    warn "Error: $msg\n";
    $errorflag = 1;
  }
}

sub fatal($) {
  my $msg = shift;
  die "Error: $msg\n";
}
El uso de our es necesario porque hemos declarado al comienzo del módulo use strict. El pragma use strict le indica al compilador Perl que debe considerar como obligatorias un conjunto de reglas de buen estilo de programación. Entre otras restricciones, el uso del pragma implica que todas las variables (no-mágicas) deben ser declaradas explícitamente (uso de my, our, etc.) La declaración our se describe en la sección 1.7.3.



Subsecciones
next up previous contents index practicapracticaPP2moodleLHPmoodlepserratacpanmodulospauseperlgoogleetsiiullpcgull
Sig: Ejercicio: El orden de Sup: La Estructura de los Ant: Práctica: Fases de un Err: Si hallas una errata ...
Casiano Rodríguez León
2006-02-21