Академический Документы
Профессиональный Документы
Культура Документы
Sum
ario
1 Introduc
ao
1.1 Evolucao das Linguagens de Programacao .
1.2 Introducao `a Compilacao . . . . . . . . . . .
1.2.1 Fases da Compilacao . . . . . . . . .
1.3 Ferramentas para Geracao de Compiladores
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
2
3
6
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7
7
8
9
10
12
3 An
alise L
exica
3.1 O Papel do Analisador Lexico . . . . . . .
3.2 Buferizacao de Entrada . . . . . . . . . . .
3.3 Gramaticas e Linguagens Regulares . . . .
3.3.1 Exerccios Propostos . . . . . . . .
3.4 Especificacao e Reconhecimento de Tokens
3.4.1 Trabalho Pratico #1 . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13
13
14
15
16
17
19
.
.
.
.
.
.
.
.
21
21
23
23
24
25
26
27
29
.
.
.
.
.
.
4 An
alise Sint
atica
4.1 O Papel do Analisador Sintatico . . . . . . . . . . . . . . . . . . . . . . .
4.2 Analise Sintatica Ascendente - BOTTOM UP . . . . . . . . . . . . . . .
4.2.1 Algoritmo Empilhar-e-Reduzir . . . . . . . . . . . . . . . . . .
4.3 Analise Sintatica Descendente - TOP DOWN . . . . . . . . . . . . . . .
4.3.1 Analise Sintatica Preditiva . . . . . . . . . . . . . . . . . . . . . .
4.3.2 Exerccios Propostos . . . . . . . . . . . . . . . . . . . . . . . . .
4.4 Reconhecedor de Gramaticas Preditivas Descendentes . . . . . . . . . . .
4.4.1 Algoritmo para Construcao da Tabela de Analise . . . . . . . . .
4.4.2 Projeto de uma Gramatica para um Analisador Sintatico Preditivo
Ascendente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.4.3 Projeto de uma Gramatica para um Analisador Sintatico Preditivo
Descendente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.4.4 Exerccios Propostos . . . . . . . . . . . . . . . . . . . . . . . . .
4.4.5 Trabalho Pratico #2 . . . . . . . . . . . . . . . . . . . . . . . . .
i
. 30
. 31
. 38
. 38
5 An
alise Sem
antica
5.1 Traducao Dirigida pela Sintaxe
5.1.1 Definicoes L-Atribudas .
5.1.2 Verificacoes de Contexto
5.2 Tabela de Smbolos . . . . . . .
5.2.1 Atributos dos Nomes dos
5.2.2 Hashing . . . . . . . . .
5.3 Projeto das Regras Semanticas .
5.3.1 Trabalho Pratico #3 . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
Identificadores
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
6 Gerac
ao de C
odigo Intermedi
ario
6.1 Linguagens Intermediarias . . . . .
6.1.1 Representacoes Graficas . .
6.1.2 Notacao Pos (e Pre) Fixadas
6.1.3 Codigo de Tres-Enderecos .
6.2 BackPatching (Retrocorrecao) . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
41
41
43
44
46
47
47
50
56
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
59
59
59
60
61
64
7 Otimizac
ao de C
odigo
7.1 Otimizacao Peephole . . . . . . . . . . . . . . . . . .
7.2 Otimizacao de Blocos Sequenciais atraves de grafos .
7.2.1 Algoritmo para Construir o GAD de um bloco
7.2.2 Algoritmo para Ordenacao de um GAD . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
67
67
68
69
70
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
8 Gerac
ao de C
odigo 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
1.2
1.3
Processo de Compilacao . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Fases da Compilacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Arvore
resultante da analise de um comando de atribuicao em PASCAL . .
2
3
4
2.1
2.2
8
8
3.1
3.2
3.3
3.4
3.5
4.1
4.2
4.3
4.4
4.5
. . .
. . .
reais
. . .
. . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13
15
17
18
18
Exemplo de Arvore
Sintatica . . . . . . . . . . . . . . . .
Derivacao `a Esquerda e `a Direita . . . . . . . . . . . . .
Analise descendente com backtracking . . . . . . . . . .
Exemplos de Recursao `a Esquerda e `a Direita . . . . . .
Funcionamento de um Analisador Sintatico Descendente
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
22
22
25
27
28
5.1
5.2
5.3
5.4
Exemplo de Arvore
Decorada para a Expressao 3*5+4
Grafo de Dependencias . . . . . . . . . . . . . . . . . .
Tipos Simples e Construtor de Tipos . . . . . . . . . .
Hashing com Encadeamento . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
42
43
44
49
6.1
6.2
7.1
iii
.
.
e
.
.
.
.
.
.
Captulo 1
Introduc
ao
Entende-se por linguagem como uma forma eficiente de comunicacao entre pessoas. Na
verdade a linguagem e um conjunto de palavras usadas, segundo certas regras, para a
formacao de frases compreensveis por ambos os interlocutores (falantes).
Quando um dos interlocutores e o computador, se faz necessario o uso de uma linguagem especial denominada linguagem de programacao que permite a comunicacao entre
homem e maquina atraves da definicao 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 acoes 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
Evoluc
ao das Linguagens de Programac
ao
A 3a geracao surgiu na decada de 60, com as linguagens procedimentais como FORTRAN, PASCAL e ALGOL e declarativas como LISP e PROLOG. Nas linguagens procedimentais, um programa especifica uma seq
uencia de passos a serem seguidos para a
solucao 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 proposicoes da logica de predicados (fatos e
regras).
Devido ao fato de programas escritos em linguagens de 3a geracao serem muito extensos e de difcil manutencao, surgiram as linguagens de aplicacao (4a geracao), onde o
desenvolvedor deixa de se preocupar com atividades secundarias e trata apenas da codificacao do problema (foco do programador deixa de ser a codificacao para ser a analise
do problema). Aspectos como: interface de entrada e sada, relatorios, etc. sao resolvidos pela propria linguagem atraves de um banco de dados e dicionarios associados `as
aplicacoes desenvolvidas.
A 5a geracao das linguagens de programacao atua em problemas altamente complexos onde a representacao de conhecimento se faz necessaria para sua solucao, como os
problemas enfrentados pela inteligencia artificial. A linguagem PROLOG e aceita como
pertencente a esta geracao.
1.2
Introduc
ao `
a Compilac
ao
COMPILADOR
Programa
Objeto
Mensagem
de Erro
1.2.1
Fases da Compilac
ao
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 programacao (inconsistencias com a linguagem fonte), ja na etapa de sntese (apos a
verificacao da corretude do programa de origem), efetua-se a traducao, propriamente dita,
do codigo fonte para a linguagem objeto em questao. A figura 1.2 abaixo ilustra todo o
processo:
Programa Fonte
Anlise Lxica
ANLISE
Anlise Sinttica
Tabela de
Smbolos
Anlise Semntica
Gerao de Cdigo Intermedirio
Manipulador
de Erros
Otimizao de Cdigo
SNTESE
Programa Objeto
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 entao o elemento de entrada para a an
alise sint
atica
ou analise gramatical (parsing ), onde e verificado se os tokens podem ser agrupados em
sentencas validas (comandos, expressoes, etc.) da linguagem fonte. Normalmente, esses
agrupamentos sao realizados atraves da construcao de uma
arvore sintatica conforme e
apresentado na figura 1.3:
Comando de
Atribuio
Identificador
Smbolo de
Atribuio
Media
:=
Expresso
Expresso
Nota1
Operador
Aritmtico
Expresso
+
Expresso
Nota2
Operador
Aritmtico
Expresso
A an
alise sem
antica tem por objetivo validar os comandos e expressoes atraves de
analises como compatibilidade de tipos e escopo de identificadores. Esta etapa analisa,
por exemplo, se um identificador declarado como variavel e usado como tal, ou se uma
expressao atribuda a uma variavel retorna um tipo compatvel com o qual foi declarada
a variavel (em algumas linguagens, uma variavel inteira nao pode receber uma expressao
real).
Ate aqui foi realizada a etapa de analise do programa fonte, ou seja, a procura por
erros de programacao. Caso nenhum erro seja encontrado, o processo de compilacao passa
entao para a etapa de sntese, ou seja, a construcao do programa objeto.
A gerac
ao do c
odigo intermedi
ario e a primeira fase da construcao do programa
objeto. O que ela faz e a representacao do programa fonte em uma linguagem intermediaria simplificada (maquina abstrata), o que permite a realizacao da proxima etapa
mais facilmente.
A proxima etapa e a otimiza
c
ao de c
odigo, que tem por objetivo tentar modificar
o codigo intermediario no intuito de melhorar a velocidade de execucao, bem como a
utilizacao do espaco de memoria, fazendo com isso, um uso mais racional dos recursos da
maquina.
Au
ltima etapa do processo de compilacao e a gerac
ao de c
odigo objeto propriamente dita. Esta fase tem como objetivos: producao de codigo objeto, reserva de memoria
a fase mais difcil, pois repara constantes e variaveis, selecao de registradores, etc. E
quer uma selecao cuidadosa das instrucoes e dos registradores da maquina alvo a fim de
produzir codigo objeto eficiente.
Exemplo de geracao de codigo para o codigo fonte:
While I < 100 do I := J + I
C
odigo Intermedi
ario
L0: if I<100 goto L1
goto L2
Otimizac
ao
L0: if I 100 goto L2
I := J+I
goto L0
L2: . . .
C
odigo Objeto
L0: MOV AX, I
CMP AX, 100
JGE L2
MOV AX, J
MOV BX, I
ADD BX
MOV I, AX
JMP L0
L2: . . .
Os dados a serem armazenados dependem do projeto do tradutor, mas os mais comuns sao: identificador, classe (variavel, parametro, procedimento, etc.), tipo, endereco,
tamanho.
A tabela de smbolos deve ser estruturada de uma forma que permita rapida insercao
e extracao de informacoes, porem deve ser tao compacta quanto possvel.
O modulo de manipulac
ao de erros tem por objetivo tratar os erros que sao detectados em todas as fases de analise do programa fonte e deve dispor de mecanismos
(recuperacao de erros) que permitam que o processo de analise prossiga mesmo que erros
tenham sido detectados.
1.3
Captulo 2
Um Compilador Simples de uma
Passagem
2.1
Definic
ao da Sintaxe
< Cmd > IF < Expr > THEN < Cmd > ELSE < Cmd >
as palavras-chave como IF, THEN e ELSE representam os smbolos terminais, enquanto
que os termos Cmd e Expr, representam os nao-terminais.
Exemplo de uma GLC simples para definir expressoes aritmeticas baseadas apenas em
adicao e subtracao:
2.2
An
alise Gramatical
A
X
Cadeia
Cadeia
Cadeia
9
+
Cadeia
Cadeia
Cadeia
Cadeia
Cadeia
5
Cadeia
+
Cadeia
2
Associatividade de Operadores
Convencionalmente, 9+5+2 e equivalente `a (9+5)+2, pois, ao analisarmos o operando 5
precisamos decidir qual operacao sera realizada primeiro. Pela convencao da matematica a
adicao e associativa `a esquerda, sendo assim o resultado (9+5)+2 e obtido. Na maioria das
linguagens de programacao, as quatro operacoes basicas (adicao, subtracao, multiplicacao
e divisao) sao associativas `a esquerda.
A exponenciacao e um exemplo de operador associativo `a direita (em Fortran) 5**2**3
e equivalente a 5**(2**3). Outro exemplo e o operador de atribuicao, onde a expressao
a=b=c (em linguagem C) e tratada como a=(b=c).
Preced
encia de Operadores
Considere a expressao 9+52. Existem duas interpretacoes possveis: (9+5)2 e 9+(52).
Quando mais de um tipo de operadores estiverem presentes em uma expressao e necessario
se definir a ordem de precedencia entre eles.
Na aritmetica, os operadores e / tem precedencia mais alta do que + e -; assim, na
expressao anterior o operador de multiplicacao e capturado antes da adicao.
2.2.1
Exerccios Propostos
2.3
write("Media = ",med);
end;
write("Media Geral = ",Media_das_medias/total);
end.
2.3.1
Exerccios Propostos
12
Captulo 3
An
alise L
exica
3.1
O Papel do Analisador L
exico
A analise lexica e a primeira fase de um compilador e tem por objetivo fazer a leitura
do programa fonte, caracter a caracter, e traduzi-lo para uma seq
uencia de smbolos
lexicos denominados tokens, os quais sao utilizados pelo analisador sintatico. Exemplos
de tokens sao os identificadores, palavras reservadas, operadores da linguagem, etc.
A interacao entre analise lexica e sintatica e normalmente implementada fazendo-se
com que o analisador lexico seja uma sub-rotina ou co-rotina do parser (ver figura 3.1).
Ao receber do parser um comando do tipo obter proximo token, o analisador lexico le
os caracteres de entrada ate que possa identificar o proximo token.
Token
Programa
Fonte
Analisador
Lxico
Obter Prximo
Token
Analisador
Sinttico
Tabela de
Smbolos
Porque efetuar an
alise l
exica?
Simplifica
c
ao de Projeto e mais simples implementar dois analisadores distintos (para
tarefas distintas) do que um analisador sintatico que faca todo trabalho de forma
unificada;
Melhor Efici
encia a analise lexica e potencialmente mais lenta que a sintatica (pois
efetua leitura de caracteres em disco). Tecnicas de buferizacao de leitura podem
acelerar significativamente este processo;
Portabilidade as peculiaridades do alfabeto de entrada de cada linguagem podem ser
tratadas exclusivamente pelo scanner.
Tokens, Padr
oes e Lexemas
Um token e um smbolo terminal da gramatica da linguagem fonte sob analise. Em geral,
existem diversas cadeias de caracteres para as quais o mesmo token e gerado.
Essas cadeias respeitam um determinado padr
ao ou regra associada a esse token.
Um lexema e um conjunto de caracteres que e reconhecido pelo padrao de um determinado token.
Exemplo:
const pi = 3.14159;
a subcadeia pi e um lexema para o token identificador, pois respeita o padrao para os
identificadores (letra)(letra | digito) .
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 coluna onde o token foi reconhecido no arquivo fonte e n
umero de caracteres lidos ate o
reconhecimento, seria exemplos de informacoes adicionais u
teis caso um erro lexico seja
detectado.
3.2
Buferizac
ao de Entrada
E = m * c * c eof
apontador
Em casos mais simples, a entrada pode ser realizada caracter a caracter, contendo
apenas um buffer de armazenamento dos caracteres lidos.
3.3
Gram
aticas e Linguagens Regulares
Seq
u
encia de Deriva
c
ao
Entende-se por derivacao 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 derivacao e uma serie de derivacoes sucessivas
que permitem a geracao de uma determinada sentenca da linguagem.
Gram
atica Regular
Uma gramatica e dita ser regular se todas as suas regras de producao respeitam a forma
A B ou A , onde A,B sao smbolos nao-terminais e e uma sentenca contendo
somente smbolos terminais.
Gram
atica 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.
Express
oes Regulares
Uma expressao regular representa uma determinada linguagem atraves de formulas indutivas.
Simbologia adotada:
a|b
A
A+ = A {}
A?
=
=
=
=
=
3.3.1
Exerccios Propostos
P: S 0S | A
A A1 | B
B0|1|
3.4
Especificac
ao e Reconhecimento de Tokens
OUTRO
DGITO
INICIO
DIGITO
1
OUTRO
DGITO
DGITO
DIGITO
E|e
+|-
E|e
DIGITO
OUTRO
* Retornar(Num_Real,
Obter_Token())
DIGITO
* Retornar(Num_Inteiro, Obter_Token())
17
LETRA OU DGITO
INICIO
LETRA
OUTRO
* Retornar(ID, Obter_Token())
CARACTERES
VLIDOS
INICIO
"
"
Retornar(String,Obter_Token())
(iniciam obrigatoriamente com e tem pelo menos 2 caracteres), e ainda, ser capaz de
tratar os caracteres nulos: espacos, enter, tab e comentarios, sem reconhecer token.
Reconhecendo palavras reservadas como identificadores simples:
Criar uma funcao de identificacao de palavras reservadas (enumeracao) que retorna a
classe palavra reservada ou identificador.
USO: Retornar(ObterClasse(Lexema),Lexema)
Erros L
exicos
1. Caracter Inv
alido : uso de um caracter (simbolo) de entrada (arquivo fonte)
que nao pertenca ao alfabeto da linguagem. Exemplo: # ou %
2. Delimitador N
ao Balanceado : definicao de uma cadeia literal (ou constante
caracter) sem o correto balanceamento das aspas. Exemplo: Entrada de Dados
umero Real Inv
alido : definicao incorreta ou incompleta de um n
umero real.
3. N
Exemplos: 1., 1.0e3, .8, 1e+
O codigo abaixo apresenta algum erro lexico? Apresente a lista lexica.
begin ; <>media==10.5E-5
/===//%_
M
edia 1.P
Teste?
Solucao:
begin Palavra Reservada
; Smbolo Ponto e Virgula
18
3.4.1
Trabalho Pr
atico #1
20
Captulo 4
An
alise Sint
atica
4.1
Arvores
de Deriva
c
ao
Arvore
de derivacao e a representacao grafica de uma derivacao de sentenca.
Exemplo: Considerando a gramatica abaixo, gerar arvore de derivacao que comprova
que a sentenca 45 e valida (ver Figura 4.1).
21
<Numero>
<Num>
<Num>
<Digito>
<Digito>
Deriva
c
ao mais `
a Esquerda e mais `
a Direita
Derivacao mais `a esquerda e obtida por gramaticas que geram inicialmente, os smbolos
mais `a esquerda da sentenca sob analise; analogamente para as derivacao mais `a direita.
Exemplo: Seja a gramatica: E E + E | E E | E E | E/E | (E) | x. Pode obter
a expressao x+x*x de duas formas, conforme a figura 4.2:
E
X
E
E
E
X
4.2
An
alise Sint
atica Ascendente - BOTTOM UP
aAbcde
aAde
aABe
4.2.1
Algoritmo Empilhar-e-Reduzir
Entrada
id+id*id
+id*id
+id*id
id*id
*id
*id
*id
id
$
$
$
Pilha
$
$id
$E
$E+
$E+id
$E+E
$E
$E*
$E*id
$E*E
$E
o
Ac
a
empilhar
reduzir E id
empilhar
empilhar
reduzir E id
reduzir E E + E
empilhar
empilhar
reduzir E id
reduzir E E E
aceitar
Sao apenas 4 as operacoes possveis por este metodo: empilhar, reduzir, aceitar ou
erro.
Conflitos durante a An
alise Sint
atica 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 conflito empilhar/reduzir.
Outro conflito possvel, o reduzir/reduzir, ocorre quando nao e possvel optar entre
as diversas reducoes possveis.
4.3
An
alise Sint
atica Descendente - TOP DOWN
A analise sintatica top-down pode ser vista como uma tentativa de se encontrar uma
derivacao mais `a esquerda para uma cadeia de entrada, ou ainda, como de se construir a
arvore gramatical a partir da raiz em direcao `as folhas.
O processo de analise pode ser feito de forma recursiva ou nao, onde a forma recursiva pode ser realizada com ou sem retrocesso (backtracking), dependendo das regras de
producao gramatica.
An
alise Sint
atica 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 producao para o nao-terminal a
ser expandido, a opcao escolhida e funcao do smbolo corrente na fita de entrada (token
sob analise). Se o token nao define 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.
Verificar se a gramatica gera a sentenca cad.
Exemplo 2: S cA A aB B D | bD D d
24
S
c
falha!
S
c
S
c
sucesso!
a
Figura 4.3: Analise descendente com backtracking
A analise sintatica e dita ser uma analise sintatica preditiva caso nao seja necessario a
realizacao de retrocesso no processo e pode ser implementada de forma recursiva ou nao
(atraves da utilizacao de uma pilha).
4.3.1
An
alise Sint
atica Preditiva
4.3.2
Exerccios Propostos
X 0X | 1Y |
Y 1Y |
Fatora
c
ao `
a Esquerda
A fatoracao `a esquerda e uma transformacao gramatical u
til para a criacao de uma
gramatica adequada `a analise sintatica preditiva. A ideia basica esta em, quando nao
estiver claro qual das duas producoes alternativas usar para expandir um nao-terminal A,
estarmos capacitados a reescrever as producoes A e postergar a decisao ate que tenhamos
visto o suficiente da entrada para realizarmos a escolha certa.
Exemplo: S aXbY cZ | aXbY
26
A
A
A
A
A
A'
A'
A'
A'
A'
Ao analisarmos o token a nao ha como saber qual das duas alternativas utilizar (o
comando com ou sem o cZ). Quando houver duas producoes A 1 | 2 , devemos
postergar a decisao expandindo A para A0 e entao expandir A para 1 | 2 . Fatorando
esta gramatica temos: S aXbY S 0 S 0 cZ | .
4.4
Reconhecedor de Gram
aticas Preditivas Descendentes
a + b $
Parser
X
Y
Z
Tabela de
Anlise
Figura 4.5: Funcionamento de um Analisador Sintatico Descendente
T E0
T E 0 |
FT0
F T 0 |
F | id
Cl
ausula 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 0 = {, }
T 0 = {, }
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
Cl
ausula 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
aplicacao da regra 2 em (E 0 T E 0 ) e regra 3 em (E 0 ). Sendo assim temos:
FOLLOW(T) = FIRST(E) + FOLLOW(E) = {, $}.
FOLLOW(T) = FOLLOW(T) pela aplicacao da regra 3 em T F T 0 . E finalmente,
FOLLOW(F) = FIRST(T) + FOLLOW(T). Aplicacao das regras 2 e 3 em T 0 F T 0 |
, ou seja FOLLOW(F) = {, , $}.
4.4.1
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 producao X a M[X,a];
Se FIRST() inclui a palavra vazia, entao 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 T E 0 tem-se FIRST(TE) = {, id} entao, M[E, ] = M[E,id] = E T E 0 .
Para E 0 T E 0 tem-se FIRST(T E 0 ) = {} entao, M[E, ] = E 0 T E 0 .
Para E 0 tem-se FOLLOW(E) = {$} entao, M[E, $] = E 0 .
Para T F T 0 tem-se FIRST(FT) = {, id} entao, M[T, ] = M[T,id] = T F T 0 .
Para T 0 F T 0 tem-se FIRST(F T 0 ) = {} entao, M[T, ] = T 0 F T 0 .
Para T 0 tem-se FOLLOW(T) = {, $} entao, M[T, ] = M[T,$] = T 0 .
Para F F tem-se FIRST(F ) = {} entao, M[F, ] = F F .
Para F id tem-se FIRST(id) = {id} entao,M[F,id] = F id.
E
E
T
T
F
id
E T E0
E T E0
E 0 T E 0
$
E0
T FT0
T FT0
0
T F T
F id
T0
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 derivacao mais `a esquerda (Leftmost Derivation), levando
em conta apenas um smbolo da entrada.
Exerccio: Considerando a gramatica para a linguagem a ser reconhecida pelo prototipo de compilador para analise descendente, construir a tabela de analise resultante.
29
4.4.2
<
<
<
+
>
>
>
<
*
>
<
>
<
$
>
>
>
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 sentenca obtida ficaria $ E + E * E $; removendo-se os
nao-terminais e acrescentando-se as relacoes de precedencia temos: $ < + < > $,
indicando que a proxima reducao deve ser realizada sobre o operador * (e seus respectivos operandos associados E* E).
Devido ao fato da sua implementacao nao ser trivial, a solucao de implementacao mais
viavel para este tipo de gramatica e fazer uso de um gerador de analisadores sintaticos,
como o YACC ou BISON.
4.4.3
Considerando o uso de analisadores sintaticos descendentes preditivo algumas preocupacoes quanto a gramatica a ser utilizada, devem ser tomadas: eliminar ambig
uidade, eliminar 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 P ASCALjr :
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 P ASCALjr respeita a seguinte estrutura:
31
SimbAtrib Expr |
SimbAtribSoma Expr |
SimbAtribSubt Expr |
SimbAtribMult Expr |
SimbAtribDivi Expr |
SimbIncr | Identificador SimbDecr
34
35
Analisando Express
oes L
ogicas e Aritm
eticas
Para descrever sentencas que formam expressoes aritmeticas compostas das cinco operacoes basicas (adicao, subtracao, multiplicacao, divisao e potenciacao), tendo como operandos: identificadores 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
representacao 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 |
Identificador | 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 simples, esta nao leva em consideracao 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:
4.4.4
Exerccios Propostos
4.4.5
Trabalho Pr
atico #2
39
40
Captulo 5
An
alise Sem
antica
5.1
Traduc
ao Dirigida pela Sintaxe
Produc
ao
LE
E E+T
ET
T T F
T F
F (E)
F inteiro
E.val = 15
T.val = 3*5 = 15
T.val = 3
F.val = 3
F
3
Regras Sem
anticas
imprimir(E.val)
E.val = E.val+T.val
E.val = T.val
T.val = T.val*F.val
T.val = F.val
F.val = E.val
F.val = inteiro.lexema
Imprimir(19)
E.val = 15+4 = 19
F.val = 5
Inteiro.Lexval = 5
T.val = 4
F.val = 4
Inteiro.Lexval = 4
Inteiro.Lexval = 3
Regras Sem
anticas
L.in = T.tipo
T.tipo = inteiro
T.tipo = real
L.in = L.in
incluir tipo(id, L.in)
incluir tipo(id, L.in)
Grafos de Depend
encia
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
A.a
X.x
Y.y
5.1.1
Definic
oes L-Atribudas
O nome L provem de left, onde a analise gramatical e feita sempre a partir do filho mais
`a esquerda na arvore sintatica atraves do seguinte algoritmo:
Algoritmo de pesquisa em profundidade (depth-first order ):
Procedimento Visitar(n: n
o);
Inicio
Para cada filho m de n da esquerda para a direita fa
ca
Inicio
Avaliar os atributos herdados de m;
Visitar(m);
Fim
Avaliar os atributos sintetizados de m;
Fim
Uma definicao dirigida pela sintaxe e L-atribuda se cada atributo herdado de Xj ,
i j n, do lado direito de A X1 , X2 , . . . , Xn depender somente:
Dos atributos dos smbolos X1 , X2 , . . . , Xj1 `a esquerda de Xj na producao e
Dos atributos herdados de A.
Note-se que cada definicao S-atribuda e L-atribuda porque as restricoes (1) e (2) se
aplicam somente aos atributos herdados.
43
5.1.2
Verificac
oes de Contexto
Um compilador precisa fazer uma verificacao estatica das estruturas do programa, para
assegurar que o mesmo esteja livre de certos tipos de erros, tais como:
Verifica
c
ao de Tipos verifica se um operador esta sendo aplicado a operandos de tipos
incompatveis. Exemplo: 10 + TRUE.
Verifica
c
ao de Fluxo de Controle os enunciados de fluxo de controle (repeticoes, 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 fluxo
de controle (while, for ou switch).
Verifica
c
oes de Unicidade verifica se os identificadores foram declarados de forma
unvoca, ou seja, sem duplicidade.
Verifica
c
oes relacionadas aos nomes existem linguagens que apresentam particularidades acerca dos identificadores do programa. Essa etapa da analise verifica se essas
particularidades foram respeitadas. Exemplo: A linguagem ADA exige que blocos
de comandos comecem e terminem com o mesmo identificador.
Sistema de Tipos
Define-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
adicao e, em caso afirmativo, qual seria o tipo resultante, e ainda, se esse tipo resultante
e compatvel com o tipo associado `a variavel X.
Todo o processo de analise semantica para verificacao de tipos e realizado a partir da
construcao de express
oes de tipo, que podem ser compostas: ou por um tipo simples ou
por um construtor de tipos (tipo resultante da aplicacao 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
int
B
= (invlido)
+ (float)
int
int
float
Tipo Simples
float
int
Construo de Tipos
Exemplo1: Para uma producao do tipo E id teramos uma regra semantica como
{ E.tipo = id.tipo }.
Exemplo2: Imagine uma linguagem que permita apenas operacoes com tipos identicos,
entao para uma producao do tipo E E + T teramos a regra semantica { E.tipo = se
E.tipo = T.tipo entao E.tipo senao invalido }
Verifica
c
ao de Tipos em Express
oes
As seguintes regras semanticas podem ser abstradas de forma geral:
E
E
E
E
tipo simples
id
E1
E1 op E2
Constru
c
ao de um Sistema de Tipos para Operadores
Apresenta as relacoes de compatibilidade, para cada operador, em funcao dos operandos.
Exemplo na linguagem Pascal:
Op1
integer
integer
integer
integer
integer
real
real
real
real
real
char
char
char
char
char
string
string
string
string
string
boolean
boolean
boolean
boolean
boolean
Op2
integer
real
char
string
boolean
integer
real
char
string
boolean
integer
real
char
string
boolean
integer
real
char
string
boolean
integer
real
char
string
boolean
+
integer
real
Erro
Erro
Erro
real
real
Erro
Erro
Erro
Erro
Erro
String
String
Erro
Erro
Erro
String
String
Erro
Erro
Erro
Erro
Erro
Erro
45
integer
real
Erro
Erro
Erro
real
real
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
*
integer
real
Erro
Erro
Erro
real
real
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
/
real
real
Erro
Erro
Erro
real
real
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
5.2
Op2
inteiro
real
caracter
cadeia
logico
inteiro
real
caracter
cadeia
logico
inteiro
real
caracter
cadeia
logico
inteiro
real
caracter
cadeia
logico
inteiro
real
caracter
cadeia
logico
+
inteiro
real
Erro
Erro
Erro
real
real
Erro
Erro
Erro
Erro
Erro
cadeia
cadeia
Erro
Erro
Erro
cadeia
cadeia
Erro
Erro
Erro
Erro
Erro
Erro
inteiro
real
Erro
Erro
Erro
real
real
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
*
inteiro
real
Erro
Erro
Erro
real
real
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
/
real
real
Erro
Erro
Erro
real
real
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
**
inteiro
real
Erro
Erro
Erro
real
real
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Erro
Tabela de Smbolos
uma estrutura de dados gerada pelo compilador com o objetivo de armazenar inE
formacoes sobre os nomes (identificadores de variaveis, de parametros, de funcoes, etc.)
definidos 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 identificador 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 informacoes associadas aos atributos dos identificadores que foram reconhecidas pelo compilador. Assim
46
sendo, essas tarefas sao realizadas nas fases de analise sintatica e/ou semantica, onde se
referencia `a tabela, cada vez que um identificador 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
suficientemente grande para suportar qualquer programa (limitacao 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
ineficiente para programas grandes, ja o hashing tem melhor desempenho mas exige
maior esforco de programacao.
cada entrada na TS esta associada a um nome de identificador. Estas entradas
podem nao ser uniformes, ou seja, os atributos de um identificador de variavel
nao sao os mesmos de um identificador de funcao, por exemplo. O uso de registros
variantes pode ser uma alternativa elegante para se organizar a estrutura que contera
tais atributos.
Enfim, 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
modificacao da estrutura de armazenamento desses dados. Essas preocupacoes poderao
estar relacionadas `a aplicacao que se deseja desenvolver.
5.2.1
De maneira geral, qualquer informacoes acerca dos identificadores definidos em um programa 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: variavel, constante, procedimento, funcao, rotulo), tamanho de memoria a ser alocada (em bytes), endereco (onde foi
alocada), escopo, etc.
Para efeito da disciplina (implementacao 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 T AM HASH; } onde, K TAM HASH
indica o tamanho (n de posicoes) do vetor hash.
A funcao hash acima gera valores entre 0 e K TAM HASH-1 em funcao do valor de
Key (chave a ser inserida).
Problema!: Caso exista duas chaves que possuam o mesmo valor de chave, a funcao
hash ira gerar o mesmo endereco de vetor. Isto e chamado de colisao ou conflito de
espalhamento.
47
Existem dois metodos basicos para manipular colisoes de espalhamento: reespalhamento ou encadeamento.
Solucionando colis
oes atrav
es de reespalhamento
Requer o uso de uma funcao de espalhamento secundaria sobre a chave, sucessivas vezes
ate que um endereco valido (disponvel) seja encontrado para insercao do elemento.
No processo de busca ocorre ideia semelhante, exemplo: desejase localizar uma determinada chave k:
1. usa-se a funcao de espalhamento principal
2. caso nao encontrado, usa-se a funcao de espalhamento secundaria
3. caso nao encontrado, repete-se o passo 2.
1. Inserindo elemento MEDIA: Hash(MEDIA)=6
1
2
3
4
5
6 MEDIA
2. Inserindo elemento X: Hash(X)=2
1
2
X
3
4
5
6 MEDIA
3. Inserindo elemento MEDIA FINAL: Hash(MEDIA FINAL)=6 (conflito!)
Hash2(MEDIA FINAL)=1 (endereco valido)
1 MEDIA FINAL
2
X
3
4
5
6
MEDIA
4. Inserindo elemento CONT: Hash(CONT)=1 (conflito!)
Hash2(CONT)=2 (conflito!)
Hash2(CONT)=3 (endereco valido)
1 MEDIA FINAL
2
X
3
CONT
4
5
6
MEDIA
48
Media
X1
X2
Res
Cont
Figura 5.4: Hashing com Encadeamento
49
5.3
3. Tipos Incompatveis
4. Identificador Duplicado
51
52
53
54
55
5.3.1
Trabalho Pr
atico #3
57
58
Captulo 6
Gerac
ao de C
odigo Intermedi
ario
a primeira fase da etapa de sntese, responsavel por transformar a arvore de derivacao
E
em um trecho de codigo (que pode eventualmente ser o proprio codigo objeto final).
Freq
uentemente porem, o codigo gerado nao especifica detalhes da maquina alvo, tais
como quais registradores serao usados ou quais enderecos de memoria serao referenciados,
etc.
Existem vantagens e desvantagens de se usar a etapa de geracao de codigo intermediario. Vantagens:
Permite otimizacoes de codigo, a fim de tornar o codigo final mais eficiente;
Simplifica a implementacao do compilador;
Possibilita que um mesmo codigo intermediario possa ser traduzido para diferentes
linguagens objeto.
Desvantagens:
Uma etapa a mais e executada durante o processo de compilacao, tornando o processo mais lento.
6.1
Linguagens Intermedi
arias
6.1.1
Representa
co
es Gr
aficas
=
a
+
*
*
c b 2
1. a = b + c d/4
2. exp = b b 4 a c >= 0 && a! = 0
3. d = (a a) (b b)
6.1.2
Notac
ao P
os (e Pr
e) Fixadas
s-Fixada
Po
ab + c
abc +
abc +
abc d+ =
-Fixada
Pre
+ abc
a + bc
+a bc
= a + bcd
6.1.3
C
odigo de Tr
es-Endere
cos
(0)
(1)
(2)
(3)
oper
+
*
=
(0)
(1)
(2)
(3)
arg1
C
T1
B
T3
oper
+
*
=
arg2
D
T2
arg1
C
(0)
B
A
result
T1
T2
T3
A
arg2
D
(1)
(2)
Expr (Expr1 )
Expr ID
{Atrib.cod = Expr.cod;
Geracod(ID.nome=Expr.nome); }
{Expr.nome = GeraT emp;
Expr.cod = Expr1 .cod || Expr2 .cod ||
Geracod(Expr.nome=Expr1 .nome+Expr2 .nome); }
{Expr.nome = GeraT emp;
Expr.cod = Expr1 .cod || Expr2 .cod ||
Geracod(Expr.nome=Expr1 .nome*Expr2 .nome); }
{Expr.nome = Expr1 .nome; Expr.cod = Expr1 .cod; }
{Expr.nome = ID.nome; Expr.cod = 00 }
61
Esquema de Tradu
c
ao para Express
oes L
ogicas
Existem dois metodos principais: representacao numerica e representacao por fluxo de
controle.
Representa
ca
o Num
erica
Codifica numericamente as constantes true (=1) e false (=0) e avalia o resultado logico
numa variavel temporaria.
Exemplo: Supondo que o codigo gerado seja armazenado a partir da quadrupla 100 o
comando A < B seria traduzido para:
099:
100:
101:
102:
103:
104:
Expr true
Expr false
...
if A < B goto 103
T1=0
goto 104
T1=1
...
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
T2=1
if E < F goto 111
T3=0
goto 112
T3=1
T4=T2 && T3
T5 = T1 || T4
S prEnquanto Expr S1
6.2
BackPatching (Retrocorrec
ao)
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
geracao de codigo num u
nico passo.
A solucao e utilizar geracao de codigos incompletos para os comandos (sem os enderecos) que serao devidamente completados quando o endereco destino for conhecido.
Chama-se de backpatching ao preenchimento desses enderecos nao resolvidos.
Sao necessarias tres funcoes para isso:
makelist(i) cria uma lista contendo i (um u
nico 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
E E1 && E2
E !E1
E (E1 )
E ID1 opRel ID2
E ID
Neste esquema, E.ListaTrue e E.ListaFalse sao atributos sintetizados (listas) que indicam 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
ListaTrue=100,104
ListaFalse=103,105
E
ListaTrue=100
ListaFalse=101
||
A<B
ListaTrue=102
ListaFalse=103
M
E
Quad=102
&&
ListaTrue=104
ListaFalse=103,105
Quad=104
C<D
E<F
if A < B goto
goto
if C < D goto
goto
if E < F goto
goto
100:
101:
102:
103:
104:
105:
65
if A < B goto
goto 102
if C < D goto 104
goto
if E < F goto
goto
ListaTrue=104
ListaFalse=105
O endereco indicado por EOF representa o fim 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
001: goto EOF
002: if C < D goto 004
003: goto 007
004: T1 = Y+Z
005: X = T1
66
006:
goto 000
007:
T2 = Y-Z
008:
X = T2
009:
goto 000
EOF:
Captulo 7
Otimizac
ao de C
odigo
Trata-se do problema da geracao de codigo eficiente: uso racional da memoria e rapidez
na execucao do codigo fonte. Porem, muitas vezes esses aspectos sao conflitantes, ou seja,
apela-se para um maior tempo de execucao para se conseguir um ganho no consumo de
memoria e vice-versa.
Compiladores que aplicam transformacoes de melhorias no codigo gerado sao denominados 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 atribuicoes
redundantes, sub-expressoes comuns, temporarios desnecessarios, etc., no intuito de diminui o codigo intermediario, enquanto que no codigo objeto e feita uma substituicao de
instrucoes por equivalentes mais rapidos que permitem melhor uso dos registradores.
7.1
Otimizac
ao Peephole
L1:
goto L1
...
goto L2
L1:
goto L2
...
goto L2
Neste caso, se nao houverem outras instrucoes que levem a L1, esta instrucao pode
ser removida do codigo.
Exemplo 2:
L1:
if a < b goto L1
...
goto L2
L1:
L3:
goto L1
...
if a < b goto L2
...
L1:
if a < b goto L2
...
goto L2
L3:
if a < b goto L2
goto L3
...
...
Exemplo 3:
Simplifica
c
ao Alg
ebrica
Remocao de redundancias que ocorrem em expressoes algebricas, tais como: x=x+0 ou
y=y*1.
7.2
Otimizac
ao de Blocos Sequenciais atrav
es de grafos
C
odigo Intermedi
ario
T1 = a+b
T2 = c+d
T3 = e-T2
T4 = T1-T3
T2
T3
T1
T4
7.2.1
=
=
=
=
c+d
e-T2
a+b
T1-T3
C
odigo Objeto
MOV a, R0
ADD b, R0
MOV c, R1
ADD d, R1
MOV, R0, T1
MOV E, R0
SUB R1, R0
MOV T1, R1
SUB R0, R1
MOV R1, T4
MOV c, R0
ADD d, R0
MOV e, R1
SUB R0, R1
MOV a, R0
ADD b, R0
SUB R1, R0
MOV R0, T4
O algoritmo supoe que cada instrucao (de tres-enderecos) segue um dos seguintes tres
formatos: (1) x = y op z; (2) x = op y; (3) x = y. Instrucoes 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, verifique se existe um no op com filhos 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, verifique se existe um no op com um u
nico filho 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
+
*
T1,T4
T3
T2
T7,y
T6
T5
7.2.2
Cria-se uma lista de ordenacao dos nos internos do GAD atraves do algoritmo a seguir:
Enquanto existirem n
os interiores n~
ao listados fa
ca inicio
1. Selecionar um n
o n n~
ao listado do qual todos os pais j
a 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 fa
ca 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
Gerac
ao de C
odigo Objeto
a fase final de um modelo de compilador. Recebe como entrada a representacao interE
mediaria do programa-fonte e produz como sada um programa-alvo equivalente.
Por ser impraticavel a criacao de um gerador de codigo otimo, contenta-se com a
criacao (atraves de heursticas) de bons geradores de codigo. A maioria dos problemas
de geracao de codigo envolve aspectos como gerencia de memoria, selecao de instrucoes,
alocacao de registradores e ordem de avaliacao.
As principais caractersticas de um bom gerador de codigo: deve ser correto (essencial),
usar de forma eficiente os recursos da maquina e o proprio gerador deve ser implementado
de forma eficiente.
Uma entrada usual para um gerador de codigo e o codigo intermediario juntamente
com as informacoes armazenadas na tabela de smbolos. Ja a sada pode ser uma linguagem absoluta de maquina (carrega em uma posicao fixa de memoria antes da execucao),
linguagem relocavel (cria modulos que podem ou nao, serem executados em conjunto,
sendo para tal necessario uma etapa de linkedicao dos modulos) ou linguagem de montagem (gera instrucoes simbolicas que sao traduzidas atraves de um montador). A terceira
opcao sera a escolhida para a implementacao do trabalho pratico #4.
Sele
c
ao de Instruco
es
Se nao nos importarmos com a eficiencia do codigo-objeto gerado, a selecao de instrucoes e
um processo direto. Para cada tipo de instrucao intermediaria (codigo de tres-enderecos)
gerada, projeta-se um esqueleto de codigo final equivalente. Exemplo: x = y + z.
MOV y, R0 (carregar y no registrador R0)
ADD z,R0 (adicionar z a R0)
MOV R0, x (armazenar R0 em x)
Infelizmente, o codigo final gerado por esta alternativa freq
uentemente e extenso e
ineficiente. Exemplo:
a = b+c
d = a+e
MOV b, R0
ADD c, R0
MOV R0, a
MOV a, R0
ADD e, R0
MOV R0, d
71
Operac
ao com Registradores
O uso de registradores no processo de execucao de instrucoes e interessante pois:
Operacao com registrador e mais rapida
Conjunto de registradores e pequeno
Utilizacao eficiente = otimizacao
Desvantagem: a ordem das instrucoes e muito importante no aspecto eficiencia.
8.1
M
aquina Objeto
Para se criar um gerador de codigo para uma determinada maquina objeto, primeiramente
e necessario conhecer seu conjunto de instrucoes.
Por motivos didaticos optou-se por uma maquina hipotetica (MAQHIPO), que tem
as seguintes caractersticas:
baseada em uma pilha;
E
Trabalha com os mesmos tipos de dados das fases anteriores;
Sua memoria e dividida em duas partes:
Area
de C
odigo contem uma lista (CODIGO) das instrucoes geradas pelo compilador (codigo de tres-enderecos);
Area
de Dados composta por uma pilha (DADOS) que contera os registradores
manipulados pelas instrucoes da maquina (so existe em tempo de execucao de
um programa) e uma lista (MEMO) que armazena as variaveis e constantes
manipuladas pelo programa. Para efeito de simplificacao, 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.
uenci2. Todas as instrucoes (indicadas pelo ponteiro PROXINST) sao executadas seq
almente ate a instrucao de parada ou ate que ocorra algum erro de execucao;
3. A execucao de cada instrucao incrementa o valor de PROXINST (exceto para instrucoes de desvio);
O conjunto de instrucoes validas para a MAQHIPO e:
Inicializa
c
ao e Finaliza
c
ao
72
DISX substitui os dois elementos mais ao topo da pilha por sua disjuncao exclusiva (& |).
{Dados[T opoDados 1] = Dados[T opoDados 1] & | Dados[T opoDados];
T opoDados ; }
NEGA inverte o valor logico do elemento no topo da pilha .
{Dados[T opoDados] =!Dados[T opoDados]; }
CPME substitui os dois elementos mais ao topo da pilha pela comparacao de menor.
{Dados[T opoDados 1] = Dados[T opoDados 1] < Dados[T opoDados];
T opoDados ; }
CMAI substitui os dois elementos mais ao topo da pilha pela comparacao de maior.
{Dados[T opoDados 1] = Dados[T opoDados 1] > Dados[T opoDados];
T opoDados ; }
CPIG substitui os dois elementos mais ao topo da pilha pela comparacao de igualdade.
{Dados[T opoDados 1] = Dados[T opoDados 1] == Dados[T opoDados];
T opoDados ; }
CDIF substitui os dois elementos mais ao topo da pilha pela comparacao de desigualdade.
{Dados[T opoDados 1] = Dados[T opoDados 1] ! = Dados[T opoDados];
T opoDados ; }
CPMI substitui os dois elementos mais ao topo da pilha pela comparacao de menor ou
igual.
{Dados[T opoDados 1] = Dados[T opoDados 1] <= Dados[T opoDados];
T opoDados ; }
CPMA substitui os dois elementos mais ao topo da pilha pela comparacao de maior ou
igual.
{Dados[T opoDados 1] = Dados[T opoDados 1] >= Dados[T opoDados];
T opoDados ; }
74
Comando de Atribui
c
ao
ARMZ n Transporta o conte
udo do topo da pilha para o endereco de memoria n.
{M emo[n] = Dados[T opoDados ]; }
Comandos Condicionais e Iterativos
DSVF p Desvio se condicional falso para a instrucao p.
{P roxInst = Dados[T opoDados]?P roxInst + 1 : p; }
DSVI p Desvio incondicional para a instrucao p. {P roxInst = p; }
EXEC n Executa a chamada a uma sub-rotina com inicio na instrucao n e empilha o
endereco de retorno
{Desvios[+ + T opoDesvio] = P roxInst + 1; P roxInst = n; }
RETR Retorna a execucao para a proxima instrucao apos a chamada `a sub-rotina
{P roxInst = Desvios[T opoDesvio ]; }
Exemplo de Geracao de Codigo Objeto na Linguagem Hipo:
Programa Fonte
Var: inteiro F, N;
Inicio
Leia(N);
F=1;
Enquanto N >= 1 faca Inicio
F = N;
N ;
Fim
Escreva(F);
Fim
75
01
02
03
Programa Objeto
INIP
ALME 1 i
ALME 1 i
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ENTR
ARMZ 1
CRCT 1 i
ARMZ 0
CRVL 1 i
CRCT 1 i
CPMA
DSVF 21
CRVL 0 i
CRVL 1 i
MULT
ARMZ 0
CRVL 1 i
CRCT 1 i
SUBT
ARMZ 1
DSVI 8
CRVL 0 i
IMPR
FIMP
8.1.1
76
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 Identificador ListaParam
{ Geracod(ARMZ Identificador.endereco);
Gerar codigo para ListaParam }
ListaParam { }
ListaParam Virg Tipo Identificador ListaParam
{ Geracod(ARMZ Identificador.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
81
82
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 Identificador
{ Geracod(CRVL identificador.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 Pr
atico #4
85