Vous êtes sur la page 1sur 44

Projeto de Compiladores (etapa 7): Pico

Nicolas Maillard Claudio Schepke Stfano Mr

2 Este documento detalha a especicao da stima etapa do pequeno compilador Pico.

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

Chapter 1

Organizao do projeto
A primeira etapa consiste na programao de duas estruturas de dados bsicas. As etapas 2, 3 e 4 consistem na implementao dos analisadores lexicais e sintticos. As etapas 5, 6 e 7 trataro da fase de gerao de cdigo. 1. Estruturas de dados bsicas (pilha e tabela de Hash). 1 ponto. 2. Analisador lexical (com auxlio do ex). 1 ponto. 3. Analisador sinttico (LL(1). 1 ponto. 4. Anlise sinttica LR (Yacc). 1,5 ponto. 5. Anlise semntica: declarao de variveis, expresses. 1,5 ponto. 6. Anlise semntica: controle de uxo. 2 pontos. 7. Gerao de cdigo Assembly. 2 pontos. O trabalho de programao da etapa 7 deve ser entregue at o dia 01 de Dezembro, 23h00 (segunda-feira). Ser apresentado ao tutor no encontro do dia 02 de Dezembro, 10h30 ou no dia 04 de Dezembro. Ele receber uma nota valendo 2 pontos do total da nota do projeto. A nota nal do projeto valer 50% da nota nal da disciplina. A programao deve ser feita no Linux, em C ANSI. Ser usados, a partir da segunda etapa, as ferramentas Lex (ex) e Yacc (Bison). O ltimo captulo deste documento d as primeiras instrues para usar o comando Make, no Linux, para compilar um programa. Tambm explica como se usa o Doxygen para documentar cdigo. absolutamente fundamental que a especicao seja respeitada totalmente: os nomes dos procedimentos devem ser iguais ao especicado; os parmetros devem ser conformes ao que se espera; etc. Os cdigos devem ser comentados, de acordo com o padro Doxygen. Encontra-se, nos programas entregues junto a essa especicao, exemplos de uso de Doxygen. O projeto todo deve ser desenvolvido por grupos de 3 alunos. Faz parte do trabalho se distribuir a carga de trabalho de forma a fornecer uma implementao de boa qualidade: por exemplo, pode se programar a quatro mos (dois alunos programam juntos), com o terceiro colega se encarregando de testes e da documentao. Pode-se tambm optar por uma distribuio dos procedimentos a ser programados. Aconselha-se que programao e teste no sejam feitos pela mesma pessoa. interditado que um aluno nunca programe nada. totalmente possvel que os integrantes de um grupo tenham notas diferenciadas em funo de seu envolvimento no trabalho.

Organizao do projeto

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

Chapter 2

Especicao da Etapa 1
O cdigo a ser implementado se encontra especicado em Pico/src/Etapa1. Pede-se implementar uma estrutura de pilha (ver o arquivo stack.h) e uma estrutura de tabela de smbolos (ver symbol_table.h). O diretrio doc/html/ contm essas especicaes, legveis on-line. A pilha dever suportar operaes de push e de pop de elementos genricos void *, ou seja referncias sobre qualquer tipo se encontraro na pilha. Outras operaes clssicas so exigidas. Uma tabela de smbolos uma estrutura de dados interna a um compilador, que ir ser usada intensivamente nas outras etapas. Por ora, basta saber que se implementa atravs de uma tabela de Hash. Uma entrada na tabela de smbolos ser caracterizada por vrios campos (cujo detalhe est fora do escopo desta primeira etapa, ver o tipo abstrato entry_t em symbol_table.h), incluindo um campo nome de tipo char* (ou string, em C). A partir deste nome, deve-se calcular um hash para associ-lo a um nmero inteiro, que ir servir para acessar um vetor. Se houver colises na funo de Hash, cuidados devem ser tomados para desempatar. As funes exigidas so de insero de uma entrada, de consulta, alm de funcionalidades de vericao e de impresso do contedo de uma tabela de smbolos. Salienta-se que se espera implementaes corretas: sem perda de memria (lembrar que C no usa coletor de lixo: caso se use um malloc, deve-se usar um free em algum outro lugar), sem acesso fora da rea de memria alocada, sem efeito colateral no controlado, etc. . . A seguir, detalha-se o perl de cada um dos procedimentos exigidos.

2.1

Referncia do Arquivo stack.h

Funes
void init_stack (stack s)
Inicializar a pilha. Uso tipico: init_stack(&minha_pilha);.

void free_stack (stack s)


Liberar a memoria usada pela pilha. Uso tipico: free_stack(&minha_pilha);.

int empty (stack s)


Testar se a pilha esta vazia.

int push (stack s, void item)


Empilhar um elemento na pilha. (O tipo do elemento void .).

Especicao da Etapa 1

void pop (stack s)


Desempilhar o elemento no topo da pilha.

void top (stack s)


Consultar o elemento no topo da pilha.

Variveis
typedef stack
Aqui, voce deve completar a parte entre o typedef e o stack para inserir sua implementacao da estrutura de dados abstrata de pilha.

2.1.1
Verso: 1.1

Descrio Detalhada

2.1.2
2.1.2.1

Funes
int empty (stack s)

Testar se a pilha esta vazia. Testa se a pilha esta vazia. Parmetros: s uma pilha Retorna: 0 se a pilha nao esta vazia, um valor diferente de zero se a pilha esta vazia. 2.1.2.2 void free_stack (stack s)

Liberar a memoria usada pela pilha. Uso tipico: free_stack(&minha_pilha);. free_stack eh o destrutor da estrutura de dados de pilha. Deve liberar qualquer espaco na memoria que tenha sido alocado para a implementacao interna da pilha passada em argumento. Um acesso a uma pilha, depois da chamada a free_stack levara a um erro na execucao. Parmetros: s um ponteiro sobre uma pilha (stack). Retorna: nada (void).
Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

2.1 Referncia do Arquivo stack.h 2.1.2.3 void init_stack (stack s)

Inicializar a pilha. Uso tipico: init_stack(&minha_pilha);. Inicializa a pilha, allocando qualquer espao na memoria que seja necessario. Nao se deve efetuar nenhuma hipotese restritiva quanto ao numero total de entradas que podera conter a pilha num dado instante. init_stack devera ser chamado pelo usuario deste estrutura de dados, antes de poder usa-la. Qualquer referencia anterior que ele possa fazer a uma pilha tera comportamento nao-especicado. Parmetros: s um ponteiro sobre uma pilha (stack). Retorna: nada (void). 2.1.2.4 void pop (stack s)

Desempilhar o elemento no topo da pilha. Desempilha o elemento no topo da pilha, e retorna-o. Remove este elemento da pilha. Parmetros: s,um ponteiro sobre a pilha de onde se deve tirar um elemento. Retorna: o elemento que foi desempilhado, ou NULL se nao tinha como desempilhar alguma coisa. 2.1.2.5 int push (stack s, void item)

Empilhar um elemento na pilha. (O tipo do elemento void .). Empilha um elemento na pilha. Parmetros: s,uma referencia sobre a pilha onde se deve inserir o elemento. item,uma referencia sobre o elemento a ser inserido. Retorna: 0 se a insercao deu certo. 2.1.2.6 void top (stack s)

Consultar o elemento no topo da pilha. Retorna um ponteiro sobre o elemento no topo da pilha. Nao remove este elemento da pilha. Parmetros: s,a pilha de que se deve consultar o topo. Retorna: o elemento, ou NULL se nao tinha nada no topo.

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

Especicao da Etapa 1

2.2

Referncia do Arquivo symbol_table.h

Estruturas de Dados
struct entry_t

Funes
int init_table (symbol_t table)
Inicializar a tabela de Hash.

void free_table (symbol_t table)


Destruir a tabela de Hash.

entry_t lookup (symbol_t table, char name)


Retornar um ponteiro sobre a entrada associada a name.

int insert (symbol_t table, entry_t entry)


Inserir uma entrada em uma tabela.

int print_table (symbol_t table)


Imprimir o conteudo de uma tabela.

int print_le_table (FILE out, symbol_t table)


Imprimir o conteudo de uma tabela em um arquivo.

Variveis
typedef symbol_t
Encapsulacao de um tipo abstrato que se chamara symbol_t.

2.2.1
Verso: 1.1

Descrio Detalhada

2.2.2
2.2.2.1

Funes
void free_table (symbol_t table)

Destruir a tabela de Hash. free_table eh o destrutor da estrutura de dados. Deve ser chamado pelo usuario no m de seu uso de uma tabela de simbolos. Parmetros: table,uma referencia sobre uma tabela de simbolos.
Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

2.2 Referncia do Arquivo symbol_table.h 2.2.2.2 int init_table (symbol_t table)

Inicializar a tabela de Hash. Parmetros: table,uma referencia sobre uma tabela de simbolos. Retorna: o valor 0 se deu certo. 2.2.2.3 int insert (symbol_t table, entry_t entry)

Inserir uma entrada em uma tabela. Parmetros: table,uma tabela de simbolos. entry,uma entrada. Retorna: um numero negativo se nao se conseguiu efetuar a insercao, zero se deu certo. 2.2.2.4 entry_t lookup (symbol_t table, char name)

Retornar um ponteiro sobre a entrada associada a name. Essa funcao deve consultar a tabela de simbolos para vericar se se encontra nela uma entrada associada a um char (string) fornecido em entrada. Para a implementacao, sera necessario usar uma funcao que mapeia um char a um numero inteiro. Aconselha-se, por exemplo, consultar o livro do dragao (Aho/Sethi/Ulman), Fig. 7.35 e a funcao HPJW. Parmetros: table,uma tabela de simbolos. name,um char (string). Retorna: um ponteiro sobre a entrada associada a name, ou NULL se name nao se encontrou na tabela. 2.2.2.5 int print_le_table (FILE out, symbol_t table)

Imprimir o conteudo de uma tabela em um arquivo. A formatacao exata e deixada a carga do programador. Deve-se listar todas as entradas contidas na tabela atraves de seu nome (char). Deve retornar o numero de entradas na tabela. A saida deve ser dirigida para um arquivo, cujo descritor e passado em parametro. Parmetros: out,um descrito de arquivo (FILE). table,uma tabela de simbolos. Retorna: o numero de entradas na tabela.
Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

10 2.2.2.6 int print_table (symbol_t table)

Especicao da Etapa 1

Imprimir o conteudo de uma tabela. A formatacao exata e deixada a carga do programador. Deve-se listar todas as entradas contidas na tabela atraves de seu nome (char). Deve retornar o numero de entradas na tabela. Parmetros: table,uma tabela de simbolos. Retorna: o numero de entradas na tabela.

2.2.3
2.2.3.1

Variveis
typedef symbol_t

Encapsulacao de um tipo abstrato que se chamara symbol_t. Voce deve inserir, entre o typedef e o symbol_t, a estrutura de dados abstrata que voce ira implementar.

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

Chapter 3

Especicao da Etapa 2
Essa segunda etapa do projeto consiste na implementao do analizador lexical (scanner), que dever retornar os tokens reconhecidos num arquivo de entrada.

3.1

Explicaes

A etapa consiste na denio, com o Flex, dos tokens que sero reconhecidos pelo compilador Pico. Cada token ser implementado, em C, por um nmero constante inteiro atravs de diretivas #define. Por exemplo, #define IDF 100 pode servir para denir o token IDF, que ser encodicado no analizador lexical (e no futuro analizador sinttico) atravs da constante 101. A lista inteira dos tokens que devero ser denidos, junto com a especicao (em portugus) dos lexemas que devero ser reconhecidos como representando esses tokens, se encontra a seguir na prxima seo. Para cada token, voc dever: 1. Deni-lo atravs de um #define. Essa lista de define dever ser programada em um arquivo separado, limitado a este contedo, chamado de tokens.h. O seu analizador lexical dever incluir tokens.h atravs de um: #include tokens.h Os arquivos entregues em Pico/src/Etapa2 incluem um esqueleto para tokens.h e a incluso do mesmo no arquivo de entrada usado com o Flex (chamado scanner.l). 2. Denir uma expresso regular, de acordo com a sintaxe do Flex, que especique os lexemas representando esses tokens. Essas expresses regulares devero se encontrar no arquivo scanner.l, usado como entrada do Flex. Para determinar essas expresses regulares, pode-se consultar o documento encontrado no Moodle, encontro 4, pgina 7. O arquivo scanner.l inicial, encontrado em Pico/src/Etapa2, contm um esqueleto pronto a compilar. 3. Associar a essa expresso regular uma ao em C, que ser acionada quando o analizador lexical ir reconhecer este token. A ao, em geral, ser trivial e consistir apenas no retorno (return) do token identicado. Eventualmente, ser necessrio executar uma ao mais complexa, de acordo com a especicao. Caber, ento, programar (em C) o cdigo necessrio ou usar estruturas de dados j prontas. Exemplo: seja a especicao (irrealista) seguinte: o token INT deve ser retornado ao reconhecer os lexemas integer ou int. Deve-se programar, por exemplo:

12 #define INT 3333 %% (integer|int) { return( INT ); }

Especicao da Etapa 2

A linha do #define ir no arquivo tokens.h. A linha com a expresso regular, simples neste caso, e a ao em C (return()), dever ser inserida no arquivo scanner.l. Seu analizador lexical, chamado pico, dever ler sua entrada a partir de um arquivo especicado em argumento na linha de comando. Ele deve usar uma tabela de smbolos para armazenar os identicadores (IDF) reconhecidos. O arquivo scanner.l e o Makele fornecidos em Pico/src/Etapa2/ so pontos de partida para escrever o analizador lexical e compil-lo. Basicamente, basta complementar scanner.l e tokens.h. make ou make pico deve invocar o ex, gerar o arquivo C que implementa o analizador, e compil-lo para obter o executvel pico. Observa-se que o scanner.l j vem com um main pronto, o qual se encarrega de ler o arquivo em entrada e de chamar o scanner nele.

3.2

Especicao dos tokens

Os tokens so denidos em duas partes: os chamados tokens simples representam apenas um lexema (string) nico, o que signica que a expresso regular os denindo trivial (mas precisa ser implementada). Os outros tokens necessitam de expresses regulares menos imediatas, porm todas discutidas nas aulas.

3.2.1

Tokens simples

Cada um dos lexemas a seguir se confunde com o token a ser retornado, ou seja o token representa apenas um nica lexema. A nica ao a ser efetuada consiste no retorno do token. Tm casos especiais ainda mais simples, onde o lexema se limita a um caractere nico (exemplos: *, ou ;). Neste caso, o Flex possibilita retornar como constante inteira o prprio caractere (sem necessitar um #define, pois ser usado um cast do char de C para um int). Por exemplo, se o lexema * deve ser reconhecido e associado a um token, ao invs de cham-lo, por exemplo, de ASTERISCO e de programar seu retorno assim:

#define ASTERISCO 3333 %% * { return( ASTERISCO ); }

basta escrever:

%% *

{ return( * ); }

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

3.2 Especicao dos tokens

13

A tabela a seguir explicita todos os tokens a serem retornados. Para ganhar espao, alguns casos juntam mais de um token numa linha s, separados por vrgulas. Espera-se bom senso para entender que, por exemplo,

Lexema (, )

Token a ser retornado (, ) (respectivamente)

signica que o lexema ( deve ser associado ao token ( (exemplo do caso onde o lexema se limita a um nico caractere) e que o lexema ) deve ser associado ao token ), e NO que o lexema (, ) (string composto por abre-parntese vrgula fecha-parntese) deve ser associado aos dois tokens ) e ) (o que no faria sentido).

Lexemas int double float char *,+,-,/ , ; " (, ) [, ], {, } <, >, = <= >= == != &&, ||, ! if then else while

Tokens a ser retornados INT DOUBLE FLOAT CHAR *, +, -, / (respectivamente) , ; QUOTE DQUOTE (, ) (respectivamente) [, ], {, } (respectivamente) <, > e = respectivamente LE GE EQ NE AND, OR, NOT (respectivamente) IF THEN ELSE WHILE

3.2.1.1

Outros tokens

Em toda essa seo, um dgito um dos caracteres 0, 1, 2, . . . , 9.


Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

14 Descrio Qualquer combinao de qualquer nmero de brancos: espao branco, tabulao, m de linha. Pelo menos uma letra (mauscula ou minscula), seguida por qualquer nmero de letras (maisculas ou minsculas), dgitos ou _. Qualquer conjunto de dgitos token a ser retornado no ser retornado token

Especicao da Etapa 2 Ao imprimir na sada (tela): BRANCO.

IDF

o lexema deve ser inserido numa tabela de smbolos. o lexema, convertido em int, deve ser copiado numa varivel C global chamada VAL_INT, de tipo int. o lexema, convertido em double, deve ser copiado numa varivel C global chamada VAL_DOUBLE, de tipo double.

INT_LIT

Um nmero com vrgula utuante (ver abaixo).

F_LIT

No caso do F_LIT, a descrio a seguinte: qualquer conjunto de dgitos (eventualmente vazio), seguido de um ponto (.), seguido de pelo menos um dgito. Isso tudo pode ser, opcionalmente, seguido de: 1. e ou E, obrigatoriamente seguido de 2. um sinal + ou - (obrigatrio), obrigatoriamente seguido de 3. pelo menos um dgito. Exemplos de lexemas reconhecidos: 3.14, 3.14e+0, .0, .0E+0. Exemplos de lexemas no reconhecidos: 0., 1e10, e+10, 10.1E, .0E0.

3.3

Tratamento de erros lexicais

Em alguns poucos casos, a entrada pode conter caracteres (ou seqncias de caracteres) no reconhecidos por nenhuma expresso regular. Observa-se que algumas seqncias que parecem, objetivamente, erradas ao programador no podem ser capturadas pelo analizador lexical. Considere por exemplo a entrada: 10E. Apesar de este lexema no condizer com nada do que se espera, o scanner ir retornar um INT_LIT (lexema 10) e logo depois um IDF (lexema E). Na verdade, essa entrada lexicamente correta, e o erro que o ser humano detecta sinttico: caber apenas gramtica do analizador sinttico no autorizar uma sucesso de dois tokens INT_LIT e IDF. Isso dito, existem casos de erros lexicais. Por exemplo, se um caractere ? aparecer na entrada, nenhuma das expresses regulares acima denidas dever aceit-lo. Isso acontece tambm com outros casos. Para pegar tais casos, a soluo no Lex incluir uma ltima regra (em ltimo lugar para que seja aplicada unicamente em ltima instncia, ou seja se nada fechou antes), do tipo: . { printf(Erro lexical - caractere nao reconhecido: %c.\n, yytext[0]); exit(-1); } A expresso regular . no incio da linha signica qualquer caractere (nico). Ao reconhecer qualquer caractere, ento, o analizador lexical gerado pelo Flex ir imprimir na tela uma mensagem relatando o caractere encontrado e no reconhecido, e depois interromper a execuo do programa (exit) com o
Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

3.4 Observaes nais

15

cdigo de retorno negativo, associado a um erro. Observa-se que se usa, no printf, a varivel interna ao Lex chamada yytext. Essa varivel, que iremos usar mais adiante, um string (char* em C) que contm sistematicamente o lexema que est sendo comparado com a E. R. Como, nessa rgra, se usa a E. R. . que autoriza apenas um caractere, yytext aqui um string de tamanho 1, e portanto yytext[0] o nico caractere compondo o lexema, que no caso o caractere que no foi reconhecido. (Para amadores de ponteiros em C, podia se escrever tambm *yytext em lugar de yytext[0].) O uso de . nessa regra deixa claro que se deve aplic-la apenas como ltimo recurso: uma regra pega tudo.

3.4

Observaes nais

Vai ser necessrio usar a tabela de smbolos da Etapa1. Existe vrias opes para incluir o cdigo C (.c e/ou .h) no cdigo do analizador lexical, uma delas sendo usar a mesma tcnica usada para incluir tokens.h. Deve-se obrigatoriamente incluir um .h que se encontra ou no diretrio corrente (no caso: Pico/src/Etapa2); ou (melhor) em Pico/include, onde o make install da etapa 1 ter copiado seus .h anteriores. Da mesma forma, deve-se linkar o cdigo objeto ou com arquivos .o do diretrio corrente, ou com os .o de Pico/objects (essa segunda opo sendo melhor). Cabe a vocs organizar seu cdigo e seus Makeles para isso. Os exemplos providos j servem para ilustrar como faz-lo. Essa etapa 2 simples - basicamente, consiste em preencher arquivos existentes com as Expresses Regulares vistas em aula, na sintaxe do Flex. Com os arquivos providos, tudo deve se compilar automaticamente. Posto isso, muito importante dedicar tempo para testar seu analizador lexical e vericar o maior nmero possvel de entradas para vericar que ele retorne os tokens que se espera. Os testes dessa etapa iro principalmente testar essas expresses regulares.

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

16

Especicao da Etapa 2

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

Chapter 4

Especicao da Etapa 3
4.1 Trabalho pedido

Nessa etapa, voc deve programar um analisador sinttico (acoplado com seu analisador lexical da Etapa 2) para uma gramtica simples, a m de reconhecer expresses aritmticas. A gramtica a reconhecer a seguinte: E E + E E E - E E E * E E E / E E ( E ) E IDF E INT_LIT E F_LIT O smbolo Start E. Os terminais so (parte de) os tokens denidos na Etapa 2. Sentenas como: IDF + ( IDF * ( F_LIT - IDF ) ) so reconhecidas por essa gramtica. Posto que o analisador lexical da Etapa 2 esteja funcionado, o programa que voc ir entregar nessa etapa dever poder analisar uma entrada do tipo: tmp + (x*( 3.14 - y_0z )), reconhecer que essa entrada se decompe em uma seqncia de tokens tal como a sentena acima, e em torno que essa sentena sintaticamente correta (ou seja de acordo com a gramtica). Para implementar o analisador sinttico, voc dever obrigatoriamente usar o algoritmo LL(1) apresentado na 7a aula (26 de Agosto). Este algoritmo usa, internamente: Uma pilha. Voc pode usar a pilha implementada na Etapa 1. A pilha dever armazenar smbolos terminais (tokens) e no-terminais (a denir em funo da gramtica). Como os terminais retornados por seu analisador lexical so representados atravs de int, sugere-se usar tambm uma encodicao em int de seus smbolos no-terminais. Un analisador lexical para ler um por um os tokens da entrada. Isso j vem provido pelo procedimento yylex() implementado, graas ao Flex, na Etapa 2 (ver o main provido na Etapa 2). Uma tabela de deciso, a tabela LL(1). Voc dever montar essa tabela e implement-la em C para que possa orientar o comportamento do seu parser. Seu parser chamado pico deve aceitar como argumento na linha de comando o nome do arquivo contendo o input (j era o comportamento do seu scanner).

18 A sada do seu parser, na tela, deve ser a seguinte: OKAY no caso que a entrada esteja de acordo com a gramtica; ERROR no caso contrrio.

Especicao da Etapa 3

Tanto o algoritmo de montagem da tabela LL(1), como seu uso pelo parser, se encontram nas lminas do encontro 7. Salienta-se que a gramtica acima indicada no LL(1), por motivos que deveriam aparecer diretamente a quem lembra da aula. A primeira parte dessa etapa consiste em transformar essa gramtica numa gramtica equivalente, e que seja LL(1). Para montar a tabela LL(1), ser necessrio calcular os conjuntos First e Follow de sua gramtica transformada. importante entender que no ser preciso programar o clculo desses conjuntos para qualquer gramtica: basta faz-lo, manualmente, para sua gramtica; deduzir a tabela LL(1) que a reconhecer; e implementar o parser para essa gramtica.

4.2

Entrega do trabalho

O diretrio src/Etapa3 de Pico dever incluir, pelo menos: um arquivo txt gramatica.txt contendo sua gramtica transformada, seguindo o formato seguinte (a ttulo de exemplo, usa-se aqui a gramtica acima apresentada): E -> E + E E -> E * E etc... um arquivo txt tabelaLL1.txt contendo a tabela LL(1), no formato seguinte: Simbolo ( E (E) etc... ) + - * / F_LIT INT_LIT F_LIT IDF $

(cada coluna para um smbolo no-terminal em uma linha mostra a derivao a ser aplicada ao nterminal, quando o prximo token lido o rtulo da coluna. Por exemplo, aqui, tendo-se E no topo da pilha, e lendo-se o token (, deve-se derivar o E em ( E ).) A implementao em C de seu parser LL(1), que dever incluir (e chamar) o analisador lexical da Etapa 2; Um arquivo Makele que o compila (ver o Makele de src/Etapa2 para se ajudar). Casos de teste.

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

Chapter 5

Especicao da Etapa 4
Essa etapa consiste na denio da gramtica usada no compilador Pico e na implementao de um parser que a reconhea, graas ferramenta Yacc (Bison). Sugere-se (mas no obrigatrio) distribuir partes da gramtica entre os trs integrantes, cada qual se responsabilizando pela implementao de um pedao. Para a programao, pode-se usar o cdigo fonte original liberado no Moodle (pico-etapa4.1.tar.gz). Vem a estrutura de diretrios usual, com Makeles prontos para serem usados. A nica parte dos Makeles que pode ser alterada so as regras que dizem respeito ao diretoio Pico/Tests/ e s etapas 2 e 3. No se pode r alterar Pico/Makele, nem Pico/src/Etapa4/Makele. Voc deve: baixar e unzipar pico-etapa4.1.tar.gz; copiar em src/Etapa1, src/Etapa2 e src/Etapa3/ os arquivos de suas implementaes prvias das 3 primeiras etapas. Para as etapas 2 e 3, podem recuperar tambm os Makeles que vocs usaram; desenvolver a etapa 4 em src/Etapa4; compil-la a partir de Pico/, com o comando make etapa4. Os tutores daro suporte no uso dos Makeles apenas se sua estrutura for respeitada.

5.1

Ajustes no analisador lexical

Foram esquecidos quatro tokens simples a serem acrescentados no scanner da Etapa 2 (arquivo Pico/src/Etapa2/scanner.l): :, END, FALSE e TRUE. Basta completar este arquivo com as 4 linhas (na seo correta): ":" {return(:);} "end" {return(END);} "true" {return(TRUE);} "false" {return(FALSE);} Comentrio importante. O cdigo fonte entregue (pico 4.1) vem com Makeles prontos para ajud-lo a compilar seu parser da etapa 4. Os Makeles sistematicamente re-aproveitam Pico/src/Etapa2/scanner.l para obter o scanner usado pelo Yacc. Ou seja, qualquer modicao que voc queira ou precise fazer em seu scanner deve ser feita no diretrio da Etapa 2, se no ela no ser enxergada pela Etapa 4.

20

Especicao da Etapa 4

5.2

Gramtica

Um programa Pico composto por uma seo de declaraes, seguida de uma seo de aes (ver o start da gramtica no arquivo pico.y fornecido).

5.2.1

Declaraes

A seo de declaraes pode estar vazia, ou composta por uma ou mais declarao, separada(s) pelo terminal ; Uma declarao composta por uma lista de identicadores, seguida por :, seguido por um indicador de tipo. Uma lista de identicadores consiste em um nmero qualquer de (pelo menos um) identicador(es) separado(s) por ,. Um indicador de tipo pode derivar em: cada um dos 4 terminais que informa um tipo (ver o captulo sobre o scanner), ou cada um dos 4 terminais que informa um tipo, seguido de [, seguido de uma lista de duplas de inteiros, seguida de ]. Uma lista de duplas de inteiros composta por qualquer nmero (maior ou igual a 1) de ocorrncias da seqncia de trs smbolos seguintes: INT_LIT : INT_LIT, cada ocorrncia sendo separada da seguinte por ,. Exemplo 1 de declaraes: x, tmp_1 : double; Exemplo 2 de declaraes: x:float; z1t2 , i :float

Exemplo 3 de declaraes: x:float; tab : double[10:20, 30:10, 0:5]; y:float Neste ltimo caso, encontra-se uma lista de trs duplas de inteiros: 10:20, 30:10 e 0:5. Comentrio. Observa-se que nessa etapa de denio da sintaxe, no se associa semntica ainda a essas construes. No entanto, parece intuitivo que este tipo de declarao servir para denir um array de 3 dimenses e que cada dupla n:m dene os valores mnimos e mximos autorizados para o ndice em uma direo. Nessa lgica, a segunda dupla 30:10 parece dever levar a um erro, pois se est tentando declarar um array com um limite inferior de ndice maior do que o limite superior na segunda dimenso. Repara-se que isso seria um erro semntico, tipicamente detectado na prxima etapa, e no nessa.

5.2.2

Aes

A seo de aes composta por um ou mais comandos, separados pelo terminal ;. Um comando pode ser composto por: uma atribuio, do tipo: lvalue = expr, ou um enunciado simples.
Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

5.2 Gramtica

21

Comentrio importante. Nota-se que o ; no faz parte, sintaticamente falando, do comando. Pelo contrrio, na linguagem denida aqui, ele um separador de comandos. Isso signica que seu parser dever aceitar, eventualmente, um nico comando sem ponto-vrgula no m (ver o exemplo abaixo com o while() {}). Isso tambm signica que no ser sintaticamente correta a presena de um ponto-vrgula nico (sem comandos a separar). lvalue pode ser ou um identicador simples, ou um identicador seguido por [, seguido por uma lista de expr, seguida por um terminal ] nal. Uma lista de expr composta por pelo menos uma expr; se tiver mais de uma, devem ser separadas por ,. Uma expr abrange: duas expr separadas por um dos terminais que representa um operador aritmtico; ou uma expr entre parnteses; ou um literal nico, inteiro ou com vrgula utuante; ou uma lvalue tal como denida acima; ou uma chamada a um procedimento. Uma chamada a procedimento consiste em um identicador, seguido de uma lista de expr (se tiver mais de uma expr na lista, elas devem ser separadas por ,) entre parnteses. Um enunciado simples se limita a uma expr ou a uma instruo de controle. Uma instruo de controle pode ser: IF, seguido de (, seguido de uma expresso booleana, seguida de ), seguido de THEN, seguido de um ou vrios comando(s) (no sentido denido acima), seguido de: Ou o terminal END; Ou o terminal ELSE, seguido por um ou vrios comandos (no sentido denido acima), seguido(s) pelo terminal END. WHILE, seguido de (, seguido de uma expresso booleana, seguida de ), seguido de {, seguido por um ou vrios comandos (no sentido denido acima), seguido(s) por }. Por m, uma expresso booleana denida recursivamente da forma seguinte: pode ser: Ou o terminal TRUE; Ou o terminal FALSE; Ou uma expresso booleana entre parnteses; Ou duas expresses booleanas separadas por um dos operadores lgicos reconhecidos pelo scanner (AND, OR); Ou uma expresso booleana precedida pelo token NOT; Ou duas expr separadas por um dos operadores relacionais (tokens: <, >, LE, GE e EQ). Exemplos de aes: x = 1; f(2-3,x) + tmp1; if ( g(tmp[1,2]*2) <= 10 ) then while ( (x>0) && y!=1 ) { x=0 } end
Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

22

Especicao da Etapa 4

(Nota-se, nas iteraes do while, que elas se limitam a apenas um comando x=0, portanto sem pontovrgula no m.) Exemplo de programa Pico inteiro: i,n : int; x:double x = 2.0; i=0 ; n=10; while (i <= n) { x = x*2 ; i = i+1 } Nota-se a ausncia de ; no m da ltima declarao, bem como no m da ltima instruo do bloco entre { e } do while. O prprio while, sendo um comando por si s, no vem seguido de ;.

5.3

Trabalho a ser entregue

A apresentao ser feita na tera-feira 30 de Setembro s 10h30. No dia anterior ao encontro, ou seja no dia 29 de Setembro, o cdigo fonte deve ser disponibilizado aos tutores at s 23h00, da forma usual. Seu parser chamado pico deve aceitar como argumento na linha de comando o nome do arquivo contendo o input (j era o comportamento do seu scanner). A sada de Pico, na tela, deve ser a seguinte: OKAY no caso que a entrada esteja de acordo com a gramtica; ERROR no caso contrrio. Sua gramtica deve ser diretamente implementada de acordo com a sintaxe do Yacc, dentro de um arquivo chamado pico.y no diretrio src/Etapa4. Encontra-se, no cdigo entregue, um esqueleto de tal arquivo a ser preenchido. Em particular, deve-se especicar os tokens atravs de %token na primeira parte do arquivo pico.y, de forma tal que sejam os mesmos tokens usados por seu scanner da Etapa 2. Pico deve usar o yylex provido por seu scanner da Etapa 2. O mais simples para poder usar e compilar seu parser junto com o scanner da Etapa 2 copiar o arquivo src/Etapa2/scanner.l em src/Etapa4/ e completar o Makele nesse diretrio para obter lex.yy.c conforme na Etapa 2 (obs.: como, dessa vez, o main est includo no cdigo do parser, deve-se deletar (ou simplesmente renomear) o main que se encontra em scanner.l).

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

Chapter 6

Especicao da Etapa 5
A etapa 5 consiste na gerao de cdigo de trs endereos ("TAC") para tratar o caso: da declarao de variveis, inclusive de arrays multi-dimensionais; das expresses aritmticas, inteiras e com virgula utuante; de algumas vericaes semnticas no uso correto de tipos.

6.1

Complementao da Etapa 4

Reaproveitamento de cdigo. possvel1 que o cdigo entregue nas etapas anteriores tenha, por engano na hora de efetuar um "tar", contido implementaes prvias de parte de gramticas, eventualmente com aes semnticas j escritas, ou ainda com estruturas de dados auxiliares (tabela de smbolos, listas, pilhas...). Se o caso, voc tem liberdade para se inspirar dos mesmos, no entanto: (a) no nada bvio que essas implementaes sejam melhores do que suas (em particular, elas foram menos testadas...). E (b) uma vez que so herdadas dos dois ltimos anos, elas podem no se conformar exatamente ao que est especicado neste semestre. Sugerimos conar mais em suas implementaes do que tentar reaproveitar esses eventuais pedaos de programas. Resoluo de parte dos conitos na gramtica. O Yacc possibilita indicar a precedncia e a associatividade de operadores. Por exemplo, pode-se indicar que o parsing de 2+x+3 deve ser efetuado na ordem (2+x)+3 (+ dito associativo esquerda), e que 2+x*3 deve ser analizado como 2+(x*3) (* possui precedncia maior do que +). Essas escolhas possibilitam tirar vrios conitos Shift-reduce que podem ter acontecido em suas gramticas. Para isso, basta inserir as linhas abaixo na primeira parte do arquivo pico.y (por exemplo depois das denio dos tokens): %left %left %left %left %left
1 Um

+ - * / OR AND NOT

grupo nos avisou que aconteceu. No consegui achar estes cdigos perdidos nos tar que j liberamos no Moodle...

24

Especicao da Etapa 5

A ordem das linhas dene a precedncia crescente dos operadores. Os left e right denem a associatividade de cada um. (Em linguagens que possibilitam mltiplas atribuies (x = y = 1), comum incluir uma linha %right = para reduzir na ordem x = (y=1). Pico no autoriza isso.) Nova funo na gramtica. Pede-se acrescentar a gramtica da Etapa 4 com uma nova funo, chamada print, que aceita um argumento nico, que pode ser tanto um nmero inteiro como um nmero com virgula utuante.

6.2

Cdigo TAC a ser gerado

Uma instruo TAC dever ter o formato seguinte: z := x OP y onde x, y e z devero ser endereos na memria e OP um operador especicado abaixo. Em alguns casos, um dos operandos x ou y pode estar vazio. Endereos na memria. Sero representados atravs de um deslocamento a partir de um endereo base, tipicamente o endereo base de um segmento de pilha. Supor-se- que esta base se encontra num registrador SP, e o endereo na memria ser escrito d(SP). Por exemplo, uma varivel que for mapeada para o endereo 16 (Bytes) ter como endereo 16(SP). Um tratamento diferente ser dado s variveis temporrias, as quais devero ser mapeadas atravs de um deslocamento relativo a um registrador diferente, chamado por conveno Rx. Operadores TAC. Usar-se- a sintaxe seguinte para os operadores da linguagem TAC:

1. Operaes aritmticas com nmeros inteiros se chamaro: ADD, SUB, MUL e DIV. Todas tero dois operandos. 2. Operaes aritmticas com nmeros em vrgula utuante se chamaro: FADD, FSUB, FMUL e FDIV. Todas tero dois operandos. 3. O smbolo := ser usado entre o operando z e os dois operandos x e y, a semntica dessa atribuio sendo que os endereos aparecendo direita sero derefenciados (usar-se- o contedo dos endereos das variveis), e que o resultado estar armazenado no endereo encontrado esquerda. Valores numricos aparecendo direita sero considerados como valores imediatos. Assim, z := z+1 signica: consulta a memria no endereo de z, soma o valor nele armazenado com 1, e armazena o valor resultante no endereo de z (ou seja, curto e grosso: faz o que se espera...). 4. PRINT, com um operando nico (sintaxe: PRINT x) 5. FPRINT, com um operando nico (sintaxe: FPRINT x) O cdigo TAC inteiro ser composto por: 1. um cabeote que informar dois nmeros inteiros, cada um numa linha separada, que deniro repectivamente o espao total (em Bytes) na memria necessrio para armazenar as variveis manipuladas pelo programa, e (na segunda linha) os temporrios.
Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

6.3 Cdigo auxilirio para gerao de cdigo TAC

25

2. a seqncia de instrues atmicas, cada qual possuindo um nmero de linha separado da instruo por :. O nmero dever ser formatado para caber em exatamente 3 caracteres, sendo os nmeros menores do que 100 completados esquerda com zeros (ou seja, ir de 000 at 999). O : dever ser seguido por 3 caracteres "espao branco". Os deslocamentos devero ser formatados para caber em exatamente trs caracteres, tambm sendo completados esquerda com zeros se for o caso. Para formatar, em C, essas instrues, recomenda-se usar a funo sprintf. Observa-se que essas cobranas relativas formatao precisam ser cumpridas na hora de imprimir uma listagem do cdigo TAC, e no obrigatoriamente deve se reetir na representao interna do cdigo.

Exemplo de cdigo TAC esperado: 816 16 000: 001: 002: 003: 004: 005:

000(Rx) := 012(SP) MUL 10 004(Rx) := 000(Rx) ADD 008(SP) 008(Rx) := 004(Rx) MUL 4 012(Rx) := 008(Rx)(016(SP)) 000(SP) := 012(Rx) PRINT 000(SP)

(Esse trecho de cdigo poderia representar a compilao de z = A[i,j]; print(z), onde A seria um array bidimensional de inteiros, tendo 10 elementos na dimenso 1 e 20 na dimenso dois (por isso necessitando 1020 4 Bytes), onde z estaria mapeado no endereo 0, i no endereo 8, j no endereo 12 e A teria seu endereo base no endereo 16.) Nota-se que essa linguagem TAC de nvel mais baixo do que a linguagem usada nas aulas. A ns de depurao, aconselha-se programar primeiramente aes semnticas que geram cdigo TAC com variveis "simblicas" (em particular TMP0, TMP1, etc...). Pode-se usar essa primeira verso e a tabela de smbolos, para gerar o cdigo TAC nal numa segunda fase. Opcionalmente, pode-se programar um ag -v do Pico para controlar seu output na forma "TAC alto nvel" ou TAC baixo nvel. Apenas ser exigido o TAC "baixo-nvel".

6.3

Cdigo auxilirio para gerao de cdigo TAC

Ser necessrio implementar pelo menos: 1. Uma representao interna de uma instruo TAC. Por exemplo, pode-se usar uma struct de quatro membros para tal. 2. Uma lista encadeada de instrues TAC, junto com mtodos para: encadear uma nova instruo na lista; concatenar duas listas; varrer e imprimir o contedo da lista sob a forma exigida acima. 3. Um procedimento para gerar "novos temporrios". Nada est imposto sobre essas estruturas de dados, porm ser considerado a qualidade da implementao na avaliao. Aconselha-se fortemente testar de forma unitria essa lista.
Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

26

Especicao da Etapa 5

6.4
6.4.1

Aes semnticas a serem implementadas


Declarao de variveis

Ao efetuar o parsing das declaraes (ver o captulo anterior), deve-se identicar o tipo abstrato de cada varivel e inserir na tabela de smbolos, para cada uma delas, uma entrada que represente de forma abstrata: O lexema associado ao token IDF sendo analizado; O tipo da varivel; O espao na memria que se dever usar para armazenar seu valor; O seu endereo, ou seja o deslocamento a partir do endereo base aonde se encontrar mapeada a varivel; Informaes extras no caso de um array (ler mais adiante). No Pico-2008, tem-se apenas um escopo nico e global para todas as variveis, e por isso se usar apenas uma tabela de smbolos global. Todas as variveis devem ter sido declaradas antes de serem usadas no programa Pico. Uma varivel referenciada sem ter sido declarada previamente dever levar seu compilador a disparar um erro UNDEFINED_SYMBOL Um erro UNDEFINED SYMBOL dever ser provocado, em C/Yacc, da forma seguinte: #define UNDEFINED_SYMBOL_ERROR -21 /* muitas linhas de codigo, pois o define acima esta lah no topo do pico.y */ /* ... */ XXX: YYY { printf(UNDEFINED SYMBOL. A variavel %s nao foi declarada.\n, lexema); return( UNDEFINED_SYMBOL_ERROR ); } (Onde XXX, YYY e o string lexema esto usados de forma genrica, obviamente. Cabe-lhes identicar as aes onde se deve usar tal disparo de erro.) O lexema se encontra, no scanner, na varivel yytext, atualizada pelo scanner com o Flex. Lembra-se que do lado do Yacc, a varivel yylval usada para o atributo. Os tipos bsicos tero o tamanho seguinte: tipo char: 1 Byte; tipo int: 4 Bytes; tipo oat: 4 Bytes; tipo double: 8 Bytes. O deslocamento e tamanho da varivel devero ser calculados medida que o parsing anda, conforme explicado em aula. Atributos semnticos devero ser associados aos smbolos no-terminais e terminais apropriados, com o Yacc, da forma descrita abaixo (e j apresentada em aula).
Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

6.4 Aes semnticas a serem implementadas

27

Arrays. No caso dos arrays, o trabalho de representao do tipo pode ser mais complexo. No Pico, iremos nos limitar ao caso de arrays de tipos simples. A representao de arrays (de arrays)* de arrays2 necessita o uso de um grafo abstrato que no vai ser implementado neste semestre. Para arrays de tipos simples, dever ser reservado o espao na memria igual ao nmero total de elementos no array, multiplicado pelo tamanho do tipo simples (em Bytes). Na tabela de smbolos, uma entrada associada a um array dever, obviamente, informar este tamanho, e tambm conter os valores pre-calculados dos elementos necessrios ao clculo semntico do acesso a um elemento indexado no array (ver a aula sobre arrays multidimensionais). Assim, a struct entry_t preparada na Etapa 1, que continha um campo void* extra, dever ser usada e complementada para que este campo se torne um ponteiro para uma nova struct, com todas as informaes necessrias (por exemplo: a constante c; o nmero de dimenses; os limites inferiores e superiores em cada uma das dimenses; etc. . . ). Caber implementao consultar esses campos, quando h necessidade numa ao semntica. Observa-se que vocs tm uma certa margem de manobra nessa representao interna de seus tipos, em especial no caso dos arrays. Ao terminar a anlise sinttica da declarao de variveis, o tamanho total usado na memria para todas as variveis dever ser armazenado, para depois ser impresso junto com o cdigo TAC no cabealho.

6.4.2

Expresses aritmticas

Todas as aes semnticas necessrias gerao de cdigo para expresses aritmticas devem ser implementadas. Entende-se, nessa etapa, por expresso aritmtica, todas as derivaes possveis, tais como denidas na Etapa 4, a partir de uma atribuio do tipo lvalue = expr (ver Sec. 5.2.2, p. 20), SENDO EXCLUDAS as derivaes de expr em chamada a um procedimento. Informalmente, isso signica que se espera qualquer combinao de operadores aritmticos atuando em identicadores ou arrays, os quais podem aparecer tanto esquerda como direita de um sinal de atribuio. No necessrio alterar sua gramtica basta implementar as aes semnticas relativas s produes que dizem respeito sub-gramtica tratada nessa Etapa. Outras viro nas Etapas 6 e 7. O fato de incluir os identicadores nas expresses, assim como os arrays, signica que se dever efetuar as aes semnticas relevantes de consulta tabela de smbolos, ao encontrar um IDF, para vericar se foi declarado, e se sim recuperar as informaes necessrias gerao de cdigo (se relevante). No caso de uma expresso derivar em um terminal F_INT ou INT_LIT, deve-se usar o prprio lexema na instruo TAC gerada. Por exemplo, ao compilar z = y + 3.14e-1, o cdigo TAC gerado ir conter uma linha com 3.14e-2 explicitamente escrito (numa instruo ADD).

6.4.3

Instruo print

Deve-se gerar cdigo TAC, atravs de aes semnticas, para o nico caso de chamada a procedimento que a chamada a print. A(s) ao(es) semntica(s) dever(o) efetuar checagem do tipo do argumento para gerar o cdigo apropriado em funo do nmero.

6.4.4

Checagem de tipos

Conforme especicado acima, a linguagem TAC diferencia operaes em nmeros inteiros das operaes em vrgula utuante. Isso signica que voc dever incluir em sua anlise semntica a determinao do tipo dos operandos de um operador, para poder:
2 Para

quem no entendeu o asterisco, isso era uma expresso regular. . .

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

28 1. Vericar a compatilidade entre os tipos dos operandos e o operador; 2. Gerar o cdigo apropriado ao tipo dos operandos.

Especicao da Etapa 5

Sobre o primeiro ponto, deve ser previsto que apenas nmeros podem ser alvo de um operador aritmtico e de um print. Por exemplo, uma tentativa de somar um char com um int deve levar a um erro (TYPE_MISMATCH_ERROR). Faz parte do trabalho formalizar o que ser um nmero (em nvel semntico)3 . Um TYPE_MISMATCH_ERROR dever ser provocado, em C/Yacc, da forma seguinte: #define TYPE_MISMATCH_ERROR -20 /* muitas linhas de codigo, pois o define acima esta lah no topo do pico.y */ /* ... */ expr: uma coisa MUITO complexa { printf(ERRO DE COMPATIBILIDADE DE TIPO. Quem te ensinou a programar?\n); return( TYPE_MISMATCH_ERROR ); } No que diz respeito gerao de cdigo, deve ser previsto: 1. que uma expresso ser considerada como inteira se todos seus operandos possuem o tipo int. 2. que to logo um operador aritmtico ser aplicado a pelo menos um nmero em vrgula utuante, dever ser criada uma varivel temporria de tipo float, qual ser atribudo o valor do eventual operando inteiro. O operador ser, ento, aplicado ao temporrio e ao operando no-inteiro. 3. Que o acesso a um elemento de um array multidimensional s pode ser efetuado pelo uso de uma lista de expresses cujo tipo seja inteiro. Qualquer outro caso deve levar ao erro TYPE_MISMATCH_ERROR. Exemplos disso: k: double x = a[1,3.14]; b[k] = 0 Neste exemplo, tm dois erros semnticos. 4. Que a instruo TAC a ser gerada ao parsar print deve ser diferenciada em funo do argumento ser inteiro ou com vrgula utuante.

6.5

Atributos no Yacc

(Consultar tambm a apostilha Yacc disponbilizada no Moodle, em baixo direita da pgina.) Os atributos semnticos no Yacc tm tipo inteiro por default. Para qualquer regra genrica na gramtica: L: A B C D E ; O atributo associado ao smbolo de esquerda (L) se chama $$, o atributo do primeiro smbolo (no-terminal ou terminal) esquerda (A) se chama $1, o seguinte $2 (no caso para B), etc. Por exemplo,
3 Essa frase pode parecer ameaadora, mas apenas signica que vocs devem implementar aes semnticas (simples) nas regras que lhes parecem manipular nmeros.

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

6.5 Atributos no Yacc L: A B C D E ; { $$ = $3+$5; }

29

levar a sintetizar o atributo de L como sendo igual soma dos atributos de C e E. No caso, pode-se efetuar a soma, pois todos os atributos tm o tipo default inteiro. Outro default importante que para uma regra do tipo: L: R; que se encontra frequentemente, h implicitamente a ao semntica {$$ = $1}. Toda a diculdade vem do fato de se querer usar atributos no-inteiros, e eventualmente mais de um atributo por smbolo. Isso se faz da forma seguinte. Vamos supor que se queira usar dois atributos para um smbolo no-terminal N, um chamado size, de tipo int; e um chamado type, de tipo void*. E vamos supor que um outro smbolo T, terminal dessa vez (ou seja: um token), v precisar de um atributo de tipo char*, chamado name. Declara-se esses atributos assim: %union { struct { int len ; void* type ; } abstract_att; char* name; } %token<name> T %type<abstract_att> N A union possibilita a descrio dos tipos de atributos que sero usados, e sua associao a um nome (abstract_att e name). Esses nomes (de tipo de atributos) so usados, opcionalmente, na instruo %token j encontrada, e na instruo %type, para especicar um tipo no inteiro do atributo $x de um smbolo token ou no-terminal. Mais adiante, o usurio pode ento usar esses atributos, por exemplo da forma seguinte na regra totalmente imaginria: N: T N ; T ; { $$.type = $1; $$.len = strlen($4); }

Que faz essa regra? Em primeiro lugar, observa-se que como $$ o atributo de N, ele de tipo struct { int len ; void* type ; }. Por isso, pode-se acessar seus campos type (void*) e len (int). Da mesma forma, uma vez que T tem atributo de tipo char*, tanto $1 (atributo do primeiro smbolo direita que T) como $4 (observa-se que $3 o atributo de ;) possuem tipo char*. Assim sendo, pode-se calcular strlen($4), ou usar cpia entre ponteiros para escrever $.type = $1.

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

30

Especicao da Etapa 5

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

Chapter 7

Especicao da Etapa 6
A etapa 6 consiste na gerao de cdigo de trs endereos ("TAC") para tratar o caso: do comando if... then... else; do comando while.

7.1

Precises sobre as Etapas 5 e 6

Nessa seo, uma srie de precises so dadas a respeito do Pico. Nmeros negativos. A especicao dos tokens que representam nmeros (Etapa 2) no inclui nmeros negativos. Isso foi proposital para evitar ambigidades entre o menos unrio (3.14) e o menos que denota a subtrao (1.0 3.14). Existe uma forma de resolver tal ambigidade com o Yacc (basta aumentar a prioridade do menos unrio em comparao prioridade do menos binrio). Como no se admite, em nvel lexical, nmero negativo, tal problema no tratado no Pico. Invocao do Pico. A partir da Etapa 6, pico -o <filename> <input>.pico, onde: impe-se o uso seguinte de Pico:

<input>.pico pode ser qualquer nome de arquivo que contenha cdigo fonte Pico. A extenso .pico obrigatrio e Pico no deve aceitar compilar um arquivo de entrada que tenha outra extenso. <filename> o nome do arquivo de sada, que deve conter cdigo TAC vlido, e apenas cdigo TAC. Este nome pode ser qualquer nome de arquivo vlido, com qualquer extenso. Deve conter apenas TAC vlido, ou seja no deve constar nele linhas tipo "OKAY", ou "ERROR". Este arquivo de sada ser compilado de TAC para linguagem de montador, por isso deve ser rigorosamente de acordo com a sntaxe TAC denida na Etapa 5. Exemplos de uso: pico -o test1.tac test1.pico pico -o output input.pico pico -o resultado.txt prog_1_.pico

32

Especicao da Etapa 6

7.2
7.2.1

Aes semnticas
Expresses booleanas

A avaliao das expresses booleanas, necessrias para o controle das instrues if e while, dever ser feita com cdigo do tipo "curto-circuito" e levar ao valor correto dos atributos falso e verda dessas expresses. Para isso, voc ir precisar de um novo procedimento chamado gera_rotulo() que ir gerar a cada chamada um novo rtulo simblico; um tal rtulo ser uma seqncia de caracteres quaisquer, seguidos por dois pontos (:). Por exemplo, gera_rotulo pode retornar sucessivamente: label0:, label1:, label2:, etc... Para as aes semnticas necessrias, ver a aula do dia 09 de Outubro.

7.2.2

Comandos de controle de uxo

Voc dever usar as aes semnticas que foram apresentadas na aula do dia 09 de outubro, com os devidos atributos herdados para o clculo dos atributos dos smbolos relativos s expresses booleanas. Para o uso de atributos herdados com o Yacc, lembra-se que a sesso de laboratrio da tera-feira 07 de Outubro incluiu material sobre o uso de uma pilha de atributos.

7.3

Cdigo TAC

O cdigo TAC produzido na Etapa 6 deve ser idntico ao da Etapa 5, extendido com os seguintes comandos: LABEL. <label>: onde <label> um identicador de rtulo vlido. Um indicador de rtulo vlido comea com uma letra (obrigatoriamente maicula) seguida ou no de qualquer combinao de letras maisculas, dgitos e underscores. Esse comando indica que a parte do cdigo subseqente est endereada atravs de um comando GOTO. Exemplo de identicador de rtulo: MEU_LABEL_2: Atente que no deve haver nmero da linha ou espacos na instruo. GOTO. A instruo GOTO: xxx: GOTO <label>

onde xxx: um nmero de linha qualquer (como a Etapa 5, deve ser suscedido de exatamente 3 espaos) e <label> um rtulo denido como acima. Esse comando ajusta o uxo de instrues para que passe a ser executado logo aps (na linha seguinte a) um rtulo. e.g., 000: GOTO COMECA_AQUI 001: 004(Rx) := 000(Rx) ADD 008(SP) COMECA_AQUI: 002: 008(Rx) := 006(Rx) ADD 4
Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

7.3 Cdigo TAC nesse exemplo, a linha 001 nunca executada.

33

Opcionalmente, o comando GOTO pode referenciar explicitamente uma linha no cdigo TAC, dispensando a denio do rtulo. Nesse caso, o nmero da linha deve ser precedido de um underscore. O seguinte cdigo faz o mesmo que o anterior: 000: 001: 002: GOTO _002 004(Rx) := 000(Rx) ADD 008(SP) 008(Rx) := 006(Rx) ADD 4

(Observa-se que, atravs de um GOTO _001, se pode executar a linha 001 anal.) IF. O comando IF implementado como abaixo. Repare que o nmero de espaos entre os componentes deve ser respeitado (como sempre)! xxx: onde: xxx um nmero de linha qualquer. op1 um operando, que pode ser ou um nmero em valor absoluto ou um nmero armazenado na memria (como a Etapa 5, com referncia aos registradores Rx e SP). comp um comparador booleano. So admitidos os seguintes comparadores: < > <= >= == != op2 como op1. label como o rtulo denido para a instruo GOTO (aceitando, inclusive, o nmero da linha, precedido por um underscore, conforme explicado anteriormente). e.g., 000: IF 4 <= 008(SP) GOTO MEU_LABEL 001: 004(Rx) := 000(Rx) ADD 008(SP) MEU_LABEL: 002: 008(Rx) := 006(Rx) ADD 4 executa a linha 001 apenas quando a comparao da linha 000 resultar em verdadeiro. IF <op1> <comp> <op2> GOTO <label>

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

34

Especicao da Etapa 6

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

Chapter 8

Especicao da Etapa 7
Nessa etapa, voc deve desenvolver um programa que traduz o cdigo TAC em cdigo assembly x86. O mesmo ser montado e executado.

8.1

TAC vs. Assembly x86

A converso de cdigo TAC em assembly se faz como segue.

8.1.1

Pr-Requisitos

Para transformar o programa gerado em cdigo de mquina, necessrio um ambiente Linux com os seguintes programas instalados e funcionais: 1. GNU Assembler (as) 2. GNU Linker (ld) 3. GNU libc (glibc) em geral, a maioria das distros Linux (e.g., Ubuntu) vm com esses programas instalados.

8.1.2

O Script a ser programado

O trabalho desta Etapa consiste em construir um script chamado pias PIco ASsembler que, dado um cdigo TAC gere o correspondente assembly x86. O seu script dever ser invocado da seguinte maneira, em bash script: pias <nome_arquivo.tac> Essa chamada dever fazer o seguinte: 1. Chamar um tradutor em cima do arquivo passado como parmetro, que transforma o cdigo TAC em um cdigo assembly. O arquivo resultante deve se chamar output.s 2. Chamar o GNU Assembler sobre o arquivo output.s, resultando no arquivo output.o, atravs do comando seguinte:

36 as output.s -o output.o

Especicao da Etapa 7

3. Chamar o GNU Linker sobre o arquivo output.o, gerando como sada o arquivo output, que um executvel vlido. Deve-se ligar o cdigo glibc, pra que se possa usar a funo printf dentro do cdigo assembly. Isso se faz atravs do comando: ld -dynamic-linker /lib/ld-linux.so.2 -o output -lc output.o

(O diretrio /lib/ld-linux.so.2 a localizao da glibc para o Ubuntu, podendo variar para outras distribuies. Consulte a documentao prpria.) Basicamente, isso signica que seu script pias ser composto por trs linhas, cada qual efetuando uma das etapas acima descritas. A real complexidade da tarefa ser implementar a etapa (1) (tradutor), conforme descrito a seguir.

8.1.3

Implementao do Tradutor

Um tradutor um analisador de expresses regulares que ir ler uma linha de instruo TAC (no formato descrito nas etapas anteriores) e transform-la em algumas linhas assembly x86. No necessrio um Compilador (anlise sinttica e semntica), pois a especicao do TAC foi feita em cima de uma Linguagem Regular, propositadamente. O tradutor pode ser implementado da maneira que o aluno preferir, escolhendo, inclusive, em que linguagem deseja implementar. A nica restrio que ele deve funcionar em alguma das mquinas do laboratrio usado para as aulas prticas (veriquem!). Pode-se usar o (F)L EX, por exemplo. Entretanto, salienta-se que ser provavelmente mais simples programar o tradutor com uma linguagem como Java, Python, Perl ou Ruby do que com o (F)L EX. Para facilitar a tarefa, um arquivo (model.template) de modelo ser fornecido. Esse arquivo j est pronto para ser processado pelo as, faltando apenas preencher as seguintes lacunas: 1. <stack_size> : o tamanho da pilha. o nmero da primeira linha do cdigo TAC. 2. <temp_size> : tamanho reservado s variveis temporrias. o nmero na segunda linha do cdigo TAC. 3. <code> : o cdigo assembly equivalent ao cdigo TAC. Recomenda-se copiar o template linha-a-linha para o arquivo output.s, substituindo nele as marcaes <stack_size>, <temp_size> e <code> com os dados requeridos, obtidos pela anlise do TAC. Para a gerao de cdigo assembly, ser fornecida documentao de todos os programas usados, bem como a linguagem assembly x86 usado no montador as. O aluno deve relembrar a arquitetura usada pelos processadores Intel (registradores, endereamento, etc.) e pode usar esses documentos para tal. Faz parte da atividade pesquisar sobre a arquitetura-alvo, como faria um projetista do compilador. Uma srie de exemplos de converses ser fornecido para ajudar essa fase, na forma de uma par de arquivos, um com o cdigo TAC e outro com o respectivo assembly gerado. Geralmente, uma instruo TAC convertida para n > 1 instrues assembly, pois h necessidade de salvar registradores, us-los e restaurar seu contedo. e.g., 003: 012(Rx) := 008(Rx) ADD 016(SP)
Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

8.2 Data de entrega convertida em _003: MOVL ADDL MOVL 8(%EDX) , %EAX 16(%ECX) , %EAX %EAX ,12(%EDX)

37

A instruo TAC print ser implementada atravs de uma chamada ao procedimento printf da linguagem C da seguinte maneira: 005: PRINT 004(SP)

convertida em _005: PUSHL 4(%ECX) PUSHL $intf CALL printf seu trabalho ser apenas traduzir 004(SP) em 4(%ECX), nesse caso. Para saber que registradores da arquitetura correspondem a SP e Rx, veja o arquivo template.model. A converso do nmero de linha TAC para o cdigo assembly como mostrado anteriormente. No ser cobrada indentao para o cdigo assembly. O cerne da avaliao ser o executvel gerado e sua correspondncia com a linguagem processada pelo pico. Isso no signica, entretanto, que o cdigo gerado no ser avaliado.

8.1.4

Limitaes & Facilidades

O emprego de nmeros em ponto utuante no ser cobrado nessa etapa, tratando apenas operaes com nmeros inteiros. A avaliao de expresses booleanas tambm no precisar ser curto-circuito.

8.2

Data de entrega

A etapa 7 dever ser entregue at o dia 01 de Dezembro, 23h00. O script pias e os demais executveis devero se encontrar no diretrio Pico/src/Etapa7.

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

38

Especicao da Etapa 7

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

Chapter 9

Avaliao
A avaliao se far em duas etapas. Primeiramente, seu programa ir ser testado de forma automatizada: programas C pre-escritos iro ser compilados junto com suas implementaes de tabela de smbolos e de pilha, e executar operaes usuais nelas para vericar sua conformidade especicao (exemplos de tais testes sero providos nessa semana). A conformidade ir levar a 50% da nota nal obtida nessa etapa 1. Salienta-se que, nessa fase, programas de vericao de cpia e programas de busca por cdigo on-line sero usados para detectar eventuais fraudes. A outra metade da nota ser atribuda em funo da apresentao informal que vocs faro em laboratrio, no dia 25 de Setembro, ao seu tutor: resultados dos testes, discusso da implementao, validao atravs de outros testes, documentao. Todos os trs alunos podem ser indagados sobre qualquer parte da implementao, at mesmo quem no programou uma funcionalidade: espera-se que todos os integrantes tenham entendido como tudo foi programado. Opcionalmente, o tutor poder aplicar um questionrio rpido, e escrito, para testar o conhecimento do programa entregue. Qualquer tentativa, mesmo parcial, de reaproveitamento de cdigo de colegas ou da Web, ir zerar a nota.

40

Avaliao

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

Chapter 10

Anexo tcnico: Doxygen e Make


10.1 Introduo ao D OXYGEN

Todo o contedo deste tutorial foi retirado de http://www.stack.nl/~dimitri/doxygen/ manual.html. Recomenda-se consultar a fonte para mais detalhes. O que segue so as notaes recomendadas para o projeto da disciplina de Compiladores (INF01147/INF01033) 2008/2. Gerador de documentao para cdigo-fonte, o D OXYGEN um sistema de documentao para C++, C, Java, Objective-C, Python, etc. Dentre outras capacidades, o D OXYGEN pode gerar um sistema de documentao on-line (em HTML) e/ou A um manual de referncia off-line (em LTEX) de um conjunto de cdigos-fonte propriamente documentados. Tambm h suporte para gerar a sada em RTF (MS-Word), PostScript, PDF com hyperlinks, HTML compactado e pginas man do UNIX. A documentao extrada diretamente dos cdigos-fonte, tornando mais fcil a manuteno da documentao. Disponvel para Windows, Linux e MacOS-X.

10.1.1

Comandos Bsicos em C

Existem muitos mtodos diferentes para escrever um comentrio no estilo D OXYGEN . Listados, a seguir, os mais comuns. 10.1.1.1 Blocos de Comentrios Simples

Comentrios simples na sintaxe da linguagem podem ser colocados normalmente. Por padro, o D OXYGEN no gerar estes comentrios na sada, ignorando sua presena. So necessrios identicadores especiais para os comandos D OXYGEN . /* comentario simples */ 10.1.1.2 Blocos de Comentrios Breves

Um dos modos em que o D OXYGEN pode gerar comentrios, estes so produzidos na sada; devem possuir apenas uma linha, so usadas para consideraes pontuais importantes (como a descrio do que faz um lao for complexo) e sua sintaxe a seguinte: /** comentario breve */

42 Tambm pode ser obtido atravs da tag \brief: /* \brief comentario breve */ 10.1.1.3 Blocos de Comentrios Detalhados

Anexo tcnico: Doxygen e Make

Um dos modos em que o D OXYGEN pode gerar comentrios, estes so produzidos na sada; usados para descries detalhadas de blocos dos arquivos, em vrias linhas (como o header do cdigo fonte ou cabealho de funo). Sua sintaxe a seguinte: /** * comentario detalhado * */ 10.1.1.4 Tags Especiais

Algumas tags (marcaes especiais do cdigo que aparecem an documentao nal) so de especial uso neste projeto: \author : nome autor do autor do cdigo em questo (vrios autores de vrios author sero agrupados no mesmo pargrafo). \version : verso atual do cdigo (e.g., nmero da etapa do projeto). \date : data da primeira e ltima modicao no cdigo. \bug : descreve possveis bugs que a aquele trecho de cdigo apresenta. \warning : aviso no uso daquele trecho de cdigo. \param : identica o parmetro de uma funo. Deve ser usado com as palavras-chave in e out para indicar o tipo de atributo (entrada ou sada) e uma breve descrio. \return : valor de retorno de uma funo. \le : identica o arquivo (nome dele no sistema de arquivos) ao qual o comentrio se refere. Em geral o nome do arquivo de cdigo-fonte e aparece apenas no header. Por exemplo, /** * \file minhas_funcoes.c * \brief Arquivo com as minhas funcoes. */ /** * * * * * * * MinhaFuncao \brief Copia sequencias de bytes. \author Bill Gates \author Linus Torvalds \version 4.0 \date 1996-1998 \bug Trava muito e requer muita memoria. \bug A medida que eh usada introduz mais bugs.
Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

10.2 Introduo ao Make * \warning Nao confie nesta funcao. * \warning Nem neste comentario. * * Nao faz nada e soh consome memoria. * \param[out] dest Area de destino. * \param[in] src Area de origem. Numero de bytes. * \param[in] n \return Nada. * */ void minha_funcao(void *dest, const void *src, size_t n); 10.1.1.5 Uso

43

O D OXYGEN consegue determinar a que bloco de cdigo um comentrio se refere. Isso signica que blocos que antecedem uma funo referem-se funo e blocos que antecedem todo o arquivo so o header do arquivo na documentao. Neste projeto, trs blocos so o suciente para que o arquivo seja considerado documentado: o header, o descritor da funo e comentrios de trechos importantes de cdigo. Em anexo, segue um exemplo.

10.2
10.2.1

Introduo ao Make
Introduo

Make um programa GNU linux que automatiza o procedimento de criao e manuteno de grupos de programas, vericando dependncias e possibilitando a compilao de cdigos. O objetivo de make determinar automaticamente as partes de um programa relativamente grande que precisam ser recompiladas, provendo os comandos necessrios para tal nalidade. Com make podem ser descritas muitas tarefas onde arquivos precisam ser atualizados automaticamente a partir da modicao de outros. Em um programa, o arquivo executvel obtido a partir de arquivos objeto, os quais so oriundos da compilao de arquivos fontes. Com apenas uma nica chamada de make, todas as mudanas realizadas em arquivos fonte podem ser recompiladas, uma vez que make se baseia nos tempos das ltimas modicaes do arquivo. Caso no haja nenhuma alterao, no ser necessria a compilao, no havendo a necessidade de recompilao de todo cdigo

10.2.2

Composio de um arquivo makele

Para usar make, necessrio criar um arquivo chamado makele que descreve as relaes entre os arquivos do programa e os estados dos comandos para a atualizao de cada arquivo. Em um arquivo makele podem ser utilizados labels (rtulos) e targets (alvos), de maneira que na chamada de make possa haver o reaproveitamento de nomes, bem como a invocao de diferentes alvos. Tambm possvel vericar dependncias. Por exemplo, se o arquivo makele tiver a seguinte especicao: FILENAME=fonte FLAGS= -g -Wall compilar: ${CC} -o ${FILENAME} ${FILENAME}.c ${FLAGS} executar: ${FILENAME} ./${FILENAME}
Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

44

Anexo tcnico: Doxygen e Make

apagar: rm ${FILENAME}.o rm ${FILENAME} clear Tem-se um label, F ILEN AM E, o qual est referenciando a palavra "fonte", sendo usado em diferentes partes do script como uma "varivel" e a label FLAGS, o qual invocada apenas em uma das targets Ao mesmo tempo, tm-se trs targets que podem ser chamados atravs de: nome@maquina:/home/usuario: make compilar nome@maquina:/home/usuario: make executar nome@maquina:/home/usuario: make apagar No primeiro caso haver a compilao do cdigo utilizando o compilador gcc. Este invocado atravs de uma varivel externa. J as ags de documentao esto referenciadas em FLAGS. No segundo caso a target verica a existncia do cdigo executvel (repare que isto feito logo aps os dois pontos) e se isto for verdadeiro ele executa o cdigo. J no terceiro caso haver a execuo de trs comandos: a excluso do arquivo objeto, a excluso do arquivo executvel e a "limpeza" do terminal. Para o acesso ao contedo de um label basta utilizar $ antes do label, sendo esta colocada entre chaves. Outra observao importante de que os comandos precisam ser colocados a partir da linha seguinte de target, iniciando sempre com um tab. atravs da invocao dos targets que se pode executar uma srie de comandos prticos, simplicando o processo de compilao.

10.2.3

Maiores Referncias

Sugere-se a consulta de: man make http : //www.gnu.org/sof tware/make http : //www.gnu.org/sof tware/make/manual/make.html

Gerado em Mon Aug 4 21:56:27 2008 para Pico por Doxygen

Vous aimerez peut-être aussi