FACULDADE DE INFORMÁTICA DE PRESIDENTE PRUDENTE SUPERIOR EM TECNOLOGIA EM DESENVOLVIMENTO WEB DESENVOLVIMENTO DE UM COMPILADOR PARA PSEUDOLINGUAGEM E FLUXOGRAMA RODRIGO CESAR DE OLIVEIRA Presidente Prudente – SP 2008 FACULDADE DE INFORMÁTICA DE PRESIDENTE PRUDENTE SUPERIOR EM TECNOLOGIA EM DESENVOLVIMENTO WEB DESENVOLVIMENTO DE UM COMPILADOR PARA PSEUDOLINGUAGEM E FLUXOGRAMA RODRIGO CESAR DE OLIVEIRA Trabalho de Conclusão de Curso, apresentado à Faculdade de Informática de Presidente Prudente, Curso Superior em Tecnologia em desenvolvimento Web, Universidade do Oeste Paulista, como parte dos requisitos para a sua conclusão. Área: Análise de Algoritmos e Complexidade de Computação Orientadores: MSc. Francisco Assis da Silva MSc. Ana Paula D. Parizoto Fabrin MSc. Leandro Luiz de Almeida Presidente Prudente – SP 2008 004 Oliveira, Rodrigo Cesar. Desenvolvimento de um compilador para Pseudolinguagem e Fluxograma / Rodrigo César de Oliveira. – Presidente Prudente - SP: UNOESTE, 2008. 58f.: il. Trabalho de Conclusão de Curso (Graduação em Superior em Tecnologia em desenvolvimento Web) – Universidade do Oeste Paulista – UNOESTE: Presidente Prudente – SP, 2008. 1. Construção de Algoritmos, 2. Pseudolinguagem, 3. Fluxograma, I. Autor. II. Título. DEDICATÓRIA Dedico este trabalho a todas as pessoas, que por intermédio de Deus, puderam me ajudar nas horas difíceis e mostraram-me o caminho certo a seguir, mesmo nas horas de dificuldades e incertezas, ao longo do desenvolvimento deste trabalho. AGRADECIMENTOS Primeiramente a DEUS, que me permitiu conquistar mais essa vitória, dando-me paciência, sabedoria e discernimento em todos os momentos de dificuldade e desânimo durante a realização deste trabalho. A todos os meus professores, que na rigidez de seus ensinamentos fizeram aprimorar meus conhecimentos, em especial aos meus orientadores, Francisco Assis da Silva, Ana Paula D. Parizoto Fabrin e Leandro Luiz de Almeida, pelo incentivo, apoio, estímulo e amizade durante este longo período de orientação para que eu pudesse concluir esta pesquisa. Enfim, a todos os meus companheiros de companheirismo e os muitos momentos de alegria compartilhados. turma, pelo “A mente que se abre a uma nova idéia jamais voltará ao seu tamanho original.” Albert Einstein RESUMO DESENVOLVIMENTO DE UM COMPILADOR PARA PSEUDOLINGUAGEM E FLUXOGRAMA Atualmente no curso Superior de Tecnologia em desenvolvimento Web o ensino de algoritmo é feito de modo tradicional, baseado no ensinamento de cada assunto e treino com exercícios. Pensa-se que a adição de novas técnicas e o uso de ferramentas possam melhorar o aprendizado dos alunos. Portanto, o objetivo deste trabalho é desenvolver uma ferramenta acadêmica que permite a visualização de algoritmos por meio de compilação e tradução. A ferramenta terá dois ambientes, um para ser digitado o portugol (pseudolinguagem) e outro para ser desenhado o fluxograma. Após ser digitado ou desenhado, a ferramenta irá compilar e traduzir o código para a linguagem JavaScript. O compilador RAF, assim chamado, será usado pelos alunos para auxiliar a compreensão dos algoritmos e desenvolver lógica de programação. Palavras-Chave: Fluxograma, Pseudolinguagem, Compilador e Construção de Algoritmo ABSTRACT DEVELOPING A COMPILER FOR PSEUDOLINGUAGE AND FLOW Currently in the course of High Technology in Web development algorithm of the teaching is done in the traditional way, based on the studying of each subject and with training exercises. It is believed that the addition of new techniques and the use of tools can improve the learning of students. Therefore, the objective of this work is to develop an academic tool that allows the viewing of algorithms through compilation and translation. The tool has two environments, one to be spelled the pseudolinguage and another designed to be the flow. After being typed or designed, the tool will compile and translate the code for the JavaScript language. The compiler RAF will be used by pupils to help develop understanding of algorithms and logic programming. Keywords: Flow, Pseudolinguage, Compiler and Construction of Algorithm. LISTA DE FIGURAS Figura 1 – Ambiente do G-Portugol................................................................... 13 Figura 2 – Ambiente do Portugol IDE................................................................ 14 Figura 3 – Ambiente do CIFluxProg. ................................................................. 15 Figura 4 – Execução de um programa fonte. .................................................... 17 Figura 5 – Estrutura de um Compilador. ........................................................... 17 Figura 6 – Analisador Léxico. ............................................................................ 18 Figura 7 – Estrutura de uma árvore................................................................... 20 Figura 8 – Análise recursiva com retrocesso..................................................... 21 Figura 9 – Exemplo de fatoração. ..................................................................... 22 Figura 10 – Análise Redutiva. ........................................................................... 24 Figura 11 – Redução da sentença abbcde........................................................ 24 Figura 12 – Classe ExecutaPrograma............................................................... 36 Figura 13 – Classe Main ................................................................................... 36 Figura 14 – Classe Principal.............................................................................. 39 Figura 15 – Classe RedoAction......................................................................... 40 Figura 16 – Classe UndoAction......................................................................... 40 Figura 17 – Classe CodigoGerado. ................................................................... 41 Figura 18 – Funções adicionais........................................................................ 42 Figura 19 – Classe Token. ................................................................................ 44 Figura 20 – Classe Lexico. ................................................................................ 45 Figura 21 – Classe Sintatico.............................................................................. 52 Figura 22 – Classe Forma. ................................................................................ 55 Figura 23 – Ligações usadas no desenho de um fluxograma. .......................... 56 Figura 24 – Classe Ligacao............................................................................... 56 LISTA DE TABELAS Tabela 1 – Tabela de Tokens, Padrões e Lexemas. ......................................... 19 Tabela 2 – Operadores aritméticos ................................................................... 28 Tabela 3 – Operadores Lógicos ........................................................................ 29 SUMÁRIO 1 INTRODUÇÃO.............................................................................................. 12 1.1 Trabalhos Correlatos................................................................................. 13 1.1.1 G-Portugol .............................................................................................. 13 1.1.2 Portugol IDE ........................................................................................... 14 1.1.3 CIFluxProg.............................................................................................. 14 1.2 Estrutura da Monografia............................................................................ 15 2 COMPILADORES ......................................................................................... 16 2.1 Analisador léxico ou Analise léxica ........................................................... 18 2.2 Tokens, padrões, lexemas ........................................................................ 18 2.3 Análise Sintática........................................................................................ 19 2.4 Tipo de Analisadores sintáticos................................................................. 20 2.5 Top-Down (Análise Descendente)............................................................. 20 2.5.1 Recursivo com retrocesso (backtracking)............................................... 21 2.5.2 Recursivo preditivo ................................................................................. 21 2.5.3 Tabular preditivo..................................................................................... 22 2.6 Bottom-Up (Análise redutiva) .................................................................... 23 2.7 Análise Semântica..................................................................................... 24 2.8 Geração de código .................................................................................... 25 2.8.1 Otimizador de Código............................................................................. 25 2.9 Especificação da BNF ............................................................................... 26 3 PORTUGUÊS ESTRUTURADO ................................................................... 27 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 Variável ..................................................................................................... 27 Atribuição .................................................................................................. 28 Operadores ............................................................................................... 28 Comando de Entrada e Saída ................................................................... 29 Corpo do algoritmo.................................................................................... 29 Estrutura de seleção ................................................................................. 29 Estrutura de repetição ............................................................................... 30 Exemplos de Algoritmos............................................................................ 31 4 IMPLEMENTAÇÃO (CLASSES) ................................................................... 35 4.1 Modelagem das classes em UML ............................................................. 35 4.2 Classe Executa programa ......................................................................... 36 4.3 Classe Main............................................................................................... 36 4.4 Classe Principal......................................................................................... 37 4.4.1 Classe RedoAction ................................................................................. 39 4.4.2 Classe UndoAction ................................................................................. 40 4.5 Classe CodigoGerado ............................................................................... 41 4.6 Classe Token ............................................................................................ 42 4.7 Classe Lexico ............................................................................................ 44 4.8 Classe Sintatico......................................................................................... 45 4.9 Classe Forma ............................................................................................ 53 4.10 Classe Ligacao.......................................................................................... 55 5 CONCLUSÕES............................................................................................. 57 REFERÊNCIAS BIBLIOGRÁFICAS .................................................................. 58 12 1 INTRODUÇÃO Para tornar problemas do mundo mais fácies e rápidos de se resolver, surgem linguagens e softwares na área da informática que auxiliam o desenvolvimento de sistema para soluções de problemas, como: Sistemas Operacionais, tradutores e aplicativos. Os softwares são formados por um conjunto de código padronizado conhecido como linguagem de máquina. Geralmente essa linguagem não é manipulada pelo homem por causa da sua complexidade, mas manipulada e gerada por tradutores. A linguagem de máquina é muita complicada aos olhos humanos, por isso foram criadas linguagens mais próximas do mundo real, conhecidas como linguagens de programação ou linguagens de alto nível. Um compilador é um programa que, a partir de um código escrito em uma linguagem, cria um programa semanticamente equivalente, porém escrito em outra linguagem. O objetivo deste projeto é desenvolver uma ferramenta acadêmica que auxilie na aprendizagem dos alunos de Tecnologia em Desenvolvimento web. A ferramenta possui dois ambientes, um para a escrita do portugol e outro para desenvolvimento de algoritmo na forma fluxograma. Ela possibilita a exibição dos erros cometidos e a tradução para a linguagem JavaScript, permitindo uma melhor visualização do resultado, sendo mais simples para os alunos interagir e entender os processos de escrita e desenvolvimento de soluções de problemas computacionais. 13 1.1 Trabalhos Correlatos Nesta seção, resumem-se os resultados da investigação a respeito da a área pesquisada, onde foi possível identificar trabalhos existentes, relacionados a este. Os trabalhos são: G-Portugol, Portugol IDE e ClFluxProg. 1.1.1 G-Portugol G-Portugol (2004) é um dialeto da linguagem portugol (ou português estruturado), muito usado para descrever algoritmos de forma livre e espontânea, em português. Em geral, livros dedicados ao ensino de algoritmos, lógica e estruturas de dados incorporam alguma forma dessa linguagem. A proposta do projeto é oferecer uma implementação para a linguagem G-Portugol, fornecendo ferramentas que ofereçam recursos de edição, compilação e execução de algoritmos, de forma a favorecer estudantes que dão os primeiros passos no aprendizado de desenvolvimento de softwares, bem como professores que ensinam disciplinas relacionadas. A Figura 1 apresenta o ambiente do G-Portugol. Figura 1 – Ambiente do G-Portugol. 14 1.1.2 Portugol IDE Portugol IDE (2008) é um simulador de linguagem algorítmica desenvolvido para apoio às aulas de Introdução à Programação dos Cursos de Engenharia da Escola Superior de Tecnologia de. A Figura 2 apresenta o ambiente do Portugol IDE. Figura 2 – Ambiente do Portugol IDE. 1.1.3 CIFluxProg O CIFluxProg (Santiago, 2004) é uma ferramenta de apoio para as disciplinas iniciais da área de programação, como por exemplo, as disciplinas de algoritmos. Desenvolveu-se esta ferramenta que permite aos alunos implementarem e testarem suas soluções lógicas de programação tanto em Portugol como em Fluxograma. Contando com o auxílio do teste de mesa e com a interpretação da solução, inclusos na ferramenta, a verificação de integridade das soluções pode ser verificada. Esta ferramenta flexibiliza o processo de treinamento dos alunos, uma vez que estes podem verificar o funcionamento das suas soluções na prática, visualizando em detalhes os passos e os resultados da sua solução. A Figura 3 apresenta o ambiente do CIFluxProg. 15 Figura 3 – Ambiente do CIFluxProg. 1.2 Estrutura desta Monografia Após o capítulo introdutório, a presente monografia está organizada conforme a estrutura citada a seguir. No capítulo 2, são tratados os conceitos referentes a Compiladores, Análises e Técnicas. O capítulo 3 aborda assuntos relacionados a pseudolinguagem. O capítulo 4 trata dos conceitos relacionados à implementação das classes usada no projeto. No capítulo 5, são abordadas as conclusões do projeto. 16 2 COMPILADORES Neste Capítulo é abordada a evolução das linguagens de programação e compiladores e suas funcionalidades. A primeira linguagem de programação para computadores foi provavelmente Plankalkül, criada por Konrad Zuse na Alemanha Nazista, mas não teve nenhum impacto no futuro das linguagens de programação. A primeira linguagem de programação de alto nível amplamente usada foi Fortran, criada em 1954 (AHO, 1986). Ao longo dos anos foram propostas muitas linguagens de programação, algumas obtiveram sucesso e são conhecidas, como Basic, C, C++, Cobol, Fortran, Java e Pascal. Outras tiveram grande influência acadêmica, mas são conhecidas apenas por alguns ou por sua importância histórica. Todo software que converta ou traduz uma linguagem de programação específica em uma linguagem interpretável pela máquina é chamada de tradutor. Definido em Aho (1995) como: Um compilador é um programa que lê um programa escrito em uma linguagem – a linguagem de origem – e o traduz em um programa equivalente em outra linguagem – a linguagem destino. Como uma importante parte no processo de tradução, o compilador reporta ao seu usuário a presença de erros no programa origem. Um Compilador é um tradutor que a partir de uma linguagem escrita, traduz para outra linguagem, a linguagem objeto, que será interpretada pela máquina. O intervalo de tempo no qual ocorre a conversão ou tradução de um programa fonte para um programa objeto é chamado de tempo de compilação. O programa objeto é executado no intervalo de tempo chamado de tempo de execução. Os dois processos acontecem em tempos distintos. (AHO, 1986). A Figura 4 ilustra a execução de um programa fonte. 17 Figura 4 – Execução de um programa fonte. O programa fonte pode ser escrito em qualquer linguagem de alto nível, por exemplo, Pascal, C++, Java, e o programa objeto em qualquer outra linguagem de alto nível ou linguagem de máquina. Um compilador pode ter várias partes como: analisador léxico, analisador sintático, analisador semântico, otimizador e gerador de código. A Figura 5 apresenta a estrutura de um compilador ilustrando as suas partes. Figura 5 – Estrutura de um Compilador. Fonte: Compiladores: princípios, técnicas e ferramentas (AHO, 1986). 18 2.1 Analisador léxico ou Análise léxica Análise léxica é a primeira fase de um compilador, nela é analisada a entrada de caracteres que produz uma seqüência de símbolos chamados “símbolos léxicos” (lexical tokens), ou somente "símbolos" (tokens). Os tokens são separados de duas maneiras: quando possui algum significado para linguagem ou quando não faz parte da linguagem. Os tokens que possuem algum significado geralmente são palavras reservadas, identificadores, delimitadores entre outros. Os tokens que não fazem parte da linguagem geralmente são espaços em brancos, tabulações, caracteres de avanço de linha e comentários. Todos os tokens devem ser armazenados em uma tabela interna, tanto para indicar erros léxicos como para servir de entrada para o analisador sintático. A Figura 6 apresenta o funcionamento do analisador léxico. Figura 6 – Analisador Léxico. Fonte: Compiladores: princípios, técnicas e ferramentas (AHO, 1986). As linhas do programa fonte devem ser controladas e armazenadas durante a varredura da análise léxica, pois os erros de análises sintáticas (parse) e/ou léxicas podem estar associados ao programa fonte. 2.2 Tokens, padrões, lexemas Não poderia deixar de ser abordados termos usados na análise léxica, como tokens, padrões e lexemas. 19 Tokens são símbolos usados na gramática de uma linguagem. Na maioria das linguagens de programação, as seguintes construções são tratadas como tokens: palavras-chave, operadores, identificadores, constantes, cadeias literais (strings) e símbolos de pontuação como parênteses, vírgulas e pontos. Um padrão (pattern) é uma regra que descreve um conjunto de lexemas que podem representar um token na linguagem. Estas regras normalmente são descritas na forma de expressões regulares. Lexema é o valor do token, muitas vezes aparecendo como um atributo para as fases seguintes de compilação. Enquanto o analisador léxico procura tokens, na geração do código precisa dos lexemas para produzir significado. A Tabela 1 apresenta o exemplo de tokens, padrões e lexmas. Tabela 1 – Tabela de Tokens, Padrões e Lexemas. Token Lexema Padrão Numero 3.1415 ([+,-])([0-9])*(.([0-9])+) O reconhecimento de tokens é realizado a partir de um documento do tipo BNF (Backus-Naur-Form). 2.3 Análise Sintática Segundo Aho (1995), o analisador sintático (parser) obtém uma seqüência de tokens do analisador léxico e verifica se esta seqüência pode ser gerada pela gramática da linguagem. Segundo Price (2000), o analisador sintático identifica seqüências de símbolos que constituem estruturas sintáticas (por exemplo, expressões, comandos), por meio de uma varredura ou parsing da representação interna (cadeia de tokens) do programa fonte. O analisador sintático produz uma estrutura de árvore, chamado árvore de derivação, que exibe a estrutura sintática do texto fonte, resultante da aplicação das regras gramaticais da linguagem. A árvore é composta por um elemento principal chamado raiz, que possui ligações para outros elementos, que são denominados de galhos ou filhos. Estes galhos levam a outros elementos que também possuem outros 20 galhos. O elemento que não possui galhos é conhecido como folha ou nó terminal. A Figura 7 apresenta a estrutura de uma árvore Figura 7 – Estrutura de uma árvore. Todas as linguagens de programação possuem regras que descrevem a estrutura sintática de programas. A sintaxe de uma linguagem de programação pode ser descrita por meio de uma gramática livre de contexto ou notação BNF. 2.4 Tipo de Analisadores sintáticos A tarefa do analisador sintático é determinar se uma entrada de dados pode ser derivada de um símbolo inicial com as regras de uma gramática formal. Isso pode ser feito de duas maneiras: • Top-Down ou descendente: analisa a gramática desde seu início até o seu fim, construindo sua árvore de derivação a partir do símbolo inicial da gramática, fazendo com que ela cresça até atingir suas folhas; • Bottom-Up ou redutiva: analisa a gramática do fim para o seu começo, a partir dos tokens de derivação do código-fonte, constrói a árvore até o símbolo inicial da gramática. 2.5 Top-Down (Análise Descendente) O Analisador sintático de cima para baixo (Top-Down) tenta deduzir as produções da gramática a partir do nó raíz. Ele lê a entrada de texto da esquerda para a direita, e produz uma derivação mais à esquerda. Segundo Aho (1986), a análise Top-Down pode ainda ser vista numa tentativa de se 21 construir uma árvore gramatical, para a cadeia de entrada, a partir da raiz, criando nós da árvore gramatical em pré-ordem. Existem três tipos de analisadores sintáticos descendentes: • Recursivo com retrocesso (backtracking); • Recursivo preditivo; • Tabular preditivo. Segundo Thosom (2004) um analisador sintático recursivo preditivo tenta prever a construção seguinte na cadeia de entrada com base em uma ou mais marcas de verificação à frente. Já um analisador sintático recursivo com retrocesso testa diferentes possibilidades de análise sintática de entrada, retrocedendo se alguma possibilidade falhar. 2.5.1 Recursivo com retrocesso (backtracking) Análise recursiva com retrocesso pode ser vista como uma tentativa de se encontrar uma derivação mais à esquerda para uma cadeia de entrada. Equivalentemente, pode ser vista como uma tentativa de construir uma árvore gramatical em pré-ordem. Caso o token não define distintamente a produção a ser usada, então todas as alternativas vão ser tentadas até que se obtenha sucesso ou falhe. A Figura 8 apresenta um exemplo da análise recursiva com retrocesso A aB | aC | aX Figura 8 – Análise recursiva com retrocesso. 2.5.2 Recursivo preditivo Segundo Aho (1986) em muitos casos, escrevendo-se cuidadosamente uma gramática, eliminado-se recursão à esquerda e fatorandose à esquerda a gramática, será obtido uma nova gramática processável por 22 um analisador sintático de descendência recursiva que não necessite de retrocesso, isto é, um analisador sintático preditivo. Quando é construído um analisador sintático preditivo, é preciso conhecer o símbolo corrente na entrada a e o não-terminal A a ser expandido, qual das alternativas da produção A a1 | a2 ...| an é a única que deriva uma cadeia começando por a. Ou seja, a alternativa adequada é detectar apenas primeiro símbolo da mesma deriva e assim partir para os outros processo de fatoração. As construções do controle são feita do seguinte modo de fatoração da Figura 9: A aY Y 1| 2 | ... |n Figura 9 – Exemplo de fatoração. Com o processo de fatoração o compilador sempre caminhará adiante e com menos tempo de execução. 2.5.3 Tabular preditivo Na análise Preditiva Tabular é possível construir analisadores preditivos não recursivos que utilizam uma pilha explícita ao invés de chamadas recursivas (pilha implícita). Esse tipo de analisador implementa um autômato de pilha controlado por uma tabela de análise. O princípio do reconhecimento preditivo é a determinação da produção a ser aplicado, cujo lado direito irá substituir o símbolo não-terminal que se encontra no topo da pilha. O analisador busca a produção a ser aplicada na tabela de análise, levando em conta o nãoterminal no topo da pilha e o token sob o cabeçote de leitura. O analisador preditivo compreende uma fita de entrada, uma pilha e uma tabela de análise. A fita de entrada contém a sentença a ser analisada seguida de $, símbolo que marca o fim da sentença. A tabela de análise é uma matriz M com n linhas e t+1 colunas, onde n é o número de símbolos não-terminais e t, o número de terminais – a coluna extra corresponde ao $. 23 Segundo Price (2000), a implementação de um analisador preditivo tabular tem como maior dificuldade a construção da tabela de análise. Para construir essa tabela, é necessário computar duas funções associadas à gramática: as funções FIRST e FOLLOW. Para computar FIRST(x) para um símbolo x da gramática, aplicam-se regras até que não se possam adicionar mais terminais ou ε ao conjunto em questão. As regras são: 1. Se a é terminal, então FIRST(a) = {a}; 2. Se x → ε é uma produção, então adicione ε a FIRST(x); 3. Se x → y1y2...yk e, para algum i, todos y1y2...yi-1 derivam ε, então FIRST(yi) está em FIRST(x). Se todo yj {j=1,2,...,k} deriva ε, então ε está em FIRST(x). Para computar FOLLOW(x), aplicam-se regras até que não se possa adicionar mais símbolos ao conjunto em questão. As regras são: 1. Se S é símbolo inicial da gramática e $ é o marcador de fim da sentença, então $ está em FOLLOW(S); 2. Se existe uma produção do tipo A → αXβ, então todos os terminais de FIRST(β) faz parte de FOLLOW(X); 3. Se existe produção do tipo A → αX ou A → αXβ sendo que β→*ε, então todos os terminais que estiverem em FOLLOW(A) fazem parte de FOLLOW(x). 2.6 Bottom-Up (Análise redutiva) Segundo Aho (1986) análise redutiva também conhecida como análise de empilhar e reduzir, tenta construir uma árvore gramatical para uma cadeia de entrada começando pelas folhas (o fundo) e trabalhando árvore acima em direção a raiz (o topo). Pode-se pensar neste processo como o de “reduzir” uma cadeia w ao símbolo de partida de uma gramática. A cada passo de redução, uma sub-cadeia particular, que reconheça o lado direito de uma produção, é substituída pelo símbolo à esquerda daquela produção e, se a subcadeia tiver sido escolhida corretamente a cada passo, uma derivação mais à direita terá sido rastreada na ordem inversa. A Figura 10 apresenta a análise redutiva. 24 S aABe A Abc | b Bd. Figura 10 – Análise Redutiva. A sentença abbcde pode ser reduzida a S mostrado na Figura 11. abbcde aAbcde aAde aABe S Figura 11 – Redução da sentença abbcde. 2.7 Análise Semântica A análise semântica é utilizada para verificar cada comando, erros semânticos no programa fonte e capturar as informações de tipo para a fase subseqüente de geração de código. As tarefas básicas desempenhadas durante a análise semântica incluem a verificação de tipos, a verificação do fluxo de controle e a verificação da unicidade da declaração de variáveis. Dependendo da linguagem de programação, outros tipos de verificações podem ser necessários. Exemplo, “Todas variáveis foram declaradas?”. Segundo Aho (1995), a fase de análise semântica verifica o código fonte para detectar erros de semântica e ao mesmo tempo coleta informações de tipo para a geração de código. Usando a estrutura hierárquica gerada pela fase de análise sintática, operadores e operandos de expressões e declarações são identificados. Um importante componente da análise semântica é a verificação de tipos. Nesta fase o compilador verifica se cada operador tem os operandos permitidos pela especificação da linguagem. Um exemplo é a utilização de números para índices de vetores. A maioria das linguagens 25 permite o uso apenas de números inteiros, sendo números reais tratados como erro neste caso. 2.8 Geração de código Na fase de geração de código a representação intermediária do código é traduzida para a linguagem de máquina da máquina alvo, ou para a linguagem destino. Entre as tarefas do gerador de código estão: gerenciamento de memória, seleção de instruções e alocação de registradores entre outras. 2.8.1 Otimizador de Código Segundo Aho (1995), compiladores devem produzir códigos tão bons quanto se fossem escritos à mão. A realidade é que este objetivo só é alcançado em poucos casos e com dificuldade. Entretanto, o código produzido por compiladores pode quase sempre rodar mais rápido ou ocupar menos espaço, ou ainda ambos. Esta melhoria é alcançada por transformações que tradicionalmente são chamadas de “otimizações”, sendo o termo otimização não muito preciso, já que não há garantias de que o código resultante é o melhor possível. 26 2.9 Especificação da BNF <comeco> programa nome_prog ; <bloco princ>. <decl> variaveis <lista_id> | ξ <lista_id> <tipox> : id <varios_id>; <contlista_id> <tipox> id | <tipo_bas> <contlista_id> <lista_id> | ξ <varios_id> ξ | , id <varios_id> <tipo_bas> inteiro | real | caracter | lógico <tipo> tipo <tipo_cons> | ξ <tipo_cons> id = vetor(num_int) de <tipo_bas> | matriz(num_int, num_int) de <tipo_bas><tipo_cons>| ξ <bloco_princ> inicio <tipo><decl><varios_cmds> fim <varios_cmds> <cmd> ; <varios_cmds> | ξ <cmd> <cmd_if> | <cmd_enquanto> | <cmd_para> | <cmd_faça> | <cmd_atrib> | <cmd_ler> | <cmd_escrever> | <cmd_caso> <cmd_enquanto>enquanto <exp_logica> faca <bloco> <cmd_para> para id de <val3> ate <val3> passo <val3> faca <bloco> <cmd_faça> faca <bloco> enquanto <exp_logica> <bloco> inicio <varios_cmds> fim | <cmd> <cmd_if> se (<exp_logica>) entao <bloco> <varios_if> <varios_if> senao <bloco> | ξ <cmd_atrib> id := <exp_arit> | <val_logico> <exp_arit> (<exp_arit>)<y> | <func_mat><y> | <val><y> <y> <oper><exp_arit> | ξ <func_mat> pot(<val>,<val>) | rad(<val>) <val> id | num_int | num_real | <val5> | (<val>) <val2> num_it | num_real | ID <val3> id | num_it <oper> + | - | / | * | mod | div <cmd_ler> leia( id <váriosleia_id>) <váriosleia_id> ξ | id <váriosleia_id> <cmd_escrever> escreva (<varios_escrever>) <varios_escrever> literal <varios_escrever2> | <exp_arit> <varios_escrever2> <varios_escrever2> , <varios_escrever> | ξ <exp_logica> <exp_arit> <operador_relacional> <exp_arit> <cont_exp> | nao <exp_nao> <cont_exp> | <val5> <operx> <val5> <cont_exp> <operx> = | <> <val5> <val_logico> | literal <cont_exp> <oper_logico> <exp_logica>| ξ <exp_nao> (<exp_logica>) | id <oper_logico> e | ou <oper_relacional> > | < | >= | <= | = | <> <val4> <val3> | literal | caracter | <val_logico> <cmd_escolha> escolha id <bloco_escolha> <bloco_escolha> inicio <cmd_casos> fim <val_logico> verdadeiro | falso <cmd_casos> caso <val4> <val_caso> : <bloco>;<casosw> | ξ <casosw> <cmd_casos> | <cmd_casocontrario> | ξ <cmd_casocontrario> casocontrario : <bloco> <val_caso> ..<val4> | , <val_caso2> | ξ <val_caso2> , <val4> <val_caso2> | ξ 27 3 PORTUGUÊS ESTRUTURADO O Portugol, português estruturado ou pseudolinguagem é uma representação didática em "português" do algoritmo. É usada em cursos de informática, de forma a facilitar o aprendizado da programação a partir da familiarização dos comandos mais usados nas linguagens de programação. 3.1 Tipos de dados Os tipos de dados são: • Inteiro: todo e qualquer informação numérica que pertence ao conjunto dos números inteiros relativos (negativo, nulo ou positivo); • Real: todo e qualquer informação numérica que pertença ao conjunto dos números reais (negativo, nulo ou positivo); • Caracter: Todo e qualquer informação composta por um conjunto de caracter alfanumérico [0..9] e/ou especiais (#, $, %..); • Lógico: Todo e qualquer informação que pode assumir duas situações (Verdadeiro ou Falso); • Matriz e Vetor: Uma matriz é uma coleção de variáveis de mesmo tipo, acessíveis com um único nome e armazenados contiguamente na memória. Os Vetores são matrizes de 1 só dimensão. 3.2 Variável Uma variável é um espaço reservado na memória do computador para armazenar um tipo de dado determinado. Variáveis devem receber nomes para poderem ser referenciadas e modificadas quando necessário. Fica estabelecido o tamanho máximo de 32 caracteres para o tamanho do maior nome válido. Valores válidos: Alpha, x, B5, nota, media, xpto Valores inválidos: 5x, a(19), C:D, nota/2 28 Exemplo de declaração de variáveis: variaveis inteiro: v1, v2, v3, s; real: n; 3.3 Atribuição A atribuição de valores é representada pelo símbolo :=. Só é permitida a atribuição de um valor por vez. Valores não aceitos: a:=b:=c:=0 Uma atribuição válida possui um identificador válido à esquerda e uma expressão do mesmo tipo do identificador a direita. Exemplos: variaveis real: nota1, nota2; nota1 := 5.5; nota2 := 9.5; 3.4 Operadores A Tabela 2 mostra os operadores aritméticos utilizados na linguagem portugol. Tabela 2 – Operadores aritméticos Operação Adição Subtração Divisão Multiplicação Divisão inteira Resto da divisão Símbolo + / * div mod A Tabela3 mostra operadores Lógicos utilizados na linguagem portugol. 29 Tabela 3 – Operadores Lógicos 3.5 Comando de Entrada e Saída Os comandos que permitem a entrada manual via teclado e exibição de informações são: • leia: Comando de entrada que permite a leitura de Variáveis de Entrada. Exemplo: leia(a); • escreva: Comando de saída que exibe uma informação na tela do monitor. Exemplo: escreva(a); 3.6 Corpo do algoritmo Exemplo da estrutura de um algoritmo escrito em pseudolinguagem. programa media; inicio variaveis <<variaveis>>; <<comandos>>; fim. 3.7 Estrutura de seleção Executa uma seqüência de comandos de acordo com o resultado de um teste. A estrutura de decisão pode ser Simples ou Composta, baseada em um resultado lógico. Seleção Simples: se <<condicao>> entao <<comando1>>; 30 Seleção Composta: se <<condicao>> entao <<comando1>> senao <<comando1>>; Composta com bloco (inicio – fim): se <<condicao>> entao inicio <<comando1>>; <<comandoN>>; fim senao inicio <<comando1>>; <<comandoN>>; fim; 3.8 Estrutura de repetição Quando houver uma seqüência de comandos que deverá ser executada repetidas vezes, tem-se uma estrutura de repetição. A estrutura de repetição, assim como a de seleção, envolve sempre a avaliação de uma condição. Existem três formas de estruturas de repetição: com teste no início (enquanto-faca), com teste no final (facaenquanto) e com variável de controle (para-ate-faca). Repetição com teste no início: enquanto (<<condicao>>) faca inicio <<comandos>> fim; Repetição com variável de controle: para <<variavel>> de <<valor1>> ate <<valor2>> passo <<valor3>> faca inicio <<comandos>> fim; Repetição com teste no final: faça inicio <<comandos>> fim enquanto (<<condicao>>); 31 3.9 Exemplos de Algoritmos Nesta sessão são apresentados nove exemplos de algoritmos escritos em pseudolinguagem. O primeiro exemplo utiliza uma estrutura de seleção composta com bloco (inicio-fim): programa media; inicio variaveis inteiro: v1, v2, v3, v4, m; escreva(“este é um algoritmo que calcula a média e verifica se o aluno foi ou não aprovado”); escreva(“coloque as 4 notas”); leia(v1, v2, v3, v4); m:= (v1 + v2 + v3 + v4)/4; se (m>6) entao inicio escreva(“aprovado”); fim senao inicio escreva(“reprovado”); fim; fim. O segundo exemplo utiliza uma estrutura de seleção simples: programa soma; inicio variaveis inteiro: v1, v2, v3, s; escreva(“este é um algoritmo que calculara a soma e verifica se é maior que 30”); escreva(“coloque 3 valores”); leia(v1, v2, v3); s:= v1 + v2 + v3; se (s>30) entao escreva(s, “é maior que 30”); fim. 32 O terceiro exemplo utiliza uma estrutura de seleção composta: programa entre20e90; inicio variaveis real: n; escreva(“digite um número”); leia(n); se (n>20 e n<90) entao escreva(“número entre 20 e 90”); senao escreva(“número não está entre 20 e 90”); fim. O quarto exemplo utiliza uma estrutura de seleção: programa escolhacaso; inicio variaveis real: preco; inteiro: codigo; escreva(“informe o preço e o código”); leia(preco); leia(codigo); escolha codigo inicio caso 1 : escreva(preco, “- sul”); caso 2 : escreva(preco, “- norte”); caso 3 : escreva(preco, “- leste”); caso 4 : escreva(preco, “- oeste”); caso 5,6 : escreva(preco, “- nordeste”); caso 7,8,9 : escreva(preco, “- sudeste”); caso 10..20 : escreva(preco, “- centro oeste”); casocontrario : escreva(preco, “- importado”); fim; fim. O quinto exemplo utiliza uma estrutura de decisão com teste no início: programa enquantofaca; inicio variaveis inteiro: n, i; escreva(“digite um número”); i := 1; enquanto (i<=n) faca inicio escreva(i); i = i + 1; fim; fim. 33 O sexto exemplo utiliza uma estrutura de repetição com variável de controle: programa tabuada; inicio variaveis inteiro: n, nn, cont; escreva(“digite o número”); leia(n); para cont de 1 ate 10 passo 1 faca inicio nn := cont * n; escreva(n, “x”, cont, “=”, nn); fim; fim. O sétimo exemplo utiliza a função pot: programa potenciacao; inicio variaveis inteiro: n, p, pp; escreva(“digite 2 números inteiros”); leia(n, p); pp := pot(n,p); escreva(pp); fim. O oitavo exemplo utiliza a declaração e manipulação de vetor: programa quantidadealunosaprovados; inicio tpv = vetor(20) de real; variaveis tpv: nota; real: soma, mediag; inteiro: cont, qtdalunos, alunosap; soma := 0; qtdalunos := 0; cont := 0; faca inicio soma = soma + nota[cont]; qtdalunos = qtdalunos + 1; cont = cont + 1; fim; enquanto (cont<=19); mediag := soma/ qtdalunos; para cont de 0 ate 19 passo 1 faca inicio se (nota[cont] > mediag) entao alunosap := alunosap + 1; fim; escreva(alunosap); fim. 34 O nono exemplo utiliza a declaração e manipulação de matriz: programa cxmatriz; inicio mat = matriz(10,3) de real; variaveis mat : n; inteiro : cont, contar real: mediaalunos, somamedia; para cont de 0 ate 9 passo 1 faca inicio para contar de 0 ate 2 passo 1 faca inicio leia(n[cont][contar]); fim; fim; para cont de 0 ate 9 passo 1 faca inicio para contar de 0 ate 2 passo 1 faca inicio mediaalunos := mediaalunos + n[cont][contar]; fim; fim; fim. 35 4 IMPLEMENTAÇÃO (CLASSES) Este Capítulo trata da descrição das classes usadas no projeto. As classes estão separadas pelos seguintes pacotes: Compilador, Compilador.Analises, Fluxograma, Desenho, Ico e Util. As classes utilizadas no projeto foram implementadas na linguagem orientada a objetos Java (pacote JDK 1.6) e modeladas em UML (Unified Modeling Language). 4.1 Modelagem das classes em UML O Pacote Compilador possui as seguintes classes e pacote: classe Executaprograma.java (que recebe por parâmetro o código já traduzido para JavaScript e cria um arquivo no disco para ser visualizado no navegador), classe Main (que chama a classe Principal para inicialização do projeto), classe Principal.java (que contém o Editor de texto) e o pacote Analises (que contém as análises do compilador). O Pacote Analises possui as seguintes classes: Lexico (que analisa a entrada de caracteres e produz uma seqüencia de símbolos chamados tokens), Token (que permite adicionar e navegar entre os símbolos usados no código), Sintatico (que recebe uma seqüência de tokens e verifica se esta seqüência pode ser gerada pela gramática da linguagem). O pacote Fluxograma possui as seguintes classes: Formas (que monta a imagem para ser usado no fluxograma), Ligacao (que monta a ligação entre um desenho e outro), Principal (que contém o ambiente para ser desenvolvido o fluxograma). O pacote desenho e Ico possuem imagens que são usadas nos botões e nos desenhos do fluxograma. O pacote Util possui as seguintes classes: Localizar (que localiza a palavra no editor de texto) e substituir (que substitui todas as palavras do editor de texto por outra). 36 4.2 Classe ExecutaPrograma A classe ExecutaPrograma (Figura 12) tem função de receber por parâmetro o código já traduzido em JavaScript e criar um arquivo com mesmo no disco chamado “RAF_programa_resutante.html”. Por medidas de segurança se houver um arquivo com o mesmo nome, será apagado e criado novamente com o novo código e finalmente ser aberto pelo navegador Mozilla Firefox. A Figura 12 apresenta a classe ExecutaPrograma. Figura 12 – Classe ExecutaPrograma. 4.3 Classe Main A Classe Main (Figura 13) é responsável pela a inicialização do sistema, ela contém o método main que chama a classe Principal (ambiente de desenvolvimento do usuário) e o método finalize() que contém o Garbage Collector (Coletor de lixo) que é chamado na finalização do sistema. A Figura 13 apresenta a classe Main. Figura 13 – Classe Main 37 4.4 Classe Principal A Classe Principal é o ambiente do usuário para edição de Texto, nela é permitido digitar, salvar, selecionar, imprimir, desfazer, refazer, recortar, colar, localizar e substituir texto. A organização e ações dos componentes são de acordo com letra inicial, sendo “m” para Menu ou itens do menu, “b” para Botões, “j” para caixa de texto e barras de ferramenta e “d” para o botão direito do mouse (menu popup). Os métodos que possui ActionPerformed e MouseClicked são chamados ao efetuar um clique em um determinado componente e os que possui KeyReleased são chamados ao soltar a tecla durante a digitação. Lista de métodos da classe Principal: • IniComponents(): método que contém toda a inicialização dos componentes para a montagem do layout do editor de texto; • InfoRodape(): método que contém o cálculo para obter a linha atual e o total de linhas digitadas pelo usuário; • BRecortarActionPerformed(...), mRecortarActionPerformed(...) e dRecortarActionPerformed(...) : métodos que permite ao usuário recortar o texto selecionado; • BCopiarActionPerformed(...), mCopiarActionPerformed(...) e dCopiarActionPerformed(...) : métodos que permitem ao usuário copiar o texto selecionado; • bColarActionPerformed, dColarActionPerformed: mColaActionPerformed e métodos que permitem ao usuário colar o texto; • mImprimirActionPerformed(...): método que permite ao usuário imprimir o texto (portugol) ; • mLocalizarActionPerformed(...): método que permite localizar um determinado texto; • mSubstituirActionPerformed(...): método que permite substituir um texto por outro; 38 • formWindowClosing(...): método que é chamado ao fechar o sistema, nele é verificado se o usuário digitou ou não algum texto, caso tenha digitado verifica se já foi salvo ou não, permitindo “Salvar como” ou salvar alterações. • MFluxoActionPerformed(...): método que chama a classe Fluxograma e obtém o código do mesmo; • JComandoKeyReleased(...) e jComandoMouseClicked(...): métodos que chamam o método infoRodape; • initComponents2: método que aumenta o tamanho da janela de acordo com a resolução do micro do usuário e associa os componentes (menus e botões) com ações de “Abrir”, “Novo”, “Salvar”, “Salvar como” e “executar”; • NovoArquivo(): método que permite ao usuário criar um novo arquivo; • SalvarComo(): método que permite ao usuário Salvar pela primeira vez o arquivo atual em um determinado local; • Salvar(): método que permite salvar as alterações do arquivo atual; • initUndoRede(): método que associa os componentes (Menus e botões) as ações desfazer e refazer; 39 Figura 14 – Classe Principal. As classes RedoAction e UndoAction são classes disponibilizadas pela Sun Microsystens que tem a função de desfazer e refazer um estado anterior que estava o texto. 4.4.1 Classe RedoAction A Classe RedoAction permite ao usuário refazer um estado anterior (desfeito) que estava o seu texto. Caso chegue ao limite de refazer a ação é desabilitada automaticamente. A Figura 15 apresenta a classe RedoAction. 40 1 public class RedoAction extends AbstractAction 2 { 3 public void actionPerformed(ActionEvent e) 4 { 5 … 6 } 7 8 protected void updateRedoState() 9 { 10 … 11 } 12 13 final Principal this$0; 14 15 public RedoAction() 16 { 17 … 18 } 19 } Figura 15 – Classe RedoAction. 4.4.2 Classe UndoAction A Classe UndoAction permite ao usuário desfazer um estado anterior que estava o seu texto. Caso chegue ao limite de desfazer a ação é desabilitada automaticamente. A Figura 16 apresenta a classe UndoAction. 1 class UndoAction extends AbstractAction 2 { 3 public void actionPerformed(ActionEvent e) 4 { 5 … 6 } 7 8 protected void updateUndoState() 9 { 10 … 11 } 12 13 final Principal this$0; 14 15 public UndoAction() 16 { 17 … 18 } 19 } Figura 16 – Classe UndoAction. 41 4.5 Classe CodigoGerado A Classe estática CodigoGerado auxilia na tradução do portugol para JavaScript com algumas funções chamadas matriz(), leia(), pot(), rad() e lógico(). Cada método retorna um trecho código traduzido com o mesmo nome, exemplo o método matriz() que retorna um trecho de código (função) em JavaScript chamada matriz(m,n). A função matriz(m,n) auxilia na tradução para o código JavaScript no tratamento de matrizes. Como a linguagem JavaScript só possui vetores, a função cria um vetor com n posições e dentro de cada elemento coloca outro vetor com n posições. A função leia() auxilia na tradução para o código JavaScript na leitura do valor da variável e identifica qual é seu tipo. Os tipos são: caracter, inteiro, real e lógico. A função pot(n,n) recebe dois valores numéricos por parâmetro e auxilia na tradução para o código JavaScript no que se refere a potenciação. A função rad(n) recebe um valor numérico por parâmetro e auxilia o código JavaScript no que se refere a raiz quadrada. A Figura 17 apresenta a classe CodigoGerado. Figura 17 – Classe CodigoGerado. A Figura 18 mostra o código-fonte dos métodos matriz(m,n), leia(), pot(m,n), rad(n), assim como as variáveis verdadeiro e falso. 42 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 function matriz(v,m){ var linha = new Array(v); for(i=0;i<m;i++){ linha[i] = new Array(m); } return linha; } function leia(){ var num = '1234567890.-'; var encontrou = false; var saida; var v = prompt('Valor',''); if (v=='verdadeiro' || v=='falso'){//tipo lógico encontrou = true; saida=v; } for(i=0;i<v.length && encontrou == false;i++){//tipo Caracter var car = v.charAt(i); if(num.indexOf(car)==-1){ encontrou = true; saida=v; } } if(encontrou==false){ saida = parseFloat(v); } return saida; } function pot(n,p){ var t; t = Math.pow(n,p); return t; } function rad(n){ var t; t = Math.sqrt(n); return t; } var verdadeiro = 'verdadeiro'; var falso = 'falso'; Figura 18 – Funções adicionais. 4.6 Classe Token A Classe Token permite que sejam adicionados elementos do código digitado pelo usuário para verificar se esta seqüência pode ser gerada pela gramática da linguagem. Métodos da Classe Token: 43 • proxValor(): método que navega para o próximo elemento já adicionado. Exemplo: após todos os token terem sido encontrados, a gramática chega ao token “programa”, e verifica se a escrita esta correta. Após a verificação, o próximo token é chamado pelo método proxValor() que faz a busca do mesmo na da lista de tokens, retornando o “Nome_programa”. Elementos Programa Chamada de proxValor Nome_programa ; método Posição 0 (Posição atua) 1 2 • ValorAnt(): método que navega para o elemento anterior; Elementos Programa Nome_programa ; Chamada de método ValorAnt Posição 0 (Posição atua) • addItem(String 1 2 valor): método que adicionar um indice): método elemento; • getItemAt(int que recebe por parâmetro um número e retorna a posição do elemento desta posição; • Posicao(): método que retorna um valor numérico da posição atual do elemento; • getItemCount(): método que retorna a quantidade de elementos adicionados; A Figura 19 mostra o diagrama da Classe Token. 44 Figura 19 – Classe Token. 4.7 Classe Lexico A Classe Lexico recebe por parâmetro o código digitado pelo usuário e separa os elementos que tem ou não importância, caso tenha, é retornado um objeto do tipo token com todos os elementos para serem usados na Classe Sintatico. Lista de Métodos da classe Lexico: • palavrareservada(String valor): recebe por parâmetro uma palavra e retorna um valor booleano dizendo se existe ou não; • setPalavra(String valor): recebe por parâmetro uma palavra para ser adicionado na lista de palavras que poderá ser usada na compilação, exemplo variáveis e números. • getToken(): retorna a seqüência de elementos que serão usados na classe Sintatico; getPosLinha(): retorna a seqüência de elementos (linha dos tokens) que serão usados na classe Sintatico; Elementos getToken getPosLinha 0 1 2 3 4 programa Nome_progra ma 1 ; inicio fim. 1 2 3 1 45 A Figura 20 mostra o diagrama da Classe Lexico. Figura 20 – Classe Lexico. 4.8 Classe Sintatico A Classe Sintatico recebe por parâmetro dois objetos do tipo Token: um que contém todos os elementos (código do usuário) e que contém as posições da linha de cada elemento. Nessa Classe é verificado se a seqüência de tokens pode ser gerada pela gramática da linguagem. Lista de Métodos da classe Sintatico: • saida(): Método que recebe por parâmetro a tradução do portugol para JavaScript durante a verificação da sintaxe; • getsaida(): Método que retorna o código completo traduzido para JavaScript; Os próximos métodos são usados para verificação do código do usuário, caso ocorra um erro, a verificação é cancelada retornando um aviso (tipo de erro) e a posição da linha, senão a compilação será com sucesso. • comeco(): método que verifica a sintaxe do inicio do programa; Código esperado Programa Nome_programa ; Código traduzido <title>Nome_programa</title> 46 • bloco_princ(): Método que verifica a sintaxe do bloco principal do programa; Código esperado Inicio fim. Código traduzido <script language=”javascript”></script> • tipo(): Método que verifica a existência da declaração de matriz ou vetor; • tipo_cons(): Método que verifica a sintaxe da matriz ou vetor; Código esperado tipoVet = vetor(n) de tipo Vetor Código traduzido tipoVet = new Array(n); Código esperado tipoMat = matriz(n,n) de tipo Matriz Código traduzido tipoVet = Matriz(n,n); • tipo_bas: Método que verifica a sintaxe dos tipos básicos (primitivos) : inteiro, caracter, real e lógico; • decl(): Método que verifica a existência da declaração de variável; Código esperado variaveis Código traduzido Var • lista_id(), varios_id() e cont_lista(): Métodos que verificam a sintaxe da declaração de variáveis; Código esperado tipo: a,b; tipo: q; Código traduzido a = ‘’; b = ‘’; q = ‘’; • tipox(): Método que verifica a sintaxe dos tipo básicos ou tipo constante: inteiro, caracter, real, lógico e os tipos de vetores ou matrizes declarados anteriormente. • varios_cmds() e cmd(): Método que verifica qual comando após a declaração de variáveis serão usados. • cmd_if(): Método que verifica o início da sintaxe do comando se; Código esperado se( Código traduzido if( 47 • exp_logica(): Método que verifica a expressão lógica; Código esperado A<>0 e (b=1) ou c>=3 Código traduzido A!=0 && (b!=1) || c>=3 • varios_if(): Método que verifica a existência do senao; Código esperado senao Código traduzido else • cmd_enquando(): Método que verifica a sintaxe do comando enquanto; Código esperado enquanto( ... )faca inicio .... fim; Código traduzido while(...){ ... } • cmd_faca(): Método que verifica a sintaxe do comando faca; Código esperado Faca Inicio .... fim enquanto(...); Código traduzido do{ ... }while(..); • exp_nao(): Método que verifica a sintaxe do comando nao (negação); Código esperado nao Código traduzido ! • cont_exp(): Método que verifica a existência dos operadores lógicos na expressão lógica; Código esperado (a>2) e (a<10) ou (a=20) Código traduzido (a>2) && (a<10) || (a=20) • val5(): Método que verifica a sintaxe do valor lógico e literal; 48 • cmd_atrib(): Método que verifica a sintaxe da atribuição de variável; Código esperado a:= Código traduzido a= • exp_arit(): Método que verifica a sintaxe da expressão aritmética Código esperado (a+2)/9 mod 8 Código traduzido (a+2)/9 % 8 • exp_arit() e y(): Métodos que verificam a sintaxe da expressão aritmética Código esperado (a+2)/9 mod 8 Código traduzido (a+2)/9 % 8 • oper(): Método que verificam a sintaxe dos operadores aritméticos; Código esperado + - / * mod div Código traduzido + - / * % parseint(n/n) • operx(): Método que verificam a sintaxe dos operadores relacionais (= e <>); Código esperado = <> Código traduzido == != • oper_logicos(): Método que verifica a sintaxe dos operadores lógicos; Código esperado e ou Código traduzido && || • oper_relacional(): Método que verifica a sintaxe dos operadores relacionais; Código esperado > => < <= <> = Código traduzido > => < <= != == • getID(): Método que verifica a sintaxe e existência das variáveis; Código esperado a[0][1] Código traduzido a[0][1] 49 • func_mat(): Método que verifica a sintaxe e existência das funções matemáticas (potência e raiz quadrada); Código esperado pot(n,n) ... rad(n); Código traduzido Math.pow(n,p); ... Math.sqrt(n); • cmd_escolha(): Método que verifica a sintaxe do comando escolha; Código esperado escolha a • bloco_escolha(): Método que verifica o bloco do comando escolha (Inicio e fim); Código esperado Inicio ... fim; • cmd_casos(): Método que a verifica sintaxe do comando caso. • val_caso() e val_caso2(): Método que verifica a sintaxe dos valores usados no comando caso. • val4(): Métodos que verifica a sintaxe dos valores lógicos (verdadeiro e falso) e literal; Código esperado caso 1,5,6: escreva(“Números”); Código traduzido if(a==1|| a== 5 ||a==6){document.write(“Números”);} else Código esperado caso 11..14: escreva(“outros Números”); Código traduzido if(a==11 || a== 12 || a==13 || a==14){ document.write(“Números”); } else • casow(): Método que verifica a existência de um próximo comando caso; • cmd_casocontrario(): Método que verifica a sintaxe do comando casocontrario; Código esperado casocontrario: escreva(“outros Números”); Código traduzido { document.write(“outros números”); } • val(): Método que verifica a sintaxe das variáveis e valores numéricos (inteiro e real), tendo possibilidade de colocar parênteses; 50 • cmd_escrever(): Método que verifica a sintaxe do comando escreva; Código esperado escreva(“Bom dia!”); Código traduzido document.write(“Bom dia”); • varios_escrever(): Método que verifica a existência da expressão aritmética no comando escreva; Código esperado escreva(“Valor é”,a+b); Código traduzido document.write(“Valor é”,a+b); • varios_escrever2(): Método que verifica a existência de um próximo elemento no comando escreva Código esperado escreva(“Valor é”,a+b); Código traduzido document.write(“Valor é ”,a+b, “Total”, c ); • cmd_ler(): Método que verifica a existência e a sintaxe do comando leia; Código esperado leia(a); Código traduzido a=leia(); • cmdler_id(): Método que verifica a existência de um próximo elemento no comando leia; Código esperado leia(a,b,c,d); Código traduzido a=leia(); b=leia(); c=leia(); • cmd_para: Método que verifica a sintaxe do comando para; Código esperado Para a de 10 ate 1 passo -1 faca Código traduzido for(a=+10;a>=+1;a=a-1) • bloco(): Método que verifica a sintaxe do bloco que contém comandos; Código esperado inicio .... fim; Código traduzido { ... } • val2(): Método que verifica a sintaxe das variáveis e valores numéricos (inteiro e real); • val3(): Método que verifica a sintaxe das variáveis e valores numéricos (inteiro); 51 • val_logico() e val_logico(String v): Método que verifica a sintaxe dos valores lógicos; • literal(): Método que verifica a sintaxe dos valores literais; • getID2(String v): Método que verifica existência de uma determinada variável; • addID(String v): Método que adiciona variáveis usadas no algoritmo; • num_it(int n): Método que valida valores numéricos (inteiro); • num_real(int n): Método que valida valores numéricos (real); • limpar_literal(String v): Método que limpa valores literal; • setErro(String v): Método que adiciona erros encontrados na análise sintática; • getErro(): Método que retorna erros encontrados na análise sintática. A Figura 21 mostra o diagrama da Classe Sintatico. 52 Figura 21 – Classe Sintatico 53 4.9 Classe Forma A Classe Forma é um componente de desenho que recebe dois parâmetro (tipo e id da forma), nela é carregado um desenho e uma tabela com propriedades de uma forma especifica. As propriedades são espécie de referência, como: • ID da forma; • Próxima forma do fluxograma; • Texto a ser exibido no seu conteúdo. Lista de métodos da classe Forma: • setText(String v): Método que permite mostrar um texto em HTML (HyperText Markup Language) no conteúdo da forma • getValue(): método que retorna o texto exibido no conteúdo da forma; • setValue(String v): Método que permite mostrar o texto no conteúdo da forma; • setID(String v): Método que permite atribuir uma identificação a forma; • getID(): Método que retorna a identificação da forma; • tipo(int cor, int tipo, int espaco): Método que configura a cor do texto a ser mostrado no conteúdo da forma (0 para preto e 1 para branco), carrega a imagem e configura o espaço entre o desenho e o texto a ser exibido; • setPropriedades(String v): Método que permite carregar a tabela com propriedades da forma especifica; • setProximo(String v): Método que permite identificar a próxima forma; • getProximo(): Método que retorna o nome da próxima forma; • getVerdadeiro(): Método que retorna o nome da próxima forma (caso seja verdadeiro). Este método somente é usado no caso de condições; • setVerdadeiro(String v): Método que é usado para identificar a próxima forma (caso seja verdadeira); 54 • getFalso(): método que retorna o nome da próxima forma (caso seja falso). Este Método somente é usado no caso de condições; • setFalso(String v): Método que é usado para identificar a próxima forma (caso seja falso); • getFimse(): método que retorna o nome da próxima forma identificando o fim da condição verdadeira; • setFimse(String v): Método que é usado para identificar a próxima forma e o fim da condição verdadeira; • getFimsenao(): método que retorna o nome da próxima forma identificando o fim da condição falsa; • setFimsenao(String v): Método que é udado para identificar a próxima forma e o fim da condição falsa; • setEnquantoInicio(String v): Método que é usado para identifica a primeira forma usada no bloco enquanto; • getEnquantoInicio(): Método que retorna o nome da primeira forma usada no bloco enquanto; • setIniciofaca(String v): Método que identifica a primeira forma usada no bloco faça; • getIniciofaca(): Método que retorna o nome da primeira forma usada no bloco faça; • getTipo(): Método que retorna o tipo da tomada de decisão (se, enquanto ou para); • tipoRepeticao(): Método que retorna o tipo de repetição (enquanto ou para); • addPropriedades(): método que permite montar os itens da tabela de propriedades; • PropriedadesBasicas(): método que permite montar as propriedades básicas da tabela básica (ID, Próximo, texto ...) • getPropriedades(): método que retorna as propriedades montada na tabela de propriedades; • PropriedadesFins(): método que permite montar a propriedade fim da tabela (fimse e fimsenao); 55 • PropriedadesCondicao(): método que permite montar as propriedades usada na tomada de decisão (verdadeiro, falso, inicioFaça ...); A Figura 22 mostra o diagrama da classe Forma. Figura 22 – Classe Forma. 4.10 Classe Ligacao A Classe Ligacao é um componente de desenho que permite ligar duas formas à tabela de propriedades. Recebe por parâmetro o tipo de ligação. A Figura 23 ilustra os tipos de ligações utilizadas no desenho de um (Fluxograma). 56 Figura 23 – Ligações usadas no desenho de um fluxograma. Lista de métodos: • getLigacao(): Método que retorna o tipo de ligação para ser desenhado no fluxograma. A Figura 23 mostra o diagrama da classe Ligacao. Figura 24 – Classe Ligacao. 57 5 CONCLUSÕES Conforme resultados obtidos com testes e pesquisas no desenvolvimento do compilador, foi possível destacar alguns aspectos importantes. No início a ferramenta teria uma versão applet para funcionar nos navegadores de internet. Mas, como a ferramenta gera um arquivo em HTML no disco local para execução do sistema (o código traduzido para linguagem JavaScript), e o applet por questão de segurança não permite este tipo de acesso, então, o sistema teve somente uma versão desktop. O compilador RAF foi concluído com os seguintes módulos: Editor de texto; Compilador para pseudolinguagem e fluxograma; Tradutor de portugol para JavaScritp; Módulo de desenho de Fluxogramas; e Tradutor de Fluxograma para pseudolinguagem. Por fim, acredita-se que a ferramenta irá facilitar a compreensão dos alunos de Superior em Tecnologia em desenvolvimento Web no aprendizado de algoritmos. 58 REFERÊNCIAS BIBLIOGRÁFICAS AHO. A. V.: SETHI, R. & ULLMAN, J. D. Compiladores: Princípios, Técnicas e Ferramentas. Guanaban Koogan 1986. Diogo Branquinho Ramos. Coral: protótipo de uma linguagem orientada a objetos usando geradores de compiladores java. Universidade do Oeste Paulista, 2005 DELAMARO, Márcio Eduardo. Como construir um compilador, utilizando ferramentas Java. Novatec. 2004. FELIPE ZSCHORNACK. Um Ambiente de execução para a Linguagem RS. Universidade Federal de Pelotas Instituto de Física d Matemática Curso de Ciência da Computação. Acesso em 2 out. de 2007. Acesso em 2 out. de 2007. Disponível em: <www.ufpel.tche.br/prg/sisbi/bibct/acervo/info/2003/ mono_felipe.pdf>. G-Portugol - A Linguagem de Programação. Acesso em 2 out. de 2007. Disponível em: <http://gpt.berlios.de/site/> LOUDEN, KENNETH. Compiladores, Princípios e Práticas. São Paulo: Pioneira Thomson Learning, 2004. Portugol IDE. Acesso em 2 <http://orion.ipt.pt/~manso/Portugol/>. out. de 2007. Disponível em: RAMOS, Diogo Branquinho. Coral: protótipo de uma linguagem orientada a objetos usando geradores de compiladores Java. 68p. Trabalho de Conclusão de Curso (Graduação em Ciência da Computação) – Universidade do Oeste Paulista – UNOESTE: Presidente Prudente - SP, 2005. SANTIAGO, Rafael de. Compilador CIFluxProg . Universidade do Vale do Itajaí, 2004. Disponível em: <www.inf.furb.br/seminco/2004/artigos/96-vf.pdf> ZSCHORNACK, Felipe. Um Ambiente de execução para a Linguagem RS. Universidade Federal de Pelotas Instituto de Física e Matemática Curso de Ciência da Computação.