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 Bell Laboratories. 2
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) oubison. 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. 4
Gerador de Analisador Sintático Criando um tradutor de entrada/saída com Yacc: Especificação Yacc translate.y y.tab.c Compilador Yacc Compilador C y.tab.c a.out entrada a.out saída 5 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) 6
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. 8
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 10
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; Ações podem ser executadas sobre estas variáveis. 12
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. Ayyparse deve ser chamada a partir domain. 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 de erro chamada yyerror. 14
Fluxo de Controle de um Tradutor main Entrada avaliada Retorna 0 se entrada é valida 1 se não yyparse( ) Requerer próximo token Valor da ação do processo yylval Retornar o token ou 0 se EOF Valor passado do token yylex( ) Ler chars da entrada input 15 Tradutor 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. 16
Gramática cmd ID=expr; expr expr+termo expr termo termo termo termo * fator termo / fator fator fator (expr) NUM ID 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) NUM{imprimir(NUM.atr)} ID{imprimir(ID.atr)} 18
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. 19 Compilação Na linha de prompt, deve-se entrar com os seguintes comandos: flex arquivo1.lex bison d arquivo2.y obs: o parâmetro d é usado para se gerar arquivo.h Deve-se abrir oarquivo2.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. 20
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;} 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;} %% 22
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 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 ; 24
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; } 25