Sunteți pe pagina 1din 105

Compiladores

Ricardo Lus de Freitas

ndice

Conceitos Formais ................................................................................................. 1


1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9
1.10
1.11
1.12

Introduo....................................................................................................................................1
Conceitos Bsicos........................................................................................................................1
Gramtica ....................................................................................................................................4
Conceitos Auxiliares....................................................................................................................6
Forma Normal de Backus (BNF).................................................................................................7
Tipos de Gramtica......................................................................................................................8
Hierarquia de Chomsky ...............................................................................................................9
rvores de Derivao para GLC ...............................................................................................13
Relaes teis ...........................................................................................................................18
Gramtica Reduzida ..................................................................................................................19
Conceitos ...................................................................................................................................20
Autmatos .................................................................................................................................23

Especificao de uma Linguagem Simplificada de Programao .................. 29


2.1
2.2

Descrio BNF da Linguagem Simplificada .............................................................................30


Fundamentos da Programao em LPD.....................................................................................34
2.2.1 A Linguagem LPD.............................................................................................................34
2.2.2 Estrutura de um Programa em LPD...................................................................................34
2.2.3 Palavras Reservadas ..........................................................................................................35
2.2.4 Identificadores Definidos pelo Usurio .............................................................................35
2.2.5 Declaraes VAR ..............................................................................................................35
2.2.6 Introduo aos Tipos de Dados..........................................................................................36
2.2.7 Comando de Atribuio .....................................................................................................37
2.2.8 Procedimentos e Funes em LPD ....................................................................................37
2.2.9 Chamadas a Procedimentos e Funes ..............................................................................38
2.2.10
Declarao Global X Local ...........................................................................................39
2.2.11
Operaes ......................................................................................................................40
2.2.12
Decises e "Loop" .........................................................................................................43

Estrutura Geral dos Compiladores ................................................................... 46


3.1
3.2
3.3
3.4
3.5
3.6
3.7

Funcionamento Bsico de um Compilador................................................................................46


Analisador Lexical.....................................................................................................................46
Analisador Sinttico ..................................................................................................................47
Analisador Semntico................................................................................................................48
Gerao de Cdigo ....................................................................................................................49
Otimizao Global e Local ........................................................................................................49
Um Exemplo do Funcionamento de um Compilador ................................................................50

Anlise Lexical..................................................................................................... 52
4.1
4.2
4.3
4.4
4.5

Temas da Anlise Lexical..........................................................................................................52


Tokens, Padres, Lexemas.........................................................................................................53
Atributos para os Tokens ...........................................................................................................54
Erros Lxicos.............................................................................................................................55
Anlise Lexical no CSD ............................................................................................................56

Tabela de Smbolos.............................................................................................. 62
5.1
5.2
5.3
5.4
5.5

Entradas na Tabela de Smbolos................................................................................................62


Tabela de Smbolos como rvore Invertida ..........................................................................63
Visibilidade dos Smbolos ....................................................................................................64
A Tabela de Smbolos Organizada como um Vetor ..................................................................65
Tabela de Smbolos no CSD......................................................................................................66

Anlise Sinttica .................................................................................................. 67


6.1
6.2
6.3
6.4
6.5
6.6

Analisador Semntico ......................................................................................... 82


7.1
7.2
7.3

Gramtica com Atributos...........................................................................................................82


Tabela de Smbolos ...................................................................................................................82
Erros Semnticos no CSD .........................................................................................................83

Mquina Virtual e Gerao de Cdigo ............................................................. 85


8.1
8.2
8.3
8.4
8.5
8.6
8.7

O Papel do Analisador Sinttico................................................................................................67


Anlise Sinttica Ascendente ....................................................................................................68
Anlise Sinttica Descendente...................................................................................................69
Tratamento dos Erros de Sintaxe...............................................................................................70
Estratgia de Recuperao de Erros ..........................................................................................73
Anlise Sinttica no CSD ..........................................................................................................74

Caractersticas Gerais da MVD .................................................................................................85


Avaliao de Expresses ...........................................................................................................86
Comandos de Atribuio ...........................................................................................................89
Comandos Condicionais e Iterativos .........................................................................................90
Comandos de Entrada e de Sada...............................................................................................94
Sub-Programas ..........................................................................................................................95
Procedimentos sem Parmetros .................................................................................................97

Bibliografia ........................................................................................................ 102

Ricardo Lus de Freitas

Notas de Aula - Compiladores 1

1 Conceitos Formais
1.1 Introduo
Como resultado de pesquisas em mais de 40 anos, as linguagens de programao tm
avanado constantemente, o que tem gerado boas perspectivas aos programadores. Seja com
as tcnicas tais como a programao estruturada, orientada a objetos, ou mesmo com a
utilizao de ferramentas CASE (Computer-Aided Software Engineering).
Nestes anos, a grande dificuldade concentra na polarizao entre padronizar ou no
as linguagens de programao. A no padronizao permite a incorporao de novas
tendncias, tais como as linguagens paralelas. Ambientes de programao (Ex. Turbo
Pascal) acrescentam um grande nmero de novas junes, fazendo com isso que cada
verso fornea novas possibilidades aos programadores. Porm, isso torna o cdigo cada
vez mais dependente da plataforma onde ele executado.
O propsito bsico das linguagens de programao gerar instrues para o
computador executar o processamento propriamente dito. Existe a necessidade de facilitar a
comunicao entre o homem e a mquina visto que os computadores operam num nvel
atmico) em linguagem de mquina (dgitos binrios, registradores, posies de memria,
endereo etc.), enquanto as pessoas preferem expressar-se usando linguagens naturais, mais
prximas da nossa.
Atravs das linguagens de programao, consegue-se :

Facilidades para se escrever programas;


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

Para diminuir as diferenas entre a linguagem de mquina e a linguagem natural que


surgiu a linguagem de programao.

1.2 Conceitos Bsicos


1) Alfabeto ou vocabulrio.
Conjunto finito no vazio de smbolos.
Ex. {a, b} {1, 2, 3} {while, for, do}
N={0, 1, 2, 3, 4, 5, 6, 7, 8, 9} V={1, +, ), a}
2) Smbolos (tomos, letras)
Elementos do alfabeto.
1, 7, 2 so smbolos de N
a, + so smbolos de V

Notas de Aula - Compiladores 2

Ricardo Lus de Freitas

3) Cadeias (palavras, sentenas)


Qualquer concatenao finita de smbolos de um alfabeto.
Cadeia de N = 1234
Cadeia de V = +1
Exerccio
V1 = {a, b}
Escreva 3 cadeias diferentes para V1 contendo 3 smbolos cada uma.
Cadeia 1 =

Cadeia 2 =

Cadeia 3 =

Cadeia vazia ( a cadeia que contm elementos) = (ou )


4) Fechamento de um alfabeto
Seja V um vocabulrio (ou alfabeto), ento o fechamento V* o conjunto das cadeias
de qualquer comprimento sobre o vocbulo V. Chama-se V+ o fechamento positivo de V,
definida por :
V+ = V* - {}
Exerccio
Seja V = {0, 1}
V* =

V+ =

5) Concatenao de cadeias
Seja s = a1 a2 ... an
t = b1 b2 ... bn
(ai, bi V)

A concatenao st = a1a2 ... an b1b2 ... bn;


Para qualquer s em V*, tem-se s = s = s (elemento neutro);
Associatividade s1 (s2s3) = (s1 s2) s3;
Comutatividade s1 s2 s2 s1.

Dada uma cadeia no vazia


s = a1 a2 ... an (ai, V), o inteiro n o seu comprimento e ser denotado por |s|.
O comprimento || = zero, assim para s e t em V*, |st| = |s| + |t|;
Dados s em V* e a em V, denotamos |s|a o nmero de ocorrncias de smbolo a em s.
Outra vez se s e t forem cadeias em V* , temos |st|a = |s|a + |t|a

Notas de Aula - Compiladores 3

Ricardo Lus de Freitas

Exerccio
Seja {a,b,c}
|aaa| =
|aabbb|c =
|| =

|aab|b =
|ccc| =
|cccd|c =

6) Produto de dois alfabetos


V1 = {a,b} V2 = {1, 2, 3}
V1 x V2 = {a1, a2, a3, b1, b2, b3}
V1 x V2 V2 x V1
7) Exponenciao de alfabetos
V0 = ; V1 = V ; Vn = V n - 1 V ; V* = V0 V1 V2 V3 ... Vn ...
Exerccios.
a) V = {0,1}
V3 = V2 .V = V1 . V.V = V0.V.V.V
V0 =

V2 =

V1 =

V3 =

b) V = {x, y, z}
V2 =
9) Linguagem
Uma linguagem sobre V (ou simplesmente linguagem) um subconjunto de V*
Ex. Seja V = {a, b}. As quinze palavras mais curtas de V* so :
{, a, b, aa, ab, ba, bb, aaa, bbb, aab, bba, aba, bab, abb, baa}
L = {s V* / 0 |s| 3}
L = {s V* / s=anb, n 0} = {b, ab, aab, aaab, ... }
Exerccio
Seja V = {+} , z = +
Escreva as cadeias abaixo indicando o tamanho de cada uma delas
z=

z5 =

zz =

z0 =

z2 =

Escrever V* e V+

Notas de Aula - Compiladores 4

Ricardo Lus de Freitas

1.3 Gramtica
Dispositivo de gerao de sentenas que formam as linguagens.
Uma gramtica G = (Vn , Vt , P, S), onde :
Vn : Alfabeto finito conhecido como vocabulrio no terminal que usado pela
gramtica para definir construes auxiliares ou intermedirias na formao de sentenas.
Ex. bloco de comandos
Os smbolos de Vn so usados como nomes para categorias sintticas (verbo - Lngua
Portuguesa).
Representao usual : Elementos de Vn em letras maisculas.
Vt : Alfabeto terminal, que so os smbolos dos quais as sentenas da linguagem so
constitudas.
Os smbolos de Vt so aqueles que aparecem em programas (letras, dgitos, palavras
reservadas etc.).
Representao usual : Elementos de Vt em letras minsculas (incio, fim etc.).
Vt Vn = V

Vt Vn = vazio ()

Cadeias mistas { Vt Vn }* ou V* e so representadas por letras gregas.


P : o conjunto finito de regras de produo, que so todas as leis de formao
utilizadas pela gramtica para definir a linguagem.
Cada produo P tem a forma , onde uma cadeia contendo no mnimo um
no terminal ( V* Vn V* ) e uma cadeia contendo terminais e/ou no terminais (
V*).
A interpretao de que pode ser substitudo por sempre que ocorrer.
S : o elemento de Vn que d incio ao processo de gerao de sentenas, S o
smbolo inicial da gramtica.
Exerccio
P : AB
B1A
B0

a) V =
b) V* =
c) 1110 L(G) ?

Supondo a linguagem L(G) = {s/s {a, b, c}* e s = am b cn ,m 0, n 1}


i) a b L(G) ?

ii) a b b c L(G) ?

iii) Qual a menor cadeia ?

Ricardo Lus de Freitas

Notas de Aula - Compiladores 5

Supondo a gramtica G = (Vn , Vt , P, Sentena)


Vn = {SENTENA, SN, SV, ARTIGO, VERBO, SUBSTANTIVO, COMPLEMENTO}
Vt = {aluno, o, estudou, compiladores}

P : SENTENA SN SV
SN ARTIGO SUBSTANTIVO
SV VERBO COMPLEMENTO
COMPLEMENTO ARTIGO SUBSTANTIVO | SUBSTANTIVO
ARTIGO o
SUBSTANTIVO aluno | compiladores
VERBO estudou
Derivar : o aluno estudou compiladores

Notas de Aula - Compiladores 6

Ricardo Lus de Freitas

1.4 Conceitos Auxiliares


1) Smbolo inicial sentena da linguagem
aplicao de sucessivas regras

2) Uma cadeia gera diretamente () uma cadeia see () P e apenas uma


produo aplicada, substituindo por , segundo a gramtica G.

G

3) Supondo que 1 , 2 ... n so cadeias de V* e 1 2 , 2 3 , ... ,n-1 n , ento


G

diz-se que 1 n se podemos obter n de 1 pela aplicao de um nmero finito de


G

derivaes pertencente a P. Este processo denomina-se derivao pela aplicao zero ou


mais derivaes diretas.
4) Uma derivao no trivial corresponde a uma aplicao de no mnimo uma derivao
direta e denotada por +

.
*

5) Se estiver clara qual a gramtica envolvida, os smbolos , ,


*

podem ser substitudos por , , , respectivamente.


*

6) Uma cadeia {Vn Vt }* uma forma sentencial see S .


Obs : S o smbolo inicial da gramtica e pela definio tambm uma forma sentencial.
7) Uma forma sentencial uma sentena de G see V*t , ou seja, uma sentena uma
forma sentencial particular onde todos os seus elementos so terminais.
8) A linguagem gerada por G, denotada por L(G), o conjunto de todas as possveis
sentenas por ela gerada atravs de derivaes a partir do smbolo inicial S.
*

L(G) = { w/w V* e S w }
9) Duas gramticas G1 e G2 so equivalentes see L(G1) = L(G2), ou seja, see suas
linguagens so iguais.
Ex. Quais as linguagens geradas para as seguintes gramticas:
G1= ({S}, {a,b}, P, S)
P:

S aSb
S ab

S ab (menor cadeia)
S aSb aabb
S aSb aaSbb aaabbb

G2 = ({S}, {a,b}, P, S)
P:

S aAb
A ab||S

S aAb ab (menor cadeia)


S aAb aabb
S aAb aSb aaAbb aaabbb

Notas de Aula - Compiladores 7

Ricardo Lus de Freitas

L(G1) = L(G2) = {w/w {a,b}* e w = anbn,n>0}. Portanto G1 e G2 so equivalentes.

1.5 Forma Normal de Backus (BNF)


Outra maneira de representar as regras de produo .
1) substitudo por ::=
w s w ::= s
2) Os ns no terminais so palavras entre < >.
A notao BNF usada para definir gramticas com as caractersticas de que o lado
esquerdo de cada regra composta por um nico smbolo no terminal.
Ex. G ({S, M, N}, {x,y}, P, S)
P:

Sx
SM
M MN
Ny
M xy

BNF
G = ({<S>, <M>, <N>}, {x,y}, P, <S>)
<S> ::= x | <M>
<M> ::= <M> <N> | xy
<N> ::= y

Os smbolos <, >, ::= no fazem parte da linguagem


L(G) = ?

Exerccio
Escreva a gramtica para a seguinte linguagem.
L(G) = {w/w {00, 11}* e w = (0011)n , n 0}

Notas de Aula - Compiladores 8

Ricardo Lus de Freitas

1.6 Tipos de Gramtica


1) Gramtica do tipo 0 (Irrestritas)
So aquelas s quais nenhuma limitao imposta, exceto a existncia de pelo menos
1 smbolo no terminal do lado esquerdo das regras. Todo o universo das linguagens
gerado por gramticas deste tipo.
As produes so do tipo onde V* Vn V* e V* V = {Vn Vt}
2) Gramtica do tipo 1 (Sensveis ao Contexto)
Se nas regras de produo for imposta a restrio de nenhuma substituio aplicado
se causar a reduo no tamanho da cadeia gerada, cria-se a classe de gramticas sensveis ao
contexto.
As produes so do tipo onde V* Vn V* e V+ e || ||
Exemplo :
aAbcD abcDbcD

tipo 1

Abc abDbc tipo 1

AC A no tipo 1

3) Gramtica do tipo 2 (Livres de Contexto)


Apresentam uma restrio adicional de que as regras de produo tem apenas 1
elemento no terminal do lado esquerdo ou seja : , Vn e V+
Exemplo :
G = ({A,B,C}, {a,b,c}, P, A)
P:

A BC | a
Bb
Cc

L(G) = {a, b, c}

4) Gramtica do tipo 3 (Regulares)


Possui a restrio de que as produes substituem um no terminal por uma cadeia de
um terminal seguido (direita) (ou precedido (esquerda)), ou no, por um nico no terminal.
Produes do tipo : A B ; A ou A B ; A
Exemplo : G = (Vn , Vt , P, S)
P:

S aS
S bA
Ac

L(G) = { w/w {a, b, c}* e w = an bc, n 0 }

Notas de Aula - Compiladores 9

Ricardo Lus de Freitas

1.7 Hierarquia de Chomsky


Toda gramtica regular livre de contexto, toda livre de contexto sensvel ao
contexto e toda sensvel ao contexto irrestrita.
Tipo 0
Tipo1
Tipo2
Tipo3

Notas de Aula - Compiladores 10

Ricardo Lus de Freitas

Lista de Exerccios n 1
1) Representar a gramtica do exemplo de um sub-conjunto da lngua Portuguesa em BNF.
2) Escreva duas gramticas equivalentes cuja linguagem seja :
L(G) = {w/w {a, b}* e w = am bn , m 0 n 1}
3) Escreva uma gramtica para cada uma das linguagens abaixo :
a) L(G1 ) = {w/w {a, b, c}* e w = a bn c, n 0}
b) L(G2 ) = {w/w {0, 1}* e w = 0m 1n, m n 1}
4) Seja a gramtica abaixo :
<S> o smbolo inicial
<S> ::= <B>
<B> ::= <C> | <C> + <D>
<C> ::= <D> | <C> * <D> | *<D>
<D> ::= x | (<S>) | - <D>
a) Definir Vt e Vn
b) Gere 10 sentenas para esta gramtica.
5)
P

S aSBC | aBC
CB BC
aB ab
bB bb
bC bc
cC cc
Qual a L(G) ?

G = ({S,B,C},{a,b,c},P,S)

Notas de Aula - Compiladores 11

Ricardo Lus de Freitas

Lista de Exerccios n 2
1) Indique qual o tipo das seguintes gramticas :
a) G = ({M,N,O,P}, {x,y,z,w}, P, O)

P:

P OP
P zzz
O PNMx
O zw
Nz
Mz

P:

P OzP
PO
O PNMx
OzO zw
NM z

P:

c) G = ({M,N,O,P}, {x,y,z,w}, P, O)
P OP | y0
O PNMx | x
OyO zwxyO
NM zM

b) G = ({M,N,O,P}, {x,z,w}, P, O)

d) G = ({M,N,O,P}, {x,y,z,w}, P, O)
O Mx | Nw
Nz
My

P:

2) Construa as gramticas para as seguintes linguagens


a) L(G1) = {0n1m , m,n 0}
b) L(G2) = {(xyz)p , p 1}
c) L(G3) = {(011)n 0p , p 1, n p}
3) Determinar o tipo da gramtica e gerar 3 sentenas para G = ({A,B,S}, {a,b,c}, P, S)
S Aa
Ac
A Ba
B abc

P:

4) Seja A = {0,1} , x = 01 e y = 110


Escrever as cadeias xy, yx, xyx, x2, (xy)3 , (xx)2
5) Descrever as linguagens geradas pelas gramticas abaixo e classific-las segundo a
hierarquia de Chomsky. Defina tambm Vt , Vn . S o smbolo inicial.
a)
P:

c)
P:

SA
A A0
A 1A
A0 10
S S0
S A1
A 0A0
A1

b)
P:

S 1S
S 1A
A 0A
A0

Ricardo Lus de Freitas

Notas de Aula - Compiladores 12

6) Mostre a linguagem gerada pelas produes da GLC abaixo e construa uma GR


equivalente.
G = ({A, B, S}, {a, b}, P, S)

P:

S AB
A aA
A aBB
B Bb
Bb

Ricardo Lus de Freitas

Notas de Aula - Compiladores 13

1.8 rvores de Derivao para GLC


Seja a gramtica abaixo :
G = ({E}, {a, b, +, *, (, ) }, P, E)
P : E E * E | E + E | (E) | a | b
A sentena (a+b)*a pode ser obtida por vrias derivaes distintas, por exemplo :
1) E E*E (E) * E (E + E) * E (a + E) * E (a + b) * E (a + b) * a
2) E E * E E * a (E) * a (E+E) * a (E + b) * a (a + b) * a
rvore 1

rvore 2

E
E *
( E )

A mesma que 1

E + E
a

No existe na rvore de derivao a ordem em que foram feitas as substituies: 1 e 2


apresentam a mesma rvore.
Definio : Dada uma GLC G = (Vn , Vt , P, S), uma rvore dita rvore de derivao
para G se:
a) Todo n tem um rtulo, que um smbolo de V = {Vn Vt }
b) O rtulo da raiz o smbolo inicial
c) Todo n no folha tem como rtulo um smbolo Vn
d) Se um n rotulado A e seus ns descendentes imediatos da esquerda para a direita,
rotulados A1 , A2 , A3 ... Ak, ento A A1A2A3 ... Ak deve ser uma produo de P.
e) A construo da rvore termina quando todas as folhas forem smbolos Vt
Exerccio : Construir a rvore de derivao para a sentena ((a*b)+(a*a)) e para a+b*a,
usando a gramtica G acima definida.

Notas de Aula - Compiladores 14

Ricardo Lus de Freitas

Definio : Dada uma gramtica G = (Vn , Vt , P, S) ela dita ambgua see existir uma
cadeia x L(G) para a qual podem ser construdas duas rvores de derivao diferentes.
Seja a cadeia a+b*a
1) E E*E E+E*E a+E*E a+b*E a+b*a
E
E *

E + E
a

a
b

2) E E+E E+E*E a+E*E a+b*E a+b*a


E
E + E
a

E * E
b

Tem-se portanto diferentes rvores para a mesma sentena. Isso decorre da ambiguidade da
gramtica. A gramtica ambgua porque no tem precedncia de operadores.

Gramtica acima convertida para no ambgua (foi inserida a precedncia na derivao dos
operadores (* e +)).
P:

EE*T|T
TT+F|F
F a | b | (E)

Mesmo com G no ambgua, tem-se mais de uma derivao para a mesma


sentena, embora com a mesma rvore de derivao. Isto se deve ao fato de poder substituir
qualquer no terminal em qualquer ordem.
possvel escolher um determinado no terminal ou mais a esquerda ou mais a
direita.
Derivao esquerda : se por um passo no qual o no terminal mais a esquerda
e

Ricardo Lus de Freitas

Notas de Aula - Compiladores 15

substitudo, escrevemos . De maneira anloga podemos escrever , para a


Derivao direita.
Derivaes direita e esquerda so chamadas cannicas.
Exerccio : Faa as derivaes esquerda e direita usando a gramtica anterior para obter
a seguinte sentena ((a+b)+b)

Ricardo Lus de Freitas

Notas de Aula - Compiladores 16

Exerccio : Dada G = ({S}, {a, b}, P, S)


P : S aS | aSb | a
a) L(G) ?
b) rvores de derivao para a, aaab, aabb, abb
c) G ambgua ? Em caso afirmativo encontre G , tal que L(G) = L(G) e Gno seja
ambgua

Notas de Aula - Compiladores 17

Ricardo Lus de Freitas

Definio : Toda sentena de uma gramtica G tem pelo menos uma derivao esquerda e
pelo menos uma direita.
Para formas sentenciais, isto no necessariamente verdadeiro.
Ex. (E + b) * E
Definio : Uma gramtica G no ambgua see toda sentena de G tem uma nica
derivao direita e uma nica esquerda.
Ex. a + b * a
Exerccios Resolvidos:
1) Dizer se so ambguas as gramticas, bem como qual a linguagem gerada.
a) S aSaS |
S aSaS aa
S aSaS aaSaSaS aaaa
S aSaS aaSaSaS aaaSaSaSaS aaaaaa
L(G) = {w/w {a}* e w = (aa)n , n 0}
S

AMBGUA

a S

a S

a S

a S

a S

a S

b) S aSa |
S aSa aa
S aSa aaSaa aaaa
S aSa aaSaa aaaSaaa aaaaaa
L(G) = {w/w {a}* e w = (aa)n , n 0}
S

a S a

a S a

a S a

NO AMBGUA

Notas de Aula - Compiladores 18

Ricardo Lus de Freitas

2) Seja a rv7e de derivao para a gramtica G = (Vn , Vt , P, S)


S
A
S
c b

B
S

A
B

b c b
a) Qual a cadeia representada pela rvore ?
b) Vn = ? Vt = ?
c) bcbbcbb L(G) ?
d) Quais produes (regra de derivaes) pertencem a P ?
e) escreva uma derivao cannica para a rvore
f) No item e quais as sentenas e formas sentenciais ?
a) cbbbbcb
b) Vn = {S,A,B} Vt = {c,b}
c) S AB BSB bSB bcbB bcbbA bcbbSS bcbbcbS bcbbcbb
d) S AB | cb | b
A SS | BS
B b | bA
e) S AB SSB bSB bbB bbb
f) formas sentenciais {AB,SSB,bSB,bbB,bbb} / sentena {bbb}

1.9 Relaes teis


Dada uma gramtica G, estamos interessados no conjunto de todos os smbolos x que
podem aparecer no incio da cadeia derivvel de um no terminal A, ou seja, no conjunto
+

{x / A x }
Supondo que a gramtica G no contm produes da forma B e considerando a
relao p (primeiro smbolo sobre V) definido por :
Ap x see A x
Em outras palavras, a relao p indica todos os smbolos que aparecem no incio de
alguma cadeia derivvel diretamente de A.
(vrias derivaes)
+
fcil ver que o conjunto desejado dado por {x / Ap x}

Notas de Aula - Compiladores 19

Ricardo Lus de Freitas

S AB
Sa/b
Ac
Bd
p = {A,a,b}
p+ = {A,a,b,c}

EE+T/T
TT*F/F
F a / b / (E)

P:

p
p
E
T
F
a
b
(
)
+
*

E
V

T
V
V

F
V

T
V
V

F
V
V

a
V
V
V

b
V
V
V

(
V
V
V

p+
p+
E
T
F
a
b
(
)
+
*

E
V

1.10 Gramtica Reduzida


Restries
1) Uma produo da forma A A intil e sua eliminao no altera a linguagem gerada
pela gramtica. Imporemos que a gramtica no deve permitir derivaes da forma:
+

A A

Notas de Aula - Compiladores 20

Ricardo Lus de Freitas

2) Todo smbolo da gramtica deve ser til, isto , deve aparecer em alguma forma
sentencial e se for no terminal deve ser possvel derivar dele uma cadeia terminal.
*

2.1) S X para algum e


X terminal ou no
*

2.2) X w para algum w Vt*


X no terminal
3) A gramtica no deve conter produes da forma A

1.11 Conceitos
EXPRESSES REGULARES
Definio :
a) Um smbolo terminal qualquer uma expresso regular;
b) Se x e y so expresses regulares, a sua concatenao xy uma expresso regular;
c) Se x e y so expresses regulares, a sua unio x + y uma expresso regular;
d) O smbolo uma expresso regular.
e) Se x uma expresso regular, (x) influencia na ordem de avaliao da expresso.
Ex. ri = r . r . r . r ...(i vezes) uma expresso regular.
x* = {, x, xx, xxx, ...}
(01)* = {, 01, 0101, 010101, ...}
abc*d = {abd, abcd, abccd, ...}
(a + b)* = {, a, b, ab, aa, bb, aab, ...}
L(G) = {0n1, n 0} = {1, 01, 001, 0001, ...} = 0*1
L(G) = {0n1, n 1} = {01, 001, 0001, ...} = 00*1 = 0+1
(0 + 1)* 00 (0 + 1)* = {00, 100, 000, 1000, ...}
1) (1 + 10)* = {, 1, 10, 110, 1010, ...}
Cadeia vazia ou no contendo 0s e 1s, com o nmero de 1s maior ou igual ao nmero de 0s.
2) (0* 1* 2*) = {, 0, 1, 2, 01, 02, 12, 00, 11, 22, 012, ...}
Cadeia vazia ou no contendo 0s, 1s e 2s, em qualquer quantidade respeitando essa ordem.
3) (0+ 1+ 2+) = {012, 0112, 0012, 0122, 001122, ...}
Cadeia contendo 0s, 1s e 2s, sendo que contm pelo menos um elemento de qualquer um,
em qualquer quantidade, respeitando essa ordem.
RECURSIVIDADE DE PRODUO
Definio : N N| logo N *|
onde : e (Vn Vt)*
N Vn
Ex. S aS | b

Notas de Aula - Compiladores 21

Ricardo Lus de Freitas

FATORAO
N | = (|)
Ex. E T | +T | -T | E + T | E - T
E + T | E - T = {+T|-T}*
T | +T | -T = (|+|-)T = [+|-]T
=> [+|-]T{+T|-T}*
Cadeia Opcional []: indica que opcional (pode ocorrer ou no).
OCORRNCIAS ADJACENTES
{, , ...} {||}*

GRAFO SINTTICO
Expresses regulares

Grafo

N ( No terminal)

T (terminal)

1 | 2 | n

1
2
n
*

[]
n

n
{||}*

abnc, n 1

Notas de Aula - Compiladores 22

Ricardo Lus de Freitas

Desenvolvimento de um subconjunto do Pascal e seu grafo sinttico


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

identificador

bloco

<bloco> ::= [<etapa de declar. de var.>] [<etapa de declar. de subrotinas>] <comandos>


comandos
etapa de declar. de var

etapa de declar. de subrotinas

<etapa de declar. de var.> ::= var <declarao de vars> ; { <declararao de vars> ; }


var

declarao de vars

<declarao de variveis> ::= <lista de identificadores> : <tipo>


Lista de identificadores

tipo

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


Identificador
,
<tipo> ::= inteiro / booleano
inteiro
boleano
<comandos> ::= incio <comando> {; comando} fim
incio

comando
;

fim

Notas de Aula - Compiladores 23

Ricardo Lus de Freitas

1.12 Autmatos
Um autmato uma mquina que possui um dispositivo de leitura e vrios estados,
sendo que seu estado pode variar de acordo com o que for lido pelo dispositivo de leitura.
Existem diversas formas de representar os autmatos, porm trs formas se destacam
e sero usadas.
a) Diagrama de estado
b) Tabelas de transio
c) Regras de transio
Diagrama de estado
Estado inicial : a partir de onde se iniciam os reconhecimentos da
cadeia.

Estado : qualquer estado que faa parte do autmato (exceto o inicial e final).
Estado final : qualquer sentena da linguagem de aceitao representada pelo
autmato leva a um destes estados aps seu reconhecimento.
transio : arco que representa a mudana de um estado para outro dentro do
autmato.
Exemplo :
a
b

L(G) = {w/w {a,b}* e e=anbm , n 0, m 1}

Exerccio :
1) w = anbm , n 1 , m 1}
1

a
b

b
3

2) (a b)m m 1
a
1

b
2

3
a

Notas de Aula - Compiladores 24

Ricardo Lus de Freitas

Tabela de Transio (do exerccio 2)


Estado Anterior
1
1
2
2
3
3

Smbolo de Entrada
a
b
a
b
a
b

Estado Posterior
2
erro
2
3
erro
3

Regra de Transio
g(1,a) = 2
g(2,a) = 2
g(2,b) = 3
g(3,b) = 3
Tabela de Transio (do exerccio 3)
Estado Anterior
1
1
2
2
3
3

Smbolo de Entrada
a
b
a
b
a
b

Regra de Transio
g(1,a) = 2
g(2,b) = 3
g(3,a) = 2
Fazer para o exerccio 1.
Exerccio Resolvido
Para o autmato a seguir :

a
b

b
c

c
C

a
a) Monte a tabela de transio correspondente.
b) Monte as regras de transio correspondente.

Estado Posterior
2
erro
erro
3
2
erro

Notas de Aula - Compiladores 25

Ricardo Lus de Freitas

c) Escreva 5 sentenas.
d) aabaaac aceita pelo autmato ?
a) Tabela de Transio
Estado Anterior
A
A
A
B
B
B
C
C
C

Smbolo de Entrada
a
b
c
a
b
c
a
b
c

Estado Posterior
A
B
erro
A
B
C
erro
erro
C

Regra de Transio
g(A,a) = A
g(A,b) = B
g(B,a) = A
g(B,b) = B
g(B,c) = C
g(C,c) = C
c) bc, bbc, abc, aabbc, abaabc
d) No porque sempre depois de um a, necessariamente precisa ter um b.
A verificao (reconhecimento) de cadeias utilizando autmatos consiste em partir de
um estado inicial, ler smbolos sequenciais, efetuando uma transio para cada smbolo lido.
Dizemos que uma cadeia de n smbolos K= K1K2K3 ... Kn , N 0 aceito, ou
reconhecido, por um autmato finito (AF) quando partindo-se do estado inicial desse AF,
forem lidos todos os smbolos de K e efetuadas as correspondentes transies de modo que
ao se ler Kn o AF para no estado final.
Exerccios Resolvidos
1) Dado
0
S0

1
1

S1

0
a) L(G) ? Cadeia no vazia terminado com 1
b) 00111 aceito ? Sim
00110 aceito ? No

a
S0

b
S1

a
Cadeias com bs e as e qq ordem
com n de as par
, b, baa, babba, babaaba

Notas de Aula - Compiladores 26

Ricardo Lus de Freitas

Definio Formal
AF uma quintupla ordenada = (S,so,F,A,g), onde:
S: conjunto de estados de um autmato
so: estado inicial, so S
F: conjunto de estados finais, F c S.
A: alfabeto de entrada
g: SxAS: aplicao da transio
Definir formalmente o autmato:

0
S0

1
1

S1

AF = ({S0,S1},S0,{S1},{0,1},g)
S : S0,S1
So : S0
F : S1
A : 0,1
g : g(S0,0) = S0
g(S0,1) = S1
g(S1,1) = S1

Notas de Aula - Compiladores 27

Ricardo Lus de Freitas

EXERCCIOS
1) Usando a seguinte gramtica responda :`
G=({E, V, N, O, T, D, L}, {0, 1, 2, 3, ...9, a, b, c, ...z, +, -, /, *, div, mod}, P, E)
P:

E V / N / VOE / NOE
V L / LT
N D / DN
T L / D / LT / DT
D 0 / 1 / 2 / ... / 9
L a / b / c / ... / z
O + / - / / / * / div / mod

a) G ambgua ?
b) Mostre a cadeia de derivao para a cadeia a + 1 div 4
c) Mostre as derivaes esquerda e direita para a cadeia do item b
d) A cadeia 0 a mod b aceita ?
e) No caso de G ser ambgua verifique, se possvel apresentar uma gramtica G1
equivalente a G que no seja ambgua
2) Para a rvore de derivao a seguir responda :
S
a

A
c

B
S

e S

a B A
d

a B A
d

a) Qual a sentena representada na rvore ?


b) Quais smbolos so Vn e Vt ?
c) Quais produes pertencem a P ?
d) A cadeia adbacb L(G) ?
e) Escreva uma direo cannica para a rvore
3) Para as regras de produo obtidas na questo 2, elabore a matriz que represente os
conjuntos p , p+ , p*
4) Usando a gramtica da questo 1, elabore os grafos (diagramas) sintticos para cada
elemento no terminal

Ricardo Lus de Freitas

Notas de Aula - Compiladores 28

5) Construir AF que reconhea as sentenas da linguagem L = {a bn c, n 0} e represente-o


formalmente
6) Seja o AF = ({q0, q1, q2, q3}, q0, {q0}, {0,1}, g)
g: g(qo,0) = q2
g(qo,1) = q1
g(q1,0) = q3
g(q1,1) = q0
g(q2,0) = q0
g(q2,1) = q3
g(q3,0) = q1
g(q3,1) = q2
a) Elabore o diagrama correspondente.
b) Elabore a tabela de transio.
c) Quais so as cadeias aceitas ?

Ricardo Lus de Freitas

Notas de Aula - Compiladores 29

2 Especificao de uma Linguagem


Simplificada de Programao
Como j visto, consideramos linguagem uma determinada coleo de cadeias de
smbolos de comprimentos finito. Estas cadeias so chamadas sentenas da linguagem e so
formadas pela justaposio de elementos individuais, que so smbolos, tomos ou tokens
da linguagem.
Podemos representar uma linguagem de trs formas:
Enumerao de todas as cadeias e smbolos possveis que formam as sentenas;
Regras de formao das cadeias reunidas em um conjunto de regras chamado gramtica;
Reconhecedor que atua atravs de regras de aceitao de cadeias .
A forma efetivamente utilizada a ltima citada. As cadeias so reconhecidas como
smbolos substituveis, denominados no-terminais, e smbolos no substituveis,
denominados terminais.
Antes de se implementar uma linguagem deve-se procurar defini-la da maneira mais
precisa possvel. Em primeiro lugar devem ser especificadas todas as seqncias de
smbolos que constituiro programas vlidos para uma linguagem. Somente estas
seqncias sero aceitas pelo compilador. Assim, tem-se especificada a sintaxe da
linguagem. A segunda fase consiste na definio do significado associado a cada construo
da linguagem, que compem sua semntica. As regras que determinam a semntica de uma
linguagem so muito mais difceis de serem determinadas se comparadas as regras
sintticas.
Devido a dificuldade de se definir regras semnticas, uma vez que no encontrado
um padro que especifique as semnticas de linguagens, a construo de compiladores
corretos bastante dificultada. Cada linguagem tem uma particularidade, o que exige um
grau de significao diferenciado, interferindo diretamente na semntica da linguagem.
Os principais objetivos a serem considerados no projeto de compiladores so:
Produzir um cdigo objeto eficiente;
Produzir cdigos objetos pequenos;
Minimizar o tempo necessrio para compilar programas;
Manter o compilador to pequeno quanto possvel;
Produzir um compilador com boa capacidade de diagnstico e recuperao de erros;
Produzir um compilador confivel.
Como fcil perceber os objetivos acima muitas vezes so conflitantes. Por
exemplo, um compilador ser mais lento e maior se o objetivo principal for um cdigo mais
eficiente, e vice-versa. Dessa forma, torna-se necessrio definir qual dos objetivos o
principal e balizar o desenvolvimento do compilador nesse objetivo, porm sem perder de
vista os restantes.
Outra deciso que deve ser tomada quanto ao nmero de passagens que sero
executadas pelo compilador. considerada uma passagem cada vez que o compilador
percorre o texto fonte. Do ponto de vista de simplicidade, os compiladores de uma nica
passagem so atraentes. No entanto, nem todas as linguagens permitem uma compilao
deste tipo. Como compilador de uma passagem entende-se aqueles que necessitam percorrer
o texto do cdigo fonte apenas uma nica vez para obter a compilao. J os de vrias
passagens necessitam percorrer o texto vrias vezes, sendo feita parcela da compilao a

Ricardo Lus de Freitas

Notas de Aula - Compiladores 30

cada passagem. Como exemplo tem-se que na primeira passagem seria feita a anlise
Lexical, na segunda a sinttica e assim por diante.
Embora existam diferentes notaes para se especificar uma linguagem, a Forma
Normal de Backus (BNF), tem se destacado devido a sua grande facilidade de compreenso.
A BNF uma metalinguagem, ou seja, uma linguagem usada para especificar outras
linguagens. A formalizao da sintaxe da linguagem feita atravs da especificao das
regras de produo, ou regras de substituio, onde cada smbolo da metalinguagem
associada uma ou mais cadeias de smbolos, indicando as diversas possibilidades de
substituio.

2.1 Descrio BNF da Linguagem Simplificada


A linguagem que ser definida representa uma linguagem de programao
estruturada semelhante linguagem de programao estruturada PASCAL. Esta linguagem
receber o nome de LPD (Linguagem de Programao Didtica). O compilador a ser
desenvolvido receber o nome de CSD (Compilador Simplificado Didtico). Os smbolos
no terminais de nossa linguagem sero mnemnicos colocados entre parnteses angulares
< e >, sendo os smbolos terminais colocados em negrito.
Descrio BNF da Linguagem Simplificada
<programa>::= programa <identificador> ; <bloco> .
<bloco>::= [<etapa de declarao de variveis>]
[<etapa de declarao de sub-rotinas>]
<comandos>
DECLARAES
<etapa de declarao de variveis>::= var <declarao de variveis> ;
{<declarao de variveis>;}
<declarao de variveis>::= <identificador> {, <identificador>} : <tipo>
<tipo> ::= (inteiro | booleano)
<etapa de declarao de sub-rotinas> ::= (<declarao de procedimento>;|
<declarao de funo>;)
{<declarao de procedimento>;|
<declarao de funo>;}
<declarao de procedimento> ::= procedimento <identificador>;
<bloco>
<declarao de funo> ::= funcao <identificador>: <tipo>;
<bloco>

Ricardo Lus de Freitas

Notas de Aula - Compiladores 31

COMANDOS
<comandos>::= inicio
<comando>{;<comando>}[;]
fim
<comando>::= (<atribuio_chprocedimento>|
<comando condicional> |
<comando enquanto> |
<comando leitura> |
<comando escrita> |
<comandos>)
<atribuio_chprocedimento>::= (<comando atribuicao>|
<chamada de procedimento>)
<comando atribuicao>::= <identificador> := <expresso>
<chamada de procedimento>::= <identificador>
<comando condicional>::= se <expresso>
entao <comando>
[senao <comando>]
<comando enquanto> ::= enquanto <expresso> faca <comando>
<comando leitura> ::= leia ( <identificador> )
<comando escrita> ::= escreva ( <identificador> )
EXPRESSES
<expresso>::= <expresso simples> [<operador relacional><expresso simples>]
<operador relacional>::= (!= | = | < | <= | > | >=)
<expresso simples> ::= [ + | - ] <termo> {( + | - | ou) <termo> }
<termo>::= <fator> {(* | div | e) <fator>}
<fator> ::= (<varivel> |
<nmero> |
<chamada de funo> |
(<expresso>) | verdadeiro | falso |
nao <fator>)
<varivel> ::= <identificador>
<chamada de funo> ::= <identificador >

Ricardo Lus de Freitas

Notas de Aula - Compiladores 32

NMEROS E IDENTIFICADORES
<identificador> ::= <letra> {<letra> | <dgito> | _ }
<nmero> ::= <dgito> {<dgito>}
<dgito> ::= (0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9)
<letra> ::= (a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|
A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z)
COMENTRIOS
Uma vez que os comentrios servem apenas como documentao do cdigo fonte, ao
realizar a compilao deste cdigo faz-se necessrio eliminar todo o contedo entre seus
delimitadores.
delimitadores :

{ }

Ricardo Lus de Freitas

Exerccios
1) Criar o diagrama sinttico para a linguagem definida.
2) Represente a rvore de derivao para o seguinte programa
programa test;
var v: inteiro;
i,max, juro,: inteiro;
incio
enquanto v != -1
faca
incio
{leia o valor inicial}
leia(v);
{leia a taxa de juros }
leia(juro);
{ Leia o periodo };
leia(max);
valor:= 1;
i:= 1;
enquanto i <= max
{ (1+juro) elevado a n }
faca incio
valor:= valor*(1+juro);
i:= i+1
fim;
escreva(valor)
fim
fim.

Notas de Aula - Compiladores 33

Ricardo Lus de Freitas

Notas de Aula - Compiladores 34

2.2 Fundamentos da Programao em LPD


2.2.1

A Linguagem LPD

Por ser bem prxima da linguagem PASCAL, a linguagem LPD possui muitas de
suas caractersticas:
um a linguagem altamente estruturada.
Necessidades rigorosas na definio de variveis e procedimentos. Voc deve
definir todas as variveis, procedimentos, e funes, antes de us-las.
Funes e procedimentos. Estas so estruturas auto-delimitadas, nas quais voc pode
organizar cada uma das diferentes sees do seu programa.
Variveis locais e globais. Voc pode declarar e usar variveis locais em qualquer
procedimento ou funo. Os valores de dados de variveis locais permanecem privados
para as rotinas que os usem. Voc pode tambm declarar variveis globais que
podem ser usadas em qualquer parte do programa.
Decises SE estruturadas. Elas permitem que voc construa complexas tomadas de
decises.
"Loop" estruturado. LPD fornece "loop" estruturado, no qual voc pode especificar a
condio sob a qual o "loop" dever continuar (ENQUANTO).
2.2.2

Estrutura de um Programa em LPD

Agora vamos examinar a estrutura de um programa em LPD, focalizando os


elementos individuais dessa estrutura, especificamente as declaraes VAR, e de
sub-rotinas (PROCEDIMENTO e FUNCAO), e a seo principal do programa.
Um programa em LPD consiste de diversos elementos estruturais, incluindo
os seguintes:
Um cabealho PROGRAMA, que fornece o nome do prprio programa. Este cabealho
deve ser includo em todos os programas, at nos mais triviais.
O comando VAR que declara todas as variveis globais usadas no programa. Esta
declarao opcional.
Procedimentos e funes, que contm as instrues para tarefas definidas
individualmente no programa.
Uma seo principal do programa, que controla as aes do mesmo atravs das
chamadas a procedimentos e funes includas no programa, ou atravs de comandos
executveis. Um esboo de um programa em LPD visto a seguir:
programa NomePrograma;
var {declarao de variaveis}
{declarao de rotinas}
incio {programa principal}
{comandos da seo principal}
fim. {programa principal}
As declaraes VAR, de procedimentos e funes so opcionais; as nicas
partes necessrias so o cabealho e a seo principal do programa. Consequentemente,
a forma mais simples de um programa em LPD tem um cabealho e um

Notas de Aula - Compiladores 35

Ricardo Lus de Freitas

bloco de comandos limitados por INCIO/FIM contendo um ou mais comandos


executveis.
programa NomePrograma;
incio
Comando;
Comando;
Comando;
fim.
2.2.3

Palavras Reservadas

As palavras que identificam as partes de um programa - por


PROGRAMA, VAR, PROCEDIMENTO, FUNCAO, INCIO, FIM - so
palavras reservadas. Todas palavras reservadas possuem significados fixos
linguagem, que no podem ser alterados. LPD possui as palavras reservadas
tabela abaixo.
E
SENAO
INTEGER
LEIA
ESCREVA

2.2.4

INCIO
FIM
NAO
ENTAO

BOOLEANO
FALSO
OU
VERDADEIRO

DIV
FUNCAO
PROCEDIMENTO
VAR

exemplo,
chamadas
dentro da
listadas na

FACA
SE
PROGRAMA
ENQUANTO

Identificadores Definidos pelo Usurio

Os identificadores na linguagem LPD podem ter comprimento de at 30


caracteres, onde todos os 30 so significativos. Os identificadores devem comear com
uma letra do alfabeto, seguida de qualquer nmero de dgitos ou letras. O caractere
sublinhado _, tambm legal dentro de um identificador.
2.2.5

Declaraes VAR

Variveis so identificadores que representam valores de dados num programa.


LPD necessita que todas as variveis sejam declaradas antes de serem usadas (a falha na
declarao de uma varivel resulta em um erro em tempo de compilao).
Voc pode criar tantas variveis quanto forem necessrias para satisfazer a
necessidade de dados dentro de um determinado programa, dentro do limite de
memria disponvel. Tipos simples de variveis so projetados para armazenar um nico
valor por vez; quando voc atribui um novo valor a uma varivel, o valor armazenado
previamente ser perdido. Existe ainda a estrutura vetor, que representa uma lista de
valores sob um nico nome.
Na seo VAR, voc especifica explicitamente o tipo de cada varivel que voc
cria. Aqui est o formato geral da seo VAR:
.
.
var
NomedaVarivel1 : Tipo1;
NomedaVarivel2 : Tipo2;
.
.

Ricardo Lus de Freitas

Notas de Aula - Compiladores 36

Alternativamente, voc poder usar o formato a seguir para declarar vrias variveis do
mesmo tipo:
.
.
var
NomedaVarivel1,
NomedaVarivel2,
NomedaVarivel3 : Tipo;
.
.
Por exemplo, a seo a seguir declara uma varivel do tipo inteiro e trs
variveis do tipo booleano.
.
var
Codigo_Erro : inteiro;
Sair,
ExisteLista,
Troca_Dados : booleano;
.
2.2.6 Introduo aos Tipos de Dados
Os programas em LPD podem trabalhar com dados expressos como valores literais
(tal como o nmero 6), ou como
valores representados simbolicamente por
identificadores (variveis). Sem considerar como o valor est expresso, voc deve tomar
cuidado para distinguir os tipos de dados que LPD reconhece. O uso de dados de
modo no apropriado resulta em erros em tempo de compilao.
LPD possui dois tipos de dados padro:
Tipo numrico inteiro.
Tipo booleano.

Tipo Inteiro
LPD oferece o tipo numrico inteiro INTEGER. Este tipo fornece uma ampla
faixa de valores de nmeros inteiros, que vai de -32768 at +32767.
Um inteiro no possui valor decimal, e sempre perfeitamente preciso dentro de
sua faixa.
Valores Booleanos
Um valor booleano pode ser tanto VERDADEIRO (verdadeiro), como FALSO
(falso). (Os valores booleanos so assim chamados em homenagem ao matemtico ingls
do sculo dezenove George Boole. Usaremos algumas vezes o termo alternativo valor
lgico.)
LPD fornece um conjunto de operaes lgicas e relacionais, que produzem
expresses que resultam em valores VERDADEIRO ou FALSO. Veremos estas operaes
mais tarde.
O identificador padro BOOLEANO define uma varivel desse tipo, como neste
exemplo:

Ricardo Lus de Freitas

Notas de Aula - Compiladores 37

.
.
var
Achei : Booleano;
.
.
Os valores lgicos constantes VERDADEIRO e FALSO so
identificadores padro na linguagem LPD, e tem significado fixo para esta.
2.2.7

tambm

Comando de Atribuio

Uma vez que voc tenha declarado uma varivel de qualquer tipo, poder utilizar
um comando de atribuio para armazenar um valor na mesma.
A sintaxe do comando de atribuio a seguinte:
NomedaVarivel := Expresso;
Para executar este comando, o CSD avalia a expresso localizada no lado
direito do sinal de atribuio e armazena o valor resultante na varivel esquerda. O
nome da varivel aparece sempre sozinho, no lado esquerdo do sinal de atribuio. A
expresso localizada do lado direito pode ser simples ou complexa. Considere estes
exemplos:
.
.
var
Achei : booleano;
Numero,
Linha,
Coluna,
Local_Erro : inteiro;
.
.
Achei := VERDADEIRO;
Numero := 5;
Local_Erro := Coluna * (Linha + 160);
.
.
O primeiro exemplo atribui o valor booleano VERDADEIRO varivel Achei, o
segundo atribui o nmero 5 varivel Numero, o terceiro atribui o resultado de uma
expresso aritmtica varivel Local_Erro.
2.2.8

Procedimentos e Funes em LPD

Como j vimos, LPD suporta dois tipos


de
blocos estruturados :
procedimentos e funes. Ns nos referimos a estes blocos genericamente como subprogramas, sub-rotinas, ou simplesmente rotinas. A estrutura de um procedimento
semelhante a de uma funo (na LPD). A diferena principal que a funo
explicitamente definida para retornar um valor de um tipo especificado.

Ricardo Lus de Freitas

Notas de Aula - Compiladores 38

Uma chamada a um procedimento ou funo representada num programa em


LPD por uma referncia ao nome da prpria rotina. Entretanto o contexto de uma
chamada depende da rotina ser procedimento ou funo:
A chamada de um procedimento independente como um comando completo e resulta
na execuo do procedimento.
A chamada de uma funo sempre parte de um comando maior. O nome da funo
representa o valor que a funo definitivamente retorna. Em outras palavras, o nome
de uma funo sempre um operando em um comando, nunca o prprio comando.
Aqui est o formato geral de um procedimento em LPD:
procedimento NomedaProcedimento;
var {declarao de variveis locais}
incio
{corpo do procedimento}
fim;
Nesta representao da sintaxe, NomedaProcedimento o identificador que
voc cria como nome do procedimento. A declarao VAR dentro do bloco do
procedimento define as variveis locais para a rotina.
O formato geral de uma funo similar, mas tem duas diferenas
importantes:
funcao NomedaFuno: TipodoResultado;
var {declarao de variveis locais}
incio
{corpo da funo}
NomedaFuno := ValordeRetorno;
fim;
NomedaFuno o nome de identificao da funo. Observe que o cabealho da funo
define o tipo da funo - ou seja, o tipo do valor que a funo retorna - com o identificador
TipodoResultado.
Igualmente, dentro de toda funo dever existir um comando de atribuio que
identifique o valor exato que a funo retornar. Esse comando possui a seguinte forma:
NomedaFuno := ValordeRetorno;
2.2.9

Chamadas a Procedimentos e Funes

Um comando de chamada a uma rotina direciona o CSD para execut-la. Uma


chamada tem a forma mostrada abaixo:
NomedaRotina
NomedaRotina faz referncia rotina definida dentro do programa. Por exemplo,
considere a chamada a seguir da procedimento Soma definida anteriormente:

Ricardo Lus de Freitas

Notas de Aula - Compiladores 39

.
.
var X,
Y: inteiro;
.
.
Soma;
.
.
Em contraste, a chamada a uma funo no independente como um comando, mas
ao invs disso, aparece como parte de um comando maior. Geralmente uma funo est
relacionada a uma seqncia de operaes que conduzem a um nico valor calculado. Por
exemplo, considere a funo Exp a seguir:
funcao Exp: inteiro;
var Contador,
Resultado,
Base, Expoente : inteiro;
incio
leia(Base);
leia(Expoente);
Contador := 1;
Resultado := 1;
enquanto Contador <= Expoente faca
incio
Resultado := Resultado * Base;
Contador := Contador + 1;
fim;
Exp := Resultado;
fim;
A funo est definida para retornar um valor do tipo INTEGER. O comando de
atribuio no fim da funo especifica que Resultado o valor de retorno.
Auxiliar := Exp (3,4) * 5;
A varivel que recebe o valor retornado pela funo, bem como o resto da
expresso, deve ser do mesmo tipo da funo. No exemplo acima, Auxiliar deve ser do
tipo INTEGER.
2.2.10 Declarao Global X Local
Ns j vimos que os procedimentos e funes podem ter sua prpria seo
VAR. A localizao de um comando de declarao determina o escopo dos
identificadores correspondentes:
Declaraes em nvel de programa (localizadas imediatamente abaixo do cabealho
PROGRAMA), definem identificadores globais, ou seja, variveis que podem ser
usadas em qualquer ponto do programa.

Ricardo Lus de Freitas

Notas de Aula - Compiladores 40

Declaraes em nvel de procedimentos e funes definem identificadores locais,


ou seja, variveis que s podem ser usadas dentro de uma rotina em particular,
sendo removidas da memria quando a execuo da rotina se completa.
Alm disso, um procedimento ou funo pode ter suas prprias definies de
procedimentos ou funes aninhadas. Uma rotina que esteja localizada inteiramente dentro
do bloco de definio de outra rotina, est aninhada dentro desta mesma rotina e local
para a rotina que a contm. Por exemplo, considere a rotina hipottica a seguir:
procedimento MaisExterna;
var {declarao de variveis para MaisExterna};
procedimento Dentro1;
{Dentro1 e local para MaisExterna}
var {declarao de variveis para Dentro1};
incio
{comandos de Dentro1}
fim;
procedimento Dentro2;
{Dentro2 tambm e local para MaisExterna}
var {declarao de variveis para Dentro2};
funcao Dentro3 : Booleano;
{Dentro3 e local para Dentro2}
var {declarao de variveis para Dentro3};
incio
{comandos de Dentro3}
fim;
incio
{comandos de Dentro2}
fim;
incio
{comandos de MaisExterna}
fim;
Neste exemplo, o procedimento MaisExterna possui dois procedimentos
locais: Dentro1 e Dentro2. Estes dois procedimentos s esto disponveis para
MaisExterna. Alm disso, o procedimento Dentro2 possui sua prpria funo local:
Dentro3. Dentro3 s est disponvel para Dentro2. Os procedimentos e funes locais
esto sempre localizados acima do bloco INCIO/FIM da rotina que as contm.
2.2.11 Operaes
A linguagem LPD oferece duas categorias de operaes, cada qual designada para
ser executada sobre um tipo de operando especfico. Estas categorias so as seguintes:

Notas de Aula - Compiladores 41

Ricardo Lus de Freitas

Operaes numricas, que trabalham com tipos inteiros.


Operaes relacionais e lgicas, que resultam em valores booleanos.
Agora veremos cada uma destas categorias de operaes.
Operaes Numricas
As quatro operaes numricas so
smbolos a seguir:
*
div
+
-

representados

na

linguagem LPD pelos

Multiplicao
Diviso
Adio
Subtrao

Estas so chamadas operaes binrias, por necessitarem sempre de dois operandos


numricos. Em contraste, o sinal de menos tambm pode ser utilizado para expressar um
nmero negativo; a negao uma operao unria. O operando DIV fornece o quociente
inteiro da diviso de dois nmeros; qualquer resto ser simplesmente descartado. Por
exemplo, considere a expresso a seguir:
a div b
Se a varivel a possuir o valor 42, neste exemplo, e b o valor 8, o resultado da
expresso ser 5. O resto da diviso, que 2, ser descartado. Em qualquer expresso
que contenha mais de uma operao, o CSD normalmente seguir a ordem de
precedncia na avaliao de cada operao. Aqui est a ordem, da mais alta para a mais
baixa precedncia:
1. Negao
2. Mutiplicao, Diviso
3. Soma, Subtrao
Onde existir mais de uma operao com o mesmo nvel de precedncia, as
operaes com mesmo nvel sero executadas da esquerda para direita. Por exemplo,
considere o fragmento de programa, no qual todos os operandos so do tipo INTEGER:
a := 5;
b := 4;
c := 3;
d := 2;
x := a * b + c div d;
Para atribuir um valor a x, o CSD ir avaliar primeiramente as expresses do lado
direito do sinal de atribuio, nesta ordem: a multiplicao, a diviso e, finalmente a
adio. A varivel x receber, assim, o valor 21.
Se voc desejar que o CSD avalie uma expresso numa ordem diferente do modo
padro, poder fornecer parnteses dentro da expresso. O CSD executa operaes dentro
de parnteses antes de outras operaes. Por exemplo, considere como a adio de
parnteses afeta o resultado da expresso anterior:

Ricardo Lus de Freitas

Notas de Aula - Compiladores 42

x := a * (b + c) div d;
Neste caso, a adio realizada primeiramente, depois a multiplicao e, por
ltimo, a diviso. Se as variveis tiverem os mesmos valores que antes, o novo resultado
ser 17.
Os parnteses podem estar aninhados dentro de uma expresso, ou seja, um
conjunto de parnteses pode aparecer completamente dentro de outro conjunto de
parnteses. O CSD executa a operao localizada dentro dos parnteses mais
internos primeiramente, e depois vai executando
as
operaes contidas nos
parnteses mais externos. Por exemplo, no comando a seguir, a adio executada
primeiramente, depois a diviso e finalmente, a multiplicao, dando um resultado igual a
15:
x := a * ((b + c) div d);
Numa expresso aninhada, cada "abre parnteses" deve ser correspondido por
um respectivo "fecha parnteses", ou ocorrer um erro em tempo de compilao.
Operaes Relacionais
Uma operao relacional compara dois itens de dados e fornece um valor booleano como
resultado da comparao. Aqui esto os seis operadores relacionais e os seus
significados:
= Igual
< Menor que
> Maior que
<= Menor ou igual a
>= Maior ou igual a
!= Diferente de
Voc pode usar estas operaes com dois valores do mesmo tipo. Por exemplo, digamos
que a varivel inteira Escolha contenha o valor 7. A primeira das expresses a seguir
fornece um valor falso, e a segunda um valor verdadeiro:
Escolha <= 5
Escolha > 4
Operaes Lgicas
A linguagem LPD possui trs operaes lgicas. Duas das quais - E, e OU - so
operaes binrias, usadas para combinar pares de valores lgicos em expresses lgicas
compostas. A terceira, NAO, uma operao unria que modifica um nico operando
lgico para o seu valor oposto. Fornecendo dois valores ou expresses lgicas,
representadas por Expresso1 e Expresso2, podemos descrever as trs operaes lgicas a
seguir:
Expresso1 E Expresso2 verdadeiro somente se ambas, Expresso1 e Expresso2,
forem verdadeiras. Se uma for falsa ou se ambas forem falsas, a operao E tambm
ser falsa.

Ricardo Lus de Freitas

Notas de Aula - Compiladores 43

Expresso1 OU Expresso2 verdadeiro se tanto Expresso1 como Expresso2 forem


verdadeiras. A operao OU s resulta em valores falsos se ambas, Expresso1 e
Expresso2, forem falsas.
NO Expresso1 avalia verdadeiro se Expresso1 for falsa; de modo contrrio, a
operao NAO resultar em falso, se Expresso1 for verdadeira.
Os operadores lgicos (E, OU, e NAO), tm uma ordem de precedncia mais
alta que os operadores relacionais (=, !=, <, >, <=, >=). Os parnteses so, portanto,
necessrios para forar o CSD a avaliar as expresses relacionais antes de avaliar a
expresso E.
2.2.12 Decises e "Loop"
Para controlar eventos complexos, um programa iterativo em LPD deve ser capaz
de tomar decises, escolher entre os cursos de aes disponveis, e executar aes
repetidamente. Todas estas atividades necessitam de avaliaes bem sucedidas de
condies lgicas, com o objetivo de decidir uma direo especfica na execuo atual
ou de determinar a durao de uma seqncia de repetio em particular. A linguagem
LPD fornece uma estrutura de controle para executar decises, e outra para executar
"loop"; essas estruturas so o nosso prximo assunto.
2.2.12.1 Estrutura de deciso SE
Uma estrutura de deciso SE seleciona um entre dois comandos (ou grupo de comandos),
para execuo. A estrutura consiste de uma clausula SE (se), em companhia de uma
clusula ENTAO (ento) e uma SENAO (seno):
se Condio
entao {comando ou grupo de comandos que
sero executados se a condio for VERDADEIRO}
senao {comando ou grupo de comandos que
sero executados se a condio for FALSO};
Durante uma execuo, o CSD comea por avaliar a condio da clusula SE. Esta
condio deve resultar num valor do tipo booleano, embora possa tomar uma variedade
de formas, ela pode ser uma expresso de qualquer combinao de operadores lgicos e
relacionais, ou simplesmente uma varivel do tipo BOOLEANO.
Se a condio for VERDADEIRO, o comando ou comandos localizados entre o
ENTAO e o SENAO so executados e a execuo pula a clusula SENAO.
Alternativamente, se a expresso for FALSO, a execuo desviada diretamente para o
comando, ou comandos, localizados aps a clusula SENAO.
As clusulas ENTAO e SENAO podem ser seguidas tanto de um comando simples
como de um comando composto. Um comando composto deve estar entre os
marcadores INCIO e FIM. A estrutura do comando SE usando comandos compostos
a seguinte:
se Condio
entao incio
{comandos da clusula ENTAO}
fim
senao incio
{comandos de clusula SENAO}

Ricardo Lus de Freitas

Notas de Aula - Compiladores 44

fim;
No cometa o erro de omitir os marcadores INCIO/FIM nos comandos
compostos de uma estrutura SE. Alm disso, no coloque o ponto e virgula entre o FIM e o
SENAO numa estrutura SE. O CSD interpretar esta marcao incorreta como o fim do
comando SE sendo que a clusula SENAO, a seguir, resultar num erro em tempo de
compilao.Tendo em vista que a clusula SENAO opcional, a forma mais simples da
estrutura SE a seguinte:
se Condio
entao {comando ou comandos que
sero executados se a condio for VERDADEIRO};
Neste caso o programa executa o comando ou comandos aps a clusula ENTAO
somente se a condio for verdadeira. Se ela for falsa, o comando SE no ter nenhuma
ao. As estruturas SE podem estar aninhadas. Em outras palavras, toda uma estrutura de
deciso, com as clusulas SE, ENTAO, e SENAO, pode aparecer dentro de uma clusula
ENTAO ou SENAO de uma outra estrutura. O aninhamento de estruturas SE pode
resultar em seqncias de decises complexas e poderosas. Como exemplo, considere o
fragmento de programa a seguir:
.
.
se N < 2
entao G := G + N
senao incio
H := G;
P (N - 1, H);
G := H;
P (N - 2, G);
fim;
.
.
Neste exemplo, a deciso ser baseada no valor de N. Se N for menor que 2, o
programa soma N ao valor de G e armazena em G. Se o valor de N for maior ou igual a 2, o
programa executa o comando composto localizado aps a clusula SENAO.
2.2.12.2 A Estrutura do "Loop" ENQUANTO
Aqui est a sintaxe do "loop" ENQUANTO:
enquanto Condio faca
Comando;
Se o bloco do "loop" possuir um comando composto, a forma geral a seguinte:
enquanto Condio faca
incio
{comandos que sero executados uma
vez a cada iterao do "loop"}
fim;

Ricardo Lus de Freitas

Notas de Aula - Compiladores 45

A condio uma expresso que o CSD pode avaliar como VERDADEIRO ou


FALSO. O CSD avalia a expresso antes de cada iterao do "loop". A repetio
continua enquanto a condio for VERDADEIRO. Em algum ponto, a ao dentro do
"loop" comuta a condio para FALSO, antes de uma nova iterao a expresso ser
avaliada novamente; como ela resultar em FALSO, o "loop" no ser executado. Como
exemplo, considere o fragmento de programa a seguir:
.
.
Contador := 0;
enquanto Contador <= 11 faca
incio
escreva (Contador);
Contador := Contador + 1;
fim;
A condio que controla este "loop" em particular o progresso da varivel
Contador dentro do prprio "loop". Antes de cada iterao, o programa testa o valor de
Contador para ver se menor que 11. Enquanto a condio for VERDADEIRO, o programa
executar o "loop". Neste exemplo, o "loop" ser executado at que a instruo que
incrementa a varivel Contador armazene na mesma um valor maior que 11. O
programa ir, ento, avaliar a condio, obter o valor FALSO, e pular o "loop"
passando a executar a instruo seguinte ao FIM. Podemos descrever a ao desse
"loop" do seguinte modo:
enquanto {enquanto} (contador for menor que 11) faca
incio
{escreva o valor do contador na tela,
e incremente o seu valor de 1}
fim;

Notas de Aula - Compiladores 46

Ricardo Lus de Freitas

3 Estrutura Geral dos Compiladores


Programa fonte
Analisador
Lexical

Analisador
sinttico

Gerenciador
da tabela de
smbolos

Analisador
semntico
Gerador de
cdigo
intermedirio

Tratador de
erros

Otimizador
de cdigo

Gerador de
cdigo

Programa alvo

3.1 Funcionamento Bsico de um Compilador


Este tpico ser dedicado uma breve explicao de cada mdulo da estrutura do
compilador. Note-se que esta estrutura apenas uma representao didtica do
funcionamento do compilador. Isto pode dar uma impresso de simplicidade e que o
compilador seja esttico. Na verdade todos os mdulos esto em constante atuao de forma
quase simultnea. Iniciaremos com o primeiro mdulo (anlise Lexical) e seguiremos na
ordem em que a figura indica para os demais mdulos.

3.2 Analisador Lexical


A anlise Lexical a primeira das trs fases que compe a anlise do texto-fonte
(source). Um Analisador Lexical, cumpre uma srie de tarefas, no somente relacionadas a
anlise Lexical, de grande importncia dentro do compilador. A principal funo deste
analisador a de fragmentar o programa fonte de entrada em trechos elementares completos
e com identidade prpria. Estes componentes bsicos so chamados tokens.

Notas de Aula - Compiladores 47

Ricardo Lus de Freitas

Ler caractere

passar tokens e seus atributos


Analisador
Lexical

entrada

Analisador
Sinttico

Empilha caractere de volta


Conexo do Analisador Lexical com o Analisador Sinttico

Do ponto de vista da implementao do compilador, o Analisador Lexical atua como


uma interface entre o texto fonte e o analisador sinttico, convertendo a sequncia de
caracteres que constituem o programa na sequncia de tomos que o analisador sinttico
consumir. Os tomos constituem-se em smbolos terminais da gramtica. O Analisador
Lexical do compilador tem como funo varrer o programa fonte da esquerda para a direita,
agrupando os smbolos de cada item lxico e determinando a sua classe. A seguir
relacionamos algumas funes internas do Analisador Lexical.

Extrao e classificao de tomos.


Eliminao de delimitadores e comentrios.
Identificao de palavras reservadas.
Recuperao de erros

Programa fonte

Analisador
Lexical

Analisador
sinttico

Tabela de
smbolos

Interao entre o Analisador Lexical, o analisador sinttico e a tabela de smbolos.

3.3 Analisador Sinttico


o segundo grande bloco componente dos compiladores. A principal funo deste
mdulo a de promover a anlise da sequncia com que os tomos componentes do texto
fonte se apresentam, a partir da qual se efetua a sntese da rvore sinttica, com base na
linguagem fonte. O analisador sinttico ir processar a sequncia de tomos, proveniente do
texto fonte, extrados pelo Analisador Lexical. A partir dessa sequncia o analisador
sinttico efetua a verificao da ordem de apresentao na sequncia, de acordo com a
gramtica na qual se baseia o reconhecedor. A anlise sinttica cuida exclusivamente da
forma das sentenas da linguagem, e procura, com base na gramtica, levantar a estrutura
das mesmas.

Notas de Aula - Compiladores 48

Ricardo Lus de Freitas

token
Programa
Fonte

Analisador
Lexical

Analisador
sinttico

Obter prximo
Token

rvore
Resto da
interface de
vanguarda

representao
intermediria

gramatical

Tabela de
smbolos

Interao do analisador sinttico com demais mdulos


Para a representao de notao da gramtica de uma linguagem, uma forma que
vem ganhando muitos adeptos devido a legibilidade de seu aspecto grfico so os diagramas
sintticos j descritos anteriormente. Esses diagramas caracterizam-se por dois pontos
diferentes: o ponto de partida e o de chegada. Os dois pontos so ligados entre si por um
grafo orientado, cujos ndos representam pontos onde eventualmente podem ser feitas
escolhas, e cujas arestas representam caminhos de percurso. Cada ramo pode incluir um
terminal ou no terminal da gramtica.

Identificao de sentenas.
Deteco de erros de sintaxe.
Recuperao de erros.
Montagem da rvore abstrata da sentena.
Comando de ativao do Analisador Lexical.
Ativao de rotinas de anlise semntica.
Ativao de rotinas de sntese do cdigo objeto.

3.4 Analisador Semntico


O principal objetivo do analisador semntico o de captar o significado das aes a
serem tomadas no texto fonte. Ele compe a terceira etapa de anlise do compilador e
refere-se a traduo do programa fonte para o programa objeto. Em geral a gerao de
cdigo vem acompanhada de atividades da anlise semntica. Denominamos semntica de
uma sentena o significado por ela assumido dentro do texto analisado, enquanto semntica
da linguagem a interpretao que se pode atribuir ao conjunto de todas as sentenas. A
fase da anlise semntica, porm, de difcil formalizao, exigindo notaes complexas.
Devido a esse fato, na maioria das linguagens de programao adota-se especificaes mais
simplificadas, com bases informais, atravs de linguagens naturais.
A partir dessas informaes, podemos afirmar que a principal funo da anlise
semntica criar, a partir do texto fonte, uma interpretao deste texto, expressa em alguma
notao adequada que , geralmente, uma linguagem intermediria do compilador. Esta
operao realizada com base nas informaes existentes nas tabelas construdas pelos
outros analisadores, tabelas de palavras reservadas, mapas e sadas dos demais analisadores.
Deve ser observada a fundamental importncia de uma anlise semntica bem realizada,

Ricardo Lus de Freitas

Notas de Aula - Compiladores 49

visto que toda a sntese estar embasada na interpretao gerada por este analisador. Entre
as aes semnticas encontramos tipicamente as seguintes:

Manter informaes sobre o escopo dos identificadores.


Representar tipos de dados

3.5 Gerao de Cdigo


A gerao de cdigos a fase final da compilao. Uma gerao de cdigos tima
sempre difcil, sendo importante destacar as dificuldades decorrentes das especificidades de
cada mquina. A importncia do estudo deve-se ao fato de que um algoritmo de gerao de
cdigos bem elaborado pode facilmente produzir cdigos que sero executados at duas
vezes mais rpido do que os cdigos produzidos por um algoritmo pouco analisado. Devese destacar que a natureza exata do cdigo objeto altamente dependente da mquina e do
sistema operacional.
PROGRAMAS OBJETO
Em geral, a entrada para uma rotina de gerao de cdigo um programa em
linguagem intermediria. A sada do gerador de cdigo o programa-objeto. Esta sada
pode apresentar-se numa variedade de formas de cdigo: linguagem de mquina absoluto,
linguagem de mquina relocvel, linguagem montadora (assembly), ou ento outra
linguagem qualquer de programao.
Para o caso de sada em cdigo de linguagem de mquina absoluto, esta colocada
em uma posio fixa na memria e executada imediatamente, podendo ser compilada e
executada em poucos segundos. Como exemplo, pode-se citar os compiladores usados para
fins didticos (student-jobs). A produo de cdigos em linguagem de mquina relocvel
(mdulo objeto), permite que sub-programas sejam compilados separadamente. Um
conjunto de mdulos objeto relocveis podem ser reunidos e carregados por um montador
(linker). O fato de ser possvel compilar sub-rotinas separadamente e chamar outros
programas compilados previamente a partir de um mdulo objeto, permite uma grande
flexibilidade. Muitos compiladores comerciais produzem mdulos objeto relocveis. A
produo de um cdigo em linguagem montadora como sada torna o processo de gerao
de cdigos mais fcil. possvel gerar instrues simblicas e utilizar as facilidades do
montador para auxiliar a gerao de cdigos. O custo adicional o passo em linguagem
montada, depois da gerao de cdigo. Porm, como para a produo do cdigo em
linguagem montada o compilador no duplicar o trabalho do montador, esta escolha
outra alternativa razovel, especialmente para uma mquina com pouca memria
disponvel, onde um compilador precise utilizar diversos passos. A produo de uma
linguagem de alto nvel simplifica ainda mais a gerao de cdigos. Este tipo de
implementao atua como pr-processador, e transfere os problemas de gerao do cdigo
de mquina para um outro compilador.

3.6 Otimizao Global e Local


As tcnicas de otimizao de cdigo so geralmente aplicadas depois da anlise
sinttica, usualmente antes e durante a gerao de cdigo. Essas tcnicas consistem em
detectar padres no programa e substitu-los por construes equivalentes, porm mais

Notas de Aula - Compiladores 50

Ricardo Lus de Freitas

eficientes. Estes padres podem ser locais ou globais, e a estratgia de substituio pode ser
dependente ou independente da mquina.
Segundo Aho, o programa-fonte como entrada para um compilador uma
especificao de uma ao. O programa-objeto, a sada do compilador, considerado como
sendo outra especificao da mesma ao. Para cada programa-fonte, existem infinitos
programas-objeto que implementam a mesma ao, ou seja, produzem a mesma sada para
uma mesma entrada. Alguns desses programas-objeto podem ser melhores do que outros
com relao a critrios tais como tamanho ou velocidade. O termo otimizao de cdigo
refere-se s tcnicas que um compilador pode empregar em uma tentativa para produzir um
programa-objeto melhorado, para um dado programa-fonte. A qualidade de um programaobjeto pode ser medida por seu tamanho ou tempo de execuo. Para programas grandes, o
tempo de execuo particularmente importante. Para computadores de pequeno porte, o
tamanho do cdigo objeto pode ser to importante quanto o tempo. Deve-se observar com
cuidado o termo otimizao, devido dificuldade de se obter um compilador que produza o
melhor programa-objeto possvel, para qualquer programa-fonte, a um custo razovel.
Assim, um termo mais exato para otimizao de cdigo seria melhoria de cdigo (code
improvement).

3.7 Um Exemplo do Funcionamento de um Compilador


Como exemplo do funcionamento de um compilador vamos analisar um simples
clculo do permetro de um tringulo. A primeira etapa consiste na anlise do problema
pelo programador. Se o problema o calculo de uma frmula matemtica, deve ser feito
uma abstrao matemtica desta situao que a seguinte:
Permetro = Lado 1 + Lado 2 + Lado 3
O prximo passo seria converter os elementos da frmula em variveis associadas
aos mesmos. Para Lado 1 chamaremos b, para Lado 2 chamaremos c, para Lado 3
chamaremos d e finalmente para Permetro chamaremos a. Em linguagem
computacional (alto nvel) a frmula de permetro representada pela seguinte equao:
a=b+c+d
O primeiro passo do compilador ser ento ler e identificar os smbolos, separando
cada elemento. O analisador acessa a tabela de smbolos e palavras reservadas, montando
assim uma relao entre os elementos e tokens da linguagem. Os tokens para este caso so:
a,=,b,+,c,+,d,fim_de_linha. O caracter fim_de_linha desprezado, assim como todos os
espaos em branco que no interessam compilao. A etapa seguinte ser a anlise
sinttica que verificar se as variveis a, b, c, d so reconhecidas. Para isso o analisador
sinttico consulta a tabela interna de smbolos ou declaraes explcitas, criada pelo
analisador sinttico. Deve-se observar que os analisadores esto continuamente em contato
entre si, sendo mutuamente ativados. O passo seguinte a verificao das operaes =e
+, que devem ser reconhecidas como pertencentes gramtica da linguagem. Na etapa
seguinte de investigao, necessrio identificar o que deve ser construdo. Neste exemplo
deve-se abstrair o sentido da operao que deseja-se efetuar. Trata-se de uma anlise
semntica muito simples. O analisador semntico constri uma linguagem intermediria que
dever realizar a operao desejada e ser entendida pela mquina. Neste caso, o significado
(semntica) que representa a equao do permetro a soma do Lado 1 com o Lado 2, o

Notas de Aula - Compiladores 51

Ricardo Lus de Freitas

resultado desta soma somado com o Lado 3. Em uma linguagem intermediria isso seria
representado da seguinte forma:
T1=b+c
T2=T1+d
A=T2
Com esta etapa realizada o compilador passa para a fase de sntese, que produzir a
linguagem assembly (cdigo produzido pela linguagem de montagem (assembler) ), e,
dependendo do compilador, efetuar duas otimizaes, global e local. Na primeira fase da
sntese o compilador ir buscar uma forma de otimizar, se possvel, as sentenas obtidas
pela anlise semntica. Esta fase chamada otimizao global. Podemos notar que a
sentena acima no foi otimizada. Uma possvel otimizao seria a eliminao das duas
ltimas sentenas em favor de uma que substitusse as duas como pode ser visto abaixo:
T1=b+c
A=T1+d
O passo seguinte seria a produo de linguagem assembly para o texto acima. Caso a
linguagem a ser produzida fosse para a arquitetura RISC_LIE, o cdigo que seria
produzido a partir do texto no otimizado seria:
Ldxw
Add
Stxw
Ldxw
Add
Stxw
Idwx
Stxw

b
c
t1
t1
d
t2
t2
a

Ldxw: carrega palavra (correspondendo a load)


Stxw: armazena palavra (correspondendo a store)
O compilador poderia ento, numa ltima etapa de sntese, efetuar uma otimizao
local, na linguagem assembly gerada. O resultado desta otimizao seria:
Ldxw
Add
Add
Stxw

b
c
d
a

A linguagem assembly gerada um mneumnico de cdigo binrio, que entendido


pela C. P. U. da mquina. Como as C. P. U. so dispositivos eletrnicos que operam sob
forma de tenses eltricas em dois nveis (0 se no h tenso e 1 se h tenso), a linguagem
assembly corresponderia a uma sequncia de 0 e 1. Se o tamanho da palavra fosse 32 bits,
teramos a seguinte sentena correspondente em linguagem de mquina instruo
Ldxw b :
0101 1000 0001 0000 0000 0010 1101 1001

Notas de Aula - Compiladores 52

Ricardo Lus de Freitas

4 Anlise Lexical
O analisador lexical a primeira fase de um compilador. Sua tarefa principal a de ler
os caracteres de entrada e produzir uma sequncia de tokens que o analisador sinttico
utiliza. Essa interao, sumarizada esquematicamente na Figura 4.1, comumente
implementada fazendo-se com que o Analisador Lexical seja uma sub-rotina ou uma corotina do analisador sinttico. Ao receber do analisador sinttico um comando obter o
prximo token, o Analisador Lexical l os caracteres de entrada at que possa identificar o
prximo token.
Token
Programa
Fonte

Analisador
Sinttico

Analisador
Lexical
Obter prximo
token

Tabela de
smbolos
Figura 4.1: Interao do Analisador Lexical com o analisador sinttico.

Como o Analisador Lexical a parte do compilador que l o texto-fonte, tambm


pode realizar algumas tarefas secundrias ao nvel da interface com o usurio. Uma delas
a de remover do programa-fonte os comentrios e os espaos em branco, os ltimos sob a
forma de espaos, tabulaes e caracteres de avano de linha. Uma outra a de
correlacionar as mensagens de erro do compilador com o programa-fonte. Por exemplo, o
Analisador Lexical pode controlar o nmero de caracteres examinados, de tal forma que um
nmero de linha possa ser relacionado a uma mensagem de erro. Em alguns compiladores, o
Analisador Lexical fica com a responsabilidade de fazer uma cpia do programa-fonte com
as mensagens de erro associadas ao mesmo. Se a linguagem-fonte suporta algumas funes
sob a forma de macros pr-processadas, as mesmas tambm podem ser implementadas na
medida em que a anlise Lexical v se desenvolvendo.
Algumas vezes, os analisadores lexicais so divididos em duas fases em cascata, a
primeira chamada de varredura(scanning) e a segunda de anlise Lexical. O scanner
responsvel por realizar tarefas simples, enquanto o Analisador Lexical propriamente dito
realiza as tarefas mais complexas. Por exemplo, um compilador Fortran poderia usar um
scanner para eliminar os espaos da entrada.

4.1 Temas da Anlise Lexical


Existem vrias razes para se dividir a fase de anlise da compilao em anlise Lexical
e anlise sinttica.
1. Um projeto mais simples talvez seja a considerao mais importante. A separao das
anlises Lexical e sinttica frequentemente nos permite simplificar uma ou outra dessas
fases. Por exemplo, um analisador sinttico que incorpore as convenes para
comentrios e espaos em branco significativamente mais complexo do que um que
assuma que os mesmos j tenham sido removidos pelo Analisador Lexical. Se

Notas de Aula - Compiladores 53

Ricardo Lus de Freitas

estivermos projetando uma nova linguagem, separar as convenes Lexicals das


sintticas pode levar a um projeto global de linguagem mais claro.
2. A eficincia do compilador melhorada. Um Analisador Lexical separado nos permite
construir um processador especializado e potencialmente mais eficiente para a tarefa.
Uma grande quantidade de tempo gasta lendo-se o programa-fonte e particionando-o
em tokens. Tcnicas de buferizao especializadas para a leitura de caracteres e o
processamento de tokens podem acelerar significativamente o desempenho de um
compilador.
3. A portabilidade do compilador realada. As peculiaridades do alfabeto de entrada e
outras anomalias especficas de dispositivos podem ser restringidas ao Analisador
Lexical. A representao de smbolos especiais ou no-padro, tais como ^ em Pascal,
pode ser isolada no Analisador Lexical.

4.2 Tokens, Padres, Lexemas


Quando se fala sobre a anlise Lexical, usamos os termos token, padro e
lexemacom significados especficos. Exemplos de seus usos so mostrados na Figura 4.2.
Em geral, existe um conjunto de cadeias de entrada para as quais o mesmo token
produzido como sada. Esse conjunto de cadeias descrito por uma regra chamada de um
padro associado ao token de entrada. O padro dito reconhecer cada cadeia do conjunto.
Um lexema um conjunto de caracteres no programa-fonte que reconhecido pelo padro
de algum token. Por exemplo, no enunciado Pascal
Const

pi = 3.1416;

A subcadeia pi um lexema para o token como smbolos terminais na gramtica


para a linguagem-fonte, usando nomes em negrito para represent-los. Os lexemas
reconhecidos pelo padro do token representa cadeias de caracteres no programa-fonte, e
podem receber um tratamento conjunto, como instncias de uma mesma unidade Lexical
(por exemplo, instncias de identificadores, nmeros etc. ).
Na maioria das linguagens de programao, as seguintes construes so tratadas
como tokens: palavras-chave, operadores, identificadores, constantes, literais, cadeias e
smbolos de pontuao, como parnteses, vrgulas e ponto-e-vrgulas. No exemplo acima,
quando a sequncia de caracteres pi aparece no programa-fonte, um token representando um
identificador repassado ao analisador sinttico. O repasse de um token frequentemente
implementado transmitindo-se um inteiro associado ao token. justamente esse inteiro que
designado por id na Figura 4.2.
Um padro uma regra que descreve o conjunto de lexemas que podem representar
um token particular nos programas-fonte. O padro para o token const na Figura 4.2
exatamente a cadeia singela const, que soletra a palavra-chave. O padro para o token
relacional o conjunto de todos os seis operadores relacionais de Pascal.
Certas convenes de linguagem aumentam a dificuldade da anlise Lexical.
Linguagens como Fortran requerem certas construes em posies fixas na linha de
entrada. Dessa forma, o alinhamento de um lexema pode ser importante na determinao da
correo de um programa-fonte. A tendncia no projeto moderno de linguagens de
programao est na direo de entradas em formato livre, permitindo que as construes
sejam colocadas em qualquer local da linha de entrada, e, por conseguinte, esse aspecto da
anlise Lexical vem se tornando menos importante.
O tratamento dos espaos varia grandemente de linguagem para linguagem. Em
algumas linguagens, os espaos no so significativos, exceto quando dentro de literais do
tipo cadeia de caracteres. Podem ser adicionados vontade a fim de melhorar a legibilidade

Notas de Aula - Compiladores 54

Ricardo Lus de Freitas

de um programa. As convenes relacionadas aos espaos podem complicar grandemente a


tarefa de identificao dos tokens.
Um exemplo popular que ilustra a dificuldade potencial em se reconhecer tokens o
enunciado DO de Fortran. No comando
DO 5 I = 1.25
No podemos afirmar que DO seja parte do identificador DO5I, e no um
identificador em si, at que tenhamos examinado o ponto decimal.
Por outro lado, no enunciado
DO 5 I = 1,25
Temos sete tokens: a palavra-chave DO, o rtulo de enunciado 5, o identificador I, o
operador =, a constante 1, a vrgula e a constante 25. Aqui no podemos estar certos, at que
tenhamos examinado a vrgula, de que DO seja uma palavra-chave. Para aliviar esta
incerteza, Fortran 77 permite que uma vrgula opcional seja colocada entre o rtulo e o
ndice do enunciado DO ( no exemplo, a varivel I). O uso dessa vrgula encorajado
porque a mesma ajuda a tornar o enunciado DO mais claro e legvel.
Em muitas linguagens, certas cadeias so reservadas, isto , seus significados so prdefinidos e no podem ser modificados pelo usurio. Se uma palavra-chave no for
reservada, o Analisador Lexical precisar distinguir uma palavra-chave de um identificador
definido pelo usurio. Em PL/I, as palavras-chave no so reservadas; consequentemente, as
regras para essa distino so um tanto complicadas, como o seguinte enunciado PL/I
ilustra:
SE ENTAO ENTAO ENTAO = SENAO; SENAO SENAO = ENTAO;

TOKEN
Const
Se
Relacional
id
num
literal

LEXEMAS EXEMPLO
const
se
< , <= , = , != , > , > =
pi , contador , D2
3 . 1416 , 0 , 6 . 02E23
contedo da memria

DESCRIO INFORMAL DO PADRO


const
se
< ou < = ou = ou != ou > = ou >
letra seguida por letras e/ou dgitos
qualquer constante numrica
quaisquer caracteres entre aspas, exceto
aspa

Figura 4.2 Exemplo de tokens.

4.3 Atributos para os Tokens


Quando um lexema for reconhecido por mais de um padro, o Analisador Lexical precisar
providenciar informaes adicionais para as fases subsequentes do compilador a respeito do
lexema particular que foi reconhecido. Por exemplo, o padro num reconhece as duas
cadeias O e 1, mas essencial para o gerador de cdigo ser informado sobre que cadeia foi
efetivamente reconhecida, ou seja, no basta reconhecer num, tem que informar que ele
vale 0 ou vale 1.

Notas de Aula - Compiladores 55

Ricardo Lus de Freitas

O Analisador Lexical coleta informaes a respeito dos tokens em seus atributos


associados. Os tokens influenciam decises na anlise gramatical; os atributos influenciam a
traduo dos tokens. Do ponto de vista prtico, o token possui usualmente somente um
nico atributo - um apontador para a entrada da tabela de smbolos na qual as informaes
sobre os mesmos so mantidas; o apontador se torna o atributo do token.
Para fins de diagnstico, podemos estar interessados tanto no lexema de um
identificador quanto no nmero da linha na qual o mesmo foi primeiramente examinado.
Esses dois itens de informao podem, ambos, ser armazenados na entrada da tabela de
smbolos para o identificador.
Exemplo: Os tokens e os valores de atributos associados ao enunciado Fortran
E = M * C ** 2
So escritos abaixo como uma sequncia de pares:
<id, apontador para a entrada da tabela de smbolos para E >
<operador_de_atribuio,>
<id, apontador para a entrada da tabela de smbolos para M>
<operador_de_multiplicao,>
<id, apontador a para a entrada da tabela de smbolos para C>
<operador_de_exponenciao,>
<num, valor inteiro 2>
Note-se que em certos pares no existe a necessidade de um valor de atributo; o primeiro
componente suficiente para identificar o lexema. Nesse pequeno exemplo, ao token num
foi associado um atributo de valor inteiro. O compilador pode armazenar a cadeia de
caracteres que forma o nmero numa tabela de smbolos e deixar o atributo do token num
ser o apontador para a entrada da tabela.

4.4 Erros Lxicos


Poucos erros so distinguveis somente no nvel lxico, uma vez que um Analisador
Lexical possui uma viso muito local do programa-fonte. Se cadeia fi for encontrada pela
primeira vez num programa C, no contexto
Fi ( a == f (x) ) ...
Um Analisador Lexical no poder dizer se Fi a palavra-chave se incorretamente
grafada ou um identificador de funo no declarada. Como Fi um identificador vlido, o
Analisador Lexical precisa retornar o token identificador e deixar alguma fase posterior do
compilador tratar o eventual erro.
Mas, suponhamos que acontea uma situao na qual o Analisador Lexical seja
incapaz de prosseguir, porque nenhum dos padres reconhea um prefixo na entrada
remanescente. Talvez a estratgia mais simples de recuperao seja a da modalidade

Ricardo Lus de Freitas

Notas de Aula - Compiladores 56

pnico. Removemos sucessivos caracteres da entrada remanescente at que o Analisador


Lexical possa encontrar um token bem-formado. Essa tcnica de recuperao pode
ocasionalmente confundir o analisador sinttico, mas num ambiente de computao
interativo pode ser razoavelmente adequada.
Outras possveis aes de recuperao de erros so:
1.
2.
3.
4.

remover um caractere estranho


inserir um caractere ausente.
substituir um caractere incorreto por um correto
transpor dois caracteres adjacentes.

Transformaes de erros como essas podem ser experimentadas numa tentativa de se


consertar a entrada. A mais simples de tais estratgias a de verificar se um prefixo da
entrada remanescente pode ser transformado num lexema vlido atravs de uma nica
transformao. Essa estratgia assume que a maioria dos erros lxicos seja resultante de um
nico erro de transformao, uma suposio usualmente confirmada na prtica, embora nem
sempre.
Uma forma de se encontrar erros num programa computar o nmero mnimo de
transformaes de erros requeridas para tornar um programa errado num que seja
sintaticamente bem-formado. Dizemos que um programa errado possui K erros se a menor
sequncia de transformaes de erros que ir mape-lo em algum programa vlido possui
comprimento K. A correo de erros de distncia mnima uma conveniente ferramenta
terica de longo alcance, mas que no geralmente usada por ser custosa demais de
implementar. Entretanto, uns poucos compiladores experimentais tm usado o critrio da
distncia mnima para realizar correes localizadas.
Outra forma que pode ser adotada registrar como erro cada entrada que no se
enquadra dentro dos padres definidos para a linguagem.

4.5 Anlise Lexical no CSD


Como foi dito anteriormente, a principal funo do Analisador Lexical ler os
caracteres de entrada e produzir uma lista de token neste processo so removidos do
programa fonte os comentrios e os espaos em branco que o analisador sinttico
utilizar. Cada token representa um smbolo vlido na linguagem LPD.
No CSD os token so representados internamente por uma estrutura formada por
dois campos:

Lexema : contm a seqncia de caracteres letras, nmeros, etc. que formam


o token;

Smbolo : este um campo do tipo numrico que contm uma representao interna
para o token. Para aumentar a eficincia do sistema, toda a manipulao da
estrutura do token ser feita utilizando-se este campo. O CSD possui a seguinte
tabela de smbolo:

Notas de Aula - Compiladores 57

Ricardo Lus de Freitas

TOKEN

Lexema

programa
incio
fim
procedimento
funcao
se
entao
senao
enquanto
faca
:=
escreva
leia
var
inteiro
booleano
identificador
nmero
.
;
,
(
)
>
>=
=
<
<=
!=
+
*
div
e
ou
nao
:

Smbolo

sprograma
sincio
sfim
sprocedimento
sfuncao
sse
sentao
ssenao
senquanto
sfaca
satribuio
sescreva
sleia
svar
sinteiro
Sbooleano
Sidentificador
Snmero
Sponto
sponto_vrgula
Svrgula
sabre_parnteses
sfecha_parnteses
Smaior
Smaiorig
Sig
Smenor
Smenorig
Sdif
Smais
Smenos
Smult
Sdiv
Se
Sou
Snao
Sdoispontos

Por exemplo, na anlise Lexical da sentena abaixo


se contador > 10 { exemplo de comentrio }
entao escreva (contador)
senao escreva (x);
gerada a seguinte lista de tokens:

Notas de Aula - Compiladores 58

Ricardo Lus de Freitas

Lexema

Smbolo

se

sse

contador

sidentificador

>

smaior

10

snmero

entao

sentao

escreva

sescreva

sabre_parnteses

contador

sidentificador

sfecha_parnteses

senao

ssenao

escreva

sescreva

sabre_parnteses

sidentificador

sfecha_parnteses

sponto_vrgula

Os comentrios na linguagem CSD so feitos atravs do uso dos caracteres {


para abrir um comentrio e } para fechar o comentrio , este caracteres assim
como a seqncia de caracteres entre eles, no so considerados tokens pelo Analisador
Lexical.
Estrutura da Lista de Tokens
A lista de Tokens uma estrutura de fila First in First out , formada por uma
lista encadeada onde cada n possui o formato apresentado na figura abaixo.

Lexema

Smbolo
N da lista de tokens.

Os Algoritmos do Analisador Lexical no CSD


Uma vez defina a estrutura de dados do Analisador Lexical, possvel descrever seu
algoritmo bsico. No nvel mais alto de abstrao, o funcionamento do Analisador Lexical
pode ser definido pelo algoritmo:

Ricardo Lus de Freitas

Notas de Aula - Compiladores 59

Algoritmo Analisador Lexical (Nvel 0)


Inicio
Abre arquivo fonte
Enquanto no acabou o arquivo fonte
Faa {
Trata Comentrio e Consome espaos
Pega Token
Coloca Token na Lista de Tokens
}
Fecha arquivo fonte
Fim

Na tentativa de aproximar o algoritmo acima de um cdigo executvel, so feitos


refinamentos sucessivos do mesmo. Durante este processo, surgem novos procedimentos,
que so refinados na medida do necessrio.

Algoritmo Analisador Lexical (Nvel 1)


Def. token: TipoToken
Inicio
Abre arquivo fonte
Ler(caracter)
Enquanto no acabou o arquivo fonte
Faa {Enquanto ((caracter = {)ou
(caracter = espao)) e
(no acabou o arquivo fonte)
Faa { Se caracter = {
Ento {Enquanto (caracter } ) e
(no acabou o arquivo fonte)
Faa Ler(caracter)
Ler(caracter)}
Enquanto (caracter = espao) e
(no acabou o arquivo fonte)
Faa Ler(caracter)
}
se caracter != fim de arquivo
ento {Pega Token
Insere Lista}
}
Fecha arquivo fonte
Fim.

Ricardo Lus de Freitas

Notas de Aula - Compiladores 60

Algoritmo Pega Token


Inicio
Se caracter digito
Ento Trata Digito
Seno Se caracter letra
Ento Trata Identificador e Palavra Reservada
Seno Se caracter = :
Ento Trata Atribuio
Seno Se caracter {+,-,*}
Ento Trata Operador Aritmtico
Seno Se caracter {<,>,=}
EntoTrataOperadorRelacional
Seno Se caracter {; , ( ) .}
Ento Trata Pontuao
Seno ERRO
Fim.
Algoritmo Trata Dgito
Def num : Palavra
Inicio
num caracter
Ler(caracter)
Enquanto caracter dgito
Faa {
num num + caracter
Ler(caracter)
}
token.smbolo snmero
token.lexema num
Fim.
Algoritmo Trata Identificador e Palavra Reservada
Def id: Palavra
Inicio
id caracter
Ler(caracter)
Enquanto caracter letra ou dgito ou _
Faa {id id + caracter
Ler(caracter)
}
token.lexema id
caso
id = programa : token.smbolo sprograma
id = se : token.smbolo sse
id = entao : token.smbolo sentao
id = senao : token.smbolo ssenao
id = enquanto : token.smbolo senquanto
id = faca : token.smbolo sfaca
id = incio : token.smbolo sincio

Ricardo Lus de Freitas

Notas de Aula - Compiladores 61

id = fim : token.smbolo sfim


id = escreva : token.smbolo sescreva
id = leia :token.smbolo sleia
id = var : token.smbolo svar
id = inteiro : token.smbolo sinteiro
id = booleano : token.smbolo sbooleano
id = verdadeiro : token.smbolo sverdadeiro
id = falso : token.smbolo sfalso
id = procedimento : token.smbolo sprocedimento
id = funcao : token.smbolo sfuncao
id = div : token.smbolo sdiv
id = e : token.smbolo se
id = ou : token.smbolo sou
id = nao : token.smbolo snao
seno : token.smbolo sidentificador
Fim.
Exerccios:
Fazer os algoritmos para:
Trata Atribuio
Trata Operador Aritmtico
Trata Operador Relacional
Trata Pontuao

Ricardo Lus de Freitas

Notas de Aula - Compiladores 62

5 Tabela de Smbolos
Uma funo essencial do compilador registrar os identificadores usados no
programa fonte e coletar as informaes sobre os seus diversos atributos. Esses atributos
podem ser informaes sobre a quantidade de memria reservada para o identificador, seu
tipo, escopo, (onde vlido o programa) e, no caso de nomes de procedimentos, coisas tais
como o nmero e os tipos de seus argumentos, o mtodo de transmisso de cada um (por
exemplo, por referncia) e o tipo retornado, se algum. Para armazenar estas informaes
existe a tabela de smbolos.
Uma tabela de smbolos uma estrutura de dados contendo um registro para cada
identificador, com os campos contendo os atributos do identificador. As informaes sobre
o identificador so coletadas pelas fases de anlise de um compilador e usada pelas fases de
sntese de forma a gerar o cdigo-alvo. Durante a Anlise Lexical a cadeia de caracteres que
forma um identificador armazenada em uma entrada da tabela de smbolos. As fases
seguintes do compilador acrescentam informaes a esta entrada. A fase de gerao utiliza
as informaes armazenadas para gerar o cdigo adequado.
Um mecanismo de tabela de smbolos precisa permitir que adicionemos novas
entradas e encontremos eficientemente as j existentes. til para um compilador ser capaz
de crescer a tabela de smbolos dinamicamente, se necessrio, em tempo de compilao. Se
o tamanho da tabela for esttico, ele deve ser grande o suficiente para tratar qualquer
programa fonte que lhe seja apresentado. Tal tamanho fixo traz o problema de desperdcio
na maioria dos casos.

5.1 Entradas na Tabela de Smbolos


O formato das entradas na tabela de smbolos no tem que ser uniforme, porque a
informao guardada a respeito de um nome depende do uso do mesmo. Cada entrada pode
ser implementada como um registro consistindo em uma sequncia de palavras consecutivas
na memria. Para manter a tabela de smbolos uniforme, pode ser conveniente que algumas
das informaes a respeito de um nome sejam mantidas fora da entrada da tabela, com
somente um apontador no registro apontando para cada uma destas informaes.
Um aspecto importante que deve ser considerado quanto aos procedimento ou
funes aninhados. Eles criam ambientes locais de variveis. Para tratar isso temos as
seguintes possibilidades:
Modelo de Pilha: uma vez terminada a compilao de um procedimento os smbolos locais
so descartados.
Compilao em duas etapas: aps a compilao de um procedimento ou funo a rvore
de programa continua fazendo referncias aos smbolos locais.
A tabela de smbolos deve ser organizada de forma que uma vez terminada a
compilao de um procedimento ou funo, os smbolos locais continuem existindo,
embora no faam parte do caminho de busca usado na compilao do restante do
programa.
Algumas das estratgias para resolver esse problema:

gerar o cdigo para um procedimento imediatamente aps a rvore de programa


correspondente ter sido construda. Aps a gerao de cdigo, os smbolos locais podem
ser descartados (o modelo de pilha continua vlido);

Notas de Aula - Compiladores 63

Ricardo Lus de Freitas

implementar a tabela de smbolos como uma lista ligada. Ao final da compilao de um


procedimento, os smbolos locais so transferidos para uma outra lista. Dessa forma as
referncias aos smbolos feitas pela rvore de programa continuam vlidas.

5.2 Tabela de Smbolos como rvore Invertida


Uma forma simples de se implementar a tabela de smbolos construir uma lista
ligada onde cada n aponta para o n inserido anteriormente na tabela, manter um
apontador para o ltimo smbolo. Ao encerrar a compilao de um procedimento ou funo,
os smbolos locais continuam na lista e as novas inseres passam a ser feitas a partir do n
que escreve o procedimento.

Programa p ;
Var a: inteiro ;
Procedimento Q ;
Var b,c: inteiro ;
Incio . . . fim;
Procedimento R ;
Var d, e: inteiro ;
Procedimento S ;
Var f ,g: inteiro ;
Incio . . . fim ;
Incio . . . fim ;
...
fim .

Caminho de
busca usado na
compilao do
programa

c
Caminho de
busca usado na
compilao de
Q

Caminho de
busca usado na
compilao de
R

Caminho de
busca usado ao
se compilar S

Ricardo Lus de Freitas

Notas de Aula - Compiladores 64

5.3 Visibilidade dos Smbolos


A tabela smbolos deve ser organizada de forma a refletir a visibilidade de smbolos
definida na linguagem.
Programa P ;
Var a : inteiro ;
Procedure T ;
Procedimento W ;
Incio
...T; ...
end ;
incio { T }
... W; ...
end ;
procedimento Q ;
var b : inteiro ;
procedimento R ;
var c ,d : inteiro ;
procedimento S ;
var d : inteiro
incio . . .
. . . a :=b+c+d;R; . . .
end ;

incio {R }
. . . T; S;a: =b+c+d ; . . .
end ;

incio { Q }
... R; ...
end ;
incio
... Q; ...
end ;

O procedimento T, visvel dentro do procedimento W (embora o seu corpo possa


ainda no ter sido compilado).
S enxerga as variveis a, b, e c definidas em nvel mais externo. A varivel d, definida
em R inacessvel em S porque existe em S uma outra definio do nome d que
esconde a anterior.

Notas de Aula - Compiladores 65

Ricardo Lus de Freitas

R enxerga as variveis a, b, c e d, e os procedimentos Q e T, mais externos. V


tambm o procedimento S, interno a ele, mas no as definies internas a S, ou seja, a
varivel d.

EXEMPLO
No exemplo acima, os nomes visveis em cada procedimento so:
Em T, W e a so visveis
Em W, T e a so visveis
Em Q, T, a, b e R so visveis
Em R, T, a, Q, b, c, d e S so visveis
Em S, T, a, Q, b, c, R e d so visveis
Em P, T, a e Q so visveis
Durante a compilao de um procedimento, devem estar visveis os nomes definidos
localmente, e os nomes definidos dentro dos escopos mais externos dentro dos quais o
procedimento est contido.
A prioridade de busca deve ser tal que, no caso de um mesmo nome estar definido em
mais de um escopo, a definio feita no escopo mais interno prevalece.
Uma vez completada a compilao de um procedimento, os smbolos internos a ele
deixam de ser visveis. O prprio procedimento continua visvel dentro do escopo onde ele
definido.

5.4 A Tabela de Smbolos Organizada como um Vetor


Uma forma simples de se organizar a tabela de smbolos como um vetor onde os
smbolos so inseridos na mesma ordem em que so declarados. As buscas nessa tabela so
feitas sempre do mais recente para o mais antigo. Ao se encerrar a compilao de um
procedimento, todos os smbolos locais so removidos da tabela.
Exemplo (situao de tabela durante a compilao do procedimento S, acima):
P
a
T

Caso o smbolo procurado seja d,


ser localizado o mais recente.

Q
b
R
c
d
S
d

ltimo

Ricardo Lus de Freitas

Notas de Aula - Compiladores 66

Como as inseres so sempre no fim da tabela e como as


retiradas sempre so dos smbolos mais recentes (locais ao
procedimento que acabou de ser compilado), a tabela
funciona como uma pilha (!).

Este modelo para a tabela, usando um vetor, supe que as buscas sero sequenciais.
Isso pode ser proibitivo se o nmero de smbolos for muito grande. A mesma lgica de
funcionamento pode ser aplicada a outras organizaes de tabela visando a melhoria no
tempo de acesso.

5.5 Tabela de Smbolos no CSD


A Tabela de Smbolos no CSD dever seguir um dos padres vistos anteriormente. A
nica restrio ficar por conta do registro a ser definido, que dever representar as
seguintes informaes:

Nome do identificador (lexema)


Escopo (nvel de declarao)
Tipo (padro do identificador)
Memria (endereo de memria alocado)
Alm do registro devero ser implementados os seguintes procedimentos bsicos:

Procedimentos:

Insere na Tabela: inserir os identificadores na Tabela.

Consulta a Tabela: percorre a Tabela procurando por um identificador. Devolve todos os


campos do registro.

Coloca Tipo nas Variveis: percorre a tabela do final para o comeo substituindo todos
os campos tipo que possuem o valor varivel pelo tipo agora localizado.

Notas de Aula - Compiladores 67

Ricardo Lus de Freitas

6 Anlise Sinttica
Cada linguagem de programao possui as regras que descrevem a estrutura sinttica
dos programas bem-formados. Em Pascal, por exemplo, um programa constitudo por
blocos, um bloco por comandos, um comando por expresses, uma expresso por tokens e
assim por diante. A sintaxe das construes de uma linguagem de programao pode ser
descrita pelas gramticas livres de contexto (usando possivelmente a notao BNF). As
gramticas oferecem vantagens significativas tanto para os projetistas de linguagens quanto
para os escritores de compiladores.
Uma gramtica oferece, para uma linguagem de programao, uma especificao
sinttica precisa e fcil de entender.
Para certas classes de gramticas, podemos construir automaticamente um analisador
sinttico que determine se um programa-fonte est sintaticamente bem-formado. Como
benefcio adicional, o processo de construo do analisador pode revelar ambiguidades
sintticas bem como outras construes difceis de se analisar gramaticalmente, as quais
poderiam, de outra forma, seguir indetectadas na fase de projeto inicial de uma
linguagem e de seu compilador.
Uma gramtica propriamente projetada implica uma estrutura de linguagem de
programao til traduo correta de programas-fonte em cdigos-objeto e tambm
deteco de erros. Existem ferramentas disponveis para a converso de descries de
tradues, baseadas em gramticas, em programas operativos.
As linguagens evoluram ao longo de um certo perodo de tempo, adquirindo novas
construes e realizando tarefas adicionais. Essas novas construes podem ser mais
facilmente includas quando existe uma implementao baseada numa descrio
gramatical da linguagem.
O ncleo desta seo est devotado aos mtodos de anlise sinttica que so
tipicamente usados nos compiladores. Apresentamos primeiramente os conceitos bsicos,
em seguida as tcnicas adequadas implementao manual e finalmente os algoritmos
usados.

6.1 O Papel do Analisador Sinttico


Em nosso modelo de compilador, o analisador sinttico obtm uma cadeia de tokens
proveniente do Analisador Lexical, como mostrado na Figura 6.1, e verifica se a mesma
pode ser gerada pela gramtica da linguagem-fonte. Esperamos que o analisador sinttico
relate quaisquer erros de sintaxe de uma forma inteligvel.
token
rvore
Programa
representao
Fonte

Analisador
Lexical

Analisador
sinttico

Obter prximo
Token

Resto da
interface de
vanguarda

gramatical

Tabela de
smbolos
Figura 6.1: Posio de um analisador sinttico num modelo de compilador

intermediria

Notas de Aula - Compiladores 68

Ricardo Lus de Freitas

Existem trs tipos gerais de analisadores sintticos. Os mtodos universais de anlise


sinttica podem tratar qualquer gramtica. Esses mtodos, entretanto, so muito ineficientes
para se usar num compilador comercial. Os mtodos mais comumente usados nos
compiladores so classificados como descendentes (top-down) ou ascendentes (bottom-up).
Como indicado por seus nomes, os analisadores sintticos descendentes constroem rvores
do topo (raiz) para o fundo (folhas), anquanto que os ascendentes comeam pelas folhas e
trabalham rvore acima at a raiz. Em ambos os casos, a entrada varrida da esquerda para
a direita, um smbolo de cada vez.
Os mtodos de anlise sinttica mais eficientes, tanto descendentes quanto
ascendentes, trabalham somente em determinadas subclasses de gramticas, mas vrias
dessas subclasses, como as das gramticas LL(left-left) e LR(left-right), so suficientemente
expressivas para descrever a maioria das construes sintticas das linguagens de
programao. Os analisadores implementados manualmente trabalham frequentemente com
gramticas LL. Os da classe mais ampla das gramticas LR so usualmente construdos
atravs de ferramentas automatizadas.
Nesta seo, assumimos que a sada de um analisador sinttico seja alguma
representao da rvore gramatical para o fluxo de tokens produzido pelo Analisador
Lexical. Na prtica existe um certo nmero de tarefas que poderiam ser conduzidas durante
a anlise sinttica, tais como coletar informaes sobre os vrios tokens na tabela de
smbolos, realizar a verificao de tipos e outras formas de anlise semntica, assim como
gerar o cdigo intermedirio. Juntamos todos esses tipos de atividades na caixa resto da
interface da vanguardada Figura 6.1.

6.2 Anlise Sinttica Ascendente


Neste tipo de anlise sinttica a construo da rvore de derivao para uma
determinada cadeia comea pelas folhas da rvore e segue em direo de sua raiz. Caso seja
obtida uma rvore cuja raiz tem como rtulo o smbolo inicial da gramtica, e na qual a
sequncia dos rtulos das folhas forma a cadeia dada, ento a cadeia uma sentena da
linguagem, e a rvore obtida a sua rvore de derivao.
O processo de se partir das folhas em direo raiz de uma rvore de derivao
conhecido como reduo. A cada passo procura-se reduzir uma cadeia (ou sub-cadeia) ao
seu smbolo de origem, objetivando-se atingir o smbolo inicial da gramtica.
Funcionamento geral:
1) Toma-se uma determinada cadeia .
2) Procura-se por uma sub-cadeia a1,a2,...an de que possa ser substituda pelo seu
smbolo de origem . Ou seja, a1,a2,...an. Assim = 1 2.

a1

a2

.........

an

3) Repetir o passo (2) at que = smbolo inicial da gramtica. Caso isso no seja possvel
tem-se que a cadeia analisada no pertence a linguagem especificada.

Notas de Aula - Compiladores 69

Ricardo Lus de Freitas

Exemplo:
G= ({E,T,F},{a,b,+,*,(,)},P,E)
P: E E + T | T
TT*F|F
F a | b | (E)
Verificando se a cadeia a+b*a pertence a linguagem utilizando-se de anlise
sinttica ascendente.
a+b*a => F+b*a => T+b*a => E+b*a => E+F*a => E+T*a => E+T*F =>
a

E+T => E
T*F

E+T

possvel dizer que os maiores problemas na anlise sinttica ascendente residem na


dificuldade de se determinar qual a parcela da cadeia (sub-cadeia) que dever ser reduzida,
bem como qual ser a produo a ser utilizada na reduo, para o caso de existirem mais do
que uma possibilidade.

6.3 Anlise Sinttica Descendente


Neste tipo de anlise sinttica tem-se procedimento inverso ao da anterior. Aqui a
anlise parte da raiz da rvore de derivao e segue em direo as folhas, ou seja, a partir do
smbolo inicial da gramtica vai-se procurando substituir os smbolos no-terminais de
forma a obter nas folhas da rvore a cadeia desejada.
Funcionamento geral:
1) Toma-se uma determinada cadeia e o smbolo inicial S da gramtica.
2) Procura-se por uma regra de derivao que permita derivar em sua totalidade ou pelo
menos se aproximar desta.
3) Repetir o passo (2) at que a cadeia no apresente mais smbolos no terminais. Nesta
situao verificado se a cadeia obtida coincide com . Caso isso no seja possvel temse que a cadeia analisada no pertence a linguagem especificada.
Embora apresente os mesmos problemas que a anterior, apresenta a possibilidade de
se poder implementar facilmente compiladores para linguagens obtidas de gramticas LL(1)
(anlise da cadeia da esquerda para a direita e derivaes esquerdas observando apenas o
primeiro smbolo da cadeia para decidir qual regra de derivao ser utilizada).

Ricardo Lus de Freitas

Notas de Aula - Compiladores 70

Nas gramticas LL(1) cada regra de derivao apresenta smbolo inicial da cadeia
resultante diferenciado dos demais, o que facilita sua escolha. Como nas linguagens de
programao isso ocorre com frequncia, ela se mostra adequada para uso em compiladores.
No resto desta seo, consideraremos a natureza dos erros sintticos e as estratgias
gerais para sua recuperao. Duas dessas estratgias, chamadas modalidade do desesperoe
recuperao em nvel de frase, so discutidas mais pormenorizadamente junto com os
mtodos individuais de anlise sinttica. A implementao de cada estratgia requer o
julgamento do desenvolvedor do compilador, mas daremos algumas diretrizes gerais
relacionadas a essas abordagens.

6.4 Tratamento dos Erros de Sintaxe


Se um compilador tivesse que processar somente programas corretos, seu projeto e
sua implementao seriam grandemente simplificados. Mas os programadores
frequentemente escrevem programas incorretos, e um bom compilador deveria assistir o
programador na identificao e localizao de erros. gritante que, apesar dos erros serem
lugar-comum, poucas linguagens sejam projetadas tendo-se o tratamento de erros em mente.
Nossa civilizao seria radicalmente diferente se as linguagens faladas tivessem as mesmas
exigncias de correo sinttica que as das linguagens de computadores.
A maioria das especificaes das linguagens de programao no descreve como um
compilador deveria responder aos erros; tal tarefa deixada para o projetista do compilador.
O planejamento do tratamento de erros exatamente desde o incio poderia tanto simplificar a
estrutura de um compilador quanto melhorar sua resposta aos erros.
Sabemos que os programas podem conter erros em muitos nveis diferentes. Por
exemplo, os erros podem ser:

lxicos, tais como errar a grafia de um identificador, palavra-chave ou operador


sintticos, tais como uma expresso aritmtica com parnteses no-balanceados
semnticos, tais como um operador aplicado um operando incompatvel
lgicos, tais como uma chamada infinitamente recursiva

Frequentemente, boa parte da deteco e recuperao de erros num compilador gira


em torno da fase de anlise sinttica. Isto porque os erros ou so sintticos por natureza ou
so expostos quando o fluxo de tokens proveniente do Analisador Lexical desobedece s
regras gramaticais que definem a linguagem de programao.
Outra razo est na preciso dos modernos mtodos de anlise sinttica; podem
detectar muito eficientemente a presena de erros sintticos num programa. Detectar
precisamente a presena de erros semnticos ou lgicos em tempo de compilao uma
tarefa muito mais difcil.
O tratador de erros num analisador sinttico possui metas simples de serem
estabelecidas:
Deve relatar a presena de erros clara e acuradamente.
Deve se recuperar de cada erro suficientemente rpido a fim de ser capaz de detectar
erros subsequentes.
No deve retardar significativamente o processamento de programas corretos.
A realizao efetiva dessas metas apresenta desafios difceis.

Ricardo Lus de Freitas

Notas de Aula - Compiladores 71

Felizmente, os erros comuns so simples e frequentemente basta um mecanismo de


tratamento de erros relativamente direto. Em alguns casos, entretanto, um erro pode ter
ocorrido muito antes de sua presena ter sido detectada e sua natureza precisa pode ser
muito difcil de ser deduzida. Em casos difceis, o tratador de erros pode ter que adivinhar o
que o programador tinha em mente quando o programa foi escrito.
Vrios mtodos de anlise sinttica, tais como os mtodos LL e LR, detectam os
erros to cedo quando possvel. Mais precisamente, possuem a propriedade do prefixo
vivel, significando que detectam que um erro ocorreu to logo tenham examinado um
prefixo da entrada que no seja o de qualquer cadeia da linguagem.
Exemplo: A fim de ter uma apreciao dos tipos de erros que ocorrem na prtica, vamos
examinar os erros que Ripley e Druseikis encontraram numa amostra de programa Pascal de
estudantes.
Ripley e Druseikis descobriram que os erros no ocorrem com tanta frequncia; 60% dos
programas compilados estavam semntica e sintaticamente corretos. Mesmo quando os
erros ocorriam de fato, eram um tanto dispersos; 80% do enunciados contendo erros
possuam apenas um, 13% dois. Finalmente, a maioria constitua-se de erros triviais; 90%
eram erros em um nico token.
Muitos dos erros poderiam ser classificados simplificadamente: 60% eram erros de
pontuao, 20% de operadores e operandos, 15% de palavras-chave e os 5% restantes de
outros tipos. O grosso dos erros de pontuao girava em torno do uso incorreto do ponto-evrgula.
Para alguns erros concretos, consideremos o seguinte programa Pascal.
(1)
(2)
(3)
(4)
(5)
(6)
(7)
(8)
(9)
(10)
(11)
(12)
(13)

programa prmax;
var
x, y: inteiro;
funcao max (i: inteiro; j: inteiro) :
{ retorna maximo de i e j }
incio
se i > j entao max := i
senao max := j
fim;
incio
leia (x, y) ;
escreva (max (x, y) )
fim.

Um erro comum de pontuao o de se usar uma vrgula em lugar de ponto-e-vrgula


na lista de argumentos de uma declarao de funo (por exemplo, usar uma vrgula em
lugar do primeiro ponto-e-vrgula linha (4)); outro o de omitir um ponto-e-vrgula
obrigatrio ao final de uma linha (por exemplo, o ponto-e-vrgula ao final da linha (4)); um
terceiro o de colocar um ponto-e-vrgula estranho ao fim de uma linha antes de um senao
(por exemplo, colocar um ponto-e-vrgula ao fim da linha (7)).
Talvez uma razo pela qual os erros de ponto-e-vrgula sejam to comuns que seu
uso varia grandemente de uma linguagem para outra. Em Pascal, um ponto-e-vrgula um
separador de enunciados; em PL/1 e C um terminador. Alguns estudos tm sugerido que a
ltima utilizao menos propensa a erros.

Ricardo Lus de Freitas

Notas de Aula - Compiladores 72

Um exemplo tpico de um erro de operador o de omitir os dois pontos em :=. Erros


de grafia em palavras-chave so usualmente raros, mas omitir o i de escreva seria um
exemplo representativo.
Muitos compiladores Pascal no tm dificuldades em tratar os erros comuns de
insero, remoo e transformao. De fato, vrios compiladores Pascal iro tratar
corretamente o programa acima com um erro comum de pontuao ou de operador; iro
emitir somente um diagnstico de alerta, apontando a construo ilegal.
No entanto, um outro tipo de erro muito mais difcil de se reparar corretamente. o
caso de um incio ou fim ausente (por exemplo, a omisso da linha (9)). A maioria dos
compiladores no ir tentar reparar esse tipo de erro.
Como deveria um tratador de erros reportar a presena de um erro? No mnimo,
deveria informar o local no programa-fonte onde o mesmo foi detectado, uma vez que
existe uma boa chance de o erro efetivo ter ocorrido uns poucos tokens antes. Uma
estratgia comum empregada por muitos compiladores a de imprimir a linha ilegal com
um apontador para a posio na qual o erro foi detectado. Se existir um razovel
prognstico do que o erro realmente foi, uma mensagem de diagnstico informativa
tambm includa; por exemplo, ponto-e-vrgula ausente nesta posio.
Uma vez que o erro tenha sido detectado, como deveria o analisador sinttico se
recuperar? Como veremos, existe um nmero de estratgias gerais, mas nenhum mtodo
claramente se impe sobre os demais. At pouco tempo atrs, no era adequado para o
analisador sinttico encerrar logo aps detectar o primeiro erro, porque o processamento da
entrada restante ainda poderia revelar outros. Assim, existia alguma forma de recuperao
de erros na qual o analisador tentava restaurar a si mesmo para um estado onde o
processamento da entrada pudesse continuar com uma razovel esperana de que o resto
correto da entrada fosse analisado e tratado adequadamente pelo compilador.
Um trabalho inadequado de recuperao pode introduzir uma avalancha de erros
esprios, que no foram cometidos pelo programador, mas introduzidos pelas
modificaes no estado do analisador sinttico durante a recuperao de erros. Numa forma
similar, uma recuperao de erros sintticos pode introduzir erros semnticos esprios que
sero detectados posteriormente pelas fases de anlise semntica e de gerao de cdigo.
Por exemplo, ao se recuperar de um erro, o analisador pode pular a declarao de alguma
varivel, digamos zap. Quando zap for posteriormente encontrada nas expresses, no
haver nada sintaticamente errado, mas como no h uma entrada na tabela de smbolos
para zap, a mensagem zap no definidoser gerada.
Uma estratgia cautelosa para o compilador a de inibir as mensagens de erro que
provenham de erros descobertos muito proximamente no fluxo de entrada. Em alguns casos,
pode haver erros demais para o compilador continuar um processamento sensvel (por
exemplo, como deveria um compilador Pascal responder ao receber um programa Fortran
como entrada?). Parece que uma estratgia de recuperao de erros tem que ser um
compromisso cuidadosamente considerado levando em conta os tipos de erros que so mais
propensos a ocorrer e razoveis de processar.
Como mencionamos, alguns compiladores tentavam reparar os erros, num processo em
que tentam adivinhar o que o programador queria escrever. Exceto, possivelmente, num
ambiente de pequenos programas escritos por estudantes principiantes, a reparao
extensiva de erros no propensa a pagar o seu custo. De fato, com a nfase crescente na
computao interativa e bons ambientes de programao, a tendncia est na direo de
mecanismos simples de recuperao de erros.

Ricardo Lus de Freitas

Notas de Aula - Compiladores 73

6.5 Estratgia de Recuperao de Erros


Existem muitas estratgia gerais diferentes que um analisador sinttico pode
empregar para se recuperar de um erro sinttico. Apesar de nenhuma delas ter provado ser
universalmente aceitvel, uns poucos mtodos tm ampla aplicabilidade. Introduzimos aqui
as seguintes estratgias:

modalidade do desespero
nvel de frase
produes de erro
correo global

Recuperao na modalidade do desespero. Este o mtodo mais simples de implementar e


pode ser usado pela maioria dos mtodos de anlise sinttica. Ao descobrir um erro, o
analisador sinttico descarta smbolos de entrada, um de cada vez, at que seja encontrado
um token pertencente a um conjunto designado de tokens de sincronizao. Os tokens de
sincronizao so usualmente delimitadores, tais como o ponto-e-vrgula ou o fim, cujo
papel no programa-fonte seja claro. Naturalmente, o projetista do compilador precisa
selecionar os tokens de sincronizao apropriados linguagem-fonte. A correo na
modalidade do desespero, que frequentemente pula uma parte considervel da entrada sem
verific-la, procurando por erros adicionais, possui a vantagem da simplicidade e,
diferentemente dos outros mtodos a serem enfocados adiante, tem a garantia de no entrar
num lao infinito. Nas situaes em que os erros mltiplos num mesmo enunciados sejam
raros, esse mtodo pode ser razoavelmente adequado.
Recuperao de frases. Ao descobrir um erro, o analisador sinttico pode realizar uma
correo local na entrada restante. Isto , pode substituir um prefixo da entrada
remanescente por alguma cadeia que permita ao analisador seguir em frente. Correes
locais tpicas seriam substituir uma vrgula por ponto-e-vrgula, remover um ponto-evrgula estranho ou inserir um ausente. A escolha da correo local deixada para o
projetista do compilador. Naturalmente devemos ser cuidadosos, escolhendo substituies
que no levem a laos infinitos, como seria o caso, por exemplo, se inserssemos para
sempre na entrada algo frente do seu smbolo corrente. Esse tipo de substituio pode
corrigir qualquer cadeia e foi usado em vrios compiladores de correo de erros. O mtodo
foi primeiramente usado na anlise sinttica descendente. Sua maior desvantagem est na
dificuldade que tem ao lidar com situaes nas quais o erro efetivo ocorreu antes do ponto
de deteco.
Regras de Produes para erro. Se tivssemos uma boa idia dos erros comuns que
poderiam ser encontrados, poderamos aumentar a gramtica para a linguagem em exame
com as produes que gerassem construes ilegais. Usamos, ento, a gramtica aumentada
com essas produes de erro para construir um analisador sinttico. Se uma produo de
erro for usada pelo analisador, podemos gerar diagnsticos apropriados para indicar a
construo ilegal que foi reconhecida na entrada.
Correo global. Idealmente, gostaramos que um compilador fizesse to poucas mudanas
quanto possvel, ao processar uma cadeia de entrada ilegal. Existem algoritmos para
escolher uma sequncia mnima de mudanas de forma a se obter uma correo global de
menor custo. Dadas uma cadeia de entrada incorreta x e uma gramtica G, esses algoritmos
iro encontrar uma rvore gramatical para uma cadeia relacionada y, de tal forma que as
inseres, remoes e mudanas de tokens requeridas para transformar x em y sejam to

Ricardo Lus de Freitas

Notas de Aula - Compiladores 74

pequenas quanto possvel. Infelizmente, esses mtodos so em geral muito custosos de


implementar, em termos de tempo e espao e, ento, essas tcnicas so correntemente
apenas de interesse terico.Devemos assinalar que o programa correto mais prximo pode
no ser aquele que o programador tinha em mente. Apesar de tudo, a noo de correo de
custo mnimo fornece um padro de longo alcance para avaliar as tcnicas de recuperao
de erros e foi usada para encontrar cadeias timas de substituio para a recuperao em
nvel de frase.
evidente que com a crescente e irreversvel tendncia de uso de computadores
pessoais interligados em rede, em substituio ao antigos computadores de grande porte,
tem-se que a compilao passou a ser uma tarefa de caracterstica interativa. Assim, a nova
tendncia concentra-se no projeto de compiladores que interrompem a compilao a cada
erro encontrado, restringindo a recuperao simplesmente a emisso de uma mensagem
compreensvel do erro detectado.

6.6 Anlise Sinttica no CSD


Para fornecer um guia na implementao do analisador sinttico do CSD, tem-se a
seguir os algoritmos levando em considerao as caractersticas de sintaxe da Linguagem
LPD. Considera-se que sempre que encontrar um ERRO tem-se o processo de compilao
interrompido. No algoritmo a seguir existem exemplos no exaustivos de trechos do
analisador semntico (em azul) e da gerao de cdigo para rtulos (em vermelho).
Algoritmo Analisador Sinttico <programa>
Def rotulo inteiro
incio
rotulo:= 1
Lxico(token)
se token.simbolo = sprograma
ento incio
Lxico(token)
se token.simbolo = sidentificador
ento incio
insere_tabela(token.lexema,nomedeprograma,,)
Lxico(token)
se token.simbolo = spontovirgula
ento incio
analisa_bloco
se token.simbolo = sponto
ento se acabou arquivo ou comentrio
ento sucesso
seno ERRO
seno ERRO
fim
seno ERRO
fim
seno ERRO
fim
seno ERRO
fim.

Ricardo Lus de Freitas

Notas de Aula - Compiladores 75

Algoritmo Analisa_Bloco <bloco>


incio
Lxico(token)
Analisa_et_variveis
Analisa_subrotinas
Analisa_comandos
fim
Algoritmo Analisa_et_variveis <etapa de declarao de variveis>
incio
se token.simbolo = svar
ento incio
Lxico(token)
se token.smbolo = sidentificador
ento enquanto(token.smbolo = sidentificador)
faa incio
Analisa_Variveis
se token.smbolo = spontvirg
ento Lxico (token)
seno ERRO
fim
seno ERRO
fim
Algoritmo Analisa_Variveis <declarao de variveis>
incio
repita
se token.smbolo = sidentificador
ento incio
Pesquisa_duplicvar_ tabela(token.lexema)
se no encontrou duplicidade
ento incio
insere_tabela(token.lexema, varivel)
Lxico(token)
se (token.smbolo = Svrgula) ou
(token.smbolo = Sdoispontos)
ento incio
se token.smbolo = Svrgula
ento incio
lxico(token)
se token.simbolo = Sdoispontos
ento ERRO
fim
fim
seno ERRO
fim
seno ERRO
seno ERRO
at que (token.smbolo = sdoispontos)
Lxico(token)
Analisa_Tipo
fim

Ricardo Lus de Freitas

Notas de Aula - Compiladores 76

Algoritmo Analisa_Tipo <tipo>


incio
se (token.smbolo sinteiro e token.smbolo sbooleano))
ento ERRO
seno coloca_tipo_tabela(token.lexema)
Lxico(token)
fim
Algoritmo Analisa_comandos <comandos>
incio
se token.simbolo = sinicio
ento incio
Lxico(token)
Analisa_comando_simples
enquanto (token.simbolo sfim)
faa incio
se token.simbolo = spontovirgula
ento incio
Lxico(token)
se token.simbolo sfim
ento Analisa_comando_simples
fim
seno ERRO
fim
Lxico(token)
fim
seno ERRO
fim
Analisa_comando_simples <comando>
incio
se token.simbolo = sidentificador
ento Analisa_atrib_chprocedimento
seno
se token.simbolo = sse
ento Analisa_se
seno
se token.simbolo = senquanto
ento Analisa_enquanto
seno
se token.simbolo = sleia
ento Analisa_leia
seno
se token.simbolo = sescreva
ento Analisa_ escreva
seno
Analisa_comandos
fim
Algoritmo Analisa_atrib_chprocedimento <atribuio_chprocedimento>
incio
Lxico(token)
se token.simbolo = satribuio
ento Analisa_atribuicao

Ricardo Lus de Freitas

Notas de Aula - Compiladores 77

seno Chamada_procedimento
fim
Algoritmo Analisa_leia <comando leitura>
incio
Lxico(token)
se token.simbolo = sabre_parenteses
ento incio
Lxico(token)
se token.simbolo = sidentificador
ento se pesquisa_declvar_tabela(token.lexema)
ento incio (pesquisa em toda a tabela)
Lxico(token)
se token.simbolo = sfecha_parenteses
ento Lxico(token)
seno ERRO
fim
seno ERRO
seno ERRO
fim
seno ERRO
fim
Algoritmo Analisa_escreva <comando escrita>
incio
Lxico(token)
se token.simbolo = sabre_parenteses
ento incio
Lxico(token)
se token.simbolo = sidentificador
ento se pesquisa_ declvarfunc_tabela(token.lexema)
ento incio
Lxico(token)
se token.simbolo = sfecha_parenteses
ento Lxico(token)
seno ERRO
fim
seno ERRO
seno ERRO
fim
seno ERRO
fim
Algoritmo Analisa_enquanto <comando repetio>
Def auxrot1,auxrot2 inteiro
incio
auxrot1:= rotulo
Gera(rotulo,NULL,
,
)
{incio do while}
rotulo:= rotulo+1
Lxico(token)
Analisa_expresso
se token.simbolo = sfaa
ento incio
auxrot2:= rotulo
Gera(
,JMPF,rotulo,
)
{salta se falso}
rotulo:= rotulo+1

Ricardo Lus de Freitas

Lxico(token)
Analisa_comando_simples
Gera(
,JMP,auxrot1,
Gera(auxrot2,NULL,
,
fim
seno ERRO
fim

Notas de Aula - Compiladores 78

) {retorna incio loop}


) {fim do while}

Algoritmo Analisa_se <comando condicional>


incio
Lxico(token)
Analisa_expresso
se token.smbolo = sento
ento incio
Lxico(token)
Analisa_comando_simples
se token.smbolo = Sseno
ento incio
Lxico(token)
Analisa_comando_simples
fim
fim
seno ERRO
fim
Algoritmo Analisa_Subrotinas <etapa de declarao de sub-rotinas>
Def. auxrot, flag inteiro
Incio
flag = 0
if (token.simbolo = sprocedimento) ou
(token.simbolo = sfuno)
ento incio
auxrot:= rotulo
GERA(
,JMP,rotulo,
)
{Salta sub-rotinas}
rotulo:= rotulo + 1
flag = 1
fim
enquanto (token.simbolo = sprocedimento) ou
(token.simbolo = sfuno)
faa incio
se (token.simbolo = sprocedimento)
ento analisa_declarao_procedimento
seno analisa_ declarao_funo
se token.smbolo = sponto-vrgula
ento Lxico(token)
seno ERRO
fim
if flag = 1
ento Gera(auxrot,NULL,
,
)
{incio do principal}
fim
Algoritmo Analisa_ declarao_procedimento <declarao de procedimento>
incio
Lxico(token)
nvel := L (marca ou novo galho)
se token.smbolo = sidentificador

Ricardo Lus de Freitas

Notas de Aula - Compiladores 79

ento incio
pesquisa_declproc_tabela(token.lexema)
se no encontrou
ento incio
Insere_tabela(token.lexema,procedimento,nvel, rtulo)
{guarda na TabSimb}
Gera(rotulo,NULL,
,
)
{CALL ir buscar este rtulo na TabSimb}
rotulo:= rotulo+1
Lxico(token)
se token.simbolo = sponto_vrgula
ento Analisa_bloco
seno ERRO
fim
seno ERRO
fim
seno ERRO
DESEMPILHA OU VOLTA NVEL
fim
Algoritmo Analisa_ declarao_funo <declarao de funo>
incio
Lxico(token)
nvel := L (marca ou novo galho)
se token.smbolo = sidentificador
ento incio
pesquisa_declfunc_tabela(token.lexema)
se no encontrou
ento incio
Insere_tabela(token.lexema,,nvel,rtulo)
Lxico(token)
se token.smbolo = sdoispontos
ento incio
Lxico(token)
se (token.smbolo = Sinteiro) ou
(token.smbolo = Sbooleano)
ento incio
se (token.smbolo = Sinteger)
ento TABSIMB[pc].tipo:=
funo inteiro
seno TABSIMB[pc].tipo:=
funo boolean
Lxico(token)
se token.smbolo = sponto_vrgula
ento Analisa_bloco
fim
seno ERRO
fim
seno ERRO
fim
seno ERRO
fim
seno ERRO
DESEMPILHA OU VOLTA NVEL
fim

Ricardo Lus de Freitas

Notas de Aula - Compiladores 80

Algoritmo Analisa_expresso <expresso>


incio
Analisa_expresso_simples
se (token.simbolo = (smaior ou smaiorig ou sig
ou smenor ou smenorig ou sdif))
ento inicio
Lxico(token)
Analisa_expresso_simples
fim
fim
Algoritmo Analisa_expresso_simples <expresso simples>
incio
se (token.simbolo = smais) ou (token.simbolo = smenos)
ento Lxico(token)
Analisa_termo
enquanto ((token.simbolo = smais) ou (token.simbolo = smenos) ou
(token.simbolo = sou))
faa inicio
Lxico(token)
Analisa_termo
fim
fim
Algoritmo Analisa_termo <termo>
incio
Analisa_fator
enquanto ((token.simbolo = smult) ou (token.simbolo = sdiv) ou
(token.simbolo = se))
ento incio
Lxico(token)
Analisa_fator
fim
fim
Algoritmo Analisa_fator <fator>
Incio
Se token.simbolo = sidentificador
(* Varivel ou Funo*)
Ento inicio
Se pesquisa_tabela(token.lexema,nvel,ind)
Ento Se (TabSimb[ind].tipo = funo inteiro) ou
(TabSimb[ind].tipo = funo booleano)
Ento Analisa_chamada_funo
Seno Lxico(token)
Seno ERRO
Fim
Seno Se (token.simbolo = snumero)
(*Nmero*)
Ento Lxico(token)
Seno Se token.smbolo = snao
(*NAO*)
Ento incio
Lxico(token)
Analisa_fator
Fim
Seno Se token.simbolo = sabre_parenteses
(* expresso entre parenteses *)

Notas de Aula - Compiladores 81

Ricardo Lus de Freitas

Fim

Ento incio
Lxico(token)
Analisa_expresso(token)
Se token.simbolo = sfecha_parenteses
Ento Lxico(token)
Seno ERRO
Fim
Seno Se (token.lexema = verdadeiro) ou
(token.lexema = falso)
Ento Lxico(token)
Seno ERRO

Exerccio:
Elaborar os algoritmos para os seguinte procedimentos:
Analisa Chamada de Procedimento
Analisa Chamada de Funo

Ricardo Lus de Freitas

Notas de Aula - Compiladores 82

7 Analisador Semntico
At agora a preocupao foi em analisar sintaticamente os programas-fonte de
acordo com as suas respectivas gramticas, considerando que um programa-fonte fosse
simplesmente uma sequncia de caracteres. Entretanto, para gerar corretamente os cdigosobjeto de um programa-fonte, um compilador deve ser capaz de reconhecer os tipos de
smbolos (por exemplo, se eles so do tipo inteiro, do tipo ponto flutuante ou de um novo
tipo construdo) e a consistncia do seu uso (por exemplo, o operador aritmtico mod em
Pascal ou o operador /).
Os programas em linguagem de alto nvel contm normalmente uma seo de
declarao de dados (identificadores) e uma de comandos e expresses propriamente ditos.
A consistncia do uso de um identificador na seo de comandos depende da sua declarao
(explcita ou implcita). Este tipo de dependncia s pode ser satisfatoriamente contemplado
por uma gramtica sensitiva ao contexto. Entretanto, existem poucos resultados prticos que
formalizam e processam, de forma satisfatria, as gramticas sensitivas.

7.1 Gramtica com Atributos


Uma soluo prtica comumente adotada para a especificao da semntica de uma
linguagem consiste em atribuir s produes da sua gramtica as aes semnticas, que
qualificam semanticamente os smbolos que aparecem na rvore sinttica de uma
sentena. Essa gramtica estendida conhecida como gramtica com atributos. A principal
funo de uma gramtica com atributos incluir restries semnticas s construes
sintaticamente corretas.
As aes semnticas estabelecem a dependncia semntica entre todos os smbolos
de uma gramtica. Atravs delas, pode-se propagar os valores conhecidos de atributos pela
rvore sinttica e determinar os desconhecidos. Com uma arvore sinttica com os atributos,
e possvel controlar em muitas situaes a consistncia semntica.
Um analisador descendente apropriado para incluir este mecanismo de atribuio
de valores para os identificadores. Normalmente, os valores dos atributos de cada smbolo
so identificados e armazenados numa tabela de smbolos durante o processo de anlise
sinttica. A partir da o analisador semntico usa as informaes existentes na tabela para
validar os tipos de dados. Em muitos compiladores, o analisador semntico considerado
como parte do analisador sinttico, porque a construo da rvore sinttica e a inferncia
dos valores dos atributos de seus smbolos ocorrem paralelamente.

7.2 Tabela de Smbolos


Como visto anteriormente, a tabela de smbolos guarda a definio dos tipos de
dados construdos pelos usurios. Em linguagens que suportam o uso de um mesmo nome
simblico em diferentes nveis de blocos de programas, os compiladores usam a tabela de
smbolos para distinguir o escopo de validade de cada smbolo. Outra utilidade da tabela de
smbolos durante o processo de anlise semntica e reconhecer o escopo para o qual um
smbolo ativado. Em programas escritos em linguagem estruturada, como Pascal, o
conceito de escopo de validade ou nvel lxico importante, porque para estas linguagens
um nome no precisa ter uma entrada nica na tabela de smbolos. admissvel o uso de
um mesmo nome em nveis distintos com distintas interpretaes semnticas.
Os outros atributos associados a um smbolo dependem da categoria dele. Por
exemplo, ao nome de um procedimento deve-se especificar ainda o nmero de parmetros

Ricardo Lus de Freitas

Notas de Aula - Compiladores 83

formais, tipo, mecanismo de passagem de cada parmetro e, dependendo da linguagem, o


tipo de dado retornado; e ao nome de uma varivel precisa-se acrescentar o seu tipo de
dado. Devido a diversidade do nmero de atributos associados a cada smbolo, comum
utilizar em implementaes o mecanismo de referncia para atributos cuja quantidade e cujo
comprimento so variveis.
A informao sobre a categoria de smbolos na tabela usada pelo analisador
semntico para, por exemplo:
evitar que o nome de uma varivel seja utilizado no contexto do nome de uma subrotina a ser chamada;
evitar que o nome de um procedimento aparea no lado esquerdo do operador de
atribuio ou numa expresso sem a sua lista de argumentos;
evitar que um numeral seja colocado no lado esquerdo do operador de atribuio;
otimizar o uso de memria atribuindo a todas as constantes idnticas um mesmo
endereo.
As tabelas de smbolo provm ainda um campo para o atributo <endereo> de cada
smbolo. Este endereo pode ser textual ou pode ser uma posio de memria, dependendo
dos cdigos intermedirios serem gerados
Linguagens que suportam a construo de novos tipos de dados, como Pascal e C,
requerem que os compiladores armazenem na tabela de smbolos os nomes dos novos tipos
construdos e os seus componentes. Com isso, o analisador semntico pode validar
facilmente as referncias aos campos de uma varivel do tipo construdo. A medida que o
analisador sinttico reconhece um smbolo, o analisador semntico verifica se o smbolo j
est na tabela de smbolos. Se no estiver, ele armazenado. Caso contrrio, o analisador
semntico procura verificar a compatibilidade entre os atributos do smbolo inserido e a
forma corrente do seu uso. A complexidade de pesquisa na tabela de smbolos um fator
importante para analisar o desempenho de um compilador.

7.3 Erros Semnticos no CSD


A anlise semntica no compilador CSD ser composta basicamente das seguintes
tarefas:
1) Verificao da ocorrncia da duplicidade na declarao de um identificador (nome
do programa, procedimento, funo ou varivel). Sempre que for detectado um novo
identificador, deve ser feita uma busca para verificar as seguintes possibilidades:
a) se ele for uma varivel verificar se j no existe outra varivel visvel1 de mesmo
nome no mesmo nvel de declarao e verificar se j no existe outro identificador
de mesmo nome (que no seja uma varivel) em qualquer nvel inferior ou igual ao
da varivel agora analisada.
b) Se for o nome de um procedimento ou funo verificar se j no existe um outro
identificador visvel de qualquer tipo em nvel igual ao inferior ao agora analisado.
2) Verificao de uso de identificadores no declarados. Sempre que for detectado um
identificador, verificar se ele foi declarado (est visvel na tabela de smbolos) e
1

Por visvel considera-se como aquele que pode ser encontrado na Tabela de Smbolos atual.

Ricardo Lus de Freitas

Notas de Aula - Compiladores 84

compatvel com o uso (exemplo: varivel usada que existe como nome de programa ou
de procedimento na tabela de smbolos deve dar erro).
3) Verificao de compatibilidade de tipos. Sempre que ocorrer um comando de
atribuio, verificar se a expresso tem o mesmo tipo da varivel ou funo que a
recebe.
4) Verificao dos comandos escreva e leia.
5) Verificao de chamadas de procedimento e funo.
6) Verificao dos operadores unrios , + , nao.
fcil perceber que as chamadas para o analisador semntico no passam de linhas de
comandos a serem inseridos no corpo do analisador sinttico, nos locais apropriados.
Vale lembrar que a Linguagem LPD no permite a passagem de parmetros nos
procedimentos e funes. Caso isso fosse permitido, ento deveriamos tambm verificar a
consistncia no nmero de argumentos e parmetros, bem como sua compatibilidade de
tipos.

Ricardo Lus de Freitas

Notas de Aula - Compiladores 85

8 Mquina Virtual e Gerao de Cdigo


Esta seo foi retirada do livro do Kowaltowski (vide bibliografia), o qual, embora
normalmente com a edio esgotada, recomendo fortemente aos alunos. As nicas
diferenas residem na nomenclatura adotada.
O nosso objetivo final construir um compilador para LPD, isto , construir um
programa que traduz um programa-fonte em LPD para um programa-objeto em linguagem
de mquina de um dado computador. Antes de empreender a tarefa de escrever um
compilador, devemos estabelecer precisamente a traduo que corresponde a cada
construo de LPD. Entretanto, traduzir para a linguagem de mquina de um computador
real , em geral, uma tarefa muito trabalhosa. As linguagens de mquina tm muitas
caractersticas com as quais teramos que nos preocupar, perdendo de vista a
correspondncia entre as construes do programa-fonte e a sua traduo. Ao invs de
traduzir, ento, os programas em LPD para a linguagem de mquina de um computador
real, definiremos uma mquina hipottica, mais conveniente para nossas finalidades. Esta
abordagem muito comum na implementao de linguagens de programao.
Os prximos tpicos so dedicados, ento, ao desenvolvimento de um computador
hipottico que chamaremos de MVD (Mquina Virtual Didtica). A mesma sigla ser usada
para denotar a linguagem de montagem desta mquina. O desenvolvimento ser gradual,
acompanhando a discusso de vrios mecanismos de LPD. Frequentemente, seremos
obrigados a rever as decises j tomadas sobre o funcionamento da MVD, a fim de
acomodar um mecanismo, Desta maneira ficaro mais claras as razes para as vrias
caractersticas da MVD, e a sua relao com as construes de LPD.

8.1 Caractersticas Gerais da MVD


Descreveremos nesta seo a estrutura bsica da MVD, que bastante simples.
Como veremos nas sees subsequentes, o repertrio de instrues que complexo.
Uma deciso importante que tomaremos agora que a MVD dever ser uma
mquina a pilha. Isto muito natural, pois a LPD permite o uso de procedimentos
recursivos, e a recurso est intimamente ligada com o uso da pilha. As razes para esta
deciso ficaro mais claras, portanto, quando tratarmos de procedimentos recursivos.
A nossa mquina ter uma memria composta de trs regies:
-

A regio de programa P que conter as instrues da MVD. O formato exato de cada


instruo irrelevante para a nossa discusso.
A regio da pilha de dados M que conter os valores manipulados pelas instrues da
MVD. Suporemos que esta regio compe-se de palavras que podem conter valores
inteiros ou ento indefinidos.

Suporemos que cada uma das trs regies tm palavras numeradas com 0, 1, 2,..., e
no nos preocuparemos com as limitaes de tamanho de cada regio, nem de cada palavra.
A MVD ter dois registradores especiais que sero usados para descrever o efeito
das instrues:
-

O registrador do programa i conter o endereo da prxima instruo a ser executada,


que ser, portanto, P [i].
O registrador s indicar o elemento no topo da pilha cujo valor ser dado, portanto, por
M [s].

Notas de Aula - Compiladores 86

Ricardo Lus de Freitas

Uma vez que o programa da MVD est carregado na regio P, e os registradores tm


os seus valores iniciais, o funcionamento da mquina muito simples. As instrues
indicadas pelo registrador i so executadas at que seja encontrada a instruo de parada,
ou ocorra algum erro. A execuo de cada instruo incrementa de um o valor de i, exceto
as instrues que envolvem desvios.
Passaremos a desenvolver nas sees seguintes o repertrio de instrues da MVD
motivado pelas vrias construes da LPD.

8.2 Avaliao de Expresses


Suponhamos que E uma expresso em LPD da forma E = E1 E2, onde E1 eE2
so duas expresses mais simples, e um operador binrio como +, -, *, <, =, etc. A
expresso E deve ser avaliada calculando-se em primeiro lugar os valores de E1 e E2 e
aplicando-se, em seguida, a operao correspondente a . Este clculo pode ser
implementado de vrias maneiras, guardando-se os valores intermedirios de E1 e E2 em
localizaes de memria especiais. Numa mquina como a MVD, uma maneira conveniente
guardar estes valores intermedirios na prpria pilha M. Assim, supondo que os valores v1
e v2 das expresses E1 e E2 so calculados de maneira semelhante, a pilha ter as
configuraes sucessivas indicadas na Figura 8.1. Denotados por m o endereo do topo da
pilha antes de iniciar a avaliao da expresso E, e por v, o valor final calculado. O smbolo
? Denota valores irrelevantes que j estavam na pilha.
*
*
*

s->

*
*
*

m+2

m+1

s->

*
*
*

*
*
*

m+2

v1
?

s->

v2

m+2

m+1

v1

m+1

*
*
*
***
clculo de v 1

*
*
*

s->

*
*
*
***
clculo de v 2
Figura 8.1

v2

m+2

m+1

*
*
*
v=v1 * v2

Note-se que estamos resolvendo o problema de avaliar expresses de maneira


indutiva, supondo em primeiro lugar que sabemos resolv-lo para expresses mais simples,
isto , mais curtas, como E1 e E2. Assim, supusemos na Figura 8.1 que os valores v1 e v2
so calculados possivelmente em vrios passos, ficando no fim o valor correspondente no
topo da pilha, uma posio acima da posio inicial. A base desta induo corresponde s
expresses o mais simples possvel, isto , s constantes e variveis. Para estas, basta
colocar os seus valores respectivos no topo da pilha. Os operadores unrios sero tratados
de maneira anloga. O caso de chamadas de funes que devolvem resultados ser tratado
mais adiante, mas importante notar desde j que qualquer que seja a implementao destas
chamadas, o seu efeito dever ser sempre o de deixar o resultado final no topo da pilha, uma
posio acima da inicial.
Podemos concluir da discusso acima que a MVD deve possuir instrues que
carregam na pilha valores de constantes e de variveis, e outras que executam operaes
correspondentes aos operadores da LPD. Definiremos, portanto, uma srie de instrues

Ricardo Lus de Freitas

Notas de Aula - Compiladores 87

para a MVD, mas devemos notar que algumas dessas definies so provisrias, e sero
modificadas mais adiante. O efeito de cada instruo est descrito numa notao semelhante
da LPD, indicando as modificaes no estado dos registradores e da memria da MVD.
Omitimos nesta descrio a operao i:=i+1 que est implcita em todas as instrues,
exceto quando h desvio. Adotaremos, tambm, a conveno de representar os valores
booleanos por inteiros: verdadeiro por 1 e falso por 0.
LDC

(Carregar constante):
S:=s + 1 ; M [s]: = k
LDV n (Carregar valor):
S:=s+1 ; M[s]:=M[n]
ADD
(Somar):
M[s-1]:=M[s-1]+M[s]; s:=s-1
SUB
(Subtrair):
M[s-1]:=M[s-1]-M[s]; s:=s-1
MULT
(Multiplicar):
M[s-1]:=M[s-1]*M[s]; s:=s-1
DIVI
(Dividir):
M[s-1]:=M[s-1]div M[s]; s:=s-1
INV
(Inverter sinal):
M[s]:=-M[s]
AND
(Conjuno):
Se M [s-1]=1 e M[s]=1 ento M[s-1]:=1 seno M[s-1]:=0; S:=s-1
OR
(Disjuno):
Se M[s-1]=1 ou M[s]=1 ento M[s-1]:=1 seno M[s-1]:=0; s:=s-1
NEG
(Negao):
M[s]:=1-M[s]
CME
(Comparar menor):
Se M[s-1]<M[s] ento M[s-1]:=1 seno M[s-1]:=0; s:=s-1
CMA
(Comparar maior):
Se M[s-1] >M[s] ento M[s-1]:=1 seno M[s-1]:=0;s:=s-1
CEQ
(comparar igual):
Se M[s-1]=M[s] ento M[s-1]:=1 seno M[s-1]:=0;s:=s-1
CDIF
(Comparar desigual):
Se M[s-1] M[s] ento M[s-1]:=1 seno M[s-1]:=0; s:=s-1
CMEQ
(Comparar menor ou igual)
Se M[s-1] M[s] ento M[s-1]:=1 seno M[s-1]:=0;s:=s-1
CMAQ
(Comparar maior ou igual):
Se M[s-1] M[s] ento M[s-1]:=1 seno M[s-1]:=0; s:=s-1
k

Exemplo:
Consideremos a expresso a + (b div 9 - 3) * c, e suponhamos que os endereos
atribudos pelo compilador s variveis a, b e c so, respectivamente, 100, 102, e 99. Ento
o trecho do programa-objeto correspondente traduo desta expresso seria:
LDV
LDV
LDC
DIVI
LDC

100
102
9
3

Notas de Aula - Compiladores 88

Ricardo Lus de Freitas

SUB
LDV 99
MULT
ADD
Suponhamos que os valores armazenados nas posies 99, 100 e 102 da pilha so
2, 10 e 100, respectivamente, e que o registrador s contm o valor 104. As configuraes
sucessivas da pilha ao executar as instrues acima so dadas na Figura 8.2. Os valores
sucessivos de s esto indicados pelas flechas.
Uma observao interessante que o cdigo da MVD gerado para expresses est
diretamente ligado com a notao polonesa posfixa, que no caso da expresso do exemplo
acima seria ab9 div 3-c*+. Esta sequncia de smbolos deve ser comparada com a sequncia
de instrues da MVD desse exemplo.
*
*
*

*
*
*

*
*
*

*
*
*

*
*
*

107

106

100

100

11

105

10

10

10

10

104

103

100

102

100

100

100

100

101

10

100

10

10

10

10

-2

99

-2

-2

-2

-2

*
*
*

*
*
*

*
*
*

*
*
*

*
*
*
LDV 100

LDV 102

LDC 9

DIVI

LDC 3

Notas de Aula - Compiladores 89

Ricardo Lus de Freitas

*
*
*

*
*
*

*
*
*

*
*
*

*
*
*

-2

-2

107

-2

11

106

-16

-16

10

105

10

10

10

-6

104

103

100

102

100

100

100

100

101

10

100

10

10

10

10

-2

99

-2

-2

-2

-2

*
*
*

*
*
*

*
*
*

*
*
*

*
*
*
SUB

LDV 99

MULT

ADD

Figura 8.2

8.3 Comandos de Atribuio


Consideramos, por enquanto, apenas as atribuies a variveis simples da forma
V:=E, onde V o nome de uma varivel e E uma expresso que j sabemos traduzir. Uma
maneira simples de implementar este comando dada
por uma instruo de
armazenamento:
STR

(Armazenar valor):
M[n]:=M[s]; s:=s-1

Esta instruo seguir o cdigo-objeto que corresponde expresso E, e n ser o


endereo atribudo pelo compilador varivel V.

Ricardo Lus de Freitas

Notas de Aula - Compiladores 90

Exemplo:
Consideremos o comando a:=a+b*c e suponhamos que os endereos e os valores
destas variveis so os mesmos do exemplo anterior. O cdigo da MVD para este comando
ser:
LDV
LDV
LDV
MULT
ADD
STR

100
102
99
100

A Figura 8.3 apresenta as configuraes sucessivas da pilha ao ser executado este


cdigo-objeto.
Note-se que, devido maneira como definimos a instruo STR, o valor final do
registrador s, aps a execuo do cdigo-objeto correspondente a um comando de
atribuio, ser igual ao seu valor inicial. Esta uma propriedade importante que ser
verdadeira para qualquer comando em LPD, como verificaremos mais tarde. As nicas
excees so os comandos de desvio, e de chamada de procedimentos e funes quando
estes no retornam por causa de desvios.

8.4 Comandos Condicionais e Iterativos


Para implementar comandos condicionais definiremos duas instrues de desvio
para a MVD:
JMP

p (Desviar sempre):
i:=p
JMPF p (Desviar se falso):
Se M[s]=0 ento i:=p seno i:=i+1;
S:=s-1
Nestas instrues, p um nmero inteiro que indica um endereo de programa da
MVD. Nos exemplos que se seguem utilizaremos rtulos simblicos no lugar de nmeros.
Note-se que, haja ou no desvio, a instruo JMPF elimina o valor que foi testado, e que
est no topo da pilha.
Introduziremos, por convenincia, mais uma instruo que no estritamente
necessria, mas que simplifica o processo de traduo e que no tem nenhum efeito sobre a
execuo:
NULL (Nada):

Notas de Aula - Compiladores 91

Ricardo Lus de Freitas

*
*
*

*
*
*

*
*
*

*
*
*

107

-2

106

100

100

105

10

10

10

104

103

100

102

100

100

100

101

10

100

10

10

10

-2

99

-2

-2

-2

*
*
*

*
*
*

*
*
*

*
*
*
LDV 100

LDV 102

LDV 99

MULT

Notas de Aula - Compiladores 92

Ricardo Lus de Freitas

*
*
*

*
*
*

*
*
*

-2

107

-2

-2

-200

106

-200

-200

10

105

-190

-190

104

103

100

102

100

100

101

10

100

10

-190

-2

99

-2

-2

*
*
*

*
*
*

*
*
*
ADD

STR

100
Figura 8.3

Um comando condicional da forma se E entao C 1 senao C 2, onde E uma


expresso e C 1 e C2 so comandos, ser traduzido por:
*
*
*
JMPF l 1
*
*
*
JMP l 2
L 1 NULL
*
*
*
l 2 NULL

traduo de E

traduo de C 1

traduo de C 2

Ricardo Lus de Freitas

Notas de Aula - Compiladores 93

Caso o comando tenha a forma se E entao C, podemos traduzi-lo por:


*
*
*
JMPF l
*
*
*
l NULL

traduo de E

traduo de C

As mesmas instrues de desvio podem ser usadas para implementar comandos


repetitivos. No caso do comando enquanto E faca C teremos a traduo:
L 1 NULL
*
*
*
JMPF l 2
*
*
*
JMP l 1
L 2 NULL

traduo de E

traduo de C

fcil mostrar que estas tradues de comandos condicionais e repetitivos so tais


que, ao serem executadas, fazem com que o nvel final da pilha seja igual ao inicial.
Exemplo:
Indicaremos a seguir as tradues correspondentes a trs comandos em LPD:
1. se q entao a:=1 senao a:=2
LDV
JMPF
LDC
STR
JMP
L1 NULL
LDC
STR
L2 NULL

Q
L1
1
A
L2
2
A

2. se a>b entao q:=p e q


senao se a < 2*b entao p:=verdadeiro
senao q:=falso

Ricardo Lus de Freitas

Notas de Aula - Compiladores 94

LDV A
LDV B
CMA
JMPF L3
LDV P
LDV Q
AND
STR
Q
JMP L4
L3 NULL
LDV A
LDC 2
LDV B
MULT
CME
JMPF L5
LDC 1
STR
P
JMP L6
L5 NULL
LDC 0
STR
Q
L6 NULL
L4 NULL
3. enquanto s< =n faca s:=s+3*s
L7 NULL
LDV S
LDV N
CMEQ
JMPF L8
LDV S
LDC 3
LDV S
MULT
ADD
STR
S
JMP L7
L8 NULL

8.5 Comandos de Entrada e de Sada


Um comando da forma leia (v1) deve ler o prximo valor inteiro do arquivo de
entrada e atribu-lo varivel inteira v1. A fim de implementar o comando de leitura
definiremos a seguinte instruo para a MVD.
RD

(Leitura):
S:=s+1; M[s]:= prximo valor de entrada.

Notas de Aula - Compiladores 95

Ricardo Lus de Freitas

Usando esta instruo, podemos traduzir leia(v1) por:


RD
STR
V1
Onde V1 o endereo da varivel v1.
O comando de sada de LPD tem a forma escreva (v1), indicando que o valor da
varivel inteira v1 deve ser impresso no arquivo de sada. Definiremos ento a instruo:
PRN

(Impresso):
Imprimir M[s]; s:=s-1

Assim, o comando indicado acima pode ser traduzido por:


LDV
PRN

V1

Exemplo:
1. leia(a)
RD
STR

A (endereo de a)

2. escreva(x)
LDV
PRN

X (endereo de x)

8.6 Sub-Programas
A Figura 8.4 mostra um programa muito simples, sem procedimentos. Deveria ser
claro que, neste caso, o programa-objeto deveria reservar as cinco posies iniciais da pilha
para as variveis, e em seguida comear a executar as instrues
programa exemplo5;
var n, k: inteiro;
f1, f2, f3: inteiro;
incio
leia(n);
f1:=0; f2:=1;k:=1;
enquanto k<=n
faca incio
f3:=f1+f2;
f1:=f2; f2:=f3;
k:=k+1
fim;
escreva (n);
escreva (f1)
fim.
Figura 8.4

Notas de Aula - Compiladores 96

Ricardo Lus de Freitas

Que correspondem ao bloco de comandos. Definiremos ento duas instrues para a


MVD.
START
ALLOC m

(Iniciar programa principal):


S:=-1
(Alocar memria):
S:=s+m

A instruo START ser sempre a primeira instruo de um programa-fonte. Uma


declarao da forma v1, v2,...vm: tipo ser traduzida por ALLOC m. Note-se que o
compilador pode calcular facilmente os endereos das variveis v1, v2, ...,vm que devero
ser 0, 1,...,m-1, respectivamente (quando esta a primeira declarao de variveis).
Para completar a traduo de programas, definiremos uma instruo de trmino de
execuo:
HLT

(Parar):
Pra a execuo da MVD

Exemplo:
Indicamos a seguir o programa-objeto que resulta da traduo do programa da
Figura 8.4. Fragmentos do programa-fonte so usados como comentrios a fim de tornar
mais clara a traduo:

L1

START
ALLOC 2
ALLOC 3
RD
STR
0
LDC
0
STR
2
LDC
1
STR
3
LDC
1
STR
1
NULL
LDV 1
LDV 0
CMEQ
JMPF L2
LDV 2
LDV 3
ADD
STR
4
LDV 3
STR
2
LDV 4
STR
3
LDV 1
LDC
1
ADD
STR
1

programa
var n, k
fl, f2, f3
leia (n)
fl:=0
f2:=1
k:=1
enquanto
k<=n
faca

f3:=f1+f2
f1:=f2
f2:=f3

k:=k+1

Notas de Aula - Compiladores 97

Ricardo Lus de Freitas

L2

JMP
L1
NULL
LDV 0
PRN
escreva (n)
LDV 2
PRN
escreva (f1)
DALLOC 3
DALLOC 2
HLT
fim.

Note-se que a traduo de um comando composto delimitado pelos smbolos incio


e fim obtida pela justaposio das tradues dos comandos componentes.
Uma observao importante sobre o nosso sistema de execuo que ele muito
complicado para o caso de considerar apenas os programas sem procedimentos. fcil
perceber que, neste caso, o compilador pode determinar os endereos de todas as variveis e
de todas as posies intermedirias na pilha. Poderamos ter definido, portanto, instrues
mais simples para MVD que fizessem acesso a localizaes de memria sem a manipulao
explcita da pilha. Esta observao mantm-se tambm verdadeira para programas que tm
procedimentos mas que no so recursivos.

8.7 Procedimentos sem Parmetros


Consideremos o programa indicado na Figura 8.5, em que p um procedimento
recursivo sem parmetros. Supondo que o valor lido pelo comando de entrada seja 4,
obtm-se o diagrama de execuo indicado na Figura 8.6. O nmero de ativaes do
procedimento p depender, em geral, do valor de n que foi lido, no sendo possvel
determinar-se um limite. Consequentemente, num certo instante de execuo, podero
existir vrias instanciaes da varivel local z de p. Por outro lado, as instrues da MVD
que definimos at agora usam endereos fixos de memria, como por exemplo LDV 3 ou
STR 7. Isto significa que, ao traduzir expresses que envolvem a varivel z, o compilador
dever usar um endereo fixo para esta varivel. Entretanto, as vrias instanciaes no
podem ocupar a mesma posio de memria.
Programa exemplo6;
Var x, y: inteiro;
Procedimento p;
Var z: inteiro;
Incio
Z:= x; x:=x-1;
Se z>1 entao p (1)
senao y:=1;
Y:=y*z
Fim { p };
Incio
Leia(x);
p; (2)
Escreva (y);
Escreva (x)
Fim.
Figura 8.5

Notas de Aula - Compiladores 98

Ricardo Lus de Freitas

X: 4,3,2,1,0

y: 1,1,2,6,24

P/ 2

z:4

P/ 1

z:3

P/1

z:2
P/1

z:1

Figura 8.6

O problema pode ser resolvido notando-se que a criao e a destruio das


instanciaes de z seguem uma disciplina de pilha, isto , a ltima instncia a ser alocada
a primeira a desaparecer. Um outro fato, que podemos concluir observando as flechas
estticas da Figura 8.6, num dado instante o programa s tem acesso instncia de z
criada por ltimo. O problema de endereamento ser resolvido, ento, usando-se a prpria
pilha. Sempre que o procedimento p for chamado, o valor anterior de z ser
temporariamente guardado no topo da pilha, liberando a posio fixa, atribuda pelo
compilador varivel z, para a prxima instncia. O valor anterior de z ser restaurado
antes de executar o retorno. A Figura 8.7 indica as configuraes sucessivas da pilha para
cada chamada do procedimento p. Aps cada retorno, a configurao prvia ser restaurada.
Os smbolos z1,z2,z3 e z4 denotam os valores das instanciaes sucessivas de z.
No difcil ver que esta soluo aplica-se no caso geral de procedimentos sem
parmetros. Cada procedimento alocar a sua memria, salvando no topo da pilha o
contedo prvio das posies de memria correspondentes s suas variveis locais. Estes
valores sero restaurados antes de executarem-se os retornos. A fim de tornar o processo de
traduo uniforme, o programa inteiro tambm ser tratado como um procedimento.
Redefiniremos, ento, a intruo ALLOC, e introduziremos uma nova instruo
DALLOC:
ALLOC m,n
(Alocar memria):
Para k:=0 at n-1 faa
{s:=s+1;M[s]:=M[m+k]}
DALLOC m,n

(Desalocar memria):
Para k:=n-1 at 0 faa

Notas de Aula - Compiladores 99

Ricardo Lus de Freitas

{M[m+k]:=M[s];s:=s-1}

z3
*
*
*

z1

z2

z2

*
*
*

*
*
*

z1

z1

z1

*
*
*

*
*
*

*
*
*

z2

z3

z4

Figura 8.7

Um outro problema a ser resolvido o prprio mecanismo de chamada de


procedimentos. A nica informao necessria para executar o retorno, neste estgio da
MVD, o endereo da instruo qual a execuo deve retornar. O lugar natural para
guardar esta informao a prpria pilha. Definiremos, portanto, as instrues:
CALL p (Chamar procedimento ou funo):
S:=s+1; M[s]:=i+1;i:=p
RETURN (Retornar de procedimento):
I:=M[s]; s:=s-1
Exerccio: fazer retorno de funo: RETURNF.
Note-se que a instruo RETURN sempre encontra a informao correta no topo da pilha.

Notas de Aula - Compiladores 100

Ricardo Lus de Freitas

Exemplo:
O programa da Figura 8.5 produz a seguinte traduo:

L2

L3
L4

L1

START
ALLOC 0,2
JMP
L1
NULL
ALLOC 2,1
LDV
0
STR
2
LDV
0
LDC
1
SUB
STR
0
LDV
2
LDC
1
CMA
JMPF L3
CALL L2
JMP
L4
NULL
LDC
1
STR
1
NULL
LDV
1
LDV
2
MULT
STR
1
DALLOC 2,1
RETURN
NULL
RD
STR
0
CALL L2
LDV
0
PRN
LDV
1
PRN
DALLOC 0,2
HLT

programa
var x,y
procedimento p
var z
z:=x

x:=x-1
se z>1
entao
p
senao
y:=1

y:=y*z
fim

leia(x)
p
escreva (x)
escreva (y)
fim.

Note-se o uso da instruo JMP L1 para pular o cdigo do procedimento p ao


iniciar-se a execuo. Desta maneira, a traduo segue a mesma ordem do programa-fonte.
Deve-se notar, tambm, a maneira como o compilador atribui endereos s variveis x, y e
z, que so, no caso, 0, 1 e 2, respectivamente.
Uma regra geral que pode ser adotada para atribuir endereos a variveis, por quanto
da ausncia de parmetros : se um procedimento q, que tem k variveis locais, est
diretamente encaixado dentro de um procedimento p cuja ltima varivel local recebeu

Ricardo Lus de Freitas

Notas de Aula - Compiladores 101

endereo m, ento as variveis de q recebem os endereos m+1, m+2,..., m+k. Suporemos


que o programa principal um procedimento encaixado num outro tal que m=-1;
consequentemente, as k variveis do programa principal tm endereos 0, 1,...,k-1.
importante notar que, tambm no caso de comandos que so chamadas de
procedimentos, vale a propriedade enunciada anteriormente, ou seja, de que a execuo das
instrues do programa-objeto correspondentes mantm o nvel da pilha final (aps o
retorno) igual ao inicial (antes da chamada).

Ricardo Lus de Freitas

Notas de Aula - Compiladores 102

9 Bibliografia
Aho,A.V.; Sethi,R.; Ullman,J.D. Compiladores: Princpios, Tcnicas e Ferramentas Livros Tcnicos e Cientficos.
Hopcroft,J.E; Ullman,J.D. Formal Languages end their relation to Automata - AddisonWesley Series.
Hopcroft,J.E; Ullman,J.D.
Introduction do Automata Theory, Languages and
Computation - Addison-Wesley Series.
Kowaltowski,T. Implementao de Linguagens de Programao - Ed. Guanabara Dois.

S-ar putea să vă placă și