Ficha nº 2

Propaganda
Ficha nº 2
Familiarização com a ferramenta JavaCC
Estrutura do ficheiro JavaCC
O JavaCC utiliza um ficheiro com a extensão .jj , onde são descritos, pelo utilizador, o léxico e
a sintaxe da linguagem e gera um conjunto de classes Java de suporte, tendo uma delas o
nome do analisador sintático (parser). As classes geradas incluem o analisador léxico
(scanner), o parser e outras classes de suporte de I/O e exceções. À medida que forem
percebendo o JavaCC vão também tomando conhecimento do significado das classes de
suporte.
O ficheiro .jj é constituído por:
1. Zona de opções (opcional): é onde pode ser definido o nível global de lookahead
(o default é lookahead=1;), quantos parsers podem ser criados (o default é apenas um
parser, static=true;), entre outros.
2. Unidade de compilação Java (PARSER_BEGIN(nome_do_parser) ...
PARSER_END(nome_do_parser)) que define a classe principal do parser e onde
pode ser incluído o método main do parser.
3. Lista de produções da gramática (as produções aceitam os símbolos +, *, e ?
com o mesmo significado do da notação EBNF).
JavaCC no Eclipse
Usando o plug-in JavaCC para o Eclipse, para criar um ficheiro .jj basta criar um novo
projeto Java, um novo package e um ficheiro JavaCC (veja http://eclipsejavacc.sourceforge.net/).
Se optou por criar o seu ficheiro .jj com o template do JavaCC plugin wizard, o ficheiro criado
contém a especificação de uma gramática para expressões aritméticas simples. O ficheiro
criado tem o nome, por default, MyNewGrammar.jj e o nome da classe parser é eg1.
Compiladores 2012-2013 | Ficha nº 2
1
Ao compilar, em JavaCC, o seu ficheiro .jj, serão automaticamente gerados os ficheiros de
suporte ao seu parser, como se mostra na figura 1.
Figura 1 – JavaCC no Eclipse: ficheiros gerados após a compilação do ficheiro new_file.jj
criado com o wizard do JavaCC.
A figura 2 mostra um exemplo de execução do ficheiro new_file.jj, cujo nome da classe do
parser é eg1, criado anteriormente.
Compiladores 2012-2013 | Ficha nº 2
2
Figura 2 – JavaCC no Eclipse: exemplo de execução do ficheiro new_file.jj criado com o
wizard do JavaCC.
Caso não pretenda utilizar o Eclipse poderá usar o NetBeans, que também tem um plug-in
para o JavaCC, ou outro qualquer IDE ou editor.
Exemplo de uma gramática para expressões aritméticas
simples que somam ou subtraem dois números inteiros
positivos
Vamos supor que desejamos implementar um analisador sintáctico que reconheça expressões
aritméticas com apenas um número inteiro positivo ou com uma adição ou uma subtracção
de dois números inteiros positivos, por exemplo, 9, 5+3, 8-4, etc. De seguida apresenta-se
uma gramática com a especificação do símbolo terminal e da regra gramatical, utilizando a
notação EBNF:
INTEGER = [0-9]+ // símbolo terminal
Aritm
→ INTEGER [(“+” | “-“) INTEGER] // regra gramatical
Compiladores 2012-2013 | Ficha nº 2
3
Para desenvolver, com o JavaCC, um parser que implemente esta gramática temos de criar
um ficheiro onde vamos especificar o parser. Vamos chamar-lhe Exemplo.jj.
Ficheiro Exemplo.jj:
PARSER_BEGIN(Exemplo)
// código Java que invoca o parser
public class Exemplo {
public static void main(String args[]) throws ParseException {
// criação do objecto utilizando o constructor com o argumento
// para ler do standard input (teclado)
Exemplo parser = new Exemplo(System.in);
parser.Aritm();
}
}
PARSER_END(Exemplo)
// símbolos que não devem ser considerados na análise léxica
SKIP :
{
“ “ | “\t” | “\r”
}
// definição dos tokens (símbolos terminais)
TOKEN :
{
< INTEGER : ([“0”–“9”])+ >
| < LF : “\n” >
}
// definição da produção
void Aritm() :
{}
{
<INTEGER> ( (“+” | “-“) <INTEGER> )? <LF> // “(...)?” é equivalente a “[...]”
}
Para gerar o parser da linha de comandos:
javacc Exemplo.jj
Compiladores 2012-2013 | Ficha nº 2
4
Para compilar o código Java gerado:
javac *.java
para executar o analisador sintáctico:
java Exemplo
Poderemos associar às funções referentes aos símbolos não-terminais pedaços de
código Java. Por exemplo, as modificações apresentadas de seguida permitem
escrever no ecrã mensagens a indicar os números que são lidos pelo parser:
void Aritm() :
{Token t1, t2;}
{
t1=<INTEGER> {
System.out.println(“Integer = “+t1.image);
}
( (“+” | “-“) t2=<INTEGER> {
System.out.println(“Integer = “+t2.image);
}
)? <LF>
}
Para cada símbolo terminal INTEGER, foi inserida uma linha de código Java, entre
chavetas, que imprime no ecrã o valor do token lido (o atributo image da classe Token
retorna uma String representativa do valor do token). Posteriormente, serão
apresentados outros métodos. Insira estas modificações no ficheiro Exemplo.jj e volte
a repetir o processo até à execução do parser. Verifique o que acontece.
Exercícios
1. Altere o código anterior para que seja escrito no ecrã o sinal ‘+’ ou ‘-‘ lido.
2. Altere o código anterior para que sejam permitidas multiplicações e divisões.
3. Altere o código anterior para que seja possível reconhecer sequências de
expressões separadas por ponto e vírgula. Utilize o token <EOF>, já prédefinido no JavaCC, para demarcar o fim da sequência.
Compiladores 2012-2013 | Ficha nº 2
5
4. Construa um analisador que reconhece uma sequência de parêntesis
correctamente emparelhados (balanceados) e escreve no ecrã o seu nível de
encaixe. Por exemplo, o output do programa para a string “((( )) )” é 3 .
Compiladores 2012-2013 | Ficha nº 2
6
Download