INSTITUTO DE TECNOLOGIA
Belém
2009
RESUMO
nossos dias, no entanto, o seu estudo ainda se reveste de grande importância, agora mais
programação. O presente trabalho, além de uma visão geral sobre compiladores, discute
preparação e validação deste antes da tradução para uma linguagem de mais baixo nível.
Será apresentada a ferramenta Bison, cujas funcionalidades podem ser aplicadas numa
vasta área da computação, pois não tem sua utilidade voltada somente para os
entrada dados que possam ser descritos em uma linguagem formal, tais como editores
INTRODUÇÃO
I. FASES DE UM COMPILADOR
I. Análise Léxica
I. Descrição Teórica
I. Histórico
Valores Semânticos
Ações Semânticas
cadastro.l e analisador.y
CONCLUSÃO
REFERÊNCIAS BIBLIOGRÁFICAS
INTRODUÇÃO
receber como entrada um arquivo texto que represente uma linguagem de programação
ou não.
pseudo código.
GLR (Generalized Left-to-right Rightmost derivation) [3] para essa gramática. Com o
aplicações. Por ser compatível com Yacc (Yet Another Compiler-Compiler) [4], todas
gramáticas escritas no Yacc podem trabalhar sem alterações no Bison. É necessário ter
conhecimento das linguagens de programação C ou C++ para sua utilização, entretanto,
qualquer um acostumado com Yacc pode ser capaz de usá-lo sem problemas.
I. FASES DE UM COMPILADOR
I. Análise Léxica
compilação. Recebendo como input o código fonte ou programa fonte. Este código
certo token não pode ser definido com os padrões existentes. Existem em certas
lido a fim de ocorrer a devida identificação, é por essa razão que o AL possui também
Exemplo: a + b = 3
As expressões regulares são a base da análise léxica. São elas que definem os
Tabela de símbolos
O AL inicia uma tabela de símbolos que será utilizada por todas as demais fases
reconhecidos com seus respectivos tipos, atributos e qualquer informação que venha a
fonte. Assim que o parser termina o processamento do conjunto atual, ele faz um
requerimento de mais tokens ao AL, que pára o reconhecimento que estava fazendo, e
os envia ao parser. Este processo se repete até que o programa fonte termine.
substituições necessárias em uma leitura prévia. Nesta mesma leitura são removidas as
significativo(s) ao programa.
Esta fase é a responsável pela verificação do fluxo de tokens repassado pelo AL, tal
verificação determina se este fluxo encontra-se ordenado de forma válida para que esta
sentença faça parte da linguagem definida por uma determinada gramática. Este
processo é feito com o uso de gramáticas livres do contexto (GLC), onde os tokens são
linguagem fonte.
Uma vez que a entrada do parser não obedeça a tais regras, o papel do parser é
análise seja terminada. A falta de um delimitador, como, por exemplo, um parêntese não
de uma árvore de derivação válida de acordo com as regras de derivações da GLC, onde
A derivação de uma sentença pode ser feita utilizando derivação mais à esquerda
ou mais à direita, assim como a análise sintática pode utilizar um método descendente
não terminal a ser derivado é sempre aquele que se encontra mais à esquerda, o que é
símbolo não-terminal inicial (raiz) e o segundo segue a ordem inversa. Pode-se então
(ao menos uma) produzirem árvores de derivação diferentes que chamamos a gramática
de ambígua.
cadeia de entrada, a partir da raiz. Este processo pode ser feito de diferentes formas, os
três tipos de analisadores sintáticos descendentes (ASD) são: recursivo com retrocesso,
O ASD recursivo preditivo faz uso de um diagrama de transições para cada não-
terminal de uma dada gramática, onde os rótulos dos lados são tokens e não-terminais.
precisa saber qual das regras deverá ser utilizada de acordo com cada cadeia de entrada
(AFD), uma vez que somente uma transição deverá ser seguida. É importante ressaltar
que assim como os demais métodos de análise top-down, gramática não pode ser
recursiva à esquerda.
determinar que produção será empregada, além de uma pilha e buffer de entrada.
terminal mais a direita. Por fazer operações de empilhamento (da cadeia para a pilha) e
redução (utilização inversa das regras da gramática) esta análise pode ser chamada
Um analisador sintático ascendente (ASA) faz uso de uma pilha e uma tabela
ASA é geralmente utilizado para que haja precedência de operadores e opera sobre uma
gramáticas não possuem produções que derivem a cadeia vazia ou símbolos não-
uso. É importante frisar que o a comparação de precedência é feita entre o terminal mais
sintático, recebe como input a árvore de derivação (output da análise sintática) e acesso
total à tabela de símbolos criada pela análise léxica. Utiliza a tradução dirigida pela
entrada. A TDS é uma técnica que juntamente com a análise sintática permite a tradução
ou geração de código.
erro, etc. Associando variáveis aos símbolos da gramática o processo torna-se mais
Esquemas de Tradução
atribuir os atributos a cada símbolo gramatical, tais atributos podem ser: valor
Herdados: O valor é computado a partir dos valores dos atributos dos irmãos e
pode ser gerado, porém é necessário um alto nível de abstração e complexidade. Para
As técnicas empregadas na geração de código podem ser usadas até mesmo sem
uma fase de otimização. O gerador de código alvo é quem realmente se preocupa com a
É exigido que o código de saída seja correto e de alta qualidade. Porém gerar um
código ótimo é uma questão que não pode ser resolvida. E para driblar esse problema,
utilizam-se técnicas heurísticas que geram códigos bons, mas não ótimos.
Se um código chegou a esta fase, significa dizer que está limpo de erros. As
I. Descrição Teórica
I. Histórico
inicialmente por Robert Corbett quando teve sua primeira versão registrada em 1988 e
compatível com o Yacc. Possuiu no decorrer desses vinte e um anos de criação, um total
Para que o Bison analise uma linguagem, ela deve ser descrita
por uma gramática livre de contexto (GLC) [5]. Isso significa que
para fazer uma expressão poderia ser: “Uma expressão pode ser feita
no entanto, deve existir pelo menos uma que leve ao fim da recursão.
pode ser feita pelo Bison, porém este programa é otimizado para o
como análise GLR a fim de que suporte qualquer GLC para qualquer
chamado de agrupamento.
de sintaxe.
Valores Semânticos
valor preciso é muito importante para indicar que uma entrada já foi
mas valor 3989. Quando uma regra gramatical diz que um INTEGER é
Ações Semânticas
uma regra gramatical pode ter uma ação feita por sentenças em C.
suas partes. No Código II, uma regra diz que uma expressão pode ser
descreve como foi construída. A ação para essa regra deve criar uma
dado ponto. Isto é, não é capaz de decidir (com base nas entradas
anteriormente.
de análise léxica toda vez que precisa de um novo token. Ele não
executado.
até então.
cada regra gramatical na língua, descrever a ação a ser tomada quando uma
instruções C.
analisador. O analisador léxico pode ser escrito em C. Poderia ser feito também
código.
gramática Bison. Tal arquivo deve conter três seções distintas, a forma geral de
% {
Declarações em C
%}
Declarações Bison
%%
Regras de gramática
%%
Código em C adicionais
Código III – Forma geral de um arquivo de gramática Bison
gramática Bison para separar as seções. Na primeira seção são inseridas definições de
tipos e variáveis a serem usadas nas ações semânticas Pode-se usar também comandos
incluir arquivos de cabeçalho que possam conter outras informações exigidas pelo
símbolos da gramática.
terminal a partir dos tokens que o compõe. Juntamente a essa seção estão as ações
A terceira seção do arquivo pode conter códigos que o programador ache necessário,
que será gerado com o código mostrado a seguir (Código IV) além de reconhecer as
double na pilha e um token NUM ou o código ASCII do caractere lido se não for um
entrada retornará 0.
%{
#define YYSTYPE double
#include <math.h>
%}
%token NUM
input: /* vazio */
| input line
;
line: '\n'
| E '\n' { printf ("\t%.10g\n", $1); }
| error '\n' { yyerrok; }
;
E:
E '+' T { $$ = $1 + $3; }
| E '-' T { $$ = $1 - $3; }
| T { $$ = $1; }
;
T: T '*' F { $$ = $1 * $3; }
| T '/' F { $$ = $1 / $3; }
| T '^' F { $$ = pow($1,$3) }
| F { $$ = $1; }
;
F: NUM { $$ = $1; }
| '(' E ')' { $$ = $2; }
;
%
#include <stdio.h>
#include <ctype.h>
int
main (void)
{
return yyparse ();
}
Código IV – Código do arquivo calculadora.y na íntegra
manual da função yylex podem ser vistos no Apêndice C. Mostra-se abaixo uma
>> calculadora
5+1
6
8-3
5
4*32
128
18/3
6
2^10
1024
5+2*3
11
5%2
syntax error
cadastro.l e analisador.y
Como dito anteriormente, o Bison age em conjunto com uma função yylex que
retorna um token a cada chamada da mesma. Esta função pode ser criada manualmente,
como no exemplo anterior, ou pode-se fazer uso de ferramentas auxiliares como o Lex
análise léxica Flex, assim como geralmente acontece na maioria das utilizações do
Bison. O arquivo analisador que será gerado com os códigos mostrados a seguir
reconhecerá uma sequência qualquer de nomes ou uma matricula válida para alunos do
possui apenas duas seções devido a não necessidade de códigos adicionais na linguagem
C.
%{
#include "cabecalho.h"
#include "analisador.h"
%}
/* Definições */
STRING [A-Za-z][a-z ]*
BRANCOS [ \n\r\t]+
DIGITO [1-9]
CODIGO_ZERO [0]
CODIGO_OITO [8]
ANY_CHAR .
%option case-sensitive
%% /* Regras */
{BRANCOS} /* ignora */
{ANY_CHAR} {
printf("Caracter invalido: '%c' (ASCII=%d)\n", yytext[0],
yytext[0]);
}
%{
#include "cabecalho.h"
#include <stdio.h>
%}
%%
exp:
T_MATRICULA matricula
| T_NOME string_list
;
num:
T_DIGITO {printf("...");}
| ZERO {printf("...");}
| OITO {printf("...");}
;
string_list:
string
| string_list string
;
string:
T_STRING {printf("\n\nNOME ACEITO!\n"); exit(0);}
;
%%
/* Código em linguagem C */
void yyerror(const char* errmsg)
{
printf("Sentenca nao reconhecida :( \n");
printf("\n*** Erro: %s\n", errmsg);
}
Nos dois arquivos utilizados para criação do arquivo analisador Bison inclui-se o
pelos arquivos.
Figura II - Interação entre as ferramentas Flex e Bison. Ambos geram arquivos na
linguagem C que ao serem ligados pelo compilador GCC geram um executável que
realiza a função da análise sintática.
#ifndef __COMMON_H__
#define __COMMON_H__
#endif
Código VII – Código do arquivo cabecalho.h na íntegra
arquivo cadastro.l com a inclusão do arquivo analisador.h que será gerado pelo
será referenciado pelo código gerado pela Flex através da linha de comando flex –
ocadastro.c cadastro.l.
Após a execução dos comando citados anteriormente, ainda é preciso tornar o
arquivo analisador executável, para isso basta apenas que o arquivo de saída do Flex
seja compilado assim como se fez com o arquivo analisador gerado no exemplo
exemplos de códigos que façam a interação entre as ferramentas Flex e Bison podem ser
>> analisador
Uso: MATRICULA [numero]
NOME [nome(s)]
MATRICULA 08080004301
>> analisador
Uso: MATRICULA [numero]
NOME [nome(s)]
MATRICULA 080790004302
>> analisador
Uso: MATRICULA [numero]
NOME [nome(s)]
NOME philipe
NOME ACEITO!
>> analisador.exe
Uso: MATRICULA [numero]
NOME [nome(s)]
NOME 3Luciana
Sentenca nao reconhecida :(
CONCLUSÃO
importância foi preservada principalmente por estar focada desde sua primeira versão
constantes.
Instalando o Bison
Após realizar o download das ferramentas open source Flex e Bison nas páginas
duplo clique com o botão esquerdo do mouse. Esta ação iniciará a instalação.
Clique em Next >, marque a caixa “I accept the agreement”, em seguida Next >,
ferramenta Bison, pois um dos arquivos essencias para a execução correta do programa
Bison não consegue ser achada devido aos espaços em branco contidos no caminho do
diretório. Modifica-se o diretório para C:\GnuWin32 ou qualquer outro que não possua
espaços em branco.
Em seguida avance quatro vezes clicando no botão Next >. Logo após clique em
acima também são válidos para instalação para sistemas operacionas Windows Vista.
Clique com o botão direito do mouse sobre o ícone do Meu computador, siga
com o cursor até Propriedades, vá até a aba Avançado e então a Variáveis de ambiente.
Clique com o botão esquerdo do mouse na linha onde se encontra a palavra Path, em
como está e siga o cursor do teclado para o final dela, adicione um ‘;’ e em seguida, sem
dar espaço o diretório onde foi instalado o Flex e o Bison, acrescentado de \bin, pois é
seguir:
[0-2]
realização da matrícula.
utilizando o mesmo padrão de linha de comando utilizados das Seções II.II.I e II.II.II.,
%{
#include <math.h> /* For math functions, cos(), sin(), etc. */
#include "symtable.h" /* Contains definition of `symrec' */
%}
%union {
double val; /* For returning numbers. */
symrec *tptr; /* For returning symbol-table pointers */
}
%right '='
%left '-' '+'
%left '*' '/'
%left NEG /* Negation--unary minus */
%right '^' /* Exponentiation */
/* Grammar follows */
%%
input: /* empty */
| input line
;
line:
'\n'
| exp '\n' { printf ("\t%.10g\n", $1); }
| error '\n' { yyerrok; }
;
#include <stdio.h>
main ()
{
init_table ();
yyparse ();
}
struct init
{
char *fname;
double (*fnct)();
};
#include <ctype.h>
yylex ()
{
int c;
if (c == EOF)
return 0;
i = 0;
do
{
/* If buffer is full, make it bigger. */
if (i == length)
{
length *= 2;
symbuf = (char *)realloc (symbuf, length + 1);
}
/* Add this character to the buffer. */
symbuf[i++] = c;
/* Get another character. */
c = getchar ();
}
while (c != EOF && isalnum (c));
s = getsym (symbuf);
if (s == 0)
s = putsym (symbuf, VAR);
yylval.tptr = s;
return s->type;
}
aquecimento
%{
#include <stdio.h>
#include <string.h>
#include "heat.h"
%}
%%
[0-9]+ yylval.number=atoi(yytext); return NUMBER;
heater return TOKHEATER;
heat return TOKHEAT;
on|off yylval.number=!strcmp(yytext,"on"); return
STATE;
target return TOKTARGET;
temperature return TOKTEMPERATURE;
[a-z0-9]+ yylval.string=strdup(yytext);return WORD;
\n /* ignore end of line */;
[ \t]+ /* ignore whitespace */;
%%
heat.l
%{
#include <stdio.h>
#include <string.h>
int yywrap()
{
return 1;
}
main()
{
yyparse();
}
char *heater="default";
%}
%token TOKHEATER TOKHEAT TOKTARGET TOKTEMPERATURE
%union
{
int number;
char *string;
}
%%
commands:
| commands command
;
command:
heat_switch | target_set | heater_select
;
heat_switch:
TOKHEAT STATE
{
if($2)
printf("\tHeater '%s' turned on\n", heater);
else
printf("\tHeat '%s' turned off\n", heater);
}
;
target_set:
TOKTARGET TOKTEMPERATURE NUMBER
{
printf("\tHeater '%s' temperature set to %d\n",heater,
$3);
}
;
heater_select:
TOKHEATER WORD
{
printf("\tSelected heater '%s'\n",$2);
heater=$2;
}
;
heat.y
REFERÊNCIAS BIBLIOGRAFICAS
http://userpages.monmouth.com/~wstreett/lex-yacc/bison.html#SEC1
http://en.wikipedia.org/wiki/LALR_parser
http://en.wikipedia.org/wiki/GLR_parser
http://dinosaur.compilertools.net/yacc/index.html
http://en.wikipedia.org/wiki/Context-free_grammar
[6] M. E. Lesk and E. Schmidt; Lex - A Lexical Analyzer Generator. Disponível em:
http://dinosaur.compilertools.net/lex/index.html
[8] Bison for Windows, Bison: Yacc-compatible parser generator, versão 2.4.1.
[9] Flex for Windows, Flex: fast lexical analyzer generator, versão 2.5.4. Disponível
em: http://gnuwin32.sourceforge.net/packages/flex.htm