Compiladores
Ferramentas Flex/Bison
Prof. Sergio F. Ribeiro
Lex e Yacc / Flex e Bison
São ferramentas de auxílio na escrita de programas
que promovem transformações sobre entradas estruturadas.
São ferramentas desenvolvidas para programadores
de compiladores e interpretadores.
Permitem um rápido desenvolvimento de
protótipos e uma manutenção simples do software.
Tanto o Lex como o Yacc foram desenvolvidos nos
Compiladores
Lex e Yacc / Flex e Bison
Papel do Lex: toma um conjunto de expressões
regulares e produz uma rotina C que irá executar a análise léxica identificando os tokens.
Papel do Yacc: toma uma descrição concisa de uma
gramática e produz uma rotina C que irá executar a análise sintática ou parsing.
3
Gerador de Analisador Sintático
É uma ferramenta similar e complementar ao
gerador de analisador léxico.
Ferramentas mais comuns: yacc (yet another
compiler-compiler) ou bison.
Lex gera a função yylex() que retorna o
identificador de um item léxico reconhecido.
Yacc gera a função yyparse() que analisa os itens
léxicos e decide se eles formam ou não uma sentença válida.
Compiladores
Gerador de Analisador Sintático
Criando um tradutor de entrada/saída com Yacc:
Compilador Yacc Compilador C a.out Especificação Yacc translate.y y.tab.c entrada y.tab.c a.out saída 5 Compiladores
Formato Geral do Programa Yacc
Constituído de três seções:
definições (declaração de nomes e tipos de tokens, de variáveis, etc.)
%%
regras de tradução (contém as produções da gramática) símbolo : derivações {ações semânticas}
%%
rotinas do usuário (contém o main(l) e outras rotinas de suporte em C)
Compiladores
Especificação Bison
Uma especificação bison descreve uma gramática
livre do contexto que pode ser usada para gerar um parser.
Ela possui elementos membros de 4 classes:
Elementos léxicos ou tokens, que é o conjunto de símbolos
terminais;
Elementos sintáticos, que são símbolos não-terminais. Regras de produção, que definem símbolos não-terminais
em termos de sequência de terminais e não-terminais;
Uma regra start, que reduz todos os elementos da
gramática para uma regra.
7
Especificação Bison
A cada regra está associado um símbolo não-terminal
(lado esquerdo).
As definições (lado direito) consistem de zero ou
mais símbolos terminais (tokens ou caracteres literais) ou não-terminais.
Tokens são símbolos terminais reconhecidos pelo
analisador léxico, apenas no lado direito das regras.
A cada regra pode ser associada uma ação em C.
Estas ações ficam entre chaves ( { } ) e ditam o que deve ser feito pra cada produção reconhecida.
Compiladores
Especificação Bison
Os nomes de símbolos podem ter qualquer tamanho,
consistindo de letras, ponto, sublinhado e números (exceto na primeira posição).
Os nomes de não-terminais são usualmente
especificados em minúsculos.
Os terminais ou tokens, em maiúsculos.
O bison transforma esta descrição da gramática, e
ações associadas, em um parser (programa capaz de analisar uma sequência de tokens de entrada, detectar produções e agir sobre elas).
9
Especificação Bison
Na seção de declarações (1ª seção):
Códigos em C entre %{ e %} (como no flex). Definição de tokens: %token T1
Definição de regras auxiliares para a solução de
ambiguidades, como por exemplo: %left MAIS MENOS
%left VEZES DIVIDIR %left MENOS_UNARIO
Compiladores
Especificação Bison
Na seção de regras de produção (2ª seção):
uma gramática definida assim:
lado_esquerdo alt1 | alt2 |...| altN
transforma-se na seguinte especificação yacc
Lado_esquerdo : alt1 {/*ação 1*/} | alt2 {/*ação 2*/} ... | altN {/*ação N*/} ; 11
Especificação Bison
Na seção de regras de produção (2ª seção):
Cada símbolo (terminal ou não) tem associado a ele uma
pseudo variável;
O símbolo do lado esquerdo tem associada a ele a
pseudo variável $$;
Cada símbolo i da direita tem associada a ele a variável $i; $i contém o valor do token (retornado por yylex()) se o
símbolo i for terminal, caso contrário contém o valor do $$ do não terminal;
Compiladores
Usando o Bison
São 4 passos para criar um parser:
Escrever uma especificação de uma gramática no formato
do bison. O arquivo terá a extensão .y.
Escrever uma especificação de um analisador léxico que
pode produzir tokens; extensão .l.
Executar o bison sobre a especificação .y e o flex sobre a
especificação .l.
Compilar e linkar os códigos fontes do parser, do
analisador léxico e suas rotinas auxiliares.
13
Usando o Bison
A saída do bison, yy.tab.c, contém a rotina yyparse
que chama a rotina yylex para obter tokens.
Como o Lex, o Bison não gera programas completos.
A yyparse deve ser chamada a partir do main.
A rotina léxica lê a entrada, e a cada token
encontrado, retorna o número do token ao parser.
A rotina léxica pode também passar o valor do token
usando a variável externa yylval (entre outras).
Um programa completo também requer uma rotina
Fluxo de Controle de um Tradutor
main yyparse( ) yylex( ) yylval input Entrada avaliada Valor da ação do processo Requerer próximo token Retornar o token ou 0 se EOF Valor passado do token Ler chars da entrada Retorna 0 se entrada é valida 1 se não 15Tradutor com Flex/Bison
Veremos agora um exemplo de implementação de
um analisador léxico e sintático usando as ferramentas Flex e Bison, respectivamente.
No programa bison, será acrescentado regras
semânticas de forma a termos no final a implementação de um tradutor.
O objetivo é implementar um tradutor que calcule e
traduza expressões aritméticas da forma infixa para a forma posfixa.
Gramática
cmd ID = expr ; expr expr + termo
| expr – termo | termo
termo termo * fator | termo / fator | fator fator ( expr ) | NUM | ID Compiladores 17
Esquema de Tradução
cmd ID{imprimir ID.atr} = expr ; {imprimir(′=′)} expr expr + termo {imprimir(′+′)}
| expr – termo {imprimir(′–′)} | termo
termo termo * fator {imprimir(′*′)} | termo / fator {imprimir(′/′)} | fator
fator ( expr )
Ferramentas
As ferramentas Flex e Bison são projetos GNU
obtidas no site:
https://sourceforge.net/projects/gnuwin32/files/flex/2.5.4a-1/flex-2.5.4a-1.exe/download
http://downloads.sourceforge.net/gnuwin32/bison-2.4.1-setup.exe
As ferramentas rodam em linha de comando no DOS.
Os arquivos .lex e .y devem estar na pasta bin para
serem reconhecidos pelas ferramentas.
Compiladores 19
Compilação
Na linha de prompt, deve-se entrar com os seguintes
comandos:
flex arquivo1.lex bison –d arquivo2.y
Deve-se abrir o arquivo2.tab.c no DevC++ e compilar
para gerar o executável arquivo2.tab.exe
Este executável é o analisador/tradutor desejado.
Depois de executado, entra-se com uma expressão
infixa e o tradutor devolve a expressão posfixa correspondente e o valor resultante.
obs:o parâmetro d é usado para se gerar arquivo .h
Programa .lex – Analisador Léxico
%{
#include "ypos.tab.h" #include <stdlib.h> extern int yylval; %}
digito [0-9]
letra [a-zA-Z]
%%
"+" {yylval = yytext[0]; return MAIS;}
"-" {yylval = yytext[0]; return MENOS;}
"*" {yylval = yytext[0]; return MULT;}
"/" {yylval = yytext[0]; return DIV;}
Compiladores 21
Programa .lex – Analisador Léxico
"=" {yylval = yytext[0]; return ATRIB;}
";" {return PV;}
"(" {return ABREPAR;}
")" {return FECHAPAR;}
{digito} {yylval = atoi(yytext); return NUM;}
{letra} {yylval = yytext[0]; return ID;}
Programa .y – Analisador Sintático
%{
#include <stdio.h>
// To avoid warning, we include below definitions: int yylex();
void yyerror(const char *s); %}
%token MAIS MENOS MULT DIV NUM ID ATRIB PV ABREPAR FECHAPAR
%left ATRIB // Operator precedence and associativity
%left MAIS MENOS
%left MULT DIV
Compiladores 23
Programa .y – Analisador Sintático
%%
cmd : ID ATRIB {printf("%c", $1);} expr PV {printf("%c", $2);
printf("\n%d\n", $4);} ;
expr : expr MAIS termo {$$ = $1 + $3; printf("%c", $2);}
| expr MENOS termo {$$ = $1 - $3; printf("%c", $2);} | termo
;
termo : termo MULT fator {$$ = $1 * $3; printf("%c", $2);}
| termo DIV fator {$$ = $1 / $3; printf("%c", $2);}
| fator ;
Programa .y – Analisador Sintático
fator : ABREPAR expr FECHAPAR {$$ = $2;}
| NUM {printf("%d", $1);} | ID {printf("%c", $1);} ; %% #include "lex.yy.c" int main(){ yyparse(); return(0); }
void yyerror(const char *s){ printf("\nERROR\n"); } int yywrap(){ return 1; }