Baixe o app para aproveitar ainda mais
Prévia do material em texto
Compiladores Prof.ª Kecia Aline Marques Ferreira CEFET-MG Análise Sintática 1 Análise Sintática – Parte 5 • Análise Sintática Ascendente – Usando gramáticas ambíguas • Geradores de Analisadores Sintáticos 2 Gramáticas Ambíguas • Nenhuma gramática ambígua é LR(k) • A tabela de parser LR(k) sempre possuirá conflitos • Entretanto, gramáticas ambíguas podem ser úteis se houver uma maneira de contornar o conflito • Dois casos: else pendente, gramática de expressões 3 Gramáticas Ambíguas • Caso do else pendente: S → if E then S else S | if E then S | other Para a entrada if a then if b then s1 else s2 há duas interpretações: 1) if a then {if b then s1 else s2} 2) if a then {if b then s1} else s2 4 S → i S e S | i S | a resumidamente Gramáticas Ambíguas • A tabela de parser LR para a gramática contém conflitos 0. S’ → S$ 1. S → i S e S 2. | i S 3. | a 5 S’ → S • $ S’ → • S$ S → • i S e S S → • i S S → • a e ... $ ... 4 s5/r2 r2 ... 0 1 2 S → i • S e S S → i • S S → • i S e S S → • i S S → • a S → a • 3 S → i S • e S S → i S • S → i S e • S S → • i S e S S → • i S S → • a S → i S e S • 6 4 5 S a i S e S i a FOLLOW(S) = {$, e} i a Opta-se pelo shift Gramáticas Ambíguas • Nesta situação, em vez de reescrever a gramática, pode-se tolerar o conflito, optando-se por realizar o shift em vez de realizar a redução • Mas isso deve ser utilizado com cuidado: situações de conflito são sintomas de gramática mal formulada e devem ser contornados eliminando-se a ambiguidade da gramática. 6 S → if E then S • else S S →if E then S • Gramáticas Ambíguas • Caso da gramática de expressões aritméticas: 7 Gramática ambígua E → E + E | E * E | (E) | id Gramática não ambígua E → E + T | T T → T * F | F F → (E) | id Gramáticas Ambíguas • A tabela de parser LR para a gramática contém conflitos 0. E’ → E$ 1. E → E + E 2. | E * E 3. | (E) 4. | id 8 E’ → E • $ E → E • + E E → E • * E E’ → • E$ E → • E + E E → • E * E E → • (E) E → • id E → E + • E E → • E + E E → • E * E E → • (E) E → • id E + x + ) $ E ... 7 s/r1 s/r1 r1 r1 8 s/r2 s/r2 r2 r2 ... 0 1 E → E * • E E → • E + E E → • E * E E → • (E) E → • id * 4 5 E → E + E • E → E • + E E → E • * E 7 E → E * E • E → E • + E E → E • * E 8 E E Gramáticas Ambíguas • A tabela completa de parser LR para a gramática. 9 id + * ( ) $ E 0 s3 s2 g1 1 s4 s5 acc 2 s3 s2 g6 3 r4 r4 r4 r4 4 s3 s2 g7 5 s3 s2 g8 6 s4 s5 s9 7 r1/s4 r1/s5 r1 r1 8 r2/s4 r2/s5 r2 r2 9 r3 r3 r3 r3 Gramáticas Ambíguas • A ambiguidade neste caso pode ser resolvida utilizando-se a informação de precedência e associatividade de operadores • Considere a entrada id + id * id • Após processar id + id, o parser estará no estado 7 • Neste estado: – Como * tem precedência sobre + , o parser deverá transferir (fazer um shift) de * para a pilha e, na sequência, reduzirá o * e os dois símbolos id ao seu redor para uma expressão 10 Gramáticas Ambíguas • Considere a entrada id + id + id • Após processar id + id, o parser estará no estado 7 • Neste caso : – Como o operador + é associativo à esquerda, os símbolos id ao redor do primeiro + precisam ser agrupados primeiro – A ação correta, então, é a redução • Resumindo: no estado 7, – Deverá ocorrer shift, se o próximo símbolo for * – Deverá ocorrer redução se o próximo símbolo for + 11 Gramáticas Ambíguas • Seguindo o mesmo raciocínio, no estado 8: – No topo da pilha há E * E – Deverá haver redução quando houver + (pois * tem precedência sob +) ou * (pois * é associativo à esquerda) na entrada 12 Gramáticas Ambíguas – Tabela de parser para a gramática após resolução de conflitos aplicando-se convenientemente as regras de associatividade e precedência de operadores 13 id + * ( ) $ E 0 s3 s2 g1 1 s4 s5 acc 2 s3 s2 g6 3 r4 r4 r4 r4 4 s3 s2 g7 5 s3 s2 g8 6 s4 s5 s9 7 r1 s5 r1 r1 8 r2 r2 r2 r2 9 r3 r3 r3 r3 Geradores de Analisadores Sintáticos • Há um grande número de geradores de analisadores sintáticos • São exemplos: – Yacc (yet another compiler-compiler): um gerador de parser LALR para C • O uso do Yacc está descrito na seção 4.9 do livro 14 Geradores de Analisadores Sintáticos – CUP: gerador de parser LALR para Java – JavaCC: gerador de parser recursivo descendente para Java – Um catálogo de geradores de compiladores pode ser encontrado em http://catalog.compilertools.net/ 15 CUP • CUP (Java Based Constructor of Useful Parsers ) http://www2.cs.tum.edu/projects/cup/ – CUP recebe como entrada um arquivo Parser.cup que contém a especificação da gramática para a qual será gerado o parser – A saída de CUP são dois arquivos: • sym.java: contém declarações de constantes para os símbolos terminais (tokens). • parser.java: contém a implementação do parser. 16 CUP • Estrutura do arquivo de especificação da gramática: – O arquivo de entrada Parser.cup possui as seções: 1. Especificação de pacotes e importações 2. Código de componentes do usuário 3. Lista de símbolos terminais e não terminais 4. Declarações de precedência 5. Especificação da gramática 17 CUP 1. Especificação de pacotes e importações – É uma seção opcional – Contém o nome do pacote para o qual deseja-se que as classes sejam geradas package name; – Após a definição do pacote, pode-se incluir os import necessários: import package_name.class_name; import package_name.*; 18 CUP 2. Código de componentes do usuário – É uma seção opcional – Contém uma série de declarações que permitem que código do usuário seja incluído como parte do parser gerado – Uma classe não pública denominada CUP$Parser$actions é gerada contendo todo o código do usuário 19 CUP – action code {: código :}; • Rotinas e variáveis para uso do código inserido na gramática podem ser incluídas nesta seção . Um exemplo típico disso são as rotinas de manipulação da tabela de símbolos. 20 CUP – parse code {: código :}; • Permite que métodos e/ou variáveis sejam incluídas diretamente na classe de parser gerada. • Isso não é muito usual, mas é possível, por exemplo, incluir métodos de análise léxica dentro do parser e sobrecarregar as rotinas padrão de recuperação de erro. 21 CUP – init with{: código:}; • Esta declaração fornece o código que será executado pelo parser antes da solicitação do primeiro token. • Tipicamente, esta declaração é utilizada para inicializar o analisador léxico, tabela de símbolos e outras estruturas de dados que sejam necessárias pelas ações semânticas. 22 CUP – scan with{: código :}; • Esta declaração indica como o parser deve solicitar pelo próximo token. • O código informado será o corpo de um método no parser que retorna um objeto do tipo java_cup.runtime.Symbol. • Desta forma, o código informado deverá retornar um valor deste tipo. 23 CUP 3. Lista de símbolos – É uma seção obrigatória – Nesta seção são declarados cada terminal e não terminal que aparecem na gramática. – Para cada não terminal e terminal devem ser indicados seus respectivos tipos 24 CUP terminal classname name1, name2, ...; non terminal classname name3, name4, ...; terminal name1, name2, ...; non terminal name3, name4, ...; – Nome de terminais e não terminais não podem ser iguais às palavras reservadas de CUP: "code", "action", "parser", "terminal", "non", "nonterminal", "init", "scan", "with", "start", "precedence", "left", "right", "nonassoc", "import", and "package". 25 CUP 4. Precedência e associatividade – É uma seção opcional – Especifica a precedência e associatividade de terminais – Isso é útil para a implementação de parser para gramática ambígua, como o caso de expressões aritméticas. – Há três tipos de declarações de precedência e associatividade: precedence left terminal[, terminal...]; precedence right terminal[, terminal...]; precedence nonassoc terminal[, terminal...]; 26 CUP – Por exemplo: precedence left ADD, SUBTRACT; precedence left TIMES, DIVIDE; Indica que multiplicação e divisão têm maior precedência do que soma e subtração e todos são associativos à esquerda 27 CUP 5. A gramática – Esta seção começa opcionalmente com start with non-terminal; • Isso indica o símbolo inicial da gramática • Se essa declaração não for realizada, o símbolo à esquerda da primeira produção é considerado o símbolo inicial – Cada produção da gramática é da forma: Não terminal seguido por “::=” seguido por sentenças separadas por “|” 28 CUP • Para executar CUP: java -jar java-cup-11a.jar parser.cup • Uso integrado de Jflex e CUP: – %cup: é a diretiva do JFlex que habilita a sua compatibilidade com CUP – Isso faz o Jflex gerar o analisador léxico com classes e métodos nomeados no padrão do CUP – O analisador léxico gerado pelo JFlex implementa a interface java cup.runtime.Scanner, usada no CUP 29 CUP • Exemplo: um parser para expressões aritméticas LEXP → LEXP EXP | EXP EXP → E; E → number | E + E | E * E | (E) 30 CUP terminal SEMI, PLUS, TIMES, LPAREN, RPAREN; terminal Integer NUMBER; non terminal expr_list, expr_part; non terminal Integer expr; precedence left PLUS; precedence left TIMES; 31 CUP expr_list ::= expr_list expr_part | expr_part; expr_part ::= expr SEMI; expr ::= NUMBER | expr PLUS expr | expr TIMES expr | LPAREN expr RPAREN ; 32 CUP • Exemplos de entrada e saída do parser gerado: Entrada: 4 + 4; Saída: ok Entrada:4; Saída: ok Entrada:4+2*3; Saída: ok Entrada: 7 - (4+2)*3; Saída: Erro – caractere não reconhecido (-) Entrada:7 + (4+2)*3; Saída: =ok Entrada: 7 + (4+2()*3; Saída: Erro de sintaxe 33 CUP No arquivo de definição do parser são incluídas as ações semânticas expr_list ::= expr_list expr_part | expr_part; expr_part ::= expr:e {: System.out.println(" = "+e+";"); :} SEMI; expr ::= NUMBER:n {: RESULT=n; :} | expr:l PLUS expr:r {: RESULT=new Integer(l.intValue() + r.intValue()); :} | expr:l TIMES expr:r {: RESULT=new Integer(l.intValue() * r.intValue()); :} | LPAREN expr:e RPAREN {: RESULT=e; :} ; 34 CUP • Exemplos de entrada e saída do parser gerado com as ações semânticas implementadas: Entrada: 4 + 4; Saída: =8; Entrada:4; Saída: =4; Entrada:4+2*3; Saída: =10; Entrada: 7 - (4+2)*3; Saída: Erro – token não reconhecido (-) Entrada:7 + (4+2)*3; Saída: =25; Entrada: 7 - (4+2()*3; Saída: Erro 35 Referência Bibliográfica Compiladores – Princípios, técnicas e ferramentas. Aho, A. V et al. 2ª edição Seção 4.8 Modern Compiler Implementation in Java. Second Edition. Andrew W. Appel. Capítulo 3. CUP User Manual http://www2.cs.tum.edu/projects/cup/ 36
Compartilhar