1.67k likes | 2.05k Vues
CES-41 COMPILADORES. Capítulo V Análise Sintática. Capítulo V – Análise Sintática. 5.1 – Classificação dos analisadores sintáticos 5.2 – Tratamento de gramáticas 5.3 – Escolha de uma derivação 5.4 – Análise top-down 5.5 – Análise bottom-up 5.6 – Complementos de Yacc.
E N D
CES-41 COMPILADORES Capítulo V Análise Sintática
Capítulo V – Análise Sintática 5.1 – Classificação dos analisadores sintáticos 5.2 – Tratamento de gramáticas 5.3 – Escolha de uma derivação 5.4 – Análise top-down 5.5 – Análise bottom-up 5.6 – Complementos de Yacc
5.1 – Classificação dos Analisadores Sintáticos • Papel do analisador sintático (visto anteriormente): • Verificar a estrutura sintática de um programa • Detectar, sinalizar e tratar erros sintáticos • Servir de esqueleto para o front-end
Classesde métodos de análise sintática: • Analisadores universais • Analisadores top-down • Analisadores bottom-up • Analisadores universais: • Conseguem analisar qualquer GLC • São muito ineficientes para uso em compiladores
Analisadores top-down e bottom-up: • Varrem o programa da esquerda para a direita, um símbolo de cada vez; alguns deles por vezes precisam voltar da direita para a esquerda • Englobam os métodos mais comumente usados em compiladores • Os métodos mais eficientes trabalham apenas com subclasses de gramáticas • Várias dessas subclasses (gramáticas LL e LR, descritas mais adiante) conseguem descrever a maioria das construções sintáticas das linguagens de programação
Analisadores top-down: • Caminham pela árvore sintática dos programas, da raiz para as folhas • Trabalham com gramáticas LL (Left-Left) • Englobam os métodos preditores, dentre os quais, os diagramas de transições • As implementações manuais utilizam métodos top-down
Analisadores bottom-up: • Caminham pela árvore sintática dos programas, das folhas para a raiz • Trabalham com gramáticas LR (Left-Right) • Englobam importantes métodos: • Análise de precedência de operadores • Análise LR • São muito usados na geração automática de analisadores sintáticos
5.2 – Tratamento de Gramáticas • Métodos de análise sintática se aplicam a determinadas classes de GLC’s • Algumas gramáticas devem ser alteradas para que um método possa ser aplicado • É a busca por uma GLCequivalente à original • Duas GLC’s são equivalentes quando geram a mesma linguagem
5.2.1 – Eliminação de ambiguidades • Alguns métodos de análise sintática exigem que a gramática seja não-ambígua • Outros métodos mantém a ambiguidade das gramáticas e usam regras para eliminar árvores sintáticas indesejáveis
Exemplo: comando IF-ELSE da Linguagem C • Produções típicas: Cmd IF ExprCmd| IF ExprCmd ELSE Cmd |c1 | c2 • A sentença: IF e1 IF e2 c1 ELSE c2 tem duas árvores sintáticas:
A regra usual para resolver a ambiguidade é fazer o ELSE corresponder ao segundo IF • Esta regra pode ser incorporada à gramática • Gramática equivalente: CmdCmdCasado|CmdNãoCasado CmdCasado IF ExprCmdCasadoELSE CmdCasado | c1 | c2 CmdNãoCasado IF ExprCmd | IF ExprCmdCasadoELSE CmdNãoCasado
CmdCmdCasado|CmdNãoCasado CmdCasado IF ExprCmdCasadoELSE CmdCasado | c1 | c2 CmdNãoCasado IF ExprCmd | IF ExprCmdCasadoELSE CmdNãoCasado • CmdCasado nunca produz IF sem ELSE • Esta gramática impede o aparecimento de comandos não-casados entre Expr e ELSE
CmdCmdCasado|CmdNãoCasado CmdCasado IF ExprCmdCasadoELSE CmdCasado | c1 | c2 CmdNãoCasado IF ExprCmd | IF ExprCmdCasadoELSE CmdNãoCasado • Essa nova gramática dificultaanálise preditora: • Em Cmd, se aparecer um IF, qual das duas produções tomar? • Cada tipo de ambiguidade deve ser eliminada mediante um tratamento particular
5.2.2 – Eliminação de recursividade à esquerda • Uma GLC é recursiva à esquerda, se tiver um não-terminal A tal que haja uma derivação A + A, onde (N )* • Os métodos de análise top-down não conseguem trabalhar com gramáticas recursivas à esquerda
Exemplo: sejam as produções ExprTermExpr + Term • Num método top-down típico, há um procedimento para cada um dos não-terminais Expr e Term: Expr ( ) { if ( func (átomos analisados e/ou futuros) == TRUE ) { Expr ( ); if (atom != ‘+’) erro (“+ esperado”) elseTerm ( ); } elseTerm ( ); }
Expr ( ) { if ( func (átomos analisados e/ou futuros) == TRUE ) { Expr ( ); if (atom != ‘+’) erro (“+ esperado”) elseTerm ( ); } elseTerm ( ); } • Casofunc (átomos analisados e/ou futuros) seja TRUE, na chamada interna de Expr, os átomos serão os mesmos • O valor de func se repete, resultando em infinitas chamadas recursivas
Para aplicar um método top-down, toda recursividade à esquerda deve ser eliminada da gramática • Há recursividade imediata e não-imediata à esquerda • Exemplos: Imediata: S S b S a Não-imediata: S Aa b , A Sc d
a) Recursividade imediata à esquerda: • É o caso de uma produção ser recursiva à esquerda • Um caso bem simples é o das seguintes produções: A A (, (N )* e não iniciam com A) • Estas produções podem ser substituídas pelas seguintes: A R RR ε que geram as mesmas formas sentenciais que as originais, conforme o próximo slide
{A A} {A R RR ε} • Resolve muitos casos, mas não é geral
Exemplo: seja a seguinte gramática, com duas produções contendo recursividade imediata à esquerda: Expr Expr+ Termo Termo Termo Termo * Fator Fator Fator ( Expr) ID Transformação da gramática: Expr Termo Eaux Eaux + Termo Eaux ε Termo Fator Taux Taux * Fator Taux ε Fator ( Expr) | ID {A A} {A R R R ε} Nas produções de Expr, substituindo: A por Expr por + Termo por Termo R por Eaux De modo análogo, na produção de Termo
Generalização: várias produções do mesmo não-terminal, com recursividade a esquerda Algoritmo 5.1: As seguintes produções: A A 1 A 2 - - - A m 1 2 - - - n Podem ser substituídas por: A 1 A' 2 A' - - - n A' A' 1 A' 2 A' - - - m A' ε Nenhum ipode ser ε
b) Recursividade não-imediata à esquerda: • O algoritmo a seguir elimina toda recursividade à esquerda de uma gramática • Condições da gramática: não deve ter ciclos nem produções vazias • Gramática sem ciclos: sem derivações da forma A + A • Gramática sem produções vazias: a única produção vazia permitida é S (S: símbolo inicial) e S não aparece do lado direito de qualquer produção • Existem algoritmos para eliminar ciclos e produções vazias de GLC’s
Algoritmo 5.2: eliminação de recursividade à esquerda Arranjar os não-terminais numa ordem A1, A2, ... , An Para (i = 1; i <= n; i++) { Para (j = 1; j <= i-1; j++) { Sendo Aj 1 2 - - - m as produções de Aj, no momento, Substituir cada produção da forma Ai Aj pelas produções Ai 1 2 - - - m } Eliminar as recursividades imediatas a esquerda entre as produções de Ai; }
Exemplo: Eliminar as recursividades à esquerda da gramática: S S a T b c T S d U e f U S g U h i • Renomeando os não-terminais: S = A1, T = A2, U = A3 • A gramática torna-se em: A1 A1 a A2 b c A2 A1 d A3 e f A3 A1 g A3 h i
Para i = 1: • Eliminação das recursividades imediatas à esquerda de A1: A1 A2 b X c X X a X ε Algoritmo 5.1 A A1 1 2 A 1 A' 2 A' A' 1 A' ε A1 A2 b X c X X a X ε A2 A1 d A3 e f A3 A1 g A3 h i Novo estado das produções
Para i = 2, j = 1: • Produções de A2: A2 A1 d A3 e f • Produções de A1no momento: A1 A2 b X c X • Substitui-se A1 d por A2 b X d c X d: Produção da forma A2 A1
Para i = 2: • Eliminação das recursividades imediatas à esquerda de A2: Algoritmo 5.1 A2 c X d Y A3 e Y f Y Y b X d Y ε A A1 1 2 3 A 1 A' 2 A' 3 A' A' 1 A' ε A1 A2 b X c X X a X ε A2 c X d Y A3 e Y f Y Y b X d Y ε A3 A1 g A3 h i Novo estado das produções
Para i = 3, j = 1: • Produções de A3: A3 A1 g A3 h i • Produções de A1no momento: A1 A2 b X c X • Substitui-se A1 g por A2 b X g c X g : A3 A2 b X g c X g A3 h i Produção da forma A3 A1
Para i = 3, j = 2: • Produções de A3: A3 A2 b X g c X g A3 h i • Produções de A2no momento: A2 c X d Y A3 e Y fY • Substitui-se A2 b X g por c X d Y b X g A3 e Y b X g f Y b X g : Produção da forma A3 A2
Para i = 3: • Eliminação das recursividades imediatas à esquerda de A3: A3 c X d Y b X g Z f Y b X g Z c X g Z i Z Z e Y b X g Z h Z ε A A1 A2 1 2 3 4 A 1 A' 2 A' 3 A' 4 A' A' 1 A' 2 A' ε Algoritmo 5.1
Novo estado das produções: A1 A2 b X c X X a X ε A2 c X d Y A3 e Y fY Y b X d Y ε A3 c X d Y b X g Z f Y b X g Z c X g Z i Z Z e Y b X g Z h Z ε
A1 A2 b X c X A2 c X d Y A3 e Y fY A3 c X d Y b X g Z f Y b X g Z c X g Z i Z X a X ε Y b X d Y ε Z e Y b X g Z h Z ε • Restaurando os nomes originais dos não-terminais: S T b X c X T c X d Y U e Y fY U c X d Y b X g Z f Y b X g Z c X g Z i Z X a X ε Y b X d Y ε Z e Y b X g Z h Z ε S = A1, T = A2, U = A3 Não há mais recursividade a esquerda
5.2.3 – Fatoração à esquerda • Sejam as seguintes produções do não-terminal A: A onde , , (N )* e o primeiro símbolo de é diferente do primeiro símbolo de • Para um método preditor, não é claro qual produção usar para expandir A • Pode-se atrasar a decisão, aplicando nesta gramática a seguinte transformação: A A’ , A’
A A A’ , A’ • Isso ainda não garante a possibilidade de aplicação de um método preditor • O primeiro símbolo de e pode ser um não-terminal, permanecendo a indecisão • Fatoração é apenas um passo para adequar gramáticas a um método preditor
5.3 – Escolha de uma Derivação • Mesmo numa GLC não-ambígua, a sequência de derivações diretas para produzir uma sentença pode variar • Exemplo: seja a gramática S ε | S ( S ) A sentença ( ) pode ser gerada pelas seguintes derivações: S S(S) (S) () S S(S) S() () • Essas derivações diferem no trecho marcado
S S(S) (S) () S S(S) S() () • Na primeira, escolheu-se o não-terminal Smais à esquerda para ser expandido • Na segunda, foi escolhido o não-terminal Smais à direita • Simbolicamente S(S) me (S) e S(S) md S() • Ou seja, S(S) deriva diretamente mais à esquerda (S) S(S) deriva diretamente mais à direita S()
Derivação mais à esquerda ou mais à direita: quando todas as derivações diretas forem me ou md • Simbolicamente: *meou *md • No exemplo anterior { S *me ( ) } = { S S(S) (S) ( ) } { S *md ( ) } = { S S(S) S( ) ( ) }
Qualquer árvore sintática tem uma única derivação mais à esquerda e uma única derivação mais à direita • Gramática ambígua – outra definição: produz mais de uma derivação mais à esquerda ou mais à direita para pelo menos uma de suas sentenças • Os analisadores sintáticos mais conhecidos analisam derivações mais à esquerda ou mais à direita dos programas
Exemplo: Produção de derivações mais a esquerda da gramática: E T | T opad E T F | F opmult T F id | cte | ( E ) Derivação mais à esquerda para a expressão: ( x + y ) * 10 / ( n – 2 )
ETFopmult T ( E ) opmult T ( Topad E ) opmult T ( Fopad E ) opmult T ( id opadE ) opmult T ( id opadT ) opmult T ( id opadF ) opmult T ( id opad id ) opmultT ( id opad id ) opmultFopmult T ( id opad id ) opmultcteopmultT ( id opad id ) opmultcteopmultF ( id opad id ) opmultcteopmult ( E ) ( id opad id ) opmultcteopmult ( Topad E ) ( id opad id ) opmultcteopmult ( Fopad E ) ( id opad id ) opmultcteopmult ( id opadE ) ( id opad id ) opmultcteopmult ( id opadT ) ( id opad id ) opmultcteopmult ( id opadF ) ( id opad id ) opmultcteopmult ( id opadcte ) ( x + y ) * 10 / ( n – 2 ) Analisadores preditores examinam derivações mais a esquerda
Exemplo: Produção de derivações mais a direita da gramática: E T | E opad T T F | T opmultF F id | cte | ( E ) Derivação mais à direita para a expressão: ( x + y ) * 10 / ( n – 2 )
ET T opmultF T opmult ( E ) T opmult ( E opadT ) T opmult ( E opadF ) T opmult ( Eopadcte ) T opmult ( Topadcte ) T opmult ( Fopadcte ) Topmult ( id opadcte ) T opmultFopmult ( id opadcte ) Topmultcteopmult ( id opadcte ) Fopmultcteopmult ( id opadcte ) ( E ) opmultcteopmult ( id opadcte ) ( E opadT ) opmultcteopmult ( id opadcte ) ( E opadF ) opmultcteopmult ( id opadcte ) ( Eopad id ) opmultcteopmult ( id opadcte ) ( Topad id ) opmultcteopmult ( id opadcte ) ( Fopad id ) opmultcteopmult ( id opadcte ) ( id opad id ) opmultcteopmult ( id opadcte ) ( x + y ) * 10 / ( n – 2 ) Analisadores bottom-up examinam derivações mais a direita reversas
( x + y ) * 10 / ( n – 2 ) ( idopad id ) opmultcteopmult ( id opadcte ) ( Fopad id ) opmultcteopmult ( id opadcte ) ( Topad id ) opmultcteopmult ( id opadcte ) ( Eopadid ) opmultcteopmult ( id opadcte ) ( E opadF ) opmultcteopmult ( id opadcte ) ( E opad T ) opmultcteopmult ( id opadcte ) ( E )opmultcteopmult ( id opadcte ) Fopmultcteopmult ( id opadcte ) T opmultcteopmult ( id opadcte ) T opmult Fopmult ( id opadcte ) T opmult ( idopadcte ) T opmult ( Fopadcte ) T opmult ( Topadcte ) T opmult ( E opadcte ) T opmult ( E opadF ) T opmult ( E opad T ) T opmult( E )T opmult FTE Em negrito, lados direitos a serem reduzidos
5.4 – Análise Top-Down • Tentativa de construir uma árvore sintática para a sentença analisada, começando da raiz, indo em direção às folhas e criando os nós em pré-ordem Exemplo:
5.4 – Análise Top-Down • Tentativa de construir uma árvore sintática para a sentença analisada, começando da raiz, indo em direção às folhas e criando os nós em pré-ordem • Também: tentativa de achar uma derivação mais à esquerda para a sentença ou programa analisado • Análise preditora: os átomos são analisados linearmente da esquerda para a direita, sem a necessidade de observar átomos futuros e de caminhar de volta, da direita para a esquerda, sobre a sequência de átomos
5.4.1 – Análise backtracking • Por vezes, a produção escolhida para produzir uma derivação direta mais à esquerda não leva à árvore sintática da sentença analisada • Então, o analisador é obrigado a voltar para a esquerda na sequência de átomos e a eliminar uma subárvore da árvore em construção, substituindo-a por outra • Exemplo: seja a gramática S a A d a B A b c B ccdddc A seguir, análise da sentença: w = a c c d
1) Início: Árvore contendo somente o símbolo inicial Cursor na raiz da árvore Cursor no primeiro átomo da sentença S S a A d a B A b c B ccdddc w = a c c d
2) 1a produção para expandir S Liga-se S ao átomo da sentença apontado pelo cursor, pois é diante dele que ocorreu a expansão Cursor da árvore avança em pré-ordem S a A d S a A d a B A b c B ccdddc w = a c c d Os cursores se casam: a = a
3) Os dois cursores avançam S a A d S a A d a B A b c B ccdddc w = a c c d
4) 1a produção para expandir A Liga-se A ao átomo da sentença apontado pelo cursor, pois é diante dele que ocorreu a expansão Cursor da árvore avança em pré-ordem S a A d b S a A d a B A b c B ccdddc w = a c c d Os cursores não se casam: b ≠ c