1.18k likes | 1.44k Views
CES-41 COMPILADORES Aulas Práticas - 2014. Capítulo II A Ferramenta Yacc. Yacc é um gerador de analisadores sintáticos:
E N D
CES-41 COMPILADORESAulas Práticas - 2014 Capítulo II A Ferramenta Yacc
Yacc é um gerador de analisadores sintáticos: • Têm como entrada a gramática livre de contexto da linguagem-fonte do compilador e implementa uma função de nome yyparse, que responde se o programa analisado tem ou não erros sintáticos • Yacc (YetAnotherCompiler-Compiler) do sistema UNIX também possui diversas versões para o sistema DOS (o Yacc do Mingw é uma delas) • O analisador gerado é um programa em Linguagem C
Programa 2.1: Saída de dados • Criar na pasta MinGW/bin um arquivo extensão .y (saida.y, por exemplo) com o seguinte programa: %% prod: {printf ("hello friends!\n");} ; %% yylex () { return 0; } • Executar os seguintes comandos: yaccsaida.y gccy.tab.c main.c yyerror.c -o saida -lfl saida Colocar pelo menos uma produção Não pode haver função main Tem de haver função yylex
%% prod: {printf ("hello friends!\n");} ; %% yylex () { return 0; } • Executar: saida > ppp • Abrir o arquivo ppp (No DOS: more ppp) • Abrir o arquivo main.c (só executa yyparse e retorna) • yyparse é o analisador sintático produzido pelo Yacc • Abrir o arquivo y.tab.c e localizar yylex, prod e o printf acima
Estrutura de um programa em Yacc: • Tal como em Lex,um programa em Yacc é dividido em três partes, a saber: Declarações % % Produções da gramática % % Rotinas auxiliares
Produções da gramática: • É a parte principal de um programa em Yacc • O programa deve conter pelo menos uma produção • As produções podem vir acompanhadas de ações escritas na Linguagem C, inseridas em qualquer posição de seu lado direito
Produções da gramática: • Exemplo: Sejam as produções: ExprExprOPAD Termo | Termo Termo ID | CTE • Em Yacc, com ações opcionais: expr : expr OPAD {comandos em C} termo | {comandos em C} termo ; termo : ID {comandos em C} | CTE {comandos em C} ;
Produções da gramática: • Ações podem aparecer no lado direito de uma produção vazia • Exemplo: yyy : {comandos em C} | xxx {comandos em C} zzz ; A primeira produção de yyy é vazia
Declarações: Nelas estão inclusas: • Em C, delimitadas por %{ e %}: • Declarações de variáveis, tipos, protótipos, etc. • Definições de constantes e macros (define’s) • Inclusão de arquivos sem definição de funções • Declarações do Yacc (fora de %{ e %}) usadas para definir terminais, não-terminais, precedência de operadores, tipos dos atributos, etc.
Declarações: • Exemplo: para as produções anteriores expr : expr OPAD {comandos em C} termo | {comandos em C} termo ; termo : ID {comandos em C} | CTE {comandos em C} ; os átomos são assim declarados: %token OPAD %token ID %token CTE
Rotinas auxiliares: • São definições de funções em C, referenciadas nas ações das produções da gramática • Podem trazer a inclusão de arquivos com extensão .c; por exemplo, o arquivo lex.yy.c, do Flex • Não devem conter a função main, pois essa já vem inclusa no ambiente da ferramenta • Devem incluir a função yylex; quando usado com Flex, essa função já vem contida no arquivo lex.yy.c
prod : {printf ("hello friends!\n"); return;} %% prod : {printf ("hello friends!\n");} ; %% yylex () { return 0; } • Fazer yylex retornar algo diferente de zero • Colocar um return dentro da ação depois do printf return 50; Retomando o programa saida.y
Explicando os fatos %% prod : {printf ("hello friends!\n");} ; %% yylex () { return 0; } • O analisador gerado pelo Yacc é bottom-up • Vai detectando lados direitos de produções e reduzindo-os para seus lados esquerdos • Tudo acaba bem quando se consegue chegar ao símbolo inicial
Explicando os fatos %% prod : {printf ("hello friends!\n");} ; %% yylex () { return 0; } • A única produção desta gramática (prod) é vazia • Antes de yyparse pedir um átomo para yylex, ele casa a falta de átomo em suas mãos com seu lado direito • yyparse faz a redução para o lado esquerdo e executa a ação no final da produção • Chegou ao símbolo inicial
Explicando os fatos %% prod : {printf ("hello friends!\n");} ; %% yylex () { return 0; } • Em seguida, chama yylex que lhe retorna zero • Recebendo zero, ele aceita e retorna • main então se encerra
prod : {printf ("hello friends!\n"); return;} Explicando os fatos %% prod : {printf ("hello friends!\n");} ; %% yylex () { return 50; } • Depois de chegar ao símbolo inicial, yyparsesó aceita o átomo zero • Se yylex lhe retornar algo diferente de zero, ele rejeitará • Com o return dentro da ação, yyparse retorna para main antes de chamar yylex
Programa 2.2: Entrada de dados • Criar um arquivo extensão .y (entra.y, por exemplo) com o seguinte programa: %% ppp: {int i, n; printf ("Digite o numero de repeticoes: "); scanf ("%d", &n); for (i = 1; i <= n; i++) printf ("\nhello friends!"); } ; %% yylex () {return 0;}
Executar os seguintes comandos: yacc entra.y gccy.tab.c yyerror.c main.c -o entra -lfl entra • Criar um arquivo entra.dat, colocando nele o número 10 • Executar os comandos: entra < entra.dat entra < entra.dat > ppp • Abrir o arquivo ppp
Esquema de produção de um programa executável usando Yacc, sem auxilio do Flex:
Programa 2.3: Reconhecimento de frase • Rodar yacc e gcc para um arquivo recfrase.y com o seguinte programa: %% prod : 'C' 'O' 'M' 'P' ' ' '1' '5' {printf ("Reconheco!\n"); return;} ; %% yylex () { returngetchar (); } O lado direito das produções tem apenas terminais (tokens) Um caractere entre apóstrofos é considerado um token Executar recfrase com: COMP 14 COMP 15 COMP 16 COMP 153
Programa 2.3: Reconhecimento de frase • Rodar yacc e gcc para um arquivo recfrase.y com o seguinte programa: %% prod : 'C' 'O' 'M' 'P' ' ' '1' '5' {printf ("Reconheco!\n"); return;} ; %% yylex () { returngetchar (); } Executar com arquivo de dados (recfrase.dat, por exemplo): uma frase de cada vez Executar: recfrase < recfrase.dat > ppp Executar recfrase com: COMP 14 COMP 15 COMP 16 COMP 153
Experimente tirar o return; sem e com arquivo de dados Tokens são convertidos em defines pelo Yacc Programa 2.4: Gramática S → ε | a S b %token a %token b %tokendolar %token erro %% SS : S dolar {printf ("Fim da analise\n"); return;} ; S : | a S b ; %% yylex () { char x; x = getchar (); while (x == ' ' || x == '\n' || x == '\t' || x == '\r') x = getchar (); printf ("Caractere lido: %c\n", x); if (x == 'a') return a; if (x == 'b') return b; if (x == '$') returndolar; return erro; } Rodar para várias cadeias, uma por vez: aabb$ $ aaabbb$ a$b$aaabb$ aabbb$ aba$ ba$ Rodar com arquivo de dados contendo: aabb$
Experimente tirar o return; sem e com arquivo de dados Programa 2.4: Gramática S → ε | a S b %token a %token b %tokendolar %token erro %% SS : S dolar {printf ("Fim da analise\n"); return;} ; S : | a S b ; %% yylex () { char x; x = getchar (); while (x == ' ' || x == '\n' || x == '\t' || x == '\r') x = getchar (); printf ("Caractere lido: %c\n", x); if (x == 'a') return a; if (x == 'b') return b; if (x == '$') returndolar; return erro; } Chegando ao símbolo inicial, yyparse sempre chama yylex, esperando zero yylexsó retorna quando um caractere é digitado e nunca retorna zero Com fim de arquivo, yylex retorna erro
Exercício 2.1: Escrever em Yacc, um analisador sintático para a seguinte gramática geradora da linguagem L abaixo: L = { (a b)ncn (d d)* | n 1} Gramática: S a b A c D A a b A c | ε D d dD | ε
Exercício 2.2: Escrever em Yacc, um analisador sintático para a seguinte gramática geradora da linguagem L abaixo: L = { aibj | j i 0 } Gramática: S A B A a A b | ε B b B | ε
Exercício 2.3: Escrever em Yacc, um analisador sintático para a seguinte gramática geradora de expressões contendo só pares de parêntesis balanceados e nenhum outro caractere Exemplos: ( ), ( ( ) ), ( ) ( ), ( ) ( ( ) ( ( ) ) ) ( ( ) ) Gramática: S ( A ) | S ( A ) A ε | S
Programa 2.5: Yacc auxiliado por Lex • Programa em Flex no arquivo expr01.l delim [ \t\n\r] ws {delim}+ digit [0-9] num {digit}+ %% {ws} { ;} {num} {return CTE;} "+" {return OPAD;} "-" {return OPAD;} "*" {return OPMULT;} "/" {return OPMULT;} "(" {return ABPAR;} ")" {return FPAR;} "$" {return DOLAR;} . {return INVAL;} %% yylex retorna tokens declarados no programa em Yacc
Programa em Yacc no arquivo expr01.y %{ #include <stdio.h> #include <stdlib.h> %} %token DOLAR %token CTE %token OPAD %token OPMULT %token ABPAR %token FPAR %token INVAL %% Tokens a serem retornados por yylex
Tokens aparecem do lado direito das produções line : expr DOLAR {printf("Fim da analise\n"); return;} ; expr : expr OPAD term | term ; term : term OPMULT fat | fat ; fat : CTE | ABPAR expr FPAR ; %% #include "lex.yy.c" yylex está em lex.yy.c
Executar os seguintes comandos: flex expr01.l yacc expr01.y gccy.tab.cmain.cyyerror.c -o expr01 -lfl expr01 (digitar uma expressão correta terminada por ‘$’) expr01 (digitar uma expressão incorreta terminada por ‘$’) • Usando arquivo de dados de entrada, yylex retorna zero ao ler fim de arquivo, quando criado pelo Flex • Então, dispensa-se o return da produção line
Esquema de produção de um programa executável usando Yacc, com auxilio do Flex:
Programa 2.6: Uso de atributos (calculadora simples) • Programa em Flex no arquivo calc01.l delim [ \t\n\r] ws {delim}+ digit [0-9] num {digit}+ %% {ws} { ;} {num} {yylval = atoi(yytext); return CTE;} "+" {yylval = MAIS; return OPAD;} "-" {yylval = MENOS; return OPAD;} "*" {yylval = VEZES; return OPMULT;} "/" {yylval = DIV; return OPMULT;} "(" {return ABPAR;} ")" {return FPAR;} "$" {return DOLAR;} . {yylval = yytext[0]; return INVAL;} %% yylval guarda o atributo de um token yylval é declarado pelo Yacc
Programa em Yacc no arquivo calc01.y %{ #include <stdio.h> #include <stdlib.h> #define MAIS 1 #define MENOS 2 #define VEZES 3 #define DIV 4 %} %token DOLAR %token CTE %token OPAD %token OPMULT %token ABPAR %token FPAR %token INVAL %% Atributos para os tokens OPAD e OPMULT Por default,yylval é inteiro
Não-terminais também têm atributos line : expr DOLAR { printf("valor: %d\n", $1); } ; expr : expr OPAD term { switch ($2) { case MAIS : $$ = $1 + $3; break; case MENOS : $$ = $1 - $3; break; } } | term ; Numa produção qualquer: $$ é o atributo do não-terminal do lado esquerdo $1, $2, $3 ... são atributos dos 1o, 2o, 3o ... símbolos do lado direito
Rodar o executável para um arquivo com a expressão 10 * (5 + 3)$ term : term OPMULT fat { switch ($2) { case VEZES: $$ = $1 * $3; break; case DIV: $$ = $1 / $3; break; } } | fat ; fat : CTE | ABPAR expr FPAR {$$ = $2;} ; %% #include "lex.yy.c" O atributo de um terminal é fornecido por yylex, em yylval Os atributos dos não-terminais devem ser calculados nas ações Por default,a ação tomada no final de uma produção é: $$ = $1;
No Yacc, a análise é bottom-up (por deslocamento e redução) • Os átomos são obtidos e deslocados para uma pilha (deslocamento) • Quando no topo da pilha se formar o lado-direito de uma produção, tem-se uma ocasião para reduzir • O analisador verifica se a redução é válida • Isso será estudado no tópico sobre Análise Bottom-Up do capítulo sobre Análise Sintática • Em caso positivo, substitui, na pilha, o lado-direito pelo lado-esquerdo da produção (redução) • Então, a ação no final da produção é executada
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR Por que não reduz segundo E → T ? A resposta virá no estudo de Análise Bottom-Up
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR
Análise e cálculo da expressão 10 * (5 + 3)$ line : expr DOLAR expr : expr OPAD term | term term : term OPMULT fat | fat fat : CTE | ABPAR expr FPAR