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

RONALDO LIMA ROCHA CAMPOS - Coordenação de … · 2.3.3 ANTLR ... o compilador Lex recebeumaespecificaçãodagramáticaaserreconhecida,eproduz,comosaída,umpro-grama em linguagem

  • Upload
    leanh

  • View
    216

  • Download
    0

Embed Size (px)

Citation preview

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 com-putação e a toda informática. O mundo como conhecemos, depende das linguagens deprogramação, pois todo software existente foi escrito em alguma linguagem de progra-mação. Também todo o hardware que executa algum software, é regido por um tipode linguagem (linguagem objeto). Porém, antes que um programa (software) possa serexecutado, ele primeiro precisa ser traduzido de alguma linguagem de programação, paraa linguagem objeto. Esta tarefa é realizada por um compilador.

O presente trabalho de conclusão de curso busca desenvolver uma ferramenta queauxilie a criação de compiladores para linguagens de programação. Sendo sua estruturadivida em partes: gerador de analisadores léxicos, gerador de analisadores sintéticos, eque também seja integrado um suporte à implementação da analise semântica.

Inicialmente esse projeto propunha-se a dar continuidade a ferramenta GALS. OGALS foi desenvolvido sobre objetivos semelhantes. Também foi um projeto de con-clusão de curso, orientado pelo Prof. Dr. Olinto Furtado. Seus resultados atendem bemsuas espectativas, e a ferramente é muito utilizada. Porém, conforme foi sendo reali-zado o estudo de sua implementação, constatou-se uma necessidade de desacoplamentoda interface gráfica, de mudanças na linguagem de descrição, e de um melhor suporte aimplementação da análise semântica.

1.1 MOTIVAÇÃO

O projeto de um compilador é o casamento entre teoria e pratica. É uma área comimensa fundamentação teórica, que de fato é usado em pratica em seus complexos algo-ritmos.

Até alguns anos atrás, haviam poucas linguagens de programação, e poucas arquite-turas 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. Com-piladores antigos, eram geralmente implementados por uma única pessoa, ou um pequenogrupo, possuíam algumas centenas de linhas de código, e eram feitos em linguagens debaixo nível. Hoje, um projeto de compilador envolve equipes inteiras, recursos avançadosde engenharia de software, fazendo uso de componentes já existentes e frameworks deauxilio a construção de compiladores. Possuindo, assim, milhares de linhas código.

A construção de um compilador completo é muito complexa, demanda tempo, recursose conhecimento, sendo um trabalho oneroso e desnecessário, pois, etapas como análiseléxica e sintática podem ser automatizadas por geradores de compiladores.

Dessa forma o desenvolvimento de um compilador pode ser melhor focado nas etapasde descrição da linguagem, pois, desta forma, o projetista não precisa tomar conhecimentodos 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 con-sagradas 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 aouso uso de mais de uma ferramenta, levando, muitas vezes, à incompatibilidade no usodos analisadores gerados. Ou usam apenas uma das diversas técnicas de análise para aconstrução do parser.

Outro fator importante é a falta de documentação das soluções existentes que, emmuitos casos são restritas somente ao código fonte, e a alguns exemplos. Uma ferramentasimples, 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 odesenvolvimento de compiladores e interpretadores de linguagens de programação, fazendouso de uma linguagem de descrição da gramática e suas regras. A ferramenta deve, deforma automática, gerar as principais partes de um compilador: o analisador léxico e osintá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 eaproveitar suas melhores idéias para a etapa de implementação;

5. Utilizar algoritmos e partes de software já desenvolvidas no GALS, para modelarum sistema gerador de compiladores;

6. Desenvolver, na ferramente, um sistema de apoio a fase de analise semântica paraque 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 dedesenvolver teorias relacionadas com as linguagens naturais. No entanto, sua importânciafoi verificada em diversas outras áreas, em especial no estudo de linguagens artificiais elinguagens originárias da Computação e Informática (MENEZES., 2000).

Em computação uma linguagem formal pode ser formalmente definida como sendoum conjunto finito de símbolos sobre um alfabeto, ou seja, se L é uma linguagem e V umalfabeto, então: L ⊆ Vε, onde Vε é V ∪ {ε}.

2.1.1 LINGUAGENS REGULARES

Linguagens Regulares são linguagens formais, empregadas na construção de reconhe-cedores de linguagens. Apesar de fazerem parte de um universo restrito de linguagens, sãoempregados 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 deautô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 obastante para se construir uma teoria matemática manejável sobre eles. Autômatos sãouma especie de computador, fundados em dois princípios simples, porém muito podero-sos, memória (estados) e transições. Dassa forma consegue-se representar uma série deproblemas complexos.

7

DFA

Autômatos finitos determinísticos (do inglês deterministic finite automata, DFA) sãoo tipo mais simples de autômatos. O funcionamento de um DFA assemelha-se ao deuma 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ê, umsimbolo por vez. E conforme o simbolo lido, o programa de transição determina um novoestado à 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, Σ, δ, q0, 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. q0 é o estado inicial do automato, onde q0 ∈ 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 eo próximo símbolo de entrada, saberemos qual será seu próximo estado, para o não-determinismo, um conjunto de estados é esperado, ou seja, há vários “caminhos” quepodem ser seguidos de forma paralela.

Um automato finito não-determinísticos é formalmente definido como sendo uma 5-upla (Q, Σ, δ, q0, 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. q0 é o estado inicial do automato, onde q0 ∈ 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, paraqualquer 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 ex-pressão simples, pode-se então, inferir como construir tal linguagem. Uma expressãoregular é definida a partir de conjuntos básicos de símbolos e operações sobre eles, união econcatenação. São adequadas para descrever linguagens, pois dada sua forma, expressãomatemá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. (R1 ∪ R2) onde R1 e R2 são expressões regulares;

4. (R1 ◦ R2) onde R1 e R2 são expressões regulares;

5. (R∗1), onde R1 é uma expressão regular.

2.1.3 LINGUAGENS LIVRES DO CONTEXTO

A classe de linguagens livre do contexto compreende um universo mais amplo, possuemcomo um de seus subconjuntos as linguagens regulares. Seu estudo é de extrema impor-tância para a informática, pois por serem mais representativas, lidam com uma classe deproblemas comum às linguagens de programação, como por exemplo, balanceamento deexpressões, construções de blocos, entre outras (SIPSER., 2007).

Seu estudo é desenvolvido através de um formalismo axiomático, ou seja, uma gra-mática, e um reconhecedor (linguagem regular).

9

GRAMÁTICAS LIVRE DE CONTEXTO

Foram usadas primeiramente no estudo de linguagens humanas. Constituem-se emum método mais poderoso de descrever linguagens, descrevem características que possuemestrutura recursiva (MENEZES., 2000).

Sua principal aplicação ocorre na compilação de linguagens de programação. Em suamaioria, as linguagens de programação são criadas a partir de uma gramática estendida,denominada EBNF.

Sua definição formal é dada como, G = (Vn, Vt, P, S), onde:

1. Vn é um conjunto de símbolos, denominados não-terminais, são utilizados na des-crição da linguagem;

2. Vt é um conjunto de símbolos, denominados terminais, são os símbolos de fatoutilizados na formação das sentenças da linguagem;

3. P é um conjunto finito de produções, na forma P(α, β) onde α ∈ Vn, deriva β ∈(Vn ∪ Vt).

4. S é o símbolo inicial da gramática.

Sendo que P é da forma α → β, onde α é uma variável de Vn e β é uma palavra de(Vn ∪ Vt).

2.2 COMPILADORES

Em tese compiladores são programas que tratam de linguagens. Basicamente trans-formam uma linguagem fonte em uma linguagem alvo, ou seja, a partir de uma linguagemde entrada, é gerada uma linguagem de saída, e ambas possuem o mesmo significado ecomportamento. Esse processo é denominado de tradução.

De forma bem simples um compilador pode ser definido como um programa que, comoentrada recebe um programa fonte (em linguagem de programação), e o traduz para umprograma equivalente em outra linguagem, em sua maioria linguagem objeto, capaz deser executado por um computador alvo (LOUDEN, 2004).

Porém dependendo de sua saída, o compilador pode ainda ser classificado como (AHOet 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 diretamenteações em um computador.

Na maioria dos casos o processo completo de compilação pode ser dividido em duaspartes, análise e síntese.

Na análise, o compilador subdivide o programa fonte em partes e é gerado uma es-trutura gramatical sobre elas. A partir dessa estrutura é criado uma representação in-termediária do programa fonte. Nessa parte a análise verifica o programa sintática esemanticamente, e coleta informações sobre dados e comportamento do programa parapassar à próxima fase. Caso o programa fonte esteja incorreto, é desejável que o compi-lador 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çãointermediária e as informações retiradas, que muitas vezes recebe o nome de tabela desímbolos.

Por sua vez o processo de análise pode ser subdividido em outras partes, comumenteem 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 caracteresda entrada do programa, agrupá-los em lexemas e produzir como saída uma seqüênciade tokens. Esse fluxo de tokens é então enviado ao analisador sintático. Além disso oanalisador 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 sin-tático faz chamadas ao léxico sempre que precisar reconhecer um novo token. Emborafosse 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 daespecificação da linguagem e formatação dos caracteres, como quebras de linha, eoutras.

• Sua eficiência é melhorada, uma vez que podemos aplicar técnicas especializadasrelacionadas apenas à tarefa léxica.

• A portabilidade é melhorada, mudanças nos dispositivos de entrada do compiladorpodem 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 re-presenta um padrão de caracteres ou identificadores. Os tokens são os símbolos deentrada 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, reco-nhecendo dessa forma, palavras, identificadores e constantes, o analisador léxico é imple-mentado como um reconhecedor de linguagens regulares. Dessa forma sua implementaçãofaz uso, a partir de uma especificação, de autômatos finitos.

Essa forma garante eficiência e simplicidade, porém é comum precisarmos ler algunscaracteres a diante, afim de descobrir sobre qual token casamos nosso padrão. Podemosresolver esse problema com uma técnica bem simples, chamada lookahead, basta, ao severificar que um lexema pode ser aplicado a mais do um token, que se mantenha o estadoatual da analise, e se caminhe para o próximo símbolo, afim de se obter informaçõessuficientes para casar apenas um padrão.

A tarefa de construir um analisador léxico pode ser inteiramente automatizada, bas-tando apenas uma especificação formal da linguem a ser reconhecida e um software geradorde 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 umprograma. Ou seja, determina como uma cadeia de símbolos terminais pode ser geradapor 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 chamamosde árvore sintática.

A maioria dos algoritmos e métodos de análise sintática estão divididos em duasclasses, 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 cadeiade 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 esquemasde 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 acadeia 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 formamais fácil dessa forma.

2.2.3 ANÁLISE SEMÂNTICA

Como estrutura à análise semântica temos algumas técnicas bem formuladas, como ageraçã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çãodirigida por sintaxe.

13

TABELA DE SÍMBOLOS

São estruturas de dados que contem informações sobre as construções do programafonte. Essas informações são obtidas pelas fase de análise, e usadas na análise semânticae na fase de síntese.

Tal tabela contém informações sobre os identificadores como, seu nome, lexema, seutipo, 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 dosatributos em uma árvore de derivação. Seus nós são os atributos de uma produção espe-cí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, deforma a, durante a análise das produções e derivações, aplicar as regras semânticas aoprograma 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 de-senvolvidas. 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 gra-mática. Ele permite uma série de opções, como gerar somente o analisador léxico, ousomente 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 umaferramenta com auxilio didático, ele oferece uma série de opções para esse fim, como avisualizaçã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, possuitambé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 suasopçõ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 es-pecificaçã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 Lexrecebe uma especificação da gramática a ser reconhecida, e produz, como saída, um pro-grama 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 gerarum 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 lingua-gens 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 delinguagem. Essa especificação traz a estrutura de reconhecimento da linguagem de en-trada, juntamente com a função, em código para a linguagem C, para ser processadoquando uma estrutura é reconhecida.

Seu funcionamento, semelhante ao Lex, ocorre da seguinte forma, o compilador recebea 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 decaracteres. 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 funci-onamento 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 reconhecimentode linguagens. Seu projeto inicial tem mais de vinte anos. E em sua ultima versão, foicompletamente reescrito (PARR, ).

De forma diferente de outras ferramentas, o Antlr é uma ferramenta completa. Atravésde 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 aofato que parser LL são mais rápidos e requerem menos memória, e, principalmente, sãomais 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 gerarreconhecedores 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. Al-ternativamente, porém, o Antlr oferece um recurso de geração de uma árvore sintáticaabstrata, onde, dessa forma, as ações semânticas podem ser feitas ao se processar os nósda árvore (PARR., 2007).

17

3 PLANO DE TRABALHO

3.1 GERADOR DE COMPILADORES

Ao desenvolver um gerador de compiladores, precisamos definir alguns pontos defundamental 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 comportamentolé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 seustipos de algoritmos.

Ao submeter tal linguagem descritiva ao gerador de compiladores, o mesmo irá produ-zir, caso a gramática esteja livre de erros, uma série de arquivos em código fonte Java, ondeestarão descritos, os analisadores léxico e sintático e as definições semânticas, conformedefinidas na linguagem.

3.1.1 GERADOR DE ANALISADOR LÉXICO

A partir da meta-linguagem de entrada, o gerador léxico irá gerar um analisadorléxico, que essencialmente é um autômato que reconhece o padrão especificado. E queseguirá 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 deproduçõ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 descritapela 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 adicionaispara 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 emconjunto com o reconhecimento das produções da linguagem. Essa técnica, juntamentecom 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 en-cadeadas na forma de árvores. Contém informações sobre as construções do programafonte, 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 suasnecessidades. Como exemplo, valores como tipo e endereçamento, nomes e lexemas, sãoinformaçõ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áticade tipos. Tal gramática é responsável pela checagem de tipos em linguagens de progra-maçã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çãode 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 quealgumas 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, ouem 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 foremutilizadas, 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 comoentrada pela ferramenta.

Atualmente ela assemelha-se a descrição BNF, em que as produções são descritas emduas partes, um símbolo não-terminal a esquerda, seguido por um delimitador, e umaprodução ou uma expressão regular a direita. Segue um exemplo da sintaxe utilizada noGALS.

<ATRIB> ::= ID "=" <EXPR> ;

As produções iniciam-se entre “<”, “>”, são seguidas por um delimitador “::=”. Logoapós iniciam-se suas respectivas derivações, separadas por “|” e ao final há um símbolode 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 quasesemelhante, 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 visual-mente, 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 defi-nição de tokens e produções no mesmo arquivo. Que se justifica com a melhor clareza nadefiniçã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, aesquerda, 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 ousuário do sistema insira um caractere especial (#), seguido de um número, do lado direitodas produções. Dessa forma, durante a análise, ao encontrar tal símbolo, o analisadorsemâ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çãoda linguagem, sempre ao lado direito, e entre os símbolos, “{”, “}”. Tais ações sãoem 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 foiinserida 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 comoparâ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, poispermite a inclusão da ferramenta em outros softwares. Dessa forma, uma interface gráficapode ser criada e integrada facilmente a ferramenta, de forma simples e eficiente.

Definida a arquitetura do software, a parte mais importante é justamente o desenvol-vimento do núcleo da aplicação. Esse assemelha-se a um compilador, ele deve analisar alinguagem de entrada. E, a partir desta, deve produzir um conjunto de códigos fonte, querepresente 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óduloirá ler a gramática e realizar as configurações iniciais. Logo após, um segundo móduloirá gerar as partes básicas do compilador. O terceiro irá gerar os analisadores léxicos esintáticos.

3.3.1 GERADOR DE ANALISADOR LÉXICO

Após a linguagem descritiva ser “compilada”, deverá ser criado um analisador léxicopara 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 deautômatos finitos determinísticos, cujo comportamento principal é ler os caracteres daentrada, de forma incremental, e agrupa-los em tokens. Tornando-o ideal para o uso emanalisadores 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 comoo GALS, o analisador implementa, não apenas um mas um conjunto de algoritmos deparser.

Diferentes descrições de linguagens requerem diferentes tipos de parsers, por exemplo,linguagens recursivas a esquerda podem ser reconhecidas por um parser LALR, o mesmojá 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 encontradaem (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 facilitaressa etapa.

Como forma mais simples de dar peso semântico a linguagem, será possível fazer, atraduçã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 feitasobre esse grafo. Dessa forma ao processar uma regra, produção, o analisador sintáticoirá 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 boadocumentação.

Inicialmente, será feita a descrição da meta-linguagem de descrição. Questões comodeclaraçã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ó oauxilio ao usuário da ferramenta, mas também ao seu suporte e desenvolvimento. Paraque 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 corrigirbugs, 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]. Dis-ponível em: <http://antlr.org/about.html>.

PARR., Terence. The Definitive ANTLR Reference. 1. ed.. ed. [S.l.]: Pragmatic Bo-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.]: ThomsonLearning, 2007.