Вы находитесь на странице: 1из 240

Prof. Dr.

Wagner Jos Dizer


wagner@unilins.edu.br

Anlise lxica (scanner). Anlise sinttica (parser). Anlise Semntica. Recuperao de erros. Aspectos e ferramentas para construo de compiladores. Sintaxe abstrata. Anlise de escopo checagem de tipos. Registros de ativao. Traduo para cdigo intermedirio. Gerao de cdigo. Otimizao.

Livro Texto:
1.

Implementao de Linguagens de Programao: Compiladores; Toscani, Simo S. & Price, Ana


Maria de Alencar; Ed. Bookman; 3 edio; 2008; 5 exemplares. 2007; 5 exemplares.

2.

Compiladores: Princpios, Tcnicas, e Ferramentas; Aho, Alfred et al.; Ed. Pearson; 2 edio; Compiladores: Princpios e Prticas; Louden, Kenneth C.; Ed. Thomson Pioneira; 1 edio;

3.

2004; 5 exemplares.

Bibliografia Complementar:
4.

Linguagens Formais: Teoria, Modelagem e Implementao; Ramos, Marcus V. M., Neto, Joo Jos; & Veja, talo Santiago; Ed. Bookman; 1 edio; 2009; 10 exemplares. Fundamentos Matemticos para a Cincia da Computao; Gersting, Judith L.; Ed. LTC; 5
edio; 2004; 17 exemplares. exemplares.

5.

6.

Linguagens Formais e Autmatos; Menezes, Paulo F. B.; Ed. Sagra Luzzato; 4 edio; 2002; 9 Elements of the Theory of Computation; Lewis, Harry R.; Ed. Prentice-Hall; 2 edio; 1998; 1 Introduction to Formal Language Theory; Harrison, Michael A.; Ed. Addison-Wesley; 1 edio;

7.

exemplar.
8.

1978; 1 exemplar.

Linguagens de Programao Tradutores


Compilador Interpretador

Fases de um compilador Anlise


Analisador Lxico Analisador Sinttico Analisador Semntico

Sntese
Gerao de cdigo intermedirio Otimizao de cdigo Gerao de cdigo objeto

Linguagens de programao so notaes para se descrever algoritmos computacionais. Os computadores, conforme os conhecemos hoje, dependem de linguagens de programao, pois todo software escrito em alguma linguagem de programao. Mas, antes que possa rodar, um programa primeiro precisa ser traduzido para um formato que lhe permita ser executado pelo computador (linguagem de mquina). Para diminuir as diferenas entre a linguagem de mquina e a linguagem natural que surgiu a linguagem de programao.

Atravs das linguagens de programao, consegue-se:


Facilidades para se escrever programas Diminuio do problema de diferenas entre mquina Facilidade de depurao e manuteno de programas Melhoria da interface homem/mquina Reduo no custo e tempo necessrio para o desenvolvimento de programas Aumento da compatibilidade e modularidade

Tradutores so programas capazes de converter um cdigo geralmente de alto nvel para um cdigo que o computador interprete. Em outras palavras, um tradutor tem a funo de traduzir uma linguagem abstrata para uma linguagem binria. Em geral, os tradutores so programas bastante complexos. H 2 tipos principais de tradutores:
Compiladores Interpretadores

Um compilador um programa que recebe como entrada um programa escrito na linguagemlinguagem-fonte e produz um programa equivalente na linguagemlinguagem-alvo. alvo Geralmente, a linguagem-fonte (cdigo cdigocdigo-fonte) fonte uma linguagem de alto nvel, como C ou Java, e a linguagem-alvo um cdigocdigo-objeto (cdigo de mquina ou programa executvel). Compilador um dos tipos de tradutor mais utilizados. Exemplo: Pascal, C, C++

Vantagens:
O cdigo compilado mais rpido Impossibilita (ou pelo menos dificulta) ser quebrado e visualizado o cdigo-fonte original Permite otimizao do cdigo por parte do compilador Compila o cdigo somente se estiver sem nenhum erro

Desvantagens:
Para ser utilizado o cdigo precisa passar por muitos nveis de compilao Assim como vantagem, a possibilidade de no poder visualizar o cdigo-fonte pode ser uma desvantagem Processo de correo ou alterao do cdigo requer que ele seja novamente recompilado

O interpretador, ao contrrio do compilador, roda o cdigo-fonte escrito como sendo o cdigo objeto. Ele traduz o programa linha a linha, e o programa vai sendo utilizado na medida em que vai sendo traduzido. Cada execuo do programa precisa ser novamente traduzido e interpretado. Exemplo: PHP, VB

Vantagens:
Correes e alteraes so mais rpidas de serem realizadas Cdigo no precisa ser compilado para ser executado Consomem menos memria

Desvantagens:
A execuo do programa mais lenta Necessita sempre ser lido o cdigo original para ser executado

Programa que transforma uma linguagem de alto nvel em linguagem de mquina. Basicamente, um compilador um programa de computador escrito em uma linguagem L, cuja finalidade converter um programa PF, denominado programa-fonte, para um programa-objeto PO, o qual ser executado em uma mquina M. Em outras palavras: um programa usado para gerar programas.

Programa Fonte

COMPILADOR

Erros

Programa Objeto

A construo de compiladores engloba vrias reas desde teoria de linguagens de programao at engenharia de software,

passando por arquitetura de mquina, sistemas operacionais e algoritmos. Algumas


tcnicas bsicas de construo de compiladores podem ser usadas na construo de ferramentas variadas para o processamento de linguagens, como por exemplo:

Compiladores para linguagens de programao:


um compilador traduz um programa escrito numa linguagem fonte em um programa escrito em uma linguagem objeto;

Formatadores de texto:
um formatador de texto manipula um conjunto de caracteres composto pelo documento a ser formatado e por comandos de formatao (pargrafos, figuras, frmulas matemticas, negrito, itlico, etc). So exemplos de textos formatados os documentos escritos em editores convencionais, os documentos escritos em HTML (HyperText Markup Language), os documentos escritos em Latex, etc.;

Interpretadores de queries (consultas a banco de dados):


um interpretador de queries traduz uma query composta por operadores lgicos ou relacionais em comandos para percorrer um banco de dados.

Independente da linguagem a ser traduzida ou do programa objeto a ser gerado, os compiladores, de um modo geral, compem-se de funes padronizadas, que compreendem a anlise do programa fonte e a posterior sntese para a derivao do cdigo objeto.

Programa Fonte
COMPILADOR

ANLISE

TABELAS

Representao Intermediria

Erros

SNTESE

Programa Objeto

Analisador Lxico ANLISE Analisador Sinttico Analisador Semntico Gerador de cdigo intermedirio Otimizador de cdigo Gerador de Cdigo-objeto

Tratamento de Erros

SNTESE

Tabela de Smbolos

O processo de compilao comumente estruturado em fases. Na prtica, sob o ponto de vista de implementao, podem no estar separados em mdulos especficos. Fase - Procedimento que realiza uma funo bem definida no processo de compilao. Passo - Passagem completa do programa compilador sobre o programa fonte que est sendo compilado.

Velocidade
possvel vantagem para um passo

Espao
possvel vantagem para um passo (dados x programa)

Modularidade
vantagem de mltiplos passos

Flexibilidade
vantagem de mltiplos passos

Transformaes/otimizaes de programas
vantagem de mltiplos passos

O objetivo principal desta fase identificar seqncias de caracteres que constituem unidades lxicas (tokens). O analisador lxico l, caractere a caractere, o texto fonte, verificando se os caracteres lidos pertencem ao alfabeto da linguagem, identificando tokens, e desprezando comentrios e brancos desnecessrios. Os tokens constituem classes de smbolos tais como palavras reservadas, delimitadores, identificadores, etc.

Por exemplo, considere a linha de cdigo: posicao := inicial + taxa * 60 Cada token composto por um ou mais caracteres. Esse cdigo contm 23 caracteres diferentes de espao, mas somente 7 tokens. Aps a anlise lxica, temos:
id 1 atrib := id 2 op + id 3 op * cte 60

TABELA DE SMBOLOS

posicao inicial taxa

1 2 3

A segunda fase do compilador a anlise sinttica. O analisador sinttico utiliza os tokens produzidos pelo analisador lxico para criar uma representao intermediria tipo rvore, que mostra a estrutura gramatical da seqncias de tokens. := <id,1> <id,2> <id,3> + * 60

O analisador semntico utiliza a rvore de sintaxe e as informaes da tabela de smbolos para verificar a consistncia semntica do programa fonte. Uma parte importante da anlise semntica a verificao de tipo, em que o compilador verifica se cada operador possui operandos compatveis. := <id,1> <id,2> <id,3> + * inttofloat 60

Depois das anlises sinttica e semntica do programa fonte, muitos compiladores geram uma representao intermediria explcita de baixo nvel, que podemos imaginar como uma representao para uma mquina abstrata.
t1 = inttofloat(60) t2 = id3 * t1 t3 = id2 + t2 id1 = t3

A fase de otimizao de cdigo, independente da arquitetura de mquina, faz algumas transformaes no cdigo intermedirio com o objetivo de produzir um cdigo objeto melhor. Normalmente, melhor significa mais rpido, mas tambm pode ser desejado um cdigo menor ou que consuma menos energia.

t1 = id3 * 60.0 id1 = id2 + t1

O gerador de cdigo recebe como entrada uma representao intermediria de programa fonte e o mapeia em uma linguagem objeto, de acordo com a arquitetura destinada. Por exemplo, usando os registradores R1 e R2, o cdigo intermedirio poderia ser traduzido para o seguinte cdigo de mquina:
LDF MULF LDF ADDF STF R2, R2, R1, R1, id1, id3 R2, id2 R1, R1 #60.0 R2

Uma funo essencial de um compilador registrar os nomes de variveis usados no programa fonte e coletar informaes sobre os diversos atributos de cada nome. Esses atributos podem prover informaes sobre o espao de memria alocado para um nome, seu tipo, seu escopo. No caso de funes, pode-se armazenar informaes sobre os argumentos, o tipo retornado e o mtodo de passagem de parmetro. A tabela de smbolos deve ser projetada como uma estrutura de dados que permita ao compilador encontrar rapidamente o registro para cada nome.

Existem vrios modos de se organizar e acessar as tabelas de smbolos sendo, os mais comuns:
1. Listas lineares - o mecanismo mais simples, mas seu desempenho pobre quando o nmero de consultas elevado. 2. rvores binrias 3. Tabelas hash - tm melhor desempenho, mas exigem mais memria e esforo de programao.

Esse mdulo de por objetivo tratar os erros que so detectados em todas as fases de anlise de programa fonte. Qualquer fase analtica deve prosseguir, ainda que erros tenham sido detectados. Cada fase de um compilador requer um tratamento de erros ligeiramente diferente. Num programa, podem ocorrer erros estticos (em tempo de compilao) e erros de execuo.

Poupar esforo ao programador


enviar mensagens claras e completas tentar corrigir o erro, ou pelo menos tentar isol-lo recuperando o resto do texto

Detectar o erro, o mais cedo possvel


no propagar o erro fase seguinte assegurar uma perda mnima de texto confiana mxima na correo efetuada

Evitar mensagens em cascata


erros assinalados em partes corretas do programa

No degradar a eficincia
tempo gasto na anlise de texto correto

Procurar solues genricas


solues que possam ser descritas formalmente de modo a serem geradas automaticamente.

Erros lxicos
erro de grafia, caracteres no previstos, sequncias invlidas, EOF durante o reconhecimento de um smbolo,

Erros sintticos
sequncias invlidas de smbolos, expresso aritmtica com parnteses no balanceados,

Erros semnticos
identificadores no declarados ou redeclarados, utilizao fora do seu escopo, incompatibilidade de tipos,

Erros lgicos
diviso por zero, chamada infinitamente recursiva, ...

Erro fatal
impossvel continuar a anlise

Erro grave
continua a anlise, mas impossvel gerar cdigo

Aviso
a anlise e gerao continuam, mas foi feita uma correo.

mensagem_erro (tipo, classe, cdigo, smbolo, posio)


caso tipo seja
1 : escrever (ERRO FATAL ); 2 : escrever (ERRO GRAVE ); 3 : escrever (AVISO );

caso classe seja


1 : escrever (NA ANLISE LXICA ); 2 : escrever (NA ANLISE SINTCTICA ); 3 : escrever (NA ANLISE SEMNTICA );

escrever (NO SMBOLO , smbolo, NA POSIO , posio.linha, posio.coluna); caso cdigo seja 1: 2: fim

Linguagem Natural

Linguagens com Estrutura de Frases

Analisador Semntico

Linguagens Sensveis ao Contexto

Analisador Sinttico

Linguagens Livre de Contexto

Analisador Lxico

Linguagens Regulares

1.

Aponte as vantagens e desvantagens dos interpretadores em relao aos compiladores. Explique o processo de compilao: fases e interrelacionamento. Qual o significado de passo no processo de compilao? Quais as vantagens e desvantagens de implementar um compilador em vrios passos? Qual a funo da tabela de smbolos? Como ela utilizada nas fases do compilador? Aponte alguns tipos de erros que podem ocorrer em cada fase de anlise de um compilador.

2.

3.

4.

5.

Lxica, Sinttica e Semntica

A anlise tem como objetivo entender o cdigo fonte e represent-lo em uma estrutura intermediria. dividida em 3 partes:
1. A funo da anlise lxica ler o cdigo fonte, caracter a caracter, buscando a separao e identificao dos elementos componentes do programa fonte, denominados smbolos lxicos ou tokens. 2. A anlise sinttica o processo de se determinar se uma cadeia de tokens pode ser gerada por uma gramtica. 3. A anlise semntica assegura que as regras sensveis ao contexto da linguagem estejam verificadas quanto sua validade.

Analisador Lxico ANLISE Analisador Sinttico Analisador Semntico Gerador de cdigo intermedirio Otimizador de cdigo Gerador de Cdigo-objeto

Tratamento de Erros

SNTESE

Tabela de Smbolos

Funo de um Analisador Lxico (AL) Tarefas secundrias Vantagens da Separao entre AL e Sinttica Erros Lxicos Especificao e Reconhecimento dos tokens Implementao de um AL

A tarefa do analisador lxico avaliar um cdigo fonte e verificar se uma srie de smbolos vlida ou no. Quando uma palavra reconhecida como vlida, diz-se que ela compe um token lxico. Por fim, produzir como sada uma seqncia de tokens com seus respectivos cdigos que o Analisador Sinttico usar para validar regras da gramtica.

Consumir comentrios e separadores


Espao em branco, tabulao e CR LF, que no fazem parte da linguagem.

Processar diretivas de controle Relacionar as mensagens de erros do compilador com o programa-fonte


Manter a contagem dos CR LFs e passar esse contador junto com a posio na linha para a rotina que imprime erros

Manipular a Tabela de Smbolos para inserir os identificadores.

Simplificao
Um AS que tenha que fazer o tratamento de comentrios e separadores bem mais complexo do que um que assume que eles j foram removidos

Eficincia
Uma parte aprecivel do tempo de compilao corresponde AL que separada facilita a introduo de certas otimizaes

Manuteno
Toda parte referente representao dos terminais est concentrada numa nica rotina tornando mais simples as modificaes da representao

Poucos erros so discernveis no nvel lxico


O AL tem uma viso muito localizada do programa-fonte

Exemplo: fi (a > b) then


O AL no consegue dizer que fi a palavra reservada if mal escrita ou o uma funo sem ; O AL devolve o cdigo de identificador e deixa para as prximas fases identificar os erros

Tratamento de Constantes
Nmero de casas decimais para reais String no finalizada Tamanho mximo dos identificadores Fim de arquivo inesperado

preciso percorrer o cdigo-fonte caractere por caractere e concaten-los para formar o token. s vezes, para se decidir por um cdigo, preciso ler um caractere a mais, o qual deve ser devolvido a cadeia de entrada (lookahead) Tipos de Tokens:
identificadores, palavras-reservadas, smbolos especiais simples e compostos, e as constantes de todos os tipos

Por exemplo:
reconhecer while como uma palavra reservada reconhecer 1234 como um nmero inteiro identificar 3%%26*& como uma seqncia invlida

Pode ser representado por: Gramtica Expresso regular Autmato

Uma gramtica G um mecanismo para gerar as sentenas (ou palavras) de uma linguagem e definida pela qudrupla: (N, T, P, S) onde:
N um conjunto de smbolos no terminais T um conjunto de smbolos terminais, T N = P um conjunto de regras de produo S o smbolo inicial da gramtica (S N)

Exemplo para gerao de identificadores que iniciam por letra (l), podendo ser seguida por qualquer nmero de letras e/ou dgitos. G = ( N, T, P, I ) N = { I, R } T = { l, d } P = { I l | l R, R l R | d R | l | d }

Uma expresso regular (ER) sobre o alfabeto definida indutivamente a partir de expresses regulares bsicas, como segue:
1. 2.

a ER que representa a linguagem vazia, ou seja { } a ER que representa a linguagem cuja nica palavra a palavra vazia, ou seja { } x, onde x , a ER a linguagem cuja nica palavra x, ou seja, { x } se r e r so ER que representam as linguagens L(r) e L(r): (r + r) a ER que representa a linguagem L(r) L(r) (r r) a ER que representa a linguagem { vw | v L(r) e w L(r) }, ou seja, a concatenao de uma palavra de L(r) com uma palavra de L(r), nesta ordem (r)* a ER que representa a linguagem L*(r), isto , o conjunto de palavras que podem ser formadas com zero ou mais letras de .

3. 4.

- Epsilon

aa ba* d(d)* l(l+d)* (a+b)* (a+b)*aa(a+b)* a*ba*ba* (a+b)*(aa+bb) (a+)(b+ba)*

somente a palavra aa todas as palavras que iniciam por b, seguido por zero ou mais a conjunto dos inteiros (assumindo que d representa dgitos) conjunto de identificadores que iniciam por uma letra seguida opcionalmente por letras e dgitos todas as palavras sobre {a,b} todas as palavras contendo aa como subpalavra todas as palavras contendo exatamente dois b todas as palavras que terminam com aa ou bb todas as palavras que no possuem dois a consecutivos

Um autmato finito M sobre um alfabeto uma 5-tupla (K, , , e, F), onde:


K um conjunto finito de estados o alfabeto dos smbolos da linguagem : K x K a funo de transio de estados e o estado inicial F o conjunto de estados finais.

- Sigma maisculo - Delta minsculo

M = ( K, , , e, F ) = ( d, . ) K = ( e, e, e, e ) F = (e, e )

(e, d) = e (e, d) = e (e, . ) = e (e, d) = e (e, d) = e

M = ( K, , , e, F ) = ( d, . ) K = ( e, e, e, e ) F = (e, e )

TABELA DE TRANSIO

d
e e e e e e e e

.
e

d d e e . e d

1.

Elaborar uma gramtica para representar os nmeros reais. Elaborar um autmato para representar o tipo literal (string) no formato ... Elaborar um autmato para representar comentrios no formato (* ... *) Elaborar uma expresso regular para representar o formato de RG Elaborar uma expresso regular para representar o formato de e-mail

2.

3.

4.

5.

H diferentes maneiras de se implementar um analisador lxico, entre elas, temos:


Elaborar um cdigo que simula o funcionamento do autmato finito Utilizar geradores, como por exemplo, LEX

Comearemos criando alguns algoritmos para reconhecer tokens. Posteriormente, incrementaremos nosso cdigo para gerar uma tabela de smbolos, que ser usada pelo analisador sinttico.

M = ( K, , , e, F ) = ( l, d ) K = ( e, e ) F = ( e )

TABELA DE TRANSIO

l
e e e e

d
e

l, d

letter: set of (a..Z); digit: set of(0..9); begin read(input) state := 1; while NOT EOF input do begin charRead = nextChar(input); case state do 1: if charRead in letter state := 2 else state := 3 //erro 2: if charRead in (letter OR digit) state := 2 //desnecessrio else state := 3 //erro end case end while if state = 2 return true else return false end.

#include <iostream> int isIdentifier(char *); int main() { char input[256]; printf("Identificador: "); gets(input); if(isIdentifier(input)) printf("Vlido"); else printf("Invlido"); getchar(); return 0; }

int isIdentifier(char *input) { char chRead, state = 1; int lineSize; lineSize = strlen(input); for(int column=0; column<lineSize; column++){ chRead = input[column]; switch(state){ case 1: if(isalpha(chRead)) state = 2; else state = 3; break; case 2: if(isalpha(chRead) || isdigit(chRead)) state = 2; //desnecessrio else state = 3; break; case 3: exit; break; } } return state==2; }

l, d

letter: set of (a..Z); digit: set of(0..9); begin read(input) state := 1; while NOT EOF input do begin charRead = nextChar(input); case state do 1: if charRead is space while charRead is space do charRead = nextChar(input); else if charRead in letter state := 2 else state := 3 //erro ...

Onde: b: espao em branco l: letra a..Z d: dgito 0..9

... case 1: if(isspace(chRead)) { column++; while(column<lineSize && isspace(input[column])){ column++; } if(column<lineSize) column--; state = 1; } else if(isalpha(chRead)) state = 2; else state = 3; break; ...

1.

2. 3.

4.

5.

Elaborar um programa reconhecer nmeros inteiros. Elaborar um programa reconhecer nmeros reais. Elaborar um programa para reconhecer literais (string) no formato ... Elaborar um programa para reconhecer comentrios no formato (* ... *) Elaborar um programa para ler uma entrada e retornar se o token representa um identificador, um nmero, um literal, um comentrio ou um smbolo invlido. obs.: antes de programar, criar o autmato.

l, d

e2 b l d ! d . d d

e1

e3

e4

e5

e6 (

e7 !* !) * * )

e8

e9

e10

e11

Manipular arquivo
O cdigo-fonte lido pelo analisador lxico a partir de um arquivo de texto (sem formatao)

Separar tokens
Cada token deve ser identificado e etiquetado com sua classe, valor e posio (linha e coluna)

Alocar em fila
Cada token deve ser armazenado numa fila (com alocao dinmica de memria)

#include <iostream> #define MAXLINESIZE 256 typedef struct { unsigned short int line; unsigned char column; } Coordinate; int main() { FILE *sourceCode; int lineSize; Coordinate cFile; char chRead; char currentLine[MAXLINESIZE]; char physicalFile[256]; printf("Informe o nome do arquivo: "); gets(physicalFile); system("cls");

if (( sourceCode = fopen( physicalFile, "r" ) ) == NULL ) { printf( "O arquivo no pode ser aberto.\n" ); exit( 1 ); } cFile.line = 1; while ( !feof( sourceCode ) ) { fgets( currentLine, ( MAXLINESIZE -2 ), sourceCode ); lineSize = strlen( currentLine ); printf("%3d: ",cFile.line); for ( cFile.column = 0; cFile.column < lineSize; cFile.column++ ) { chRead = currentLine[cFile.column]; printf("%c", chRead); } cFile.line++; } fclose( sourceCode ); getchar(); return 0; }

#include <iostream> void concat (char *, char); void initToken(char *, char *); void addToken(char *, char *); int lexicalScanner(char *); int main() { char input[256]; printf("Digite um identificador: "); gets(input); lexicalScanner(input); getchar(); return 0; }

void concat (char *str, char ch) { int i = strlen(str); *(str + i) = ch; *(str + (i + 1)) = '\0'; } void initToken(char *token, char *state) { strcpy(token,"\0"); *state = 1; } void addToken(char *token, char *state) { printf("-> %s\n",token); initToken(token, state); }

int lexicalScanner(char *input){ char chRead, state; int lineSize; char token[100]; lineSize = strlen(input); initToken(token, &state); for(int column=0; column<=lineSize; column++){ chRead = input[column]; switch(state){ case 1: if(isspace(chRead)) { column++; while(column<lineSize && isspace(input[column])){ column++; } if(column<lineSize) column--; state = 1; }

else if(isalpha(chRead)){ concat(token, chRead); state = 2; } else state = 3; break; case 2: if(isalpha(chRead) || isdigit(chRead)) { concat(token, chRead ; state = 2; } else { addToken(token, &state); column--; } break; case 3: initToken(token, &state); column--; break; } } return 0; }

#include <iostream> typedef struct { int value; } Item; typedef struct TCell *Pointer; typedef struct TCell { Item item; Pointer next; } Cell; typedef struct { Pointer first, last; } Queue;

int newQueue(Queue *queue){ queue->first = (Pointer) malloc(sizeof(Cell)); queue->last = queue->first; queue->first->next = NULL; return 0; } int queueEmpty(Queue *queue){ return (queue->first == queue->last); } int push(Queue *queue, Item *item){ if(queue->last->next = (Pointer) malloc(sizeof(Cell))) { queue->last = queue->last->next; queue->last->item = *item; queue->last->next = NULL; return 0; } else { return 1; } }

int pop(Queue *queue, Item *item){ Pointer aux; if(queueEmpty(queue)){ return 1; } else { *item = queue->first->next->item; aux = queue->first; queue->first = queue->first->next; free(aux); return 0; } } int freeQueue(Queue *queue){ Pointer aux; while(queue->first){ aux = queue->first; queue->first = queue->first->next; free(aux); } }

int printQueue(Queue *queue){ if(queueEmpty(queue)){ printf("Empty Queue\n"); return 1; } else { Pointer aux = queue->first->next; while(aux != NULL){ printf("%d\n", aux->item.value); aux = aux->next; } return 0; } } char menu() { printf("\n1. Push"); printf("\n2. Pop"); printf("\n3. Print"); printf("\n0. Quit"); printf("\nChoice: "); fflush(stdin); return getchar(); }

int main(){ char op; Queue queue; Item item; newQueue(&queue); do { op = menu(); switch(op){ case '1': printf("Enter a value: "); scanf("%d", &item.value); push(&queue, &item) ? printf("Error\n") : printf("PUSH %d\n", item.value); break; case '2': pop(&queue, &item) ? printf("Empty Queue\n") : printf("POP %d\n", item.value); break; case '3': printQueue(&queue); break; } } while(op!='0'); freeQueue(&queue); return 0; }

Elaborar um autmato finito capaz de representar o analisador lxico de um subconjunto da linguagem Pascal.
Consumir espaos em branco no incio da cadeia Reconhecer:
Identificadores Nmeros (inteiros e reais) Comentrios Literais Operadores aritmticos +, -, *, / Operadores lgicos :=, =, <>, <, >, <=, >= Delimitadores ( ) : ; , .

l, d e27 e26 . ; e25 b , e1 a e24 e23 = ) < > : e20 > e18 = = e21 e19 = e17 e16 e15 e11 * !* e12 ( { e9 d !} } e10 Q l e3 . ! e7 e28 e2 d l l e6

l, d

l d e4

e5

e8

!) ou !* *

* e13 ) e14

e22

Onde: l: [ a . . Z ] d: [ 0 . . 9 ] a: [ + - * / ] b: espao em branco Q: caractere no reconhecido

Baseado no autmato proposto, criar um analisador lxico que leia um arquivo de entrada e gere as tabelas de smbolos e de erros usando lista dinmica.
Para cada token, armazenar: nome, classe, valor, coordenada (linha,coluna) Identificar as palavras reservadas: begin, end, program, const, var, string, integer, real, for, to, while, do, if, then, else, not, and e or.

Analisador Lxico ANLISE Analisador Sinttico Analisador Semntico Gerador de cdigo intermedirio Otimizador de cdigo Gerador de Cdigo-objeto

Tratamento de Erros

SNTESE

Tabela de Smbolos

Constitui a segunda fase de um compilador. Sua funo verificar se as construes usadas na programa esto gramaticalmente corretas. Cada linguagem de programao possui regras que descrevem a estrutura sinttica dos programas.

Fase Lxica Sinttica

Entrada Cadeia de Caracteres Cadeia de Tokens

Sada Cadeia de Tokens rvore Sinttica

O analisador (ou reconhecedor) sinttico, tambm chamado de parser, recebe do analisador lxico a sequncia de tokens que constitui a sentena s e produz como resultado uma rvore de derivao, se a sentena vlida, ou emite uma mensagem de erro, caso contrrio.
Analisador Sinttico Sequncia de Tokens rvore Sinttica

Por exemplo:
um programa composto por blocos; um bloco composto por comandos; um comando composto por expresses; uma expresso formada por tokens ...

Dada uma gramtica livre de contexto G e uma sentena (programa-fonte) s, o objetivo do analisador sinttico verificar se a sentena s pertence linguagem gerada por G.

A rvore de derivao pode ser construda explicitamente (representada atravs de uma estrutura de dados) ou ficar implcita nas chamadas das rotinas que aplicam as regras de produo durante o reconhecimento. recomendado que os analisadores sintticos sejam projetados de modo que possam prosseguir na anlise at o fim do programa, mesmo que encontrem erros no texto fonte.

if E1 then C1 else if E2 then C2 else C3


comando

if

expr E1

then

comando C1 if expr E2

else

comando

then

comando C2

else

comando C3

Normalmente, as estruturas sintticas vlidas so especificadas atravs de uma gramtica livre de contexto (GLC) ou pela notao BNF. Dado o formalismo da linguagem BNF, possvel construir um programa que cria automaticamente um parser para uma determinada sintaxe. Yacc (Yeat Another Compiler of Compilers)

H duas estratgias bsicas para a AS:


Estratgia TOP-DOWN ou DESCENDENTE Estratgia BOTTOM-UP ou REDUTIVA

Os mtodos de anlise baseados na estratgia top-down (descendente) constroem a rvore de derivao a partir do smbolo inicial da gramtica (raiz da rvore), fazendo a rvore crescer at atingir suas folhas. Principais tipos:
Recursivo com retrocesso (backtracking) Recursivo preditivo Tabular preditivo

A anlise redutiva de uma sentena (ou programa) pode ser vista como uma tentativa de construir uma rvore de derivao a partir das folhas. O processo de reconhecimento consiste em transferir smbolos da fita de entrada para a pilha at que se tenha na pilha um lado direito de produo. Quando isso ocorre, esse lado direito substitudo (reduzido) pelo smbolo do lado esquerdo da produo.

Analisadores Sintticos

Descendente (Top-donw)

Ascendente (Bottom-up)

Recursivo com retrocesso (backtracking)

Recursivo sem retrocesso (preditive)

Preditiva Tabular (no recursivo)

O standard ISO 14977 define uma extenso ao BNF designado EBNF e no qual existem quatro novos operadores:
| [] {} escolha mltipla smbolos opcionais (zero ou uma vez) smbolos opcionais com repetio (zero ou mais vezes)

{ }+ smbolos com repetio (uma ou mais vezes)

Expresses constitudas por dgitos e sinais de mais e menos podem ser descritas atravs da gramtica:
<expr> ::= <num> | <num> <oper> <expr> <num> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 <oper> ::= + | -

Uma cadeia de linguagem, gerada por uma gramtica livre de contexto, pode ser exibida atravs de uma rvore sinttica.

Por exemplo, a rvore gramatical para "9-5+2" :

Comearemos com uma expresso simplificada:


<EXPRESSAO> ::= <TERMO> | <TERMO> <OPERADOR> <EXPRESSAO> <OPERADOR> <TERMO> <NUMERO> <IDENT> <DIGITO> <LETRA> ::= + | ::= <NUMERO> | <IDENT> ::= {<DIGITO>}+ ::= <LETRA> {<LETRA> | <DIGITO>} ::= 0|1|2|3|4|5|6|7|8|9 ::= a|b|c|d|...|X|Y|Z

Podemos interpretar a regra como: "expresso um nmero seguido de zero ou mais expresses conectadas por um operador. Assim como: expresso = numero expresso = numero + expresso expresso = numero + expresso + expresso e assim por diante.

Procedimento analisador_sintatico { obter_token(); /* chama o lxico */ EXPR(); } Procedimento EXPR() { TERMO(); se token = '+' ou token = '-' ento { obter_token(); EXPR(); } } Procedimento TERMO() { se smbolo_lido = NMERO ento { /* trate adequadamente um nmero */ obter_token(); } senao se smbolo_lido = IDENT ento { /* trate o identificador */ obter_token(); } }

uma gramtica que permite construir mais de uma rvore de derivao para uma mesma sentena. Isto representa um problema para o analisador sinttico, pois ela no especifica com preciso a estrutura sinttica do um programa. Este problema pode ser comparado ao dos autmatos nodeterminsticos, onde dois caminhos podem aceitar a mesma cadeia.

A eliminao de ambiguidade no to simples como a eliminao de nodeterminismo em autmatos. Existem duas maneiras:
Estabelecer uma regra que especifique a cada caso ambguo qual o caminho correto. (Sem alterar a gramtica) Alterar a gramtica para forar a construo da rvore sinttica correta, removendo a ambiguidade.

expr -> expr op expr | ( expr ) | id | num op -> + | - | * | / expr expr

expr

op +

expr num

expr num

op * expr num

expr

expr num

op *

expr num

op +

expr num

Para tratar o problema de ambiguidade em gramtica so utilizados os conceitos de precedncia e associatividade. Eles especificam uma ordem na avaliao dos operadores. Operadores com maior precedncia so avaliados primeiro. Operadores com igual precedncia so avaliados de acordo com a associatividade (esquerdadireita, diretaesquerda).

Para tratar a precedncia dos operadores:


Dividese os operadores em grupos de igual precedncia Para cada nvel de precedncia, introduzse um no terminal e uma regra gramatical

Para tratar a associatividade dos operadores:


Criase regras gramaticais que sero recursivas direita ou esquerda.

Gramtica para expresses sem ambiguidade:


expr -> expr addop term | term term -> term mulop fator | fator fator -> ( expr ) | id | num addop -> + | mulop -> * | /
term

expr

addop +

term num

fator num

mulop *

expr num

Se um compilador tivesse que processar apenas programas corretos, seu projeto e sua implementao seriam muito simplificados. Porm, espera-se que um compilador auxilie o programador na localizao e rastreamento de erros que inevitavelmente surgem nos programas. Erros sintticos incluem ponto-e-vrgulas mal colocados, chaves extras ou faltando, instrues incompletas ou expresses invlidas.

O recuperador de erros de um analisador sinttico possui objetivos simples, mas desafiadores em sua implementao:
Informar a presena de erros de forma clara e precisa. Recuperar-se de cada erro com rapidez suficiente para detectar erros subsequentes. Acrescentar um custo mnimo no processamento de programas corretos.

No mnimo, preciso informar o local no programa fonte onde o erro foi detectado, pois existe uma grande chance de que o local exato do erros seja em um dos tokens anteriores.

A tcnica mais simples interromper a anlise sinttica e emitir uma mensagem de erro informativa assim que o primeiro erro for detectado. Se os erros se acumularem, melhor o compilador desistir depois de ultrapassar algum limite de erros do que produzir uma incomoda avalanche de falsos erros. Principais estratgias de recuperao de erros:
Modo pnico Nvel de frase Produes de erro Correo global

Com esse mtodo, ao detectar um erro, o analisador sinttico descarta um smbolo de entrada de cada vez at que um conjunto de tokens de sincronismo seja encontrado (normalmente delimitadores, como ponto-evrgula). Embora o modo pnico normalmente ignore um quantidade considervel de smbolos, ele tem a vantagem da simplicidade, alm da garantia de no entrar em um loop infinito.

Ao detectar um erro, o analisador sinttico pode realizar a correo local sobre o restante da entrada, de maneira a ser possvel continuar a anlise. Tem sido usada em diversos compiladores com recuperao de erros, pois pode corrigir qualquer cadeia de entrada. A sua principal desvantagem a dificuldade de lidar com situaes em que o erro real ocorreu antes do ponto de deteco. preciso ter cuidado para que as substituies no provoquem loops infinitos.

Nesta estratgia de recuperao de erro podemos estender a gramtica da linguagem em mos com produes que geram construes erradas, antecipando assim os erros mais comuns. O analisador sinttico pode, ento, gerar diagnsticos de erro apropriado sobre a construo errnea que foi reconhecida na entrada.

Idealmente, gostaramos que um compilador fizesse o mnimo de mudanas possvel no processamento de uma cadeia de entrada incorreta. Existem algoritmos que auxiliam na escolha de uma sequncia mnima de mudanas afim de obter uma correo com um custo global menor. Infelizmente, esses mtodos em geral so muito caros em termos de tempo e espao de implementao, de modo que essas tcnicas tm atualmente interesse meramente terico.

Elaborar uma gramtica para reconhecer:


expresses relacionais declarao de variveis chamada de funes

Criar a rvore de derivao para as expresses:


a-b*c (a+b)*c a+(b*c)/d

<programa> ::= program <identificador> ; <bloco> .

program

identificador

bloco

<bloco> ::= [<declar. variavel>] <comando composto>

comando composto declar. variavel

<declar. variavel> ::= var <declar. Vars> {<declar. vars>}

var

declar. vars

<declar. vars> ::= <lista identificadores> : <tipo> ;

lista identificadores

tipo

<lista identificadores> ::= <identificador> {, <identificador>}

identificador

<tipo> ::= integer | real | string

integer

real

string

<comando composto> ::= begin <comando> {; <comando> } end

begin

comando

end

<comando> ::= <atribuicao> | <chamada procedimento> | <desvio condicional> | <laco repeticao> | <comando composto> | <vazio>

atribuicao

chamada procedimento

desvio condicional

laco repeticao

comando composto

<atribuicao> ::= <identificado> := <expressao>

identificador

:=

expressao

<chamada procedimento> ::= <identificador> [(<expressao>{,<expressao>})]

identificador

expressao ,

<desvio condicional> ::= if <expressao> then <comando> [ else <comando> ]

if

expressao

then

comando

else

comando

<laco repeticao> ::= while <expressao> do <comando>

while

expressao

do

comando

<expresao> ::= <expres. simples> [<operador relacional> <expres. simples>]

expres. simples

operador relacional

expres. simples

<operador relacional> ::= > | >= | < | <= | <> | =

>

>=

<

<=

<>

<expres. simples> ::= [+|-] <termo> {(+|-|OR) <termo>}

+ termo + termo or

<termo> ::= <fator> {(*|DIV|AND) <fator>}

fator * fator div and

<fator> ::= <identificador> | <numero> | <chamada funcao> | (<expressao>) | NOT <fator>

variavel

numero

chamada funcao

expressao

NOT

fator

<chamada funcao> ::= <identificador> [(<lista expressoes>)]

identificador

lista expressoes

<lista expressoes> ::= <expressao> {, <expressao>}

expressao ,

<programa> ::= program <identificador> ; <bloco> . <bloco> ::= [<declar. variavel>] <comando composto> <declar. variavel> ::= var <declar. vars> {<declar. vars>} <declar. vars> ::= <lista identificadores> : <tipo> ; <lista identificadores> ::= <identificador> {, <identificador>} <tipo> ::= integer | real | string <comando composto> ::= begin <comando> {; <comando> } end <comando> ::= <atribuicao> | <chamada procedimento> | <desvio condicional> | <laco repeticao> | <comando composto> | <vazio> <atribuicao> ::= <identificador> := <expressao> <chamada procedimento> ::= <identificador> [(<expressao>{,<expressao>})] <desvio condicional> ::= if <expressao> then <comando> [ else <comando> ] <laco repeticao> ::= while <expressao> do <comando> <expresao> ::= <expres. simples> [<operador relacional><expres. simples>] <operador relacional> ::= > | >= | < | <= | <> | = <expres. simples> ::= [+|-] <termo> {(+|-|OR) <termo>} <termo> ::= <fator> {(*|DIV|AND) <fator>} <fator> ::= <identificador> | <numero> | <chamada funcao> | (<expressao>) | NOT <fator> <chamada funcao> ::= <identificador> [(<lista expressoes>)] <lista expressoes> ::= <expressao> {, <expressao>} <numero> ::= {digito}+[.{digito}+] <identificador> ::= <letra> {<letra> | <digito>} <letra> ::= a|b|c|d|e|f|...|x|y|z|A|B|C|D|...|X|Y|Z <digito> ::= 0|1|2|3|4|5|6|7|8|9

CONST FUNCTION e PROCEDURE (declarao) TYPE RECORD ARRAY BOOLEAN POINTER FILE ... Em http://www.lrz.de/~bernhard/Pascal-EBNF.html encontra-se a definio EBNF completa da gramtica para linguagem Pascal.

Receber o lista de tokens


Os tokens gerados pelo analisador lxico (armazenados numa fila) so passados ao analisador sinttico.

Reconhecer a gramtica
A sequncia de tokens dever ser reconhecida e validada pela gramtica.

Gerar a rvore de derivao


Cada token que for validado deve ser armazenado numa rvore.

#include <iostream> #define TRUE 1 #define FALSE 0 // <EXPRESSAO> // <OPERADOR> // <TERMO> ::= <TERMO> | <EXPRESSAO> <OPERADOR> <TERMO> ::= + | ::= 0|1|2|3|4|5|6|7|8|9

char input[256]; char token; int p = 0; void nextToken(){ if(p<strlen(input)) { token = input[p++]; } else { token = '\0'; } }

int isOperator(void) { if(token=='+' || token=='-') { nextToken(); return TRUE; } else { return FALSE; } } int isTerm(void) { if(isdigit(token)) { nextToken(); return TRUE; } else { return FALSE; } }

int isExpression() { if(isTerm()) { if(token=='\0') { return TRUE; } else if(isOperator()) { return isExpression(); } else { return FALSE; } } else { return FALSE; } } int parser() { nextToken(); return isExpression(); }

int main(){ printf("Digite uma expresso: "); gets(input); if(parser()) printf("Expresso vlida"); else printf("Expresso invlida"); getchar(); return 0; }

1.

Aceitar, como termo, tambm identificadores

<EXPRESSAO> ::= <TERMO> | <EXPRESSAO> <OPERADOR> <TERMO> <OPERADOR> <TERMO> <NUMERO> <IDENTIF> ::= + | ::= <NUMERO> | <IDENTIF> ::= 0|1|2|3|4|5|6|7|8|9 ::= A|B|C|D|E|...|Z

2.

Adicionar na gramtica os fatores de multiplicao e diviso

<EXPRESSAO> ::= <TERMO> { <OPER_ADD> <TERMO> } <TERMO> <OPER_ADD> ::= <FATOR> { <OPER_MULT>| <FATOR> } ::= + | -

<OPER_MULT> ::= * | / <FATOR> <NUMERO> <IDENTIF> ::= <IDENTIF> | <NUMERO> | (<EXPRESSAO) ::= 0|1|2|3|4|5|6|7|8|9 ::= A|B|C|D|E|...|Z

3.

De acordo com a gramtica abaixo, validar se a declarao de variveis est sintaticamente correta.

<declar. variavel> ::= v <declar. vars>; {<declar. vars>;} <declar. vars> ::= <lista identificadores> : <tipo> <lista identific.> ::= <identificador> {, <identificador>} <tipo> ::= i | r | s <identificador> ::= A|B|C|D|E|...|Z

4.

De acordo com a gramtica abaixo, validar se a estrutura de um programa est correta.

<program> ::= p <ident> ; <bloco> . <bloco> <ident> ::= b e ::= a|b|c|d|e|f| ... |z

#include <iostream> #define #define #define #define #define CMD_INSERIR '1' CMD_LISTAR '2' CMD_SAIR '0' TRUE 1 FALSE 0

typedef struct { int valor; } info; struct No { info dados; struct No *dir; struct No *esq; }; typedef struct No no;

Um compilador no precisa utilizar rvore binria. Isto apenas um exemplo de rvore para ser adaptado para a rvore sinttica.

char menu() { system("cls"); printf("1. Inserir\n"); printf("2. Listar\n"); printf("0. Sair\n"); printf("\nInforme sua opo: "); return getchar(); } void lerDados(info *dados) { printf("Digite um valor: "); scanf("%d", &dados->valor); } no * criarNo(info d) { no *novo = (no *) malloc(sizeof(no)); if(novo!=NULL) { novo->dados = d; novo->dir = NULL; novo->esq = NULL; } return novo; }

int inserir(no **raiz, info d) { no *novo; novo = criarNo(d); if(novo==NULL) { return FALSE; } else { if(*raiz == NULL) { *raiz = novo; } else { if(d.valor < (*raiz)->dados.valor) inserir(&(*raiz)->esq, d); if(d.valor > (*raiz)->dados.valor) inserir(&(*raiz)->dir, d); } } return TRUE; }

void listar(no **raiz) { if(*raiz) { listar(&(*raiz)->esq); printf("%d\n",(*raiz)->dados.valor); listar(&(*raiz)->dir); } }

void liberar(no **raiz) { if(*raiz) { liberar(&(*raiz)->esq); liberar(&(*raiz)->dir); free(*raiz); } }

int main(void) { no *raiz = NULL; info dados; char opcao; do { opcao = menu(); switch(opcao) { case CMD_INSERIR: lerDados(&dados); inserir(&raiz, dados)==TRUE ? printf("Sucesso ao inserir!\n") : printf("Erro ao inserir!\n"); system("pause"); break; case CMD_LISTAR: listar(&raiz); system("pause"); break; } } while(opcao != CMD_SAIR); liberar(&raiz); return 0; }

Analisador Lxico ANLISE Analisador Sinttico Analisador Semntico Gerador de cdigo intermedirio Otimizador de cdigo Gerador de Cdigo-objeto

Tratamento de Erros

SNTESE

Tabela de Smbolos

A Anlise Semntica - terceira fase da compilao descreve o significado de construes sintticas.

Fase Lxica Sinttica Semntica

Entrada Cadeia de Caracteres Cadeia de Tokens rvore Sinttica

Sada Cadeia de Tokens rvore Sinttica rvore com Anotaes

Sua funo verificar se existem erros semnticos na rvore sinttica e coletar as informaes necessrias para a prxima fase da compilao, que a gerao de cdigo objeto.
Analisador Semntico rvore Sinttica rvore com Anotaes

Anlise Semntica por vezes referenciada como anlise sensvel ao contexto porque lida com questes dependentes de contexto, o que est alm das capacidades de uma GLC.
Por exemplo: A := B + C; ilegal em Pascal se:
houverem variveis no declaradas houverem variveis de tipos diferentes houverem variveis declaradas como booleanas

Que tipo de significado est envolvido e que excede a capacidade das GLCs:
X foi declarado apenas uma vez? X foi declarado antes do seu primeiro uso? X foi definido antes do seu primeiro uso? X um escalar, um array, uma funo, ou uma classe? X declarado mas nunca utilizado? Os tipos de uma expresso so compatveis? As dimenses casam com o declarado?

Semntica Esttica
Em tempo de compilao

Dinmica
Em tempo de execuo

Conjunto de restries que determinam se programas sintaticamente corretos so vlidos. Compreende checagem de tipos, anlise de escopo de declaraes, checagem de nmero e tipo de parmetros de funes/procedimentos. realizado em tempo de compilao em linguagens tipadas, que exigem declaraes, tais como C, Pascal, Java, etc. Pode ser especificada informalmente (e geralmente artesanal) atravs de descries em manuais de cada linguagem ou formalmente, por exemplo, com uma Gramtica de Atributos.

Ocorre em tempo de execuo em linguagens em que as variveis so determinadas pelo contexto de uso, tais como LISP, PROLOG, PHP, etc. geralmente especificada informalmente nos manuais mas tambm existem modelos formais:
Vienna Definition Language (VDL) Definies Axiomticas Modelos Denotacionais Modelos Operacionais Gramtica de Atributos

As tarefas bsicas desempenhadas durante a anlise semntica incluem verificao de:


Declaraes Tipos
todas as variveis foram declaradas? O tipo declarado o correto para o operador? O comando vlido nesse contexto? O nmero e o tipo dos parmetros so compatveis? O identificador nico no escopo?

Fluxo de controle Parmetros Unicidade

Todas as variveis devem ser declaradas. int a; a = b + 5; No exemplo, o identificador b no est declarado.

Considere o seguinte exemplo de cdigo: int f1(int a, float b) { return a%b; } O analisador semntico detecta um erro na verificao de tipos, tipos indicando que o operador mdulo % no pode ter um operador real.

O compilador deve relatar um erro se um nmero real for usado para indexar um array. int vetor[3]; float a = 2.5; vetor[a] = 10; vetor[3] = 20; Alm do tipo, outro erro semntico ocorre ao tentar acessar um ndice fora da faixa de valores vlidos.

Em alguns casos, o compilador realiza a converso automtica de um tipo para outro que seja adequado aplicao do operador. Por exemplo, na expresso: int a, b; a = b '9' A constante do tipo carter '9' automaticamente convertida para inteiro para compor corretamente a expresso aritmtica na qual ela toma parte.

Outro exemplo de erro detectado pela anlise semntica, neste caso pela verificao de fluxo de controle, controle ilustrado pelo cdigo: void f2(int j, int k) { if (j == k) { break; } } Ele reconhece que o comando break s pode ser usado para quebrar a sequncia de um comando de iterao (loop) ou para indicar o fim de um case (switch).

A verificao de parmetros em chamadas funes e procedimentos deve checar o nmero e o tipo de parmetros. Por exemplo, a compilao do seguinte cdigo:
int sum(int a, int b, int c) { return (a+b+c); } ... x = sum(y, 5.1);

Dois erros semnticos ocorrem:


A chamada a funo est passando apenas 2 parmetros O segundo parmetro incompatvel com o prottipo

A verificao de unicidade detecta situaes tais como duplicao, dentro do escopo, em declaraes de variveis, de componentes de estruturas e em rtulos do programa. Por exemplo, a compilao do seguinte cdigo:
int a; struct { b: int; b: float; } a;

Dois erros semnticos ocorrem:


H duas declaraes de varivel com nome a H, no registro, dois campos com nome b

Os nomes servem para representar vrias entidades dentro de um programa, seja uma varivel, funo, classe, etc. mais simples se referir a uma varivel pelo seu nome do que pela sua localizao na memria. Podemos dizer que o escopo de uma varivel representa a regio na qual o acesso ao seu contedo visvel. O escopo pode ser local ou global.

int x, y; int somatorio(int n) { int s = 0; for (int i=1; i<=n; i++) { s += i; x++; } return s; } int g() { int s; s = somatorio(10); return s+x; } void main() { int a; x = 5; y = g(); }

x somatorio s g s main a n

for i

A maioria das linguagens de programao implementa escopo esttico e, para resolver conflito de nomes, aplica a regra do contexto envolvente mais prximo (escopo ancestral mais prximo). Para vincular uma referncia a uma varivel, deve se encontrar a declarao dessa varivel no escopo atual ou nos seus escopos ancestrais. Variveis com o mesmo nome, mas em escopos diferentes, escondem as variveis definidas no pai esttico.

Como diferenciar variveis globais de locais na TS program meu_prog; var x, y: integer; procedure meu_proc(x: integer) var y: real; begin read(y); Varivel y local x:=x+y; end; begin read(y); x:=x*y; end.
Varivel y global

1.

Incluso de um campo a mais na tabela de smbolos indicando o nvel da varivel no programa


Controle do nvel durante a compilao do programa
Quando se chama um procedimento (ou funo), faz-se nvel:=nvel+1 Quando se sai de um procedimento (ou funo), faz-se nvel:=nvel-1 Busca do fim para o incio da TS a fim de encontrar a declarao mais recente

2.

Associao das variveis locais a um procedimento (ou funo) entrada relativa ao procedimento (ou funo) por meio, por exemplo, de uma lista encadeada.
Ateno: para a checagem de tipos, deve-se saber quantos so e quais so os parmetros de um procedimento (ou funo) na tabela de smbolos

3.

Tabelas diferentes para diferentes escopos

Geralmente, usase a tabela de smbolos para realizar/computar a semntica. Mais formalmente, pode ser utilizada a traduo dirigida pela sintaxe com gramticas de tributos.

Um compilador usa uma Tabela de Smbolos (TS) para guardar informaes sobre os nomes declarados em um programa. A TS serve para saber:
Quais smbolos foram definidos? O que o smbolo representa? Qual escopo de validade do smbolo?

Uma TS uma estrutura de dados que guarda as informaes necessrias para cada nome visto no programa, seja um nome de varivel, de tipo, de classe ou de qualquer outro conceito na linguagem fonte. Esta tabela usada durante vrias etapas da compilao e, algumas vezes, tambm na depurao de programas. Aps a rvore de derivao, a tabela de smbolos o principal atributo herdado em um compilador.

A TS pesquisada cada vez que um nome encontrado no programa fonte. Alteraes so feitas na TS sempre que um novo nome ou nova informao sobre um nome j existente obtida. O gerenciamento da TS de um compilador deve ser implementada de forma a permitir inseres e consultas da forma mais eficiente possvel, alm de permitir o crescimento dinmico da mesma. Cada entrada na TS pode ser implementada como um registro ("record" ou "struct") contendo campos (nome, tipo, classe, tamanho, escopo, etc.) que a qualificam.

Como necessrio obter as informaes relativas a um smbolo (nome) cada vez que ele ocorre no texto do programa-fonte, a tabela de smbolos deve ser indexada pelo prprio nome do smbolo, ou seja, dado o nome da varivel, tipo ou funo, necessrio obter as informaes para aquele nome. De preferncia, a busca pelas informaes de um nome deve ser rpida e eficiente, tanto para obter as informaes associadas quanto para atualiza-las.

Se for utilizada uma estrutura como um array ou uma lista, a busca pelo nome do smbolo ter complexidade linear O(n) no tempo, onde n o tamanho da tabela. Uma outra alternativa seria utilizar alguma estrutura de rvore balanceada, como as rvores AVL. Nesse caso a complexidade no tempo cai para logartmica O(log n), o que j pode significar um ganho de desempenho considervel. Mas possvel obter uma estrutura ainda mais eficiente para busca direta de smbolos: a chamada tabela de disperso ou tabela hash. Nesta, a busca por um nome tem complexidade praticamente constante (O(1)), independendo do nmero de smbolos utilizados pelo programa.

Conceitualmente, estas tabelas so compostas de pares (chave, valor) onde a chave alguma informao utilizada para indexar o valor. Uma tabela hash para um dado tipo de chave consiste de:
Uma funo hash h Um arranjo (tabela) de tamanho N

Uma funo hash h mapeia chaves de um dado tipo em inteiros em um intervalo fixo [0, N - 1] O valor inteiro h(k) [0, N - 1] chamado de valor hash da chave k

Em suma, os dois componentes principais de uma tabela hash so:


1. A funo de hash 2. A tcnica de resoluo de coliso

E as operaes que uma tabela hash oferece so duas:


1. Buscar de um item, atravs da chave; e, 2. Inserir um novo item.
Opcionalmente, pode-se ter uma operao para remover itens.

Por exemplo, num cadastro de pessoas, poderamos usar o CPF como chave, e o valor seria o conjunto de informaes guardado sobre a pessoa. O nmero de CPF tem 11 dgitos, sendo portanto possveis 99 bilhes de valores para a chave. Mas, se tivermos um conjunto restrito de pessoas, podemos criar um arranjo de tamanho N = 100 e a seguinte funo hash: h(k) = 2 ltimos dgitos de k. Assim, se o CPF da pessoa for 347.961.571-66, guardamos seus dados na posio 66. Para otimizar o uso de recursos, possvel compactar o valor do ndice.

257.532.055-00

676.752.039-09

30

162.226.534-30

99

564.249.864-99

Colises ocorrem quando diferentes chaves so mapeadas sem distino na mesma clula da tabela. Note que impossvel evitar completamente colises se o fator de carga (load factor) l de uma tabela hash for l>1 Esse fator definido como a razo entre o no. n de chaves pelo tamanho da tabela N: l = n/N Uma possibilidade para resolver o problema das colises ter uma lista de entradas associada a uma posio na tabela. Considera-se que, em uma tabela hash bem dimensionada, devemos ter 1,5 acessos tabela, em mdia, para encontrar um elemento.

257.532.055-00

676.752.039-09

731.142.500-09

178.447.933-09

30

162.226.534-30

383.196.689-30

99

564.249.864-99

caracteres

Anlise Lxica tokens Anlise Sinttica rvore sinttica Anlise Semntica

rvore com anotaes

programa #$%&*; var a,b,c: integer c,d,e: string; f,101: boolean; begin a := 987654321; b[1] := !1; c := 2.x; d := 'yes; if a > 3 do a := 5.5; while b+1 < a bgin b := b + d; f := a + b; g := 2a / 0; end; end; end

program meta; var a,b: real; c: integer; begin a := 5000; b := 7000; c := 0; while a <= b do begin a := a + (a*3/100); b := b + (b*2/100); c := c + 1; end; if c < 35 then write('Meta atingida') else write('Meta no atingida'); end.

cdigo intermedirio, otimizao e gerao

Aps realizada a anlise, na fase de sntese o compilador dever gerar o cdigo em linguagem simblica equivalente ao cdigo analisado. Neste ponto do projeto, finda-se a parte de anlise e entramos no processo de sntese. Tipicamente, dividida em 3 partes:
1. O gerador de cdigo intermedirio permite a gerao de instrues para uma mquina abstrata. 2. O otimizador de cdigo tenta melhorar o cdigo intermedirio, de forma a gerar um cdigo de mquina mais eficiente. 3. O gerador de cdigo objeto tem como objetivo analisar o cdigo j otimizado e gerar o cdigo objeto definitivo para uma mquina alvo.

Analisador Lxico ANLISE Analisador Sinttico Analisador Semntico Gerador de cdigo intermedirio Otimizador de cdigo Gerador de Cdigo-objeto

Tratamento de Erros

SNTESE

Tabela de Smbolos

O gerador de cdigo intermedirio ser acionado quando o programa for analisado lxica, sinttica e semanticamente, e estiver correto do ponto de vista das trs anlises citadas. Ou seja, o gerador de cdigo intermedirio usa as estruturas produzidas pelas fases de anlise para criar uma sequncia de instrues para uma mquina abstrata dita como cdigo intermedirio.

A gerao de cdigo intermedirio , portanto, a transformao da rvore de derivao em um segmento de cdigo. Para essa gerao, deve-se percorrer a rvore em profundidade (depth first), para que o cdigo seja gerado das folhas para os ns. Esse cdigo pode, eventualmente, ser o cdigo final, mas, na maioria das vezes, constitui-se num cdigo intermedirio. A desvantagem que a traduo direta leva a uma compilao mais rpida.

A grande diferena entre o cdigo intermedirio e o cdigo objeto final que o intermedirio no especifica detalhes da mquina alvo, tais como quais registradores sero usados, quais endereos de memria sero referenciados, etc. Um exemplo clssico desse modelo a linguagem Java, que gera seu bytecode compatvel com diferentes arquiteturas.

Possibilita a otimizao intermediria, de modo a obter-se o cdigo objeto final mais eficiente. Simplifica a implementao do compilador, resolvendo, gradativamente, as dificuldades da passagem de cdigo fonte para objeto. Possibilita a traduo do cdigo intermedirio para diversas mquinas.

Os vrios tipos de cdigo intermedirio fazem parte de uma das seguintes categorias:
Representaes grficas: rvore e grafo de sintaxe. Notao ps-fixada ou pr-fixada Cdigo de trs-endereos: qudruplas e triplas.

Uma rvore de sintaxe uma forma condensada de rvore de derivao na qual somente os operandos da linguagem aparecem como folhas; Os operadores constituem ns interiores da rvore. Um grafo de sintaxe, alm de incluir as simplificaes da rvore de sintaxe, faz a fatorao das subexpresses comuns, eliminando-as.

:= a * b c b + * c a

:= + * b c

rvore de Sintaxe

Grafo de Sintaxe

Se E1 e E2 so expresses ps-fixadas e q um operador binrio, ento E1 E2 q a representao ps-fixada para a expresso E1 q E2. Se, por outro lado, E1 e E2 so expresses prfixadas, ento q E1 E2 a representao prfixada para a expresso E1 q E2.
Notao Infixada (a+b)*c a*(b+c) a+b*c Ps-fixada ab+c* abc+* abc*+ Pr-fixada *+abc *a+bc +a*bc

No cdigo intermedirio de trs-endereos, cada instruo faz referncia, no mximo, a trs variveis. Cdigo de trs-endereos para o comando A := X + Y * Z T1 := Y * Z T2 := X + T1 A := T2 onde T1 e T2 so variveis temporrias. Um cdigo de trs-endereos pode ser implementado atravs de qudruplas ou triplas.

As quadruplas so constitudas dos campos: um operador, dois operandos e o resultado. Exemplo: comando A := B*(-C+D) em qudruplas
Oper (0) (1) (2) (3) -u + * := Arg1 C T1 B T3 D T2 Arg2 Result T1 T2 T3 A

As triplas so formadas por: um operador e dois operandos. Essa representao utiliza ponteiros para a prpria estrutura, evitando a nomeao explcita de temporrios. Exemplo: comando A := B*(-C+D) em triplas
Oper (0) (1) (2) (3) -u + * := Arg1 C (0) B A D (1) (2) Arg2

Por exemplo, a expresso em C a = b + c * d; Seria traduzida para as instrues: _t1 := c * d a := b + _t1

Instrues de desvio condicional tem o formato if x opr y goto L onde opr um operador relacional de comparao e L o rtulo da linha que deve ser executada se o resultado da aplicao do operador relacional for verdadeiro; caso contrrio, a linha seguinte executada.

Por exemplo, a seguinte iterao em C


while (i++ <= k) x[i] = 0; x[0] = 0;

Poderia ser traduzida para


_L1: if i > k goto _L2 i := i + 1 x[i] := 0 goto _L1 _L2: x[0] := 0

Analisador Lxico ANLISE Analisador Sinttico Analisador Semntico Gerador de cdigo intermedirio Otimizador de cdigo Gerador de Cdigo-objeto

Tratamento de Erros

SNTESE

Tabela de Smbolos

O otimizador de cdigo (independente de mquina) um mdulo opcional (presente na grande maioria dos compiladores) que objetiva melhorar o cdigo intermedirio de modo que o programa objeto produzido ao fim da compilao seja menor (ocupe menos espao de memria) e/ou mais rpido (tenha tempo de execuo menor). A sada do otimizador de cdigo um novo cdigo intermedirio.

Aspectos de uso de memria e de velocidade de execuo so muitas vezes conflitantes, pois, em geral, ganhos de espao utilizado implicam perdas no tempo de execuo, e vice-versa. Sabe-se que a gerao de cdigo timo um problema indecidvel. Na prtica, o que se faz utilizar heursticas (tcnicas ad hoc ou empricas) para tentar otimizar o cdigo ao mximo.

Normalmente, o processo de otimizao desenvolve-se em duas fases:


Otimizao do cdigo intermedirio Otimizao do cdigo objeto

A primeira inclui tcnicas para eliminar atribuies redundantes, suprimir subexpresses comuns, eliminar temporrios desnecessrios, trocar instrues de lugar, etc., de modo a obter um cdigo intermedirio menor. A otimizao de cdigo objeto realizada atravs da troca de instrues de mquina por instrues mais rpidas e de melhor utilizao dos registradores.

Em uma mquina com um registrador principal (acumulador) e instrues Load, Store, Add, Sub e Mult, com a semntica usual. Quando analisamos cdigo para este comando, identificamos o uso de vrias regras, como:
C E E T V=E E+T E-T T*F

Geramos o seguinte cdigo intermedirio e as seguintes instrues em cdigo objeto:


t1 = a + b (regra E E+T) t2 = c + d regra E E-T t3 = t1 * t2 (regra T T*F) x = t3 (regra C V=E) Load a Add b Store t1 Load c Sub d Store t2 Load t1 Mult t2 Store t3 Load t3 Store x

11 instrues 1 2 3 4 5 6 7 8 9 10 11 Load a Add b Store t1 Load c Sub d Store t2 Load t1 Mult t2 Store t3 Load t3 Store x

9 instrues 1 2 3 4 5 6 7 8 9 Load a Add b Store t1 Load c Sub d Store t2 Load t1 Mult t2 Store x

7 instrues 1 2 3 4 5 6 7 Load a Add b Store t1 Load c Sub d Mult t1 Store x

Eliminao de subexpresses comuns Eliminao de cdigo morto Renomeao de variveis temporrias Transformaes algbricas Dobramento de constantes Otimizao de loop Eliminao de variveis de induo
Esta lista no exaustiva, e visa apenas mostrar as possibilidades existentes.

x = a + b; ... y = a + b; Se os valores de a e de b no so alterados, possvel guardar o valor da expresso a+b em uma temporria (digamos t1) e us-lo outra vez posteriormente. t1 = a + b x = t1; ... y = t1; ou, se a varivel x ainda est disponvel com o mesmo valor da segunda vez que a expresso calculada, dispensando o uso da temporria t1. x = a + b; ... y = x;

No caso de variveis ponteiros, devemos considerar, por segurana, que todas as variveis do tipo apontado foram alteradas. Considere o exemplo: int *p, a, b; x = a + b; ... *p = ...; ... y = a + b; Neste caso, no sabemos para qual inteiro p aponta, e portanto no podemos garantir que *p no um pseudnimo para a ou para b.

Normalmente, no se espera que um programador escreva cdigo morto. Entretanto, isso pode acontecer em vrias situaes, uma das quais um estgio intermedirio da otimizao de um programa, que sofreu algumas transformaes que causaram a existncia de cdigo no alcanvel. O cdigo morto (dead code) pode ser identificado em uma srie de situaes.

Ocorre aps uma instruo de encerramento de um programa ou de uma funo.


int f(int x) { return x++; /* o incremento no ser executado */ }

Ocorre aps um teste com uma condio impossvel de ser satisfeita.


#define DEBUG 0 ... if(DEBUG) { ... /* este cdigo no ser executado */ }

Ocorre aps um comando de desvio, e no alvo de nenhum outro desvio.


goto x; i=3; /* este cdigo no ser executado */ ... x: ...

J vimos em vrias ocasies que as variveis temporrias introduzidas durante a gerao de cdigo intermedirio podem no ser estritamente necessrias. Por exemplo, se tivermos no cdigo fonte x=a+b;, o cdigo intermedirio ser
t1 = a + b; x = t1;

e a varivel t1 pode ser eliminada.

Podemos aplicar algumas transformaes baseadas em propriedades algbricas, como comutatividade, associatividade, identidade, etc. Por exemplo, como a soma comutativa, podemos transformar x=a+b*c; em x=b*c+a; o que corresponde a trocar cdigo como:
Load b Mult c Store t1 Load a Add t1 Store x Load b Mult c Add a Store x

Transformaes semelhantes podem ser baseadas na associatividade, permitindo trocar x=(a+b)+(c+d); por x=(((a+b)+c)+d); o que corresponde a trocar:
Load a Add b Store t1 Load c Add d Store t2 Load t1 Add t2 Store x

Load a Add b Add c Add d Store x

Expresses ou sub-expresses compostas de valores constantes podem ser avaliadas em tempo de compilao (dobradas), evitando sua avaliao repetida em tempo de execuo. Por exemplo, se tivermos:
#define N 100 ... while (i<N-1) { ... }

no h necessidade de calcular repetidamente o valor de N-1. Este valor pode ser pr-calculado, e substitudo por 99.

H vrias otimizaes que se aplicam a loops, a mais comum das quais a transferncia de computaes invariantes do loop para fora dele.
for (i=0, i<N, i++) { k=2*N; f(k*i); }

que se transformaria em:


k=2*N; for (i=0, i<N, i++) f(k*i);

Por exemplo, se substituirmos o cdigo objeto correspondente a:


for(i=1; i<=10; i++) f(5*i);

Pelo cdigo objeto correspondente a:


f( 5); f(10); f(15); f(20); f(25); f(30); f(35); f(40); f(45); f(50);

Teremos um cdigo mais longo, mas certamente mais rpido, porque dispensa as dez multiplicaes, dez operaes de incremento e os onze testes de permanncia do for.

Outra possibilidade de otimizao a eliminao de variveis de induo.


Variveis de induo so variveis que assumem valores em progresso aritmtica, a cada vez que o cdigo do loop executado.

for (i=1, i<N, i++) { j=2*i; p(j); }

j=0; z: if (j>2*N) goto x; p(j); j=j+2; goto z; x: ...

Analisador Lxico ANLISE Analisador Sinttico Analisador Semntico Gerador de cdigo intermedirio Otimizador de cdigo Gerador de Cdigo-objeto

Tratamento de Erros

SNTESE

Tabela de Smbolos

O gerador de cdigo produz o cdigo objeto final, tomando decises com relao alocao de espao para os dados do programa, selecionando a forma de acesslos, definindo que registradores da UCP sero usados, etc. Projetar um gerador de cdigo que produza programas objeto verdadeiramente eficientes uma das tarefas mais difceis no projeto de um compilador.

Basicamente, quatro aspectos devem ser considerados no projeto de geradores de cdigo:


Forma do cdigo objeto a ser gerado: Linguagem absoluta, relocvel ou assembly. Seleo das instrues de mquina:
A escolha da sequncia apropriada pode resultar num cdigo mais curto e mais rpido.

Alocao de registradores. Escolha da ordem de avaliao:


A determinao da melhor ordem para execuo das instrues um problema insolvel. Algumas computaes requerem menos registradores para resultados intermedirios.

A gerao de um programa em linguagem absoluta de mquina tem a vantagem de que o programa objeto pode ser armazenado numa rea de memria fixa e ser imediatamente executado. Compiladores deste tipo so utilizados em ambientes universitrios, onde altamente conveniente diminuir o custo de compilao. Os compiladores que geram cdigo absoluto e executam-no imediatamente so conhecidos como load and go compilers.

A gerao de cdigo em linguagem de mquina relocvel permite a compilao separada de subprogramas. Mdulos e objetos relocveis podem ser ligados e carregados por um Ligador-Carregador. Essa estratgia d flexibilidade para compilar subrotinas separadamente e para chamar outros programas previamente Compilados.

A traduo para linguagem assembly facilita o processo de gerao de cdigo. So geradas instrues simblicas e podem ser usadas as facilidades de macro instrues. O preo pago um passo adicional:
Traduo para linguagem de mquina.

uma estratgia razovel, especialmente, para mquinas com pouca memria, nas quais o compilador deve desenvolver-se em vrios passos.

Instrues com registradores so mais curtas e mais rpidas do que instrues envolvendo memria. Portanto, o uso eficiente de registradores muito importante. A atribuio tima de registradores a variveis muito difcil, e muito problemtica quando a mquina trabalha com registradores aos pares (para instrues de diviso e multiplicao), ou prov registradores especficos para endereamentos e para dados.

Familiaridade com a mquina e com o conjunto de instrues pr-requisito para projetar um bom gerador de cdigo. Formato das instrues: op fonte, destino Instrues:
ADD SUB MUL DIV LOAD COPY R1, R2 R1, R2 R1, R2 R1, R2 M, R M R2 = R2 + R1 (adio) R2 = R2 R1 (subtrao) R2 = R2 * R1 (multiplicao) R2 = R2 / R1 (diviso) Registrador = Memria (carregamento) Memria = Registrador (armazenamento) R2 = R1 (cpia)

STORE R,

R1, R2

a := b + c
LOAD LOAD ADD STORE b, R0 c, R1 R1, R0 R0, a R0 = b R1 = c R0 = R0 + R1 a = R0

a := b + c * d
LOAD LOAD MUL LOAD ADD STORE d, R0 c, R1 R0, R1 b, R0 R1, R0 R0, a R0 = d R1 = c R1 = R1 * R0 R0 = b R0 = R0 + R1 a = R0

rvore com anotaes Gerao de Cdigo Intermedirio cdigo intermedirio Otimizao de Cdigo cdigo otimizado Gerao de Cdigo Objeto cdigo final

Dado o comando de atribuio x := (a+b) * c (a+b) / d


1. 2. 3. 4. 5. 6.

Traduzir para uma rvore de sintaxe Traduzir para um grafo de sintaxe Derivar cdigo intermedirio ps-fixado Derivar o cdigo intermedirio de trs-endereos Otimizar o cdigo intermedirio gerado Gerar o cdigo de mquina para a expresso

Um compilador um tipo de tradutor que l um programa escrito numa linguagem, a linguagem fonte, e transforma-o em um outro programa equivalente escrito em outra linguagem, a linguagem objeto.
Linguagem de alto nvel

c = a + b; mov ax, $a add ax, $b mov $c, ax

Linguagem de baixo nvel

importante notar que o programa fonte e o programa objeto so completamente diferentes, porm possuem o mesmo significado. Alm da traduo importante destacar a tarefa do compilador na deteco de erros feitos pelo programador.

A diviso de um compilador em front-end (anlise) e back-end (sntese) reflete numa codificao modular com independncia entre as fases.
O que, em princpio, permite o reuso de cdigo, sendo possvel escrever um front-end para cada linguagem de programao e um back-end para cada mquina alvo.
compilador Ling. A Ling. B Anlise A Anlise B AD e TS Sntese X Sntese Y Maq. X Maq. Y

...
Ling. C

...
Anlise C

...
Sntese Z

...
Maq. Z

Livro Texto:
1.

Implementao de Linguagens de Programao: Compiladores; Toscani, Simo S. & Price, Ana


Maria de Alencar; Ed. Bookman; 3 edio; 2008; 5 exemplares. 2007; 5 exemplares.

2.

Compiladores: Princpios, Tcnicas, e Ferramentas; Aho, Alfred et al.; Ed. Pearson; 2 edio; Compiladores: Princpios e Prticas; Louden, Kenneth C.; Ed. Thomson Pioneira; 1 edio;

3.

2004; 5 exemplares.

Bibliografia Complementar:
4.

Linguagens Formais: Teoria, Modelagem e Implementao; Ramos, Marcus V. M., Neto, Joo Jos; & Veja, talo Santiago; Ed. Bookman; 1 edio; 2009; 10 exemplares. Fundamentos Matemticos para a Cincia da Computao; Gersting, Judith L.; Ed. LTC; 5
edio; 2004; 17 exemplares. exemplares.

5.

6.

Linguagens Formais e Autmatos; Menezes, Paulo F. B.; Ed. Sagra Luzzato; 4 edio; 2002; 9 Elements of the Theory of Computation; Lewis, Harry R.; Ed. Prentice-Hall; 2 edio; 1998; 1 Introduction to Formal Language Theory; Harrison, Michael A.; Ed. Addison-Wesley; 1 edio;

7.

exemplar.
8.

1978; 1 exemplar.

Вам также может понравиться