Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Compiladores
Universidade do Porto/FEUP
Departamento de Engenharia Informtica
ver. 1.0, Janeiro de 2011
Contedo
1.
2.
3.
4.
Referncias........................................................................................................................ 14
5.
6.
_____________________________________________________________
Objectivo: Familiarizao com a ferramenta de construo de analisadores sintcticos
(parsers) JavaCC.
Estratgia: Construo de um interpretador de expresses aritmticas tendo em conta vrias
opes.
___________________________________________________________________________
parte)
Este documento tem como objectivo iniciar a aprendizagem do gerador de analisadores
sintcticos JavaCC [1][2]. Os conhecimentos sobre analisadores sintcticos descendentes
permitiro mais tarde uma melhor percepo sobre o funcionamento dos geradores de
parsers, dos quais o JavaCC um exemplo.
O JavaCC utiliza um ficheiro com extenso .jj onde se encontram descritos os lexemas e a
gramtica para o parser, e gera um conjunto de classes Java de suporte1, tendo uma delas o
nome do parser. A Figura 1 ilustra genericamente a entrada do JavaCC e as classes geradas.
Parser.java
ParserTokenManager.java
ParserConstants.java
Token.java
ParseError.java
nome.jj
JavaCC
Figura 1. Entradas e sadas do JavaCC supondo que foi especificado Parser como
nome do parser no ficheiro nome.jj.
Os ficheiros .jj so constitudos por:
1. Zona de opes (opcional): onde pode ser definido o nvel global de lookahead, por
exemplo.
2. Unidade de compilao Java (PARSER_BEGIN(nome) ... PARSER_END(nome)) que
define a classe principal do parser e onde pode ser incluido o mtodo main do parser.
3. Lista de produes da gramtica (as produes aceitam os smbolos +, *, e ? com o
mesmo significado, aquando da utilizao em expresses regulares)
Exemplo
Vamos supor que desejamos implementar um analisador sintctico que reconhea
expresses aritmticas com apenas um nmero inteiro positivo ou com uma adio ou uma
subtraco de dois nmeros inteiros positivos (por exemplo: 2+3, 1-4, 5). De seguida
apresenta-se uma gramtica com a especificao do smbolo terminal utilizando uma
expresso regular:
INTEGER = [0-9]+
// smbolo terminal
medida que forem percebendo o JavaCC vo tambm tomando conhecimento do significado das
classes de suporte.
2/15
Para desenvolver com o JavaCC um parser que implemente esta gramtica temos de criar
um ficheiro onde vamos especificar o parser. Vamos chamar-lhe Exemplo.jj.
Ficheiro Exemplo.jj:
PARSER_BEGIN(Exemplo)
// cdigo Java que invoca o parser
public class Exemplo {
public static void main(String args[]) throws ParseException {
// criao do objecto utilizando o constructor com argumento para
// ler do standard input (teclado)
Exemplo parser = new Exemplo(System.in);
parser.Aritm();
}
}
PARSER_END(Exemplo)
// smbolos que no devem ser considerados na anlise
SKIP :
{
| \t | \r
}
// definio dos tokens (smbolos terminais)
TOKEN :
{
< INTEGER : ([0 9])+ >
| < LF : \n >
}
// definio da produo
void Aritm() : {}
{
<INTEGER> ( (+ | -) <INTEGER> )? <LF>
}
3/15
Por cada smbolo terminal INTEGER, foi inserida uma linha de cdigo Java que imprime no
ecr o valor do token lido (o atributo image da classe Token retorna uma String
representativa do valor do token2)
Insira estas modificaes no ficheiro Exemplo.jj e volte a repetir o processo at execuo
do parser. Verifique o que acontece.
Como poderamos escrever no ecr o sinal (+ ou -) lido?
nome.jjt
JJTree
4/15
Expr1 LF
Expr1
Expr2
Expr3
INTEGER
Expr3
- Expr3
Expr3
( Expr1 )
Para gerar a rvore sintctica vamos especificar a gramtica original num ficheiro com
extenso jjt e vamos adicionar as directivas que indicam ao JJTree para gerar o cdigo
necessrio para criar a rvore.
O ficheiro apresentado de seguida contm as regras gramaticais anteriores e as
modificaes introduzidas, identificadas a negrito, para gerar a rvore. O mtodo
dump(String) foi tambm modificado para imprimir no ecr a rvore gerada para cada
expresso introduzida.
Ficheiro Calculator.jjt:
options
{
LOOKAHEAD=1;
}
PARSER_BEGIN(Calculator)
public class Calculator
{
public static void main(String args[]) throws ParseException {
Calculator myCalc = new Calculator(System.in);
SimpleNode root = myCalc.Expression(); // devolve referncia para o n raiz da rvore
root.dump(""); // imprime no ecr a rvore
}
}
PARSER_END(Calculator)
SKIP :
{
| "\r" | "\t"
}
TOKEN:
{
< INTEGER: (["0"-"9"])+ >
| < LF: "\n" >
}
5/15
Executar o JJTree:
jjtree Calculator.jjt
3
4
6/15
Descrio:
Devolve o n pai do n actual
Devolve o n filho n i
Devolve o nmero de filhos do n
Escreve no ecr a rvore sintctica a
partir do n que o invocou
Depois da classe SimpleNode ser gerada pela primeira vez pelo JJTree podemos adicionar-lhe
mtodos e/ou atributos. Neste momento interessa-nos acrescentar classe dois atributos
que permitam armazenar o valor do inteiro (no caso das folhas da rvore) 5 e a operao a
realizar6.
Para este exemplo vamos definir uma classe com as operaes que precisamos:
Ficheiro MyConstants.java:
public class MyConstants {
public static final int ADD =1;
public static final int SUB =2;
public static final int MUL =3;
public static final int DIV =4;
public static final char[] ops = {'+','-','*','/'};
}
A classe SimpleNode apresentada de seguida com as alteraes efectuadas (a negrito).
Note-se que em vez de anotarmos os ns com a operao a realizar poderamos orientar o
JJTree para associar ns da rvore respectiva operao aritmtica (usando directivas do
tipo #Add). Mais frente veremos como usar essas directivas.
Ficheiro SimpleNode.java:
/* Generated By:JJTree: Do not edit this line. SimpleNode.java */
public class SimpleNode implements Node {
protected
protected
protected
protected
protected
Node parent;
Node[] children;
int id;
Object value;
Calculator parser;
// added
public int val;
Poderiam ter sido adicionados dois mtodos que permitem aceder ao campo (atribuir e retornar o
seu valor).
6
Poderamos usar o ID do n (ver campo id da classe SimpleNode e os tipos de ns para uma
dada gramtica na classe CalculatorTreeConstants).
7/15
8/15
apresentado de seguida o ficheiro que permite obter o analisador sintctico que inclui a
gerao da rvore sintctica anotada.
Para utilizarmos a rvore sintctica importante que possamos verificar de que tipo um
determinado n. Uma das possibilidades anotar cada n com a informao relacionada
com o respectivo tipo. Neste exemplo usamos as constantes definidas na classe
MyConstants.
Novo ficheiro Calculator.jjt:
options
{
LOOKAHEAD=1;
}
PARSER_BEGIN(Calculator)
public class Calculator
{
public static void main(String args[]) throws ParseException {
Calculator myCalc = new Calculator(System.in);
SimpleNode root = myCalc.Expression(); // devolve referncia para o n raz da rvore
root.dump(""); // imprime no ecr a rvore
}
}
PARSER_END(Calculator)
SKIP :
{
| "\r" | "\t"
}
TOKEN:
{
< INTEGER: (["0"-"9"])+ >
| < LF: "\n" >
}
SimpleNode Expression(): {}
{
Expr1() <LF> {return jjtThis;}
}
void Expr1(): {}
{
Expr2(1)
[
("+" {jjtThis.Op = MyConstants.ADD;}
| "-" {jjtThis.Op = MyConstants.SUB;}
9/15
10/15
11/15
12/15
Node parent;
Node[] children;
int id;
Object value;
Calculator parser;
// added
public int val;
int
int
int
int
int
int
int
JJTEXPRESSION = 0;
JJTVOID = 1;
JJTADD = 2;
JJTSUB = 3;
JJTMUL = 4;
JJTDIV = 5;
JJTTERM = 6;
13/15
"Add",
3. Exerccios Propostos
Exerccio 1
Modifique a gramtica anterior para que aceite atribuies a smbolos e a utilizao de
smbolos nas expresses aritmticas, como ilustrado no exemplo seguinte:
A=2;
B=3;
A*B; //
Expr1 LF
Expr1
Expr2
Expr3
INTEGER
Expr3
- Expr3
Expr3
( Expr1 )
4. Referncias
[1] JavaCC, https://javacc.dev.java.net/
[2] Tom Copeland, Generating Parsers with JavaCC, Second Edition, Centennial Books,
Alexandria, VA, 2009. ISBN 0-9762214-3-8, http://generatingparserswithjavacc.com/
[3] JavaCC [tm]: Documentation Index, https://javacc.dev.java.net/doc/docindex.html
14/15
15/15