Análises léxica e sintática
description
Transcript of Análises léxica e sintática
Análises léxica e sintática Teoria e Implementação de Linguagens
Computacionais - IF688
Mauro La-Salette C. L. de AraújoCentro de Informática – CIn
Universidade Federal de Pernambuco – UFPE
Roteiro Visão geral Análise léxica
Definição Especificação Implementação Correspondência
Análise sintática Definição Especificação Implementação
Algoritmos de parsing e gramáticas Gramáticas ambíguas Sintaxe abstrata
executável
Visão Geral
Análise sintática
Código fonte
AST
Análise semântica
Geração decódigo
AST decorada
Análiseléxica
Tokens
Análise Léxica
Definição Fase da compilação responsável por
extrair os tokens do código fonte de um programa.
if (n == 0) { return 1;} else { ...} RPAR LCUR
RCUR
if LPAR
return
else
"n"id
"0"intLit
assign
"1"intLit ...comm
EspecificaçãoOs tokens de uma linguagem
comumente são especificados através de Expressões Regulares
[a-z][a-z0-9]* identifier
[0-9]+ intLiteral
Implementação Autômatos finitos
1 2
a-z a-z
0-9
ID
21 3
i fIF
Análise Sintática
Definição Fase da compilação responsável por
determinar se uma dada cadeia de entrada pertence ou não à linguagem definida por uma gramática
Tem como entrada os tokens processados pela análise léxica
Produz uma estrutura comumente denominada AST – abstract syntax tree
Especificação
BNF - Backus-Naur form S, A, B, C, D : não-terminais
a,b,d: terminais
S ::= A | BA ::= C | DB ::= bbaC ::= abD ::= dab
Produções
ImplementaçãoAlgoritmos de parsing e gramáticas
ClassificaçãoTop-down
• Recursive-descent / LL(1)
Bottom-up
• LR, SLR, LALR, LR(k)
Recursive descent
Algoritmo baseado em previsões
Funções mutuamente recursivas
Uma função para cada não-terminal
Uma cláusula para cada produção
Recursive descent
Desenvolvendo um recursive descent parser
Cada não terminal 'X' dará origem a um método/função parseX();
Produções do tipo 'A | B' darão origem a cláusulas cases
Recursive descent
A ::= aBcC
B ::= CB | cC
C ::= da
parseA() { accept(‘a’); parseB(); accept(‘c’); parseC();}
parseB() { case (d): parseC(); parseB(); case (c): accept(‘c’); parseC();}
parseC() { accept(‘d’); accept(‘a’); }
Recursive descent Na prática constrói uma tabela de
produções indexadas por não-terminais e terminais
A ::= aBcC
B ::= CB | cC
C ::= da
a c d
A A::= aBcC
B B::= cC B::= CB
C C::= da
Recursive descent
A ::= aBcC
B ::= CB | CA
C ::= da
parseA() { accept(‘a’); parseB(); accept(‘c’); parseC();}
parseB() { case (d): parseC(); parseB(); case (d): parseC(); parseA();}
parseC() { accept(‘d’); accept(‘a’);}
Recursive descent Na prática constrói uma tabela de
produções indexadas por não-terminais e terminais
A ::= aBcC
B ::= CB | CA
C ::= da
a c d
A A::= aBC
B B::= CBB::= CA
C C::= da
Recursive descent
Vantagens Fácil de implementar
Desvantagens Performance Gramática reconhecida possui restrições
Sem recursão à esquerdaDeve estar fatorada ...
Recursive descent
A ::= aBC
B ::= CB | CA
C ::= da
A ::= aBC
B ::= CX
X ::= B | A
C ::= da
a c dA A::= aBCB B::= CXC C::= daX X::=A X::=B
GramáticaLL(1)
Gramáticas LL(1)
Left-to-right parse Leftmost-derivation 1-symbol-lookahead
Algoritmos bottom-up
Algoritmos LL(k) precisam decidir que produção usar tendo visto apenas k tokens da entrada
Algoritmos bottom-up são baseados em técnicas LR(k) Left-to-right parse, Right-most derivation,
k-symbol-lookahead
Algoritmos bottom-up
Baseados no conceito de autômato a pilha Pilha + lookahead Duas tipos de ações
Shift: • Coloca o primeiro token da entrada no topo da
pilhaReduce:
• Escolhe a regra X::= A B C • Retira C, B, A da pilha• Coloca X na pilha
Gramáticas LR LR(0)
Olham apenas para a pilha SLR
Melhoramento sobre o LR(0) LR(1)
Lookahead de 1 símbolo Consegue descrever a maioria das linguagens de
programação LALR(1)
Melhoramento sobre o LR(1) Diminuí o tamanho da tabela de parsing
Gramáticas Ambíguas Uma gramática é ambígua se a partir dela
uma sentença pode dar origem a duas arvores de parsing
Problemáticas para a compilação
Eliminação de ambigüidade é quase sempre possível Transformações na gramática
Gramáticas Ambíguas Caso clássico: gramática para expressões
aritméticas
E ::= intLiteral | E '*' E | E '/' E | E '+' E | E '-' E |'(' E ')'
Gramáticas Ambíguas
1 + 2 * 3
E
E E
E E
*
+ 3
21
E
E E
EE
+
*1
2 3
Gramáticas Ambíguas Solução:
Transformar a gramática * e / com maior precedência que + ou - Operadores associativos a esquerda
E ::= intLiteral | E '*' E | E '/' E | E '+' E | E '-' E |'(' E ')'
E ::= E '+' T | E '–' T | TT ::= T '*' F | T '/' F | FF ::= intLiteral |'(' E ')'
Parsing LR de Gramáticas Ambíguas
Gramáticas ambíguas ocasionam conflitos em parsers LR Shift-reduce conflict
O parser não consegue decidir se empilha o próximo símbolo da entrada, ou se reduz para uma regra já disponível
Reduce-reduce conflictO parser pode realizar uma redução para duas
regras distintas
Parsing LR de Gramáticas Ambíguas Caso clássico: dangling-else
S ::= 'if' E 'then' S 'else' SS ::= 'if' E 'then' SS ::= ...
Parsing LR de Gramáticas Ambíguas
if a then if b then s1 else s2
if a then { if b then s1 else s2 }
if a then { if b then s1 } else s2
?
Parsing LR de Gramáticas Ambíguas
if a then if b then s1 else s2Input:
Stack:
if a then if b then s1
reduce St ?shift else ?
St
Optando pelo reduce...
else s2
Parsing LR de Gramáticas Ambíguas
if a then if b then s1 else s2Input:
Stack:
if a then if b then s1
reduce St ?shift else ?
else s2
Optando pelo shift...
St
Parsing LR de Gramáticas Ambíguas Solução:
Transformar a gramática Introdução dos conceitos de matched e unmatched
S ::= 'if' E 'then' S 'else' SS ::= 'if' E 'then' SS ::= ...
S ::= M | U
M ::= 'if' E 'then' M 'else' M | ...
U ::= 'if' E 'then' S | 'if' E 'then' M 'else' U
LR(0)LL(0)
SLR
LALR(1)
LL(k)
LL(1)
Gramáticas não-ambíguas Gramáticas ambíguas
LR(1)
LR(k)
Sintaxe abstrata Apenas reconhecer se uma sentença
pertence ou não a linguagem especificada por uma gramática não é o suficiente
É necessário produzir uma estrutura que sirva de base para a próxima fase do processo de compilação Parse trees nunca são montadas na prática
AST – Abstract Syntax Tree Capturam a essência da estrutura de uma
gramática abstraindo não-terminais
Representação possível Java: Classes que possam se relacionar a fim de
montar uma árvore
Pode ser produzida através da inserção de ações semânticas no parser
AST – Abstract Syntax Tree
IfThenElse ::= 'if' expr 'then' comm1 'else' comm2
return new IfThenElse(expr, comm1, comm2);
IfThenElseexpr : Expressioncomm1 : Commandcomm2 : Command
Análises léxica e sintática Teoria e Implementação de Linguagens
Computacionais - IF688
Mauro La-Salette C. L. de AraújoCentro de Informática – CIn
Universidade Federal de Pernambuco – UFPE