UNIVERSIDADE DO ESTADO DO AMAZONAS - UEA
ESCOLA SUPERIOR DE TECNOLOGIA
ENGENHARIA DE COMPUTAÇÃO
JOSIANE RODRIGUES DA SILVA
IMPLEMENTAÇÃO DAS ESTRUTURAS DE
CONTROLE DA LINGUAGEM JAVA PARA O
COMPILADOR JARAKI
Manaus
2012
JOSIANE RODRIGUES DA SILVA
IMPLEMENTAÇÃO DAS ESTRUTURAS DE CONTROLE DA
LINGUAGEM JAVA PARA O COMPILADOR JARAKI
Trabalho de Conclusão de Curso apresentado
à banca avaliadora do Curso de Engenharia
de Computação, da Escola Superior de
Tecnologia, da Universidade do Estado do
Amazonas, como pré-requisito para obtenção
do tı́tulo de Engenheiro de Computação.
Orientador: Prof. M. Sc. Jucimar Maia da Silva Júnior
Manaus
2010
ii
Universidade do Estado do Amazonas - UEA
Escola Superior de Tecnologia - EST
Reitor:
José Aldemir de Oliveira
Vice-Reitora:
Marly Guimarães Fernandes Costa
Diretor da Escola Superior de Tecnologia:
Mário Augusto Bessa de Figueiredo
Coordenadora do Curso de Engenharia de Computação:
Raimundo Corrêa de Oliveira
Coordenador da Disciplina Projeto Final:
Mário Augusto Bessa de Figueiredo
Banca Avaliadora composta por:
Data da Defesa: 23/11/2012.
Prof. M.Sc. Jucimar Maia da Silva Júnior (Orientador)
Prof. M.Sc. Prof. Msc. Raimundo Corrêa de Oliveira
Prof. M.Sc. Prof. Msc. Rodrigo Choji de Freitas
CIP - Catalogação na Publicação
S586i
SILVA, Josiane
Implementação das Estruturas de Controle Java para o Compilador Jaraki
/ Josiane da Silva; [orientado por] Prof. MSc. Jucimar Maia da Silva Júnior Manaus: UEA, 2012.
62 p.: il.; 30cm
Inclui Bibliografia
Trabalho de Conclusão de Curso (Graduação em Engenharia de Computação). Universidade do Estado do Amazonas, 2012.
CDU: 004.4’4
iii
JOSIANE RODRIGUES DA SILVA
IMPLEMENTAÇÃO DAS ESTRUTURAS DE CONTROLE DA
LINGUAGEM JAVA PARA O COMPILADOR JARAKI
Trabalho de Conclusão de Curso apresentado
à banca avaliadora do Curso de Engenharia
de Computação, da Escola Superior de
Tecnologia, da Universidade do Estado do
Amazonas, como pré-requisito para obtenção
do tı́tulo de Engenheiro de Computação.
Aprovado em: 23/11/2012
BANCA EXAMINADORA
Prof. Jucimar Maia da Silva Júnior, Mestre
UNIVERSIDADE DO ESTADO DO AMAZONAS
Prof. Raimundo Corrêa de Oliveira, Mestre
UNIVERSIDADE DO ESTADO DO AMAZONAS
Prof. Rodrigo Choji de Freitas, Mestre
UNIVERSIDADE DO ESTADO DO AMAZONAS
iv
Agradecimentos
Agradeço primeiramente à Deus, que foi meu
maior porto seguro, sem ele não teria conseguido chegar até aqui. À meus pais que
mesmo longe foram peça fundamental nessa
trajetória, com palavras de apoio fizeram
com que este caminho se tornasse mais sereno e menos apreensivo. A toda a minha
famı́lia que sempre se fez presente em minha
vida acadêmica. Aos meus amigos, principalmente os que trabalharam juntos neste projeto, que sempre me deram boas dicas de implementação. Ao meu orientador Prof. Msc.
Jucimar Maia da Silva Júnior que me recebeu
tão bem no projeto e que me ajudou tanto.
Enfim, a todas as pessoas que de algum modo
contribuı́ram para que este trabalho se tornasse realidade.
v
Resumo
Este trabalho apresenta a implementação das estruturas de controle, presentes na linguagem de programação Java para o compilador Jaraki, um projeto que compila códigosfonte Java para a Máquina Virtual do Erlang. Utiliza as ferramentas Leex e Yecc para
gerar as análises léxica e sintática, além de módulos em Erlang e bibliotecas que facilitam
a geração de códigos das estruturas desenvolvidas.
Palavras Chave: Compilador, Java, Erlang, Estrutura de Controle
vi
Abstract
This work presents the implementation of control structures present in the Java programming language for Jaraki compiler, a project that compiles Java source code for the
Erlang Virtual Machine. Users Leex and Yecc tools to generate lexical and syntactic
analysis, and in Erlang modules and libraries that facilitate the generation of codes of the
structures developed.
Key-words: Compiler, Java, Erlang, Control Structures
vii
Sumário
Lista de Tabelas
x
Lista de Figuras
xi
Lista de Códigos
xii
1 Introdução
1.1 Introdução . . . . . . . . . .
1.2 Objetivo . . . . . . . . . . .
1.2.1 Objetivos Especı́ficos
1.3 Trabalhos relacionados . . .
1.4 Justificativa . . . . . . . . .
1.5 Metodologia . . . . . . . . .
1.6 Estrutura da Monografia . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2 Referencial Teórico
2.1 Compiladores . . . . . . . . . . . . . . . .
2.1.1 Análise Léxica . . . . . . . . . . . .
2.1.2 Análise Sintática . . . . . . . . . .
2.1.3 Análise Semântica . . . . . . . . .
2.1.4 Geração de código final . . . . . . .
2.1.5 Gerenciador da Tabela de Sı́mbolos
2.1.6 Tratamento de Erros . . . . . . . .
2.2 Gramática Livre de Contexto . . . . . . .
2.2.1 EBNF . . . . . . . . . . . . . . . .
2.3 A Linguagem Java . . . . . . . . . . . . .
2.3.1 Caracterı́sticas da Linguagem Java
2.3.2 Classes e Objetos . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
2
2
3
3
3
4
.
.
.
.
.
.
.
.
.
.
.
.
5
5
6
7
7
8
8
8
9
9
10
11
11
viii
2.4
2.5
2.6
2.3.3 Estruturas de Controle . . . . . . . .
A Linguagem Erlang . . . . . . . . . . . . .
2.4.1 Caracterı́sticas da Linguagem Erlang
Estruturas de Controle . . . . . . . . . . . .
O projeto Jaraki . . . . . . . . . . . . . . .
3 Desenvolvimento
3.1 Estrutura Geral do Compilador .
3.2 Geradores dos analisadores . . . .
3.2.1 Leex . . . . . . . . . . . .
3.2.2 Yecc . . . . . . . . . . . .
3.3 Estruturas de Repetição . . . . .
3.3.1 Estrutura While . . . . . .
3.3.2 Estrutura Do While . . .
3.3.3 Estrutura For . . . . . . .
3.4 Estruturas de Condição . . . . . .
3.4.1 Estrutura IF . . . . . . . .
3.5 Estruturas de Sequência . . . . .
3.5.1 Estrutura System.out.print
3.5.2 Estrutura Scanner . . . .
3.5.3 Estrutura Random . . . .
3.5.4 Estrutura Arquivos . . . .
.
.
.
.
.
.
.
.
.
.
.
e
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
System.out.println
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4 Testes
4.1 Processo de Compilação . . . . . . . . . . . . . . . . . .
4.1.1 Árvore sintática para a linguagem Java - JAST .
4.1.2 Árvore abstrata do Erlang - AST . . . . . . . . .
4.2 Teste 1 - Print . . . . . . . . . . . . . . . . . . . . . . . .
4.3 Teste 2 - Scanner . . . . . . . . . . . . . . . . . . . . . .
4.4 Teste 3 - Random . . . . . . . . . . . . . . . . . . . . . .
4.5 Teste 4 - Arquivos . . . . . . . . . . . . . . . . . . . . .
4.6 Teste 5 - IF . . . . . . . . . . . . . . . . . . . . . . . . .
4.7 Teste 6 - FOR . . . . . . . . . . . . . . . . . . . . . . . .
4.8 Teste 7 - While . . . . . . . . . . . . . . . . . . . . . . .
4.9 Teste 8 - Do While . . . . . . . . . . . . . . . . . . . . .
4.10 Resumo dos tempos de Compilação e Execução dos testes
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13
14
15
16
16
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
18
18
19
19
19
20
20
23
25
27
27
31
31
34
36
38
.
.
.
.
.
.
.
.
.
.
.
.
42
42
42
44
45
47
48
50
51
53
54
56
58
ix
5 Conclusões
5.1 Trabalhos Futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59
60
Referências Bibliográficas
61
A
62
x
Lista de Tabelas
3.1
3.2
3.3
3.4
Tabela de Tokens .
Palavras reservadas
Palavras reservadas
Palavras reservadas
. . . . . . . . . . . . . .
da linguagem - While .
da linguagem - Scanner
da linguagem - Arquivos
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
21
21
34
39
4.1
Tempos de compilação e execução dos testes . . . . . . . . . . . . . . . . .
58
xi
Lista de Figuras
2.1
2.2
2.3
2.4
Visão geral de um Compilador
Fases de Compilação . . . . .
Ávore sintática . . . . . . . .
Compilador Jaraki . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
6
7
17
3.1
3.2
3.3
3.4
3.5
3.6
3.7
3.8
3.9
3.10
3.11
3.12
3.13
Fases de Compilação do Compilador Jaraki . . . . . . . . . .
Análise Semântica - While . . . . . . . . . . . . . . . . . . .
Análise Semântica - Do While . . . . . . . . . . . . . . . . .
Análise Semântica - FOR . . . . . . . . . . . . . . . . . . . .
Árvore Sintática - IF com ambiguidade . . . . . . . . . . . .
Árvore Sintática - IF sem ambiguidade . . . . . . . . . . . .
Análise Semântica - If . . . . . . . . . . . . . . . . . . . . .
Semântica do Print . . . . . . . . . . . . . . . . . . . . . . .
Semântica do Scanner . . . . . . . . . . . . . . . . . . . . .
Semântica do Random . . . . . . . . . . . . . . . . . . . . .
Semântica - Instanciação das classes FileReader e FileWriter
Semântica - Função read() . . . . . . . . . . . . . . . . . .
Semântica - Funções write() e close() . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
19
23
25
27
29
29
30
32
36
37
40
40
41
4.1
4.2
4.3
4.4
4.5
4.6
4.7
4.8
4.9
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
46
46
46
47
47
48
48
48
49
de
de
de
de
de
de
de
de
de
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
compilação Print - Java . . .
execução Print - Java . . . .
compilação Print - Erlang .
execução Print - Erlang . . .
compilação Scanner - Java .
execução Scanner - Java . .
compilação Scanner - Erlang
execução Scanner - Erlang .
compilação Random - Java .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
xii
4.10
4.11
4.12
4.13
4.14
4.15
4.16
4.17
4.18
4.19
4.20
4.21
4.22
4.23
4.24
4.25
4.26
4.27
4.28
4.29
4.30
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
de
de
de
de
de
de
de
de
de
de
de
de
de
de
de
de
de
de
de
de
de
execução Random - Java . . . . . . . . .
compilação Random - Erlang . . . . . . .
execução Random - Erlang . . . . . . . .
compilação e execução Arquivos - Java .
compilação e execução Arquivos - Erlang
compilação If - Java . . . . . . . . . . . .
execução If - Java . . . . . . . . . . . . .
compilação If - Erlang . . . . . . . . . .
execução If - Erlang . . . . . . . . . . . .
compilação For - Java . . . . . . . . . . .
execução For - Java . . . . . . . . . . . .
compilação For - Erlang . . . . . . . . .
execução For - Erlang . . . . . . . . . . .
compilação while - Java . . . . . . . . . .
execução while - Java . . . . . . . . . . .
compilação While - Erlang . . . . . . . .
execução While - Erlang . . . . . . . . .
compilação Do While - Java . . . . . . .
execução Do While - Java . . . . . . . .
compilação Do While - Erlang . . . . . .
execução Do While - Erlang . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
49
50
50
51
51
52
52
53
53
54
54
54
54
55
55
56
56
56
57
57
57
xiii
Lista de Códigos
2.5.1 Pattern Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
3.3.1 Código Java - While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20
3.3.2 Lista dos tokens gerados pelo Leex . . . . . . . . . . . . . . . . . . . . . . .
22
3.3.3 Regras EBNF - While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
23
3.3.4 Código Java - Do While . . . . . . . . . . . . . . . . . . . . . . . . . . . .
24
3.3.5 Regras EBNF - Do While . . . . . . . . . . . . . . . . . . . . . . . . . . .
24
3.3.6 Código Java - FOR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
26
3.3.7 Regras EBNF - For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
26
3.4.1 Código Java - If . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28
3.4.2 Regras EBNF - If . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
3.4.3 Estrutura geral do comando Case . . . . . . . . . . . . . . . . . . . . . . .
30
3.4.4 Código gerado em Erlang - If . . . . . . . . . . . . . . . . . . . . . . . . .
31
3.5.1 Código Java - Print . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
3.5.2 Regras EBNF - Print . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
32
3.5.3 Código gerado em Erlang - Print . . . . . . . . . . . . . . . . . . . . . . .
33
3.5.4 Código Java - Scanner . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
34
3.5.5 Regra EBNF - Scanner . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
3.5.6 Código gerado em Erlang - Scanner . . . . . . . . . . . . . . . . . . . . . .
35
3.5.7 Código Java - Random . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
36
3.5.8 Regra EBNF - Random . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
37
3.5.9 Código gerado em Erlang - Random . . . . . . . . . . . . . . . . . . . . . .
38
3.5.10Código Java - Arquivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
39
3.5.11Regra EBNF - FileReader e FileWriter . . . . . . . . . . . . . . . . . . . .
39
xiv
4.1.1 Código Java - Hello World . . . . . . . . . . . . . . . . . . . . . . . . . . .
43
4.1.2 JAST - Hello World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
43
4.1.3 AST - Hello World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
44
4.1.4 Código Erlang - Hello World . . . . . . . . . . . . . . . . . . . . . . . . . .
45
4.2.1 Código Java - Print . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
46
4.3.1 Código Java - Scanner . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
47
4.4.1 Código Java - Random . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
49
4.5.1 Código Java - Arquivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
51
4.6.1 Código Java - IF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
52
4.7.1 Código Java - For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53
4.8.1 Código Java - While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
55
4.9.1 Código Java - Do While . . . . . . . . . . . . . . . . . . . . . . . . . . . .
56
Capı́tulo 1
Introdução
Neste capı́tulo é feita uma breve introdução do trabalho, apresentando os objetivos, justificativa e metodologia de desenvolvimento do projeto.
1.1
Introdução
As estruturas de controle das linguagens de programação facilitam tanto a escrita
quanto a legibilidade de códigos-fonte. Durante a década de 1960 tornou-se claro que
a utilização indiscriminada de transferências de controle era a raiz de muitas dificuldades
experimentadas por grupos de desenvolvimento de software. A culpa disso é a instrução
goto, que permite ao programador especificar uma transferência de controle para um entre
vários espectros de destinos possı́veis em um programa. O trabalho de Bohm e Jacopini demonstrou que todos os programas poderiam ser escritos baseados em somente três
estruturas de controle - sequência, seleção e repetição. [Deitel2010]
Os tipos de estruturas de controle diferem de linguagem para linguagem, porém podem
ser, de forma geral, caracterizadas por seus efeitos. O primeiro é a continuação da execução
em uma outra instrução, como em uma estrutura sequencial. O segundo é a execução de
um bloco de código somente se uma condição é verdadeira, como acontece nas estruturas de
seleção. Ou ainda a execução de um bloco de código enquanto uma condição é verdadeira,
ou de forma a iterar uma coleção de dados, como nas estruturas de repetição [Sebesta2011].
Objetivo
2
Na linguagem de programação Java, essas estruturas se fazem presentes, e implementam
as três estruturas de controle de entrada e saı́da citadas anteriormente [Java2012]. Assim
como em outras linguagens, essas intruções facilitam a construção de programas Java.
Erlang é uma linguagem de programação funcional não-pura para programação de sistemas concorrentes e distribuı́dos, e incorpora muita das recentes inovações de linguagem de
programação, incluindo funções de alta ordem, avaliação estrita, gerenciamento de memória, pattern-matching, compreensão de listas, sistema de módulos, tipos de dados primitivos, incluindo inteiros, números reais, suporte para programação sequencial e concorrente.
Os programas em Erlang são escritos inteiramente com funções e uma variável pode receber
apenas um valor [Armstrong2007].
Segundo Aho, existem duas partes na compilação: a análise e a sı́ntese. A parte de
análise divide o programa fonte nas partes constituintes e cria uma representação intermediário do mesmo. A de sı́ntese constrói o programa alvo desejado, a partir da representação
intermediária [Aho2007]. Seguindo essa linha, o compilador Jaraki é um projeto que visa
compilar códigos-fonte Java para a máquina virtual Erlang, implementando os principais
aspectos imperativos da linguagem Java, como as estruturas de controle.
1.2
Objetivo
O principal objetivo desse trabalho é implementar as estruturas de controle encontradas na linguagem Java, que são as estruturas de sequência, condição e repetição, para o
compilador Jaraki.
1.2.1
Objetivos Especı́ficos
Os objetivos especı́ficos são:
• Implementar a estrutura de sequência, estruturas de entrada e saı́da de dados;
• Implementar a estrutura de condição de dados;
• Implementar a estrutura de repetição de dados;
Trabalhos relacionados
1.3
3
Trabalhos relacionados
Similar ao compilador Jaraki, outras linguagens foram criadas com o mesmo intuito.
Entre elas estão o Jython, uma implementação da linguagem de programação Python escrita em Java 100% puro. Um programa Jython pode ser executado em qualquer máquina
compatı́vel com o Java Virtual Machine. Por meio dessa linguagem pode-se escrever programas em Python que se integram perfeitamente com qualquer código Java [Jython2012].
Da mesma forma temos o Jruby, uma implementação da linguagem Ruby também escrita
em Java. Jruby é um Ruby para o JVM, e fornece um conjunto completo de classes e de
sintaxe para a linguagem Ruby [Jruby2012]. Seguindo o mesmo raciocı́onio a linguagem
Reia é similiar ao Ruby, porém foi desenvolvida como linguagem de script para a máquina
virtual do Erlang, combinando a sintaxe amigável do Ruby com a vantagens da linguagem
funcional Erlang [Reia2012].
1.4
Justificativa
Para linguagens de programação orientada a objetos é necessário se fazer o processamento de entrada e saı́da dos dados, assim como executar as instruções de condição e
repetição de dados, uma vez que essas são estruturas básicas da maioria das linguagens
de programação, sem elas ficaria difı́cil desenvolver programas mais complexos. Como vimos na seção Trabalhos Relacionados, existem alguns trabalhos similares ao compilador
Jaraki, mas nenhum desses compiladores porta uma linguagem de programação orientada
a objetos para uma linguagem funcional. Além disso, estruturas de repetição não são implementadas em linguagens funcionais, neste caso deverá ser abordada uma nova forma de
gerar estruturas desse tipo em Erlang.
1.5
Metodologia
A metodologia adotada para o desenvolvimento deste trabalho é orientada a testes.
Para cada um desses testes são criados subconjuntos que consistem em códigos-fonte Java,
contendo todas as estruturas da linguagem Java definidas nesses subconjuntos, implemen-
Estrutura da Monograa
4
tando de forma interativo incremental as análises léxicas, sintáticas e semântica necessárias
para a geração desse código-fonte Java em Erlang. Após a geração do código, esse teste
será compilado e executado.
1.6
Estrutura da Monografia
Neste trabalho, é apresentado a implementação de um compilador da linguagem de
programação Java para a máquina virtual Erlang. No capı́tulo 2, Referencial Teórico, é
descrito alguns conceitos importantes de compiladores, da Linguagem Java e da Linguagem
Erlang, fazendo uma comparação entre as duas linguagens. O capı́tulo 3 apresenta os
detalhes da implementação, o tratamento utilizado para o desenvolvimento das análises
léxica, sintática e semântica, bem como a estrutura do compilador. O capı́tulo 4 trata
dos testes que foram realizados no decorrer do desenvolvimento do projeto. E por fim, no
capı́tulo 5 é apresentada uma conclusão com sugestões de futuros trabalhos utilizando o
compilador Jaraki como referencial.
Capı́tulo 2
Referencial Teórico
Neste capı́tulo é abordado os principais conceitos sobre Compiladores, Linguagem Java e
Linguagem Erlang, necessários para melhor entendimento do desenvolvimento deste trabalho.
2.1
Compiladores
Segundo Aho, um compilador é um programa que lê um programa escrito numa linguagem, chamada de linguagem fonte, e o traduz em uma outra linguagem, a chamada
linguagem alvo [Aho2007]. A figura 2.1 exemplifica um compilador.
Figura 2.1: Visão geral de um Compilador
O processo de compilação é composto da análise e sı́ntese. A análise tem por objetivo
entender o código-fonte e representá-lo em uma estrutura intermediária. A sı́ntese constrói
o código objeto a partir dessa representação intermediária [Aho2007].
Compiladores
6
A análise é subdividida em análise léxica, análise sintática, análise semântica e geração
de código intermediário. A sı́ntese varia de um compilador a outro, podendo ser composta
pelas etapas de otimização de código e geração de código final, sendo que apenas essa
última etapa é obrigatória. A figura 2.2 mostra as fases que compõe um compilador.
Figura 2.2: Fases de Compilação
2.1.1
Análise Léxica
A análise léxica é a primeira fase de um compilador. A função de um analisador léxico é
ler o código fonte, caractere a caractere, fazendo a separação e identificação dos elementos
do programa fonte que pertencem a gramática da linguagem fonte, denominados sı́mbolos
léxicos ou tokens. Esses caracteres são agrupados em tokens formando uma tabela, que
servirá de entrada para a análise sintática. Nessa fase, o programa fonte é visto como uma
sequência de caracteres que serão agrupados e identificados como palavras reservadas da
linguagem (como public, class, new, scanner no caso da linguagem Java), constantes (65,
‘A’) e nomes de variáveis ou identificadores. Elementos como espaços em branco, marcas
de formatação de textos e comentários são eliminados.
Compiladores
7
Existem alguns geradores automáticos de analisadores léxicos, como por exemplo o
Leex, que é o gerador de análise léxica para Erlang.
2.1.2
Análise Sintática
A análise sintática, também chamada de análise gramatical ou parsing, preocupa-se com
o processo de determinar se uma cadeia de tokens pode ser gerada por uma gramática. É
a fase responsável por verificar se os sı́mbolos contidos no programa fonte, reconhecidos na
análise léxica, formam um programa válido. A partir dessa análise é gerada uma estrutura
intermediária, denominada de árvore sintática. Os nós internos da árvore sintática são
rotulados pelos nomes das estruturas que elas recebem, as regras que são geradas, e as
folhas representam a sequência recebida, como mostra a figura 2.3.
Figura 2.3: Ávore sintática
Exemplos de geradores automáticos de analisadores sintáticos são o Bison e o Yecc,
sendo este último o gerador de análise sintática para Erlang.
2.1.3
Análise Semântica
Segundo Aho, a análise semântica verifica os erros semânticos no programa fonte e
captura as informações de tipo para a fase subsequente de geração de código. Tem como
entrada a árvore sintática da análise sintática, a fim de identificar os operadores e operandos
das expressões e enunciados [Aho2007].
Compiladores
8
O papel do analisador semântico é assegurar que todas as regras sensı́veis ao contexto
da linguagem estejam analisadas e verificadas quanto à sua validade. Um exemplo de tarefa
própria do analisador semântico é a checagem de tipos de variáveis em expressões. Seguindo
a especificação da linguagem fonte, é verificado se cada operando recebe os operadores que
são permitidos, ou seja, se um tipo float pode receber um tipo inteiro, por exemplo.
2.1.4
Geração de código final
A geração de código final é a última etapa de compilação. Segundo Aho, o código objeto
pode ser uma sequência de instruções absolutas de máquina, uma sequência de instruções
de máquina realocáveis, um programa em linguagem assembly ou um programa em outra
linguagem [Aho2007], no caso do compilador Jaraki, o programa alvo é gerado em uma
outra linguagem.
2.1.5
Gerenciador da Tabela de Sı́mbolos
Um papel importante de um compilador é fazer o gerenciamento da tabela de sı́mbolos.
É necessário que todos os identificadores usados no programa fonte sejam registrados,
assim como as informações sobre seus atributos, tais como a memória reservada para o
identificador, seu tipo e escopo.
A tabela de sı́mbolos é uma estrutura de dados, pode ser uma árvore ou uma tabela
hash, utilizada para o armazenamento de informações dos identificadores e é utilizada em
quase todas as fases de compilação.
2.1.6
Tratamento de Erros
Como parte importante do processo de compilação temos o tratamento de erros, que
tem como principal objetivo detectar cada erro, reportá-lo ao usuário e fazer o devido
tratamento para que o processamento possa continuar. Um bom compilador deve passar
por todas as fases de compilação e ao final reportar todos os erros encontrados, a fim de
que o processamento não seja interrompido logo no primeiro erro detectado.
Gramática Livre de Contexto
9
Os erros podem ser detectados em cada fase de compilação. Os erros léxicos ocorrem
quando um token identificado não pertence a gramática da linguagem fonte. Os erros
sintáticos ocorrem quando alguma estrutura da frase não está de acordo com a gramática,
como por exemplo parênteses sem correspondência. Os erros semânticos em geral são
associados a não declaração de objetos como variáveis e erros de tipos.
2.2
Gramática Livre de Contexto
Em Linguagem formal, uma gramática livre-do-contexto (GLC) é uma gramática formal
em que cada regra de produção é da forma
V -> w
onde V é um único sı́mbolo não-terminal(Variável), e w é uma cadeia de terminais e/ou
variáveis (w pode ser a cadeia vazia) [Sebesta2011].
As linguagens geradas por gramáticas livres-do-contexto são conhecidas como Linguagens livres-do-contexto.
Gramáticas livres-do-contexto são importantes em Linguı́stica para descrição da estrutura de sentenças e palavras em Lı́ngua natural. Em Ciência da computação é importante
para descrição da estrutura de Linguagens de programação e outras linguagens artificiais [Sebesta2011].
2.2.1
EBNF
EBNF (Extended Backus–Naur Form) é uma notação formal para representação de
uma gramática livre de contexto. É uma extensão da BNF (Extended -BNF), que devido
algumas inconveniências encontadas na BNF, foi desenvolvida para ampliar a facilidade de
leitura e concisão entre as produções [Sebesta2011].
Três extensões são comumente incluı́das nas várias versões da EBNF. A primeira delas
denota uma parte opcional de um RHS, que é delimitada por colchetes. Por exemplo, uma
instrução de seleção C pode ser descrita como
<selecao> -> if ( <expressao> ) <instrucao> [else <instrucao>];
A Linguagem Java
10
Sem o uso dos colchetes, a descrição sintática dessa instrução exigiria duas regras [Sebesta2011].
A segunda extensão é o uso de chaves em um LD para indicar que a prate nelas contida
pode ser repetida indefinidamente ou omitida completamente. Esta permite que listas
sejam construı́das com uma única regra, em vez de usar recursão e duas regras. Por
exemplo, listas de identificadores separadas por vı́rgulas podem ser escritas da maneira
mostrada a seguir [Sebesta2011].
<lita_ident> -> <identificador> {, <identificador>}
A terceira extensão comum é lida com opções de múltipla escolha. Quando um único
elemento deve ser escolhido de um grupo, as opções são colocadas entre parêsteses e separadas pelo operador OU, |. Por exemplo, a regra seguinte descreve uma isntrução Pascal
for:
<inst_for> -> for <var> ::= <expr> (to | downto) <expr> do <inst>
Mais uma vez, seriam necessários duas regras BNF para descrever essa estrutura. Os
colchetes, as chaves e os parênteses nas extensões BNF são metassı́mbolos, o que significa
que eles são ferramentas notacionais e não sı́mbolos terminais nas entidades sintáticas
que ajudam a descrever. Nos casos em que esses metassı́mbolos também são sı́mbolos
terminais na linguagem que será descrita, as instâncias que são sı́mbolos terminais podem
ser sublinhadas [Sebesta2011].
2.3
A Linguagem Java
Java é uma linguagem de programação orientada o objeto desenvolvida na primeira
metade da década de 90 por uma equipe de programadores liderada por James Gosling,
nos laboratórios da Sun Microsystems, adequada para o desenvolvimento de aplicações
baseadas em rede Internet e redes fechadas. Diferente das linguagens convencionais, que
são compiladas para código nativo, a linguagem Java é compilada para um bytecode que é
executado por uma máquina virtual [Deitel2010].
A Linguagem Java
2.3.1
11
Caracterı́sticas da Linguagem Java
Java é uma linguagem simples e de fácil manipulação que possue sintaxe muita parecida
com C++. Apesar de ser uma linguagem 100% orientada a objetos, possue tipos primitivos
de dados que não são objetos, mas que foram criados e incorporados para permitir uma
melhor utilização da linguagem pelos programadores.
Um programa desenvolvido em Java precisa ser compilado, gerando um bytecode. Para
executá-lo é necessário então, que um interpretador leia o bytecode e repasse as instruções
ao processador da máquina especı́fica. Esse interpretador é conhecido como JVM (Java
Virtual Machine).
As funcionalidades que são fornecidas pela liguagem Java para desenvolver programas
que manipulem as redes através das APIs são simples e de grande potencialidade. Por meio
dessas APIs é possı́vel manipular protocolos como TCP/IP, HTTP, FTP e utilizar objetos
da grande rede via URLs.
Outra caracterı́stica importante da linguagem Java é a portabilidade. Um programa
Java pode ser executado em qualquer arquitetura de hardware e sistema operacional, sem
precisar ser recompilado, pode ser executado em qualquer plataforma que possua um interpretador Java.
Java fornece uma série de mecanismos para garantir a segurança dos aplicativos. Um
programa em Java não tem contato com o computador real; ele conhece apenas a máquina
virtual (JVM). A máquina virtual decide o que pode ou não ser feito. Um programa Java
nunca acessa dispositivos de entrada e saı́da, sistema de arquivos, memória, ao invés disso
ele pede a JVM que acesse.
2.3.2
Classes e Objetos
Java é uma linguagem orientada a objetos, e como tal usa classes e objetos para representar os conceitos de abstração e encapsulamento. A estrutura de software que suporta
abstração de dados é conhecida como classe. Classe é um tipo de dado que captura a
essência de uma abstração. É caracterizada por um conjunto de aspectos, que podem ser
um dado ou uma operação. Os dados são representados por instâncias de variáveis em uma
classe. Em Java as operações são conhecidas como métodos [Buyya2009].
A Linguagem Java
12
Classe é um tipo de dado e portanto não pode ser diretamente manipulada. Ela descreve
um conjunto de objetos. Por exemplo,
maçã é uma fruta
implica que maçã é um exemplo de fruta. O termo “fruta” é um tipo de comida e maçã
é uma instância de fruta. Dessa forma, classes contêm os atributos e os métodos e ainda
são responsáveis pela estrutura dos objetos.
A criação de uma classe em Java é iniciada pela palavra reservada class. A sintaxe é a
seguinte:
[modificadores] class <NomeDaClasse>
{
//corpo da classe
}
[modificadores]: são os modificadores que impõem regras de acesso (public, protected,
private) e de tipo (abstract, final ).
Os objetos são as instâncias das classes e através deles podemos manipular os métodos
e atributos de um objeto. No exemplo de declaração de variável,
Date data;
data não é um objeto, mas simplesmente uma definição do tipo da variável data. Como
essa variável não se trata de um objeto, a mesma não pode se referenciar aos métodos e
atributos do objeto. Para se obter um objeto, é necessário inicializar a variável e para
inicializar esta variável é necessário utilizar o operador new. O exemplo acima ficaria da
seguinte forma:
Data data = new Data();
A palavra reservada new cria uma referência para um objeto, e esta referência é armazenada na variável do objeto. As variáveis de objetos locais não são automaticamente
inicializadas para null, por isso devem ser inicializadas explicitamente com o operador new
ou com o valor null.
A Linguagem Java
2.3.3
13
Estruturas de Controle
A linguagem Java é baseada em três estruturas básicas de controle: a estrutura de
sequência, a estrutura de condição e a estrutura de repetição.
Estruturas de Sequência
Nas estruturas de sequência o computador executa as instruções Java uma depois da
outra na ordem em que elas são escritas, ou seja, em sequência. Exemplo de estrutura de
sequência é qualquer atribuição, expressão matemática ou ainda um comando de print.
Estruturas de Condição
Nas estruturas de condição um bloco de instruções Java é executado se a condição for
verdadeira. A linguagem Java fornece três instruções de seleção. A instrução if realiza
uma ação se essa for verdadeira ou pula se a condição for falsa:
if (condicao){
bloco_comandos;
}
A instrução if...else realiza a ação se a condição for verdadeira e realiza uma diferente
se a condição for falsa:
if (condicao) {
bloco_comandos_caso_verdade
}
else {
bloco_comandos_caso_falso
}
O comando switch...case também expressa alternativas de execução, mas nesse caso as
condições estão restritas à comparação de uma variável inteira com valores constantes:
Estruturas de Repetição
O Java tem suporte a três estruturas de repetição, que permitem os programas executar
repetidas instruções, contanto que a condição de continuação do loop permaneça verdadeira.
As instruções de repetição são as intruções for, while e do...while.
A Linguagem Erlang
14
switch (variavel) {
case valor1:
bloco_comandos
break;
case valor2:
bloco_comandos
break;
...
case valorn:
bloco_comandos
break;
default:
bloco_comandos
}
O comando for permite expressar iterações combinando uma expressão de inicialização,
um teste de condição e uma expressão de incremento:
for (inicializacao; condicao; incremento) {
bloco_comandos
}
O comando while permite expressar iterações que devem ser executadas se e enquanto
uma condição for verdadeira:
while (condicao) {
bloco_comandos
}
O comando do...while também permite expressar iterações, mas neste caso pelo menos
uma execução do bloco de comandos é garantida:
2.4
A Linguagem Erlang
Erlang é uma linguagem de programação funcional projetada para sistemas complexos
tolerantes a falhas, desenvolvido originalmente no Laboratório de Ciência de computação
da empresa sueca Ericsson Telecom. Através de um conjunto de processos paralelos que
podem interagir apenas por troca de mensagens, modela facilmente a programação paralela.
A Linguagem Erlang
15
do {
bloco_comandos
} while (condicao);
Diferente de muitas linguagens, no Erlang, há processos paralelos, mas sem bloqueios,
métodos sincronizados e memória compartilhada. Fornece soluções para problemas que
são comumente encontrados em grandes sistemas durante a programação simultânea de
tempo real [Armstrong2007].
2.4.1
Caracterı́sticas da Linguagem Erlang
Erlang é uma linguagem declarativa, ou seja, trabalha com o princı́pio de tentar descrever o que deve ser calculado, em vez de dizer como um valor é calculado. Descreve em
alto nı́vel uma aplicação onde os programadores não especificam sequências de operações,
como em uma linguagem imperativa. Tem um modelo de concorrência baseado em processos com passagem de mensagem assı́ncrona. Cada processo em Erlang é executado em
seu próprio espaço de memória, comunicando-se através de mensagens. Os mecanismos de
concorrência são leves (lightweight), ou seja, requerem pouca memória, e demandam um
esforço menor para criar, destruir processos e passar mensagens entre eles [Cesarini2009].
A linguagem Erlang é destinada para programação de sistemas de tempo real, onde
são obrigatórios tempos de resposta em milissegundos. O gerenciamento de memória em
Erlang é automatizado, com garbage colection implementado na base de cada processo. A
memória é alocada automaticamente e desalocada quando não usada. Portanto, tı́picos
erros de programação relacionados ao gerenciamento de memória não podem ocorrer. Tem
um conjunto simples de mecanismos para tratamentos de erros, monitoramento de exceções
e bibliotecas construı́das para esse propósito, deixando os programas com códigos mais
curtos e fáceis de entender [Cesarini2009].
Além disso, tem distribuição incorporada na sintaxe e semântica da linguagem e como
não compartilha memória, sistemas distribuı́dos podem facilmente ser construı́dos. Aplicações que utilizam um único processador podem, sem dificuldade, serem portadas para
rodar em uma rede de computadores.
Erlang pode facilmente fazer uso de programas escritos em outras linguagens de progra-
Estruturas de Controle
16
mação e podem ser interligados ao sistema de uma maneira tal que pareça ao programador
ter sido escrito em Erlang [Cesarini2009].
2.5
Estruturas de Controle
As estruturas de controle em Erlang são baseadas no uso do pattern matching (casamento de padrão), que é usado para escolher a clásula sem a função. Pattern matching é
também utilizado para a escolha de caminho de execução dentro do corpo de cláusulas, o
exemplo do código 2.5.1 ilustra o uso dos comandos if e case.
case Expr of
Pattern1 [when Guard1] -> Seq1;
Pattern2 [when Guard2] -> Seq2;
...
PatternN [when GuardN] -> SeqN;
end
if
Guard1 -> Seq1;
Guard2 -> Seq2;
...
GuardN -> SeqN;
end
Código 2.5.1: Pattern Matching
A cláusula case avalia Expr e casa com o resultado dos padrões. A seqüência correspondente ao primeiro padrão é escolhido. Pattern pode ser qualquer estrutura de dados,
com variáveis. Variáveis anônimas “ ” podem ser usadas para os casos que não casar com
nenhum padrão. Guard são usados para estender o padrão de correspondência, e consistem
de mais testes especiais sobre a variável no padrão.
2.6
O projeto Jaraki
Jaraki é o projeto de um compilador, que tem como linguagem fonte a linguagem
de programação Java e como linguagem alvo a linguagem Erlang. Faz uso da máquina
virtual do Erlang para gerar as análises léxica, sintática, semântica e geração de código.
Para a análise léxica e sintática utiliza-se das ferramentas Leex e Yecc que são geradores
automáticos do analisador léxico e sintático para Erlang, respectivamente.
O projeto Jaraki
17
Figura 2.4: Compilador Jaraki
O projeto preocupa-se em gerar um código em Erlang que corresponda ao código fonte
Java, ao qual será subimetido ao compilador Jaraki. Esse código gerado, ao ser executado,
deve obter uma saı́da idêntica a do código em Java. Porém, não seria possı́vel implementar
as estruturas de controle sem, por exemplo, fazer o tratamento das variáveis ou tratamentos
de erros. Entretando, Java é uma linguagem com muitos recursos, dessa forma o projeto
foi dividido em partes, que são:
• Tratamentos de variáveis e Erros;
• Estruturas de Controle;
• Pacotes e Subprogramas;
• Orientação a objetos.
Então para o desenvolvimento deste trabalho, são utilizados o suporte a outros recursos
da linguagem Java, que não são demonstrados nessa implementação, pois foram desenvolvidos em outros trabalhos.
Capı́tulo 3
Desenvolvimento
Este capı́tulo apresenta o desenvolvimento do projeto Jaraki, descrevendo a implementação
das fases do compilador para cada estrutura de controle da linguagem Java.
3.1
Estrutura Geral do Compilador
Para o desenvolvimento de cada estrutura de controle foram criados alguns exemplos
de código-fonte em Java, partindo de exemplos mais simples a exemplos mais complexos,
contendo a estrutura de controle a ser desenvolvida. E a partir desse código-fonte, foram
geradas as análises léxica, sintática, semântica e geração de código, suficientes para que
este programa gere a mesma saı́da em Erlang. O objetivo, ao final da implementação de
todos os exemplos gerados contendo cada estrutura, é compreender os principais recursos
dessas estruturas para desenvolver um código-fonte em Java.
Para a implementação do compilador, utilizou-se módulos do Erlang. Para fazer o
encapsulamento das funções de compilação, por exemplo, foi criado o módulo jaraki.erl.
Esse módulo chama o analisador léxico, repassando os tokens para o analisador sintático,
extrai a árvore sintática e informações da estrutura da classe, as repassa para o analisador
semântico e por fim cria arquivos em Erlang resultantes da compilação. Além disso, para a
implementação de algumas estruturas de controle, como é o caso das estruturas de repetição
que não são implementadas em Erlang, foram criados alguns módulos, que são bibliotecas,
para facilitar o tratamento desses comandos durante a execução dos arquivos gerados em
Erlang. A figura 3.1 mostra a estrutura geral do compilador Jaraki.
Geradores dos analisadores
19
Figura 3.1: Fases de Compilação do Compilador Jaraki
3.2
Geradores dos analisadores
Para o desenvolvimento das análises léxica e sintática, utilizou-se as ferramentas Leex e
Yecc, que são geradores dos analisadores léxico e sintático, respectivamente, da linguagem
Erlang.
3.2.1
Leex
Similar ao lex e flex, o Leex lê uma entrada de dado e a converte para um fluxo de
tokens que será analisado no parser. O arquivo que compõe esse analisador léxico tem uma
extensão .xrl e é composto por três partes:
• Definitions: representação dos tokens da linguagem.
• Rules: regras que formam os tokens com base nos Definitions.
• Erlang Code: são funções auxiliares em Erlang utilizadas para a formação dos tokens.
3.2.2
Yecc
Similar ao Yacc, o Yecc implementa uma gramática LALR-1 (lookahead LR) e gera
a árvore sintática do pragrama de entrada com base na tabela de tokens repassada pelo
Estruturas de Repetição
20
analisador léxico. O arquivo que compõe esse analisador sintático tem uma extensão .yrl e
é composto por quatro partes:
• Nonterminals: nomes das BNF.
• Terminals: são os tokens vindos do lexer.
• Rootsymbols: regras inicias na BNF.
• Erlang Code: são funções auxiliares em Erlang para geração do parser.
3.3
Estruturas de Repetição
Como mencionado no capı́tulo 2, as estrutura de controle são usadas para executar
repetidas instruções até que a condição de continuação seja verdadeira.
Para o compilador Jaraki, foram implementadas as seguintes instruções de repetição da
linguagem Java: while, do...while e for.
3.3.1
Estrutura While
No comando while uma instrução é executada até que a condição de parada seja verdadeira.
Para a implementação do while considere o exemplo do código 3.3.1 em Java para
desenvolvimento das fases do compilador.
1
public class TestandoWhile{
2
public static void main(String[] Args){
3
4
int i = 1;
5
6
while(i < 20){
7
8
System.out.println("Estou dentro do while :D");
9
10
System.out.println(i);
11
12
i++;
13
}
14
}
15
16
}
Código 3.3.1: Código Java - While
Estruturas de Repetição
21
Para o exemplo do código 3.3.1 tem-se os tokens e palavras reservadas da linguagem,
como mostra as tabelas 3.1 e 3.2.
Definição
Lexema
Identifier
[a-zA-Z ][a-zA-Z0-9 ]*
OpenParentheses
(
CloseParentheses
)
OpenBrackets
[
CloseBrackets
]
Digit
[0-9]
ComparatorOp
(<|<=|==|>=|>|!=)
AddOp
(+|-)
Tabela 3.1: Tabela de Tokens
Palavras reservadas
public
class
static
void
String
while
int
System.out.println
Tabela 3.2: Palavras reservadas da linguagem - While
A lista dos tokens, gerada pelo compilador Jaraki, para esse exemplo é mostrada no
código 3.3.2.
No código 3.3.3, é mostrado a regra EBNF que define a estrutura do while.
Onde <statement> é regra que gera qualquer comando ou blocos de comando da linguagem Java, <no_short_if_stmt> são blocos de comando que não precisam do uso de
chaves e <bool_expr> é uma expressão que define a condição de parada. Com essas regras
o compilador consegue reconhecer não só a estrutura do while do exemplo do código 3.3.1,
mas como exemplos usando outros comandos Java, no corpo do while.
Estruturas de Repetição
22
[{public,1,public},
{class,1,class},
{identifier,1,’TestandoWhile’},
{’{’,1},
{public,3,public},
{static,3,static},
{void,3,void},
{identifier,3,main},
{’(’,3},
{string_t,3,’String’},
{’[’,3},
{’]’,3},
{identifier,3,’Args’},
{’)’,3},
{’{’,3},
{int_t,5,int},
{identifier,5,i},
{’=’,5},
{integer,5,1},
{’;’,5},
{while,7,while},
{’(’,7},
{identifier,7,i},
{comparation_op,7,’<’},
{integer,7,20},
{’)’,7},
{’{’,7},
{print,9,println},
{’(’,9},
{text,9,"Estou dentro do while :D"},
{’)’,9},
{’;’,9},
{print,11,println},
{’(’,11},
{identifier,11,i},
{’)’,11},
{’;’,11},
{identifier,13,i},
{increment_op,13,’++’},
{’;’,13},
{’}’,14},
{’}’,15},
{’}’,16}]
Código 3.3.2: Lista dos tokens gerados pelo Leex
No desenvolvimento da análise semântica foi utilizado a própria linguagem Erlang para
implementação. O esquema 3.2 mostra a sequência dos passos que foram seguidos para
gerar o elemento ast do Erlang (árvore sintática) para geração de código.
Inicialmente, a função match_statement() faz o casamento da estrutura do comando
repassado pelo analisador sintático. Essa função repassa os parâmetros necessários para a
criação do while em Erlang na função create_while(). Na implementação dessa função
foram considerados dois itens principais: a condição de parada e os comandos que com-
Estruturas de Repetição
23
<statement> ::= <method_invocation> | <for_stmt> | <while_stmt> |
<if_stmt> | <if_else_stmt> | <no_trailing_stmt>
<no_short_if_stmt> ::= <for_no_trailing> | <while_no_trailing> |
<no_trailing_stmt> | <if_else_no_trailing>
<while_stmt> ::= "while" "(" <bool_expr> ")" <statement>
<while_no_trailing> ::= "while" "(" <bool_expr> ")" <no_short_if_stmt>
Código 3.3.3: Regras EBNF - While
Figura 3.2: Análise Semântica - While
põem o corpo do while, para isso foram criadas duas variáveis que recebem o retorno do
tratamento desses dois parâmetros, e no final, são repassadas como parâmetro na função
call() que gera a árvore sintática do Erlang, e é composta pela chamada do módulo criado,
o loop.erl, para auxiliar na execução das estruturas de repetição geradas.
3.3.2
Estrutura Do While
Assim como na instrução while, no comando do while uma instrução é executada até
que a condição de parada seja verdadeira, com a diferença que pelo menos uma execução do bloco de comandos é realizada. A implementação para essa estrutura de controle
segue a mesma linha de raciocı́nio daquela implementada para o comando while. Para o
desenvolvimento das fases do compilador considere o exemplo do código 3.3.4.
Estruturas de Repetição
1
2
3
24
public class DoWhile {
public static void main(String [] args){
int x = 10;
4
do{
System.out.print("value of x : " + x );
x++;
System.out.print("\n");
} while( x < 20 );
5
6
7
8
9
}
10
11
}
Código 3.3.4: Código Java - Do While
A cada exemplo de código criado em Java tem-se o reconhecimento de novos tokens,
umas vez que são usados novos comandos. Para o exemplo acima o único token diferente é
a palavra do, o restante das palavras já foram anteriormente reconhecidas, então não faz-se
necessário criar novas regras para fazer o reconhecimento desses tokens. Assim, na análise
léxica, para essa estrutura de controle, foi criada apenas uma definição e uma regra para
reconhecer a palavra do.
Diferente do comando while a instrução do while sempre vem entre chaves, então na
análise sintática foi criada apenas uma regra EBNF principal para definir a estrutura do
comando do...while, código 3.3.5.
<no_short_if_stmt> ::= <for_no_trailing> | <while_no_trailing> |
<no_trailing_stmt> | <if_else_no_trailing>
<do_while_stmt> ::= "do" <no_short_if_smt> "while" "(" <bool_expr> ")" ";"
Código 3.3.5: Regras EBNF - Do While
Em que <no_short_if_smt> gera qualquer comando ou bloco de comandos entre chaves, e <bool_expr> é a regra que define a condição de parada.
Para o desenvolvimento da análise semântica seguiu-se o mesmo padrão do while, porém,
nesse caso a condição de parada é testada após a execução do bloco de comandos, como
mostrado na figura 3.3.
Seguindo o fluxo do esquema, inicialmente a função match_statement() faz o casamento da estrutura do comando repassada pelo analisador sintático. Essa função repassa os parâmetros necessários para a criação do do while em Erlang na função create_do_while(). No desenvolvimento dessa função foram considerados duas variáveis
Estruturas de Repetição
25
Figura 3.3: Análise Semântica - Do While
principais: uma que recebe o retorno do tratamento dos comandos que compõem o corpo
do do while e outra para a condição de parada, que então são repassadas como parâmetro
na função call() que gera a árvore sintática do Erlang, e é composta pela chamada do
módulo criado, o loop.erl, para auxiliar na execução das estruturas de repetição geradas.
3.3.3
Estrutura For
O comando for permite expressar iterações combinando uma expressão de inicialização,
um teste de condição de parada e uma expressão de incremento, como mostra o exemplo
no código 3.3.6.
Na análise léxica, para o exemplo acima, faz-se necessário criar apenas mais uma definição e regra para fazer o conhecimento da palavra for, ainda não reconhecida nos casos
anteriores.
Para a análise sintática foram definidas uma regra para a expressão de inicialização,
uma para a condição de parada, uma para a expressão de incremento e uma regra para o
bloco de comando que compõem o for, como segue no código 3.3.7.
Essas regras definem a estrutura de comandos for para o exemplo do código 3.3.6, que
usa na expressão de inicialização uma variável do tipo int, sendo que a terceira regra é
definida para comandos entre chaves e a quarta para comandos sem o uso de chaves.
O desenvolvimento da análise semântica segue o mesmo pensamento das outras duas
Estruturas de Repetição
1
26
public class TestandoFor5 {
2
public static void main(String[] Args) {
3
4
int n = 5;
int x = 0;
5
6
7
for (int i=0; i <= n; i++) {
8
9
System.out.print("Estou contando e calculando dentro o for :D ");
10
11
System.out.println(i);
12
13
x = x + i;
14
15
System.out.println(x);
16
}
17
}
18
19
}
Código 3.3.6: Código Java - FOR
<statement> ::= <method_invocation> | <for_stmt> | <while_stmt> |
<if_stmt> | <if_else_stmt> | <no_trailing_stmt>
<no_short_if_stmt> ::= <for_no_trailing> | <while_no_trailing> |
<no_trailing_stmt> | <if_else_no_trailing>
<for_stmt> ::= "for" "(" "int_t" <identifier> "=" <bool_expr> ";" <bool_expr> ";"
<for_update> ")" <statement> | "for" "(" <array_acess> "="
<bool_expr> ";" <bool_expr> ";" <for_update> ")" <no_short_if_stmt>
<for_no_trailing> ::= "for" "(" "int_t" <identifier> "=" <bool_expr> ";"
<bool_expr> ";" <for_update> ")" <no_short_if_stmt> |
"for" "(" <array_acess> = <bool_expr> ";" <bool_expr> ";"
<for_update> ")" <statement>
Código 3.3.7: Regras EBNF - For
estruturas de controle de repetição vistas anteriormente, figura 3.4. Porém, no caso do for,
há mais dois parâmetros: a expressão de inicialização e a expressão de incremento.
A variável InitAst recebe o retorno do tratamento da expressão de inicialização, assim
como a variável IncAst recebe o retorno da expressão de incremento. Para o desenvolvimento do for em Erlang, são necessários passar como parâmetro para a função call(),
que faz a chamada da função for do módulo loop.erl, apenas as expressões de condição
de parada, incremento e os comandos que compõem o corpo do for, já que a expressão de
inicialização é executada apenas uma vez. Em seguida, a variável ForAst recebe o retorno
da função call() para então ser gerada o elemento sintático em Erlang para geração de
Estruturas de Condição
27
Figura 3.4: Análise Semântica - FOR
código.
3.4
Estruturas de Condição
Nas estruturas de condição um comando ou um bloco de comandos é executado caso
a condição seja verdadeira. Para o compilador Jaraki foi implementado a estrutura de
condição if.
3.4.1
Estrutura IF
A instrução if realiza uma ação se essa for verdadeira ou pula essa mesma ação se a
condição for falsa e a instrução if...else realiza uma ação se a condição for verdadeira e
realiza uma diferente se a condição for falsa, como no exemplo do código 3.4.1.
No exemplo do código 3.4.1, é calculado a média de três notas, com o resultado da
média é verificado a primeira condição, caso essa não seja verdadeira o comando else é
executado.
Na análise léxica fez-se o reconhecimento de mais dois tokens, a palavra if e a palavra
else. No desenvolvimento da análise sintática é verificado o problema clássico da ambiguidade. Quando é usado a instrução if...else é necessário identificar a qual else corresponde
Estruturas de Condição
1
28
public class Media{
2
public static void main(String[] Args) {
3
4
float nota1, nota2, nota3, media;
nota1 = 6; nota2 = 6;
nota3 = 5;
5
6
7
media = (nota1 + nota2)/2;
8
9
if (media < 6) {
System.out.print("Reprovado com media: ");
System.out.println(media);
}else
System.out.print("Aprovado com media: ");
System.out.println(media);
}
10
11
12
13
14
15
16
}
17
18
}
Código 3.4.1: Código Java - If
um determinado if, quando há mais de um comando if. Para exemplificar esse problema,
considere a seguinte regra EBNF para gerar a estrutura if...else.
<smt> ::= "if" "(" <expr> ")" <stmt> | "if" "(" <expr> ")" <stmt> "else" <smt>
| <other_smt>
Com base na regra acima considere o exemplo da expressão a seguir:
if(E1) if(E2) S1 else S2
No exemplo, há dois comandos if seguidos de apenas um else. Nesse caso, é difı́cil
determinar, com apenas essas regras, a qual if pertence o comando else. Assim, quando a
arvóre sintática é gerada, pode-se percorrer dois caminhos, como é ilustrado na figura 3.5.
Observe que na primeira árvore o comando else corresponde ao primeiro comando if,
já na segunda árvore corresponde ao segundo if.
Para solucionar esse problema é necessário criar regras de forma que o else mais interno
corresponda ao if mais interno, bem como o else mais externo corresponda ao if mais
externo.
A partir das regras EBNF apresentadas no código 3.4.2 é possı́vel eliminar o problema
da ambiguidade.
Estruturas de Condição
29
Figura 3.5: Árvore Sintática - IF com ambiguidade
<statement> ::= <method_invocation> | <for_stmt> | <while_stmt> |
<if_stmt> | <if_else_stmt> | <no_trailing_stmt>
<no_short_if_stmt> ::= <for_no_trailing> | <while_no_trailing> |
<no_trailing_stmt> | <if_else_no_trailing>
<if_else_stmt> ::= "if" "(" <bool_expr> ")" <no_short_if_stmt> "else" <statement>
<if_else_no_trailing> ::= "if" "(" <bool_expr> ")" <no_short_if_stmt>
"else" <no_short_if_stmt>
Código 3.4.2: Regras EBNF - If
A terceira regra corresponde a uma expressão com if seguido ou não de else, a segunda
regra apenas comandos if seguido de else e a quarta regra faz o tratamento de comandos
if...else sem o uso de chaves. Dessa forma é possı́vel gerar apenas uma árvore sintática
para a gramática do exemplo anterior, como ilustrado na figura 3.6.
Figura 3.6: Árvore Sintática - IF sem ambiguidade
Erlang já implementa estruturas de controle de condição, então para o desenvolvimento
Estruturas de Condição
30
da análise semântica não fez-se necessário criar um módulo separado, como foi feito para as
estruturas de repetição. Para implementar a análise semântica foi usado o comando case
do Erlang. A estrutura geral do comando case é mostrada a seguir:
1
2
3
4
5
6
case Condicao of
true ->
[comandos]
false ->
[comandos]
end
Código 3.4.3: Estrutura geral do comando Case
Nesse comando a condição é separado em cláusulas. No caso do if sempre teremos uma
cláusula true e uma false. Quando houver um else para esse if a cláusula false será
executada, caso a condição seja falsa.
E, finalmente, na análise semântica, seguindo o esquema da figura 3.7, a função
match_statement() faz o casamento de expressões com if repassadas pelo analisador
sintático.
Na função create_if() é feito o tratamento da condição e das cláusulas
que irão compor o comando case em Erlang, através das funções match_attr_expr()
e match_inner_stmt(), respectivamente.
Figura 3.7: Análise Semântica - If
Ao final o compilador irá gerar o código correspondente ao exemplo do código 3.4.1 em
Erlang, como mostrado no código 3.4.4.
Estruturas de Sequência
1
2
31
-module(media).
-compile(export_all).
3
4
’__constructor__’() -> oo_lib:new(media, [], []).
5
6
7
8
main({{array, ’String’}, V_Args}) ->
Nota1 = 6, Nota2 = 6, Nota3 = 5,
Media = (6+6+5)/3,
9
case Media < 6 of
true -> io:format("~s", ["Reprovado direto com: "]),
io:format("~f~n", [Media]);
false -> io:format("~s", ["Aprovado direto com: "]),
io:format("~f~n", [Media]);
end.
10
11
12
13
14
15
Código 3.4.4: Código gerado em Erlang - If
3.5
Estruturas de Sequência
Nas estruturas de sequência as instruções são executadas uma após a outra, na ordem
que em que são escritas. Para o compilador Jaraki foram implementadas os comandos System.out.print e System.out.println, as classes Scanner e Random, e o suporte a Arquivos.
3.5.1
Estrutura System.out.print e System.out.println
As instruções de print em Java são realizadas pelos comandos System.out.print e System.out.println. A diferença entre os dois comandos é que o segundo pula uma linha ao
fazer a impressão. O exemplo do código 3.5.1 ilustra o uso desse comando.
1
public class Print {
2
public static void main(String[] args) {
int grausFarenheit = 57;
int grausCelsius;
3
4
5
6
grausCelsius = (5 * (grausFarenheit-32) / 9);
7
8
System.out.println(grausFarenheit + " graus Farenheit corresponde a "
+ grausCelsius + " graus Celsius");
9
10
}
11
12
}
Código 3.5.1: Código Java - Print
Para o desenvolvimento dessa estrutura de sequência mais dois tokens devem ser reconhecidos, as palavras System.out.print e System.out.println.
Estruturas de Sequência
32
Através desse comando Java é possı́vel fazer a impressão tanto de textos como de variáveis, vetores e matrizes ou a combinação de todos eles. Dessa forma, foram consideradas
regras EBNF na análise sintática, descritas no código 3.5.2.
<print_stmt> ::= "print" "(" <print_content> ")" ;
<print_content> ::= <text> | <identifier> | <identifier> "." <identifier> |
<identifier> "." <identifier> <add_op> <print_content> |
<text> <add_op> <print_content> |
<identifier> <add_op> <print_content> | <array_access> |
<array_access> <add_op> <print_content>
Código 3.5.2: Regras EBNF - Print
O comando utilizado para impressão em Erlang foi o comando io:format(). Esse
comando é composto de dois parâmetros, sendo o primeiro o formato do dado que será
recebido (int, string, float), e o segundo o dado propriamente dito. O código 3.5.3 ilustra
como fica o exemplo do código 3.5.1 em Erlang.
A figura 3.8 apresenta o fluxo de desenvolvimento considerado para a implementação
da análise semântica.
Figura 3.8: Semântica do Print
Mais uma vez a função match_statement() faz o casamento da estrutura repassada
pela análise sintática. Para a função create_print_function() são passados dois parâmetros principais, um identificando se a estrutura repassada pelo parser trata-se de um
Estruturas de Sequência
1
33
-module(testeprint).
2
3
-compile(export_all).
4
5
’__constructor__’() -> oo_lib:new(testeprint, [], []).
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
main({{array, ’String’}, V_args}) ->
st:new(),
st:get_new_stack({’TestePrint’,
{main, [{array, ’String’}]}}),
st:put_value({{’TestePrint’,
{main, [{array, ’String’}]}},
"args"},
{{array, ’String’}, V_args}),
begin
st:put_value({{’TestePrint’,
{main, [{array, ’String’}]}},
"grausFarenheit"},
{int, trunc(57)})
end,
st:put_value({{’TestePrint’,
{main, [{array, ’String’}]}},
"grausCelsius"},
{int,trunc(5 *
((st:get_value({’TestePrint’,
{main, [{array, ’String’}]}},
"grausFarenheit")-32) / 9))}),
io:format("~p~s~p~s~n",
[st:get_value({’TestePrint’,
{main, [{array, ’String’}]}},
"grausFarenheit"),
" graus Farenheit corresponde a ",
st:get_value({’TestePrint’,
{main, [{array, ’String’}]}},
"grausCelsius")," graus Celsius"]),
st:destroy().
37
Código 3.5.3: Código gerado em Erlang - Print
System.out.print ou System.outprintln e outro com o conteúdo que será impresso. O tratamento dos dois comandos é feito da mesma forma, a única diferença é que o segundo
é acrescido de um “\n” para pular linha. Nessa função é feito o tratamento do conteúdo
de impressão, levando em consideração se esse é um texto ou uma variável, vetor ou matriz. A variável Print_text recebe o retorno de todos os textos encontrados e a variável
Print_content lista todas as variáveis, vetores ou matrizes. Ao final, a função rcall()
gera o elemento sintático para geração de código em Erlang.
Estruturas de Sequência
3.5.2
34
Estrutura Scanner
Scanner é uma classe da linguagem Java que permite ler entradas de dados pelo teclado
no console. Para o compilador Jaraki foram implementados três tipos de dados para essa
classe: int, float e string. O exemplo do código 3.5.4 utiliza o tipo int como entrada.
1
import java.util.Scanner;
2
3
4
public class Scanner {
public static void main(String[] args){
5
int num;
Scanner input = new Scanner(System.in);
6
7
8
System.out.println("Informe um numero");
num = input.nextInt();
9
10
11
System.out.println("O numero informado foi: " + num);
12
}
13
14
}
Código 3.5.4: Código Java - Scanner
Além das palavras reservadas que aparecem no exemplo do código 3.5.4, outras palavras
são usadas na classe Scanner, então, na análise léxica é feito o reconhecimento das palavras
reservadas que seguem na tabela 3.3.
Palavras reservadas
Scanner
new
System.in
nextInt
nextFloat
nextLine
Tabela 3.3: Palavras reservadas da linguagem - Scanner
Como toda classe em Java, para usar o Scanner é necessário fazer a instanciação de
um objeto para então usar os atributos da classe. Apesar dessa instanciação ser ignorada
na análise semântica, já que em Erlang não há suporte a orientação a objetos, é necessário
fazer o reconhecimento dessa estrutura na análise sintática.
As regras EBNF para essa estrutura de sequência são mostradas no código 3.5.5.
Estruturas de Sequência
1
35
<no_trailing_stmt> ::= <local_variable_declaration_statement>
2
3
<local_variable_declaration_statement> ::= "scanner" <variable_list> ;
4
5
<new_stmt> ::= "new" "scanner" "(" ")" | "new" "scanner" "(" "system_in" ")"
6
7
<next_int_stmt> ::= <identifier> "." "next_int" "(" ")"
8
9
<next_float_stmt> ::= <identifier> "." "next_float" "(" ")"
10
11
<next_line_stmt> ::= <identifier> "." "next_line" "(" ")"
Código 3.5.5: Regra EBNF - Scanner
As regras <local_variable_declaration_statement> e <new_stmt> correspondem
a instanciação da classe e as regras seguintes reconhecem as estruturas que fazem a leitura
no console, uma para cada tipo de dado.
Em Erlang, existem mais de um comando para entrada de dados. Para o desenvolvimento da análise semântica foi escolhido o comando io:fread(Prompt, Format). Esse
comando é composto por dois parâmetros, sendo o primeiro uma identificação de prompt,
que como não é determinado em Java, no compilador foi definido o caractere “>” e o
segundo parâmetro identifica o tipo do dado de entrada.
Após passar pelo processo de compilação, o exemplo do código 3.5.4 fica como mostrado
no código 3.5.6.
1
-module(scanner).
2
3
-compile(export_all).
4
5
’__constructor__’() -> oo_lib:new(scanner, [], []).
6
7
main({{array, ’String’}, V_args}) ->
8
9
10
11
io:format("~s~n", ["Informe um numero"]),
Num = trunc(io:fread(">", "~d")),
io:format("~s~p~n", ["O numero informado foi: ", Num]).
12
Código 3.5.6: Código gerado em Erlang - Scanner
Como foi dito anteriormente, a instanciação do objeto da classe Scanner é ignorado na
semântica, por isso não aparece no código final.
O esquema da figura 3.9 ilustra os passos seguidos para o desenvolvimento da análise
semântica. A função match_statement() casa expressões vindas do parser. A função
Estruturas de Sequência
36
create_function_object_class() é usada no desenvolvimento de outras classes, então
é passado como parâmetro o nome classe, além do tipo de dado que está sendo lido. No
final, a função rcall() cria o elemento sintático para geração de código em Erlang.
Figura 3.9: Semântica do Scanner
3.5.3
Estrutura Random
Random é uma classe da linguagem Java utilizada para a geração de números aleatórios.
Permite gerar números inteiros, reais e float randomicamente. O código 3.5.7 exemplica o
uso dessa classe.
1
import java.util.Random;
2
3
4
public class Random1 {
public static void main(String[] Args){
5
int randomInt = 6;
6
7
System.out.println("Generating 10 random integers in range 0..99.");
8
9
Random randomGenerator = new Random();
10
11
for (int i = 1; i <= 10; i++){
randomInt = randomGenerator.nextInt(100);
System.out.println("Generated : " + randomInt);
}
12
13
14
15
}
16
17
}
Código 3.5.7: Código Java - Random
No exemplo, há apenas mais uma palavra reservada que deve ser reconhecida na análise
léxica, a palavra Random, o restante já foram reconhecidas nos casos anteriores.
Estruturas de Sequência
37
Observe que a estrutura sintática dessa classe é muito parecida com a classe Scanner,
de modo que as regras EBNF seguem o mesmo padrão na análise sintática.
1
<no_trailing_stmt> ::= <local_variable_declaration_statement>
2
3
<local_variable_declaration_statement> ::= "random" <variable_list> ;
4
5
<new_stmt> ::= "new" "random" "(" ")"
6
7
8
<next_int_stmt> ::= <identifier> "." "next_int" "(" "integer" ")"|
<identifier> "." "next_int" "(" ")"
9
10
<next_float_stmt> ::= <identifier> "." "next_float" "(" ")"
Código 3.5.8: Regra EBNF - Random
As duas primeiras regras correspondem a instanciação da classe, e as regras que seguem
são para a geração dos números aleatórios, duas para números inteiros e outra para float.
Em Erlang, já existe um módulo para geração de números aleatórios, a lib random.
Dentro dessa biblioteca existem algumas funções usadas para esse fim. Para o desenvolvimento da análise semântica foi utilizada a função uniforme(), que gera tanto número
inteiro como float.
Figura 3.10: Semântica do Random
Assim como no desenvolvimento das estruturas de repetição, para a implementação da
classe Random também foi criado um módulo para auxiliar no desenvolvimento da análise
semântica, o random_lib.erl (código no apêndice).
A figura 3.10 mostra os passos seguidos para o desenvolvimentos da análise semântica.
Como pode ser observado, segue o mesmo esquema usado na classe Scanner, a diferença
Estruturas de Sequência
38
é que o elemento sintático do Erlang não é gerado para chamar diretamente a função
random:uniform(), neste caso, em particular, primeiramente é feita uma chamada ao
módulo random_lib.erl que contém essa função. O código 3.5.9 apresenta o código gerado
para essa estrutura.
1
-module(random).
2
3
-compile(export_all).
4
5
-import(random_lib, [function_random/2]).
6
7
’__constructor__’() -> oo_lib:new(random1, [], []).
8
9
main({{array, ’String’}, V_Args}) ->
10
RandomInt = trunc(6),
io:format("~s~n", ["Generating 10 random integers in range 0..99."]),
11
12
13
begin
I = 1,
for(fun () -> I =< 10 end,
14
15
16
17
fun () -> I = I + 1 end,
fun () ->
RandomInt = trunc(function_random(next_int, 100)),
io:format("~s~p~n", ["Generated : ", RandomInt]) end,
18
19
20
21
end,
io:format("~s~n", ["Done."]).
22
23
24
Código 3.5.9: Código gerado em Erlang - Random
3.5.4
Estrutura Arquivos
Não diferente da maioria dos recursos, para lidar com entrada e saı́da de dados (input/output) a linguagem Java utiliza classes e instâncias delas (objetos) para tratar de
leitura e gravação de arquivos. A linguagem Java dispõe de várias classes para manipular arquivos. Para a implementação do Compilador Jaraki foram desenvolvidas as classes
FileReader e FileWriter. Um código usando essas classes é mostrado no exemplo 3.5.10.
No exemplo, é lido apenas um caractere do arquivo de entrada e escrito no arquivo de
saı́da.
No desenvolvimento da análise léxica foram reconhecidas as palavras reservadas que
seguem na tabela 3.4, e na análise sintática, para o reconhecimento das estruturas usadas
no exemplo acima, foram criadas as regras EBNF descritas no código 3.5.11.
Estruturas de Sequência
1
2
3
39
import java.io.FileWriter;
import java.io.FileReader;
import java.io.IOException;
4
5
6
public class Arquivos {
public static void main(String[] args) throws IOException{
7
int caracter;
8
9
FileReader arq = new FileReader("/home/pec/entrada.txt");
FileWriter writer = new FileWriter("/home/pec/saida.txt", true);
10
11
12
caracter = arq.read();
writer.write(caracter);
13
14
15
arq.close();
writer.close();
16
17
}
18
19
}
Código 3.5.10: Código Java - Arquivos
Palavras reservadas
FileReader
FileWriter
throws
IOException
read
write
close
Tabela 3.4: Palavras reservadas da linguagem - Arquivos
1
2
<local_variable_declaration_statement> ::= file_reader <variable_list> ";" |
file_writer <variable_list> ";"
3
4
5
<new_stmt> ::= "new" "file_reader" "(" <text> ")" |
"new" "file_writer" "(" <text> "," "true" ")"
6
7
<read_stmt> ::= <identifier> "." "read" "(" ")" ";"
8
9
10
<write_stmt> ::= <identifier> "." "write" "(" <text> ")" ";" |
<identifier> "." "write" "(" <identifier> ")" ";"
11
12
<close_stmt> -> identifier "." "close" "(" ")" ";"
Código 3.5.11: Regra EBNF - FileReader e FileWriter
Estruturas de Sequência
40
Diferente da implementação das classes Random e Scanner, a instanciação das classes
FileReader e FileWriter não pode ser ignorada na análise semântica, uma vez que, para
gerar o mesmo código em Erlang, será necessário saber o caminho do arquivo de leitura e
escrita dos dados. Além disso, foi criado o módulo file_lib.erl para auxiliar na geração
de código.
Figura 3.11: Semântica - Instanciação das classes FileReader e FileWriter
Figura 3.12: Semântica - Função read()
Em Erlang, a instanciação para essas classes equivale a função open() do módulo file
do Erlang. Para implementar esse trecho do código, a figura 3.11 ilustra as funções usadas
na análise semântica. A função create_declaration() faz o casamento da expressão de
instanciação vinda do parser, que chama a função create_declaration_list(). Essa função lista todas as declarações de variáveis que existem no código e a instanciação de classes é
tratada, no compilador, como uma declaração. Depois, na função create_attribution(),
Estruturas de Sequência
41
é feito o tratamento da estrutura com o auxilio das função call() e rcall(), a primeira
cria o elemento sintático para a chamada da função do módulo file_lib.erl e a segunda
cria o elemento sintático em Erlang para gerar a estrutura de atribuição.
Figura 3.13: Semântica - Funções write() e close()
A figura 3.12 mostra o desenvolvimento para o comando que usa a função read() e a
figura 3.13 para os comandos que usam as funções write() e close(). O tratamento de
ambos é bem parecido, a diferença entre a primeira e a segunda figura, é que a primeira
faz o tratamento de um comando com atribuição. No módulo file do Erlang as funções
read(), write_file() e close() tem as mesmas caracterı́sticas dessas funções em Java,
respectivamente. O desenvolvimento dessas estruturas segue a mesma idéia apresentada
na implementação das funções das classes Random e Scanner, como mostra a figura 3.13.
Capı́tulo 4
Testes
Neste capı́tulo são apresentados os testes realizados para cada estrutura de controle implementada para o compilador Jaraki, a árvore sintática gerada a partir de cada código-fonte
Java, a árvore sintática para geração do código final em Erlang e os tempos de compilação
e execução nas duas linguagens.
4.1
Processo de Compilação
Os testes realizados durante o processo de desenvolvimento das estruturas de controle
vão de testes envolvendo estruturas básicas, à testes mais completos, abrangendo uma
maior variação do uso de cada estrutura.
Foram utilizadas nos testes as versões de sofware 1.6.0 24 do Java e 5.8.4 do Erlang.
Utilizou-se um processor Intel Core i5 com 2.53GHz de processamento, 4GB de memória
e sistema operacional de 64Bits. Tanto nos testes da linguagem Java como em Erlang,
os tempos de compilação e execução foram medidos usando o comando time, e para cada
linguagem foi considerado o tempo real.
Para exemplificar o processo de compilação utilizada no compilador Jaraki, considere o
código 4.1.1. Nesse exemplo temos apenas a impressão de um texto simples com o comando
System.out.println.
4.1.1
Árvore sintática para a linguagem Java - JAST
Os testes realizados consistem em gerar a árvore sintática do código-fonte Java, aqui
chamada de JAST, gerada com a ferramenta Yecc para o Compilador Jaraki. A árvore
Processo de Compilação
1
43
public class HelloWorld {
2
public static void main(String[] args){
3
4
System.out.println("Hello world!");
5
6
}
7
8
}
Código 4.1.1: Código Java - Hello World
sintática do exemplo do código 4.1.1 é apresentada no código 4.1.2. A JAST foi construı́da
a partir das regras implementadas no parser, como apresentado no capı́tulo de desenvolvimento.
1
2
3
4
5
6
7
8
9
10
11
12
13
[{class,{1,
{name,’HelloWorld’},
{parent,null},
{body,[{method,{3,
{return,{3,void}},
{name,main},
{modifiers,[public,static]},
[{3,
{var_type,{3,{array,’String’}}},
{parameter,args}}],
{block,3,
[{5,println,
[{text,5,"Hello world!"}]}]}}}]}}}]
14
15
Código 4.1.2: JAST - Hello World
Como pode ser observado, a árvore segue o padrão utilizado para a implementação da
árvore abstrata do Erlang, e é estruturada em tuplas ou listas de tuplas que representam
cada bloco ou trecho do código. A tupla referente ao comando em questão começa na linha
11 do código, {block,3,[{5,println,[{text,5,"Hello world!"}]}]}. O atom block
indica o inı́cio de um bloco de comandos, que nesse caso é composto por apenas um
comando de print, logo em seguida o número 3 identifica a linha que esse bloco foi iniciado,
depois tem-se a lista de tuplas identificando cada comando. Independente do comando,
essa tupla sempre será iniciada pelo número da linha e nome referentes a esse comando. A
instrução System.out.println foi identificada pelo atom println seguido por uma lista
de tuplas que compõem o conteúdo que será impresso, que neste caso é apenas um texto
identificado pelo atom text.
Processo de Compilação
4.1.2
44
Árvore abstrata do Erlang - AST
A próxima fase do processo de compilação, implementada na análise semântica, é a
geração da árvore abstrata do Erlang. É a partir dessa árvore que o código Erlang correspondente ao programa Java, é construı́do. A AST do exemplo do código 4.1.1 pode ser
visualizada no código 4.1.3.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[{attribute,1,file,{"helloworld.erl",1}},
{attribute,1,module,helloworld},
{attribute,3,compile,export_all},
{function,15,’__constructor__’,0,
[{clause,15,[],[],
[{call,15,
{remote,15,{atom,15,oo_lib},{atom,15,new}},
[{atom,15,helloworld},{nil,15},{nil,15}]}]}]},
{function,17,main,1,
[{clause,17,
[{tuple,17,
[{tuple,17,[{atom,17,array},{atom,17,’String’}]},
{var,17,’V_args’}]}],
[],
[{call,18,{remote,18,{atom,18,st},{atom,18,new}},[]},
{call,19,
{remote,19,{atom,19,st},{atom,19,get_new_stack}},
[{tuple,19,
[{atom,19,’HelloWorld’},
{tuple,20,
[{atom,20,main},
{cons,20,
{tuple,20,[{atom,20,array},{atom,20,’String’}]},
{nil,20}}]}]}]},
{call,21,
{remote,21,{atom,21,st},{atom,21,put_value}},
[{tuple,21,
[{tuple,21,
[{atom,21,’HelloWorld’},
{tuple,22,
[{atom,22,main},
{cons,22,
{tuple,22,
[{atom,22,array},{atom,22,’String’}]},
{nil,22}}]}]},
{string,23,"args"}]},
{tuple,24,
[{tuple,24,[{atom,24,array},{atom,24,’String’}]},
{var,24,’V_args’}]}]},
{call,25,
{remote,25,{atom,25,io},{atom,25,format}},
[{string,25,"~s~n"},
{cons,25,{string,25,"Hello world!"},{nil,25}}]},
{call,26,{remote,26,{atom,26,st},{atom,26,destroy}},[]}]}]},
{eof,28}]
Código 4.1.3: AST - Hello World
Teste 1 - Print
45
Nesse código é apresentado o tratamento de outros escopos, como o tratamento de
variáveis, não apresentados neste trabalho. A tupla referente ao comando de print inicia
na linha 40, com a chamada ao comando do Erlang de I/O para impressão, io:format(),
{call,25,{remote,25,{atom,25,io,{atom,25,format}}}. As linhas seguintes, 42 e 43,
contém o conteúdo a ser impresso, com o devido tratamento.
Após essas e as demais construções da AST, o código 4.1.2 da linguagem Java tem um
código correspondente na linguagem Erlang como mostra o código 4.1.4
1
-module(helloworld).
2
3
-compile(export_all).
4
5
’__constructor__’() -> oo_lib:new(helloworld, [], []).
6
7
8
9
10
11
12
13
14
15
16
main({{array, ’String’}, V_args}) ->
st:new(),
st:get_new_stack({’HelloWorld’,
{main, [{array, ’String’}]}}),
st:put_value({{’HelloWorld’,
{main, [{array, ’String’}]}},
"args"},
{{array, ’String’}, V_args}),
io:format("~s~n", ["Hello world!"]),
st:destroy().
17
Código 4.1.4: Código Erlang - Hello World
Todos os arquivos em Erlang gerados nos testes a seguir, se encontram no apêndice
deste trabalho.
4.2
Teste 1 - Print
Considere o exemplo do código 4.2.1 como entrada para o Compilador Jaraki, para
testar o comando de print do Java.
O programa usa uma instrução System.out.println que imprime textos e variáveis.
Primeiramente, o código foi compilado em Java com o comando time javac e executado
com time java como mostrado nas figuras 4.1 e 4.2
Na figura 4.1 é mostrado o tempo de compilação e na figura 4.2 o tempo de execução
do código Java juntamente com a sua saı́da. É considerado como referência o tempo real.
Neste caso o tempo de compilação e execução foram de 0.632s e 0.108s, respectivamente.
Teste 1 - Print
1
2
3
4
5
46
public class Print {
public static void main(String[] args){
int num1 = 5;
int num2 = 8;
int soma;
6
soma = num1 + num2;
System.out.println("A soma de " + num1 + " + "
+ num2 + " é igual a " + soma);
7
8
9
}
10
11
}
Código 4.2.1: Código Java - Print
Figura 4.1: Tempo de compilação Print - Java
Figura 4.2: Tempo de execução Print - Java
São apresentado nas figuras 4.3 e 4.4 os tempos de compilação e execução em Erlang,
respectivamente. O comando ./jkc Print.java compila o arquivo que é submetido ao
compilador Jaraki e o comando ./jk print.beam executa o arquivo que é gerado em
Erlang. Os tempos foram 0.265s de compilação e 0.525s de execução. Como pode ser
visualizado, o tempo de compilação em Erlang é menor, porém o tempo de execução é
maior. Isso deve-se ao fato de Erlang ter uma abordagem diferente do Java no tratamento
de variáveis.
Figura 4.3: Tempo de compilação Print - Erlang
Outro ponto importante que deve ser observado é a saı́da que é gerada nos dois arquivos,
Teste 2 - Scanner
47
Figura 4.4: Tempo de execução Print - Erlang
que devem ser as mesmas. Tanto em Java como em Erlang é observado a mesma saı́da na
execução dos dois programas: A soma de 5 + 8 é igual e 13.
4.3
Teste 2 - Scanner
Para o teste usando a classe Scanner, considere o exemplo do código 4.3.1. O código
ler no console um número inteiro e exibe para o usuário o número que foi informado por
ele.
1
import java.util.Scanner;
2
3
4
public class Scanner {
public static void main(String[] args){
5
int num;
Scanner input = new Scanner(System.in);
6
7
8
System.out.println("Informe um numero");
num = input.nextInt();
9
10
11
System.out.println("O numero informado foi: " + num);
12
}
13
14
}
Código 4.3.1: Código Java - Scanner
Os tempos de compilação e execução desse programa, 0.783 e 1.174s, respectivamente,
juntamente com a saı́da , são mostrado nas figuras 4.5 e 4.6
Figura 4.5: Tempo de compilação Scanner - Java
Teste 3 - Random
48
Figura 4.6: Tempo de execução Scanner - Java
Em seguida, na figura 4.7, é mostrado o tempo de compilação do arquivo gerado em
Erlang, 0.264s, e na figura 4.8 a saı́da e o tempo de execução de 1.088s.
Figura 4.7: Tempo de compilação Scanner - Erlang
Figura 4.8: Tempo de execução Scanner - Erlang
Como pode ser visualizado na comparação dos testes, os tempos em Erlang foram
menores, porém no tempo de execução deve ser levado em consideração o tempo de resposta
do usuário, já que se trata de um teste com entrada de dado.
4.4
Teste 3 - Random
O código 4.4.1 mostra um exemplo que usa a classe Random na linguagem Java que
servirá como teste nessa seção. No programa, são gerados 10 números randomicamente, de
0 a 99.
O programa foi compilado e executado em Java. A saı́da e os tempos de teste são
mostrados na figura 4.9 e 4.10. Os tempos obtidos foram 0.702s de compilação e 0.069s
de execução.
Teste 3 - Random
1
49
import java.util.Random;
2
3
public class TesteRandom {
4
public static void main(String[] Args){
5
6
int randomInt;
randomInt = 6;
7
8
9
System.out.println("Gerando números inteiros aleatórios 0..99.");
10
11
Random randomGenerator = new Random();
for (int idx = 1; idx <= 10; idx++){
randomInt = randomGenerator.nextInt(100);
System.out.println("Gerado : " + randomInt);
}
12
13
14
15
16
17
System.out.println("Feito.");
18
}
19
20
21
}
22
Código 4.4.1: Código Java - Random
Figura 4.9: Tempo de compilação Random - Java
Figura 4.10: Tempo de execução Random - Java
O código-fonte foi submetido ao Compilador Jaraki com tempo de compilação de
0.496s. O arquivo gerado em Erlang foi executado com tempo de 0.172s e saı́da idêntica a
saı́da gerada em Java, como é visualizado nas figuras 4.11 e 4.12. Como pode ser observado
Teste 4 - Arquivos
50
o tempo de compilação em Erlang foi menor, porém o de execução foi maior para o mesmo
código em Java.
Figura 4.11: Tempo de compilação Random - Erlang
Figura 4.12: Tempo de execução Random - Erlang
4.5
Teste 4 - Arquivos
Como exemplo de suporte a arquivos, considere o código do exemplo 4.5.1, que utiliza
as classe de leitura, FileReader, e escrita, FileWriter, da linguagem Java. O código ler um
caractere de um arquivo de entrada e escreve em um arquivo de saı́da.
Em Java o tempo de compilação foi de 0.893s e 0.067s de execução, como ilustra a
figura 4.13
Os tempos de compilação e execução em Erlang, foram de 0.267s e 0.172s. Para
esse teste, o tempo de compilação em Java foi maior que em Erlang, porém o tempo de
execução, na linguagem Java, foi menor, figura 4.14.
Teste 5 - IF
1
2
3
51
import java.io.FileWriter;
import java.io.FileReader;
import java.io.IOException;
4
5
6
public class Arquivos {
public static void main(String[] args) throws IOException{
7
int caracter;
8
9
FileReader arq = new FileReader("/home/pec/entrada.txt");
FileWriter writer = new FileWriter("/home/pec/saida.txt", true);
10
11
12
caracter = arq.read();
writer.write(caracter);
13
14
15
arq.close();
writer.close();
16
17
}
18
19
}
Código 4.5.1: Código Java - Arquivos
Figura 4.13: Tempo de compilação e execução Arquivos - Java
Figura 4.14: Tempo de compilação e execução Arquivos - Erlang
4.6
Teste 5 - IF
Para o teste da estrutura de condição if, foi considerado o código 4.6.1. O programa ler
duas entradas no console e verifica se a aluno foi Aprovado ou Reprovado.
As figuras 4.15 e 4.16 mostram, respectivamente, os tempos de compilação e execução
desse código em Java, 0.715s e 3.614s, respectivamente.
Teste 5 - IF
1
52
import java.util.Scanner;
2
3
public class If {
4
public static void main(String[] args){
5
6
float nota1, nota2, media;
7
8
Scanner input = new Scanner(System.in);
9
10
System.out.println("Informe as notas parciais");
nota1 = input.nextFloat();
nota2 = input.nextFloat();
11
12
13
14
media = (nota1 + nota2)/2;
15
16
if(media >= 7 && media < 10)
System.out.println("Aprovado");
else if(media < 7)
System.out.println("Reprovado");
else
System.out.println("Aprovado com distinç~
ao");
}
17
18
19
20
21
22
23
24
}
25
Código 4.6.1: Código Java - IF
Figura 4.15: Tempo de compilação If - Java
Figura 4.16: Tempo de execução If - Java
Os tempos em Erlang são mostrados nas figuras 4.17 e 4.18. Nesse caso, observa-se
que o tempo de compilação em Erlang foi menor, porém o tempo de execução depende do
tempo de entrada no console.
Teste 6 - FOR
53
Figura 4.17: Tempo de compilação If - Erlang
Figura 4.18: Tempo de execução If - Erlang
4.7
Teste 6 - FOR
Para o suporte a estrutura de repetição for, foi utilizado o código 4.7.1 para teste.
1
public class For {
2
public static void main(String[] Args) {
3
4
int n = 20;
int x = 0;
int y = 0;
5
6
7
8
for (int i=0; i <= n; i++) {
x = x + i;
for (int j=0; j <= n; j++)
y = x + j;
}
9
10
11
12
13
14
System.out.println("Valor de x: " + x);
System.out.println("Valor de y: " + y);
15
16
17
}
18
19
}
Código 4.7.1: Código Java - For
No programa há dois comandos de for, um dentro do outro, fazendo uma soma simples
da soma atual mais o contador.
Os tempos de compilação e execução, na linguagem Java, foram de 0.645s e 0.074s,
como mostra, respectivamente, as figura 4.19 e 4.20.
E na linguagem Erlang o tempo de compilação foi de 0.281s e 0.145s de execução,
Teste 7 - While
54
Figura 4.19: Tempo de compilação For - Java
Figura 4.20: Tempo de execução For - Java
figuras 4.21 e 4.22. O tempo de compilação do Erlang foi menor, porém o de execução foi,
consideravelmente, maior.
Figura 4.21: Tempo de compilação For - Erlang
Figura 4.22: Tempo de execução For - Erlang
4.8
Teste 7 - While
Seguindo com os testes das estruturas de repetição, para o comando while, foi utilizado
o código 4.8.1.
O algoritmo calcula a série de fibonacci para um valor de n = 20. Os tempos de
compilação e execução, 0.710s e 0.068s, deste código em Java, bem como a saı́da para a
série de fibonacci gerada, são mostrados nas figuras 4.23 e 4.24.
Teste 7 - While
1
55
public class While{
2
public static void main(String[] args){
3
4
int
int
int
int
int
5
6
7
8
9
anterior = 0;
proximo = 1;
temp = 0;
n = 20;
i = 1;
10
System.out.println("-----Série de Fibonacci-----");
11
12
while(i <= n){
13
14
System.out.print(anterior + ",");
15
16
temp = anterior;
anterior = proximo;
proximo = temp + proximo;
17
18
19
20
i++;
21
}
22
}
23
24
}
Código 4.8.1: Código Java - While
Figura 4.23: Tempo de compilação while - Java
Figura 4.24: Tempo de execução while - Java
Para o algoritmo da série de fibonacci também foi obtido a mesma saı́da no código
gerado em Erlang, como pode ser observado na figura 4.26. Mais uma vez o tempo de
compilação em Erlang foi menor, 0.288s, porém o tempo de execução foi maior, 0.144s,
o mesmo ocorrido para o comando for.
Teste 8 - Do_While
56
Figura 4.25: Tempo de compilação While - Erlang
Figura 4.26: Tempo de execução While - Erlang
4.9
Teste 8 - Do While
O último teste realizado foi com a estrutura de repetição do_while. No teste foi utilizado o exemplo do código 4.9.1.
1
2
3
public class DoWhile {
public static void main(String [] args){
int x = 10;
4
do{
System.out.print("value of x : " + x );
x++;
System.out.print("\n");
} while( x < 20 );
5
6
7
8
9
}
10
11
}
Código 4.9.1: Código Java - Do While
O programa apenas imprime os valores do contador a partir do valor que x recebe na
sua declaração.
Ao compilarmos o código, os tempos de compilação e execução em Java foram de 0.648s
e 0.068s, mostrados, repectivamente, nas figuras 4.27 e 4.28.
Figura 4.27: Tempo de compilação Do While - Java
Teste 8 - Do_While
57
Figura 4.28: Tempo de execução Do While - Java
Em Erlang, observa-se a mesma saı́da apresentada em Java, com os tempos de teste
0.268s e 0.144s. E como nos testes das estruturas de repetição anteriores, para esse teste
também obteve-se um maior tempo de execução comparado ao tempo de teste em Java.
Figura 4.29: Tempo de compilação Do While - Erlang
Figura 4.30: Tempo de execução Do While - Erlang
Resumo dos tempos de Compilação e Execução dos testes
4.10
58
Resumo dos tempos de Compilação e Execução
dos testes
A tabela 4.1 mostra os tempos de compilação e execução de todos os testes realizados
neste capı́tulo.
Testes
Compilação(s)
Java
Erlang
Execução(s)
Java Erlang
Print
Scanner
Random
Arquivos
If
For
While
Do While
0.632
0.783
0.702
0.893
0.715
0.645
0.710
0.648
0.108
1.174
0.069
0.067
3.614
0.074
0.068
0.068
0.265
0.264
0.496
0.267
0.269
0.281
0.288
0.268
0.525
1.088
0.172
0.172
3.113
0.145
0.144
0.144
Tabela 4.1: Tempos de compilação e execução dos testes
Como pode-se observar, todos os tempos de compilação em Erlang foram menores, o
que não é verificado nos tempos de execução. Uma das principais causas dessa diferença nos
tempos de execução em Erlang, como mencionado anteriormente, deve-se ao tratamento
de variáveis, que difere muito de uma linguagem orientada a objetos para uma linguagem
funcional, além de que Erlang não tem suporte a estruturas de repetição. Além disso,
outro fator que caracteriza essa diferença nos tempos é o fato de o compilador não ter
otimização.
Capı́tulo 5
Conclusões
Neste trabalho foram implementadas as principais definições das estruturas de controle da
linguagem Java para o Compilador Jaraki. A implementação dessas estruturas contribui
muito no desenvolvimento de uma linguagem, uma vez que seria difı́cil programar sem
o suporte as três estruturas de controle (condição, repetição e sequência) implementadas
neste trabalho. Ferramentas como o Leex e o Yecc contribuı́ram para o melhor desenvolvimento do compilador. Além disso, a criação dos módulos facilitaram a implementação
do trabalho, e deixaram o código mais flexı́vel a possı́veis mudanças, caso venham ser
necessárias.
Como em todo trabalho de implementação, foi difı́cil atingir a melhor solução, muitas
dessas foram pensadas e até mesmo implementadas até alcançar uma que combinasse melhor desempenho e que se aproximasse mais do resultado obtido na linguagem Java. Para
isso, fez-se necessário buscar soluções já desenvolvidas para outros compiladores e estudar,
com base nessas soluções, a melhor forma de desenvolver as análises léxica, sintática, como
o tratamento de ambiguidade do if, e semântica, para portar as estruturas de controle da
linguagem Java para o Erlang.
Desenvolver um compilador é sempre um desafio, principalmente quando se trata de
linguagens com paradigmas diferentes. Implementar funcionalidades inexistentes na linguagem alvo foi uma das principais dificuldades encontradas. Ao final, essas dificuldades são
recompensadas quando superadas, uma delas foi criar o suporte a estruturas de repetição
em uma linguagem funcional.
Em paralelo, além da experiência adquirida com a construção de compiladores, quando
se trabalha desenvolvendo suporte a estruturas de uma linguagem acaba-se aprendendo
Trabalhos Futuros
60
peculiaridades que normalmente não são notadas em uma programação normal.
5.1
Trabalhos Futuros
Java é uma linguagem com muitos recursos, assim muitas definições das estruturas de
controle, que não foram desenvolvidas neste trabalho, podem ser implementadas para o
compilador Jaraki, são elas:
• Suporte a estrutura de condição switch..case.
• Suporte a try..catch.
• Suporte aos comandos break e continue.
• Suporte a loop infinito.
• Outras classes de suporte a arquivos, como: File, BufferedReader, BufferedWriter.
Referências Bibliográficas
[Aho2007] Aho, Alfred V. SETHI, R. U. J. D. (2007). Compiladores: Princı́pios, Técnicas
e Ferramentas. Person, 2 edition.
[Armstrong2007] Armstrong, J. (2007). Pragmatic Programming in Erlang. Pragmatic
Bookshelf.
[Buyya2009] Buyya, Rajkumar. Selvi, S. T. (2009). Object-Oriented Programming with
Java. Tata MacGraw Hill.
[Cesarini2009] Cesarini, F. Thompson, S. (2009). Erlang Programming. O’Reilly.
[Deitel2010] Deitel, P. D. H. M. (2010). Java como programar. Pearson Prentice Hall, 8
edition.
[Java2012] Java (2012).
The Java Language Specification.
<http://docs.oracle.com/javase/specs/jls/se7/html/index.html>.
Disponı́vel em:
Acesso
em:
03/11/2012.
[Jruby2012] Jruby (2012).
The Ruby Programming Language.
Disponı́vel em:
<http://jruby.org//>. Acesso em: 03/11/2012.
[Jython2012] Jython
(2012).
The
Jython
Project.
Disponı́vel
em:
<http://www.jython.org//>. Acesso em: 03/11/2012.
[Reia2012] Reia (2012). Reia Language. Disponı́vel em: <http://reia-lang.org/>. Acesso
em: 03/11/2012.
[Sebesta2011] Sebesta, R. W. (2011). Conceitos de Linguagem de Programação. Bookman,
9 edition.
Apêndice A
Os códigos-fonte dos módulos implementados para o desenvolvimento das estruturas de
controle apresentados neste trabalho, assim como os códigos dos testes gerados em Erlang,
encontram-se no CD que acompanha esta monografia, organizado nas pastas conforme
abaixo.
1. /jaraki - arquivos dos módulos que compõem o compilador.
• /ebin: bytecodes dos .erl.
• /include:
– jaraki_define.erl: definição de constantes.
• /java src: testes utlizados durante o desenvolvimento.
• jkc: script para executar o .erl gerado.
• jk: script para compilar o .java.
• Makefile: makefile para compilador o compilador.
2. /src: códigos-fonte do compilador.
• ast.erl: interface com o analisador sintático.
• core.erl: interface para converter jAST em eAST.
• jaraki_lexer.erl: analisador léxico gerado.
• jaraki_lexer.xrl: código fonte para o leex gerar o analisador léxico.
• jaraki_parser.yrl: código fonte para o yecc gerar o analisador sintático.
• jaraki_parser.yrl: analisador sintático gerado.
63
• jaraki.erl: interface com usuário para compilar .java.
• gen_erl_code.erl: principal módulo do analisador semântico.
• gen_ast.erl: funções auxiliares para montar nós da eAST.
• st.erl: funções para gerenciar a tabela de sı́mbolos.
• jaraki_exception.erl: módulo que trata erros de compilação.
• jaraki_utils.erl: funções para auxiliar o desenvolvimento do Jaraki.
• helpers: funções auxiliares diversas.
• loop.erl: biblioteca para suportar laços.
• random_lib.erl: biblioteca para suportar o uso da classe Random.
• file_lib.erl: biblioteca para suportar manipulação de arquivos.
• matrix.erl: biblioteca para suportar matrizes.
• vector.erl: biblioteca para suportar vetores.
• oo_lib.erl: biblioteca para suportar orientação a objetos.
3. /pdf - contém dois arquivos .pdf: monografia e apresentação.
4. /testes - arquivos dos testes realizados nesta monografia. Para cada um dos testes
existe uma subpasta com os seguintes arquivos:
• um arquivo .java: codigo-fonte Java para teste.
• um arquivo .erl: codigo gerado em Erlang a partir do arquivo teste.
• jast: árvore sintática da linguagem Java gerada.
• east: árvore abstrata do Erlang gerada.
5. /video - pasta contendo um vı́deo com a simulação dos testes demonstrados na apresentação.
Download

implementa¸c˜ao das estruturas de controle da linguagem java para