Implementação de Tipos Abstratos de Dados no Ambiente...
Transcript of Implementação de Tipos Abstratos de Dados no Ambiente...
Trabalho de Conclusão de Curso
Implementação de Tipos Abstratos de Dados no Ambiente FURBOL
Autor: André Luís GarliniOrientador: José Roque Voltolini da Silva
Roteiro
• Introdução
� Objetivos do trabalho
• Fundamentação teórica
• Desenvolvimento
� Requisitos do sistema
� Especificação
� Implementação
� Operacionalidade
• Conclusões
Trabalho de Conclusão de Curso
Introdução
Introdução
• Linguagem de Programação (LP)
Serve como meio de comunicação entre o individuo que deseja resolver um problema e o computador escolhido para ajudá-lo na solução.
• Compiladores
Para realizar esta comunicação entre o individuo e o computador existem programas chamados de compiladores.
• Tipos Abstratos de Dados (TADs)
Toda LP apresenta seu próprio conjunto de símbolos e regras para formação de programas. Algumas oferecem a possibilidade da criação de TADs definidos pelo usuário.
• FURBOL
É uma LP desenvolvida na FURB que ainda não oferece suporte a TADs.
• Objetivo do trabalho
Este trabalho propõe-se a estender o FURBOL, a partir da implementação de Silva (2002), adicionando o suporte a TADs.
Objetivos do trabalho
• O objetivo deste trabalho é estender a LP FURBOL.
• O objetivo específico do trabalho é adicionar a possibilidade da criação de TADs pelos usuários do FURBOL.
Trabalho de Conclusão de Curso
Fundamentação teórica
Paradigmas de linguagens de programação
• Conjunto de características que servem para categorizar um grupo de LPs
Fonte: Varejão (2004, p. 17).
• Imperativo� Especificam como o computador deve realizar uma tarefa.� Orientação a objetos – Evolução dos TADs.
• Declarativo� Especificam o que é a tarefa a ser realizada.
Tipos Abstratos de Dados
• Conjuntos de valores (atributos) e operações executadas sobre esses valores (métodos). Exemplo - classe em C++ (sem herança/polimorfismo).
• Algumas vantagens
� Diminui a complexidade dos programas.
� Abstração. Representação interna fica “escondida” do usuário do TAD.
• Quatro tipos de operações
� Construtoras – inicializam os valores do TAD.
� Consultoras – usadas para obter informações do TAD.
� Atualizadoras – permitem a alteração dos valores do TAD.
� Destrutoras – qualquer atividade de finalização do TAD.
Exemplo de TAD
class pilha {private: //** Estes membros são visíveis somente a outros membros
//** e amigosint *ptr_pilha ;int tam_max ;int top_ptr ;
public: //** Estes membros são visíveis aos clientespilha () { //** Um construtor
ptr_pilha = new int [ 100 ];tam_max = 99;top_ptr = -1;
}~pilha () {delete [] ptr_pilha;}; //** Um destrutor~pilha () {delete [] ptr_pilha;}; //** Um destrutorvoid push (int numero) {
if (top_ptr == tam_max)cout << “Erro no pop – a pilha esta cheia\n”;
else ptr_pilha[++top_ptr] = numero;}void pop () {
if (top_ptr == -1)cout << “Erro no pop – a pilha esta vazia\n”;
else top_ptr--;}int top () {return (ptr_pilha[top_ptr]);}int empty () {return (top_ptr == -1);}
}
Fonte: adaptado de Sebesta (2000, p. 408-409).
Compiladores
• Programa tradutor que mapeia programas escritos em uma LP de alto nível para programas equivalentes em linguagem simbólica (assembly) ou linguagem de máquina.
• Etapas
� Análise léxica – faz a leitura do programa fonte e o traduz em símbolos léxicos (tokens). Exemplos - identificadores e palavras reservadas.
� Análise sintática – verifica se as construções usadas no programa estão gramaticalmente corretas.
� Análise semântica – determina se as estruturas sintáticas analisadas fazem sentido. Exemplo – compatibilidade entre operandos de uma expressão.
� Geração de código – nesta etapa é gerado código para a máquina alvo.
Tradução dirigida pela sintaxe
• Técnica que permite realizar a tradução do programa simultaneamente a análise sintática.
• Esquema de tradução
Extensão de uma Gramática Livre de Contexto (GLC) que associa atributos aos símbolos gramaticais e de ações semânticas (envolvidas entre chaves) às regras de produção.
• Atributos - qualquer informação necessária para a tradução de um programa. Exemplo – tipo de dado de uma variável. São classificados em dois tipos:
� Sintetizados – são calculados pela função. Fluem de filho para pai na árvore de derivação.
� Herdados – são entradas da função. Fluem de pai ou irmão na árvore de derivação.
Exemplo de um esquema de tradução
E � T RR � Op T { print(Op. simbol ) } R | Є
T � num { print(num. lexval )}
Fonte: adaptado de Price e Toscani (2001, p. 88).
Projeto de um tradutor preditivo
• Algoritmo
Entrada: um esquema de tradução (o esquema deve ser baseado numa gramática apropriada para análise preditiva).
Resultado: código de um tradutor dirigido por sintaxe.
Método:1) Para cada não-terminal A, construa uma função que tenha um parâmetro formal para cada atributo herdado de A e que retorne os valores dos atributos sintetizados de A. Além disso, a função para A deve ter uma variável local para cada atributo de símbolo gramatical que aparece nas produções de A.nas produções de A.2) O código para o não-terminal A deve decidir qual produção usar, baseado no símbolo corrente da entrada. Considerando o lado direito da produção a ser usada, da esquerda para a direita, o código associado deve implementar o seguinte:i) para cada token X, verificar se X é o token lido e avançar a leitura;ii) para cada não-terminal B, gerar uma atribuição c := B(b1, b2, ..., bk), sendo b1, b2, ..., bk variáveis para os atributos herdados de B, e c a variável para o atributo sintetizado de B;iii) para cada ação semântica, copiar o código correspondente, substituindo cada referência a atributo pela variável desse atributo.
Fonte: adaptado de Price e Toscani (2001, p. 110).
Código intermediário
• Forma intermediária de código independente de máquina.� Código intermediário de três endereços – tipo de código intermediário onde cada instrução faz referência no máximo a três variáveis (endereços de memória).
Geração de código-alvo
• Fase final de um compilador.
• Gerar código para a máquina alvo (FURBOL - Microprocessador 8088).
Microprocessador 8088
• Chip que executa os programas armazenados na memória.
• Desenvolvido pela empresa Intel Corporation, sendo uma versão do modelo 8086.
• Processador de 16 bits.
Memória do 8088
• 216 (65.536) - 220 (1.048.576)
• Esquema de endereçamento de 20 bits
• Endereços segmentados� Divide o espaço de memória endereçável em segmentos (64 KB).� Endereça a memória usando dois registradores. Um para o segmento e outro para o deslocamento (offset) dentro do deslocamento. deslocamento.
• Pilha – Segmento de memória. Algumas utilidades:� Salvar valores de registradores que serão utilizados.� Salvar endereço de retorno de um método.� Parâmetros e variáveis locais de um método.
Registradores do 8088
• Quatorze registradores de 16 bits
Registradores de propósito geral AX, BX, CX e DX
Registradores de ponteiros e de índices BP, SP, SI, DI
Registradores de segmento CS, DS, SS, ES
Registrador de ponteiro de instrução IP
Registrador de sinalizadores FLAGS
• Propósito geral - operações aritméticas e lógicas.
• Ponteiros e de índices – deslocamento dentro dos segmentos.
• Segmentos – identificam os quatro segmentos endereçáveis em um programa.
� CS– segmento de código� DSe ES – segmentos de dados� SS – segmento de pilha
• Ponteiro de instrução – indica o deslocamento da instrução que está sendo executada dentro do segmento de código.
• Sinalizadores – indicam resultados da última operação executada.
Algumas instruções do 8088
Instrução Formato Propósito
CALL CALL alvo Chamar uma sub-rotina.
INT INT tipo Alterar o fluxo de execução do programa, desviando-se para uma rotina de interrupção.
MOV MOV destino, fonte Copiar o conteúdo do operando fonte para o destino.
POP POP destino Retirar a palavra armazenada no topo da pilha colocando-a no registrador ou posição de memória especificada por destino.
PUSH PUSH fonte Colocar no topo da pilha o valor de fonte.
RET RET [dado] Encerrar uma sub-rotina, transferindo o fluxo do processamento para a RET RET [dado] Encerrar uma sub-rotina, transferindo o fluxo do processamento para a instrução seguinte à chamada da sub-rotina. O valor [dado] é opcional. Nele pode-se especificar um valor que após o retorno da sub-rotina será adicionado ao SP.
Fonte: adaptado de Santos e Raymundi (1989, p. 33-92).
FURBOL
• Vem sendo desenvolvida por vários integrantes do curso de Ciências da Computação da FURB.
• Características �adota o paradigma imperativo.� comandos em língua portuguesa.� comando de condição e repetição.� unidades de programas concorrentes.� mecanismo de sincronização da comunicação entre unidades � mecanismo de sincronização da comunicação entre unidades concorrentes do tipo semáforo.
• Gera código de montagem (assembly) compatível com microprocessadores 8088.
• Silva 2002 – unidades de programas concorrentes.
Trabalho de Conclusão de Curso
Desenvolvimento
Requisitos do Sistema
• Manter os seguintes requisitos do ambiente de Silva (2002):� possuir um editor para os arquivos fonte (Requisito Funcional – RF)� compilar o código fonte FURBOL, gerando como saída código de
montagem (assembly) compatível com microprocessadores 8088 (RF)
� emitir mensagens com os resultados da compilação (RF)� permitir a visualização do código intermediário de três endereços
gerado (RF)� permitir a visualização do código de montagem (assembly) gerado � permitir a visualização do código de montagem (assembly) gerado
(RF)� invocar o montador Turbo Assembler e o ligador Turbo Link para a
geração de código de máquina executável em microprocessadores 8088 (RF)
• Permitir a definição de TADs pelos usuários (RF)
• Usar esquemas de tradução para representar a semântica (Requisito Não-Funcional - RNF)
• Ser implementado usando o ambiente Borland Delphi 7.0 (RNF)
Especificação dos TADs (BNF Extendida)
TAD → id Se nãoSimbolos.SimboloRedeclarado(id.nome) entãoid.simbobj := TSimbolo.Create(id.nome, tsClasse);Simbolos.Instalar(id.simbobj);id.simbobj.TabelaMembros := Símbolos.AbrirEscopo(id.nome);
AtributosTAD
MetodosTAD
‘fim’ TAD.codigo := MetodosTAD.codigo;TAD.codasm := MetodosTAD.codasm;Simbolos.FecharEscopo;
';'
Especificação de um método (BNF Extendida)
MetodoTAD → id Se nãoSimbolos.SimboloRedeclarado(id.nome) entãoid.simbobj := TSimbolo.Create(id.nome, tsMetodo);Simbolos.Instalar(id.simbobj);id.simbobj.TabelaAgregada := Símbolos.AbrirEscopo(id.nome);id.simbobj.TipoMetodo := MetodoTAD.TipoMetodo;ParamOculto := TSimbolo.Create(‘esse’, tsObjeto);Símbolos.Instalar(ParamOculto);
ParamFormais
‘;’
EstruturaDados
CComposto MetodoTAD.codigo := id.nome || ‘proc near’ || CRLF ||CComposto.codigo || CRLF ||‘ret’ || CRLF ||id.nome || ‘endp’;MetodoTAD.codasm := id.nome || ‘proc near’ || CRLF ||‘push bp’|| CRLF ||‘mov bp, sp’|| CRLF ||‘sub sp, ’ || Símbolos.EscopoAtual.LarguraVariaveis || CRLF ||CComposto.codasm ||‘mov sp, bp’|| CRLF ||‘pop bp’ || CRLF ||‘ret’ || Símbolos.EscopoAtual.LarguraParametros + 2 || CRLF ||id.nome || ‘endp’;Símbolos.FecharEscopo;
';'
Esboço da tabela de símbolos para um programa em FURBOL
programa Retangulo;
classe TRetanguloesquerda, topo, direita, fundo: inteiro;
construtor constroi (esquerda, topo, direita, fundo: inteiro);inicio
imprime("Construindo um retangulo");esse.esquerda := esquerda;esse.topo := topo; esse.direita := direita;esse.fundo := fundo;
fim;
procedimento Area ;var
largura, altura, area: inteiro;inicio
imprime("Area do Retangulo:");largura := direita - esquerda;largura := direita - esquerda;altura := fundo - topo;area := largura * altura;imprime(area);
fim;
destrutor destroi ;inicio
imprime("Destruindo um retangulo");fim;
fim;
vargRet : TRetangulo;
iniciocriarobj(gRet, constroi(0, 0, 20, 20));gRet.Area;destruirobj(gRet, destroi);
fim.
Gerencia de memória dos TADs
• Registro de ativação dos métodos encontra-se na pilha.
• Atributos do objeto encontram-se na heap (memória alocada pelo comando criarobj ).
classe TRetanguloesquerda, topo, direita, fundo: inteiro;
construtor constroi (esquerda, topo, direita, fundo: inteiro);inicio
imprime("Construindo um retangulo");esse.esquerda := esquerda;esse.topo := topo; esse.direita := direita;esse.fundo := fundo;
Atributos –heap
Parâmetros – pilha
esse.fundo := fundo; fim;
procedimento Area ;var
largura, altura, area: inteiro;inicio
imprime("Area do Retangulo:");largura := direita - esquerda;altura := fundo - topo;area := largura * altura;imprime(area);
fim;
destrutor destroi ;inicio
imprime("Destruindo um retangulo");fim;
fim;
Variáveis locais – pilha
Criação de objetos
Furbol assembly
classe TPontox, y: inteiro;construtor constroi(x, y: inteiro);inicio
esse.x := x;esse.y := y;
fim;fim;
varponto1: TPonto;ponto2: TPonto;
ponto1 dw ?ponto2 dw ?
Furbol assembly
� Parâmetro do procedimento criarobj .� Tamanho da representação do TAD (atributos).
Furbol assembly
criarobj (ponto1);
(* chamada do construtor é opcional *)
1) push ax2) push 43) call criarobj4) mov ponto1,ax5) pop ax
Furbol assembly
criarobj(ponto2, constroi(gi, gj)); 01) push ax02) push 403) call criarobj04) mov ponto2,ax05) pop ax06) push gj07) push gi08) push ponto209) push bp10) call TPonto@constroi
TAD (atributos).
Parâmetro oculto esse
• Usado para acessar os atributos do objeto.
• Similar ao this do C++.
• É sempre o primeiro parâmetro do método, portanto sua posição na pilha é sempre a mesma.
Chamada de métodos
01)programa teste;02)03)classe TPonto04) x, y: inteiro;05) procedimento setXY(x, y: inteiro);06) inicio07) esse.x := x;08) esse.y := y;09) fim;
• Parâmetros empilhados da direita para a esquerda.
• Em seguida o conteúdo da variável do tipo objeto que está sendo referenciada também é empilhado (parâmetro oculto esse).
09) fim;10)fim;11)12)var13) ponto1: TPonto;14) gi, gj: inteiro;15)inicio16)(...)17) ponto1.setXY(gi, gj);18)(...)19)fim.
Furbol assembly
ponto1.setXY(gi, gj); 1) push gj2) push gi3) push ponto14) push bp5) call TPonto@setXY
Acesso aos atributos
• ES esse
• ES:[deslocamento_do_atributo]
Acesso aos atributos
classe TRetangulotopo, esquerda, fundo, direita : inteiro;
construtor constroi;inicio
topo := 7;esquerda := 77;fundo := 777;direita := 7777;
fim;fim;
Furbol assembly
� Deslocamento calculado pelo compilador consultado a tabela de simbolos do TAD.
� Descolamentos 0, 2, 4 e 6, respectivamente
construtor constroi;inicio
topo := 7;
esquerda := 77;
(...)fim;
01) TRetangulo@constroi proc near02) push bp03) mov bp, sp04) sub sp, 005)06) mov ax,707) mov es,word ptr [bp+6]08) mov word ptr es:[0],ax09)10) mov ax,7711) mov es,word ptr [bp+6]12) mov word ptr es:[2],ax13)14) (...)15) TRetangulo@constroi endp
Palavra reservada nulo
01)programa teste;02)03)classe TPonto04)(...)05)fim;06)07)var08) ponto: TPonto;09)
• Usada para indicar que uma referência a um TAD é inválida (não aponta para um endereço de memória válido).
• Para o compilador é equivalente ao valor 0.
09)10)inicio11) (...) 12) 13) destruirobj(ponto);14) ponto := nulo;15)16) (...)17)fim.
Furbol assembly
ponto := nulo; 1) mov ax,02) mov ponto,ax
Exclusão de objetos
Furbol assembly
destruirobj (ponto1);
( * chamada do destrutor é opcional *)
1) push ax2) mov ax,ponto13) push ax4) call destruirobj5) pop ax
Furbol assembly
destruirobj(ponto2, destroi); 1) push ponto22) push bp3) call TPonto@destroi4) push ax4) push ax5) mov ax,ponto26) push ax7) call destruirobj8) pop ax
Diagrama de casos de uso
Caso de uso compilar
COMPILAR
•Descrição: usuário solicita que o programa seja compilado.
•Ator principal : usuário.
•Cenário principal : compilara) através da interface o usuário escolhe a opção Compilar;b) o sistema compila o programa fonte;c) o sistema invoca o montador Turbo Assembler;d) o sistema invoca o ligador Turbo Link;e) o sistema exibe a mensagem “Programa compilado com sucesso”.
•Cenário deexceção: errodecompilação•Cenário deexceção: errodecompilação•Caso o programa fonte apresente algum erro o sistema encerra a compilação e apresenta qual é o problema para ousuário.
•Cenário de exceção: erro na geração de código de máquina•Caso ocorra algum erro durante a geração do código de máquina o sistema apresenta uma mensagem de erro para ousuário.
•Pré-condição: programa escrito em LP FURBOL.
•Pós-condição: programa em código de máquina criado.
Diagrama de classes
Diagrama de seqüências para caso de uso compilar
Implementação
• Código de Silva (2002) totalmente reaproveitado
• Interface não foi alterada
• Analisador Léxico – novas palavras reservadas relacionadas aos TADs� criarobj� destruirobj� classe� construtor� construtor� destrutor� esse� nulo
• Analisador Sintático – adaptado para seguir a nova especificação da LP
Implementação do não-terminal TAD
01)function TAnalisadorSintatico._TAD(var CodigoAsm : string): string;02)var03) TADName: string;04) MetodosTADCodigo,05) MetodosTADCodAsm: string;06) Simb: TSimbolo;07)begin08) if FLexico.Token = tkIdentificador then begin09)10) if FSimbolos.SimboloRedeclarado(FLexico.Lexe ma) then11) raise ESinErro.CreateFmt(strErrSinIdentDup licado, [FLexico.Linha, FLexico.Coluna, 12)FLexico. Lexema]);13)14) TADName := FLexico.Lexema;15)16) FLexico.ProximoToken;17)18) Simb := TSimbolo.Create(TADName, TipoSimbolo Classe);19) FSimbolos.Instalar(Simb);20) Simb.TabelaMembros := FSimbolos.AbrirEscopo( TADName);21)22) _AtributosTAD;22) _AtributosTAD;23)24) MetodosTADCodigo := _MetodosTAD(Simb, Metodo sTADCodAsm);25)26) if (FLexico.Token = tkReservada) and (FLexic o.Reservada = prFim) then begin27) FLexico.ProximoToken;28) end29) else30) raise ESinErro.CreateFmt(strErrSinTokenEsp erado, [FLexico.Linha, FLexico.Coluna, 31)strLexRes ervadas[prFim]]);32)33) FSimbolos.FecharEscopo;34)35) Result := MetodosTADCodigo;36) CodigoAsm := MetodosTADCodAsm;37)38) if (FLexico.Token = tkEspecial) and (FLexico .Lexema = ';') then begin39) FLexico.ProximoToken;40) end41) else42) raise ESinErro.CreateFmt(strErrSinTokenEsp erado, [FLexico.Linha, FLexico.Coluna, 43)';']);44) end45) else46) raise ESinErro.CreateFmt(strErrSinIdentEsper ado, [FLexico.Linha, FLexico.Coluna]);47)end;
Operacionalidade do sistema
Interface exibindo código intermediário
Interface exibindo código de montagem
Trabalho de Conclusão de Curso
Conclusões
Conclusões
• O objetivo do trabalho foi alcançado� Definir TADs� Quatro tipos de operações� Declarar referências para os TADs em outras unidades do programa � Criar instâncias destes TADs
• Outras contribuições� consistências e correção de bugs� implementação de um procedimento para imprimir inteiros
• As ferramentas mostraram-se adequadas
• Relevância do trabalho� Contribuir com um trabalho de pesquisa realizado pela FURB
Limitações
01)programa limitacoes_arrays;02)03)classe TPonto04) x, y: inteiro;05) procedimento setXY(x, y: inteiro);06) inicio07) esse.x := x;08) esse.y := y;09) fim;10)fim;11)12)classe TPilha13) fPilha: matriz; (* não compila *)14)fim;15)15)16)var17) gPontos: matriz[1..120]: TPonto;18)inicio19) criarobj(gPontos[1]); (* não compila *)20) gPontos[1].setXY(7, 120); (* não compila *)21) destruirobj(gPontos[1]); (* não compila *)22)fim.
Limitações
01)programa limitacao_simbolo_nao_definido;02)03)classe TRetangulo04) topo_esquerda, 05) fundo_direita: TPonto; (* não compila – Tponto ainda não foi definido *)06) 07) (...)08)fim; 09)10)classe TPonto11) x, y: inteiro;12) 13) construtor constroi(x, y: inteiro);14) inicio15) setXY(x, y); (* não compila – setXY ainda não foi definido *)16) fim;16) fim;17) 18) procedimento setXY(x, y: inteiro);19) inicio20) esse.x := x;21) esse.y := y;22) fim;24)fim; 23)24)inicio25) (...)26)fim.
Extensões
• Ampliar a LP para oferecer suporte a orientação a objetos.
• Criar uma biblioteca com funções de entrada e saída para vários dispositivos.
• Implementar a recuperação de erros de compilação.
• Otimizar o código gerado.• Otimizar o código gerado.
• Ampliar a LP para permitir a definição de funções.
• Gerar código para outras plataformas (microcontroladores, .NET, entre outros).
Trabalho de Conclusão de Curso
FIM