analise semantica

5
Compiladores An´ alise Semˆ antica vers˜ ao α - 0.001 Sim˜ ao Melo de Sousa Este documento ´ e uma tradu¸c˜ao adaptada do cap´ ıtulo ”Analyse S´ emantique” da se- benta ”Cours de Compilation”de Christine Paulin-Morhing e Marc Pouzet (http://www.lri.fr/~paulin). 1 Introdu¸ ao A an´ alise semˆ antica trata a entrada sint´ actica e transforma-a numa representa¸c˜ ao mais simples e mais adaptada ` agera¸c˜ ao de c´ odigo. Esta camada do compilador fica igualmente encarregue de analisar a utiliza¸c˜ao dos identificadores e de ligar cada uma delas a sua declara¸ c˜ao.Nestasitua¸c˜ ao verificar-se-´a que o programa respeita as regras de visibilidade e de porte dos identificadores. ´ E tamb´ em esperado que esta fase da compila¸c˜ ao verifi- que que cada express˜ ao definida tenha um tipo adequado conforme as regras pr´ oprias ` a linguagem. Neste parte da li¸c˜ ao iremos estudar a gest˜ ao da tabela dos s´ ımbolos que serve para a liga¸c˜ao dos nomes manipulados aos objectos que estes de facto designam. Estudaremos igualmente a tipifica¸c˜ao dos programas. Por fim definiremos as no¸c˜ oes de gram´ aticas de atributos que permitam associar valores aos nodos da ´ arvore de deriva¸ c˜aosint´actica. 2 Tabela dos s´ ımbolos 2.1 Introdu¸ ao As linguagens de programa¸c˜ ao manipulam identificadores que s˜ ao essencialmente s´ ımbo- los que servem para designar objectos – conte´ udo dum endere¸co mem´ oria, no caso por exemplo duma vari´avel, peda¸cos de c´odigo no caso de nomes de procedimentos, tipos, etc... A tabela de s´ ımbolos arquiva as informa¸ c˜oes sobre os objectos designados por nomes na linguagem em quest˜ao. Esta´ e actualizada de cada vez que ´ e analisada uma declara¸ ao dum novo identificador. De forma semelhante, a tabela ´ e consultada de cada vez que ´ e utilizado um identificador no programa analisado. A tabela de s´ ımbolos permite para cada identificador o arquivo das informa¸c˜oes as- sociadas ao objecto identificado. Estas podem ser de natureza diversa, como o tipo do 1

description

Análise Semântica

Transcript of analise semantica

  • CompiladoresAnalise Semanticaversao 0.001

    Simao Melo de Sousa

    Este documento e uma traducao adaptada do captulo Analyse Semantique da se-benta Cours de Compilation de Christine Paulin-Morhing e Marc Pouzet(http://www.lri.fr/~paulin).

    1 Introducao

    A analise semantica trata a entrada sintactica e transforma-a numa representacao maissimples e mais adaptada a geracao de codigo. Esta camada do compilador fica igualmenteencarregue de analisar a utilizacao dos identificadores e de ligar cada uma delas a suadeclaracao. Nesta situacao verificar-se-a que o programa respeita as regras de visibilidadee de porte dos identificadores. E tambem esperado que esta fase da compilacao verifi-que que cada expressao definida tenha um tipo adequado conforme as regras proprias alinguagem.

    Neste parte da licao iremos estudar a gestao da tabela dos smbolos que serve para aligacao dos nomes manipulados aos objectos que estes de facto designam. Estudaremosigualmente a tipificacao dos programas. Por fim definiremos as nocoes de gramaticas deatributos que permitam associar valores aos nodos da arvore de derivacao sintactica.

    2 Tabela dos smbolos

    2.1 Introducao

    As linguagens de programacao manipulam identificadores que sao essencialmente smbo-los que servem para designar objectos conteudo dum endereco memoria, no caso porexemplo 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 nomesna linguagem em questao. Esta e actualizada de cada vez que e analisada uma declaracaodum novo identificador. De forma semelhante, a tabela e consultada de cada vez que eutilizado um identificador no programa analisado.

    A tabela de smbolos permite para cada identificador o arquivo das informacoes as-sociadas ao objecto identificado. Estas podem ser de natureza diversa, como o tipo do

    1

  • objecto, uma posicao na lista das variaveis declaradas (com a finalidade de calcular oendereco relativo aquando da geracao de codigo), um valor...

    E igualmente possvel coexistirem varias tabelas de smbolos, por exemplo quandoexistem varios espacos de nomes, como e o caso para linguagens orientadas a objecto(e.g. os packages do java). Classicamente, encontraremos nestas linguagens uma tabelade smbolos para cada espaco de nome. Por exemplo arquivaremos nos diferentes espacoso nome das classes e o nome dos metodos associados. Em linguagens de tipo ML, ostipos, os modulos e os valores podem ser agrupados em tabelas de smbolos diferentes.

    Um mesmo identificador pode ser utilizado para representar diferentes objectos. Esteobjectos podem estar arquivados ou referenciados em diferentes tabelas de smbolos. Estesituacao obriga conhecer a natureza do objecto para determinar em que tabela procuraros seus dados. Este conhecimento e em geral adquirido. Assim este aparente conflito podeser facilmente resolvido. Um mesmo identificador (variavel, procedimento) pode igual-mente estar declarado mais do que uma vez na mesma tabela. Aquando da compilacaosera necessario conhecer precisamente o objecto referenciado por cada identificador. Asregras de porte (scope em ingles) dos identificadores que permitira resolver os conflitossubjacentes.

    2.2 Porte dos identificadores

    Os programas manipulam varios identificadores. Por razoes de eficacia, e com a finalidadede melhorara a robustez do codigo, as linguagens permitam indicar sintacticamente que autilizacao de certas variaveis sera confinada a uma parte bem determinada do programa,designado de bloco.

    Assim, fora deste bloco, nao sera necessario alocar espaco para as variaveis atribudasao bloco em questao.

    Durante a analise semantica, o compilador assegurar-se-a que todas as variaveis utili-zadas foram declaradas de forma adequada e sao bem visveis durante as suas utilizacoes(i.e. as suas utilizacoes tem lugar no bloco ao qual pertencem).

    As regras de porte/alcance dos identificadores sao especficas a cada linguagem. Porexemplo em C, uma variavel e local ao procedimento em que foi declarada, ou e global atodo o programa. Em Pascal, qualquer identificador tem de ser declarado de forma cen-tralizada antes da sua utilizacao (da a necessidade da instrucao forward para as funcoesmutuamente recursivas). O corpo dum procedimento pode utilizar variaveis declaradasem qualquer procedimento que o engloba (ele proprio includo).

    Em ML, qualquer identificador deve ter sido previamente declarado numa expressaolet id = ... in ... ou let id = .... O porte de tal declaracao esta restrito a ex-pressao associada a expressao a direita do in no caso da declaracao local, ou ao resto doprograma/modulo em que esta definido no segundo caso (ficheiro = modulo em OCaml).

    Fora do modulo um identificador exportado (i.e. cuja visibilidade fora do moduloe permitida) pode ser acedido atraves do que se designa por nome qualificado ou sejaNome_do_modulo.identificador. Se existirem directivas de abertura de modulos (porexemplo open em OCaml, import em Java) a qualificacao pode ser omitida.

    Se um identificador e declarado mais do que uma vez o objecto acedido pelo nomequalificado e o ultimo declarado. E no entanto possvel aceder a um objecto via o seunome completamente qualificado. No caso de um nome completamente qualificado poder

    2

  • referir dois objectos diferentes, entao este fica por designar o ultimo declarado.Uma declaracao de funcao em ML nao e por defeito recursiva. Para tal e preciso

    juntar a palavra chave rec ao let.Em Java, o corpo dum metodo pode utilizar outros metodos da mesma classe mesmo

    se a definicao destes metodos ocorre posteriormente. Isto obriga ao processamento embloco das definicoes de classes. Mais, podemos definir numa classe varios metodos como mesmo nome. O tipo dos parametros permite determinar de forma estatica (i.e. emtempo de compilacao) o metodo por aplicar. De uma forma similar, varias classes podemredefinir os mesmos metodos. Se as classes sao disjuntas, esta ambiguidade de nomepode ser resolvida por uma verificacao dos tipos (i.e. type checking). Se um mesmoidentificador esta definido numa classe e redefinido numa das suas sub-classes entao deve-se impor coerencia entre os tipos dos objectos assim definidos, como no caso dos metodospor exemplos. A escolha do metodo por executar e geralmente feita de forma dinamica(na altura da execucao). Em algumas situacoes e possvel indicar sintacticamente quemetodo se pretende chamar, como no caso do uso das palavras chaves super ou this.No primeiro caso e assim indicado explicitamente que se pretende invocar o metodo daclasse mae mesmo se essa foi redefinida na classe activa.

    2.3 Representacao da tabela de smbolos

    Deve ser possvel apos a analise dum programa encontrar toda a informacao associadaa um identificador. Isto pode ser feito enfeitando (analogia feita ao enfeito da arvorede natal que fica apos este trabalho com todo o significado a semantica de natal) aarvore de sintaxe abstracta (ASA). E possvel associar a cada utilizacao dum identificadorum apontador para parte da ASA que corresponde a declaracao do identificador. Estaassociacao pode (costuma) igualmente incluir outras informacoes uteis como, por exemplo,o tipo do objecto referenciado. De facto o cuidado por ter aqui e o compromisso entreutilidade da informacao arquivada, o seu tamanho e a frequencia da sua utilizacao. Semcuidado, a gestao duma tabela de smbolo pode se tornar pesada.

    Uma outra solucao consiste em arquivar as informacoes sobre um objecto numa tabelae associar um endereco para esta tabela a cada utilizacao do identificador. Este enderecopode ser um apontador, um inteiro ou um nome unico.

    Ao lado desta estrutura persistente, e necessario gerir uma tabela para a verificacao doporte de cada identificador. Esta tabela deve poder informar em qualquer instante destafase de analise dos dados dos identificadores visveis. deve ser igualmente possvel juntarnovos identificadores, determinar rapidamente e facilmente se um dado identificador evisvel. De forma semelhante deve ser possvel retirar identificadores desta estruturaquando o objecto referenciado deixa de existir ou de ser acessvel (um identificador deuma variavel local quando se atinge o fim do bloco em que esta definido).

    Consideremos a analise do seguinte programa:

    1 let x = (let y = 2 in y*y + 2*y +1) in x+y

    Quando comecamos a analisar esta expressao, construmos uma tabela dos smbolosvisveis T . Primeiro analisa-se a construcao let x = e in e que declara o novo identifi-cador x.

    Para encontrar informacao sobre este identificador, analisa-se o corpo e da defini-cao (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 na qual se acrescentou a

    entrada y com a informacao de que este e, por exemplo, inteiro. Esta declaracao tornaqualquer informacao previa sobre y em T invisvel. Utilizando a tabela T analisamos aexpressao y*y + 2*y +1 que e determinada como sendo do tipo inteiro. Ao sair do blocoy*y + 2*y +1, a entrada y na tabela de smbolos deve desaparecer e assim voltamos deT para T . Neste ponto preciso sabe-se que x e inteiro e podemos entao juntar estainformacao a tabela de smbolos T e proceder a analise de x+y. Com se ve na expressaopor analisar e pelas regras de porte em OCaml a ocorrencia de y nao faz referenciaa definicao interna a definicao de x mas sim a uma declaracao previa. No fim destaanalise, as diferentes utilizacoes dos identificadores na ASA devem estar associadas a boadeclaracao.

    Estas tabelas devem ser optimizadas porque o numero de identificadores pode serimportante e o acesso a informacao deve ser rapido. Podem ser implementadas de formaimperativa ou funcional No caso duma representacao funcional a analise de visilibilidadepoderia 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 constru-cao de T (juncao de (x, tip)) nao altere de facto a tabela T . Nao e o caso, por exemplose T for uma tabela de Hash (como e o caso em OCaml). A copia e arquivo da copia databela de hash com o objectivo de a repor caso necessario seria aqui altamente inefici-ente. Se optarmos por uma tabela de hash, e assim necessario retirar explicitamente osidentificadores dos quais pretendemos apagar o registo. Assim sendo esta tabela por serglobal:

    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 per-tencendo a mesma lista correspondem aos valores com a mesma chave de dispersao (hashkey)) sao em regra geral boas estruturas para implementar tabelas de smbolos visveis.

    De facto quando identificadores com o mesmo nome estao declarados o ultimo escondenaturalmente os anteriores. Isto porque a lista de entradas e gerida como uma pilha. Pararetirar um identificador basta eliminar a primeira ocorrencia encontrada na tabela (quecorresponde ao ultimo inserido).

    4

  • E possvel introduzir em cada bloco do programa analisado um numero arbitrario deidentificadores. Na sada de cada bloco devemos poder identificar e remover todos osidentificadores da tabela que nele foram introduzidos (estes deixam de ser visveis). Eassim necessario conservar uma pilha dos blocos abertos com a informacao, para cadabloco aberto, dos identificadores que este introduz. Este processo pode ser feito de formafuncional arquivando uma pilha de listas de identificadores ou de forma mais imperativainterligando o conjunto de identificadores introduzidos num mesmo bloco na tabela dossmbolos visveis e guardando num estrutura (uma pilha por exemplo) o endereco natabela do ultimo identificador introduzido no bloco activo.

    2.4 Representacao dos smbolos

    Quando se analisa os smbolos do programa, e necessario fazer numerosas comparacoes(igualdades, desigualdades se se utiliza arvores binarias de pesquisa). Para tal e pertinenteutilizar uma representacao eficiente dos identificadores com por exemplo uma codificacaocom base em inteiros. Neste caso e interessante dispor duma tabela de dispersao (hashtable) onde se arquiva a associacao entre inteiro - identificador. Em termos de requisitos,(a) esta associacao deve ser unica, isto e, um inteiro representa um unico identificador;(b) esta tabela deve poder devolver o identificador representado por um inteiro e vice-versa; (c) a gestao desta tabela nao deve sobrecarregar computacionalmente o processode analise.

    5

    IntroduoTabela dos smbolosIntroduoPorte dos identificadoresRepresentao da tabela de smbolosRepresentao dos smbolos