RAFF Um Compilador para Facilitar o Aprendizado de Algoritmos Almir Joaquim de Sousa 1, Fábio Silveira Vidal 1, Fredson Vieira Costa 1, Ranildo Costa Santana 1 Curso de Bacharelado em Ciência da Computação Universidade do Tocantins(UNITINS), Palmas, TO, Brasil almir@unitins.br, fvidal@unitins.br, frdson@unitins.br, ranildocosta@hotmail.com Resumo. Este artigo tem como objetivo descrever a implementação de um compilador cuja linguagem de programação que possua uma gramática parecida com a linguagem de programação C e que utilize termos da Língua Portuguesa em seu vocabulário para que a mesma torne-se simples e de boa compreensão, principalmente para os iniciantes em programação, trazendo assim, um melhor aproveitamento da disciplina de Introdução à Programação, nos cursos de nível superior nas áreas de informática e engenharia.
1 Introdução Ao iniciar um curso de nível superior na área de informática ou de engenharia, muitos acadêmicos encontram dificuldades na primeira disciplina referente a algoritmos ou programação de computadores, muitas vezes porque as linguagens utilizam termos em inglês ou por possuírem uma sintaxe complicada. Com o objetivo de amenizar essas dificuldades, este projeto propõe a construção de um compilador que possua uma sintaxe simples e que utilize termos da língua portuguesa. Os princípios e técnicas de construção de compiladores são tão penetrantes, que as idéias encontradas serão usadas muitas vezes na carreira de um cientista da computação. A construção de compiladores se estende através dos temas de linguagens de programação, arquitetura de máquina, teoria das linguagens, algoritmos e engenharia de software. Afortunadamente, poucas técnicas básicas para construção de compiladores podem ser usadas para desenvolver tradutores pra uma ampla variedade de linguagens e máquinas [Aho, Sethi & Ullman 1986]. Posto de forma simples, o compilador RAFF lê um programa escrito numa linguagem a linguagem fonte e o traduz num programa equivalente numa outra linguagem a linguagem alvo (no nosso caso, linguagem de máquina). Como importante parte desse processo de tradução, o compilador relata a seu usuário a presença de erros no programa fonte. A partir do código fonte traduzido para linguagem de máquina o RAFF gera um arquivo executável com o auxílio do compilador Tasm. Com isso o programa pode ser executado e interagir com o usuário através de comandos de entrada e saída. 2 Metodologia 2.1 Análise A metodologia utilizada na análise deste projeto é a UML (Unified Modeling Language), pois é a linguagem-padrão para elaboração da estrutura de projetos de software usando o paradigma orientado a objetos. A UML pode ser empregada para a visualização, a especificação, a construção e a documentação de artefatos que façam uso de sistemas complexos de software. A UML é adequada para a modelagem de sistemas, cuja abrangência poderá incluir sistemas de informação corporativos a serem distribuídos a aplicações baseadas em Web e até sistemas complexos embutidos de tempo real. É uma linguagem muito expressiva, abrangendo todas as visões necessárias ao desenvolvimento e implantação desses sistemas. Apesar de sua expressividade, não é difícil compreender a UML. Aprender a aplicar a UML de maneira efetiva tem início com a formação de um modelo conceitual da linguagem, o que pressupõe o entendimento de três principais elementos: os blocos básicos de construção UML, as regras que determinam como esses blocos de construção deverão ser combinados e alguns mecanismos básicos que se aplicam a toda a linguagem [Booch, Rumbaugh & Jacobson 2000]. A UML é apenas uma linguagem de representação e, portanto, é somente uma parte de método para desenvolvimento de software. A UML é independente de processo, apesar
de ser perfeitamente utilizada orientada a casos de usos, centrada na arquitetura, iterativa e incremental [Booch, Rumbaugh & Jacobson 2000]. 2.2 Implementação A principal ferramenta utilizada na implementação deste compilador foi o Borland C++ Builder. E como ferramentas auxiliares foram utilizadas o ParGen e o Tasm. 2.2.1 Borland C++ Builder O Borland C++ Builder é um ambiente de desenvolvimento de aplicações orientado a objeto que permite desenvolver software para o sistema operacional Windows utilizando a linguagem de programação C++ [Dias 2000]. A razão da criação da linguagem C foi a necessidade de uma ferramenta poderosa para escrever programas, que utilizasse os recursos de máquina de uma forma mais fácil que a linguagem assembly. A linguagem C é derivada da linguagem ALGOL 68 e foi baseada na linguagem B de Ken Thompson. A grande aceitação dessa linguagem decorre da facilidade de conciliar o poder de programação de baixo nível com o seu alto grau de portabilidade [Mateus 2000]. O C++ Builder é uma linguagem de Nível Médio, exatamente pelo fato de usar o C++, apesar de viabilizar facilmente o desenvolvimento de aplicações de diversos tipos, tais como banco de dados, Internet entre outros [Dias 2000]. Quando falamos em C++, às vezes lembramos dos velhos compiladores para DOS da Borland ou da Microsoft, como o Borland C++ e o Microsoft Visual C++, entre outros, mas este é poderoso em relação aos demais e pode ajudar na construção de uma aplicação rápida e sem muito esforço [Dias 2000]. 2.2.2 Tasm (Assembly) Assembly é uma linguagem de programação de baixo nível, que é usada para acelerar algumas tarefas ou para ter um maior controle sobre o hardware. Basicamente ela consiste de sentenças que representam instruções em linguagem de máquina. Tanto que, quando o 8086 surgiu, a programação tinha que ser feita em código de máquina, que não era uma tarefa fácil, deste modo foi desenvolvida uma linguagem onde aquelas instruções binárias forma distribuídas por instruções simbólicas, pois é mais fácil lembrar algo como SUB do 0010110 que é o código em linguagem de máquina que instrui o processador a realizar uma subtração. Quando o compilador C/C++ e Pascal transformam o código fonte em um programa executável, eles simplesmente o transformam em códigos binários que o processador entende. E uando o Debug, pode ser tranqüilamente visto o programa em Assembly. Existem basicamente três formas de usar o Assembly, uma é ele puro, o qual aceita somente a instrução seca e é possível usar o Debug e salvar na forma de arquivos.com. A segunda é usar o Macro Assembly, o qual é usado pelo TASM, e permite usar constantes,
bibliotecas e outros artifícios. A última é usar ele junto ao seu programa, o chamado Embedded Assembly. 2.2.3 ParGen O Pargen é uma ferramenta de programação YACC e Léxico. O software possui duas versões de YACC e Léxico, chamadas AYACC e Alex, além disso esta ferramenta possui uma interface gráfica com o usuário. O ParGen pode gerar analisadores léxicos para C, C++ e Java e inclui as duas variações de caracteres Unicode e Multibyte Character Set (MBCS). No seu aplicativo você pode criar várias instâncias deste Parser e Analisador Léxico. 3 RAFF O Compilador desenvolvido possui quatro módulos principais:?? Analisador Léxico;?? Analisador Sintático;?? Analisador Semântico; e,?? Gerador de Código. O Analisador Léxico é a primeira fase de um compilador. Sua tarefa principal é a de ler os caracteres de entrada e produzir uma seqüência de tokens (identificadores) que serão utilizados na análise sintática. Além disso, o Analisador Léxico também elimina os comentários e os caracteres neutros como espaços em branco, tabulações ou avanço de linha, facilitando assim, a análise sintática [Aho, Sethi & Ullman 1986]. O Analisador Sintático obtém uma cadeia de tokens provenientes do Analisador Léxico e verifica se a mesma pode ser gerada pela linguagem-fonte, relatando quaisquer erros de sintaxe de forma inteligível para o usuário [Aho, Sethi & Ullman 1986]. O Parser contém a gramática da linguagem usada no compilador, sendo consultado pelo Analisador Sintático para que o mesmo possa detectar os erros sintáticos que possam estar contidos no código fonte [Aho, Sethi & Ullman 1986]. A gramática é utilizada para descrever a maioria das sintaxes da linguagem de programação RAFF. A maioria porque o analisador léxico produz uma seqüência de tokens a partir dos caracteres de entrada. O Analisador Semântico verifica os erros semânticos no programa fonte e captura as informações de tipo para a fase subseqüente de geração de código. Utiliza a estrutura hierárquica determinada pela fase de análise sintática, a fim de identificar os operadores e operandos das expressões e enunciados [Aho, Sethi & Ullman 1986]. A verificação de tipos é um componente muito importante da análise sintática. Nela o compilador checa se cada operador recebe os operandos que são permitidos pela especificação da linguagem fonte. O Gerador de Código pode ser dividido em duas partes, para uma melhor implementação, são elas: Gerador de Código Intermediário e o Gerador de Código de Máquina propriamente dito.
Após as análises léxica, e sintática, o Compilador RAFF gera uma representação intermediária explícita do programa fonte. Essa representação intermediária possui duas propriedades importantes: é fácil de produzir e fácil de traduzir no programa alvo. A fase final do compilador é a geração de código alvo, consistindo em código de máquina relocável. As localizações de memória são selecionadas para cada uma das variáveis usadas pelo programa. Então, as instruções intermediárias são cada uma, traduzidas numa seqüência de instruções de máquina que realizam a mesma tarefa. Um aspecto crucial é a atribuição das variáveis aos registradores [Aho, Sethi & Ullman 1986]. 3.1 Descrição da Linguagem A linguagem do compilador RAFF utiliza termos da Lingua Portuguesa no seu vocabulário e possui uma sintaxe semelhante à linguagem de programação C, para que a migração para o C seja mais confortável. Porém a linguagem RAFF terá uma gramática reduzida possuindo a seguinte sintaxe: numero? 0..9 letra? a..z A..Z espaco? caracter? Qualquer símbolo da Tabela ASCII string? {caracter} inteiro? numero {numero} sep_decimal?. sep_exp?, sep_comando? ; real? inteiro inteiro sep_decimal inteiro valor? inteiro real string abre_bloco? { fecha_bloco? } abre_par? ( fecha_par? ) op? + - / * = = = > < >= <=!!= op_atrib? = constante? constante estrutura? estrutura exp? exp op exp abre_par exp fecha_par - exp valor id seq_esq? exp {sep_exp exp}
seq_parametros? ((valor id exp) {sep_exp (valor id exp) }) e id? letra {letra numero} dec_variavel? id espaco (id id op_atrib valor){sep_exp (id id op_atrib valor)} sep_comando dec_parametro? (id espaco id {sep_exp id espaco id }) e dec_const? constante dec_variavel dec_funcao? id espaco id abre_par dec_paramentros fecha_par sep_comando dec_estrutura? estrutura id abre_bloco dec_variavel {dec_variavel }fecha_bloco sep_comando imp_funcao? id espaco id abre_par dec_paramentros fecha_par abre_bloco comando {comando} fecha bloco comando? se se_senao enquanto faca_enquanto para chama_func dec_variavel dec_const comandos? comando (abre_bloco comando {comando} fecha_bloco) se? se abre_par exp fecha_par comandos se_senao? se abre_par exp fecha_par comandos senao comandos enquanto? enquanto abre_par exp fecha_par comandos faca_enquanto? faca comandos enquanto abre_par exp fecha_par para? para abre_par seq_exp sep_comando exp sep_comando seq_exp fecha_par chama_func? id abre_par seq_paramentros fecha_par sep_comando programa? {dec_const dec_variavel imp_func dec_func dec_estrutura} A linguagem RAFF terá os seguintes tipos: inteiro, real e caracter, possibilitando ao programador trabalhar com vetores destes tipos e também declarar novos tipos. 4 Relatórios Os relatórios a serem emitidos apresentam os erros ocorridos em alguma das fases da compilação, referente à análise. Em um outro relatório o sistema emite o sucesso na compilação de um programa, podendo conter: número de linhas compiladas, tamanho do arquivo executável e tempo de compilação. Um relatório que também será emitido é o próprio código fonte do programa. 5 Conclusão Um projeto de compilador pode estar destinado a processar uma nova linguagem-fonte ou a produzir um novo código-alvo. Usando a estrutura adotada neste projeto, obtemos em última análise, um projeto para um compilador que consiste em um conjunto de módulos. Três fatores distintos impactam o projeto e a implementação desses módulos:?? Temas das Linguagens-Fontes;
?? Os Temas das Linguagens-Alvo; e,?? Critérios de Desempenho. Seguindo esses fatores, o Compilador RAFF desempenhará o papel de gerar um código executável, a partir de uma linguagem mais amigável, minimizando as dificuldades de aprendizagem dos alunos que ingressam nos cursos de informática e engenharia. 6 Referências [Aho, Sethi & Ullman 1986] Aho, A. V.; Sethi, R.; Ullman, J. D. Compiladores Princípios, Técnicas e Ferramentas. Rio de Janeiro, 1986. Editora Guanabara Koogan SA. [Booch, Rumbaugh & Jacobson 2000] Booch, G.; Rumbaugh, J.; Jacobson, I. UML Guia do Usuário. Rio de Janeiro, 2000. Editora Campus Ltda. [Lee & Tepfenhart 2001] Lee, R. C.; Tepfenhart, W. M. UML E C++ - Guia Prático de Desenvolvimento Orientado a Objeto. São Paulo, 2001. Makron Books Ltda. [Shildt & Guntle 2001] Shildt, H.; Guntle, G. Borland C++ Builder Referência Completa. Rio de Janeiro, 2001. Editora Campus Ltda. [Pereira 2000] Pereira, S. do L. Estrutura de Dados Fundamentais. São Paulo, 2000 3ª Edição. Editora Érica. [Dias 2000] Dias, A. de S. Desenvolvendo em C++ Builder 5.0. Rio de Janeiro, 2000. Editora Ciência Moderna Ltda. [Mateus 2000] Mateus, C. A. C++ Builder 5 Guia Prático. São Paulo, 2000. Editora Érica. [Tenenbaum, Langsam & Augenstein 1995] Tenenbaum, A. M.; Langsam, Y.; Augenstein, M. J. Estrutura de Dados Usando C. São Paulo, 1995. Editora Makron Books. [Deitel & Deitel 2001] Deitel, H. M., Deitel, P. J. C++ Como Programar. Porto Alegre, 2001 3ª Edição. Editora Bookman.