380 likes | 509 Views
Análises léxica e sintática. Teoria e Implementação de Linguagens Computacionais - IF688 Mauro La-Salette C. L. de Araújo Centro de Informática – CIn Universidade Federal de Pernambuco – UFPE mscla@cin.ufpe.br. Roteiro. Visão geral Análise léxica Definição Especificação Implementação
E N D
Análises léxica e sintática Teoria e Implementação de Linguagens Computacionais - IF688 Mauro La-Salette C. L. de Araújo Centro de Informática – CIn Universidade Federal de Pernambuco – UFPE mscla@cin.ufpe.br
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
Análiseléxica Análise semântica Geração de código AST decorada AST Visão Geral Código fonte Análise sintática executável Tokens
"n" id intLit intLit "1" "0" 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 { ... } if LPAR assign RPAR LCUR return comm RCUR else ...
Especificação • Os tokens de uma linguagem comumente são especificados através de Expressões Regulares • [a-z][a-z0-9]* identifier • [0-9]+ intLiteral
IF i f 1 2 ID a-z a-z 2 3 1 0-9 Implementação • Autômatos finitos
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 S ::= A | B A ::= C | D B ::= bba C ::= ab D ::= dab Produções • BNF - Backus-Naur form • S, A, B, C, D : não-terminais • a,b,d: terminais
Implementação • Algoritmos de parsing e gramáticas • Classificação • Top-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 descentparser • 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 parseA() { accept(‘a’); parseB(); accept(‘c’); parseC(); } parseB() { case (d): parseC(); parseB(); case (c): accept(‘c’); parseC(); } A ::= aBcC B ::= CB | cC C ::= da 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
Recursive descent parseA() { accept(‘a’); parseB(); accept(‘c’); parseC(); } parseB() { case (d): parseC(); parseB(); case (d): parseC(); parseA(); } A ::= aBcC B ::= CB | CA C ::= da 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
Recursive descent • Vantagens • Fácil de implementar • Desvantagens • Performance • Gramática reconhecida possui restrições • Sem recursão à esquerda • Deve estar fatorada • ...
Recursive descent A ::= aBC B ::= CB | CA C ::= da A ::= aBC B ::= CX X ::= B | A C ::= da Gramática LL(1)
Gramáticas LL(1) Leftmost-derivation 1-symbol-lookahead Left-to-right parse
Algoritmos bottom-up • Algoritmos LL(k) precisam decidir que produção usar tendo visto apenas ktokens 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 pilha • Reduce: • 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 E 3 1 + * E E E E 1 2 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 | T T ::= T '*' F | T '/' F | F F ::= 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 conflict • O 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' S S ::= 'if' E 'then' S S ::= ...
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 s2 Input: Stack: s2 if a then if St else b then s1 reduce St ? shift else ? Optando pelo reduce...
Parsing LR de Gramáticas Ambíguas if a then if b then s1 else s2 Input: Stack: if a then if St b then s1 else s2 reduce St ? shift else ? Optando pelo shift...
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' S S ::= 'if' E 'then' S S ::= ... S ::= M | U M ::= 'if' E 'then' M 'else' M | ... U ::= 'if' E 'then' S | 'if' E 'then' M 'else' U
Gramáticas não-ambíguas Gramáticas ambíguas LR(k) LL(k) LL(1) LR(1) LALR(1) SLR LR(0) LL(0)
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);
Análises léxica e sintática Teoria e Implementação de Linguagens Computacionais - IF688 Mauro La-Salette C. L. de Araújo Centro de Informática – CIn Universidade Federal de Pernambuco – UFPE mscla@cin.ufpe.br