5.6 – Complementos de Yacc – Usando Yacc com gramáticas ambíguas

Slides:



Advertisements
Apresentações semelhantes
Funções em C Prof. Fabiano Utiyama.
Advertisements

Software Básico Silvio Fernandes
Tópicos em Compiladores
Capítulo II – Algoritmos e Programas
5.5 – Análise Bottom-Up Tentativa de construir uma árvore sintática para a sentença analisada, começando das folhas, indo em direção à raiz (pós-ordem.
Compiladores Claudio Benossi
III – Análise sintáctica Geradores de parsers Bibliografia aconselhada: –Aho, Sethi e Ullman – secção 4.9 LFA 1999/ Jorge Morais.
Compiladores I Cristiano Damiani Vasconcellos
Recursividade Prof. Rosana Palazon.
Deyvisson, Rafael M.P., Renato, Robson C.F., Rodolfo
Análise Sintática Ascendente ­
Introdução à Computação - Jorge Macêdo
YACC.
Lex e Yacc.
Relações em uma Gramática
Análise Léxica Supondo o trecho de programa abaixo:
Construção de Compiladores
Universidade Federal da Paraíba Departamento de Informática Construção de Compiladores Verificação de Tipos.
Construção de Compiladores
Construção de Compiladores
Construção de Compiladores
LINGUAGEM C Centro Federal de Educação Tecnológica da Paraíba Coordenação de Informática Professor: Lafayette B. Melo.
Vetores, Matrizes e Funções
Slides: Prof. João Fabro UTFPR - Curitiba
Estruturas de Decisão Permitir testes para decidir acções alternativas: if if - else switch (?:) Operador Condicional MF.
O Portal do Estudante de Computação
Introdução a Computação e Cálculo Numérico
Lex Linguagem (e compilador) para especificar analisadores léxicos.
Linguagem de Programação I Parte II
Ferramentas para a Construção de Compiladores: Lex & Yacc
UNIDADE 6 - complemento Funções recursivas
Seminário 1: Revisão de C
Algoritmos e Programação de Computadores
Compiladores, Aula Nº 10 João M. P. Cardoso
Sintaxe e Semântica Prof.: Gláucya Carreiro Boechat
Mayerber Carvalho Neto
Aula 6 - Estruturas de Controle
Algoritmo e Programação
Capítulo II Gramáticas e Linguagens
Analise sintática aula-07-analise-sintática.pdf.
Estruturas de Dados Aula 2: Estruturas Estáticas 07/04/2014.
Esquemas L-atribuídos
Educação Profissional Técnica de Nível Médio Curso Técnico de Informática
Análise Sintática LR Prof. Alexandre Monteiro
Análises léxica e sintática
Projeto de Tradutor Preditivo. Introdução Introdução Esquemas L-atribuidos são reconhecidos por analisadores Top-Down. Entre estes analisadores, se encontra.
Aula 12 1 Análise Sintáctica Compiladores, Aula Nº 12 João M. P. Cardoso.
Análise Sintática Prof. Alexandre Monteiro
Tradução Dirigida por Sintaxe
CES-41 COMPILADORES Aulas Práticas Capítulo II A Ferramenta Yacc.
Aula 18 1 Análise Sintáctica Compiladores, Aula Nº 18 João M. P. Cardoso.
Aula prática 3 Aprofundando em Funções Parâmetros de uma função Uso do return Execução Variáveis Global, local e estática Monitoria de Introdução à.
CES-10 INTRODUÇÃO À COMPUTAÇÃO Aulas Práticas – 2013
Capítulo VI – Variáveis Indexadas 6.1 – A necessidade de variáveis indexadas 6.2 – Vetores e matrizes 6.3 – Aplicações com vetores numéricos 6.4 – Aplicações.
PCI- Estrutura Condicional Profa. Mercedes Gonzales Márquez.
Comando de Seleção switch
CES-41 COMPILADORES Aulas Práticas
Faculdade Pernambucana - FAPE Setembro/2007
COMPILADORES 04 Prof. Marcos.
Um Tradutor Dirigido por Sintaxe Simples
Algoritmos e Programação MC102 Prof. Paulo Miranda IC-UNICAMP Aula 15 Ponteiros.
Programação Computacional Aula 8: Entrada e Saída pelo Console Prof a. Madeleine Medrano
Sintaxe de uma Linguagem
Profa. Maria Augusta Constante Puget
Profa. Maria Augusta Constante Puget
Analisador sintático: Tipos de análises sintáticas
Compilador Software que traduz o texto (linguagem fonte) que representa um programa para código máquina(linguagem alvo) capaz de ser executado pelo.
Faculdade Pernambuca - FAPE Compiladores Abril/2007 Compiladores Abril/2007.
Fundamentos de Programação 1 Slides 19 Prof. SIMÃO Jean Marcelo SIMÃO Linguagem C “Arquivos Binários”.
Transcrição da apresentação:

5.6 – Complementos de Yacc 5.6.1 – Usando Yacc com gramáticas ambíguas Gramáticas ambíguas causam conflitos durante a construção de um analisador sintático pelo Yacc Os tipos de conflitos são: Deslocamento-redução: o analisador tanto pode deslocar um átomo para a pilha, como pode reduzir o topo da pilha por uma produção Redução-redução: o analisador pode usar mais de uma produção para reduzir o topo da pilha

Há regras para a solução de ambiguidades Yacc gera analisadores mais eficientes para gramáticas ambíguas do que para gramáticas não ambíguas

Exemplo 5.34: Programa para análise da seguinte gramática ambígua para cálculo de expressões: E  E + E | E - E | E * E | E / E | ( E ) | - E | num Com a seguinte precedência de operadores: ‘*’ e ‘/’ tem precedência sobre ‘+’ e ‘-’ Menos-unário tem precedência sobre ‘*’ e ‘/’ O programa deve calcular expressões de valor real

E  E + E | E - E | E * E | E / E | ( E ) | - E | num A gramática é ambígua pois para a sentença 5 + 8 * 3 tem duas árvores sintáticas E + 5 * 8 3 E * + 3 5 8

#include <stdio.h> #include <ctype.h> Programa com regras para eliminação de ambiguidades e comandos de escrita para mostrar as reduções feitas: %{ #include <stdio.h> #include <ctype.h> #define YYSTYPE double %} %token NUM %left '+' '-' %left '*' '/' %right MENUN %% Declarações para solução de ambiguidades

line : expr '$' {printf ( "Valor: %g\n", $1); return 0;} ; expr : expr '+' expr {$$ = $1 + $3; printf ("Regra1: %g = %g + %g\n", $$, $1, $3);} | expr '-' expr {$$ = $1 - $3; printf ("Regra2: %g = %g - %g\n", $$, $1, $3);} | expr '*' expr {$$ = $1 * $3; printf ("Regra3: %g = %g * %g\n", $$, $1, $3);}

printf ("Regra4: %g = %g / %g\n", $$, $1, $3);} | '(' expr ')' | expr '/' expr {$$ = $1 / $3; printf ("Regra4: %g = %g / %g\n", $$, $1, $3);} | '(' expr ')' {$$ = $2; printf ("Regra5: %g = ( %g )\n", $$, $2);} | '-' expr %prec MENUN {$$ = -$2; printf ("Regra6: %g = - %g \n", $$, $2);} | NUM {$$ = $1; printf ("Regra7: %g = %g\n", $$, $1);} ; %% Dispositivo para solução de ambiguidades

yylex () { int c; do c = getchar (); while (c == ' '); if (c == '.' || isdigit (c)) { ungetc (c, stdin); scanf ("%lf", &yylval) ; return (NUM); } return c;

A seguir, são descritas regras e dispositivos de programação do Yacc para solucionar ambiguidades Por default, o conflito redução-redução é resolvido em favor da produção que aparece primeiro na lista de produções Por default, o conflito deslocamento-redução é resolvido em favor do deslocamento

No exemplo anterior, não fosse pelas regras de precedência a serem comentadas logo a seguir, a forma sentencial: provocaria o deslocamento do ‘+’ para o topo da pilha A soma seria realizada antes da multiplicação Pilha Entrada expr ‘*’ expr ‘+’ 3

provocaria o deslocamento do ‘+’ para o topo da pilha Nas declarações do programa Yacc, pode-se atribuir precedências e associatividades aos terminais da gramática; sejam as declarações: %left '+' '-' %left '*' '/' %right '~' (menos unário) '+' e '-' têm mesma precedência '*' e '/' têm mesma precedência A precedência cresce de cima para baixo No programa aparece: %right MENUN provocaria o deslocamento do ‘+’ para o topo da pilha A soma seria realizada antes da multiplicação Diferentes

Cada produção em Yacc também tem associada a si uma precedência e uma associatividade: Por default, é a precedência e associatividade de seu terminal mais a direita Exemplo: a precedência e associatividade da produção A  a B c D e F G será a de ‘e’ Na decisão (deslocar ‘a’) ou (reduzir por A) haverá deslocamento se ‘a’ tiver maior precedência que A, e vice-versa

Na forma sentencial: haverá redução, pois a produção expr ‘*’ expr tem a precedência de ‘*’ que é maior que a de ‘+’ Pilha Entrada expr ‘*’ expr ‘+’ 3

Na declaração %left ‘-’, o operador ‘-’ é associativo à esquerda; a seguinte forma sentencial causa redução: A expressão 5-3-1 = 2-1 = 1 Se fosse %right ‘-’, causaria deslocamento O cálculo de 5-3-1 seria 5-3-1 = 5-2 = 3 Pilha Entrada expr ‘-’ expr ‘-’ 3

Pode-se também forçar uma produção a ter uma determinada precedência O programa visto apresenta: %left '+' '-' %left '*' '/' %right MENUN - - - - - - - - expr | '-' expr %prec MENUN Esta produção é forçada a ter precedência maior que ‘+’, ‘-’, ‘*’, ‘/’

Exemplo: o programa visto produziu os seguintes resultados experimentais: 1) Entrada: 5-2-2$ Regra7: 5 = 5 Regra7: 2 = 2 Regra2: 3 = 5 - 2 Regra2: 1 = 3 - 2 Valor: 1 2) Trocando %left '+' '-' por %right '+' '-':   Entrada: 5-2-2$ Regra7: 5 = 5 Regra7: 2 = 2 Regra2: 0 = 2 - 2 Regra2: 5 = 5 - 0 Valor: 5

Exemplo: o programa visto produziu os seguintes resultados experimentais: 4) Entrada: -3+8$ Regra7: 3 = 3 Regra6: -3 = - 3 Regra7: 8 = 8 Regra1: 5 = -3 + 8 Valor: 5 3) Destrocando: Entrada: 5*-(3+2)$ Regra7: 5 = 5 Regra7: 3 = 3 Regra7: 2 = 2 Regra1: 5 = 3 + 2 Regra5: 5 = ( 5 ) Regra6: -5 = - 5 Regra3: -25 = 5 * -5 Valor: -25 5) Trocando %right MENUN %left '+' '-' %left '*' '/' Entrada: -3+8$ Regra7: 3 = 3 Regra7: 8 = 8 Regra1: 11 = 3 + 8 Regra6: -11 = - 11 Valor: -11

5.6.2 – Notificação e tratamento de erros no Yacc Em Yacc, o tratamento de erros pode ser feito usando produções de erros entre as produções da gramática Primeiramente deve-se decidir quais não-terminais da gramática terão essas produções Esses podem ser não-terminais que possuam átomos em suas produções normais, para que mensagens notificando a falta deles possam ser emitidas Ou então outros estratégicos geradores de expressões, comandos, sub-programas, etc.

Seja A um não-terminal escolhido; introduz-se, na gramática, produções da forma A   error   e  são cadeias de terminais e ou não-terminais, vazias ou não error é uma palavra reservada do Yacc (um token especial) Na montagem do analisador, as produções de erros são tratadas como produções normais da gramática No entanto, quando o analisador produzido encontra um erro, a manipulação dos estados e da pilha é diferente do normal

Programa analisador LR Encontrando um erro, desempilha-se símbolos e estados, até encontrar um estado no topo da pilha, cujo conjunto de itens contenha um item do tipo A  . error  erro Entrada Programa analisador LR Estado com item do tipo A  . error  Saída  Pilha Tabelas LR

Programa analisador LR Encontrando um erro, desempilha-se símbolos e estados, até encontrar um estado no topo da pilha, cujo conjunto de itens contenha um item do tipo A  . error  erro Entrada Programa analisador LR Estado com item do tipo A  . error  Saída  Pilha Tabelas LR

Programa analisador LR Desloca-se o átomo fictício error para o topo da pilha, como se tivesse visto um erro na entrada Estado com item do tipo A   error .  erro Entrada error Programa analisador LR Estado com item do tipo A  . error  Saída  Pilha Tabelas LR

Programa analisador LR Se  for vazia, reduz-se imediatamente para A e uma eventual ação semântica pode ocorrer (notificação e tratamento de erros programado) Estado com item do tipo A   error .  erro Entrada error Programa analisador LR Estado com item do tipo A  . error  Saída  Pilha Tabelas LR

Programa analisador LR Se  for vazia, reduz-se imediatamente para A e uma eventual ação semântica pode ocorrer (notificação e tratamento de erros programado) erro Entrada Programa analisador LR A Saída Pilha Tabelas LR

Programa analisador LR Se  não for vazia: erro Entrada Programa analisador LR A Saída Pilha Tabelas LR

Programa analisador LR Se  não for vazia: o analisador observa os próximos símbolos de entrada, até encontrar uma sub-cadeia redutível para  redutível para  Estado com item do tipo A   error .  erro Entrada error Programa analisador LR Estado com item do tipo A  . error  Saída  Pilha Tabelas LR

Programa analisador LR Essa sub-cadeia é deslocada para a pilha provocando a redução para A redutível para   Estado com item do tipo A   error .  erro Entrada error Programa analisador LR Estado com item do tipo A  . error  Saída  Pilha Tabelas LR

Programa analisador LR Essa sub-cadeia é deslocada para a pilha provocando a redução para A redutível para  O caso mais comum é o de  ser uma sequência de terminais erro Entrada Programa analisador LR A Saída Pilha Tabelas LR

Programa analisador LR Também uma eventual ação semântica pode ocorrer (notificação e tratamento de erros programado) redutível para  O caso mais comum é o de  ser uma sequência de terminais erro Entrada Programa analisador LR A Saída Pilha Tabelas LR

O analisador deixa o estado de erro e volta ao normal, através da execução de uma macro: yyerrok O exemplo a seguir é experimental e relativamente simples: Mostra os casos de  e  serem vazios e compostos de um e dois terminais Mostra também o uso da macro yyerrok A literatura (livros, Internet, etc.) apresenta outros dispositivos para recuperação de erros no Yacc

Função : Cabeçalho Declarações Exemplo: programa para a seguinte gramática: Função : Cabeçalho Declarações Cabeçalho : ID ( ) Declarações : { ListDecl } ListDecl : ε | ListDecl Declaração Declaração : Tipo ListElemDecl ; Tipo : INT | FLOAT | CHAR | BOOL ListElemDecl : ElemDecl | ListElemDecl , ElemDecl ElemDecl : ID

Analisador léxico: delim [ \t\n\r] ws {delim}+ letra [A-Za-z] digito [0-9] id {letra}({letra}|{digito})* %% {ws} { ;} bool {return BOOL;} char {return CHAR;} float {return FLOAT;} int {return INT;} {id} {strcpy (yylval.cadeia, yytext); return ID;} "(" {return ABPAR;} ")" {return FPAR;} "{" {return ABCHAV;} "}" {return FCHAV;} ";" {return PVIRG;} "," {return VIRG;} . {yylval.carac = yytext[0]; return INVAL;}

Analisador sintático: %{ #include <string.h> #include <stdio.h> #include <stdlib.h> void Esperado (char *); %} %union { char cadeia[50]; char carac; }

%token BOOL %token CHAR %token FLOAT %token INT %token <cadeia> ID %token ABPAR %token FPAR %token ABCHAV %token FCHAV %token PVIRG %token VIRG %token <carac> INVAL %%

Funcao : Cabecalho Declaracoes ; Cabecalho : ID ABPAR {printf ("%s \( ", $1);} FPAR {printf (")\n");} | ID error {printf ("%s ", $1); Esperado ("(");} FPAR {yyerrok; printf (")\n");} | ID error {printf ("%s\n", $1); Esperado ("( )"); yyerrok;} | error {Esperado ("Identificador");} ABPAR FPAR {yyerrok; printf ("() ");} | ID ABPAR error {printf ("%s (\n", $1); Esperado (")"); yyerrok;}

Declaracoes : ABCHAV {printf ("\{\n");} ListDecl FCHAV {printf ("}\n");} ; ListDecl : | ListDecl Declaracao Declaracao : Tipo ListElemDecl PVIRG {printf ("; \n");} | Tipo ListElemDecl error PVIRG {Esperado("';' "); yyerrok;}

Tipo : INT {printf ("int ");} | FLOAT {printf ("float ");} | CHAR {printf ("char ");} | BOOL {printf ("bool ");} ; ListElemDecl : ElemDecl | ListElemDecl VIRG {printf (", ");} ElemDecl ElemDecl : ID {printf ("%s ", $1);} | error ID {Esperado("Identificador"); yyerrok;}

%% #include "lex.yy.c" void Esperado (char *s) { printf ("\n***** Esperado: %s \n", s); } A seguir alguns testes e seus resultados

1) Programa para teste: (sem erros) main ( ) { int i, j, k, fat; bool m, n; float x; char a; } Resultado: main ( ) { int i , j , k , fat ; bool m , n ; float x ; char a ; }

2) Programa para teste: ( ) { int i, j, k, fat; bool m, n; float x; char a; } Resultado: syntax error ***** Esperado: Identificador () { int i , j , k , fat ; bool m , n ; float x ; char a ; }

3) Programa para teste: abc ) { int i, j, k, fat; bool m, n; float x; char a; } Resultado: syntax error abc ***** Esperado: ( ) { int i , j , k , fat ; bool m , n ; float x ; char a ; }

4) Programa para teste: abc ( { int i, j, k, fat; bool m, n; float x; char a; } Resultado: syntax error abc ( ***** Esperado: ) { int i , j , k , fat ; bool m , n ; float x ; char a ; }

5) Programa para teste: abc { int i, j, k, fat; bool m, n; float x; char a; } Resultado: syntax error abc ***** Esperado: ( ) { int i , j , k , fat ; bool m , n ; float x ; char a ; }

6) Programa para teste: abc ( ) { int bool i, j, k, fat; bool m, n float x; char a; } Resultado: abc ( ) { int syntax error ***** Esperado: Identificador , j , k , fat ; bool m , n syntax error ***** Esperado: ';' char a ; }