Aula sobre JavaCC
Parsing
Tarciana Dias
[email protected]
Abril 2013
1
Roteiro
• Sobre o JavaCC – forma de especificação das gramáticas
• Instalando o plugin do JavaCC no eclipse
• Análise da gramática default gerada pelo JavaCC
• Analisando a linguagem de expressões 1
– Revisando conceitos básicos de gramáticas LL
– Aplicando os conceiros na gramática do exp1
• Referências
2
Exemplo 1
• Gramática
Start := NUMBER ( “+” NUMBER )
Classe do parser
options {
STATIC = false ;
}
PARSER BEGIN(Adder)
public class Adder {
static void main( String[] args )
throws ParseException, TokenMgrError {
Adder parser = null;
try {
parser = new Adder(new FileInputStream("input.txt"));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(parser.start());}
}
PARSER END(Adder)
Analisador léxico
SKIP : { ” ” }
SKIP : { ”\n” | ”\r” | ”\r\n” }
TOKEN : { < PLUS : ”+” > }
TOKEN : { < NUMBER : ([”0”-”9”])+ > }
Gramática “EBNF”
void Start() :
{} {
<NUMBER>
(
<PLUS>
<NUMBER>
)*
<EOF>
}
Possiveis saidas
• Entrada válida: 1 + 5
• Token inválido: 1 – 3 ( Exceção)
• Erro sintático: 1 ++ 5 (Exceção)
Código gerado
final public void start() throws ParseException {
jj_consume_token(NUMBER);
label_1:
while (true) {
switch ((jj_ntk==-1)? jj_ntk() : jj_ntk) {
case PLUS:
;break;
default:
jj_la1[0] = jj_gen;
break label_1;
}
jj_consume_token(PLUS);
jj_consume_token(NUMBER);
}
jj_consume_token(0); }
Adicionando ações
int Start() throws NumberFormatException : {
}{
Token t ;
int i ;
int value ;
t = <NUMBER>
{ i = Integer.parseInt( t.image ) ; }
{ value = i ; }
(<PLUS>
t = <NUMBER>
{ i = Integer.parseInt( t.image ) ; }
{ value += i ; }
)*
<EOF>
{ return value ; }}
Roteiro
• Sobre o JavaCC – forma de especificação das gramáticas
• Instalando o plugin do JavaCC no eclipse
• Análise da gramática default gerada pelo JavaCC
• Analisando a linguagem de expressões 1
– Revisando conceitos básicos de gramáticas LL
– Aplicando os conceiros na gramática do exp1
• Referências
10
Plugin Eclipse
• Baixar o plugin:
– Pagina do plugin:
http://eclipse-javacc.sourceforge.net/
– Para atualizar o eclipse com o plugin:
• Pegar a pasta ...\sf.eclipse.javacc-1.5.27plugin\plugins\sf.eclipse.javacc_1.5.27 e copiá-la
para o caminho do eclipse ...\eclipse\plugins
• Fazer o mesmo para a pasta ...\sf.eclipse.javacc1.5.27plugin\features\sf.eclipse.javacc.feature_1.5.27 só
que para o caminho ...\eclipse\features
Quick start plugin eclipse
• Criar um projeto Java no eclipse
• Criar uma pasta foo submissa à pasta do projeto
• Em seguida, criar um novo projeto só que selecionando
a opção Other, escolhendo a pasta JavaCC e a opção
JavaCCTemplateFile, conforme mostrado a seguir:
New->Other, ir na opção javaCC-> JavaCC Template File
Quick start plugin eclipse [2]
Quick start plugin eclipse [3]
• Ao clicar em Next, a tela seguinte
aparece.
• Observe que se deve indicar a gramática
utilizada como referência. No caso default,
ele utiliza MyNewGrammar.jj
• Mas podemos colocar uma das
gramáticas disponíveis na página da
disciplina /~in101 e vistas até então
(iremos fazer depois):
– Expressoes1.jj ou Expressoes2.jj ou
Funcional1.jj
Quick start plugin eclipse [4]
Quick start plugin eclipse [5]
• Após escolhida a gramática, clica-se em
Finish:
Arquivos gerados
•
•
•
•
•
•
•
•
EG1
EG1Constants
EG1TokenManager
MyNewGrammar.jj
ParserException
SimpleCharStream
Token
TokenMgrError
Roteiro
• Sobre o JavaCC – forma de especificação das gramáticas
• Instalando o plugin do JavaCC no eclipse
• Análise da gramática default gerada pelo JavaCC
• Analisando a linguagem de expressões 1
– Revisando conceitos básicos de gramáticas LL
– Aplicando os conceiros na gramática do exp1
• Referências
18
Outro Exemplo
• Gramática
– Vide MyNewGrammar.jj
Roteiro
• Sobre o JavaCC – forma de especificação das gramáticas
• Instalando o plugin do JavaCC no eclipse
• Análise da gramática default gerada pelo JavaCC
• Analisando a linguagem de expressões 1
– Revisando conceitos básicos de gramáticas LL
– Aplicando os conceiros na gramática do exp1
• Referências
20
• Vamos agora analisar a gramática da
linguagem de expressões 1
• Antes disso, vamos revisar as técnicas de
fatorar uma gramática à esquerda e
eliminar recursões à esquerda
21
Conceitos Básicos
• Transformações de Gramática
– Fatoração à esquerda
– Ex;: XY | XZ equivale à X (Y | Z)
single-Command ::= V-name := Expression
| if Expression then single-Command
| if Expression then single-Command
else single-Command
single-Command ::= V-name := Expression
| if Expression then single-Command
( ε | else single-Command)
22
Conceitos Básicos
• Transformações de Gramática
– Eliminação de recursão à esquerda
N ::= X | NY, onde N é um símbolo não-terminal e X e Y
são REs estendidas. Esta produção é recursiva à esquerda.
Substituindo por uma regra EBNF
equivalente
N ::= X(Y)*
23
Conceitos Básicos
• Transformações de Gramática
Identifier ::= Letter
| Identifier Letter
| Identifier Digit
Fatorando
à esquerda
Identifier ::= Letter
| Identifier (Letter | Digit)
Eliminando
recursão à
esquerda
Identifier ::= Letter (Letter | Digit)*
24
Conceitos Básicos
• A necessidade da eliminação de recursão
à esquerda é melhor entendida depois
que se vai usar a abordagem top-down;
25
Estratégias de Parsing
•
Objetivo
– Reconhecimento de uma string de entrada (seqüência de tokens) e decisão se
esta é ou não uma sentença de G
– Determinação de sua estrutura de frases (pode ser representada por uma árvore
sintática)
– Gramática não ambígua: cada sentença tem exatamente uma syntax tree
•
Top-Down
– Examina os símbolos terminais da esquerda para a direita
– Forma a ST (syntax tree) de cima para baixo
– Parsing ok: string de entrada totalmente conectada à ST
• L(eft-to-right) L(eft-most-derivation) => LL
•
Bottom-Up
– Examina os símbolos terminais da esquerda para a direita
– Forma a ST (syntax tree) de baixo (nós terminais) para cima(nó raiz)
– Parsing ok: string de entrada reduzida a uma S-tree
• S(imple) L(eft-to-right) R(ight-most-derivation) => SLR
• L(eft-to-right) R(ight-most-derivation) => LR
• L(ook) A(head) L(eft-to-right) R(ight-most-derivation) => LALR
26
Estratégia Bottom-Up
•
Sentence ::= Subject Verb Object
Subject ::= I | a Noun | the Noun
Object ::= me | a Noun | the Noun
Noun ::= cat | mat | rat
Verb ::= like | is | see | sees
Exemplo:
Sentence
Object
Subject
Noun
the
Noun
Verb
cat sees
Aqui ele não
poderia ter
escolhido um
Subject?
a
rat .
27
Estratégia Top-Down
•
Sentence ::= Subject Verb Object
Subject ::= I | a Noun | the Noun
Object ::= me | a Noun | the Noun
Noun ::= cat | mat | rat
Verb ::= like | is | see | sees
Exemplo:
Sentence
Object
Subject
Noun
the
Noun
Verb
cat sees
a
rat .
28
Estratégias de Parsing
•
Qual estratégia de parsing devemos usar?
– Isso vai depender do tipo de gramática !
– Sempre é necessário escolher qual regra de produção aplicar
•
Isto é feito de acordo com o algoritmo de Parsing
– Recursive Descent é um algoritmo de parsing top-down
– Consiste de um grupo de métodos parseN, um para cada símbolo não-terminal
N de G. Estes métodos cooperam para fazer o parse das sentenças completas
parseSentence
parseSubject
parseVerb
parseObject
parseNoun
parseNoun
the
cat
sees
a
rat .
29
Gramáticas LL(1)
•
Deve-se expressar a gramática em EBNF, com uma regra de produção
simples para cada símbolo não-terminal, e realizar as transformações de
gramática necessárias, por exemplo, sempre eliminar recursão à esquerda
e fatorizar à esquerda sempre que possível
•
Gramáticas LL(1):
–
–
Se a gramática contém X | Y, starters[[ X ]] e starters[[ Y ]] devem ser disjuntos
Se a gramática contém X* , starters[[ X ]] deve ser disjunto do conjunto de tokens que
podem seguir X*
•
Na prática quase toda gramática de uma linguagem de programação pode
ser transformada em LL(1), sem mudar a linguagem que a gramática gera
•
Recursive-descent parsing é somente adequado para gramáticas LL(1)
–
Em geral, o projetista da linguagem projeta a sua sintaxe para ser adequada à um parsing
recursive-descent.
30
Gramáticas não-LL(1)
•
Exemplo de uma Gramáticas não LL(1):
Program ::= single-Command
Command ::= single-Command (; single-Command)*
V-name ::= Identifier
single-Command ::= V-name := Expression
| Identifier ( Expression )
| if Expression then single-Command
| if Expression then single-Command
else single-Command
…
Expression ::= primary-Expression (Operator primary-Expression)*
primary-Expression ::= Integer-Literal
| Identifier
…
31
Gramáticas não-LL(1)
•
Desenvolvimento do método parseSingleCommand:
private void parseSingleCommand(){
Uma simples
switch(currentToken.kind){
fatoração à
esquerda resolve
case Token.IDENTIFIER: {
parseVname();
o problema!
accept(Token.BECOMES);
parseExpression();
}
}
break;
case Token.IDENTIFIER: {
parseIdentifier ();
accept(Token.LPAREN);
parseExpression();
accept(Token.RPAREN);
}
break;
}
…
V-name ::= Identifier
single-Command ::= V-name := Expression
| Identifier ( Expression )
| …
…
single-Command ::= Identifier (:= Expression | ( Expression ) )
32
Gramáticas não-LL(1)
•
•
Exemplo de uma Gramáticas não LL(1):
…
Block::= begin Declaration (; Declaration)* ; Command end
Declaration ::= integer Identifier (, Identifier)*
…
Aqui, starters[[ ;Declaration ]] = {;} e o conjunto de terminais que seguem (;
Declaration)* neste contexto também é {;}
–
Como não são disjuntos, então a gramática não é LL(1)
private void parseBlock(){
accept(Token.BEGIN){
parseDeclaration();
while (currentToken.kind ==
Token.SEMICOLON)
acceptIt();
parseDeclaration();
}
accept(Token.SEMICOLON);
parseCommand();
accept(Token.END);
}
Como
resolver?
…
Block::= begin Declaration ; (Declaration;)*
Command end
…
33
Recursive Descent Parser
• Recursive Descent Parser
– Algoritmo de parsing para gramáticas LL
– Visão geral
• Para cada produção N, crie um método parseN
• Crie uma classe parser com um atributo currentToken
– E os métodos parseN
– E os métodos auxiliares: accept e acceptIt
– E um método público parse que chama parseS
• O código de cada método parseN depende da produção N
• A árvore é dada implicitamente pela chamada dos métodos
– Pode ser criada explicitamente
34
Roteiro
• Sobre o JavaCC – forma de especificação das gramáticas
• Instalando o plugin do JavaCC no eclipse
• Análise da gramática default gerada pelo JavaCC
• Analisando a linguagem de expressões 1
– Revisando conceitos básicos de gramáticas LL
– Aplicando os conceiros na gramática do exp1
• Referências
35
Criando a estrutura para a
linguagem exp1
• Baixar a gramática Expressoes1.jj contida em
http://www.cin.ufpe.br/~in1007/linguagens/Expresso
es1/expressao1.html
• Observar no início do arquivo que:
PARSER_BEGIN(Exp1Parser)
package plp.expressions1.parser;
import plp.expressions1.*;
import plp.expressions1.expression.*;
import plp.expressions1.util.*;
• Logo devemos criar o projeto java seguindo a
estrutura plp.expressions1 etc etc !
Criando a estrutura para a linguagem
exp1
• Criar um projeto java comum
• Em seguida criar um pacote chamado plp
e em seguida um chamado expressions1,
submisso à plp
• Em seguida criar uma pasta/pacote parser
dentro de expressions1
• Selecione a pasta parser e repita o
processo anterior de criar um projeto
JavaCC
Criando a estrutura para a linguagem
exp1
• File-> New->Other, ir na opção javaCC->
JavaCC Template File
Criando a estrutura para a linguagem
exp1
Criando a estrutura para a linguagem
exp1
• Vá até a pasta onde os arquivos foram
gerados, pasta parser, apague todos e
coloque na pasta apenas o arquivo da
gramática Expressoes1.jj
• Você vai notar que o eclipse gera todos os
arquivos novamente, de forma automática,
só que agora tendo como referência a
nova gramática.
Criando a estrutura para a linguagem
exp1
• Dois dos arquivos gerados, Exp1Parser e
Exp1ParserTokenManager apresentarão
erros de compilação.
• Isto porque estão faltando ainda algumas
classes básicas como por exemplo Valor,
ValorInteiro, etc, mencionadas no arquivo .jj
por isso referenciadas pelo Exp1Parser.java
gerado pelo JavaCC.
• Além desta a classe Programa também é
referenciada.
Criando a estrutura para a linguagem
exp1
• Baixar da página da disciplina as classe
listadas abaixo
Criando a estrutura para a linguagem
exp1
• Criar o pacote expression no mesmo nível do
pacote parser e colocar as classes listadas
anteriormente no pacote expression
• Ainda, classe Programa também é referenciada
nas classes geradas. Baixar a mesma na página
da disciplina e colocá-la no pacote expressions1
• Ainda, as classes Tipo e TipoPrimitivo também
são referenciadas. Baixá-las e colocá-las dentro
do pacote util, a ser criado no mesmo nível do
pacote parser e do pacote expression.
Referências
• WATT, D.; BROWN, D.
Programming Language Processors in Java.
– Capítulo 4
• Foco maior na abordagem LL
• Site do plugin JavaCC para eclipse:
– http://eclipse-javacc.sourceforge.net/
• Tutorial JavaCC:
– http://www.engr.mun.ca/~theo/JavaCC-Tutorial
• Site do JavaCC:
– http://javacc.java.net/
44
Aula sobre JavaCC
Parsing
Tarciana Dias
[email protected]
Abril 2013
45
Download

Aula JavaCC