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

Compiladores: P ASCALjr

Rogerio Eduardo da Silva, M.Sc.


2005/2

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

2 Um Compilador Simples de uma Passagem


2.1 Definicao da Sintaxe . . . . . . . . . . . . .
2.2 Analise Gramatical . . . . . . . . . . . . . .
2.2.1 Exerccios Propostos . . . . . . . . .
2.3 Caractersticas da linguagem P ASCALjr . .
2.3.1 Exerccios Propostos . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

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

Representacao da arvore gramatical da producao AXYZ . . . . . . . . .


Ambig
uidade Gramatical . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8
8

3.1
3.2
3.3
3.4
3.5

O papel do analisador lexico . . . . . . . . . . . . . . .


Buffer de entrada para um analisador lexico . . . . . .
Automato finito de reconhecimento de n
umeros inteiros
AFD de reconhecimento de identificadores simples . . .
AFD de reconhecimento de strings . . . . . . . . . . .

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

Exemplo de Representacao Grafica de Operadores para a=b*c+b*2 . . . . 60


Backpatching para expressoes logicas . . . . . . . . . . . . . . . . . . . . . 65

7.1

Grafo Acclico Dirigido - GAD . . . . . . . . . . . . . . . . . . . . . . . . . 70

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

Cronologicamente, as L. P.s sao classificadas em cinco geracoes: (1a ) linguagens de


maquina; (2a ) linguagens simbolicas (Assembly); (3a ) linguagens orientadas ao usuario;
(4a ) linguagens orientadas `a aplicacao e (5a ) 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 linguagem de maquina (codigo binario), onde cada operacao possua sua representacao binaria
que era passada `a maquina atraves de circuitos eletricos. Esse processo, alem de extremamente difcil e cansativo, era altamente sujeito a erros devido a sua grande complexidade
de execucao.
A seguir, como uma primeira tentativa de simplificacao, surgem as linguagens simbolicas ou de montagem (Assembly). Agora, extensas seq
uencias binarias sao substitudas por
mnem
onicos que sao palavras especiais que representam certas acoes basicas. Exemplo
MOV, JMP, etc. Os mnemonicos precisavam ser traduzidos para a linguagem de maquina
antes da sua execucao.
1

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

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 necessariamente.
Programa
Fonte

COMPILADOR

Programa
Objeto

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 traducao 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 preocupacao 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 criacao do programa objeto.
Interpretadores sao mais simples de serem implementados, porem, compiladores geram
execucoes mais rapidas de programas, pois nao ha a perda de tempo de traducoes virtuais
a cada nova instrucao executada.
2

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

Gerao de Cdigo Objeto

Programa Objeto

Figura 1.2: Fases da Compilacao


A an
alise l
exica 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 programacao;
ele e representado por um conjunto de caracteres que apresentam um significado 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 identificador Media
2. O smbolo de atribuicao :=
3. O identificador Nota1
4. O sinal de adicao +
5. O identificador Nota2
6. O sinal de multiplicacao *
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 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

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 definicao de expressoes:
1. Qualquer identificador e uma expressao
2. Qualquer n
umero e uma expressao
3. Se expressao1 e expressao2 sao expressoes validas, entao expressao1 op. aritmetico
expressao2 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:
oi | . . .
hcomandoi ::= hwhilei | hatribuic
a
hwhilei ::= while hexpr booli do hcomandoi
oi ::= identificador := hexpr aritmi
hatribuic
a
hexpr booli ::= hexpr aritmi op.L
ogico hexpr aritmi
hexpr aritmi ::= hexpr aritmi op.Aritm htermoi | htermoi
htermoi ::= n
umero | identificador
Apos a analise sintatica, tem-se a certeza de que o programa esta escrito corretamente
(respeita as regras gramaticais da linguagem fonte), porem, sera que o programa escrito
faz algum sentido? Ou seja, executa de forma apropriada?
4

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

L1: Temp := J+I


I := Temp
goto L0
L2: . . .

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: . . .

Alem dessas fases, ha tambem os modulos de gerenciamento de tabelas e manipula


c
ao de erros.
O gerenciamento de tabelas consiste de um conjunto de tabelas e rotinas associadas
que sao utilizadas por quase todas as fases do tradutor. A principal estrutura deste
modulo e a Tabela de Smbolos, que e responsavel por armazenar informacoes acerca dos
identificadores do programa sob analise, como por exemplo: declaracao das variaveis,
procedimentos e sub-rotinas, lista de parametros, etc..
5

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

Ferramentas para Gerac


ao de Compiladores

Existem diversas ferramentas para auxiliar a construcao de compiladores chamadas de


geradores de compiladores ou sistemas de escritas de tradutores. A seguir sao apresentados
alguns exemplos:
Geradores de Analisadores Gramaticais responsaveis por desenvolver analisadores
sintaticos, normalmente a partir de entrada baseada numa gramatica livre de contexto.
Geradores de Analisadores L
exicos geram automaticamente analisadores lexicos a
partir de uma especificacao baseada em expressoes regulares.
Dispositivos de traduc
ao dirigida pela sintaxe produzem colecoes de rotinas que
percorrem uma arvore gramatical, gerando codigo intermediario.
Geradores autom
aticos de c
odigo tal ferramenta toma uma colecao de regras que
definem a traducao de cada operacao da linguagem intermediaria para linguagem
alvo. Tais regras precisam incluir detalhamento suficiente para que possamos lidar
com os diferentes metodos de acesso possveis para os dados.
Dispositivos de fluxo de dados Ferramentas que auxiliam na etapa de otimizacao de
codigo.
Nao e de escopo desta disciplina o estudo de ferramentas de implementacao de compiladores, mais detalhes podem ser obtidos na bibliografia de apoio.

Captulo 2
Um Compilador Simples de uma
Passagem
2.1

Definic
ao da Sintaxe

A especificacao da sintaxe de uma linguagem de programacao pode ser obtida atraves de


uma gramatica livre de contexto.
Exemplo: Seja o comando condicional da forma:
o THEN Comando ELSE Comando
IF Expressa
se Expr denotar a construcao de uma expressao e Cmd denotar um comando (ou enunciado), pode-se usar as regras de producao de uma GLC1 para representar tal estrutura
da seguinte forma:

< 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:

< Lista >


< Lista >
< Lista >
< Digito >

< Lista > + < Digito >


< Lista > < Digito >
< Digito >
0|1|2|3|4|5|6|7|8|9

onde os smbolos 0 a 9 e + ou - sao os elementos terminais, enquanto que Lista e Digito


representam os nao-terminais. Convencionalmente, o primeiro nao-terminal representa o
axioma da gramatica. Expressoes exemplo: 1+1, 3-6+9, 1+2+3+4+5+6
1

Gramatica Livre de Contexto

2.2

An
alise Gramatical

A analise gramatical e feita atraves de derivacoes de cadeias a partir do axioma da


gramatica. Se um nao-terminal A possui uma producao A XYZ entao, uma arvore
gramatical pode ter um no rotulado de A com 3 filhos X, Y e Z da esquerda para a direita,
conforme a figura 2.1.

A
X

Figura 2.1: Representacao da arvore gramatical da producao AXYZ


Formalmente, segundo uma GLC, a arvore gramatical resultante apresenta as seguintes
propriedades:
A raiz e rotulada pelo smbolo de partida (axioma);
Cada folha e rotulada por um terminal ou por ;
Cada no interno e rotulado por um elemento nao-terminal;
Se A X1 X2 . . . Xn e uma producao entao, algum no interno da arvore sera rotulado
por A sendo X1 X2 . . . Xn os rotulos dos filhos desse no.
Ambiguidade
Uma gramatica pode ter mais de uma arvore gramatical gerando uma dada cadeia, neste
caso, ela e dita ser ambgua. Ambos os exemplos da figura 2.2 geram a sentenca 9-5+2.

Cadeia
Cadeia
Cadeia
9

+
Cadeia

Cadeia
Cadeia

Cadeia

Cadeia
5

Figura 2.2: Ambig


uidade Gramatical

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

1. Prova, atraves da construcao da arvore de derivacao, que os exemplos anteriores sao


validos para a gramatica de expressoes aritmeticas vista.
2. Considere a gramatica livre de contexto: S SS+ | SS | a
(a) Mostre que a cadeia aa+a pode ser gerada por esta gramatica.
(b) Construa a arvore gramatical para esta cadeia.
(c) Qual e a linguagem gerada por esta gramatica? Justifique sua resposta.
3. Quais sao as linguagens geradas pelas seguintes gramaticas?
(a) S 0S1 | 01
(b) S +SS | -SS | a
(c) S S(S)S |
(d) S aSbS | bSaS |
(e) S a | S+S | SS | S* | (S)
umeros romanos (1 a 10).
4. Construa uma gramatica livre de contexto para os n
5. Construa uma G.L.C. para as expressoes aritmeticas de inteiros e identificadores
com as quatro operacoes basicas (+, -, , /).
9

2.3

Caractersticas da linguagem P ASCALjr

1. Nao e caso sensitivo (A = a)


2. Suporta os tipos: integer, real, char, string e boolean
3. Comandos:
Atribuicao com operadores: :=, +=, -=, *=, /=, ++, - -
Entrada com o comando read( )
Sada com os comandos write( ) e writeln()
Condicional com o comando if - then - else
Repeticoes:
Pre teste com o comando while - do
Pos teste com o comando repeat - until
Contada com o comando for - to - do
Sub-rotinas atraves dos comando procedure e function.
Retorno de funcoes com o comando result
Nome de identificador de subrotinas inicia obrigatoriamente com
(ex.: Tela)
4. Constantes caracteres delimitados por ( ) e constantes strings por ( )
5. Operadores relacionais: =, >=, <=, >, <, <>
6. Operadores logicos and, or, xor (ou exclusivo), not
7. Operadores aritmeticos: +, -, *, /, ** (potenciacao)
8. Suporta operadores ternarios: Expr ? valor1 : valor2
9. Precedencia de operadores:
(a) =, +=, -=, *=, /=, ++, - -
(b) and, or, xor
(c) =, >=, <=, >, <, <>
(d) not
(e) +, -
(f) *, /
(g) **
(h) ( )
(i) -
10. Smbolos especiais: ,, :, ;, (, ), .
10

11. Bloco de comandos delimitados por begin e end


12. Comentario de linha com operador //
13. Comentario de bloco com os delimitadores { e }
14. Lista de Palavras Reservadas: var, const, while, do, for, read, write, writeln, if,
then, else, true, false, integer, real, char, string, boolean, result, procedure,
function,and,or,xor,not,to,repeat,until,program,downto
Exemplos de programas a serem reconhecidos pela linguagem P ASCALjr :
{
PILOTO.TXT
Exemplo completo de programa na linguagem PASCALjr
Desenvolvido por Rogerio Eduardo da Silva
Agosto, 2005
}
Program Piloto;
// declara
c~
oes de variaveis e constantes globais
var: integer cont; real Nota1, Nota2, Media_das_medias, med;
const: integer total = 10;
// Subrotina de preenchimento de tela
procedure _Tela()
begin
writeln("******** ENTRADA DE DADOS ***************");
writeln("Digite os valores da entrada:");
end;
// Calculo da media aritmetica entre duas notas
func real _Media(real a , b)
var: real media;
begin
media := (a+b)/2.0;
result := media;
end;
// Inicio do Programa Principal
begin
Media_das_medias := 0;
for cont=0 to total do
begin
_Tela();
read(Nota1, Nota2);
med := _Media(Nota1, Nota2);
Media_das_medias += med;
11

write("Media = ",med);
end;
write("Media Geral = ",Media_das_medias/total);
end.

2.3.1

Exerccios Propostos

Usando a linguagem P ASCALjr faca:


1. Um programa para calculo do fatorial de N.
2. Um programa para calculo de N-esimo termo da serie de Fibonacci

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

Figura 3.1: O papel do analisador lexico


Um analisador lexico classico pode ser entendido como um sistema de estados finitos e, portanto, utiliza-se um automato finito para sua implementacao. As principais
caractersticas desse automato:
O alfabeto de entrada sao os caracteres pertencentes ao arquivo fonte
Cada estado final reconhece uma classe especfica de tokens da linguagem fonte
denominado erro l
E
exico a qualquer evento (durante o processo de analise lexica)
que impossibilite a interpretacao de um token.
Uma lista de tokens e o resultado do processo de analise lexica, caso nenhum erro
lexico tenha sido encontrado.
13

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

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
significativamente lento.
Existem 3 alternativas de implementacao de analisadores lexicos (listados em ordem
crescente de complexidade de implementacao):
1. Usar ferramentas de construcao 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 implementacao de um scanner :
Buffer
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
entao, devolvidos ao fluxo de entrada para que possam ser lidos novamente na analise de
outro lexema posterior.
Assim sendo, um buffer de entrada que acumula varios caracteres e criado, conforme
a figura 3.2. O processo de analise lexica e realizado sobre este buffer. Os tokens que
foram reconhecidos sao eliminados do buffer 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: Buffer de entrada para um analisador lexico

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

A seguir, serao revisados alguns conceitos importantes da disciplina linguagens formais e


maquinas (LFM) para entao prosseguir na analise lexica.
Gram
atica
definida
Uma gramatica e um mecanismo gerador de sentencas de uma dada linguagem. E
pela quadrupla (VN , VT , P, S), onde: VN representa o conjunto de smbolos nao-terminais
da linguagem; VT representa o conjunto de smbolos terminais ou alfabeto; P e um conjunto de regras de producao e S e o axioma da gramatica (smbolo inicial).
As regras de producao sao definidas 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
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?

=
=
=
=
=

sentenca vazia (comprimento = 0);


representa uma selecao entre a sentenca a ou b;
conjunto de todas as sentencas de comprimento 0 sobre A;
fechamento positivos sobre A
representa que a expressao A ocorre zero ou uma vez.

Exemplos: Digito(Digito) = representa a descricao de n


umeros inteiros.
Letra(Letra|Digito) = representa a descricao de identificadores.

3.3.1

Exerccios Propostos

1. Defina expressoes regulares e sua respectiva gramatica regular para as seguintes


linguagens:
todas as palavras contendo a e/ou b.
todas as palavras contendo a e/ou b com sufixo aa.
todas as palavras contendo a e/ou b com aaa como sub-palavra.
todas as palavras contendo a e/ou b com exatamente dois b.
2. Para a gramatica G=({S,A,B},{0,1},P,S), indique a linguagem reconhecida.
16

P: S 0S | A
A A1 | B
B0|1|

3.4

Especificac
ao e Reconhecimento de Tokens

A especificacao de tokens e feita atraves de expressoes regulares e reconhecida atraves dos


reconhecedores de gramaticas regulares chamados de automatos finitos.
Exemplo:

< Numero >


< Frac Opc >
< Exp Opc >
< Digitos >
< Digito >

< Digitos >< Frac Opc >< Exp Opc >


. < Digitos >|
(E | e)(+ | | ) < Digitos >|
< Digito >< Digitos >|< Digito >
0 | 1 | 2 | ... | 9

esta gramatica e capaz de reconhecer n


umeros inteiros como 1, 100, 1234, etc. e tambem
n
umeros reais expressos ou nao por notacao exponencial como: 1.5, 10.34, 1.3e15, 1E+2;
porem, e incapaz de reconhecer n
umeros como 1., sem a parte fracionaria. A figura 3.3
reconhece esta gramatica, enquanto que a figura 3.4 reconhece identificadores simples1 .

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())

Figura 3.3: Automato finito de reconhecimento de n


umeros inteiros e reais
O reconhecimento de strings e apresentado na figura 3.5, onde caracteres validos
representa o alfabeto valido para strings, geralmente letras, n
umeros, espacos e sinais
ortograficos.
Exerccio: Criar um AFD capaz de reconhecer os tokens da linguagem P ASCALjr :
smbolos (Dois Pontos, Ponto e Vrgula, Vrgula, Abre e Fecha Parenteses, Atribuicao),
Operadores Relacionais e Aritmeticos, Constante Caracter e identificadores de sub-rotinas
1

Exceto identificadores de sub-rotinas

17

LETRA OU DGITO

INICIO

LETRA

OUTRO

* Retornar(ID, Obter_Token())

Figura 3.4: AFD de reconhecimento de identificadores simples

CARACTERES
VLIDOS

INICIO

"

"

Retornar(String,Obter_Token())

Figura 3.5: AFD de reconhecimento de strings

(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

<> Operador Relacional Diferente


media Identificador
= Operador Relacional de Igualdade
= Operador Relacional de Igualdade
10.5E-5 N
umero Real
/= Smbolo de Atribuicao
= Operador Relacional de Igualdade
= Operador Relacional de Igualdade
Teste Identificador
? Smbolo Interrogacao

3.4.1

Trabalho Pr
atico #1

Implementar um modulo (sub-rotina) analisador lexico para um prototipo de compilador


para a linguagem P ASCALjr vista em aula.
Caractersticas:
Do modulo scanner:
A sub-rotina retorna um token (classe e lexema) cada vez que for chamada.
Considera que o programa fonte para analise ja esta aberto.
Nao retorna nada quando atingir o fim de arquivo (flag de controle).
Implementa um AFD para o reconhecimento de tokens.
Do programa a ser criado:
Abre um arquivo fonte para analise.
Chama (sucessivas vezes) a rotina de scanner e exibe o valor do token.
Fecha o arquivo fonte ao final da compilacao.
Para o processo de compilacao caso um erro seja encontrado.
Exibe erros de compilacao (se ocorrerem) ou mensagem de sucesso.
Criterios de Avaliacao:
Implementacao usando linguagem C ou C++.
Entrega de fontes e executavel (em um arquivo zipado) via disquete/CD ou e-mail:
rsilva@joinville.udesc.br ou professor.rogerio@gmail.com
19

Grupo de 02 alunos (maximo).


Valor do trabalho: 10.0 (25% da nota pratica).
Data de Entrega: A Definir
Punicoes:
de 10% por cada analise incorreta.
de 20% do valor do trabalho por dia de atraso.
de 20% 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).
Prazo maximo para defesa e arguicao sobre o trabalho: 5 dias letivos apos entrega.
Punicoes:
de 25% para arguicao nao respondida ou respondida incorretamente. Obs.: A
arguicao e individual.
de 33% ponto por dia de atraso da defesa.

20

Captulo 4
An
alise Sint
atica
4.1

O Papel do Analisador Sint


atico

A analise sintatica constitui a segunda etapa de um tradutor. Sua funcao e verificar


se as construcoes usadas no programa estao gramaticalmente corretas. Normalmente, as
estruturas sintaticas validas sao especificadas atraves de uma gramatica livre de contexto.
Dada uma GLC e uma sentenca (programa fonte) s, o objetivo do parser e verificar
se s pertence a GLC, atraves da construcao de uma arvore de derivacao.
O processo de construcao dessa arvore pode ser feito de forma explcita (construindose o TDA) ou implcita, atraves de chamadas recursivas das rotinas que aplicam as regras
de producao da gramatica durante o reconhecimento.
Existem duas estrategias basicas: Descendente (Top-Down) e Ascendente (BottomUp). Na estrategia top-down constroi-se a arvore a partir da raiz em direcao `as folhas
(tokens), enquanto que na bottom-up, o processo e invertido e a construcao e realizada
partindo-se das folhas, agrupando-se os tokens ate que a raiz da arvore seja gerada.
A arvore gramatical e entao a sada para as proximas fases da compilacao.
Revis
ao sobre Gram
aticas Livre de Contexto (GLC)
Uma gramatica livre de contexto e qualquer gramatica da forma: A , onde A e um
smbolo nao-terminal e um elemento pertencente a (VN VT ) .
Exemplo de producoes de uma G.L.C.: S SS+ | SS | a.

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 > < Num >< Digito >|< Digito >
< Digito > 0 | 1 | 2 | . . . | 9

<Numero>
<Num>
<Num>

<Digito>

<Digito>

Figura 4.1: Exemplo de Arvore


Sintatica

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

Figura 4.2: Derivacao `a Esquerda e `a Direita


Exerccio: Para a gramatica G = ({S,A},{0,1},P,S) sendo P: S 0S | A A 1A | 1,
provar as seguintes sentencas: 0001, 01, 0011.
22

4.2

An
alise Sint
atica Ascendente - BOTTOM UP

A criacao da arvore gramatical e realizada no sentido folhas raiz, ou seja, geracao de


sentencas e feita atraves do processo de empilhar e reduzir. A ideia e reduzir a sentenca
original ate o axioma da gramatica atraves de sucessivas substituicoes por nao-terminais.
Exemplo: S aABe A Abc | b B d
Verificar se a sentenca abbcde pode ser reduzida pela gramatica:
abbcde

aAbcde

aAde

aABe

4.2.1

Algoritmo Empilhar-e-Reduzir

Este procedimento de analise sintatica ascendente consiste de dois passos:


1. Escolha de um candidato a reducao (handle);
2. Reducao do candidato pelo nao-terminal A `a esquerda da producao 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 producao e cuja
reducao ao nao-terminal do lado esquerdo da producao representa um passo ao longo do
percurso de uma derivacao.
denominado de poda do candidato ao processo de substitu-lo pelo nao-terminal
E
`a esquerda da regra de producao, obtendo desta forma, uma reducao na sentenca sob
analise.
Uma forma conveniente de implementar um analisador sintatico de empilhar e reduzir
e usar uma pilha para guardar os smbolos gramaticais. O analisador sintatico opera
empilhando zero ou mais smbolos ate que um candidato surja no topo da pilha. Uma
poda do candidato e entao feita. Repete-se este processo ate que no topo da pilha esteja
o axioma da gramatica ou um erro seja encontrado (nenhuma poda seja possvel).
Exemplo: E E + E | E E | E E | E/E | (E) | id.
Sentenca sob analise: id + id * id
23

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

O processo de analise preditiva (sem retrocesso) exige modificacoes na gramatica original


para analise:
eliminacao de recursao `a esquerda;
fatoracao `a esquerda das regras de producao;
os nao-terminais que apresentarem mais de uma regra de producao, tenham o primeiro terminal derivavel u
nico (capaz de identificar 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 producao B D | bD D d apresenta duas
alternativas de derivacao. A escolha e feita a partir do primeiro terminal para cada regra
(d ou b).
O conjunto de smbolos terminais que iniciam sentencas 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 definem o conjunto FIRST sao:
25

Se , entao e um elemento de FIRST.


Se a, sendo a um smbolo terminal, entao a pertence a FIRST.
Se X1 X2 . . . XN , sendo X1 X2 . . . XN elementos nao-terminais, entao FIRST()
= FIRST(X1 ). Se em FIRST(X1 ) constar o elemento , entao incluir FIRST(X2 )
em FIRST() e assim por diante.
Elimina
c
ao da Recurs
ao `
a Esquerda
possvel que um analisador gramatical descendente recursivo execute indefinidamente.
E
O problema ocorre em producoes recursivas `a esquerda, tais como: A A0 | 1.
Este tipo de producao gera uma arvore que cresce recursivamente `a esquerda ate que
um terminal 1 seja gerado `a esquerda da seq
uencia de 0s.
Para se evitar isso deve-se substituir o elemento causador da recursao `a esquerda, que e
do tipo A A | , onde , representam outras seq
uencias de terminais e nao-terminais
nao iniciadas por A .
Para eliminar a recursao `a esquerda deve-se reescrever essa producao, da seguinte
forma: A A0 e A0 A0 | . A figura 4.4 apresenta as arvors de derivacao para uma
sentenca qualquer da forma .

4.3.2

Exerccios Propostos

Para as gramaticas abaixo elimine sua recursao `a esquerda.


1. G=({S,A,B},{a,b},P,S) onde P: S Sa | Sb | A | B A Aa | a B bB | b
2. G=({S,A},{0,1,2},P,S) onde P: S S0 | S1 | A | 0 A S2
3. G=({S,A,B},{0,1},P,S) onde P: S SA | A A A0B | 0 B B1 |
4. G=({A},{0,1},P,A) onde P: A A0A | 1
Apresente a clausula First para as producoes das gramaticas abaixo:
1. G=({S,X,Y,Z},{0,1,2,3},P,S) onde P: S XY Z X OXO | 1 Y 2Y 2 |
3 Z 0Z1 |
2. G=({S,A,B,C},{a,b,c},P,S) onde P: S Sa | aA A aA | Bb B cB |
3. G=({S,X,Y,Z},{0,1},P,S) onde P: S XY Z
Z 01Z |

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'

Figura 4.4: Exemplos de Recursao `a Esquerda e `a Direita

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

Um reconhecedor preditivo descendente (orientado por tabela) compreende uma fita de


entrada, uma pilha e uma tabela de analise, conforme e mostrado na figura 4.5. A fita
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 (correspondendo 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 6= $, o analisador desempilha a e avanca o cabecote de leitura para o
proximo smbolo na fita 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 (ficando X no topo). Se M[X,a] for vazio isto e um erro sintatico.
Na implementacao de um analisador sintatico, a maior dificuldade esta na construcao
da tabela de analise. Para construir essa tabela, e necessario computar duas funcoes
associadas `a gramatica: FIRST e FOLLOW.
27

a + b $

Parser

X
Y
Z

Tabela de
Anlise
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 fim de sentenca, entao $
esta em FOLLOW(S);
2. Se existe producao do tipo A X, entao todos os terminais de FIRST(), fazem
parte de FOLLOW(X);
3. Se existe producao 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 0 , T, T 0 , F }, {, , , id}, P, E) para expressoes logicas:
E
E0
T
T0
F

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

Algoritmo para Construc


ao da Tabela de An
alise

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

Projeto de uma Gram


atica para um Analisador Sint
atico
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 sintatica ascendente LR?
porque e possvel ser elaborados reconhecedores para todas as GLC, sem restricao;
porque o metodo de analise LR e tao eficiente quanto os demais metodos de analise;
porque um analisador LR consegue encontrar um erro sintatico o mais cedo possvel
em uma analise da esquerda para a direita.
As gramaticas GLC para as quais e viavel a implementacao manual de reconhecedores ascendentes (devido `a complexidade de implementacao) apresentam as seguintes
restricoes:
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
restricoes estao associadas a estes:
dificuldades de analisar operadores com mais de um significado semantico (ex.: operador 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 definidos as relacoes de precedencia entre os operadores, 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 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

Projeto de uma Gram


atica para um Analisador Sint
atico
Preditivo Descendente

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

[ Declaracao de Variaveis e Constantes ]


[ Declaracao de Sub-Rotinas ]
<Programa Principal>
onde: [ ] indica secao opcional e <> indica secao 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
P ASCALjr :
Programa AreaDecl AreaSubRot Principal
Analisando a Sec
ao de Declarac
ao de Vari
aveis e Constantes
possvel a
Esta secao declara todas as variaveis e constantes utilizadas pelo programa. E
declaracao 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 Identificador ListaID
ListaID Virg Identificador ListaID |
AreaDeclConst prConst DoisPt DeclConsts
DeclConsts Tipo ListaIDConst PtVirg DeclConsts
DeclConsts Tipo ListaIDConst PtVirg DeclConsts |
ListaIDConst Identificador Atrib Valor ListaIDConst
ListaIDConst Virg Identificador Atrib Valor ListaIDConst |
Valor OpAritSubt Numeros | Numeros |
ConstChar | ConstString | prTrue | prFalse
Numeros NumeroInteiro | NumeroReal
Analisando a Sec
ao de Declarac
ao de Procedimentos e Fun
c
oes
A secao de declaracao de procedimentos e funcoes declara todas as sub-rotinas utilizadas
possvel a declaracao de varias areas de declaracao de sub-rotinas
pelo programa. E
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 Identificador ListaParam |
ListaParam Virg Tipo Identificador 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 definida
pela funcao main. Apesar da linguagem P ASCALjr nao permitir a passagem de
parametros para esta funcao, ainda sim utilizar-se-ao os parenteses ( ) na sintaxe do
comando meramente por uma questao didatica. Esta secao 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 Atribuic


ao
Pode ser realizado atraves de 7 diferentes operadores:
= atribuicao simples
+= atribuicao apos adicao (X+ = Y X = X + Y )
33

-= atribuicao apos subtracao (X = Y X = X Y )


*= atribuicao apos multiplicacao (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 Identificador
Identificador
Identificador
Identificador
Identificador
Identificador

SimbAtrib Expr |
SimbAtribSoma Expr |
SimbAtribSubt Expr |
SimbAtribMult Expr |
SimbAtribDivi Expr |
SimbIncr | Identificador SimbDecr

porem, temos problemas de fatoracao. Fatorando `a esquerda estas producoes temos:


Atrib Identificador Atrib
Atrib SimbAtrib Expr |
SimbAtribSoma Expr |
SimbAtribSubt Expr |
SimbAtribMult Expr |
SimbAtribDivi Expr |
SimbIncr | SimbDecr

Analisando o comando Condicional


O comando condicional (sem fatoracao) ficaria:
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 Repetic


ao
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 funcoes.
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 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:

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 |


Identificador | 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
sintatica 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 eliminacao 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 |
Identificador | NumeroInteiro | NumeroReal |
ConstCaracter | ConstString
Exerccio: Montar a arvore sintatica para a expressao: 2 (X 5.0) + 10/B.
Analisando Express
oes L
ogicas
Uma expressao logica e, na verdade, uma comparacao 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 expressoes 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 sintatica da expressao (A + 5 == B/C 2) && D || ! A >= B


2. Criar a clausula FIRST para as producoes da gramatica preditiva descendente como
forma de verificar sua implementacao.
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 Pr
atico #2

Implementar um modulo (sub-rotina) analisador sintatico descendente para um prototipo


de compilador para a linguagem P ASCALjr vista em aula.
Caractersticas:
Do modulo parser:
A sub-rotina retorna um flag indicando sucesso ou nao da analise sintatica.
Utiliza os tokens provenientes do modulo scanner (ja implementado) para simular a
lista lexica.
Implementa uma pilha de execucao (fsica ou por recursividade) para analise das
regras de producao da gramatica vista.
Do programa a ser criado:
Abre um arquivo fonte para analise.
38

Executa a analise da producao axioma da gramatica.


Fecha o arquivo fonte ao final da compilacao.
Para o processo de compilacao caso um erro seja encontrado.
Exibe erros de compilacao (se ocorrerem) ou mensagem de sucesso.
Criterios de Avaliacao:
Implementacao 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
Punicoes:
de 10% por erro sintatico 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 arguicao sobre o trabalho: 5 dias letivos apos entrega.
Punicoes:
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
An
alise Sem
antica
5.1

Traduc
ao Dirigida pela Sintaxe

A ideia e associar informacoes aos smbolos gramaticais que representam a construcao de


uma LLC; tais informacoes sao atributos dos smbolos segundo certas regras semanticas
associadas `as producoes da gramatica.
Existem duas notacoes para associar regras semanticas `as producoes: definicoes dirigidas pela sintaxe e esquemas de traducao.
Defini
c
oes Dirigidas pela Sintaxe
Uma definicao dirigida pela sintaxe e uma GLC na qual cada smbolo gramatical possui 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 filhos daquele no da arvore e dito ser herdado se foi obtido a partir da
computacao 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 producao do tipo A , temos associado a ela uma regra semantica da
forma b := f (c1 , c2 , . . . , cn ), onde f e uma funcao que:
Ou b e um atributo sintetizado de A e c1 , c2 , . . . , cn 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 c1 , c2 , . . . , cn sao atributos pertencentes aos smbolos da producao.
Numa definicao dirigida pela sintaxe, assume-se que os terminais tenham apenas atributos sintetizados visto que para os mesmos nao existem regras semanticas. Se, para todas
as producoes, so forem utilizados atributos sintetizados diz-se se tratar de uma definicao
S-atribuda.
Exemplo: Seja a gramatica abaixo responsavel pelo funcionamento de uma calculadora simples e tendo como u
nico atributo sintetizado o valor val responsavel pelo armazenamento de um n
umero inteiro, entao as regras semanticas (para determinacao do valor
resultante de uma expressao aritmetica) sao:
41

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

Figura 5.1: Exemplo de Arvore


Decorada para a Expressao 3*5+4
Atributos herdados sao convenientes para expressar a dependencia de uma construcao
de linguagem de programacao no contexto em que a mesma figurar. Por exemplo, podemos
usar um atributo herdado para controlar se um identificador aparece ao lado esquerdo ou
direito de um comando de atribuicao a fim de decidirmos se e necessario usar o endereco
ou o valor do mesmo.
Exemplo: Declaracao de variaveis: int x,y,z
Produc
ao
D TL
T int
T f loat
L L0 , id
L id

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

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 representacao para esta dependencia em um grafo de dependencia ficaria (conforme a
figura 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

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

Figura 5.3: Tipos Simples e Construtor de Tipos


O processo de verificacao de tipos consiste da aplicacao de regras semanticas `as
producoes da gramatica sob analise. Usualmente, um atributo sintetizado tipo e suficiente 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 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

{E.tipo = tipo simples}


{E.tipo = ProcurarTS(id).tipo}
{E.tipo = E1 .tipo}
{E.tipo = SistemaT ipos[E1 .tipo, op, E2 , tipo]}

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

Exerccio: Construir as tabelas de sistema de tipos para todos os operadores utilizados


na gramatica da linguagem P ASCALjr .
Operadores Aritmeticos: +, -, *, /, **
Operadores Relacionais: ==, !=, >, <, >=, <=
Smbolos Atribuicao: =, +=, -=, *=, /=
Operadores Logicos: &&, ||, & |
Operadores Unarios: - , ! , ++, - Op1
inteiro
inteiro
inteiro
inteiro
inteiro
real
real
real
real
real
caracter
caracter
caracter
caracter
caracter
cadeia
cadeia
cadeia
cadeia
cadeia
logico
logico
logico
logico
logico

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

Atributos dos Nomes dos Identificadores

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

O metodo mais simples de solucionar colisoes de espalhamento e colocar o registro na


proxima posicao disponvel no vetor.
OBS.: Deve-se trabalhar os dados no vetor da mesma forma que em uma lista circular,
ou seja, a proxima posicao apos o u
ltimo elemento e novamente o primeiro elemento.
Solucionando colis
oes atrav
es de encadeamento
Usa-se uma lista encadeada para armazenar os elementos conflitantes no local do conflito.
Cada posicao do vetor e o inicio de uma lista de elementos.
Sempre que um conflito na insercao de dados ocorrer, o novo elemento (conflitante) e
inserido em uma lista encadeada na posicao.

Media

X1

X2

Res

Cont
Figura 5.4: Hashing com Encadeamento

49

5.3

Projeto das Regras Sem


anticas

Os smbolos semanticos no prototipo P ASCALjr armazenarao as seguintes informacoes:


Lexema, Tipo, Endereco, Natureza (VAR ou CONST ), Valor (No caso de constantes).
O TDA (Hashing) e acessado atraves dos seguintes metodos:

CriaTS aloca a tabela de smbolos vazia

InsereTS insere um novo smbolo na tabela de smbolos

BuscaTS retorna as informacoes de um determinado smbolo na tabela

DestroiTS desaloca toda a tabela de smbolos

Os possveis erros semanticos sao:

1. Identificador Nao Declarado

2. Identificador de Variavel Esperado

3. Tipos Incompatveis

4. Identificador Duplicado

5. Retorno de Funcao Esperado

6. Uso Incorreto de Chamada de Sob-Rotina

As seguintes regras semanticas serao aplicadas `as regras da gramatica da linguagem


P ASCALjr . Obs.: Nao esquecer de considerar os grafos de dependencia entre os atributos
semanticos.
50

Programa AreaDecl AreaSubRot Principal


{ CriaTS(); EnderecoLivre=0; }
AreaDecl AreaDeclVar AreaDecl | AreaDeclConst AreaDecl |
{ Nenhuma Acao Semantica }
AreaDeclVar prVar DoisPt DeclVars
{ Nenhuma Acao Semantica }
DeclVars Tipo ListaID PtVirg DeclVars
{ ListaID.Acao = DeclV ar; ListaID.T ipo = Tipo.T ipo }
DeclVars Tipo ListaID PtVirg DeclVars
{ ListaID.Acao = DeclV ar; ListaID.T ipo = Tipo.T ipo }
DeclVars { Nenhuma Acao Semantica }
Tipo prInt { Tipo.T ipo = Inteiro }
Tipo prFloat { Tipo.T ipo = Real }
Tipo prChar { Tipo.T ipo = Caracter }
Tipo prString { Tipo.T ipo = Cadeia }
Tipo prBool { Tipo.T ipo = Logico }
ListaID Identificador ListaID
{ ListaID.Acao = ListaID.Acao; ListaID.T ipo = ListaID.T ipo;
if(ListaID.Acao==DeclVar){
if (!InsereT S(VAR, Identificador.Lexema, ListaID.T ipo, EnderecoLivre + +)
ERRO SEM 4; }
else Entrada=BuscaTS(Identificador.Lexema);
if(!Entrada) ERRO SEM 1 else if(Entrada.Natureza != VAR) ERRO SEM 2 }
ListaID1 Virg Identificador ListaID2
{ ListaID2 .Acao = ListaID1 .Acao; ListaID2 .T ipo = ListaID1 .T ipo;
if(ListaID1 .Acao==DeclVar){
if (!InsereT S(VAR, Identificador.Lexema, ListaID.T ipo, EnderecoLivre + +)
ERRO SEM 4; }
else Entrada=BuscaTS(Identificador.Lexema);
if(!Entrada) ERRO SEM 1 else if(Entrada.Natureza != VAR) ERRO SEM 2 }
ListaID { Nenhuma Acao Semantica }
AreaDeclConst prConst DoisPt DeclConsts
{ Nenhuma Acao Semantica }
DeclConsts Tipo ListaIDConst PtVirg DeclConsts
{ ListaIDConst.T ipo = Tipo.T ipo }
DeclConsts Tipo ListaIDConst PtVirg DeclConsts
{ ListaIDConst.T ipo = Tipo.T ipo }
DeclConsts { Nenhuma Acao Semantica }
ListaIDConst Identificador Atrib Valor ListaIDConst
{ ListaIDConst.T ipo = ListaIDConst.T ipo;
InsereT S(CONST, Identificador.Lexema, ListaIDConst.T ipo,
EnderecoLivre + +, Valor.V alor) }

51

ListaIDConst1 Virg Identificador Atrib Valor ListaIDConst2


{ ListaIDConst2 .T ipo = ListaIDConst1 .T ipo;
InsereT S(CONST, Identificador.Lexema, ListaIDConst1 .T ipo,
EnderecoLivre + +, Valor.V alor) }
ListaIDConst { Nenhuma Acao Semantica }
Valor OpAritSubt Numeros { Valor.V alor = Numeros.V alor }
Valor Numeros { Valor.V alor = Numeros.V alor }
Valor ConstCaracter { Valor.V alor = ConstCaracter.Lexema }
Valor ConstString { Valor.V alor = ConstString.Lexema }
Valor prTrue { Valor.V alor = true }
Valor prFalse { Valor.V alor = f alse }
Numeros NumeroInteiro { Numeros.V alor = NumeroInteiro.Lexema }
Numeros NumeroReal { Numeros.V alor = NumeroReal.Lexema }
BlocoCom AbreChaves ListaCom FechaChaves
{ Nenhuma Acao Semantica }
ListaCom ComSimp ListaCom { Nenhuma Acao Semantica }
ListaCom { Nenhuma Acao Semantica }
ListaCom PtVirg ComSimp ListaCom { Nenhuma Acao Semantica }

< ListaCom > { Nenhuma Acao Semantica }


< ComSimp >< Atrib > { Nenhuma Acao Semantica }
< ComSimp >< RepetPre > { Nenhuma Acao Semantica }
< ComSimp >< RepetPos >
{ Nenhuma Acao Semantica }
< ComSimp >< RepetCont >
{ Nenhuma Acao Semantica }
< ComSimp >< Entrada >
{ Nenhuma Acao Semantica }
< ComSimp >< Saida >
{ Nenhuma Acao Semantica }
< ComSimp >< Condic >
{ Nenhuma Acao Semantica }
< ComSimp >< BlocoCom >
{ Nenhuma Acao Semantica }
< ComSimp >
{ Nenhuma Acao Semantica }
< Atrib > Identificador < Atrib >
{Entrada = BuscaT S(Identificador);
if (!Entrada)ERRO SEM 1 else if (Entrada.N atureza! = V AR)ERRO SEM 2;
< Atrib > .T ipo = Entrada.T ipo;}

52

< Atrib > SimbAtrib < Expr >


{if (!SistemaT ipos(< Atrib > .T ipo, SimbAtrib, < Expr > .T ipo))ERRO SEM 3}
< Atrib > SimbAtribSoma < Expr >
{if (!SistemaT ipos(< Atrib > .T ipo, SimbAtribSoma, < Expr > .T ipo))ERRO SEM 3}
< Atrib > SimbAtribSubt < Expr >
{if (!SistemaT ipos(< Atrib > .T ipo, SimbAtribSubt, < Expr > .T ipo))ERRO SEM 3}
< Atrib > SimbAtribMult < Expr >
{if (!SistemaT ipos(< Atrib > .T ipo, SimbAtribMult, < Expr > .T ipo))ERRO SEM 3}
< Atrib > SimbAtribDivi < Expr >
{if (!SistemaT ipos(< Atrib > .T ipo, SimbAtribDivi, < Expr > .T ipo))ERRO SEM 3}
< Atrib > SimbIncr
{if (!SistemaT ipos(< Atrib > .T ipo, SimbIncr))ERRO SEM 3}
< Atrib > SimbDecr
{if (!SistemaT ipos(< Atrib > .T ipo, SimbDecr))ERRO SEM 3}
< Condic > prSe < Expr > prEntao < ComSimp >< Condic >
{if (< Expr > .T ipo! = Logico)ERRO SEM 3}
< Condic > prSenao < ComSimp >
{ Nenhuma Acao Semantica }
< Condic >
{ Nenhuma Acao Semantica }
< RepetPos > prRepita < ListaCom > prAte < Expr >
{if (! < Expr > .T ipo == Logico)ERRO SEM 3}
< RepetPre > prEnquanto < Expr > prFaca < CompSimp >
{if (! < Expr > .T ipo == Logico)ERRO SEM 3}
< RepetCont > prPara Identificador SimbAtribValor > prAte
< Expr > prFaca < RepetCont >< ComSimp >
{Entrada = BuscaT S(Identificador);
if (Entrada.T ipo! =< Valor > .T ipo || Entrada.T ipo! = Inteiro)ERRO SEM 3
elseif (< Expr > .tipo! = inteiro)ERRO SEM 3
< RepetCont > prPasso < Expr >
if (< Expr > .tipo! = inteiro)ERRO SEM 3
< RepetCont >
{ Nenhuma Acao Semantica }
< Valor > NumeroInteiro
{ < Valor > .T ipo = inteiro }
< Valor > Identificador
{ < Valor > .T ipo = BuscaT S(Identificador).T ipo }
< Entrada > prLeia AbrePar < ListaID > FechaPar
{ < ListaID > .Acao = Leia; }
< Saida > prImprima Abrepar < ListaExpr > FechaPar
{ Nenhuma Acao Semantica }

53

< ListaExpr >< Expr >< ListaExpr >


{ Nenhuma Acao Semantica }
< ListaExpr > Virg < Expr >< ListaExpr >
{ Nenhuma Acao Semantica }
< ListaExpr >
{ Nenhuma Acao Semantica }
< Expr >< TermoLog >< Expr >< Ternario >
{< Expr > .in =< TermoLog > .out; < Ternario > .in =< Expr > .out;
< Expr > .tipo =< Ternario > .out; }
< Ternario > Interrog < Expr >1 DoisPt < Expr >2
{if (< Ternario > .in ! = Logico)ERRO SEM 3 else
if (< Expr >1 .T ipo ! = < Expr >2 .T ipo) ERRO SEM 3 else
< Ternario > .out =< Expr >1 .T ipo}
< Ternario >
{< Ternario > .out =< Ternario > .in}
< Expr > OpLogAnd < TermoLog >< Expr >1
{< Expr >1 .in = SistemaT ipos[< Expr > .in, OpLogAnd, < TermoLog > .out];
< Expr > .out =< Expr >1 .out}
< Expr > OpLogOr < TermoLog >< Expr >
{< Expr >1 .in = SistemaT ipos[< Expr > .in, OpLogOr, < TermoLog > .out];
< Expr > .out =< Expr >1 .out}
< Expr > OpLogXor < TermoLog >< Expr >
{< Expr >1 .in = SistemaT ipos[< Expr > .in, OpLogXor, < TermoLog > .out];
< Expr > .out =< Expr >1 .out}
< Expr >
{< Expr > .out =< Expr > .in}
< TermoLog >< FatorLog >< TermoLog >
{< TermoLog > .in =< FatorLog > .out;
< TermoLog > .out =< TermoLog > .out; }
< TermoLog > OpRelacMaior < FatorLog >
{< TermoLog > .out = SistemaT ipos[< TermoLog > .in, OpRelacMaior,
< FatorLog > .out]; }
< TermoLog > OpRelacMenor < FatorLog >
{< TermoLog > .out = SistemaT ipos[< TermoLog > .in, OpRelacMenor,
< FatorLog > .out]; }
< TermoLog > OpRelacMenorIgual < FatorLog >
{< TermoLog > .out = SistemaT ipos[< TermoLog > .in, OpRelacMenorIgual,
< FatorLog > .out]; }
< TermoLog > OpRelacMaiorIgual < FatorLog >
{< TermoLog > .out = SistemaT ipos[< TermoLog > .in, OpRelacMaiorIgual,
< FatorLog > .out]; }

54

< TermoLog > OpRelacIgual < FatorLog >


{< TermoLog > .out = SistemaT ipos[< TermoLog > .in, OpRelacIgual,
< FatorLog > .out]; }
< TermoLog > OpRelacDifer < FatorLog >
{< TermoLog > .out = SistemaT ipos[< TermoLog > .in, OpRelacDifer,
< FatorLog > .out]; }
< TermoLog >
{< TermoLog > .out =< TermoLog > .in}
< FatorLog >< ExprAr >
{< FatorLog > .out =< ExprAr > .out}
< FatorLog > OpLogNeg < Expr >
{< FatorLog > .out = SistemaT ipos[< Expr > .out, OpLogNeg]; }
< FatorLog > prVerdadeiro
{< FatorLog > .out = V erdadeiro}
< FatorLog > prFalso
{< FatorLog > .out = F also}
< ExprAr >< TermoAr >< ExprAr >
{< ExprAr > .in =< TermoAr > .out;
< ExprAr > .out =< ExprAr > .out; }
< ExprAr > OpAdic < TermoAr >< ExprAr >1
{< ExprAr >1 .in = SistemaT ipos[< ExprAr > .in, OpAdic, < TermoAr > .out];
< ExprAr > .out =< ExprAr >1 .out; }
< ExprAr > OpSubt < TermoAr >< ExprAr >1
{< ExprAr >1 .in = SistemaT ipos[< ExprAr > .in, OpSubt, < TermoAr > .out];
< ExprAr > .out =< ExprAr >1 .out; }
< ExprAr >
{< ExprAr > .out =< ExprAr > .in}
< TermoAr >< FatorAr >< TermoAr >
{< TermoAr > .in =< FatorAr > .out;
< TermoAr > .out =< TermoAr > .out; }
< TermoAr > OpMult < FatorAr >< TermoAr >1
{< TermoAr >1 .in = SistemaT ipos[< TermoAr > .in, OpMult, < FatorAr > .out];
< TermoAr > .out =< TermoAr >1 .out; }
< TermoAr > OpDivi < FatorAr >< TermoAr >1
{< TermoAr >1 .in = SistemaT ipos[< TermoAr > .in, OpDivi, < FatorAr > .out];
< TermoAr > .out =< TermoAr >1 .out; }
< TermoAr >
{< TermoAr > .out =< TermoAr > .in}
< FatorAr >< ElementoAr >< FatorAr >1
{< FatorAr >1 .in =< ElementoAr > .out;
< FatorAr > .out =< FatorAr >1 .out; }

55

< FatorAr > OpPote < ElementoAr >< FatorAr >1


{< FatorAr >1 .in = SistemaT ipos[< FatorAr > .in, OpPote,
< ElementoAr > .out];
< FatorAr > .out =< FatorAr >1 .out; }
< FatorAr >
{< FatorAr > .out =< FatorAr > .in}
< ElementoAr > AbrePar < Expr > FechaPar
{< ElementoAr > .out =< Expr > .out; }
< ElementoAr > OpSubt < ExprAr >
{< ElementoAr > .out = SistemaT ipos[< ExprAr > .out, OpSubt]; }
< ElementoAr > Identificador
{< ElementoAr > .out = BuscaT S(Identificador).T ipo; }
< ElementoAr > NumeroInteiro
{< ElementoAr > .out = Inteiro}
< ElementoAr > NumeroReal
{< ElementoAr > .out = Real}
< ElementoAr > ConstCaracter
{< ElementoAr > .out = Caracter}
< ElementoAr > ConstString
{< ElementoAr > .out = Cadeia}

5.3.1

Trabalho Pr
atico #3

Implementar um modulo analisador semantico para um prototipo de compilador para a


linguagem P ASCALjr (simplificada) vista em aula.
Caractersticas:
Do modulo semantico:
Cada chamada a elementos nao-terminais processa as acoes semanticos necessarias
para cada producao da gramatica.
Utiliza a arvore gramatical criada no modulo parser.
Implementa uma tabela de smbolos (Hashing) a fim de armazenar os smbolos
reconhecidos durante a compilacao.
Do programa a ser criado:
Abre um arquivo fonte para analise.
Executa a analise semantica a partir da producao axioma da gramatica.
Fecha o arquivo fonte ao final da compilacao.
Para o processo de compilacao caso um erro seja encontrado.
Exibe erros de compilacao (se ocorrerem) ou mensagem de sucesso.
Criterios de Avaliacao:
56

Implementacao usando linguagem C ou C++.


Entrega de fontes e executavel (em um disquete identificado).
Grupo de 02 alunos (maximo).
Valor do trabalho: 10.0 (25% da nota pratica).
Data de Entrega: 08/11/2004.
Punicoes:
de 20% do valor do trabalho por dia de atraso.
de 20% por erro lexico nao analisado corretamente.
de 15% por erro sintatico nao analisado corretamente.
de 10% por erro semantico nao analisado corretamente.
de 20% 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).
Prazo maximo para defesa e arguicao sobre o trabalho: 7 dias letivos apos entrega.
Punicoes:
de 33% ponto por dia de atraso da defesa.
de 25% para arguicao nao respondida ou respondida incorretamente. Obs.: A
arguicao e individual.

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

Sao divididas em 3 categorias:


Representacoes graficas;
Notacao pos (ou pre) fixadas;
Codigo de tres-enderecos.

6.1.1

Representa
co
es Gr
aficas

uma forma condensada de arvore de derivacao na qual somente os operandos da linE


guagem aparecem como folhas; os operadores constituem nos interiores da arvore (figura
6.1).
Exerccio: Gerar as representacoes grafica para as expressoes abaixo:
59

=
a

+
*

*
c b 2

Figura 6.1: Exemplo de Representacao Grafica de Operadores para a=b*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

Dada uma certa expressao E1 q E2, onde E1 e E2 sao os operandos e q um operador, a


expressao pos-fixada e representada por E1 E2 q, enquanto que a representacao pre-fixada
por q E1 E2.
Exemplos:
Infixa
(a + b) c
a (b + c)
a+bc
a=bc+d

s-Fixada
Po
ab + c
abc +
abc +
abc d+ =

-Fixada
Pre
+ abc
a + bc
+a bc
= a + bcd

Exerccios: Gerar as notacoes pre e pos fixas 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 fixadas:
E E1 + T
ET
T T1 F
T F
F id

{E.cod = E1.cod T.cod +}


{E.cod = T.cod}
{T.cod = T 1.cod T.cod *}
{T.cod = F.cod}
{F.cod = id.nome}
60

6.1.3

C
odigo de Tr
es-Endere
cos

Cada instrucao faz referencia, no maximo, a tres variaveis (enderecos de memoria). As


instrucoes 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-enderecos pode ser implementado atraves de quadruplas (um operador, dois operandos e um resultado) ou triplas (um operador e dois operandos), conforme
os exemplos abaixo.
Exemplo: A = B*(-C+D)

(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)

Na representacao por triplas existentes apontadores para a propria estrutura, evitando


assim o uso de temporarios.
Esquema de Tradu
c
ao para um comando de atribuic
ao
Atrib ID=Expr
Expr Expr1 +Expr2

Expr Expr1 *Expr2

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

O atributo nome armazena o nome de uma variavel (ou temporario). O atributo


cod armazena o codigo fonte gerado para o comando. A funcao geracod gera um texto
correspondente a(s) instrucao(oes) fornecida(s) como parametro. A funcao 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 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 Expr1 || Expr2


Expr Expr1 && Expr2
Expr ! Expr1
Expr (Expr1 )
Expr ID1 opRel ID2

Expr true
Expr false

...
if A < B goto 103
T1=0
goto 104
T1=1
...

{Expr.nome = GeraT emp;


Geracod(Expr.nome = Expr1 .nome || Expr2 .nome)}
{Expr.nome = GeraT emp;
Geracod(Expr.nome = Expr1 .nome && Expr2 .nome)}
{Expr.nome = GeraT emp;
Geracod(Expr.nome = ! Expr1 .nome); }
{Expr.nome = Expr1 .nome; }
{Expr.nome = GeraT emp;
Geracod(if ID1 .nome opRel ID2 .nome goto P roxq + 3);
Geracod(Expr.nome =0); Geracod(goto P roxq + 2);
Geracod(Expr.nome =1); }
{Expr.nome = GeraT emp; Geracod(Expr.nome=1); }
{Expr.nome = GeraT emp; Geracod(Expr.nome=0); }

A variavel proxq indica o ndice da proxima quadrupla disponvel.


Exemplo: A < B || C < D && E < F
62

100:
101:
102:
103:
104:
105:
106:

if A < B goto 103


T1=0
goto 104
T1=1
if C < D goto 107
T2=0
goto 108

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

O valor final da expressao e sempre no u


ltimo temporario gerado (no exemplo T5). O
nome desse temporario e entao armazenado no nao-terminal expressao da gramatica.
Esquema de Tradu
c
ao 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); }
Representa
ca
o por Fluxo de Controle
Este metodo traduz expressoes logicas para um codigo formado por instrucoes if-goto.
Sao gerados rotulos true e false, que armazenam os enderecos de desvio de execucao caso
a avaliacao resulte em true ou false respectivamente. Por este motivo, e mais eficiente que
o metodo de avaliacao numerica.
Traducao de expressoes logicas por fluxo de controle
Expr Expr1 || Expr2
{Expr1 .true = Expr.true; Expr1 .f alse = GeraRotulo;
Expr2 .true = Expr.true; Expr2 .f alse = Expr.f alse;
Expr.cod = Expr1 .cod || Expr2 .cod; }
Expr Expr1 && Expr2 {Expr1 .true = GeraRotulo; Expr1 .f alse = Expr.f alse;
Expr2 .true = Expr.true; Expr2 .f alse = Expr.f alse;
Expr.cod = Expr1 .cod || Expr2 .cod; }
Expr ! Expr1
{Expr1 .true = Expr.f alse; Expr1 .f alse = Expr.true;
Expr.cod = Expr1 .cod; }
Expr (Expr1 )
{Expr1 .true = Expr.true; Expr1 .f alse = Expr.f alse;
Expr.cod = Expr1 .cod; }
Expr ID1 opRel ID2 {Expr.cod = Geracod(if ID1 .nome opRel ID2 .nome
goto Expr.true); Geracod(goto Expr.f alse); }
Expr true
{Expr.cod = Geracod(goto Expr.true); }
Expr false
{Expr.cod = Geracod(goto Expr.f alse); }
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 Tradu
c
ao para Comandos de Controle de Fluxo
S prSe Expr prEntao S1
S prSe Expr prEntao S1
prSenao S2

S prEnquanto Expr S1

6.2

{Expr.true = GeraRotulo; Expr.f alse = S.prox;


S1 .prox = S.prox; S.cod = Expr.cod || S1 .cod; }
{Expr.true = GeraRotulo; Expr.f alse = GeraRotulo;
S1 .prox = S.prox; S2 .prox = S.prox;
S.cod = Expr.cod || S1 .cod; ||
Geracod(goto S.prox); || S2 .cod; }
{S.inicio = GeraRotulo; Expr.true = GeraRotulo;
Expr.f alse = S.prox; S1 .prox = S.inicio;
S.cod = Expr.cod || S1 .cod; ||
Geracod(goto S.inicio); }

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

Backpatching para Express


oes L
ogicas
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 E1 || E2

E E1 && E2

E !E1
E (E1 )
E ID1 opRel ID2

E ID

{backpatching(E1 .ListaF alse, M.quad);


E.ListaT rue = merge(E1 .ListaT rue, E2 .ListaT rue);
E.ListaF alse = E2 .ListaF alse; }
{backpatching(E1 .ListaT rue, M.quad);
E.ListaT rue = E2 .ListaF alse;
E.ListaF alse = merge(E1 .ListaF alse, E2 .ListaF alse); }
{E.ListaT rue = E1 .ListaF alse; E.ListaF alse = E1 .ListaT rue; }
{E.ListaT rue = E1 .ListaT rue; E.ListaF alse = E1 .ListaF alse; }
{E.ListaT rue = makelist(P roxq);
E.ListaF alse = makelist(P roxq + 1);
Geracod(if ID1 .nome opRel ID2 .nome goto
);
Geracod(goto
); }
{E.ListaT rue = makelist(P roxq);
E.ListaF alse = makelist(P roxq + 1);
Geracod(if ID.nome goto
);
Geracod(goto
); }
{M.quad = P roxq; }

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

Figura 6.2: Backpatching para expressoes logicas


100:
101:
102:
103:
104:
105:

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

Backpatching para Comandos de Controle


Entendendo-se o esquema de traducao 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 S1
S Se E entao M1 S1 N
senao M2 S2
N
S Enquanto M1 E Faca
M2 S1
S {L}
SA
A ID = E
L L1 ; M S
LS
P S.

{backpatching(E.ListaT rue, M.quad);


S.prox = merge(E.ListaF alse, S1 .prox); }
{backpatching(E.ListaT rue, M1 .quad);
backpatching(E.ListaF alse, M2 .quad);
S.prox = merge(S1 .prox, merge(N.go, S2 .prox)); }
{N.go = makelist(P roxq); Geracod(goto
); }
{backpatching(S1 .prox, M1 .quad);
backpatching(E.ListaT rue, M2 .quad);
S.prox = E.ListaF alse; Geracod(goto M1 .quad); }
{S.prox = L.prox;
{S.prox = makelist(N U LL); }
Acoes semanticas ja vistas em aula
{backpatching(L1 .prox, M.quad); L.prox = S.prox; }
{L.prox = S.prox; }
{backpatching(S.prox, EOF ); }

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

Uma tecnica simples para melhorar localmente o codigo gerado e a otimiza


c
ao peephole,
que trabalha substituindo seq
uencias de instrucoes (peepholes) por outras mais eficientes.
As principais acoes da otimizacao peephole sao:
eliminacao de instrucoes redundantes
otimizacoes de fluxo de controle
simplificacoes algebricas
Elimina
c
ao de Instruco
es Redundantes
Uma seq
uencia de instrucoes do tipo a = b e b = a, pode ser substitudo somente por a
= b, pois o resultado logico e o mesmo.
Otimiza
c
ao de Fluxo de Controle
Os algoritmos de geracao de codigo intermediario freq
uentemente produzem: (1) desvios
para desvios, (2) desvios para desvios condicionais ou (3) desvios condicionais para desvios.
Uma otimizacao peephole pode remover tais instrucoes redundantes:
Exemplo 1:
67

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

O uso de grafos acclicos dirigidos (GAD) para se representar uma seq


uencia de instrucoes,
permite mais facilmente rearranjar a ordem das instrucoes a fim de reduzir o codigo objeto
final.
Exemplo: (a+b)-(e-(c+d))
68

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

Algoritmo para Construir o GAD de um bloco

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

Figura 7.1: Grafo Acclico Dirigido - GAD

7.2.2

Algoritmo para Ordenac


ao de um GAD

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

INIP inicializa a execucao do programa {T opoM emo = T opoDesvio = 0; T opoDados =


1; }
FIMP encerra a execucao do programa {}
Aloca
c
ao de Mem
oria
ALME m t Aloca m posicoes de memoria, todas do tipo t
{f or(i = T opoM emo; i < T opoM emo + m; i + +)M emo[i].tipo = t;
T opoM emo+ = m; }
DEAL m Desloca m posicoes de memoria
{T opoM emo = m; }
Comandos de E/S
ENTR Entrada de dados {gets(Dados[+ + T opoDados]); }
IMPR Impressao de valores {printf (%s00 , Dados[T opoDados ]); }
Comandos para Express
oes
CRCT k t Carrega uma constante do tipo t. {Dados[+ + T opoDados] = (k, t); }
CRVL n t Transporta o conte
udo do endereco de memoria n para a pilha de dados.
{Dados[+ + T opoDados] = M emo[n]; }
SOMA substitui os dois elementos mais ao topo da pilha por sua soma.
{Dados[T opoDados 1]+ = Dados[T opoDados ]; }
SUBT substitui os dois elementos mais ao topo da pilha por sua diferenca.
{Dados[T opoDados 1] = Dados[T opoDados ]; }
MULT substitui os dois elementos mais ao topo da pilha por seu produto.
{Dados[T opoDados 1] = Dados[T opoDados ]; }
DIVI substitui os dois elementos mais ao topo da pilha por seu quociente.
{Dados[T opoDados 1]/ = Dados[T opoDados ]; }
POTE substitui os dois elementos mais ao topo da pilha pela sua potencia.
{Dados[T opoDados 1] = pow(Dados[T opoDados 1], Dados[T opoDados ]); }
INVE inverte o sinal do elemento no topo da pilha.
{Dados[T opoDados] = 1; }
CONJ substitui os dois elementos mais ao topo da pilha por sua conjuncao (&&).
{Dados[T opoDados 1] = Dados[T opoDados 1] && Dados[T opoDados];
T opoDados ; }
DISJ substitui os dois elementos mais ao topo da pilha por sua disjuncao (||).
{Dados[T opoDados 1] = Dados[T opoDados 1] + Dados[T opoDados] == 1;
T opoDados ; }
73

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

Regras para Gerac


ao de C
odigo 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 Identificador ListaID


{ if (Acao == DeclV ar)ContIds + +;
else{Geracod(EN T REntrada.T ipo); Geracod(ARM Z Entrada.Endereco Entrada.T ipo); }
Gerar codigo para ListaID }
ListaID Virg Identificador ListaID
{ if (Acao == DeclV ar)ContIds + +;
else{Geracod(EN T REntrada.T ipo); Geracod(ARM Z Entrada.Endereco Entrada.T ipo); }
Gerar codigo para ListaID }
ListaID
{ if (Acao == DeclV ar)Geracod(ALM E 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 Identificador Atrib Valor ListaIDConst
{ Geracod(ALME 1 Identificador.tipo);
Gerar codigo para Valor
Geracod(ARMZ Identificador.Endereco); Gerar codigo para ListaIDConst }
ListaIDConst Virg Identificador Atrib Valor ListaIDConst
{ Geracod(ALME 1 Identificador.tipo);
Gerar codigo para Valor
Geracod(ARMZ Identificador.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 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

Comando Retorno PtVirg


{ Gerar codigo para Retorno }
Comando SubRot PtVirg
{ Gerar codigo para SubRot }
Atrib Identificador Atrib
{ Gerar codigo para Atrib; Geracod(ARMZ Identificador.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); Buffer = Gerar codigo para Atrib
Gerar codigo para Comando
Descarrega o buffer 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 Expr1 DoisPt Expr2
{ Quad1 = ProxQ; Geracod(DSVF); Gerar codigo para Expr1
Quad2 = ProxQ; Geracod(DSVI); Backpatching(Quad1, ProxQ)
Gerar codigo para Expr2 ; 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 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

Implementar um modulo analisador semantico para um prototipo de compilador para a


linguagem P ASCALjr (simplificada) vista em aula.
Caractersticas:
Do modulo gerador de codigo:
Cada chamada a elementos nao-terminais processa esquemas de traducao 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 instrucoes em linguagem HIPO na lista CODE da MaqHipo.
Fecha o arquivo fonte ao final da compilacao.
Para o processo de compilacao caso um erro seja encontrado.
Exibe erros de compilacao (se ocorrerem) ou mensagem de sucesso.
Inicia o processo de execucao da maquina objeto, segundo as regras definidas para
cada instrucao.
Para o processo de execucao ao atingir a instrucao FIMP (neste caso informa fim de
execucao) ou algum erro de execucao seja detectado (neste caso informa mensagem
de erro).
Criterios de Avaliacao:
Implementacao usando linguagem C ou C++.
Entrega de fontes e executavel (em um disquete identificado ou e-mail).
Grupo de 02 alunos (maximo).
Valor do trabalho: 10.0 (25% da nota pratica).
Data de Entrega: 30/06/2005.
Punicoes:
de 20% do valor do trabalho por dia de atraso.
de 1.75 por erro lexico nao analisado corretamente.
de 1.5 por erro sintatico 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

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