Compiladores Ciência e Tecnologia da Computaç Computação Engenharia Informá Informática e de Computadores Tabela de Símbolos Universidade dos Açores Departamento de Matemática www.uac.pt/~hguerra Programa fonte (cadeia de caracteres) Tabela de símbolos Analisador léxico y Conceito chave na compilação. Cadeia de tokens y y Armazenam os nomes das variáveis, dos tipos, das classes e outras estruturas de programação…. Geralmente, todas as fases da compilação necessitam da tabela de símbolos para criar novas entradas, adicionar informação às entradas existentes e consultar informação. 2008/09 Compiladores Analisador sintáctico Tabela de símbolos Árvore sintáctica Analisador semântico Representação intermédia Gerador de código Programa alvo (código assembly) Exemplo 1. class C{ 2. inta,intb,intc; 3. public void m(){ 4. System.out.println(a+c); 5. intj=a+b; 6. String a=“Hello”; 7. System.out.println(a); 8. System.out.println(j); 9. System.out.println(a) } 10. 11. } 2008/09 Compiladores 2008/09 Compiladores Exemplo 1. package M{ 2. class E{ staticinta=5; 3. 4. } 5. class N{ staticintb=10; 6. staticinta=E.a+b; 7. 8. } 9. class D{ staticintd=E.a+N.a; 10. } 11. 12. } Tabela de símbolos y A tabela de símbolos é uma estrutura de dados que armazena para cada identificador todos os elementos informativos (atributos): ◦ Nome (geralmente é a chave) ◦ Tipo ◦ Alcance ◦ Local, global, modificável ◦ Endereço de memória onde se situa a entidade representada pelo identificador (variável, procedimento, função, etc.) 2008/09 Compiladores Tabela de símbolos y Contém um registo para cada identificador com os campos contendo os respectivos atributos. y Deve ser uma estrutura que permita armazenamento/alteração dos dados e pesquisa eficiente. 2008/09 Compiladores Tabela de símbolos y Tabela de símbolos com um nível ◦ têm apenas um único nível de acesso para as variáveis. ◦ usadas em linguagens que têm apenas variáveis globais. y Tabela de símbolos multinível ◦ usadas em linguagens com estruturas de bloco, para guardar a informação do alcance dos nomes. 2008/09 Compiladores Tabela de símbolos com um nível y O analisador léxico pode criar as entradas na tabela. y Suporta as seguintes operações: ◦ add(x) – criar nova entrada com nome x ◦ lookup(x) – retorna a posição na tabela contendo o nome x, ou a indicação de que não existe. ◦ delete(x) – retira nome x da tabela. 2008/09 Compiladores Tabela de símbolos multinível y A estrutura da tabela reflecte o aninhamento dos blocos. y O mesmo identificador para uma variável pode ter significados diferentes em locais diferentes. y Pode ser criada uma tabela (individual) de símbolos para cada alcance ou uma tabela (global) de símbolos com indicação do alcance de cada símbolo. y A hieraquia de níveis deriva do encandeamento de alcances. 2008/09 Compiladores Tabela de símbolos multinível y Deverá suportar as seguintes operações: ◦ create_scope(parent_scope) – cria novo nível embutido no nível “parent_scope”, retornando a respectiva referência. ◦ insert(scope,x) – coloca nome x no nível “scope” ◦ lookup(x) – procura nome x, começando no alcance mais próximo. Retorna a posição na tabela contendo o nome x, ou a indicação de que não existe. ◦ Lookup_last(x) – procura nome x no alcance mais próximo. ◦ delete_scope(scope) – retira nível correspondente ao alcance “scope”. 2008/09 Compiladores Tabela de símbolos multinível y Pode ser implementada através de tabelas de símbolos individuais para cada alcance. y A pesquisa é feita mediante o uso de uma pilha de tabelas de identificadores. ◦ Sempre que se inicia um novo bloco, o endereço da nova tabela de identificadores é inserido no topo da pilha de tabelas. ◦ Acesso é feito começando pelo topo da pilha. ◦ Quando a análise de um bloco termina, o respectivo endereço para a tabela de símbolos é retirado da pilha ◦ Muitas vezes, os compiladores limitam o aninhamento de blocos a uma dezena. 2008/09 Compiladores Tabela de símbolos multinível y Pode ser implementada através de uma única tabela de símbolos. y Para diferenciar o uso do mesmo identificador para variáveis diferentes, é associado a cada identificador o número de alcance. y Os números de alcance possuem o formato xx.yy, onde . representa um subbloco. y A pesquisa é feita simultaneamente pelo identificador e alcance. 2008/09 Compiladores Implementação da Tabela de símbolos y Pode ser implementada recorrendo a: ◦ Tabelas de dispersão ◦ Árvores binárias (de pesquisa) ◦ Listas ligadas (ordenadas, não ordenadas) ◦ … 2008/09 Compiladores 2008/09 Compiladores Listas ligadas Àrvores binárias de pesquisa 2008/09 Compiladores Tabelas ou dicionários y Estrutura de dados que armazena um conjunto finito de registos y Cada registo é um par de valores: ◦ Chave – identifica univocamente o elemento em relação aos restantes ◦ Valor do elemento– informação que se pretende armazenar. 2008/09 Compiladores Tabelas ou dicionários 2008/09 Compiladores Tabelas de dispersão y Uma tabela de dispersão é uma implementação do TDA tabela que usa a chave para calcular a posição onde o elemento deve ser inserido. y Recorre a uma função (de dispersão) que relaciona o domínio do tipo da chave e o contradomínio definido pelo conjunto de endereços da estrutura de dados. y Uma função de dispersão perfeita associa a cada chave uma posição diferente para inserir o elemento. 2008/09 Compiladores Tabela de dispersão y Método de dispersão por divisão ◦ h(k) = ord(k) mod N, onde N é a dimensão do vector y Muitas vezes, a função de dispersão não é injectiva: retorna o mesmo endereço para identificadores distintos. Diz-se que ocorreu uma Colisão. y É necessário: ◦ Escolher uma função de dispersão que seja fácil de calcular e que proceda a uma dispersão o mais uniforme possível ◦ Resolver as potenciais colisões 2008/09 Compiladores Resolução de colisões y y Endereçamento fechado ◦ elementos cuja chave devolve o mesmo valor de dispersão são armazenados numa lista. Endereçamento aberto ◦ as chaves que são enviadas para o mesmo endereço são reenviadas para uma posição livre (aberta) ◦ Precisa de outra função que permita encontrar posições alternativas – função de pesquisa. 2008/09 Compiladores Principais técnicas de reendereçamento y Método de pesquisa linear (linear probing) hi(k,i)=(h(k)+i) mod N y Método de pesquisa quadrática hi(k,i)=(h(k)+i2) mod N y Método de pesquisa com dispersão dupla hi(k,i)=(h(k)+i.h1(k)) mod N, onde h1 é a segunda função de dispersão. 2008/09 Compiladores y Compiladores de linguagens de programação imperativas recorrem às tabelas de dispersão y Compiladores de linguagens de programação funcionais recorrem às árvores binárias de pesquisa 2008/09 Compiladores Compiladores Ciência e Tecnologia da Computaç Computação Engenharia Informá Informática e de Computadores Código Intermédio Universidade dos Açores Departamento de Matemática www.uac.pt/~hguerra Código intermédio y y y y 1ª etapa da fase de síntese (backend). Representação abstracta do código final. Facilita o transporte de compiladores de determinada linguagem de programação para máquinas distintas. A escolha de uma linguagem de representação intermédia para linguagens de paradigmas distintos não é trivial. 2008/09 Compiladores Analisador sem‰ntico Representa¨ ‹ o intermˇ dia Gerador de c—digo Programa alvo (c—digo assembly) Código intermédio y Diferentes esquemas de representação. y Esquemas mais usados nos compiladores: ◦ Notação em árvore ◦ Notação sufixa ◦ Código de triplo endereço 2008/09 Compiladores Notação em árvore y Representação gráfica do programa fonte. y Permite separar os aspectos sintácticos dos semânticos – modularidade. y Transmite a estrutura sintáctica do programa fonte já com os problemas da análise sintáctica resolvidos (e.g., recursividade à esquerda, ambiguidade). y Sem interpretação semântica. 2008/09 Compiladores Árvore sintáctica abstracta y abstract syntatic tree – AST y Versão simplificada da árvore sintáctica concreta y Nós com variáveis são eliminados y Reflecte dependência estrutural entre os terminais y Nós internos são “operadores” y Folhas são “operandos” ou informação 2008/09 Compiladores AST - Árvores binárias de expressões 2008/09 Compiladores AST - Árvores binárias de expressões 2008/09 Compiladores Árvores Sintácticas Abstractas a*3/(b-4) E → E+T | E – T | T T → T*F | T / F | F F → id | num | (E) Podem ser construídas recorrendo a gramáticas de atributos sintetizados 2008/09 Compiladores Árvores Sintácticas Abstractas E → E+E | E – E | T | E*E | E / E | id | num | (E) // uma classe abstracta para cada variável public abstract class E { //uma classe derivada para cada produção public class PlusExp extends E{ private e1, e2; public PlusExp( E e1, E e2) {e1=a1; e2=a2;} } public class MinusExp extends E{ … } public class TimesExp extends E{ … } 2008/09 Compiladores Árvores Sintácticas Abstractas public … } public … } public … } public … } class DivExp extends E{ class Identifier extends E{ class IntegerLiteral extends E{ class ParenExp extends E{ 2008/09 Compiladores Notação sufixa y Notação infixa ◦ Operadores binários aparecem entre os operandos ◦ a + b y Notação prefixa ◦ Operadores binários aparecem antes dos operandos ◦ + a b y Notação sufixa ◦ Operadores binários aparecem após os operandos ◦ a b + 2008/09 Compiladores Notação sufixa y As expressões sufixas definem-se indutivamente do seguinte modo: ◦ literais são expressões na notação sufixa. ◦ se op é um operador de aridade k (>0) e e1, e2, …, ek são expressões na notação sufixa, então a e1e2…ekop é uma expressão na notação sufixa. y Exemplo: a expressão x+1 na notação infixa é x1+ na notação sufixa. y As expressões não necessitam de parentesis para definirem a prioridade das operações. 2008/09 Compiladores Conversão da expressão na notação infixa a + b * c para a notação sufixa 2008/09 Compiladores Conversão da expressão na notação infixa a – b + c para a notação sufixa 2008/09 Compiladores Conversão da expressão na notação infixa a^b^c para a notação sufixa 2008/09 Compiladores Conversão da notação infixa para a notação sufixa a/b*(c+(d–e)) 2008/09 Compiladores Avaliação de expressões na notação sufixa Expression sufixa a b /, onde a é 2 e b é 4 2008/09 Compiladores Avaliação de expressões na notação sufixa Expression sufixa a b + c / onde a é 2, b é 4 e c é 3 2008/09 Compiladores Algoritmo de avaliação de expressão sufixa Algorithm evaluatePostfix(postfix) // Evaluates a postfix expression. valueStack = a new empty stack while (postfix has characters left to parse) { nextCharacter = next nonblank character of postfix switch (nextCharacter) { case variable: valueStack.push(value of the variable nextCharacter) break case '+': case '-': case '*': case '/': case '^': operandTwo = valueStack.top(), valueStack.pop() operandOne = valueStack.top(), valueStack.top() result = the result of the operation in nextCharacter and its operands operandOne and operandTwo valueStack.push(result) break default: break } } return valueStack.top() 2008/09 Compiladores Código de triplo endereço C3E y A forma de representação é imagem do processador onde o programa irá ser executado, excluindo detalhes como a gestão dos registos. y É fácil de transcrever para assembly. y Instruções são da forma NNN oper (arg1, arg2, res) ◦ res=arg1 oper arg2 ◦ NNN é o endereço abstracto de memória y Recorre a variáveis temporárias para guardar valores intermédios. 2008/09 Compiladores Tipos de instruções C3E y Instruções de operadores binários e unários ◦ +, -, x, /, not, = y Instruções de cópia de literal ou variável ◦ cpy(y, _, z) y z=y Instruções de acesso a elementos de um vector ◦ idxr(x,i,z) z=x[i] ◦ idxl(x,i,z) x[i]=z 2008/09 Compiladores Tipos de instruções C3E y Instruções de salto ◦ Condicional x if oper (x,y,m) se (x oper y) execução salta para endereço m, senão executa instrução seguinte. ◦ Incondicional x jmp(_,_,m) 2008/09 Compiladores Tipos de instruções C3E y Instruções de chamada e retorno de rotinas P(A1,…,An) ◦ Param(A1,_,_) ◦ … ◦ Param(An,_,_) ◦ Call(P,_,z) z=P(A1,…,An) 2008/09 Compiladores Implementação de Instruções C3E y Existem duas estratégias de representação das instruções: ◦ Quádruplos – representação explicita das variáveis temporárias ◦ Triplos – sem representação explicita das variáveis temporárias 2008/09 Compiladores Quádruplos y O operador, os dois operandos e o resultado são campos de registos de C3E. y Maior facilidade na implementação do código e na optimização. 2008/09 Compiladores Triplos y O operador e os dois operandos são os campos de registos de C3E. y Sempre que uma instrução tiver um operando com referencia a uma variável temporária, é indicado no seu lugar o endereço abstracto da instrução onde o valor foi calculado. y Maior poupança de memória. 2008/09 Compiladores