1. 23/03/2014
1
Diseño de compiladores
JFlex
JFlex
• Es un generador de analizadores lexicográficos
• Esta desarrollado en Java y genera
analizadores en código Java
• Es una reescritura de la herramienta JLex, la
cual es una reescritura de la herramienta
lex/flex para Unix
• Esta pensado para ser usado en conjunto con
el generador de parsers LALR CUP
JFlex
• Es open source
• Puede ser descargado de la pagina del curso o del
sitio de JFlex
– http://jflex.de/download.html
• Puede ser utilizado en forma standalone o
integrado con Eclipse, a través de una tarea ANT
o de la herramienta de gestión de proyecto
MAVEN
• Información detallada del funcionamiento se
puede encontrar en el manual en línea:
– http://jflex.de/manual.html
Funcionamiento
• Entrada
– Archivo con la especificación del scanner
• Salida
– El analizador léxico (scanner)
– Escrito en código Java
Especificacion.lex
JFlex
Scanner.java
javac
Scanner.class
Texto del
programa
Lista de
tokens
Archivo de especificación
• Código de usuario
– Copiado directamente al scanner
• %%
• Directivas JFlex
– Definen macros, nombres de estados
• %%
• Reglas del analizador
– Expresiones regulares
– Cambios de estado
– Acciones cuando se localiza un token
Código de usuario
• Definición de packages
• Importaciones necesarias
• Cualquier código de ayuda necesario para que el
scanner funcione correctamente
• Generalmente contiene código utilizado desde las
acciones realizadas al encontrar un token
package uy.edu.fing.compiladores.obligatorio
import java.util.Stack;
public void debug(String texto) {
System.out.println(texto);
}
2. 23/03/2014
2
Directivas JFlex
• Directivas: Controlan el comportamiento de JFlex
– %line: Prende el conteo de líneas
– %char: Prende el conteo de caracteres
– %class class-name: Cambia el nombre de la clase
generada
– %cup: Activa la compatibilidad con CUP
– %type token-class-name: Clase usar para el Token
– %public: Hace que la clase generada es publica
– %function read-token-method
– %scanerror exception-type-name
Directivas JFlex
• Definiciones de estado
– %state nombre-de-estado
• Definiciones de macro
– nombre-de-macro = expresion regular
Expresiones regulares
r $ match reg. exp. r at end of a line
. (dot) any character except the newline
"..." verbatim string
{name} macro expansion
* zero or more repetitions
+ one or more repetitions
? zero or one repetitions
(...) grouping within regular expressions
a|b match a or b
[...]
class of characters - any one character enclosed in
brackets
a–b range of characters
[^…] negated class – any one not enclosed in brackets
Macros
• ALPHA=[A-Za-z_]
• DIGIT=[0-9]
• ALPHA_NUMERIC={ALPHA}|{DIGIT}
• IDENT={ALPHA}({ALPHA_NUMERIC})*
• NUMBER=({DIGIT})+
• WHITE_SPACE=([ nrtf])+
Reglas
• Estructura de las reglas
– [estados] regexp { acción (código java) }
– regexp: Describe como detectar los tokens
– Prioridad para el matcheo de la regla: El string
mas largo
– Si para el mismo largo tenemos mas de una
concordancia: La primera regla es la que se utiliza
• Importante:
– Las reglas deben matchear toda posible entrada
Acciones
• Escritas en código Java
• Puede utilizar métodos y variables especiales
provistos por JFlex
– yytext(): Retorna el lexema del token actual
– yyline: Retorna el numero de línea
• Transiciones de estado
– yybegin(nombre-estado): Le indica a JFlex para
saltar al nuevo estado
– YYINITIAL: Nombre que le da JFlex al estado inicial
3. 23/03/2014
3
Cambios de estado
YYINITIAL COMMENTS
‘//’
n
^n
Cambios de estado
<YYINITIAL> {NUMBER} {
return new Symbol(sym.NUMBER, yytext(), yyline));
}
<YYINITIAL> {WHITE_SPACE} { }
<YYINITIAL> "+" {
return new Symbol(sym.PLUS, yytext(), yyline);
}
<YYINITIAL> "-" {
return new Symbol(sym.MINUS, yytext(), yyline);
}
<YYINITIAL> "*" {
return new Symbol(sym.TIMES, yytext(), yyline);
}
...
<YYINITIAL> "//" { yybegin(COMMENTS); }
<COMMENTS> [^n] { }
<COMMENTS> [n] { yybegin(YYINITIAL); }
<YYINITIAL> . { return new Symbol(sym.error, null); }
Contador de líneas: lineCount.lex
import java_cup.runtime.Symbol;
%%
%cup
%{
private int lineCounter = 0;
%}
%eofval{
System.out.println("line number=" + lineCounter);
return new Symbol(sym.EOF);
%eofval}
NEWLINE=n
%%
<YYINITIAL>{NEWLINE} {
lineCounter++;
}
<YYINITIAL>[^{NEWLINE}] { }
Contador de líneas: lineCount.lex
JFlex
javac
lineCount.lex
Lexical
analyzer
text
tokens
Yylex.java
java JFlex.Main lineCount.lex
javac *.java
Main.java
sym.java
JFlex y JavaCUP deben estar en el CLASSPATH, junto con cualquier otra
biblioteca necesaria para que el programa funcione
Contador de líneas: lineCount.lex
import java.io.*;
public class Main {
public static void main(String[] args) {
Symbol currToken;
try {
FileReader txtFile = new FileReader(args[0]);
Yylex scanner = new Yylex(txtFile);
do {
currToken = scanner.next_token();
// do something with currToken
} while (currToken.sym != sym.EOF);
} catch (Exception e) {
throw new RuntimeException("IO Error (brutal exit)” +
e.toString());
}
}
}
Un ejemplo
• Un “compilador” sencillo de expresiones
• Soporta +, -, * y /
• Maneja enteros, flotantes e identificadores
• Construiremos el analizador léxico y el
analizador sintáctico
• Lo vamos a embeber dentro de un proyecto
Eclipse (para posterior reutilización)
4. 23/03/2014
4
Proyecto Eclipse
• Creamos un proyecto Eclipse estándar
(Aplicación Java de consola)
– Separamos directorios src (fuentes) y bin (clases)
– Creamos un directorio lib (para almacenar
bibliotecas externas)
– Creamos una carpeta “language” para almacenar
los archivos relacionados con JFlex y Cup
Sección “Java” del compilador
Relacionada al modelado del
lenguaje y al control del
proceso de compilación
Biblioteca de soporte
utilizada para la ejecución del
programa (Al ejecutar el
parse en runtime
necesitamos esto)
Físicamente, la dependencia
anterior va en esta carpeta
Todo lo relacionado a la
generación de los elementos
de análisis (léxico y sintáctico)
va en esta carpeta
5. 23/03/2014
5
Archivo con la especificación
sintáctica del lenguaje (para
CUP)
Archivo con la especificación
léxica del lenguaje (para
JFlex)
Script ANT para la ejecución
del CUP y JFlex
Modelado del lenguaje
El proceso de análisis genera
esta estructura para posterior
procesamiento
Clases generadas por el
proceso de análisis (léxico y
sintáctico)
Tester
6. 23/03/2014
6
Modelo del lenguaje
Representación intermedia
Fuente de JFlex
Fuente de JFlex Fuente de CUP
Fuente de CUP
Que hace nuestro compilador?
• Leer el fuente (en este caso un string, pero puede
ser un archivo)
• Analizarlo lexicalmente
• Analizarlo sintácticamente
• Construir una estructura que nos permita
procesar lo leído
– En este caso, solo lo recorremos para mostrarlo en
otro formato
• Nuestro “compilador” es mas bien un “traductor”