Documente Academic
Documente Profesional
Documente Cultură
Analise Semantica
versao 0.001
Simao Melo de Sousa
Este documento e uma traducao adaptada do captulo Analyse Semantique da sebenta Cours de Compilation de Christine Paulin-Morhing e Marc Pouzet
(http://www.lri.fr/~paulin).
Introduc
ao
2
2.1
As linguagens de programacao manipulam identificadores que sao essencialmente smbolos que servem para designar objectos conte
udo dum endereco memoria, no caso por
exemplo duma variavel, pedacos de codigo no caso de nomes de procedimentos, tipos,
etc...
A tabela de smbolos arquiva as informacoes sobre os objectos designados por nomes
na linguagem em questao. Esta e actualizada de cada vez que e analisada uma declaracao
dum novo identificador. De forma semelhante, a tabela e consultada de cada vez que e
utilizado um identificador no programa analisado.
A tabela de smbolos permite para cada identificador o arquivo das informacoes associadas ao objecto identificado. Estas podem ser de natureza diversa, como o tipo do
objecto, uma posicao na lista das variaveis declaradas (com a finalidade de calcular o
endereco relativo aquando da geracao de codigo), um valor...
igualmente possvel coexistirem varias tabelas de smbolos, por exemplo quando
E
existem varios espacos de nomes, como e o caso para linguagens orientadas a objecto
(e.g. os packages do java). Classicamente, encontraremos nestas linguagens uma tabela
de smbolos para cada espaco de nome. Por exemplo arquivaremos nos diferentes espacos
o nome das classes e o nome dos metodos associados. Em linguagens de tipo ML, os
tipos, os modulos e os valores podem ser agrupados em tabelas de smbolos diferentes.
Um mesmo identificador pode ser utilizado para representar diferentes objectos. Este
objectos podem estar arquivados ou referenciados em diferentes tabelas de smbolos. Este
situacao obriga conhecer a natureza do objecto para determinar em que tabela procurar
os seus dados. Este conhecimento e em geral adquirido. Assim este aparente conflito pode
ser facilmente resolvido. Um mesmo identificador (variavel, procedimento) pode igualmente estar declarado mais do que uma vez na mesma tabela. Aquando da compilacao
sera necessario conhecer precisamente o objecto referenciado por cada identificador. As
regras de porte (scope em ingles) dos identificadores que permitira resolver os conflitos
subjacentes.
2.2
2.3
Representa
c
ao da tabela de smbolos
Deve ser possvel apos a analise dum programa encontrar toda a informacao associada
a um identificador. Isto pode ser feito enfeitando (analogia feita ao enfeito da arvore
de natal que fica apos este trabalho com todo o significado a semantica de natal) a
possvel associar a cada utilizacao dum identificador
arvore de sintaxe abstracta (ASA). E
um apontador para parte da ASA que corresponde a declaracao do identificador. Esta
associacao pode (costuma) igualmente incluir outras informacoes u
teis como, por exemplo,
o tipo do objecto referenciado. De facto o cuidado por ter aqui e o compromisso entre
utilidade da informacao arquivada, o seu tamanho e a frequencia da sua utilizacao. Sem
cuidado, a gestao duma tabela de smbolo pode se tornar pesada.
Uma outra solucao consiste em arquivar as informacoes sobre um objecto numa tabela
e associar um endereco para esta tabela a cada utilizacao do identificador. Este endereco
pode ser um apontador, um inteiro ou um nome u
nico.
Ao lado desta estrutura persistente, e necessario gerir uma tabela para a verificacao do
porte de cada identificador. Esta tabela deve poder informar em qualquer instante desta
fase de analise dos dados dos identificadores visveis. deve ser igualmente possvel juntar
novos identificadores, determinar rapidamente e facilmente se um dado identificador e
visvel. De forma semelhante deve ser possvel retirar identificadores desta estrutura
quando o objecto referenciado deixa de existir ou de ser acessvel (um identificador de
uma variavel local quando se atinge o fim do bloco em que esta definido).
Consideremos a analise do seguinte programa:
1
Quando comecamos a analisar esta expressao, construmos uma tabela dos smbolos
visveis T . Primeiro analisa-se a construcao let x = e in e que declara o novo identificador x.
Para encontrar informacao sobre este identificador, analisa-se o corpo e da definicao (aqui let y = 2 in y*y + 2*y +1). Nesta situacao devemos entao analisar o corpo da
3
definicao de y ou seja 2.
Este processo leva assim a actualizacao da tabela T em T 0 na qual se acrescentou a
entrada y com a informacao de que este e, por exemplo, inteiro. Esta declaracao torna
qualquer informacao previa sobre y em T invisvel. Utilizando a tabela T 0 analisamos a
expressao y*y + 2*y +1 que e determinada como sendo do tipo inteiro. Ao sair do bloco
y*y + 2*y +1, a entrada y na tabela de smbolos deve desaparecer e assim voltamos de
T 0 para T . Neste ponto preciso sabe-se que x e inteiro e podemos entao juntar esta
informacao a tabela de smbolos T e proceder a analise de x+y. Com se ve na expressao
por analisar e pelas regras de porte em OCaml a ocorrencia de y nao faz referencia
a` definicao interna a` definicao de x mas sim a uma declaracao previa. No fim desta
analise, as diferentes utilizacoes dos identificadores na ASA devem estar associadas a` boa
declaracao.
Estas tabelas devem ser optimizadas porque o n
umero de identificadores pode ser
importante e o acesso a informacao deve ser rapido. Podem ser implementadas de forma
imperativa ou funcional No caso duma representacao funcional a analise de visilibilidade
poderia se escrever da seguinte forma:
function visivel : tabela * asa -> asa_tipada
visivel (T,let(x,e,e)) =
seja f = visivel(T,e),
tip = tipo_de(T,f),
T = add ((x,tip),T),
f = visivel(T,e),
let(x:tip,f,f)
Este processo funciona se a representacao da tabela e funcional, ou seja se a construcao de T 0 (juncao de (x, tip)) nao altere de facto a tabela T . Nao e o caso, por exemplo
se T for uma tabela de Hash (como e o caso em OCaml). A copia e arquivo da copia da
tabela de hash com o objectivo de a repor caso necessario seria aqui altamente ineficiente. Se optarmos por uma tabela de hash, e assim necessario retirar explicitamente os
identificadores dos quais pretendemos apagar o registo. Assim sendo esta tabela por ser
global:
function visible_imp : asa -> asa_tipada
visival_imp (let(x,e,e )) =
seja f = visivel_imp(e),
tip = tipo_de(f),
add x;
seja f = visivel_imp(e),
del (x); let(x:tip,f,f)
As tabelas de hash com ligacoes para listas de entradas (onde os identificadores pertencendo a mesma lista correspondem aos valores com a mesma chave de dispersao (hash
key)) sao em regra geral boas estruturas para implementar tabelas de smbolos visveis.
De facto quando identificadores com o mesmo nome estao declarados o u
ltimo esconde
naturalmente os anteriores. Isto porque a lista de entradas e gerida como uma pilha. Para
retirar um identificador basta eliminar a primeira ocorrencia encontrada na tabela (que
corresponde ao u
ltimo inserido).
4
identificadores da tabela que nele foram introduzidos (estes deixam de ser visveis). E
assim necessario conservar uma pilha dos blocos abertos com a informacao, para cada
bloco aberto, dos identificadores que este introduz. Este processo pode ser feito de forma
funcional arquivando uma pilha de listas de identificadores ou de forma mais imperativa
interligando o conjunto de identificadores introduzidos num mesmo bloco na tabela dos
smbolos visveis e guardando num estrutura (uma pilha por exemplo) o endereco na
tabela do u
ltimo identificador introduzido no bloco activo.
2.4
Representa
c
ao dos smbolos