Capítulo 4 Uso de análisis de Java CUP
jorgeasael1Resumen10 de Agosto de 2015
7.001 Palabras (29 Páginas)194 Visitas
Capítulo 4 Uso de análisis de Java CUP
CUP es un generador de analizadores sintácticos. Se necesita un programa CUP - esencialmente una LALR (1) apta para su procesamiento gramática, y genera un programa Java que analizar la entrada que satisfaga esa gramática.
CUP supone que un analizador léxico se proporciona por separado. Normalmente, el analizador léxico es generado usando JFlex. Código de soporte adicional puede ser proporcionado en Java.
Un ejemplo (INTERP1)
A continuación JFlex, CUP, y los archivos fuente de Java en conjunto generan un simple "intérprete". Esta intérprete lee las líneas de entrada, los analiza, y "interpreta" de ellos. Una línea de entrada es una sentencia de asignación, que especifica el valor de una variable o una expresión que se imprima.
En el primer caso, el intérprete evalúa la expresión, y almacena el valor en la variable.
En el segundo caso el intérprete evalúa la expresión, y se imprime el valor.
El analizador léxico
package grammar;
import java.io.*;
import java_cup.runtime.*;
%%
%public
%type Symbol
%char
%{
public Symbol token( int tokenType ) {
System.err.println(
"Obtain token " + sym.terminal_name( tokenType )
+ " \"" + yytext() + "\"" );
return new Symbol( tokenType, yychar,
yychar + yytext().length(), yytext() );
}
%}
number = [0-9]+
ident = [A-Za-z][A-Za-z0-9]*
space = [\ \t]
newline = \r|\n|\r\n
%%
"=" { return token( sym.ASSIGN ); }
"+" { return token( sym.PLUS ); }
"-" { return token( sym.MINUS ); }
"*" { return token( sym.TIMES ); }
"/" { return token( sym.DIVIDE ); }
"(" { return token( sym.LEFT ); }
")" { return token( sym.RIGHT ); }
{newline} { return token( sym.NEWLINE ); }
{space} { }
{number} { return token( sym.NUMBER ); }
{ident} { return token( sym.IDENT ); }
. { return token( sym.error ); }
<
El analizador léxico coincide con símbolos especiales "=", "+", "-", "/", "(", ")", constantes enteros, y los identificadores. En este caso, las nuevas líneas son sintácticamente importante, pero los espacios y las pestañas no son, así que devolver un token para nuevas líneas, pero no para los espacios en blanco y tabuladores. Si hay un error de léxico, vuelvo un token de error (que genera un error de sintaxis para el analizador).
El analizador
package grammar;
import java.util.*;
import java.io.*;
import java_cup.runtime.*;
action code
{:
Hashtable table = new Hashtable();
:};
parser code
{:
private Yylex lexer;
private File file;
public parser( File file ) {
this();
this.file = file;
try {
lexer = new Yylex( new FileReader( file ) );
}
catch ( IOException exception ) {
throw new Error( "Unable to open file \"" + file + "\"" );
}
}
...
:};
scan with
{:
return lexer.yylex();
:};
terminal LEFT, RIGHT, NEWLINE, PLUS, MINUS, TIMES, DIVIDE, ASSIGN;
terminal String NUMBER;
terminal String IDENT;
nonterminal StmtList, Stmt;
nonterminal Integer Expr, Term, Factor;
start with StmtList;
StmtList::=
|
StmtList Stmt
;
Stmt::=
IDENT:ident ASSIGN Expr:expr NEWLINE
{:
table.put( ident, expr );
:}
|
Expr:expr NEWLINE
{:
System.out.println( expr.intValue() );
:}
|
error NEWLINE
|
NEWLINE
;
Expr::=
Expr:expr PLUS Term:term
{:
RESULT = new Integer( expr.intValue() + term.intValue() );
:}
|
Expr:expr MINUS Term:term
{:
RESULT = new Integer( expr.intValue() - term.intValue() );
:}
|
MINUS Term:term
{:
RESULT = new Integer( - term.intValue() );
:}
|
Term:term
{:
RESULT = term;
:}
;
Term::=
Term:term TIMES Factor:factor
{:
RESULT = new Integer( term.intValue() * factor.intValue() );
:}
|
Term:term DIVIDE Factor:factor
{:
RESULT = new Integer( term.intValue() / factor.intValue() );
:}
|
Factor:factor
{:
RESULT = factor;
:}
;
Factor::=
LEFT Expr:expr RIGHT
{:
RESULT = expr;
:}
|
NUMBER:value
{:
RESULT = new Integer( value );
:}
|
IDENT:ident
{:
Integer value = ( Integer ) table.get( ident );
if ( value == null ) {
parser.report_error( "Undeclared Identifier " + ident,
new Symbol( sym.IDENT, identleft, identright, ident ) );
value = new Integer( 0 );
}
RESULT = value;
:}
;
The lines
action code
{:
Hashtable table = new Hashtable();
:};
agregar código a una clase de la acción privada utilizada para contener el código de las acciones. En este caso no tengo añade una variable de tabla hash que representa una "tabla de símbolos" para realizar el mapeo de identificadores de los valores. (¿No es maravilloso no tener que programar estas cosas!)
las líneas
parser code
{:
private Yylex lexer;
private File file;
public parser( File file ) {
this();
this.file = file;
try {
lexer = new Yylex( new FileReader( file ) );
}
catch ( IOException exception ) {
throw new Error( "Unable to open file \"" + file + "\"" );
}
}
...
:};
añadir código a la clase parser. En este caso he añadido un constructor, y el campo de instancia variables que representan el analizador léxico y el archivo de entrada.
las líneas
scan with
{:
return lexer.yylex();
:};
especifica que para obtener un token, el analizador debe invocar lexer.yylex (). Así, no hay obligación de utilizar JFlex para generar el analizador léxico, aunque por lo general es una buena idea.
las líneas
terminal LEFT, RIGHT, NEWLINE, PLUS, MINUS, TIMES, DIVIDE, ASSIGN;
terminal String NUMBER;
terminal String IDENT;
especificar los nombres de los símbolos terminales en la gramática. Los símbolos terminales izquierdo,
DERECHA, nueva línea, más, menos, TIEMPOS, dividir ASSIGN no tienen ningún valor asociado.
...