ndice
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
Conceitos Formais.............................................................................................. 1
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
2
2.1 2.2
3
3.1 3.2 3.3 3.4 3.5 3.6 3.7
4
4.1 4.2 4.3 4.4 4.5
5
5.1 5.2 5.3 5.4 5.5
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
7
7.1 7.2 7.3
8
8.1 8.2 8.3 8.4 8.5 8.6 8.7
Bibliografia..................................................................................................... 102
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.
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* = 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. V+ =
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
Exerccio Seja {a,b,c} |aaa| = |aabbb|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 = V1 = 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= zz = z2 = z5 = z0 = Escrever V* e V+ V2 = V3 =
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.). V t 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 ?
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
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 ,
* G
* G
+ G
, respectivamente.
*
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 S S ab (menor cadeia) aSb aabb aSb aaSbb aaabbb
G2 = ({S}, {a,b}, P, S) P: S aAb A ab||S S S S aAb aAb aAb ab (menor cadeia) aabb aSb aaAbb aaabbb
Exerccio Escreva a gramtica para a seguinte linguagem. L(G) = {w/w {00, 11}* e w = (0011)n , n 0}
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) L(G) = {a, b, c}
P:
A BC | a Bb Cc
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 }
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)
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 OP P zzz O PNMx O zw Nz Mz b) G = ({M,N,O,P}, {x,,z,w}, P, O) P OzP PO O PNMx OzO zW NM z d) G = ({M,N,O,P}, {x,y,z,w}, P, O) O Mx | Nw Nz My
P:
P:
P:
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) P: S Aa Ac A Ba B abc
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: SA A A0 A 1A A0 10 S S0 S A1 A 0A0 A1 b) P: S 1S S 1A A 0A A0
c) P:
6) Mostre a linguagem gerada pelas produes da GLC abaixo e construa uma GR equivalente. G = ({A, B, S}, {a, b}, P, S) S AB A aA A aBB B Bb Bb
P:
(E+E) * a
(E + b) * a
(a + b) * a rvore 2
rvore 1 E E ( E ) E + E a b * E a
A mesma que 1
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.
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 + E a 2) E E+E E E a + E E * E b a b E+E*E a+E*E a+b*E a+b*a * E a E+E*E a+E*E a+b*E a+b*a
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 d
, para a
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)
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
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 a S a S a S S a S S a 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 S a S a a S a NO AMBGUA S
AMBGUA
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
P: p p E T F a b ( ) + * p+
E V
T V V
F V
a V
b V
( V
p+ E T F a b ( ) + *
E V
T V V
F V V
a V V V
b V V V
( V V V
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 2.2) X
*
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
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 {, , ...} {||}*
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 ;
<lista de identificadores> ::= <identificador> {, <identificador>} Identificador , <tipo> ::= inteiro / booleano inteiro boleano <comandos> ::= incio <comando> {; comando} fim incio comando ; fim
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 : 1 Exerccio : 1) w = anbm , n 1 , m 1} 1 a 2 a b 3 b a b b 2
2) (a b)m m 1 a b a
Tabela de Transio (do exerccio 2) Estado Anterior 1 1 2 2 3 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 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 a a) Monte a tabela de transio correspondente. b) Monte as regras de transio correspondente. b c c Smbolo de Entrada a b a b a b Estado Posterior 2 erro erro 3 2 erro Smbolo de Entrada a b a b a b Estado Posterior 2 erro 2 3 erro 3
c) Escreva 5 sentenas. d) aabaaac aceita pelo autmato ? a) Tabela de Transio Estado Anterior A A A B B B C C 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 S0 0 1 S1 1 b S0 a S1 b Smbolo de Entrada a b c a b c a b c Estado Posterior A B erro A B C erro erro C
0 a) L(G) ? Cadeia no vazia terminado com 1 b) 00111 aceito ? Sim 00110 aceito ? No
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:
S0
S1
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 c A S B e S a B d A b
a B A d b
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
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 ?
cada passagem. Como exemplo tem-se que na primeira passagem seria feita a anlise lxica, 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.
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 >
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 : { }
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.
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 INCIO FIM NAO ENTAO BOOLEANO FALSO OU VERDADEIRO DIV FUNCAO PROCEDIMENTO VAR
2.2.4 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; . .
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:
. . 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 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.
tambm
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:
. . 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.
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:
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 + Multiplicao Diviso Adio Subtrao representados na linguagem LPD pelos
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:
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.
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}
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;
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;
Tratador de erros
Gerador de cdigo
Programa alvo
Do ponto de vista da implementao do compilador, o analisador lxico 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 lxico 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 lxico. Extrao e classificao de tomos. Eliminao de delimitadores e comentrios. Identificao de palavras reservadas. Recuperao de erros
Analisador lxico Analisador sinttico
Programa fonte
Tabela de smbolos
Programa Fonte
token
Analisador lxico Analisador sinttico
rvore
Resto da interface de vanguarda
representao intermediria
gramatical
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 lxico. Ativao de rotinas de anlise semntica. Ativao de rotinas de sntese do cdigo objeto.
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
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).
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
4 Analisador Lxico
O analisador lxico 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 lxico seja uma sub-rotina ou uma co-rotina do analisador sinttico. Ao receber do analisador sinttico um comando obter o prximo token, o analisador lxico l os caracteres de entrada at que possa identificar o prximo token.
Token Analisador Lxico Obter prximo token Analisador Sinttico
Programa Fonte
Tabela de smbolos Figura 4.1: Interao do analisador lxico com o analisador sinttico.
Como o analisador lxico 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 lxico 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 lxico 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 lxica v se desenvolvendo. Algumas vezes, os analisadores lxicos so divididos em duas fases em cascata, a primeira chamada de varredura(scanning) e a segunda de anlise lxica. O scanner responsvel por realizar tarefas simples, enquanto o analisador lxico propriamente dito realiza as tarefas mais complexas. Por exemplo, um compilador Fortran poderia usar um scanner para eliminar os espaos da entrada.
projetando uma nova linguagem, separar as convenes lxicas das sintticas pode levar a um projeto global de linguagem mais claro. 2. A eficincia do compilador melhorada. Um analisador lxico 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 lxico. A representao de smbolos especiais ou no-padro, tais como ^ em Pascal, pode ser isolada no analisador lxico.
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 lxico 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;
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
O analisador lxico 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.
pnico. Removemos sucessivos caracteres da entrada remanescente at que o analisador lxico 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.
TOKEN
programa incio fim procedimento funcao se entao senao enquanto faca := escreva leia var inteiro booleano identificador nmero . ; , ( ) > >= = < <= <> + * div e ou nao :
Lexema
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
Smbolo
Por exemplo, na anlise lxica da sentena abaixo se contador > 10 { exemplo de comentrio } entao escreva (contador) senao escreva (x); gerada a seguinte lista de tokens:
Lexema
se contador > 10 entao escreva ( contador ) senao escreva ( x ) ;
Smbolo
sse sidentificador smaior snmero sentao sescreva sabre_parnteses sidentificador sfecha_parnteses ssenao 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 Lxico. 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
Os Algoritmos do Analisador Lxico no CSD Uma vez defina a estrutura de dados do analisador lxico, possvel descrever seu algoritmo bsico. No nvel mais alto de abstrao, o funcionamento do analisador lxico pode ser definido pelo algoritmo:
Algoritmo Analisador Lxico (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 Lxico (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.
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
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
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 lxica 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.
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.
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 .
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.
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.
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.
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.
gramatical
intermediria
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 lxico. 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.
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.
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 F T b F a
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.
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.
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 lxico 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.
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.
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.
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
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.
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 se token.smbolo = Svrgula ento incio lxico(token) se token.simbolo = Sdoispontos ento ERRO fim seno ERRO fim seno ERRO seno ERRO at que (token.smbolo = sdoispontos) Lxico(token) Analisa_Tipo fim
Algoritmo Analisa_Tipo <tipo> incio se (token.smbolo (sinteiro ou 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 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 Lxico(token)
Analisa_comando_simples Gera( ,JMP,auxrot1, Gera(auxrot2,NULL, , fim seno ERRO fim 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 inteiro incio auxrot:= rotulo GERA( ,JMP,rotulo, ) {Salta sub-rotinas} rotulo:= rotulo + 1 enquanto (token.simbolo = sprocedimento) ou (token.simbolo = sfuno) faa incio token.simbolo = sprocedimento) ento analisa_declarao_procedimento seno analisa_ declarao_funo se token.smbolo = sponto-vrgula ento Lxico(token) seno ERRO fim 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 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
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 *)
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
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.
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.
Por visvel considera-se como aquele que pode ser encontrado na Tabela de Smbolos atual.
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.
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].
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.
*** clculo de v 1
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
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 k (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 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
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 105 * * * ? ? 10 * * * ? 100 10 * * * 9 100 10 * * * 9 11 10
? ? 100 ? 10 -2 * * *
? ? 100 ? 10 -2 * * *
? ? 100 ? 10 -2 * * * DIVI
? ? 100 ? 10 -2 * * * LDC 3
LDV 100
* * * 3 107
* * * 3
* * * -2
* * * -2
* * * -2
11 10 ? ? 100 ? 10
8 10 ? ? 100 ? 10
8 10 ? ? 100 ? 10
-16 10 ? ? 100 ? 10
-16 -6 ? ? 100 ? 10
-2 * * *
99
-2 * * *
-2 * * * ADD
-2 * * *
SUB
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.
* * * ? ? 10
* * * ? 100 10
* * * -2 100 10
? ? 100
? ? 100
? ? 100
? ? 100
? 10 -2 * * *
101 100 99
? 10 -2 * * *
? 10 -2 * * * MULT
LDV 100
* * * -2 107
* * * -2
* * * -2
-200 10 ? ? 100 ? 10 -2 * * *
ADD
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
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
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
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:
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; Fl f2 f3; inteiro; Incio Leia(n); fl:=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
MVD.
Que correspondem ao bloco de comandos. Definiremos ento duas instrues para a 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: 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
L1
k:=k+1
L2
JMP L1 NULL LDV 0 PRN escreva (n) LDV 2 PRN escreva (f1) DALLOC 2 DALLOC 3 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.
X: 4,3,2,1,0
y: 1,1,2,6,24
P/ 2
z:4
P/ 1
z:3
P/1 P/1
z:2 z:1
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
Figura 8.6
{M[m+k]:=M[s];s:=s-1}
z3 * * *
z2 * * *
z2 * * *
z1 * * *
z1 * * *
z1 * * *
z1
z2
z3
z4
y x
y x
y x Figura 8.7
y x
y x
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.
Exemplo: O programa da Figura 8.5 produz a seguinte traduo: 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
L2
L3 L4
y:=y*z fim
L1
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
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).
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.