RONALDO LIMA ROCHA CAMPOS GERADOR DE COMPILADORES

Documentos relacionados
RONALDO LIMA ROCHA CAMPOS INTRODUÇÃO DE AÇÕES SEMÂNTICAS NA FERRAMENTA GALS

Introdução à Programação

CP Compiladores I Prof. Msc.. Carlos de Salles

FERRAMENTA DE AUXÍLIO AO PROCESSO DE DESENVOLVIMENTO DE SOFTWARE INTEGRANDO TECNOLOGIAS OTIMIZADORAS

Compiladores I Prof. Ricardo Santos (cap 1)

Universidade de Santa Cruz do Sul UNISC Departamento de informática COMPILADORES. Introdução. Geovane Griesang

Compiladores. Motivação. Tradutores. Motivação. Tipos de Tradutores. Tipos de Tradutores

Análise Sintática I. Eduardo Ferreira dos Santos. Abril, Ciência da Computação Centro Universitário de Brasília UniCEUB 1 / 42

V.2 Especificação Sintática de Linguagens de Programação

Compiladores. Análise lexical. Plano da aula. Motivação para análise lexical. Vocabulário básico. Estrutura de um compilador

Linguagens Livres do Contexto. Adaptado de H. Brandão

DESENVOLVIMENTO DO COMPILADOR PARA A LINGUAGEM SIMPLE

INE5416 Paradigmas de Programação. Ricardo Azambuja Silveira INE CTC UFSC E Mail: URL:

Autômatos e Linguagens

Especificações Gerais do Compilador e Definição de FRANKIE

FACULDADE LEÃO SAMPAIO

Compiladores. Conceitos Básicos

Como construir um compilador utilizando ferramentas Java

Compiladores - Análise Ascendente

Compiladores - Análise Ascendente

Apresentação. !! Familiarização com os métodos de construção de compiladores de linguagens e com as técnicas de compilação mais habituais.

I LINGUAGENS E PROCESSADORES: INTRODUÇÃO 1

Universidade Federal de Goiás Bacharelado em Ciências da Computacão Compiladores

Universidade Federal de Alfenas

Interfaces de Vanguarda do Compilador

Reduce: reduz o que está imediatamente à esquerda do foco usando uma produção

Pró-Reitoria Acadêmica Diretoria Acadêmica Assessoria Pedagógica da Diretoria Acadêmica

Universidade Federal do ABC Rua Santa Adélia, Bairro Bangu - Santo André - SP - Brasil CEP Telefone/Fax:

Revisão. Fases da dacompilação

Prof. Adriano Maranhão COMPILADORES

Introdução. Compiladores Análise Semântica. Introdução. Introdução. Introdução. Introdução 11/3/2008

Linguagens Formais e Autômatos

Compiladores I Prof. Ricardo Santos (cap 3 Análise Léxica: Introdução, Revisão LFA)

Concurso Público para provimento de cargo efetivo de Docentes. Edital 20/2015 CIÊNCIA DA COMPUTAÇÃO II Campus Rio Pomba

Tokens, Padroes e Lexemas

Linguagens Regulares. Prof. Daniel Oliveira

Análise Sintática. Eduardo Ferreira dos Santos. Outubro, Ciência da Computação Centro Universitário de Brasília UniCEUB 1 / 18

Conceitos de Linguagens de Programação

LINGUAGENS FORMAIS Definições. Desenvolveram-se na História em função da necessidade dos grupos humanos que as empregavam

Compiladores. Análise Léxica

IFSC/Florianópolis - Programação Orientada a Objetos com Java - prof. Herval Daminelli

Linguagens de Programação

Análise Sintática II. Eduardo Ferreira dos Santos. Outubro, Ciência da Computação Centro Universitário de Brasília UniCEUB 1 / 34

Compiladores. Prof. Bruno Moreno Aula 8 02/05/2011

Compiladores - JACC. Fabio Mascarenhas

Universidade Estadual da Paraíba - UEPB Curso de Licenciatura em Computação

COMPILAÇÃO. Ricardo José Cabeça de Souza

Compiladores. Lex e Yacc / Flex e Bison. Ferramentas Flex/Bison

Linguagens Formais e Autômatos. Autômatos Finitos Determinísticos (AFD)

Projeto de Compiladores

Linguagens Formais. Aula 01 - Conceitos Básicos. Prof. Othon Batista Mestre em Informática

Linguagens Formais e Autômatos P. Blauth Menezes

Compiladores 04 Analise léxica Jflex. Prof José Rui

Desenvolvimento de Aplicações Desktop

Analisador Léxico parte II

Tratamento dos Erros de Sintaxe. Adriano Maranhão

Conclusões. Baseado no Capítulo 9 de Programming Language Processors in Java, de Watt & Brown

Compiladores. Introdução

Função, interação com o compilador Especificação e reconhecimento de tokens Implementação Tratamento de erros. Prof. Thiago A. S.

Compiladores Ciência e Tecnologia da Computação Engenharia Informática e de Computadores

Conceitos de Linguagens de Programação

Compiladores Analisador Sintático. Prof. Antonio Felicio Netto Ciência da Computação

COMPILADORES. Análise sintática. Prof. Geovane Griesang Universidade de Santa Cruz do Sul UNISC Departamento de informática

Python e sua sintaxe LNCC UFRJ

Estrutura geral de um compilador programa-fonte

Compiladores - Especificando Sintaxe

Linguagens de Programação Aula 3

Um Compilador Simples. Definição de uma Linguagem. Estrutura de Vanguarda. Gramática Livre de Contexto. Exemplo 1

Máquina de Turing Linguagens Sensíveis ao Contexto e Enumeráveis Recursivamente

Compiladores Aula 3. Celso Olivete Júnior.

Conceitos de Linguagens de Programação

Compiladores - Autômatos

Questões de Paradigmas de Programação Matéria: Prova 1 4ª EDIÇÃO

Noções de compilação

Compilação: Erros. Detecção de Erros: * Analisadores Top-Down - Preditivo Tabular (LL) - Feito a mão. * Analisadores Botton-Up: - Shift-Reduce (SLR)

Analisadores Sintáticos LR

Paradigmas de Linguagens de Programação. Descrevendo a Sintaxe e a Semântica

Compiladores Aula 1. Celso Olivete Júnior.

Linguagens Livres de Contexto

Sintaxe e Semântica. George Darmiton da Cunha Cavalcanti.

CAP. VI ANÁLISE SEMÂNTICA

IV.2 Aspectos Léxicos Convencionais

Introdução à Programação Aula 03. Prof. Max Santana Rolemberg Farias Colegiado de Engenharia de Computação

Noções de compilação

Pró-Reitoria Acadêmica Diretoria Acadêmica Assessoria Pedagógica da Diretoria Acadêmica

Compiladores. Introdução à Compiladores

Construção de Compiladores

Compiladores. Eduardo Ferreira dos Santos. Fevereiro, Ciência da Computação Centro Universitário de Brasília UniCEUB 1 / 38

Construção de Compiladores Aula 16 - Análise Sintática

Máquinas Universais. Máquina de Turing. Celso Olivete Júnior.

UNIVERSIDADE FEDERAL RURAL DO SEMI-ÁRIDO CURSO: CIÊNCIA DA COMPUTAÇÃO. Prof.ª Danielle Casillo

Compiladores 02 Analise léxica

PLANO DE APRENDIZAGEM

Linguagens Formais e Autômatos 02/2016. LFA Aula 01 24/10/2016. Celso Olivete Júnior.

LINGUAGEM LIVRE DE CONTEXTO GRAMÁTICA LIVRE DE CONTEXTO

Introdução parte II. Compiladores. Mariella Berger

Gramáticas Livres de Contexto Parte 1

Gramáticas Livres de Contexto

COMPILADORES PROGRAMA E BIBLIOGRAFIA

Transcrição:

RONALDO LIMA ROCHA CAMPOS GERADOR DE COMPILADORES Florianópolis junho de 2009

SUMÁRIO 1 INTRODUÇÃO................................................... p. 3 1.1 MOTIVAÇÃO............................................................ p. 3 1.2 OBJETIVOS GERAIS.................................................... p. 4 1.3 OBJETIVOS ESPECÍFICOS.............................................. p. 4 2 REVISÃO BIBLIOGRÁFICA..................................... p. 6 2.1 TEORIA DAS LINGUAGENS FORMAIS................................. p. 6 2.1.1 LINGUAGENS REGULARES.......................................... p. 6 2.1.2 AUTÔMATOS FINITOS................................................ p. 6 2.1.3 LINGUAGENS LIVRES DO CONTEXTO.............................. p. 8 2.2 COMPILADORES........................................................ p. 9 2.2.1 ANÁLISE LÉXICA..................................................... p. 10 2.2.2 ANÁLISE SINTÁTICA................................................. p. 12 2.2.3 ANÁLISE SEMÂNTICA................................................ p. 12 2.2.4 GERAÇÃO DE CÓDIGO INTERMEDIÁRIO........................... p. 13 2.3 GERADORES DE COMPILADORES..................................... p. 13 2.3.1 GALS.................................................................. p. 13 2.3.2 LEX/YACC............................................................ p. 14 2.3.3 ANTLR................................................................ p. 16 3 PLANO DE TRABALHO......................................... p. 17 3.1 GERADOR DE COMPILADORES....................................... p. 17 3.1.1 GERADOR DE ANALISADOR LÉXICO............................... p. 17

3.1.2 GERADOR DE ANALISADOR SINTÁTICO............................ p. 18 3.1.3 SUPORTE A ANÁLISE SEMÂNTICA.................................. p. 18 3.2 GALS.................................................................... p. 19 3.2.1 LINGUAGEM DE DESCRIÇÃO DA FERRAMENTA................... p. 19 3.3 IMPLEMENTAÇÃO...................................................... p. 21 3.3.1 GERADOR DE ANALISADOR LÉXICO............................... p. 21 3.3.2 GERADOR DE ANALISADOR SINTÁTICO............................ p. 21 3.3.3 PROPOSTA AO SUPORTE DE AÇÕES SEMÂNTICAS................ p. 22 3.4 DOCUMENTAÇÃO...................................................... p. 22 3.5 VALIDAÇÃO E TESTES................................................. p. 22 REFERÊNCIAS...................................................... p. 23

3 1 INTRODUÇÃO Reconhecidamente, compiladores são de extrema importância para ciências da computação e a toda informática. O mundo como conhecemos, depende das linguagens de programação, pois todo software existente foi escrito em alguma linguagem de programação. Também todo o hardware que executa algum software, é regido por um tipo de linguagem (linguagem objeto). Porém, antes que um programa (software) possa ser executado, ele primeiro precisa ser traduzido de alguma linguagem de programação, para a linguagem objeto. Esta tarefa é realizada por um compilador. O presente trabalho de conclusão de curso busca desenvolver uma ferramenta que auxilie a criação de compiladores para linguagens de programação. Sendo sua estrutura divida em partes: gerador de analisadores léxicos, gerador de analisadores sintéticos, e que também seja integrado um suporte à implementação da analise semântica. Inicialmente esse projeto propunha-se a dar continuidade a ferramenta GALS. O GALS foi desenvolvido sobre objetivos semelhantes. Também foi um projeto de conclusão de curso, orientado pelo Prof. Dr. Olinto Furtado. Seus resultados atendem bem suas espectativas, e a ferramente é muito utilizada. Porém, conforme foi sendo realizado o estudo de sua implementação, constatou-se uma necessidade de desacoplamento da interface gráfica, de mudanças na linguagem de descrição, e de um melhor suporte a implementação da análise semântica. 1.1 MOTIVAÇÃO O projeto de um compilador é o casamento entre teoria e pratica. É uma área com imensa fundamentação teórica, que de fato é usado em pratica em seus complexos algoritmos. Até alguns anos atrás, haviam poucas linguagens de programação, e poucas arquiteturas de hardware. Hoje, há centenas de linguagens, e diversas arquiteturas de hardware. Os compiladores tiveram também que acompanhar essa evolução.

4 Ao se comparar compiladores antigos com modernos, vemos grandes mudanças. Compiladores antigos, eram geralmente implementados por uma única pessoa, ou um pequeno grupo, possuíam algumas centenas de linhas de código, e eram feitos em linguagens de baixo nível. Hoje, um projeto de compilador envolve equipes inteiras, recursos avançados de engenharia de software, fazendo uso de componentes já existentes e frameworks de auxilio a construção de compiladores. Possuindo, assim, milhares de linhas código. A construção de um compilador completo é muito complexa, demanda tempo, recursos e conhecimento, sendo um trabalho oneroso e desnecessário, pois, etapas como análise léxica e sintática podem ser automatizadas por geradores de compiladores. Dessa forma o desenvolvimento de um compilador pode ser melhor focado nas etapas de descrição da linguagem, pois, desta forma, o projetista não precisa tomar conhecimento dos diversos algoritmos envolvidos nessas etapas, análise léxica e sintática. Hoje exitem diversas ferramentas que propõem o mesmo objetivo, tais como, as consagradas Lex e Yacc, Antlr, dentre outras. Porém, elas apresentam alguns problemas: algumas fazem somente a análise léxico-sintática ou geração de código, o que obriga ao uso uso de mais de uma ferramenta, levando, muitas vezes, à incompatibilidade no uso dos analisadores gerados. Ou usam apenas uma das diversas técnicas de análise para a construção do parser. Outro fator importante é a falta de documentação das soluções existentes que, em muitos casos são restritas somente ao código fonte, e a alguns exemplos. Uma ferramenta simples, porém que alie tecnologias e facilidades existentes, faz-se necessário. 1.2 OBJETIVOS GERAIS O presente trabalho tem como objetivo desenvolver uma ferramente que auxilie o desenvolvimento de compiladores e interpretadores de linguagens de programação, fazendo uso de uma linguagem de descrição da gramática e suas regras. A ferramenta deve, de forma automática, gerar as principais partes de um compilador: o analisador léxico e o sintático, bem como prover técnicas de suporte a implementação de ações semânticas. 1.3 OBJETIVOS ESPECÍFICOS 1. Compreender como funciona e como é implementado um compilador; 2. Analisar as diferentes técnicas e algoritmos de parser;

5 3. Estudar a ferramenta GALS, que propõe os mesmos objetivos; 4. Analisar a ferramenta GALS, para identificar novos objetivos, propor mudanças e aproveitar suas melhores idéias para a etapa de implementação; 5. Utilizar algoritmos e partes de software já desenvolvidas no GALS, para modelar um sistema gerador de compiladores; 6. Desenvolver, na ferramente, um sistema de apoio a fase de analise semântica para que este atenda seus objetivos.

6 2 REVISÃO BIBLIOGRÁFICA 2.1 TEORIA DAS LINGUAGENS FORMAIS Teoria das linguagens foi desenvolvida na década de 1950, com o objetivo inicial de desenvolver teorias relacionadas com as linguagens naturais. No entanto, sua importância foi verificada em diversas outras áreas, em especial no estudo de linguagens artificiais e linguagens originárias da Computação e Informática (MENEZES., 2000). Em computação uma linguagem formal pode ser formalmente definida como sendo um conjunto finito de símbolos sobre um alfabeto, ou seja, se L é uma linguagem e V um alfabeto, então: L V ɛ, onde V ɛ é V {ɛ}. 2.1.1 LINGUAGENS REGULARES Linguagens Regulares são linguagens formais, empregadas na construção de reconhecedores de linguagens. Apesar de fazerem parte de um universo restrito de linguagens, são empregados na construção, de forma geral, de linguagens de computação. Formalmente, se L é uma linguagem regular, então ela pode ser reconhecida, ou descrita, através de autômatos finitos, expressões regulares ou gramáticas regulares. 2.1.2 AUTÔMATOS FINITOS Em teoria da computação, autômatos finitos são modelos computacionais, simples o bastante para se construir uma teoria matemática manejável sobre eles. Autômatos são uma especie de computador, fundados em dois princípios simples, porém muito poderosos, memória (estados) e transições. Dassa forma consegue-se representar uma série de problemas complexos.

7 DFA Autômatos finitos determinísticos (do inglês deterministic finite automata, DFA) são o tipo mais simples de autômatos. O funcionamento de um DFA assemelha-se ao de uma máquina, composta por três partes, um registro, ou fita, uma unidade de controle, e um programa de transição. A partir da fita de entrada, a unidade de controle a lê, um simbolo por vez. E conforme o simbolo lido, o programa de transição determina um novo estado à máquina. Ao final da leitura da fita, se a máquina estiver em um estado especial, denominado aceitação, a fita é reconhecida, caso contrário, rejeitada (SIPSER., 2007). Um automato finito é formalmente definido como sendo uma 5-upla (Q, Σ, δ, q 0, F), onde 1. Q é um conjunto finito de estados; 2. Σ é um conjunto de símbolos, chamados alfabeto; 3. δ é uma função de transição do tipo: Q Σ Q; 4. q 0 é o estado inicial do automato, onde q 0 Q; 5. F Q é o conjunto de aceitação. NFA Autômatos finitos não-determinísticos (do inglês nondeterministic finite automato, NFA) diferentemente de máquinas determinísticas, as quais conhecendo seu estado e o próximo símbolo de entrada, saberemos qual será seu próximo estado, para o nãodeterminismo, um conjunto de estados é esperado, ou seja, há vários caminhos que podem ser seguidos de forma paralela. Um automato finito não-determinísticos é formalmente definido como sendo uma 5- upla (Q, Σ, δ, q 0, F), onde 1. Q é um conjunto finito de estados; 2. Σ é um conjunto de símbolos, chamados alfabeto; 3. δ é uma função de transição do tipo: Q Σ ɛ P(Q); 4. q 0 é o estado inicial do automato, onde q 0 Q;

8 5. F Q é o conjunto de aceitação; 6. ɛ é a palavra vazia. Sendo, Σ ɛ definido como: Σ {ɛ}, e P(Q) é o conjunto das partes de Q, ou seja, para qualquer conjunto Q temos P(Q) como sendo a coleção de todos os subconjuntos de Q. EXPRESSÕES REGULARES Trata-se de um gerador, pois toda linguagem regular pode ser descrita por uma expressão simples, pode-se então, inferir como construir tal linguagem. Uma expressão regular é definida a partir de conjuntos básicos de símbolos e operações sobre eles, união e concatenação. São adequadas para descrever linguagens, pois dada sua forma, expressão matemática, é bem pertinente a comunicação. Segue sua definição formal, R é uma expressão formal se: 1. Qualquer símbolo α pertencente a algum alfabeto Σ; 2. ɛ, que representa a linguagem contendo exclusivamente a palavra vazia; 3. (R 1 R 2 ) onde R 1 e R 2 são expressões regulares; 4. (R 1 R 2 ) onde R 1 e R 2 são expressões regulares; 5. (R1), onde R 1 é uma expressão regular. 2.1.3 LINGUAGENS LIVRES DO CONTEXTO A classe de linguagens livre do contexto compreende um universo mais amplo, possuem como um de seus subconjuntos as linguagens regulares. Seu estudo é de extrema importância para a informática, pois por serem mais representativas, lidam com uma classe de problemas comum às linguagens de programação, como por exemplo, balanceamento de expressões, construções de blocos, entre outras (SIPSER., 2007). Seu estudo é desenvolvido através de um formalismo axiomático, ou seja, uma gramática, e um reconhecedor (linguagem regular).

9 GRAMÁTICAS LIVRE DE CONTEXTO Foram usadas primeiramente no estudo de linguagens humanas. Constituem-se em um método mais poderoso de descrever linguagens, descrevem características que possuem estrutura recursiva (MENEZES., 2000). Sua principal aplicação ocorre na compilação de linguagens de programação. Em sua maioria, as linguagens de programação são criadas a partir de uma gramática estendida, denominada EBNF. Sua definição formal é dada como, G = (V n, V t, P, S), onde: 1. V n é um conjunto de símbolos, denominados não-terminais, são utilizados na descrição da linguagem; 2. V t é um conjunto de símbolos, denominados terminais, são os símbolos de fato utilizados na formação das sentenças da linguagem; 3. P é um conjunto finito de produções, na forma P(α, β) onde α V n, deriva β (V n V t ). 4. S é o símbolo inicial da gramática. Sendo que P é da forma α β, onde α é uma variável de V n e β é uma palavra de (V n V t ). 2.2 COMPILADORES Em tese compiladores são programas que tratam de linguagens. Basicamente transformam uma linguagem fonte em uma linguagem alvo, ou seja, a partir de uma linguagem de entrada, é gerada uma linguagem de saída, e ambas possuem o mesmo significado e comportamento. Esse processo é denominado de tradução. De forma bem simples um compilador pode ser definido como um programa que, como entrada recebe um programa fonte (em linguagem de programação), e o traduz para um programa equivalente em outra linguagem, em sua maioria linguagem objeto, capaz de ser executado por um computador alvo (LOUDEN, 2004). Porém dependendo de sua saída, o compilador pode ainda ser classificado como (AHO et al., 2008, p. 1-2):

10 Pré-processador, se a linguagem de entrada for a mesma de saída; Interpretador, se a partir de uma linguagem de entrada, forem geradas diretamente ações em um computador. Na maioria dos casos o processo completo de compilação pode ser dividido em duas partes, análise e síntese. Na análise, o compilador subdivide o programa fonte em partes e é gerado uma estrutura gramatical sobre elas. A partir dessa estrutura é criado uma representação intermediária do programa fonte. Nessa parte a análise verifica o programa sintática e semanticamente, e coleta informações sobre dados e comportamento do programa para passar à próxima fase. Caso o programa fonte esteja incorreto, é desejável que o compilador gere uma análise dos erros (AHO et al., 2008). A parte de síntese irá então construir o programa objeto, usando essa representação intermediária e as informações retiradas, que muitas vezes recebe o nome de tabela de símbolos. Por sua vez o processo de análise pode ser subdividido em outras partes, comumente em análise léxica, análise sintática e análise semântica. A figura a seguir exemplifica o processo de análise. Figura 1: Modelo fase de análise de um compilador 2.2.1 ANÁLISE LÉXICA É a primeira fase do processo de compilação. Sua principal tarefa é ler os caracteres da entrada do programa, agrupá-los em lexemas e produzir como saída uma seqüência de tokens. Esse fluxo de tokens é então enviado ao analisador sintático. Além disso o analisador léxico pode também interagir com a tabela de símbolos.

11 Normalmente a etapa de analise léxica está integrada a sintática. O analisador sintático faz chamadas ao léxico sempre que precisar reconhecer um novo token. Embora fosse possível deixar ao analisador sintático a tarefa de reconhecer os tokens, essa etapa é separada, pois: Dessa forma o projeto é simplificado, pode-se tratar com facilidade detalhes da especificação da linguagem e formatação dos caracteres, como quebras de linha, e outras. Sua eficiência é melhorada, uma vez que podemos aplicar técnicas especializadas relacionadas apenas à tarefa léxica. A portabilidade é melhorada, mudanças nos dispositivos de entrada do compilador podem ser mais facilmente tratados. Para a discussão sobre análise léxica, precisamos definir alguns termos importantes: Token é um símbolo abstrato, que representa uma unidade léxica, cada token representa um padrão de caracteres ou identificadores. Os tokens são os símbolos de entrada que o analisador sintático processa. Lexema são seqüências de caracteres que casam com o padrão de um token, e é identificado como uma instância de um. Para transformar o fluxo de caracteres de entrada em uma seqüência de tokens, reconhecendo dessa forma, palavras, identificadores e constantes, o analisador léxico é implementado como um reconhecedor de linguagens regulares. Dessa forma sua implementação faz uso, a partir de uma especificação, de autômatos finitos. Essa forma garante eficiência e simplicidade, porém é comum precisarmos ler alguns caracteres a diante, afim de descobrir sobre qual token casamos nosso padrão. Podemos resolver esse problema com uma técnica bem simples, chamada lookahead, basta, ao se verificar que um lexema pode ser aplicado a mais do um token, que se mantenha o estado atual da analise, e se caminhe para o próximo símbolo, afim de se obter informações suficientes para casar apenas um padrão. A tarefa de construir um analisador léxico pode ser inteiramente automatizada, bastando apenas uma especificação formal da linguem a ser reconhecida e um software gerador de analisadores léxicos.

12 2.2.2 ANÁLISE SINTÁTICA A análise sintática é o processo pelo qual determina-se a sintaxe, ou estrutura, de um programa. Ou seja, determina como uma cadeia de símbolos terminais pode ser gerada por uma gramática. A análise sintática está associada às gramáticas livre do contexto, dessa forma é conveniente expressar suas derivações através de uma árvore, que chamamos de árvore sintática. A maioria dos algoritmos e métodos de análise sintática estão divididos em duas classes, descendentes e ascendentes. Essas classes referem à ordem com que os nós das árvores são construídos. ANÁLISE ASCENDENTE A analise ascendente corresponde à construção da árvore de derivação, para sua cadeia de entrada, de baixo para cima, ou seja a partir de suas folhas, rumo ao topo da árvore. Essa classe de analisadores pode tratar de um maior número de gramáticas e esquemas de tradução. Porém na prática, e em seus algoritmos, tal árvore dificilmente é construída. Por tais fatores são utilizados em geradores de analisadores sintáticos. ANÁLISE DESCENDENTE O método de análise descendente consiste em construir a árvore de derivação, para a cadeia de entrada, de cima para baixo, ou seja, dos nós das árvores em direção às folhas. Sua popularidade dá-se ao fato de que analisadores podem ser construídos de forma mais fácil dessa forma. 2.2.3 ANÁLISE SEMÂNTICA Como estrutura à análise semântica temos algumas técnicas bem formuladas, como a geração de tabelas de símbolos, grafos de dependência e árvores de derivação. Como suporte, uma técnica muito utilizada e de simples implementação é a tradução dirigida por sintaxe.

13 TABELA DE SÍMBOLOS São estruturas de dados que contem informações sobre as construções do programa fonte. Essas informações são obtidas pelas fase de análise, e usadas na análise semântica e na fase de síntese. Tal tabela contém informações sobre os identificadores como, seu nome, lexema, seu tipo, endereço em memória, qualquer informação que julgue-se importante. GRAFO DE DEPENDÊNCIAS Os grafos de dependências representam o fluxo de informações entre instâncias dos atributos em uma árvore de derivação. Seus nós são os atributos de uma produção específica e suas arestas expressão as regras semânticas entre os nós. TRADUÇÃO DIRIGIDA POR SINTAXE Esse tipo de técnica associa informações, atributos, aos símbolos da gramática, de forma a, durante a análise das produções e derivações, aplicar as regras semânticas ao programa fonte. 2.2.4 GERAÇÃO DE CÓDIGO INTERMEDIÁRIO.................................................................................... 2.3 GERADORES DE COMPILADORES Como suporte ao desenvolvimento de um compilador, algumas ferramentas foram desenvolvidas. Elas vão desde um simples gerador de reconhecedores de linguagens regulares, até complexas ferramentas de geração e otimização de código objeto. Algumas das mais importantes serão tratadas a seguir. 2.3.1 GALS O GALS é uma ferramente de geração de analisadores léxicos e sintáticos. Seu uso é feito através de sua interface gráfica, onde são definidos os tokens e produções da gramática. Ele permite uma série de opções, como gerar somente o analisador léxico, ou somente o sintático (GESSER, 2003).

14 Como parser, também permite a escolha de seu algoritmo, podendo ser: Descendentes: Descendente Recursivo LL. Ascendente: SRL LALR LR. Como, além de auxiliar na construção de compiladores, o GALS se propõe a ser uma ferramenta com auxilio didático, ele oferece uma série de opções para esse fim, como a visualização das tabelas de analise léxica e sintática, e conjuntos frist follow. Tem sido muito utilizado nas cadeiras de construção de compiladores. Pois, possui também um simulador, permitindo testar o funcionamento da gramática. FUNCIONAMENTO Seu funcionamento é semelhante as demais ferramentas. Uma linguagem de descrição é feita, e a partir dela é possível gerar os analisadores léxico e sintático, em uma das suas opções, já citadas. O GALS fornece também um simulador léxico, e um sintático. 2.3.2 LEX/YACC O Lex foi projetado para o processamento léxico de caracteres. Ele aceita uma especificação que reconheça um conjunto de caracteres, ou seja, uma linguagem regular. E produz um programa em linguagem de propósito geral, nesse caso, em linguagem C, capaz de reconhecer tal especificação (SCHMIDT; LESK, ). O funcionamento do Lex dá-se da seguinte forma, como entrada, o compilador Lex recebe uma especificação da gramática a ser reconhecida, e produz, como saída, um programa em linguagem C, que reconhece tal gramática. Como exemplo a figura a seguir (SCHMIDT; LESK, ):

15 +-------+ Source -> Lex -> yylex +-------+ Input -> +-------+ yylex -> Output +-------+ Normalmente, por apenas reconhecer linguagens regulares, o Lex é usado para gerar um programa, que é utilizado como rotina em um analisador sintático (AHO et al., 2008, p. 90). Sendo muito utilizado em conjunto com o Yacc. Por sua vez o Yacc, diferente do Lex, foi criado com o propósito de reconhecer linguagens recursivamente enumeráveis. Reconhecendo desta forma as linguagens de programação (JOHNSON, ). O Yacc, tem como função gerar um analisador sintático, para uma especificação de linguagem. Essa especificação traz a estrutura de reconhecimento da linguagem de entrada, juntamente com a função, em código para a linguagem C, para ser processado quando uma estrutura é reconhecida. Seu funcionamento, semelhante ao Lex, ocorre da seguinte forma, o compilador recebe a especificação da linguagem, e produz, em linguagem C, um parser para ela. Porém, esse parser, como entrada, espera receber já um fluxo de tokens, e não um conjunto de caracteres. Por tal fato, o Yacc precisa trabalhar em conjunto com um analisador léxico. Dessa forma o Yacc é normalmente associado ao uso do Lex. Sua estrutura de funcionamento segue como a figura: lexical grammar rules rules v v +---------+ +---------+ Lex Yacc +---------+ +---------+ v v

16 +---------+ +---------+ Input -> yylex -> yyparse -> Parsed input +---------+ +---------+ 2.3.3 ANTLR Atualmente, o Antlr é uma das ferramentas mais utilizadas para o reconhecimento de linguagens. Seu projeto inicial tem mais de vinte anos. E em sua ultima versão, foi completamente reescrito (PARR, ). De forma diferente de outras ferramentas, o Antlr é uma ferramenta completa. Através de uma linguagem de especificação, ele é capaz de gerar um reconhecedor de linguagens, que integra as fazes de analise léxica e sintática. A ferramenta faz uso de algoritmos que geram um parser LL. Tal escolha deve-se ao fato que parser LL são mais rápidos e requerem menos memória, e, principalmente, são mais fáceis de implementar mecanismos de detecção e recuperação de erros. Apesar de ser implementado em linguagem de programação Java, o Antlr pode gerar reconhecedores em diversas linguagens, como C++, Phyton, Java e C#. Como suporte a análise semântica, faz-se uso do mesmo dispositivo utilizado no Yacc. Ações são descritas juntamente com as produções, e são executadas ao processa-las. Alternativamente, porém, o Antlr oferece um recurso de geração de uma árvore sintática abstrata, onde, dessa forma, as ações semânticas podem ser feitas ao se processar os nós da árvore (PARR., 2007).

17 3 PLANO DE TRABALHO 3.1 GERADOR DE COMPILADORES Ao desenvolver um gerador de compiladores, precisamos definir alguns pontos de fundamental importância. Em principio, iremos adotar uma arquitetura de software em pipeline, ou seja, em várias fazes, dessa forma o projeto é simplificado. O gerador irá conter as seguintes estruturas, Gerador de Analisador Léxico; Gerador de Analisador Sintático; Suporte a Análise Semântica. Para isso uma espécie de meta-linguagem será criada, para descrever o comportamento léxico e sintático. Essa linguagem assemelha-se às descrições EBNF, e a partir dela será gerado, de forma automática, os analisadores léxico e sintático, podendo ser definido seus tipos de algoritmos. Ao submeter tal linguagem descritiva ao gerador de compiladores, o mesmo irá produzir, caso a gramática esteja livre de erros, uma série de arquivos em código fonte Java, onde estarão descritos, os analisadores léxico e sintático e as definições semânticas, conforme definidas na linguagem. 3.1.1 GERADOR DE ANALISADOR LÉXICO A partir da meta-linguagem de entrada, o gerador léxico irá gerar um analisador léxico, que essencialmente é um autômato que reconhece o padrão especificado. E que seguirá corretamente as especificações citadas na linguagem.

18 3.1.2 GERADOR DE ANALISADOR SINTÁTICO O gerador sintático irá reconhecer, a partir da linguagem de entrada, as regras de produções dessa gramática. Determinadas essas regras, irá gerar um analisador sintático. Nesse ponto o gerador irá identificar e resolver possíveis conflitos, quanto às derivações. Ao final desta fase teremos um analisador sintático que reconhece a gramática descrita pela meta-linguagem. 3.1.3 SUPORTE A ANÁLISE SEMÂNTICA A análise semântica é a fase de um compilador que computa as informações adicionais para a compilação, quando a estrutura sintática já é conhecida (??, p. 259). De forma simples, a tradução dirigida por sintaxe propõe a analise semântica em conjunto com o reconhecimento das produções da linguagem. Essa técnica, juntamente com o uso de estruturas de dados específicas, tabelas de símbolos e grafos de dependências, vem por realizar, de forma eficiente, a fase de análise semântica. TABELA DE SÍMBOLOS São implementadas como tabelas Hash. Geralmente separadas por produções, e encadeadas na forma de árvores. Contém informações sobre as construções do programa fonte, especificamente as que estão sob análise do parser, ao processar uma produção. Tais informações são definidas pelo construtor da gramática, conforme forem suas necessidades. Como exemplo, valores como tipo e endereçamento, nomes e lexemas, são informações que, geralmente, são armazenadas em uma tabela de símbolos. GRAFO DE DEPENDÊNCIAS Grafos de dependências são comumente utilizados para o processamento da gramática de tipos. Tal gramática é responsável pela checagem de tipos em linguagens de programação. Os grafos de dependências são construídos, sobre as árvores de derivação de produções. Mas diferente destas, eles relacionam os atributos dos nós da árvore, através de uma função de validação (AHO et al., 2008, p. 198).

19 3.2 GALS Inicialmente esse projeto pretendia simplesmente dar continuidade a ferramenta GALS, desenvolvida como projeto de conclusão de curso, estendendo suas funções (GESSER, 2003). No entanto, após o estudo da ferramente e de seu código fonte, foram constatadas que algumas mudanças seriam necessárias, tais como, um desacoplamento da interface gráfica, que, embora útil em parte do trabalho, dificulta a utilização da ferramenta em scripts, ou em futuras modificações. Ao se desenvolver uma ferramente em modo texto, simplifica-se, de inicio, seu projeto, e a torna mais simples de utilizar e manter. E, se algumas técnicas bem consagradas forem utilizadas, uma interface gráfica poderá ser criada de forma a complementar o projeto. 3.2.1 LINGUAGEM DE DESCRIÇÃO DA FERRAMENTA Uma segunda mudança trata a linguagem de descrição da gramática, usada como entrada pela ferramenta. Atualmente ela assemelha-se a descrição BNF, em que as produções são descritas em duas partes, um símbolo não-terminal a esquerda, seguido por um delimitador, e uma produção ou uma expressão regular a direita. Segue um exemplo da sintaxe utilizada no GALS. <ATRIB> ::= ID "=" <EXPR> ; As produções iniciam-se entre <, >, são seguidas por um delimitador ::=. Logo após iniciam-se suas respectivas derivações, separadas por e ao final há um símbolo de fim de produção, no caso ;. Segue outro exemplo mais elaborado (GESSER, 2003), <EXPR> ::= <EXPR> <OP> <EXPR> "(" <EXPR> ")" ID NUM ; Já a definição de tokens é feita de através de expressões regulares, de forma quase semelhante, um exemplo ilustra melhor esse caso,

20 ID : {LETRA} ( {LETRA} {DIGITO} )* Essa linguagem é de fácil compreensão, e de simples implementação. Porém visualmente, a medida que a descrição da gramatica aumenta, a definição fica muito carregada, dificultando sua percepção. Neste trabalho é proposto, uma limpeza na sintaxe da linguagem, bem como a definição de tokens e produções no mesmo arquivo. Que se justifica com a melhor clareza na definição da gramática. Dessa forma, a definição da linguagem fica semelhante tanto a descrição dos tokens, como a das produções. Essa descrição passa a ser feitas em duas partes. De um lado, a esquerda, um símbolo não terminal, e a direita, separado por um delimitador, a produção, ou definição do token, se for o caso. Como exemplo: //definição de token NUM : [0-9]+ ; //definição de produções expr : expr ( + - ) expr ( expr ) NUM ; Quanto a inserção de ações semânticas, o GALS, em sua linguagem, permite que o usuário do sistema insira um caractere especial (#), seguido de um número, do lado direito das produções. Dessa forma, durante a análise, ao encontrar tal símbolo, o analisador semântico é chamado e a ação, elaborada pelo usuário, é executada. Nessa proposta, as ações semânticas passam a ser inseridas diretamente na descrição da linguagem, sempre ao lado direito, e entre os símbolos, {, }. Tais ações são em linguagem de programação, e são inseridas direto no código do analisador gerado, especificamente no momento após em que é processado a produção anterior a que foi inserida a ação.

21 3.3 IMPLEMENTAÇÃO Por simplicidade, a interface da ferramenta será desenvolvida somente em modo texto. Seu funcionamento ocorre da seguinte maneira, na chamada ao software, é passado como parâmetro, um arquivo, contendo a descrição da linguagem, e uma lista de parâmetros, os quais definem sua configuração. Apesar de simples esse modelo é muito poderoso, pois permite a inclusão da ferramenta em outros softwares. Dessa forma, uma interface gráfica pode ser criada e integrada facilmente a ferramenta, de forma simples e eficiente. Definida a arquitetura do software, a parte mais importante é justamente o desenvolvimento do núcleo da aplicação. Esse assemelha-se a um compilador, ele deve analisar a linguagem de entrada. E, a partir desta, deve produzir um conjunto de códigos fonte, que represente as fases de um compilador para a tal linguagem. O núcleo da aplicação contém, praticamente, três módulos. Um primeiro módulo irá ler a gramática e realizar as configurações iniciais. Logo após, um segundo módulo irá gerar as partes básicas do compilador. O terceiro irá gerar os analisadores léxicos e sintáticos. 3.3.1 GERADOR DE ANALISADOR LÉXICO Após a linguagem descritiva ser compilada, deverá ser criado um analisador léxico para a gramática descrita. Essa função cabe ao módulo gerador de analisador léxico. Tal módulo irá gerar um analisador léxico, construído com base em um modelo de autômatos finitos determinísticos, cujo comportamento principal é ler os caracteres da entrada, de forma incremental, e agrupa-los em tokens. Tornando-o ideal para o uso em analisadores sintáticos. 3.3.2 GERADOR DE ANALISADOR SINTÁTICO Como passo subseqüente a geração do analisador léxico, é criado o analisador sintático. Tal analisador irá conter um conjunto de funções básica, para sua utilização. E como o GALS, o analisador implementa, não apenas um mas um conjunto de algoritmos de parser. Diferentes descrições de linguagens requerem diferentes tipos de parsers, por exemplo, linguagens recursivas a esquerda podem ser reconhecidas por um parser LALR, o mesmo já não acontece com o parser LL. Porém, o parser LL é mais rápido, menor e consegue

22 tratar melhor a recuperação de erros. Uma discussão mais elaborada pode ser encontrada em (LL(1)..., ). Por tais motivos, aproveitamos do GALS a implementação dos parsers LL e LR. Também, implementando outros algoritmos. 3.3.3 PROPOSTA AO SUPORTE DE AÇÕES SEMÂNTICAS Como suporte a análise semântica, é proposto algumas estruturas que visam facilitar essa etapa. Como forma mais simples de dar peso semântico a linguagem, será possível fazer, a tradução dirigida por sintaxe, ou seja, a medida em que as reduções forem sendo realizadas, ações descritas na especificação da linguagem serão executadas. Essas ações são descritas, já em linguagem de programação, direto na especificação da linguagem. Alternativamente, será possível criar um grafo de dependências para cada redução, contendo, os tokens e seus atributos. Dessa forma a definição semântica poderá ser feita sobre esse grafo. Dessa forma ao processar uma regra, produção, o analisador sintático irá construir o grafo de dependência e passa-lo ao analisador semântico. 3.4 DOCUMENTAÇÃO Uma documentação será construída, no decorrer da implementação da ferramenta. Como qualquer software, e mais ainda um desta complexidade, faz-se necessária uma boa documentação. Inicialmente, será feita a descrição da meta-linguagem de descrição. Questões como declaração de tokens e regras, inserção e uso de ações semânticas, serão descritas. Uma descrição de uso, e do próprio código fonte também será feita, visando não só o auxilio ao usuário da ferramenta, mas também ao seu suporte e desenvolvimento. Para que facilite sua extensão e seu uso. 3.5 VALIDAÇÃO E TESTES Como etapa final, a validação da ferramenta será feita de forma a, identificar e corrigir bugs, checar a representatividade da meta-linguagem.

23 REFERÊNCIAS AHO, Alfred V. et al. Compiladores: principios, técnicas e ferramentas. Tradução da 2. ed. [S.l.]: Pearson Addison-Wesley, 2008. GESSER, Carlos E. GALS - Gerador de Analisadores Léxicos e Sintáticos. [S.l.]: UFSC, 2003. JOHNSON, Stephen C. Yacc: Yet Another Compiler-Compiler. [Acesso em: 11-Julho- 2009]. Disponível em: <http://dinosaur.compilertools.net/yacc/index.html>. LL(1) vs LALR(1) parsers. [Acesso em: 11-Julho-2009]. Disponível em: <http://compilers.iecc.com/comparch/article/95-11-051>. LOUDEN, Kenneth C. Compiladores: principios e práticas. Tradução da 1. ed. [S.l.]: Pioneira Thomson Learning, 2004. MENEZES., Paulo. Linguagens formais e autômatos. 4. ed.. ed. [S.l.]: Sagra Luzzatto, 2000. PARR, Terence. About The ANTLR Parser Generator. [Acesso em: 11-Julho-2009]. Disponível em: <http://antlr.org/about.html>. Pragmatic Bo- PARR., Terence. The Definitive ANTLR Reference. 1. ed.. ed. [S.l.]: okshelf., 2007. SCHMIDT, Eric; LESK, Mike E. Lex - A Lexical Analyzer Generator. [Acesso em: 11- Julho-2009]. Disponível em: <http://dinosaur.compilertools.net/lex/index.html>. SIPSER., Michael. Introdução à teoria da computação. Tradução da 2. ed. [S.l.]: Thomson Learning, 2007.