Escolar Documentos
Profissional Documentos
Cultura Documentos
jr
Rogerio Eduardo da Silva, M.Sc.
2005/2
Sumario
1 Introducao 1
1.1 Evolucao das Linguagens de Programac ao . . . . . . . . . . . . . . . . . . 1
1.2 Introducao `a Compilac ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2.1 Fases da Compilac ao . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3 Ferramentas para Geracao de Compiladores . . . . . . . . . . . . . . . . . 6
2 Um Compilador Simples de uma Passagem 7
2.1 Denicao da Sintaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Analise Gramatical . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2.1 Exerccios Propostos . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3 Caractersticas da linguagem PASCAL
jr
. . . . . . . . . . . . . . . . . . . 10
2.3.1 Exerccios Propostos . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3 Analise Lexica 13
3.1 O Papel do Analisador Lexico . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2 Buferizacao de Entrada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3 Gramaticas e Linguagens Regulares . . . . . . . . . . . . . . . . . . . . . . 15
3.3.1 Exerccios Propostos . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.4 Especicacao e Reconhecimento de Tokens . . . . . . . . . . . . . . . . . . 17
3.4.1 Trabalho Pratico #1 . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4 Analise Sintatica 21
4.1 O Papel do Analisador Sint atico . . . . . . . . . . . . . . . . . . . . . . . . 21
4.2 Analise Sint atica Ascendente - BOTTOM UP . . . . . . . . . . . . . . . . 23
4.2.1 Algoritmo Empilhar-e-Reduzir . . . . . . . . . . . . . . . . . . . 23
4.3 Analise Sint atica Descendente - TOP DOWN . . . . . . . . . . . . . . . . 24
4.3.1 Analise Sintatica Preditiva . . . . . . . . . . . . . . . . . . . . . . . 25
4.3.2 Exerccios Propostos . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.4 Reconhecedor de Gramaticas Preditivas Descendentes . . . . . . . . . . . . 27
4.4.1 Algoritmo para Construcao da Tabela de Analise . . . . . . . . . . 29
4.4.2 Projeto de uma Gramatica para um Analisador Sintatico Preditivo
Ascendente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.4.3 Projeto de uma Gramatica para um Analisador Sintatico Preditivo
Descendente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.4.4 Exerccios Propostos . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.4.5 Trabalho Pratico #2 . . . . . . . . . . . . . . . . . . . . . . . . . . 38
i
5 Analise Semantica 41
5.1 Traduc ao Dirigida pela Sintaxe . . . . . . . . . . . . . . . . . . . . . . . . 41
5.1.1 Denic oes L-Atribudas . . . . . . . . . . . . . . . . . . . . . . . . . 43
5.1.2 Vericac oes de Contexto . . . . . . . . . . . . . . . . . . . . . . . . 44
5.2 Tabela de Smbolos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
5.2.1 Atributos dos Nomes dos Identicadores . . . . . . . . . . . . . . . 47
5.2.2 Hashing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.3 Projeto das Regras Semanticas . . . . . . . . . . . . . . . . . . . . . . . . . 50
5.3.1 Trabalho Pratico #3 . . . . . . . . . . . . . . . . . . . . . . . . . . 56
6 Geracao de Codigo Intermediario 59
6.1 Linguagens Intermedi arias . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
6.1.1 Representa coes Gracas . . . . . . . . . . . . . . . . . . . . . . . . 59
6.1.2 Notac ao Pos (e Pre) Fixadas . . . . . . . . . . . . . . . . . . . . . . 60
6.1.3 Codigo de Tres-Enderecos . . . . . . . . . . . . . . . . . . . . . . . 61
6.2 BackPatching (Retrocorrecao) . . . . . . . . . . . . . . . . . . . . . . . . . 64
7 Otimizacao de Codigo 67
7.1 Otimizacao Peephole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
7.2 Otimizacao de Blocos Sequenciais atraves de grafos . . . . . . . . . . . . . 68
7.2.1 Algoritmo para Construir o GAD de um bloco . . . . . . . . . . . . 69
7.2.2 Algoritmo para Ordenacao de um GAD . . . . . . . . . . . . . . . . 70
8 Geracao de Codigo Objeto 71
8.1 Maquina Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
8.1.1 Regras para Geracao de Codigo Objeto . . . . . . . . . . . . . . . . 76
8.1.2 Trabalho Pratico #4 . . . . . . . . . . . . . . . . . . . . . . . . . . 84
ii
Lista de Figuras
1.1 Processo de Compilacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Fases da Compilacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3
Arvore resultante da analise de um comando de atribuicao em PASCAL . . 4
2.1 Representacao da arvore gramatical da produc ao AXYZ . . . . . . . . . 8
2.2 Ambig uidade Gramatical . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.1 O papel do analisador lexico . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2 Buer de entrada para um analisador lexico . . . . . . . . . . . . . . . . . 15
3.3 Automato nito de reconhecimento de n umeros inteiros e reais . . . . . . . 17
3.4 AFD de reconhecimento de identicadores simples . . . . . . . . . . . . . . 18
3.5 AFD de reconhecimento de strings . . . . . . . . . . . . . . . . . . . . . . 18
4.1 Exemplo de
Arvore Sintatica . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.2 Derivac ao `a Esquerda e `a Direita . . . . . . . . . . . . . . . . . . . . . . . 22
4.3 Analise descendente com backtracking . . . . . . . . . . . . . . . . . . . . 25
4.4 Exemplos de Recursao `a Esquerda e `a Direita . . . . . . . . . . . . . . . . 27
4.5 Funcionamento de um Analisador Sint atico Descendente . . . . . . . . . . 28
5.1 Exemplo de
Arvore Decorada para a Expressao 3*5+4 . . . . . . . . . . . 42
5.2 Grafo de Dependencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
5.3 Tipos Simples e Construtor de Tipos . . . . . . . . . . . . . . . . . . . . . 44
5.4 Hashing com Encadeamento . . . . . . . . . . . . . . . . . . . . . . . . . . 49
6.1 Exemplo de Representac ao Graca de Operadores para a=b*c+b*2 . . . . 60
6.2 Backpatching para expressoes logicas . . . . . . . . . . . . . . . . . . . . . 65
7.1 Grafo Acclico Dirigido - GAD . . . . . . . . . . . . . . . . . . . . . . . . . 70
iii
Captulo 1
Introducao
Entende-se por linguagem como uma forma eciente de comunicac ao entre pessoas. Na
verdade a linguagem e um conjunto de palavras usadas, segundo certas regras, para a
formac ao de frases compreensveis por ambos os interlocutores (falantes).
Quando um dos interlocutores e o computador, se faz necessario o uso de uma lingua-
gem especial denominada linguagem de programac ao que permite a comunicacao entre
homem e maquina atraves da denic ao de comandos.
Uma L. P. e ser dita de baixo nvel, se esta somente aceitar comandos na propria
linguagem da maquina (0s e 1s) que e de difcil aplicacao. Ja as linguagens ditas de
alto nvel, sao representadas por ac oes proximas ao problema a ser resolvido que sao,
posteriormente, traduzidas para a linguagem de maquina, atraves de um agente especial
denominado compilador ou interpretador.
Concluindo: compilador e um programa capaz de traduzir um certo programa fonte
(escrito em uma linguagem fonte) para outro programa objeto (escrito em uma linguagem
objeto) geralmente a propria linguagem de maquina.
1.1 Evolucao das Linguagens de Programacao
Cronologicamente, as L. P.s sao classicadas em cinco gerac oes: (1
a
) linguagens de
maquina; (2
a
) linguagens simbolicas (Assembly); (3
a
) linguagens orientadas ao usuario;
(4
a
) linguagens orientadas `a aplicacao e (5
a
) linguagens de conhecimento.
As duas primeiras sao consideradas linguagens de baixo nvel, enquanto que as demais
de alto nvel.
Os primeiros computadores so podiam ser programados atraves da sua propria lingua-
gem de maquina (codigo binario), onde cada operacao possua sua representacao binaria
que era passada `a maquina atraves de circuitos eletricos. Esse processo, alem de extrema-
mente difcil e cansativo, era altamente sujeito a erros devido a sua grande complexidade
de execucao.
A seguir, como uma primeira tentativa de simplicac ao, surgem as linguagens simb oli-
cas ou de montagem (Assembly). Agora, extensas seq uencias binarias sao substitudas por
mnemonicos que sao palavras especiais que representam certas ac oes basicas. Exemplo
MOV, JMP, etc. Os mnemonicos precisavam ser traduzidos para a linguagem de maquina
antes da sua execuc ao.
1
A 3
a
geracao surgiu na decada de 60, com as linguagens procedimentais como FOR-
TRAN, PASCAL e ALGOL e declarativas como LISP e PROLOG. Nas linguagens pro-
cedimentais, um programa especica uma seq uencia de passos a serem seguidos para a
soluc ao do problema. Ja as linguagens declarativas sao subdivididas em funcionais e
logicas. A programacao funcional se baseia na teoria das funcoes recursivas, enquanto
que, as linguagens logicas se baseiam em proposic oes da logica de predicados (fatos e
regras).
Devido ao fato de programas escritos em linguagens de 3
a
geracao serem muito ex-
tensos e de difcil manutenc ao, surgiram as linguagens de aplicacao (4
a
geracao), onde o
desenvolvedor deixa de se preocupar com atividades secundarias e trata apenas da co-
dicac ao do problema (foco do programador deixa de ser a codicac ao para ser a analise
do problema). Aspectos como: interface de entrada e sada, relatorios, etc. sao resol-
vidos pela propria linguagem atraves de um banco de dados e dicionarios associados `as
aplicac oes desenvolvidas.
A 5
a
geracao das linguagens de programacao atua em problemas altamente comple-
xos onde a representac ao de conhecimento se faz necessaria para sua soluc ao, como os
problemas enfrentados pela inteligencia articial. A linguagem PROLOG e aceita como
pertencente a esta geracao.
1.2 Introducao `a Compilacao
Conforme ja dito, um compilador nada mais e do que um programa tradutor responsavel
por converter uma certa linguagem fonte em outra linguagem objeto (ver Figura 1.1).
Usualmente a linguagem objeto e a propria linguagem de maquina, mas nao necessaria-
mente.
Programa
Fonte
Programa
Objeto
COMPILADOR
Mensagem
de Erro
Figura 1.1: Processo de Compilacao
Existem dois tipos basicos de tradutores: os compiladores e os interpretadores. Os
primeiros fazem uma analise completa sobre o programa fonte, caso nao encontre erros
faz a traduc ao de todo o codigo fonte para a linguagem objeto que sera posteriormente
executado em uma maquina capaz de faze-lo. Ja os interpretadores nao tem essa preo-
cupac ao holstica (analise completa) sobre o programa fonte. Um interpretador traduz
um comando fonte por vez e o executa em uma maquina virtual (programa que simula o
funcionamento de um computador) sem a necessidade da criac ao do programa objeto.
Interpretadores sao mais simples de serem implementados, porem, compiladores geram
execuc oes mais rapidas de programas, pois nao ha a perda de tempo de traducoes virtuais
a cada nova instruc ao executada.
2
1.2.1 Fases da Compilacao
O processo de compilacao pode ser dividido em dois grupos de etapas: as etapas de analise
e as etapas de sntese. Na analise, o programa fonte e percorrido em busca de erros
de programac ao (inconsistencias com a linguagem fonte), ja na etapa de sntese (apos a
vericac ao da corretude do programa de origem), efetua-se a traducao, propriamente dita,
do codigo fonte para a linguagem objeto em questao. A gura 1.2 abaixo ilustra todo o
processo:
Tabela de
Smbolos
Anlise Lxica
Anlise Sinttica
Anlise Semntica
Gerao de Cdigo Intermedirio
Otimizao de Cdigo
Gerao de Cdigo Objeto
Manipulador
de Erros
ANLISE
SNTESE
Programa Fonte
Programa Objeto
Figura 1.2: Fases da Compilacao
A analise lexica ou scanning e a primeira etapa do processo de compilacao. Ela
e responsavel por analisar linearmente os caracteres do programa fonte e agrupa-los em
unidades lexicas denominadas tokens. O token e o elemento mais basico da programac ao;
ele e representado por um conjunto de caracteres que apresentam um signicado claro
para o programa. Exemplo: Para o seguinte codigo em PASCAL:
Media := Nota1 + Nota2 * 2
Os caracteres poderiam ser agrupados da seguinte forma:
1. O identicador Media
2. O smbolo de atribuicao :=
3. O identicador Nota1
4. O sinal de adic ao +
5. O identicador Nota2
6. O sinal de multiplicac ao *
3
7. O n umero 2
Os espacos em branco presentes na sentenca sao ignorados durante a analise.
O resultado da analise lexica e uma lista contendo todos os tokens encontrados no
programa fonte. Essa lista lexica e ent ao o elemento de entrada para a analise sintatica
ou analise gramatical (parsing), onde e vericado se os tokens podem ser agrupados em
sentencas validas (comandos, expressoes, etc.) da linguagem fonte. Normalmente, esses
agrupamentos sao realizados atraves da construc ao de uma arvore sintatica conforme e
apresentado na gura 1.3:
Comando de
Atribuio
Identificador
Smbolo de
Atribuio
Expresso
Expresso Expresso
Operador
Aritmtico
Expresso Expresso
Operador
Aritmtico
Media :=
Nota1 +
Nota2 * 2
Figura 1.3:
Arvore resultante da analise de um comando de atribuicao em PASCAL
A estrutura hierarquica de um programa e usualmente expressa por regras recursivas.
Por exemplo, poderamos ter as seguintes regras como parte denic ao de expressoes:
1. Qualquer identicador e uma expressao
2. Qualquer n umero e uma expressao
3. Se expressao
1
e expressao
2
sao expressoes validas, ent ao expressao
1
op. aritmetico
expressao
2
tambem e
A estrutura utilizada para a representacao dessas regras e a gramatica livre de contexto
(GLC), normalmente apresentada na Forma Normal de Backus (BNF).
Exemplo:
comando ::= while | atribuic
ao | . . .
while ::= while expr bool do comando
atribuic
.
Atributos para os tokens
Um token e comumente representado como um par [LEXEMA, CLASSE], onde a classe
indica qual foi o padrao utilizado para reconhecer o lexema.
Outras informacoes adicionais podem ser incorporadas `a descricao do token, de acordo
com as necessidades das fases subseq uentes, como por exemplo, n umero da linha e co-
luna onde o token foi reconhecido no arquivo fonte e n umero de caracteres lidos ate o
reconhecimento, seria exemplos de informacoes adicionais uteis caso um erro lexico seja
detectado.
3.2 Buferizacao de Entrada
Conforme ja visto, o processo de analise lexica e normalmente realizado efetuando-se uma
leitura do arquivo fonte de entrada, caracter a caracter, o que resulta em um processo
signicativamente lento.
Existem 3 alternativas de implementac ao de analisadores lexicos (listados em ordem
crescente de complexidade de implementacao):
1. Usar ferramentas de construc ao de analisadores lexicos (como o Lex), atraves de
expressoes regulares;
14
2. Escrever um programa numa linguagem de programacao convencional, usando seus
recursos de entrada e sada;
3. Escrever um programa numa linguagem de montagem e manipular explicitamente
a entrada e a sada.
Alguns aspectos a serem considerados no projeto de implementac ao de um scanner:
Buer
Em muitas linguagens, existem momentos que o analisador lexico precisa examinar varios
caracteres `a frente do lexema, antes que seja anunciado um reconhecimento.
Os caracteres que foram lidos e nao foram aproveitados no lexema sob analise, sao
ent ao, devolvidos ao uxo de entrada para que possam ser lidos novamente na analise de
outro lexema posterior.
Assim sendo, um buer de entrada que acumula varios caracteres e criado, conforme
a gura 3.2. O processo de analise lexica e realizado sobre este buer. Os tokens que
foram reconhecidos sao eliminados do buer e novos caracteres sao adicionados a ele ate
que todo o arquivo fonte seja lido e analisado.
E = m * c * c eof
apontador
Figura 3.2: Buer de entrada para um analisador lexico
Em casos mais simples, a entrada pode ser realizada caracter a caracter, contendo
apenas um buer de armazenamento dos caracteres lidos.
3.3 Gramaticas e Linguagens Regulares
A seguir, serao revisados alguns conceitos importantes da disciplina linguagens formais e
maquinas (LFM) para ent ao prosseguir na analise lexica.
Gramatica
Uma gramatica e um mecanismo gerador de sentencas de uma dada linguagem.
E denida
pela quadrupla (V
N
, V
T
, P, S), onde: V
N
representa o conjunto de smbolos nao-terminais
da linguagem; V
T
representa o conjunto de smbolos terminais ou alfabeto; P e um con-
junto de regras de produc ao e S e o axioma da gramatica (smbolo inicial).
As regras de producao sao denidas na forma
1
|
2
| . . . |
N
, onde representa
um smbolo nao-terminal e os
N
representam sentencas podendo conter tanto smbolos
terminais quanto nao-terminais.
15
Seq uencia de Derivacao
Entende-se por derivac ao ao processo de substituicao de por um dos
N
na regra de
producao, desta forma obtendo-se uma nova sentenca que por sua vez, pode ser novamente
derivada por outra regra. Uma seq uencia de derivac ao e uma serie de derivac oes sucessivas
que permitem a geracao de uma determinada sentenca da linguagem.
Gramatica Regular
Uma gramatica e dita ser regular se todas as suas regras de produc ao respeitam a forma
A B ou A , onde A,B sao smbolos nao-terminais e e uma sentenca contendo
somente smbolos terminais.
Gramatica Linearmente `a Esquerda e `a Direita
Quando uma regra de producao e da forma A B, ou seja, novos smbolos nao-terminais
sao inseridos `a direita da sentenca, diz-se se tratar de uma gramatica linearmente `a direita.
Se a producao for da forma A B denomina-se como linearmente `a esquerda.
Expressoes Regulares
Uma expressao regular representa uma determinada linguagem atraves de formulas in-
dutivas.
Simbologia adotada:
= sentenca vazia (comprimento = 0);
a | b = representa uma selec ao entre a sentenca a ou b;
A
.
Exemplo de producoes de uma G.L.C.: S SS+ | SS | a.
Arvores de Derivacao
aAbcde
aAde
aABe
S
4.2.1 Algoritmo Empilhar-e-Reduzir
Este procedimento de analise sint atica ascendente consiste de dois passos:
1. Escolha de um candidato a reduc ao (handle);
2. Reduc ao do candidato pelo nao-terminal A `a esquerda da produc ao A ;
3. Repetir os passos 1 e 2 ate que a sentenca tenha sido reduzida ao axioma da
gramatica.
Um candidato e uma subcadeia que reconhece o lado direito de uma produc ao e cuja
reduc ao ao nao-terminal do lado esquerdo da produc ao representa um passo ao longo do
percurso de uma derivac ao.
ao
id+id*id $ empilhar
+id*id $id reduzir E id
+id*id $E empilhar
id*id $E+ empilhar
*id $E+id reduzir E id
*id $E+E reduzir E E +E
*id $E empilhar
id $E* empilhar
$ $E*id reduzir E id
$ $E*E reduzir E E E
$ $E aceitar
Sao apenas 4 as operacoes possveis por este metodo: empilhar, reduzir, aceitar ou
erro.
Conitos durante a Analise Sintatica de Empilhar e Reduzir
Existem gramaticas livres de contexto para as quais o procedimento empilhar-e-reduzir
nao pode ser utilizado, porque, em certos casos, o analisador pode atingir um estado tal,
que:
Mesmo conhecendo toda a pilha e o proximo smbolo de entrada, nao pode decidir
entre empilhar e reduzir. Isto e chamado de conito empilhar/reduzir.
Outro conito possvel, o reduzir/reduzir, ocorre quando nao e possvel optar entre
as diversas reducoes possveis.
4.3 Analise Sintatica Descendente - TOP DOWN
A analise sint atica top-down pode ser vista como uma tentativa de se encontrar uma
derivac ao mais `a esquerda para uma cadeia de entrada, ou ainda, como de se construir a
arvore gramatical a partir da raiz em direc ao `as folhas.
O processo de analise pode ser feito de forma recursiva ou nao, onde a forma recur-
siva pode ser realizada com ou sem retrocesso (backtracking), dependendo das regras de
producao gramatica.
Analise Sintatica Recursiva com Retrocesso
A construcao da arvore e feita a partir da raiz, expandindo sempre o nao-terminal mais `a
esquerda primeiro. Quando existe mais de uma regra de produc ao para o nao-terminal a
ser expandido, a opc ao escolhida e func ao do smbolo corrente na ta de entrada (token
sob analise). Se o token nao dene a producao a ser usada, entao todas as alternativas
vao ser tentadas ate que se obtenha sucesso (ou todas falhem).
Exemplo 1:S cAd A ab | a.
Vericar se a gramatica gera a sentenca cad.
Exemplo 2: S cA A aB B D | bD D d
24
S
S
c A d
S
c A d
a b
S
c A d
a
falha!
sucesso!
Figura 4.3: Analise descendente com backtracking
A analise sint atica e dita ser uma analise sintatica preditiva caso nao seja necessario a
realizac ao de retrocesso no processo e pode ser implementada de forma recursiva ou nao
(atraves da utilizac ao de uma pilha).
4.3.1 Analise Sintatica Preditiva
O processo de analise preditiva (sem retrocesso) exige modicac oes na gramatica original
para analise:
eliminac ao de recursao `a esquerda;
fatorac ao `a esquerda das regras de produc ao;
os nao-terminais que apresentarem mais de uma regra de producao, tenham o pri-
meiro terminal derivavel unico (capaz de identicar a producao a ser analisada).
Ou seja, deve ser possvel determinar, para um dado smbolo a, qual das producoes
deve ser derivada.
Exemplo: No exemplo 2 visto acima a produc ao B D | bD D d apresenta duas
alternativas de derivac ao. A escolha e feita a partir do primeiro terminal para cada regra
(d ou b).
O conjunto de smbolos terminais que iniciam senten cas derivaveis a partir de uma
producao b e denominado FIRST() ou PRIMEIRO().
Exemplo: FIRST(S) = {c}; FIRST(A) = {a}; FIRST(B) = {b, d}; FIRST(D) = {d}.
As regras que denem o conjunto FIRST sao:
25
Se , ent ao e um elemento de FIRST.
Se a, sendo a um smbolo terminal, entao a pertence a FIRST.
Se X
1
X
2
. . . X
N
, sendo X
1
X
2
. . . X
N
elementos nao-terminais, entao FIRST()
= FIRST(X
1
). Se em FIRST(X
1
) constar o elemento , entao incluir FIRST(X
2
)
em FIRST() e assim por diante.
Eliminacao da Recursao `a Esquerda
e A
A
A'
A
A
A
A
A'
A'
A'
A'
cZ | .
4.4 Reconhecedor de Gramaticas Preditivas Descen-
dentes
Um reconhecedor preditivo descendente (orientado por tabela) compreende uma ta de
entrada, uma pilha e uma tabela de analise, conforme e mostrado na gura 4.5. A ta
contem a sentenca a ser analisada seguida de $. A pilha contem os smbolos utilizados
durante o processo de analise. A tabela de analise e uma matriz com n linhas (correspon-
dendo aos smbolos nao-terminais) e t+1 colunas (correspondendo aos smbolos terminais
mais o smbolo especial $).
Considerando X o elemento no topo da pilha e a o smbolo de entrada sob analise, o
analisador executa uma de tres acoes possveis:
1. se X = a = $, o analisador para, aceitando a sentenca;
2. se X = a = $, o analisador desempilha a e avan ca o cabecote de leitura para o
proximo smbolo na ta de entrada;
3. se X e um smbolo nao-terminal, o analisador consulta a tabela M[X,a] da tabela
de analise. Essa entrada podera conter uma producao da gramatica ou ser vazia.
Supondo M[X,a] = { X XY Z }, o analisador substitui X (no topo da pilha) por
ZYX (cando X no topo). Se M[X,a] for vazio isto e um erro sint atico.
Na implementac ao de um analisador sintatico, a maior diculdade esta na construcao
da tabela de analise. Para construir essa tabela, e necessario computar duas funcoes
associadas `a gramatica: FIRST e FOLLOW.
27
Tabela de
Anlise
Parser
a + b $
X
Y
Z
Figura 4.5: Funcionamento de um Analisador Sintatico Descendente
O algoritmo para calcular a funcao FIRST ja foi visto anteriormente. O algoritmo
para calcular a funcao FOLLOW e apresentado a seguir:
1. Se S e o smbolo inicial da gramatica e $ e o marcador de m de sentenca, entao $
esta em FOLLOW(S);
2. Se existe producao do tipo A X, ent ao todos os terminais de FIRST(), fazem
parte de FOLLOW(X);
3. Se existe produc ao do tipo A X, ou A X, sendo que , entao todos
os terminais que estiverem em FOLLOW(A) fazem parte de FOLLOW(X).
Dada a gramatica G = ({E, E
, T, T
TE
|
T FT
FT
|
F F | id
Clausula First
Convem iniciar o processo pelos nao-terminais que gerem conjuntos triviais. No exemplo,
temos os nao-terminais F, E e T que so geram elementos terminais (ou vazio):
F = {, id}
E
= {, }
T
= {, }
Como T deriva apenas em FT e F nao leva em vazio, conclui-se que FIRST(T) =
FIRST(F). E ainda, FIRST(E) = FIRST(T) = FIRST(F) = {, id}.
28
Clausula Follow
Pela regra 1 temos que FOLLOW(E) = {$}. Pela regra 3 tem-se que FOLLOW(E)
= FOLLOW(E). FOLLOW(T) e obtido a partir da uniao dos conjuntos obtidos pela
aplicac ao da regra 2 em (E
TE
) e regra 3 em (E
. E nalmente,
FOLLOW(F) = FIRST(T) + FOLLOW(T). Aplicac ao das regras 2 e 3 em T
FT
|
, ou seja FOLLOW(F) = {, , $}.
4.4.1 Algoritmo para Construcao da Tabela de Analise
Metodo:
Para cada producao X , execute os passos 2 e 3 (para criar a linha X da tabela
M);
Para cada terminal a de FIRST(), adicione a produc ao X a M[X,a];
Se FIRST() inclui a palavra vazia, ent ao adicione X a M[X,b] para cada b
em FOLLOW(X);
Aplicando-se o algoritmo acima `a gramatica de expressoes logicas temos:
Para E TE
.
Para E
TE
tem-se FIRST(TE
) = {} entao, M[E, ] = E
TE
.
Para E
.
Para T FT
.
Para T
FT
tem-se FIRST(FT
FT
.
Para T
.
Para F F tem-se FIRST(F) = {} entao, M[F, ] = F F.
Para F id tem-se FIRST(id) = {id} ent ao,M[F,id] = F id.
id $
E E TE
E TE
E E
TE
T T FT
T FT
T T
FT
F F id F id
Se, em cada entrada da Tabela de Analise, existe apenas uma producao, entao a
gramatica que originou a tabela e dita ser do tipo LL(1), ou seja: as sentencas geradas
pela gramatica sao passveis de serem analisadas da esquerda para a direita (Left to
Right), produzindo uma deriva cao mais `a esquerda (Leftmost Derivation), levando
em conta apenas um smbolo da entrada.
Exerccio: Considerando a gramatica para a linguagem a ser reconhecida pelo prototi-
po de compilador para analise descendente, construir a tabela de analise resultante.
29
4.4.2 Projeto de uma Gramatica para um Analisador Sintatico
Preditivo Ascendente
Analisa gramaticas do tipo LR(k), ou seja, left-to-right e rightmost derivation com k
smbolos lidos da entrada a cada etapa de analise.
Porque usar analise sint atica ascendente LR?
porque e possvel ser elaborados reconhecedores para todas as GLC, sem restricao;
porque o metodo de analise LR e tao eciente quanto os demais metodos de analise;
porque um analisador LR consegue encontrar um erro sint atico o mais cedo possvel
em uma analise da esquerda para a direita.
As gramaticas GLC para as quais e viavel a implementacao manual de reconhece-
dores ascendentes (devido `a complexidade de implementa cao) apresentam as seguintes
restric oes:
nenhum lado direito das producoes seja
nenhum lado direito tenha dois nao-terminais adjacentes (gramatica de operadores)
Exemplo: E E +E | E E | E E | E/E | (E) | E | id
Um forma simples de se implementar um reconhecedor ascendente e atraves da analise
de precedencia de operadores, porem, justamente devido `a sua simplicidade, uma serie de
restric oes estao associadas a estes:
diculdades de analisar operadores com mais de um signicado semantico (ex.: ope-
rador unario e binario de subtracao)
somente uma pequena classe de linguagens pode ser analisada por esta alternativa,
apesar disso, ja foram desenvolvidos analisadores de precedencia para linguagens
inteiras.
Na analise de precedencia temos denidos as relac oes de precedencia entre os opera-
dores, sendo (a < b) onde a confere precedencia a b; (a = b) onde a possui a mesma
precedencia de b e (a > b) a tem precedencia sobre b.
Seja o exemplo da gramatica anterior onde a precedencia dos operadores e dada por:
id + * $
id > > >
+ < > < >
* < > > >
$ < < <
Analisando a expressao: id+id*id temos as seguintes relacoes de precedencia:
$ < id > + < id > < id > $
O algoritmo para se determinar o handle para reducao e:
30
1. Percorrer a cadeia, a partir da esquerda ate que o primeiro > seja encontrado.
2. Percorrer, entao, de volta (para a esquerda) por sobre quaisquer relacoes (=) ate
que < seja encontrado.
3. O handle contem tudo `a esquerda do primeiro > e `a direita do < , incluindo
quaisquer nao-terminais presentes.
No exemplo acima, o primeiro handle e dado pelo primeiro id encontrado que pode
ser reduzido para o nao-terminal E (segundo a gramatica vista), seguido pelos proximos
dois ids da sentenca. A seguir, a senten ca obtida caria $ E + E * E $; removendo-se os
nao-terminais e acrescentando-se as relac oes de precedencia temos: $ < + < > $,
indicando que a proxima reducao deve ser realizada sobre o operador * (e seus respec-
tivos operandos associados E* E).
Devido ao fato da sua implementa cao nao ser trivial, a solucao de implementacao mais
viavel para este tipo de gramatica e fazer uso de um gerador de analisadores sint aticos,
como o YACC ou BISON.
4.4.3 Projeto de uma Gramatica para um Analisador Sintatico
Preditivo Descendente
Considerando o uso de analisadores sintaticos descendentes preditivo algumas preocupa-
c oes quanto a gramatica a ser utilizada, devem ser tomadas: eliminar ambig uidade, eli-
minar as recursoes `a esquerda e fatorar `a esquerda a gramatica.
Analisando um Programa Simples
A seguir, e apresentado um exemplo de um programa simples na linguagem PASCAL
jr
:
var: float N1, N2, M; int Ct;
const: int Qtde = 10;
func float _Media(float a,float b)
float media;
{
media = (a+b)/2.0;
return media;
}
main( ) {
Ct = 0;
do {
print("Digite duas notas:");
scanf(N1,N2);
printl("Media = ",_Media(N1,N2));
Ct ++;
} while(Ct != Qtde);
}
Todo programa em PASCAL
jr
respeita a seguinte estrutura:
31
[ Declarac ao de Variaveis e Constantes ]
[ Declaracao de Sub-Rotinas ]
<Programa Principal>
onde: [ ] indica secao opcional e <> indica sec ao obrigatoria.
Pode-se descrever esta estrutura na forma de uma regra de producao de uma GLC da
seguinte forma, onde o nao-terminal Programa sera o axioma da gramatica da linguagem
PASCAL
jr
:
Programa AreaDecl AreaSubRot Principal
Analisando a Secao de Declaracao de Variaveis e Constantes
Esta sec ao declara todas as variaveis e constantes utilizadas pelo programa.
E possvel a
declarac ao de varias areas de declaracao de variaveis e/ou constantes simultaneamente.
Um programa pode ainda nao conter esta secao.
AreaDecl AreaDeclVar AreaDecl |
AreaDeclConst AreaDecl |
AreaDeclVar prVar DoisPt DeclVars
DeclVars Tipo ListaID PtVirg DeclVars
DeclVars Tipo ListaID PtVirg DeclVars |
Tipo prInt | prFloat | prChar | prString | prBool
ListaID Identicador ListaID
ListaID Virg Identicador ListaID |
AreaDeclConst prConst DoisPt DeclConsts
DeclConsts Tipo ListaIDConst PtVirg DeclConsts
DeclConsts Tipo ListaIDConst PtVirg DeclConsts |
ListaIDConst Identicador Atrib Valor ListaIDConst
ListaIDConst Virg Identicador Atrib Valor ListaIDConst |
Valor OpAritSubt Numeros | Numeros |
ConstChar | ConstString | prTrue | prFalse
Numeros NumeroInteiro | NumeroReal
Analisando a Secao de Declaracao de Procedimentos e Funcoes
A secao de declaracao de procedimentos e funcoes declara todas as sub-rotinas utilizadas
pelo programa.
E possvel a declaracao de varias areas de declaracao de sub-rotinas
simultaneamente. Um programa pode ainda nao conter esta secao.
32
AreaSubRot AreaProc AreaSubRot |
AreaFunc AreaSubRot |
AreaProc prProc IdentSR AbrePar ListaParam
FechaPar AreaDecl BlocoCom
ListaParam Tipo Identicador ListaParam |
ListaParam Virg Tipo Identicador ListaParam |
AreaFunc prFunc Tipo IdentSR AbrePar ListaParam
FechaPar AreaDecl BlocoCom
Analisando o Programa Principal
O programa principal e o ponto onde inicia-se a execucao do codigo fonte. Ela e denida
pela func ao main. Apesar da linguagem PASCAL
jr
nao permitir a passagem de
parametros para esta func ao, ainda sim utilizar-se-ao os parenteses ( ) na sintaxe do
comando meramente por uma questao didatica. Esta sec ao e obrigatoria em qualquer
programa.
Principal prMain AbrePar FechaPar BlocoCom
Analisando um Bloco de Comandos
Um bloco de comandos pode ser entendido como um comando composto por uma lista
de outros comandos simples (ou outros blocos) podendo (em alguns casos) ser separados
por ; e delimitados por { e }. Sendo assim:
BlocoCom AbreChaves ListaCom FechaChaves
ListaCom Comando ListaCom |
Comando Condicional | RepetPre | RepetPos PtVirg |
RepetCont | Entrada PtVirg | Saida PtVirg |
Atrib PtVirg | SubRot PtVirg | BlocoCom |
Retorno PtVirg |
Analisando o comando Atribuicao
Pode ser realizado atraves de 7 diferentes operadores:
= atribuic ao simples
+= atribuic ao apos adic ao (X+ = Y X = X +Y )
33
-= atribuicao apos subtrac ao (X = Y X = X Y )
*= atribuicao apos multiplica cao (X = Y X = X Y )
/= atribuicao apos divisao (X/ = Y X = X/Y )obs.:Nao preve divisao por zero
++ atribuicao incremental (X + + X = X + 1)
- - atribuicao decremental (X X = X 1)
Exemplo de uma gramatica que reconhece esses comandos:
Atrib Identicador SimbAtrib Expr |
Identicador SimbAtribSoma Expr |
Identicador SimbAtribSubt Expr |
Identicador SimbAtribMult Expr |
Identicador SimbAtribDivi Expr |
Identicador SimbIncr | Identicador SimbDecr
porem, temos problemas de fatoracao. Fatorando `a esquerda estas producoes temos:
Atrib Identicador Atrib
Atrib SimbAtrib Expr |
SimbAtribSoma Expr |
SimbAtribSubt Expr |
SimbAtribMult Expr |
SimbAtribDivi Expr |
SimbIncr | SimbDecr
Analisando o comando Condicional
O comando condicional (sem fatorac ao) caria:
Condic prIf AbrePar Expr FechaPar Comando |
prIf AbrePar Expr FechaPar Comando
prElse Comando
e apos fatoracao teremos:
Condic prIf AbrePar Expr FechaPar Comando Condic
Condic prElse Comando |
34
Analisando os comandos de Repeticao
Os comandos de repeticao podem ser reconhecidos por:
RepetPos prDo ListaCom prWhile AbrePar Expr FechaPar
RepetPre prWhile AbrePar Expr FechaPar Comando
RepetCont prFor AbrePar Atrib PtVirg Expr PtVirg Atrib
FechaPar Comando
Analisando os comandos para chamada a Sub-Rotinas
Os comandos para chamadas a sub-rotinas incluem o comando < Retorno > que deve
ser usado nas chamadas a func oes.
SubRot IdentSR AbrePar ListaExpr FechaPar
Retorno prReturn Expr
Analisando os comandos de Entrada e Sada
Para os comandos de entrada e sada temos:
35
Entrada prScanf AbrePar ListaVar FechaPar
Saida prPrint AbrePar ListaExpr FechaPar |
prPrintl AbrePar ListaExpr FechaPar
ListaExpr Expr ListaExpr |
ListaExpr Virg Expr ListaExpr |
Analisando Expressoes Logicas e Aritmeticas
Para descrever sentencas que formam expressoes aritmeticas compostas das cinco opera-
c oes basicas (adicao, subtracao, multiplica cao, divisao e potenciac ao), tendo como ope-
randos: identicadores de variaveis e constantes, n umeros inteiros e reais, chamadas a
sub-rotinas e ainda permitir o uso de parenteses e do operador unario de sinal -; a
representac ao mais simples possvel seria:
ExprAr ExprAr OpAdic ExprAr |
ExprAr OpSubt ExprAr |
ExprAr OpMult ExprAr |
ExprAr OpDivi ExprAr |
ExprAr OpPote ExprAr |
AbrePar ExprAr FechaPar |
OpSubt ExprAr | SubRot |
Identicador | NumeroInteiro | NumeroReal |
ConstCaracter | ConstString
Exerccio: Montar a arvore gramatical para a expressao 2 (X 5.0) + 10/B
Apesar de que, com esta gramatica, e possvel gerar qualquer expressao aritmetica sim-
ples, esta nao leva em considerac ao todas as restricoes ja estudadas para a implementacao
de reconhecedores de gramatica TOP-DOWN.
O primeiro problema que se percebe e o fato da gramatica anterior nao considerar a
questao da precedencia de operadores. Para resolver este problema deve-se inserir novos
elementos nao-terminais `a gramatica:
ExprAr ExprAr OpAdic TermoAr |
ExprAr OpSubt TermoAr | TermoAr
TermoAr TermoAr OpMult FatorAr |
TermoAr OpDivi FatorAr | FatorAr
FatorAr FatorAr OpPote ElementoAr | ElementoAr
ElementoAr AbrePar ExprAr FechaPar |
36
OpSubt ExprAr | SubRot |
Identicador | NumeroInteiro | NumeroReal |
ConstCaracter | ConstString
Exerccio: Montar a arvore gramatical para a expressao 2 (X 5.0) + 10/B
A ideia e gerar os elementos de menor precedencia mais proximos `a raiz da arvore
sint atica e os de maior precedencia, mais proximos `as folhas.
Novamente temos problemas com a solucao proposta: recursao `a esquerda. A nova
gramatica apos realizado o processo (ja estudado) de eliminac ao da recursao `a esquerda,
temos:
ExprAr TermoAr ExprAr
ExprAr OpAdic TermoAr ExprAr |
OpSubt TermoAr ExprAr |
TermoAr FatorAr TermoAr
TermoAr OpMult FatorAr TermoAr |
OpDivi FatorAr TermoAr |
FatorAr ElementoAr FatorAr
FatorAr OpPote ElementoAr FatorAr |
ElementoAr AbrePar ExprAr FechaPar |
OpSubt ExprAr | SubRot |
Identicador | NumeroInteiro | NumeroReal |
ConstCaracter | ConstString
Exerccio: Montar a arvore sintatica para a expressao: 2 (X 5.0) + 10/B.
Analisando Expressoes Logicas
Uma expressao logica e, na verdade, uma comparac ao entre resultados de expressoes
aritmeticas, ou ainda, a uniao de duas expressoes aritmeticas atraves de um operador
relacional.
Sao possveis ainda, expressoes logicas mais complexas atraves da uniao de duas ex-
pressoes logicas simples por operadores logicos.
Expr TermoLog Expr Ternario
Ternario Interrog Expr DoisPt Expr |
Expr OpLogAnd TermoLog Expr |
OpLogOr TermoLog Expr |
OpLogXor TermoLog Expr |
TermoLog FatorLog TermoLog
37
TermoLog OpRelacMaior FatorLog |
OpRelacMenor FatorLog |
OpRelacMenorIgual FatorLog |
OpRelacMaiorIgual FatorLog |
OpRelacIgual FatorLog |
OpRelacDifer FatorLog |
FatorLog ExprAr | OpLogNeg Expr |
prTrue | prFalse
e ainda, ElementoAr AbrePar Expr FechaPar.
4.4.4 Exerccios Propostos
1. Montar a arvore sint atica da expressao (A + 5 == B/C 2) && D || ! A >= B
2. Criar a clausula FIRST para as produc oes da gramatica preditiva descendente como
forma de vericar sua implementac ao.
3. Criar a arvore gramatical para o programa abaixo:
var: float A, B, C;
main() {
scanf(A,B);
C=A+B*2;
print(C);
}
4.4.5 Trabalho Pratico #2
Implementar um modulo (sub-rotina) analisador sint atico descendente para um prototipo
de compilador para a linguagem PASCAL
jr
vista em aula.
Caractersticas:
Do modulo parser:
A sub-rotina retorna um ag indicando sucesso ou nao da analise sint atica.
Utiliza os tokens provenientes do modulo scanner (ja implementado) para simular a
lista lexica.
Implementa uma pilha de execuc ao (fsica ou por recursividade) para analise das
regras de produc ao da gramatica vista.
Do programa a ser criado:
Abre um arquivo fonte para analise.
38
Executa a analise da produc ao axioma da gramatica.
Fecha o arquivo fonte ao nal da compilacao.
Para o processo de compilac ao caso um erro seja encontrado.
Exibe erros de compilac ao (se ocorrerem) ou mensagem de sucesso.
Criterios de Avaliacao:
Implementa cao usando linguagem C ou C++.
Entrega de fontes e executavel (em um arquivo zipado) via e-mail:
rsilva@joinville.udesc.br ou professor.rogerio@gmail.com
Obrigatoriamente o mesmo grupo do trabalho anterior.
Valor do trabalho: 10.0 (25% da nota pratica).
Data de Entrega: A definir
Punic oes:
de 10% por erro sint atico nao analisado corretamente.
de 12.5% por erro lexico nao analisado corretamente.
de 20% do valor do trabalho por dia de atraso.
de 20% do valor do trabalho para a entrega nao conforme dos arquivos e/ou
formato pedidos.
de 50% do valor do trabalho para o caso de nao executar ou travar (apos teste
em 2 computadores, sendo um o do professor).
de 100% do valor do trabalho para o caso de copias (mesmo de trabalhos de
semestres anteriores).
Prazo maximo para defesa e arguic ao sobre o trabalho: 5 dias letivos apos entrega.
Punic oes:
de 25% para arguicao nao respondida ou respondida incorretamente. Obs.: A
arguicao e individual.
de 33% ponto por dia de atraso da defesa.
39
40
Captulo 5
Analise Semantica
5.1 Traducao Dirigida pela Sintaxe
A ideia e associar informacoes aos smbolos gramaticais que representam a construc ao de
uma LLC; tais informac oes sao atributos dos smbolos segundo certas regras semanticas
associadas `as produc oes da gramatica.
Existem duas notacoes para associar regras semanticas `as produc oes: denicoes diri-
gidas pela sintaxe e esquemas de traducao.
Denicoes Dirigidas pela Sintaxe
Uma denicao dirigida pela sintaxe e uma GLC na qual cada smbolo gramatical pos-
sui um conjunto associado de atributos, particionados em dois subconjuntos: atributos
sintetizados e herdados.
Um atributo e dito ser sintetizado se seu valor foi obtido a partir da computacao
dos valores dos lhos daquele no da arvore e dito ser herdado se foi obtido a partir da
computac ao dos irmaos e pai do respectivo no.
Uma arvore gramatical mostrando os valores dos atributos a cada no e denominada
de uma arvore gramatical anotada ou decorada.
Para cada produc ao do tipo A , temos associado a ela uma regra semantica da
forma b := f(c
1
, c
2
, . . . , c
n
), onde f e uma funcao que:
Ou b e um atributo sintetizado de A e c
1
, c
2
, . . . , c
n
sao atributos pertencentes aos
smbolos gramaticais da producao ou,
b e um atributo herdado, pertencente a um dos smbolos gramaticais do lado direito
de producao e c
1
, c
2
, . . . , c
n
sao atributos pertencentes aos smbolos da produc ao.
Numa denicao dirigida pela sintaxe, assume-se que os terminais tenham apenas atri-
butos sintetizados visto que para os mesmos nao existem regras semanticas. Se, para todas
as produc oes, so forem utilizados atributos sintetizados diz-se se tratar de uma denic ao
S-atribuda.
Exemplo: Seja a gramatica abaixo responsavel pelo funcionamento de uma calcula-
dora simples e tendo como unico atributo sintetizado o valor val responsavel pelo arma-
zenamento de um n umero inteiro, ent ao as regras semanticas (para determinacao do valor
resultante de uma expressao aritmetica) sao:
41
Producao Regras Semanticas
L E imprimir(E.val)
E E +T E.val = E.val+T.val
E T E.val = T.val
T T F T.val = T.val*F.val
T F T.val = F.val
F (E) F.val = E.val
F inteiro F.val = inteiro.lexema
E
L
E T
T
F
T F
F
3
5
4
*
+
Imprimir(19)
E.val = 15+4 = 19
T.val = 4
F.val = 4
Inteiro.Lexval = 4
E.val = 15
T.val = 3*5 = 15
F.val = 5
F.val = 3
T.val = 3
Inteiro.Lexval = 5
Inteiro.Lexval = 3
Figura 5.1: Exemplo de
Arvore Decorada para a Expressao 3*5+4
Atributos herdados sao convenientes para expressar a dependencia de uma construc ao
de linguagem de programac ao no contexto em que a mesma gurar. Por exemplo, podemos
usar um atributo herdado para controlar se um identicador aparece ao lado esquerdo ou
direito de um comando de atribuic ao a m de decidirmos se e necessario usar o endereco
ou o valor do mesmo.
Exemplo: Declarac ao de variaveis: int x,y,z
Producao Regras Semanticas
D TL L.in = T.tipo
T int T.tipo = inteiro
T float T.tipo = real
L L
, id L.in = L.in
incluir tipo(id, L.in)
L id incluir tipo(id, L.in)
Grafos de Dependencia
Se um atributo b a um no da arvore depender de um atributo c, a regra semantica para b
`aquele no precisa ser avaliada apos a regra semantica para c. As interdependencias entre
os atributos herdados e sintentizados sao delineadas atraves de um grafo de dependencia.
42
Exemplo: Supondo que a producao A XY tenha uma regra semantica da forma
A.a = f(X.x, Y.y), ou seja, o atributo sintentizado a em A depende dos atributos x e y.
A representac ao para esta dependencia em um grafo de dependencia caria (conforme a
gura 5.2):
1. Existem tres nos A.a, X.x e Y.y
2. Existe um arco partindo de X.x para A.a pois a depende de x
3. Existe um arco partindo de Y.y para A.a pois a depende de y
A.a
X.x
Y.y
Figura 5.2: Grafo de Dependencias
5.1.1 Denicoes L-Atribudas
O nome L provem de left, onde a analise gramatical e feita sempre a partir do lho mais
`a esquerda na arvore sintatica atraves do seguinte algoritmo:
Algoritmo de pesquisa em profundidade (depth-rst order):
Procedimento Visitar(n: no);
Inicio
Para cada filho m de n da esquerda para a direita faca
Inicio
Avaliar os atributos herdados de m;
Visitar(m);
Fim
Avaliar os atributos sintetizados de m;
Fim
Uma denic ao dirigida pela sintaxe e L-atribuda se cada atributo herdado de X
j
,
i j n, do lado direito de A X
1
, X
2
, . . . , X
n
depender somente:
Dos atributos dos smbolos X
1
, X
2
, . . . , X
j1
`a esquerda de X
j
na producao e
Dos atributos herdados de A.
Note-se que cada denicao S-atribuda e L-atribuda porque as restricoes (1) e (2) se
aplicam somente aos atributos herdados.
43
5.1.2 Vericacoes de Contexto
Um compilador precisa fazer uma vericacao estatica das estruturas do programa, para
assegurar que o mesmo esteja livre de certos tipos de erros, tais como:
Vericacao de Tipos verica se um operador esta sendo aplicado a operandos de tipos
incompatveis. Exemplo: 10 + TRUE.
Vericacao de Fluxo de Controle os enunciados de uxo de controle (repetic oes, p.
ex.) precisam ter algum local de retorno para onde transferir o controle. Exemplo:
um comando tipo break em C precisa estar envolvido por algum comando de uxo
de controle (while, for ou switch).
Vericacoes de Unicidade verica se os identicadores foram declarados de forma
unvoca, ou seja, sem duplicidade.
Vericacoes relacionadas aos nomes existem linguagens que apresentam particulari-
dades acerca dos identicadores do programa. Essa etapa da analise verica se essas
particularidades foram respeitadas. Exemplo: A linguagem ADA exige que blocos
de comandos comecem e terminem com o mesmo identicador.
Sistema de Tipos
Dene-se como um sistema de tipos ao conjunto de regras de aplicabilidade entre os tipos
dos operandos e os operadores da linguagem. Exemplo: Seja o comando X = A + B. Um
sistema de tipos iria validar se os tipos associados aos operandos A e B sao validos para a
adic ao e, em caso armativo, qual seria o tipo resultante, e ainda, se esse tipo resultante
e compatvel com o tipo associado `a vari avel X.
Todo o processo de analise semantica para vericac ao de tipos e realizado a partir da
construc ao de expressoes de tipo, que podem ser compostas: ou por um tipo simples ou
por um construtor de tipos (tipo resultante da aplicac ao de um operador).
Voltando no exemplo: X = A+B; supor que X e B sejam declarados do tipo inteiro e
A seja do tipo real, assim temos:
=
X
+
A
B
=
int
+
float
int
Tipo Simples
=
int
+
float
int
(float)
(invlido)
Construo de Tipos
Figura 5.3: Tipos Simples e Construtor de Tipos
O processo de vericac ao de tipos consiste da aplicac ao de regras semanticas `as
producoes da gramatica sob analise. Usualmente, um atributo sintetizado tipo e su-
ciente para tal analise.
44
Exemplo1: Para uma producao do tipo E id teramos uma regra semantica como
{ E.tipo = id.tipo }.
Exemplo2: Imagine uma linguagem que permita apenas operac oes com tipos identicos,
ent ao para uma produc ao do tipo E E + T teramos a regra semantica { E.tipo = se
E.tipo = T.tipo ent ao E.tipo senao inv alido }
Vericacao de Tipos em Expressoes
As seguintes regras semanticas podem ser abstradas de forma geral:
E tipo simples {E.tipo = tipo simples}
E id {E.tipo = ProcurarTS(id).tipo}
E E
1
{E.tipo = E
1
.tipo}
E E
1
op E
2
{E.tipo = SistemaTipos[E
1
.tipo, op, E
2
, tipo]}
Construcao de um Sistema de Tipos para Operadores
Apresenta as relac oes de compatibilidade, para cada operador, em funcao dos operandos.
Exemplo na linguagem Pascal:
Op
1
Op
2
+ - * /
integer integer integer integer integer real
integer real real real real real
integer char Erro Erro Erro Erro
integer string Erro Erro Erro Erro
integer boolean Erro Erro Erro Erro
real integer real real real real
real real real real real real
real char Erro Erro Erro Erro
real string Erro Erro Erro Erro
real boolean Erro Erro Erro Erro
char integer Erro Erro Erro Erro
char real Erro Erro Erro Erro
char char String Erro Erro Erro
char string String Erro Erro Erro
char boolean Erro Erro Erro Erro
string integer Erro Erro Erro Erro
string real Erro Erro Erro Erro
string char String Erro Erro Erro
string string String Erro Erro Erro
string boolean Erro Erro Erro Erro
boolean integer Erro Erro Erro Erro
boolean real Erro Erro Erro Erro
boolean char Erro Erro Erro Erro
boolean string Erro Erro Erro Erro
boolean boolean Erro Erro Erro Erro
45
Exerccio: Construir as tabelas de sistema de tipos para todos os operadores utilizados
na gramatica da linguagem PASCAL
jr
.
Operadores Aritmeticos: +, -, *, /, **
Operadores Relacionais: ==, !=, >, <, >=, <=
Smbolos Atribuic ao: =, +=, -=, *=, /=
Operadores Logicos: &&, ||, & |
Operadores Unarios: - , ! , ++, - -
Op
1
Op
2
+ - * / **
inteiro inteiro inteiro inteiro inteiro real inteiro
inteiro real real real real real real
inteiro caracter Erro Erro Erro Erro Erro
inteiro cadeia Erro Erro Erro Erro Erro
inteiro logico Erro Erro Erro Erro Erro
real inteiro real real real real real
real real real real real real real
real caracter Erro Erro Erro Erro Erro
real cadeia Erro Erro Erro Erro Erro
real logico Erro Erro Erro Erro Erro
caracter inteiro Erro Erro Erro Erro Erro
caracter real Erro Erro Erro Erro Erro
caracter caracter cadeia Erro Erro Erro Erro
caracter cadeia cadeia Erro Erro Erro Erro
caracter logico Erro Erro Erro Erro Erro
cadeia inteiro Erro Erro Erro Erro Erro
cadeia real Erro Erro Erro Erro Erro
cadeia caracter cadeia Erro Erro Erro Erro
cadeia cadeia cadeia Erro Erro Erro Erro
cadeia logico Erro Erro Erro Erro Erro
logico inteiro Erro Erro Erro Erro Erro
logico real Erro Erro Erro Erro Erro
logico caracter Erro Erro Erro Erro Erro
logico cadeia Erro Erro Erro Erro Erro
logico logico Erro Erro Erro Erro Erro
5.2 Tabela de Smbolos
E uma estrutura de dados gerada pelo compilador com o objetivo de armazenar in-
formac oes sobre os nomes (identicadores de vari aveis, de parametros, de func oes, etc.)
denidos no programa fonte.
Ela associa atributos tais como tipo, escopo, tamanho, limite (no caso de vetores,
n umero de parametros (no caso de subrotinas) a cada identicador armazenado na tabela.
Em geral, uma tabela de smbolo pode comecar a ser construda ja na fase de analise
lexica, mas geralmente nesta fase ainda nao e possvel se determinar as informac oes asso-
ciadas aos atributos dos identicadores que foram reconhecidas pelo compilador. Assim
46
sendo, essas tarefas sao realizadas nas fases de analise sint atica e/ou semantica, onde se
referencia `a tabela, cada vez que um identicador for encontrado no programa.
Os problemas enfrentados ao se projetar uma tabela de smbolos sao:
a quantidade de smbolos armazenados na tabela, depende do programa fonte sob
analise, ou seja, e desejavel uma estrutura dinamica de alocacao de memoria para
a tabela. Se uma estrutura estatica for construda, esta deve ter um tamanho
sucientemente grande para suportar qualquer programa (limitac ao de tamanho);
a quantidade de acesso `a tabela (inclusoes e consultas), pode ser bastante grande,
dependendo do programa. Pode-se organizar os dados na tabela atraves de listas
lineares, arvores binarias ou tabelas hash. O mecanismo linear, apesar de simples, e
ineciente para programas grandes, ja o hashing tem melhor desempenho mas exige
maior esforco de programac ao.
cada entrada na TS esta associada a um nome de identicador. Estas entradas
podem nao ser uniformes, ou seja, os atributos de um identicador de vari avel
nao sao os mesmos de um identicador de funcao, por exemplo. O uso de registros
variantes pode ser uma alternativa elegante para se organizar a estrutura que contera
tais atributos.
Enm, ao se projetar uma tabela de smbolos deve se levar em conta: a quantidade de
dados a serem armazenados, a natureza desses dados, o tempo de acesso e a facilidade de
modicacao da estrutura de armazenamento desses dados. Essas preocupac oes poderao
estar relacionadas `a aplicac ao que se deseja desenvolver.
5.2.1 Atributos dos Nomes dos Identicadores
De maneira geral, qualquer informacoes acerca dos identicadores denidos em um pro-
grama fonte, podem (e devem) ser armazenados em uma tabela de smbolos. O conjunto
de atributos e inerente `as caractersticas da linguagem sendo reconhecida.
Atributos como nome, tipo, uso no programa (isto e: vari avel, constante, procedi-
mento, funcao, rotulo), tamanho de memoria a ser alocada (em bytes), endereco (onde foi
alocada), escopo, etc.
Para efeito da disciplina (implementa cao do prototipo) apenas os atributos: nome,
tipo, endereco, natureza (var ou const).
5.2.2 Hashing
A funcao de espalhamento e responsavel por determinar qual endereco (na tabela Hash),
uma determinada chave k deve ser inserida.
Exemplo: int Hash(int Key){return Key%K TAM HASH; } onde, K TAM HASH
indica o tamanho (n
os-Fixada Pr
e-Fixada
(a +b) c ab +c +abc
a (b +c) abc + a +bc
a +b c abc + +a bc
a = b c +d abc d+ = = a +bcd
Exerccios: Gerar as notacoes pre e pos xas das expressoes abaixo:
1. a +a b +b
2. a +b 4/d c
3. (a +a) (b +b)
4. a = b +c d/4
5. exp = b b 4 a c >= 0 && a! = 0
6. d = (a a) (b b)
A seguir e apresentado um esquema de traducao para expressoes pos xadas:
E E1 +T {E.cod = E1.cod T.cod +}
E T {E.cod = T.cod}
T T1 F {T.cod = T1.cod T.cod *}
T F {T.cod = F.cod}
F id {F.cod = id.nome}
60
6.1.3 Codigo de Tres-Enderecos
Cada instrucao faz referencia, no maximo, a tres vari aveis (enderecos de memoria). As
instruc oes dessa linguagem intermediaria sao:
A = B op C
A = op B
A = B
goto L
if A oprel B goto L
Exemplo: A = X+Y*Z
T1 = Y*Z
T2 = X+T1
A = T2
Um codigo de tres-endere cos pode ser implementado atraves de quadruplas (um opera-
dor, dois operandos e um resultado) ou triplas (um operador e dois operandos), conforme
os exemplos abaixo.
Exemplo: A = B*(-C+D)
oper arg
1
arg
2
result
(0) - C T1
(1) + T1 D T2
(2) * B T2 T3
(3) = T3 A
oper arg
1
arg
2
(0) - C
(1) + (0) D
(2) * B (1)
(3) = A (2)
Na representac ao por triplas existentes apontadores para a propria estrutura, evitando
assim o uso de temporarios.
Esquema de Traducao para um comando de atribuicao
Atrib ID=Expr {Atrib.cod = Expr.cod;
Geracod(ID.nome=Expr.nome); }
Expr Expr
1
+Expr
2
{Expr.nome = GeraTemp;
Expr.cod = Expr
1
.cod || Expr
2
.cod ||
Geracod(Expr.nome=Expr
1
.nome+Expr
2
.nome); }
Expr Expr
1
*Expr
2
{Expr.nome = GeraTemp;
Expr.cod = Expr
1
.cod || Expr
2
.cod ||
Geracod(Expr.nome=Expr
1
.nome*Expr
2
.nome); }
Expr (Expr
1
) {Expr.nome = Expr
1
.nome; Expr.cod = Expr
1
.cod; }
Expr ID {Expr.nome = ID.nome; Expr.cod =
}
61
O atributo nome armazena o nome de uma vari avel (ou temporario). O atributo
cod armazena o codigo fonte gerado para o comando. A funcao geracod gera um texto
correspondente a(s) instruc ao(oes) fornecida(s) como parametro. A func ao geratemp gera
o nome de uma variavel temporaria.
Exemplo para o comando A=X+Y*Z gera o seguinte codigo:
T1 = Y * Z
T2 = X + T1
A = T2
Esquema de Traducao para Expressoes Logicas
Existem dois metodos principais: representacao numerica e representac ao por uxo de
controle.
Representac ao Numerica
Codica numericamente as constantes true (=1) e false (=0) e avalia o resultado logico
numa vari avel temporaria.
Exemplo: Supondo que o codigo gerado seja armazenado a partir da quadrupla 100 o
comando A < B seria traduzido para:
099: . . .
100: if A < B goto 103
101: T1=0
102: goto 104
103: T1=1
104: . . .
Expr Expr
1
|| Expr
2
{Expr.nome = GeraTemp;
Geracod(Expr.nome = Expr
1
.nome || Expr
2
.nome)}
Expr Expr
1
&& Expr
2
{Expr.nome = GeraTemp;
Geracod(Expr.nome = Expr
1
.nome && Expr
2
.nome)}
Expr ! Expr
1
{Expr.nome = GeraTemp;
Geracod(Expr.nome = ! Expr
1
.nome); }
Expr (Expr
1
) {Expr.nome = Expr
1
.nome; }
Expr ID
1
opRel ID
2
{Expr.nome = GeraTemp;
Geracod(if ID
1
.nome opRel ID
2
.nome goto Proxq + 3);
Geracod(Expr.nome =0); Geracod(goto Proxq + 2);
Geracod(Expr.nome =1); }
Expr true {Expr.nome = GeraTemp; Geracod(Expr.nome=1); }
Expr false {Expr.nome = GeraTemp; Geracod(Expr.nome=0); }
A vari avel proxq indica o ndice da proxima quadrupla disponvel.
Exemplo: A < B || C < D && E < F
62
100: if A < B goto 103 107: T2=1
101: T1=0 108: if E < F goto 111
102: goto 104 109: T3=0
103: T1=1 110: goto 112
104: if C < D goto 107 111: T3=1
105: T2=0 112: T4=T2 && T3
106: goto 108 113: T5 = T1 || T4
O valor nal da expressao e sempre no ultimo temporario gerado (no exemplo T5). O
nome desse temporario e ent ao armazenado no nao-terminal expressao da gramatica.
Esquema de Traducao para o comando Enquanto
O comando < Enquanto > prEnquanto < Expr > prFaca < ComSimp >, segue
o seguinte esquema de traducao:
Inicio: Expr.cod
if Expr.nome == 0 goto Prox
ComSimp.cod
goto Inicio
Prox: . . .
Atraves da seguinte regra de traducao:
S prEnquanto Expr prFaca {S.inicio = GeraRotulo; S.prox = GeraRotulo;
ComSimp S.cod = Expr.cod ||
Geracod(if Expr.nome == 0goto S.prox);
ComSimp.cod; || Geracod(goto S.inicio); }
Representac ao por Fluxo de Controle
Este metodo traduz expressoes logicas para um codigo formado por instruc oes if-goto.
Sao gerados rotulos true e false, que armazenam os enderecos de desvio de execucao caso
a avaliac ao resulte em true ou false respectivamente. Por este motivo, e mais eciente que
o metodo de avaliacao numerica.
Traduc ao de expressoes logicas por uxo de controle
Expr Expr
1
|| Expr
2
{Expr
1
.true = Expr.true; Expr
1
.false = GeraRotulo;
Expr
2
.true = Expr.true; Expr
2
.false = Expr.false;
Expr.cod = Expr
1
.cod || Expr
2
.cod; }
Expr Expr
1
&& Expr
2
{Expr
1
.true = GeraRotulo; Expr
1
.false = Expr.false;
Expr
2
.true = Expr.true; Expr
2
.false = Expr.false;
Expr.cod = Expr
1
.cod || Expr
2
.cod; }
Expr ! Expr
1
{Expr
1
.true = Expr.false; Expr
1
.false = Expr.true;
Expr.cod = Expr
1
.cod; }
Expr (Expr
1
) {Expr
1
.true = Expr.true; Expr
1
.false = Expr.false;
Expr.cod = Expr
1
.cod; }
Expr ID
1
opRel ID
2
{Expr.cod = Geracod(if ID
1
.nome opRel ID
2
.nome
goto Expr.true); Geracod(goto Expr.false); }
Expr true {Expr.cod = Geracod(goto Expr.true); }
Expr false {Expr.cod = Geracod(goto Expr.false); }
63
Exemplo: A < B || C < D && E < F
if A < B goto RT
goto L1
L1 if C < D goto L2
goto RF
L2 if E < F goto RT
goto RF
Supondo que os atributos true e false tenham recebido os rotulos RT e RF.
Esquema de Traducao para Comandos de Controle de Fluxo
S prSe Expr prEntao S
1
{Expr.true = GeraRotulo; Expr.false = S.prox;
S
1
.prox = S.prox; S.cod = Expr.cod || S
1
.cod; }
S prSe Expr prEntao S
1
{Expr.true = GeraRotulo; Expr.false = GeraRotulo;
prSenao S
2
S
1
.prox = S.prox; S
2
.prox = S.prox;
S.cod = Expr.cod || S
1
.cod; ||
Geracod(goto S.prox); || S
2
.cod; }
S prEnquanto Expr S
1
{S.inicio = GeraRotulo; Expr.true = GeraRotulo;
Expr.false = S.prox; S
1
.prox = S.inicio;
S.cod = Expr.cod || S
1
.cod; ||
Geracod(goto S.inicio); }
6.2 BackPatching (Retrocorrecao)
O principal problema, na geracao de codigo, e que o codigo gerado deve incluir comandos
de desvio para enderecos que, em geral, ainda nao sao conhecidos. Isso inviabiliza a
gerac ao de codigo num unico passo.
A solucao e utilizar geracao de codigos incompletos para os comandos (sem os en-
derecos) que serao devidamente completados quando o endereco destino for conhecido.
Chama-se de backpatching ao preenchimento desses enderecos nao resolvidos.
Sao necessarias tres func oes para isso:
makelist(i) cria uma lista contendo i (um unico elemento) e retorna um ponteiro para
a lista criada; o elemento i e um ndice do vetor de quadruplas;
merge(p1,p2) concatena as listas apontadas por p1 e p2, e retorna um ponteiro para
lista resultante;
backpatching(p,i) insere i (rotulo destino) no campo de endereco de cada uma das
quadruplas da lista apontada por p.
64
Backpatching para Expressoes Logicas
Foi acrescentado o smbolo nao-terminal M, que tem como objetivo guardar o endereco
da proxima quadrupla disponvel no momento em que M e empilhado (apos um && ou
||).
E E
1
|| E
2
{backpatching(E
1
.ListaFalse, M.quad);
E.ListaTrue = merge(E
1
.ListaTrue, E
2
.ListaTrue);
E.ListaFalse = E
2
.ListaFalse; }
E E
1
&& E
2
{backpatching(E
1
.ListaTrue, M.quad);
E.ListaTrue = E
2
.ListaFalse;
E.ListaFalse = merge(E
1
.ListaFalse, E
2
.ListaFalse); }
E !E
1
{E.ListaTrue = E
1
.ListaFalse; E.ListaFalse = E
1
.ListaTrue; }
E (E
1
) {E.ListaTrue = E
1
.ListaTrue; E.ListaFalse = E
1
.ListaFalse; }
E ID
1
opRel ID
2
{E.ListaTrue = makelist(Proxq);
E.ListaFalse = makelist(Proxq + 1);
Geracod(if ID
1
.nome opRel ID
2
.nome goto );
Geracod(goto ); }
E ID {E.ListaTrue = makelist(Proxq);
E.ListaFalse = makelist(Proxq + 1);
Geracod(if ID.nome goto );
Geracod(goto ); }
M {M.quad = Proxq; }
Neste esquema, E.ListaTrue e E.ListaFalse sao atributos sintetizados (listas) que in-
dicam quadruplas com comandos de desvio incompletos. Os enderecos sao preenchidos
com o valor armazenado em M.quad quando um backpatching ocorre na lista.
Exemplo: a < b || c < d && e < f
E
E E
E E
M
M
||
&&
A < B
C < D E < F
ListaTrue=100
ListaFalse=101
ListaTrue=102
ListaFalse=103
ListaTrue=104
ListaFalse=105
ListaTrue=104
ListaFalse=103,105
ListaTrue=100,104
ListaFalse=103,105
Quad=102
Quad=104
Figura 6.2: Backpatching para expressoes logicas
100: if A < B goto 100: if A < B goto
101: goto 101: goto 102
102: if C < D goto 102: if C < D goto 104
103: goto 103: goto
104: if E < F goto 104: if E < F goto
105: goto 105: goto
65
Backpatching para Comandos de Controle
Entendendo-se o esquema de traduc ao anterior, e introduzido um nao-terminal N antes
do comando senao para ocasionar um salto sobre o bloco do senao se for o caso.
S Se E entao M S
1
{backpatching(E.ListaTrue, M.quad);
S.prox = merge(E.ListaFalse, S
1
.prox); }
S Se E entao M
1
S
1
N {backpatching(E.ListaTrue, M
1
.quad);
senao M
2
S
2
backpatching(E.ListaFalse, M
2
.quad);
S.prox = merge(S
1
.prox, merge(N.go, S
2
.prox)); }
N {N.go = makelist(Proxq); Geracod(goto ); }
S Enquanto M
1
E Faca {backpatching(S
1
.prox, M
1
.quad);
M
2
S
1
backpatching(E.ListaTrue, M
2
.quad);
S.prox = E.ListaFalse; Geracod(goto M
1
.quad); }
S {L} {S.prox = L.prox;
S A {S.prox = makelist(NULL); }
A ID = E Ac oes semanticas ja vistas em aula
L L
1
; M S {backpatching(L
1
.prox, M.quad); L.prox = S.prox; }
L S {L.prox = S.prox; }
P S. {backpatching(S.prox, EOF); }
O endereco indicado por EOF representa o m do codigo fonte e o retorno ao sistema
operacional.
Exerccio: Gere o codigo para o trecho de programa abaixo (supor primeira instrucao
no endereco 000):
Enquanto A < B entao
se C < D entao
X = Y+Z
senao
X = Y-Z
Codigo resultante:
000: if A < B goto 002 006: goto 000
001: goto EOF 007: T2 = Y-Z
002: if C < D goto 004 008: X = T2
003: goto 007 009: goto 000
004: T1 = Y+Z EOF:
005: X = T1
66
Captulo 7
Otimizacao de Codigo
Trata-se do problema da geracao de codigo eciente: uso racional da memoria e rapidez
na execucao do codigo fonte. Porem, muitas vezes esses aspectos sao conitantes, ou seja,
apela-se para um maior tempo de execuc ao para se conseguir um ganho no consumo de
memoria e vice-versa.
Compiladores que aplicam transformacoes de melhorias no codigo gerado sao denomi-
nados compiladores otimizantes.
Normalmente este processo e feito em duas fases: otimizacoes do codigo intermediario
e otimizacoes do codigo objeto. No codigo intermediario pode-se eliminar atribuic oes
redundantes, sub-expressoes comuns, temporarios desnecessarios, etc., no intuito de di-
minui o codigo intermedi ario, enquanto que no codigo objeto e feita uma substituicao de
instruc oes por equivalentes mais rapidos que permitem melhor uso dos registradores.
7.1 Otimizacao Peephole
Uma tecnica simples para melhorar localmente o codigo gerado e a otimizacao peephole,
que trabalha substituindo seq uencias de instrucoes (peepholes) por outras mais ecientes.
As principais ac oes da otimizac ao peephole sao:
eliminac ao de instruc oes redundantes
otimizac oes de uxo de controle
simplicac oes algebricas
Eliminacao de Instruc oes Redundantes
Uma seq uencia de instruc oes do tipo a = b e b = a, pode ser substitudo somente por a
= b, pois o resultado logico e o mesmo.
Otimizacao de Fluxo de Controle
Os algoritmos de geracao de codigo intermedi ario freq uentemente produzem: (1) desvios
para desvios, (2) desvios para desvios condicionais ou (3) desvios condicionais para desvios.
Uma otimizacao peephole pode remover tais instruc oes redundantes:
Exemplo 1:
67
goto L1 goto L2
. . . . . .
L1: goto L2 L1: goto L2
Neste caso, se nao houverem outras instruc oes que levem a L1, esta instrucao pode
ser removida do codigo.
Exemplo 2:
if a < b goto L1 if a < b goto L2
. . . . . .
L1: goto L2 L1: goto L2
Exemplo 3:
goto L1 if a < b goto L2
. . . goto L3
L1: if a < b goto L2 . . .
L3: . . . L3: . . .
Simplicacao Algebrica
Remoc ao de redundancias que ocorrem em expressoes algebricas, tais como: x=x+0 ou
y=y*1.
7.2 Otimizacao de Blocos Sequenciais atraves de gra-
fos
O uso de grafos acclicos dirigidos (GAD) para se representar uma seq uencia de instruc oes,
permite mais facilmente rearranjar a ordem das instrucoes a m de reduzir o codigo objeto
nal.
Exemplo: (a+b)-(e-(c+d))
68
Codigo Intermediario Codigo Objeto
T1 = a+b MOV a, R0
T2 = c+d ADD b, R0
T3 = e-T2 MOV c, R1
T4 = T1-T3 ADD d, R1
MOV, R0, T1
MOV E, R0
SUB R1, R0
MOV T1, R1
SUB R0, R1
MOV R1, T4
T2 = c+d MOV c, R0
T3 = e-T2 ADD d, R0
T1 = a+b MOV e, R1
T4 = T1-T3 SUB R0, R1
MOV a, R0
ADD b, R0
SUB R1, R0
MOV R0, T4
7.2.1 Algoritmo para Construir o GAD de um bloco
O algoritmo supoe que cada instruc ao (de tres-enderecos) segue um dos seguintes tres
formatos: (1) x = y op z; (2) x = op y; (3) x = y. Instruc oes if-goto sao tratadas como o
caso (1).
Passos:
1. Se o no y ainda nao existe no grafo, crie uma folha para y (e para z se for o caso 1);
2. No caso 1, verique se existe um no op com lhos y e z (nessa ordem). Se sim,
chame-o, tambem de x; senao, crie um no op com nome x e dois arcos dirigidos do
no op para y e z. No caso 2, verique se existe um no op com um unico lho y. Se
nao existir, cria tal no e um arco para y; chame de x o no criado ou encontrado. No
caso 3, chame tambem de x o no y.
Exemplo: y = ((a+b)*(a-b))+((a+b)*(a-c))
T1 = a+b
T2 = a-b
T3 = T1*T2
T4 = a+b
T5 = a-c
T6 = T4*T5
T7 = T3+T6
y = T7
69
+
+
* *
a
-
b c
- T1,T4 T2 T5
T3 T6
T7,y
Figura 7.1: Grafo Acclico Dirigido - GAD
7.2.2 Algoritmo para Ordenacao de um GAD
Cria-se uma lista de ordenac ao dos nos internos do GAD atraves do algoritmo a seguir:
Enquanto existirem nos interiores n~ao listados faca inicio
1. Selecionar um no n n~ao listado do qual todos os pais ja foram listados;
2. Listar n;
Enquanto o filho mais `a esquerda m de n tiver todos os pais listados
e n~ao for uma folha faca inicio
3. Listar m;
4. n := m
fim
fim
O codigo e entao gerado atraves da ordem inversa da obtida pela lista resultante.
Exemplo para o codigo intermediario visto anteriormente: y (ou T7), T3, T6, T1 (ou
T4), T5, T2.
70
Captulo 8
Geracao de Codigo Objeto
E baseada em uma pilha;
Trabalha com os mesmos tipos de dados das fases anteriores;
Sua memoria e dividida em duas partes:
Area de Codigo contem uma lista (CODIGO) das instruc oes geradas pelo com-
pilador (codigo de tres-enderecos);
Area de Dados composta por uma pilha (DADOS) que conter a os registradores
manipulados pelas instruc oes da maquina (so existe em tempo de execuc ao de
um programa) e uma lista (MEMO) que armazena as variaveis e constantes
manipuladas pelo programa. Para efeito de simplicac ao, todas as variaveis
terao tamanho 1;
Possui um registrador especial (PROXINST) para a proxima instrucao (na area de
codigo) a ser executada, outro (TOPODADOS) que indica o topo da pilha de dados
e TOPOMEMO a quantidade de memoria alocada;
O funcionamento da MAQHIPO e bastante simples:
1. Carrega-se o programa-objeto (gerado pelo compilador) na area de codigo.
2. Todas as instrucoes (indicadas pelo ponteiro PROXINST) sao executadas seq uenci-
almente ate a instruc ao de parada ou ate que ocorra algum erro de execuc ao;
3. A execucao de cada instrucao incrementa o valor de PROXINST (exceto para ins-
truc oes de desvio);
O conjunto de instrucoes validas para a MAQHIPO e:
Inicializacao e Finalizacao
72
INIP inicializa a execucao do programa {TopoMemo = TopoDesvio = 0; TopoDados =
1; }
FIMP encerra a execuc ao do programa {}
Alocacao de Memoria
ALME m t Aloca m posicoes de memoria, todas do tipo t
{for(i = TopoMemo; i < TopoMemo +m; i + +)Memo[i].tipo = t;
TopoMemo+ = m; }
DEAL m Desloca m posicoes de memoria
{TopoMemo = m; }
Comandos de E/S
ENTR Entrada de dados {gets(Dados[+ +TopoDados]); }
IMPR Impressao de valores {printf(%s
, Dados[TopoDados ]); }
Comandos para Expressoes
CRCT k t Carrega uma constante do tipo t. {Dados[+ +TopoDados] = (k, t); }
CRVL n t Transporta o conte udo do endereco de memoria n para a pilha de dados.
{Dados[+ +TopoDados] = Memo[n]; }
SOMA substitui os dois elementos mais ao topo da pilha por sua soma.
{Dados[TopoDados 1]+ = Dados[TopoDados ]; }
SUBT substitui os dois elementos mais ao topo da pilha por sua diferenca.
{Dados[TopoDados 1] = Dados[TopoDados ]; }
MULT substitui os dois elementos mais ao topo da pilha por seu produto.
{Dados[TopoDados 1] = Dados[TopoDados ]; }
DIVI substitui os dois elementos mais ao topo da pilha por seu quociente.
{Dados[TopoDados 1]/ = Dados[TopoDados ]; }
POTE substitui os dois elementos mais ao topo da pilha pela sua potencia.
{Dados[TopoDados 1] = pow(Dados[TopoDados 1], Dados[TopoDados ]); }
INVE inverte o sinal do elemento no topo da pilha.
{Dados[TopoDados] = 1; }
CONJ substitui os dois elementos mais ao topo da pilha por sua conjuncao (&&).
{Dados[TopoDados 1] = Dados[TopoDados 1] && Dados[TopoDados];
TopoDados ; }
DISJ substitui os dois elementos mais ao topo da pilha por sua disjuncao (||).
{Dados[TopoDados 1] = Dados[TopoDados 1] + Dados[TopoDados] == 1;
TopoDados ; }
73
DISX substitui os dois elementos mais ao topo da pilha por sua disjunc ao exclusiva (& |).
{Dados[TopoDados 1] = Dados[TopoDados 1] & | Dados[TopoDados];
TopoDados ; }
NEGA inverte o valor logico do elemento no topo da pilha .
{Dados[TopoDados] =!Dados[TopoDados]; }
CPME substitui os dois elementos mais ao topo da pilha pela comparac ao de menor.
{Dados[TopoDados 1] = Dados[TopoDados 1] < Dados[TopoDados];
TopoDados ; }
CMAI substitui os dois elementos mais ao topo da pilha pela comparac ao de maior.
{Dados[TopoDados 1] = Dados[TopoDados 1] > Dados[TopoDados];
TopoDados ; }
CPIG substitui os dois elementos mais ao topo da pilha pela comparacao de igualdade.
{Dados[TopoDados 1] = Dados[TopoDados 1] == Dados[TopoDados];
TopoDados ; }
CDIF substitui os dois elementos mais ao topo da pilha pela comparac ao de desigualdade.
{Dados[TopoDados 1] = Dados[TopoDados 1] ! = Dados[TopoDados];
TopoDados ; }
CPMI substitui os dois elementos mais ao topo da pilha pela comparacao de menor ou
igual.
{Dados[TopoDados 1] = Dados[TopoDados 1] <= Dados[TopoDados];
TopoDados ; }
CPMA substitui os dois elementos mais ao topo da pilha pela comparac ao de maior ou
igual.
{Dados[TopoDados 1] = Dados[TopoDados 1] >= Dados[TopoDados];
TopoDados ; }
74
Comando de Atribuicao
ARMZ n Transporta o conte udo do topo da pilha para o endereco de memoria n.
{Memo[n] = Dados[TopoDados ]; }
Comandos Condicionais e Iterativos
DSVF p Desvio se condicional falso para a instruc ao p.
{ProxInst = Dados[TopoDados]?ProxInst + 1 : p; }
DSVI p Desvio incondicional para a instruc ao p. {ProxInst = p; }
EXEC n Executa a chamada a uma sub-rotina com inicio na instrucao n e empilha o
endereco de retorno
{Desvios[+ +TopoDesvio] = ProxInst + 1; ProxInst = n; }
RETR Retorna a execucao para a proxima instruc ao apos a chamada `a sub-rotina
{ProxInst = Desvios[TopoDesvio ]; }
Exemplo de Geracao de Codigo Objeto na Linguagem Hipo:
Programa Fonte Programa Objeto
Var: inteiro F, N; 01 INIP
02 ALME 1 i
03 ALME 1 i
Inicio
Leia(N); 04 ENTR
05 ARMZ 1
F=1; 06 CRCT 1 i
07 ARMZ 0
Enquanto N >= 1 faca Inicio 08 CRVL 1 i
09 CRCT 1 i
10 CPMA
11 DSVF 21
F = N; 12 CRVL 0 i
13 CRVL 1 i
14 MULT
15 ARMZ 0
N ; 16 CRVL 1 i
17 CRCT 1 i
18 SUBT
19 ARMZ 1
Fim 20 DSVI 8
Escreva(F); 21 CRVL 0 i
22 IMPR
Fim 23 FIMP
75
8.1.1 Regras para Geracao de Codigo Objeto
Programa AreaDecl AreaSubRot Principal
{ Geracod(INIP);
Gerar codigo para AreaDecl
Quad = ProxQ; Geracod(DSVI);
Gerar codigo para AreaSubRot
Backpatching(Quad, ProxQ);
Gerar codigo para Principal
if(TS.Qtde 0) Geracod(DEAL TS.Qtde); Geracod(FIMP);}
AreaDecl AreaDeclVar AreaDecl
{ Gerar codigo para AreaDeclVar
Gerar codigo para AreaDecl }
AreaDecl AreaDeclConst AreaDecl
{ Gerar codigo para AreaDeclConst
Gerar codigo para AreaDecl }
AreaDecl {}
AreaDeclVar prVar DoisPt DeclVars
{ Gerar codigo para DeclVars }
DeclVars Tipo ListaID PtVirg DeclVars
{ ContIds=0;
Gerar codigo para ListaID
Gerar codigo para DeclVars }
DeclVars Tipo ListaID PtVirg DeclVars
{ ContIds=0;
Gerar codigo para ListaID
Gerar codigo para DeclVars }
DeclVars {}
Tipo prInt {}
Tipo prFloat {}
Tipo prChar {}
Tipo prString {}
Tipo prBool {}
76
ListaID Identicador ListaID
{ if(Acao == DeclV ar)ContIds + +;
else{Geracod(ENTREntrada.Tipo); Geracod(ARMZ Entrada.Endereco Entrada.Tipo); }
Gerar codigo para ListaID }
ListaID Virg Identicador ListaID
{ if(Acao == DeclV ar)ContIds + +;
else{Geracod(ENTREntrada.Tipo); Geracod(ARMZ Entrada.Endereco Entrada.Tipo); }
Gerar codigo para ListaID }
ListaID
{ if(Acao == DeclV ar)Geracod(ALME ContIds); }
AreaDeclConst prConst DoisPt ListaConst
{ Gerar codigo para ListaConst }
ListaConst Tipo ListaIDConst PtVirg ListaConst
{ Gerar codigo para ListaIDConst
Gerar codigo para ListaConst }
ListaConst Tipo ListaIDConst PtVirg ListaConst
{ Gerar codigo para ListaIDConst
Gerar codigo para ListaConst }
ListaConst {}
ListaIDConst Identicador Atrib Valor ListaIDConst
{ Geracod(ALME 1 Identicador.tipo);
Gerar codigo para Valor
Geracod(ARMZ Identicador.Endereco); Gerar codigo para ListaIDConst }
ListaIDConst Virg Identicador Atrib Valor ListaIDConst
{ Geracod(ALME 1 Identicador.tipo);
Gerar codigo para Valor
Geracod(ARMZ Identicador.Endereco); Gerar codigo para ListaIDConst }
ListaIDConst {}
Valor OpAritSubt Numeros
{ Gerar codigo para Numeros
Geracod(INVE); }
Valor Numeros
{ Gerar codigo para Numeros }
77
Valor ConstCaracter
{ Geracod(CRCT ConstCaracter.lexema c); }
Valor ConstString
{ Geracod(CRCT ConstString.lexema s); }
Valor prTrue
{ Geracod(CRCT true b); }
Valor prFalse
{ Geracod(CRCT False b); }
Numeros NumeroInteiro
{ Geracod(CRCT NumeroInteiro.lexema i); }
Numeros NumeroReal
{ Geracod(CRCT NumeroReal.lexema f); }
AreaSubRot AreaProc AreaSubRot
{ Gerar codigo para AreaProc
Gerar codigo para AreaSubRot }
AreaSubRot AreaFunc AreaSubRot
{ Gerar codigo para AreaFunc
Gerar codigo para AreaSubRot }
AreaSubRot { }
AreaProc prProc IdentSR AbrePar ListaParam FechaPar
AreaDecl BlocoCom
{ Gerar codigo para ListaParam
Gerar codigo para AreaDecl
Gerar codigo para BlocoCom
if(TS.qtde 0) Geracod(DEAL TS.qtde);
Geracod(RETR); }
ListaParam Tipo Identicador ListaParam
{ Geracod(ARMZ Identicador.endereco);
Gerar codigo para ListaParam }
ListaParam { }
ListaParam Virg Tipo Identicador ListaParam
{ Geracod(ARMZ Identicador.endereco);
Gerar codigo para ListaParam }
78
ListaParam { }
AreaFunc prFunc Tipo IdentSR AbrePar ListaParam FechaPar
AreaDecl BlocoCom
{ Gerar codigo para ListaParam
Gerar codigo para AreaDecl
Gerar codigo para BlocoCom
if(TS.qtde 0) Geracod(DEAL TS.qtde);
Geracod(RETR); }
Principal prMain Abrepar FechPar BlocoCom
{ Gerar codigo para BlocoCom }
BlocoCom AbreChaves ListaCom FechaChaves
{ Gerar codigo para ListaCom }
ListaCom Comando ListaCom
{ Gerar codigo para Comando
Gerar codigo para ListaCom }
ListaCom {}
Comando Atrib PtVirg
{ Gerar codigo para Atrib }
Comando RepetPre
{ Gerar codigo para RepetPre }
Comando RepetPos PtVirg
{ Gerar codigo para RepetPos }
Comando RepetCont
{ Gerar codigo para RepetCont }
Comando Entrada PtVirg
{ Gerar codigo para Entrada }
Comando Saida PtVirg
{ Gerar codigo para Saida }
Comando Condic
{ Gerar codigo para Condic }
Comando BlocoCom
{ Gerar codigo para BlocoCom }
79
Comando Retorno PtVirg
{ Gerar codigo para Retorno }
Comando SubRot PtVirg
{ Gerar codigo para SubRot }
Atrib Identicador Atrib
{ Gerar codigo para Atrib; Geracod(ARMZ Identicador.Endereco) }
Atrib SimbAtrib Expr
{ Gerar codigo para Expr }
Atrib SimbAtribSoma Expr
{ Geracod(CRVL Entrada.Endereco Entrada.tipo)
Gerar codigo para Expr
Geracod(SOMA) }
Atrib SimbAtribSubt Expr
{ Geracod(CRVL Entrada.Endereco Entrada.tipo)
Gerar codigo para Expr
Geracod(SUBT) }
Atrib SimbAtribMult Expr
{ Geracod(CRVL Entrada.Endereco Entrada.tipo)
Gerar codigo para Expr
Geracod(MULT) }
Atrib SimbAtribDivi Expr
{ Geracod(CRVL Entrada.Endereco Entrada.tipo)
Gerar codigo para Expr
Geracod(DIVI) }
Atrib SimbIncr
{ Geracod(CRVL Entrada.Endereco Entrada.tipo)
Geracod(CRCT 1 i)
Geracod(SOMA) }
Atrib SimbDecr
{ Geracod(CRVL Entrada.Endereco Entrada.tipo)
Geracod(CRCT 1 i)
Geracod(SUBT) }
Condic prIf AbrePar Expr FechPar Comando Condic
{ Gerar codigo para Expr; Quad1 = Proxq
Geracod(DSVF); Gerar codigo para Comando
Gerar codigo para Condic }
80
Condic prElse Comando
{ Quad2 = ProxQ; Geracod(DSVI)
Backpatching(Quad1, ProxQ); Gerar codigo para Comando
Backpathing(Quad2, ProxQ); }
Condic
{ Backpatching(Quad1, ProxQ) }
RepetPos prDo BlocoCom prWhile AbrePar Expr FechaPar
{ Quad1 = ProxQ; Gerar codigo para BlocoCom
Gerar codigo para Expr; Geracod(DSVF ProxQ+2); Geracod(DSVI Quad1) }
RepetPre prWhile AbrePar ExprFechaPar Comando
{ Quad1 = ProxQ; Gerar codigo para Expr
Quad2 = ProxQ; Geracod(DSVF); Gerar codigo para Comando
Geracod(DSVI Quad1); Backpatching(Quad2, ProxQ) }
RepetCont prFor AbrePar Atrib PtVirg Expr PtVirg Atrib FechaPar
Comando
{ Gerar codigo para Atrib; Quad1 = ProxQ;
Gerar codigo para Expr; Quad2 = ProxQ
Geracod(DSVF); Buer = Gerar codigo para Atrib
Gerar codigo para Comando
Descarrega o buer temporario; Geracod(DSVI Quad1)
Backpatching(Quad2, ProxQ) }
Entrada prScanf AbrePar ListaID FechaPar
{ Gerar codigo para ListaID }
Saida prPrint Abrepar ListaExpr FechaPar
{ Gerar codigo para ListaExpr }
Saida prPrintl Abrepar ListaExpr FechaPar
{ Gerar codigo para ListaExpr }
ListaExpr Expr ListaExpr
{ Gerar codigo para Expr; Gerar codigo para ListaExpr }
ListaExpr VirgExpr ListaExpr
{ Gerar codigo para Expr; Gerar codigo para ListaExpr }
ListaExpr {}
Retorno prReturn Expr
{ Gerar codigo para Expr }
81
SubRot IdentSR AbrePar ListaArg FechaPar
{ Gerar codigo para ListaArg; Geracod(EXEC IdentSR.Endereco) }
ListaArg ListaExpr
{ Gerar codigo para ListaExpr }
ListaArg {}
Expr TermoLog Expr Ternario
{ Gerar codigo para TermoLog; Gerar codigo para Expr
Gerar codigo para Ternario }
Ternario Interrog Expr
1
DoisPt Expr
2
{ Quad1 = ProxQ; Geracod(DSVF); Gerar codigo para Expr
1
Quad2 = ProxQ; Geracod(DSVI); Backpatching(Quad1, ProxQ)
Gerar codigo para Expr
2
; Backpatching(Quad2, ProxQ) }
Ternario {}
Expr OpLogAnd TermoLog Expr
{ Gerar codigo para TermoLog; Geracod(CONJ)
Gerar codigo para Expr }
Expr OpLogOr TermoLog Expr
{ Gerar codigo para TermoLog; Geracod(DISJ)
Gerar codigo para Expr }
Expr OpLogXor TermoLog Expr
{ Gerar codigo para TermoLog; Geracod(DISX)
Gerar codigo para Expr }
Expr {}
TermoLog FatorLog TermoLog
{ Gerar codigo para FatorLog; Gerar codigo para TermoLog; }
TermoLog OpRelacMaior FatorLog
{ Gerar codigo para FatorLog; Geracod(CMAI); }
TermoLog OpRelacMenor FatorLog
{ Gerar codigo para FatorLog; Geracod(CPME); }
TermoLog OpRelacMenorIgual FatorLog
{ Gerar codigo para FatorLog; Geracod(CPMI); }
82
TermoLog OpRelacMaiorIgual FatorLog
{ Gerar codigo para FatorLog; Geracod(CPMA); }
TermoLog OpRelacIgual FatorLog
{ Gerar codigo para FatorLog; Geracod(CPIG); }
TermoLog OpRelacDifer FatorLog
{ Gerar codigo para FatorLog; Geracod(CDIF); }
TermoLog {}
FatorLog ExprAr
{ Gerar codigo para ExprAr; }
FatorLog OpLogNeg Expr
{ Gerar codigo para Expr; Geracod(NEGA); }
FatorLog prTrue
{ Geracod(CRCT true l); }
FatorLog prFalse
{ Geracod(CRCT false l); }
ExprAr TermoAr ExprAr
{ Gerar codigo para TermoAr; Gerar codigo para ExprAr; }
ExprAr OpAdic TermoAr ExprAr
{ Gerar codigo para TermoAr; Geracod(SOMA);
Gerar codigo para ExprAr; }
ExprAr OpSubt TermoAr ExprAr
{ Gerar codigo para TermoAr; Geracod(SUBT);
Gerar codigo para ExprAr; }
ExprAr {}
TermoAr FatorAr TermoAr
{ Gerar codigo para FatorAr; Gerar codigo para TermoAr; }
TermoAr OpMult FatorAr TermoAr
{ Gerar codigo para FatorAr; Geracod(MULT);
Gerar codigo para TermoAr; }
TermoAr OpDivi FatorAr TermoAr
{ Gerar codigo para FatorAr; Geracod(DIVI);
Gerar codigo para TermoAr; }
83
TermoAr {}
FatorAr ElementoAr FatorAr
{ Gerar codigo para ElementoAr; Gerar codigo para FatorAr; }
FatorAr OpPote ElementoAr FatorAr
{ Gerar codigo para ElementoAr; Geracod(POTE);
Gerar codigo para FatorAr; }
FatorAr {}
ElementoAr AbrePar Expr FechaPar
{ Gerar codigo para Expr; }
ElementoAr OpSubt ExprAr
{ Gerar codigo para ExprAr; Geracod(INVE); }
ElementoAr Identicador
{ Geracod(CRVL identicador.Endereco); }
ElementoAr Numeros
{ Gerar codigo para Numeros }
ElementoAr SubRot
{ Gerar codigo para SubRot }
ElementoAr ConstCaracter
{ Geracod(CRCT ConstCaracter.lexema c); }
ElementoAr ConstString
{ Geracod(CRCT ConstString.lexema s); }
8.1.2 Trabalho Pratico #4
Implementar um modulo analisador semantico para um prototipo de compilador para a
linguagem PASCAL
jr
(simplicada) vista em aula.
Caractersticas:
Do modulo gerador de codigo:
Cada chamada a elementos nao-terminais processa esquemas de traduc ao de codigo
necessarios para cada producao da gramatica.
Utiliza a arvore decorada criada no modulo semantico.
Utiliza a tabela de smbolos (altera e consulta dados) construda na fase anterior.
84
Do programa a ser criado:
Abre um arquivo fonte para analise.
Executa todas as fases de analise e, caso nenhum erro seja encontrado, armazena a
sequencia de instruc oes em linguagem HIPO na lista CODE da MaqHipo.
Fecha o arquivo fonte ao nal da compilacao.
Para o processo de compilac ao caso um erro seja encontrado.
Exibe erros de compilac ao (se ocorrerem) ou mensagem de sucesso.
Inicia o processo de execucao da maquina objeto, segundo as regras denidas para
cada instruc ao.
Para o processo de execucao ao atingir a instruc ao FIMP (neste caso informa m de
execuc ao) ou algum erro de execuc ao seja detectado (neste caso informa mensagem
de erro).
Criterios de Avaliacao:
Implementa cao usando linguagem C ou C++.
Entrega de fontes e executavel (em um disquete identicado ou e-mail).
Grupo de 02 alunos (maximo).
Valor do trabalho: 10.0 (25% da nota pratica).
Data de Entrega: 30/06/2005.
Punic oes:
de 20% do valor do trabalho por dia de atraso.
de 1.75 por erro lexico nao analisado corretamente.
de 1.5 por erro sint atico nao analisado corretamente.
de 1.25 por erro semantico nao analisado corretamente.
de 1.0 por erro semantico nao analisado corretamente.
de 0.5 do valor do trabalho para a entrega nao conforme dos arquivos pedidos.
de 50% do valor do trabalho para o caso de nao executar ou travar (apos teste
em 2 computadores, sendo um o do professor).
de 100% do valor do trabalho para o caso de copias (mesmo de trabalhos de
semestres anteriores).
85