Você está na página 1de 1

Editado por Aleardo Manacero Jr.

Captulo 4 - Anlise semntica


1. 2. 3. 4. 5. Gramticas de atributo Atributos sintetizados e herdados Avaliao de atributos atravs de no-terminais Manipulao da tabela de smbolos Tipo e escopo de variveis

Neste captulo estudaremos como feita a anlise semntica dentro de um compilador. Porm, antes precisamos lembrar que para o nosso objeto de estudo a anlise semntica consiste basicamente na verificao de tipos, isto , em verificar se as variveis e constantes que aparecem em cada expresso possuem tipos compatveis entre si. Uma outra atividade realizada na anlise semntica a verificao do escopo de uma varivel, isto , na determinao dos pontos do programa para os quais uma dada varivel est definida e portanto seria visvel para as demais variveis do programa. Isso exige uma gramtica sensvel ao contexto para determinar os contextos em que cada identificador est sendo utilizado. Entretanto, uma gramtica desta categoria exige (segundo Chomsky) um autmato limitado, o qual impraticvel de ser implementado. A soluo para este problema a adoo de restries na anlise semntica, fazendo com que a mesma possa ser feita a partir da anlise sinttica com apenas alguns acrscimos feitos gramtica livre de contexto. A seguir apresentamos as gramticas de atributo, que na realidade podem ser interpretadas como uma gramtica livre de contexto na qual so acrescentadas regras para fazer restries quanto a compatibilidade de tipos e determinao de escopo. O uso de atributos para fazer a anlise semntica em conjunto com a anlise sinttica vantajoso pois podemos ento construir o analisador semntico de forma automtica. Na realidade, se usarmos esta abordagem estaremos construindo um analisador sinttico que pode tambm fazer a anlise semntica do programa em compilao.

4.1 Gramticas de atributo


Uma forma de expandir uma gramtica livre de contexto atravs do uso de atributos dados aos elementos da rvore de derivao. Isto nos leva s gramticas de atributos, em que podemos associar valores e restries aos smbolos e produes da gramtica. Gramticas de atributo so definidas atravs da tripla A = (G,V,F), em que G uma gramtica livre de contexto, V um conjunto de atributos e F um conjunto de predicados ou asseres. Cada atributo est associado a apenas um nico smbolo terminal ou noterminal, enquanto que cada predicado ou assero est associada a apenas uma das produes da gramtica G. As asseres so as diretivas para a atribuio de valores aos atributos. Uma assero pode ser de dois tipos, as funes de avaliao e os predicados. A diferena entre essas variantes no to fundamental, do ponto de vista formal da gramtica. Apenas as diferenciamos para clarificar quando que o valor de um atributo est sendo definido de forma exata e quando est sendo apenas caracterizado. Assim, quando temos uma assero do tipo funo de avaliao, o atributo ao qual ela se refere estar recebendo um valor bem definido, bastante restrito, enquanto que no caso de asseres tipo predicado o que ocorre apenas a restrio do valor do atributo a algum outro valor ou intervalo de valores. preciso deixar claro, no entanto, que uma assero pode tanto ser uma funo de avaliao como tambm um predicado, dependendo apenas da ordem na qual fazemos a avaliao das asseres existentes na gramtica. O processo de anlise semntica pode, ento, ser realizado atravs da verificao da validade dos valores de atributos associados a cada smbolo no-terminal, fazendo a anlise de predicados e funes de avaliao desses atributos quando aplicados a cada string de entrada. Se as asseres se mantm verdadeiras, ento a string considerada semanticamente correta. A obteno de uma gramtica de atributos a partir de uma gramtica livre de contexto G feita de forma bastante simples. Para cada smbolo no-terminal em G associamos zero ou mais atributos. Para tanto adota-se uma notao parecida com a de registro-campo de linguagens como C ou pascal, isto , para um atributo 'v' associado ao no-terminal 'N' teriamos 'N.v' . As asseres e predicados seriam indicados da mesma forma que as aes semnticas o so no analisador lxico, isto , atravs de expresses entre colchetes associadas a cada uma das produes.

4.2 Atributos sintetizados e herdados


Um atributo de um no-terminal pode ter seu valor sintetizado ou herdado dependendo da posio na rvore de derivao de onde ele recebe o valor. Ele ento vai ser sintetizado se for recebido de um n abaixo do no-terminal, ou herdado caso contrrio. Na prtica, decide-se se o atributo vai herdar ou sintetizar o seu valor a partir das suas caracteristicas e tambm do no-terminal associado. Por exemplo, na gramtica para nmeros binrios definida a seguir temos que ao no-terminal 'N' (o nmero completo) associa-se o atributo 'v' (valor) que vai ser sintetizado a partir dos dgitos binrios lidos. Gramtica binria: N -> S '.' S S -> S B B -> '0' S -> B B -> '1' J ao no-terminal 'B' (dgito binrio) associamos um outro atributo valor 'v' e tambm um atributo fator de escala 'f', que d o peso da contribuio do dgito ao valor global do nmero. O atributo 'v' sintetizado a partir do valor real do bit lido, enquanto que o valor de 'f' tem que ser herdado a partir da posio do bit em relao ao ponto decimal, a qual so conhecida a partir da raiz da rvore de derivao. Por fim, para o no-terminal 'S' (string binria) temos tambm os atributos 'v' e 'f' (valor e fator de escala), mas tambm vamos ter um outro atributo, que nos vai dar o tamanho real do string que vai ser usado na determinao do seu valor a partir de 'f'. Este novo atributo tem a denominao 'l' e ter que ser sintetizado a partir do nmero de bits que foram lidos. Assim, a gramtica acima pode ser reescrita usando os atributos 'v', 'f' e 'l'. Para tanto, um atributo herdado definido pelo simbolo ! (flecha para baixo), enquanto que um atributo sintetizado dado por ^ (flecha para cima). Usando essa simbologia e acrescentando os predicados relativos aos smbolos temos a seguinte gramtica de atributos: -> S !f1 ^v1 ^l1 '.' S !f2 ^v2 ^l2 [ v = v1 + v2; f1 =1;f2 = 2 ** (-l2)] [f1 = 2xf; f2 = f; v =v1 + v2; l = l1 + S !f ^v ^l -> S !f1 ^v1 ^l1 B !f2 ^v2 1] S !f ^v ^l -> B !f ^v [l = 1] B !f ^v -> '0' [v = 0] B !f ^v -> '1' [v = f] A partir destas expresses podemos calcular os valores de cada atributo, por exemplo, para a string 11.01 temos que inicialmente so sintetizados os valores de 'l', partindo-se das folhas da rvore de derivao. Aps a determinao de 'l' partimos da raiz calculando os valores de 'f'. Com 'l' e 'f' determinados podemos ento calcular os valores de 'v' at a raiz, que ter valor igual a 3.25, conforme pode ser examinado na Figura 4.1 a seguir. N ^v

Figura 4.1 - Derivao dos atributos para a gramtica de binrios Como podemos ver, o processo no trivial e pode complicar ainda mais se ocorrer dependncia circular, isto , se tivermos atributos cujos valores dependam circularmente um do outro. Muita pesquisa tem sido feita no sentido de descobrir formas de evitar a dependncia circular e de encontrar meios timos para a ordem do clculo dos atributos. Uma das coisas que podem ser feitas forar com que todos os atributos sejam ou herdados ou sintetizados, mas nunca termos os dois casos ao mesmo tempo. Na gramtica de nmeros binrios isso pode ser feito substituindo os parmetros 'f' e 'v' dos no-terminais S e B pelo parmetro ', que representa o valor integral do dgito binrio ou string. Com isso nossa gramtica simplificada para: N ^v -> S ^i1 ^l1 '.' S ^i2 ^l2 [ v = i1 + i2 * 2 ** (-l2)] S ^i ^l -> S ^i1 ^l1 B ^i2 [i = 2 * i1 + i2; l = l1 + 1] S ^i ^l -> B ^i [l = 1] B ^i -> '0' [i = 0] B ^i -> '1' [i = 1] A derivao de 11.01 com esses atributos esta apresentada na Figura 4.2

Figura 4.2 - Derivao dos atributos para a nova gramtica de binrios Entretanto nem sempre se consegue fazer com que todos os atributos estejam num s sentido. Na realidade muitas vezes nem queremos que isso acontea, como por exemplo na verificao de tipos ou na manipulao da tabela de smbolos. Ento, de acordo com os vrios sentidos de clculo de atributos existentes temos que usar diferentes mtodos para determinar os valores dos atributos. O primeiro deles o bottom-up, que se presta mais quando os atributos so puramente sintetizados. J a avaliao top-down vantajosa quando os atributos forem todos herdados. Um terceiro fluxo seria o right-to-left, porm este muito pouco usado apesar de o parsing bottom-up representar bastante bem este tipo de fluxo. Por fim, o fluxo left-to-right o mais usado pois tanto o parsing top-down quanto o bottom-up podem ser feitos com este sentido de avaliao pois a leitura do arquivo a ser compilado feita sempre da esquerda para a direita. Alm disso, linguagens que exijam prdeclarao de variveis tem implicitamente atributos left-to-right, uma vez que no programa a declarao vem antes do ponto em que usada, ou seja, vem esquerda do uso. Uma ilustrao da aplicao do fluxo left-to-right vista na Figura 4.3, para a gramtica dada por: B A A A A -> -> -> -> -> A !vo^v1 [v0=0] A !vo^v1 A!v1^v2 "0" [v2=2*v0] "1" [v2=2*v0+1] \lambda [v2=v0]

!v0^v2 !v0^v2 !v0^v2 !v0^v2

Figura 4.3 - Derivao left-to-right dos atributos

4.3 Avaliao de atributos atravs de no-terminais


Uma forma de tornar mais clara a gramtica de atributos encapsular as funes de avaliao dos atributos em smbolos no-terminais que levem a produes vazias. Por exemplo, para a gramtica E -> T ^t1 '+' T ^t2 [t2 = t1] T ^t -> num [t = int] em que [t2 = t1] verifica se os tipos so idnticos. Podemos ento acrescentar o no-terminal X encapsulando a funo [t2 = t1] da seguinte forma: E -> T ^t1 '+' T ^t2 X !t1 !t2 X !t1 !t2 -> \lambda [t2 = t1] CASO 1 T ^t -> num [t = int] Como os dois atributos so herdados (vale a mesma observao caso fossem sintetizados) temos que a expresso [t2 = t1] de igualdade, isto , verifica-se se t2 igual a t1. Se quisessemos que t2 recebesse o valor de t1, ento teriamos que fazer os atributos de X terem sentidos opostos, na realidade teriamos que fazer t2 ser sintetizado a partir de t1, que seria herdado por algum mecanismo. Nosso exemplo ficaria assim: E -> T ^t1 '+' T ^t2 X !t1 ^t2 X !t1 ^t2 -> \lambda [t2 = t1] CASO 2 T ^t -> num [t = int] No CASO 1 temos que os atributos t1 e t2 sintetizam seus valores do no-terminal T e estes valores so passados implicitamente aos atributos herdados por X, que so ento comparados na produo "X -> \lambda". J no CASO 2 o atributo t1 sintetizado por T e passado a X. Em "X -> \lambda" o atributo t2 sintetizado (recebe o valor de t1) e passado para a produo "E -> T + T X", onde vai ser implicitamente comparado com o valor de t2 sintetizado por T.

4.4 Manipulao da tabela de smbolos


Uma das aplicaes para atributos a manipulao da tabela de smbolos, isto , a insero de um smbolo ou a consulta sobre a existncia de um smbolo podem ser representadas atravs de atributos. Neste caso os atributos seriam a prpria tabela de smbolos, o smbolo sendo inserido ou consultado e seu valor. Introduz-se ento duas novas funes de predicado para fazer a manipulao da tabela de smbolos de acordo com a funcionalidade das operaes de insero e consulta tabela. Estas funes recebem o nome de INTO e FROM, para insero e consulta respectivamente. Para a funo FROM temos a tabela de smbolos em seu estado atual, o smbolo que est sendo consultado e queremos uma resposta, que tanto pode ser o valor do smbolo (ou endereo ou algo do tipo) ou ento uma simples resposta tipo "existe/no existe". Assim, temos que os atributos symtab e symbol devem ser herdados de algum lugar, enquanto que o atributo value seria sintetizado a partir dos demais atributos. A expresso para FROM ficaria ento: [FROM !symtab !symbol ^value] J para a funo INTO temos que alm dos atributos smbolo e valor temos que ter duas instncias da tabela de smbolos, uma antes e outra aps a insero. Isto no significa que teriamos duas tabelas de smbolos, mas apenas uma, que vista em dois momentos diferentes. Para indicar isso atravs de atributos criamos dois novos atributos, oldsymtab e newsymtab, sendo que o primeiro seria herdado da situao atual do sistema, enquanto que o segundo sintetizado do estado ps-insero da tabela. Assim temos: [INTO !oldsymtab !symbol !value ^newsymtab] Vale observar que agora o atributo value herdado e no mais sintetizado como em FROM. Isto bvio uma vez que estamos querendo inserir symbol e seu valor na tabela de smbolos.

4.5 Tipo e escopo de variveis


A gramtica de atributos pode ser usada para a verificao do tipo e do escopo de variveis. J vimos que o tipo de uma varivel pode ser analisado atravs dos predicados associados aos atributos e produes. Vimos na seo 4.3 como encapsular a anlise dos tipos, de forma a verificar todas as possveis incompatibilidades atravs de expresses de verificao de tipos (ver exemplo, simples, porm revelador). Vamos ver agora como verificar o escopo de variveis, de modo a incrementar nossa anlise de tipos. O escopo de uma varivel pode ser expresso atravs do que chamamos nvel lxico, isto , o grau de aninhamento da varivel dentro de um dado programa. Assim, no fragmento abaixo a varivel "a" teria nvel lxico 0, enquanto que a varivel "b" teria nvel lxico 1.

#include "stdio.h" int a; main() {float b; scanf ("%d",&a); b = a/10; }

Para fazer nosso compilador verificar o escopo de uma varivel teramos ento que modificar o analisador lxico, para que ele consiga agora tambm colocar o nvel lxico em cada entrada na tabela de smbolos. Isso resolve o nosso problema pois agora a anlise semntica a partir da gramtica de atributos continua sendo a mesma, isto , a anlise (e verificao de tipos compatveis) atravs da gramtica de atributos no sofre alterao alguma.

Você também pode gostar