implementa¸c˜ao do tratamento de vari´aveis e

Propaganda
UNIVERSIDADE DO ESTADO DO AMAZONAS - UEA
ESCOLA SUPERIOR DE TECNOLOGIA
ENGENHARIA DE COMPUTAÇÃO
LÍDIA LIZZIANE SEREJO DE CARVALHO
IMPLEMENTAÇÃO DO TRATAMENTO DE
VARIÁVEIS E ERROS EM ERLANG NO
COMPILADOR JARAKI
Manaus
2012
LÍDIA LIZZIANE SEREJO DE CARVALHO
IMPLEMENTAÇÃO DO TRATAMENTO DE VARIÁVEIS E ERROS EM
ERLANG NO 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. MSc. Jucimar Maia da Silva Júnior
Manaus
2012
ii
Universidade do Estado do Amazonas - UEA
Escola Superior de Tecnologia - EST
Reitor:
José Aldemir de Oliveira
Vice-Reitor:
Marly Guimarães Fernandes Costa
Diretor da Escola Superior de Tecnologia:
Mário Augusto Bessa de Figueiredo
Coordenador 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. MSc. Jucimar Maia da Silva Júnior (Orientador)
Prof. MSc. Raimundo Corrêa de Oliveira
Prof. MSc. Rodrigo Choji de Freitas
CIP - Catalogação na Publicação
C331i
CARVALHO, Lı́dia
Implementação do Tratamento de Variáveis e Erros em Erlang no Compilador Jaraki / Lı́dia Carvalho; [orientado por] Prof. MSc. Jucimar Maia da
Silva Júnior - Manaus: UEA, 2012.
61 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
LÍDIA LIZZIANE SEREJO DE CARVALHO
IMPLEMENTAÇÃO DO TRATAMENTO DE VARIÁVEIS E ERROS EM
ERLANG NO 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
A Deus pela vida e privilégio da graduação
que me foi proporcionada. A minha famı́lia
por todo apoio e compreensão. Aos colegas e
amigos de jornada, professores e mestres, em
especial Professor Orientador Jucimar Junior
pelas correções, orientações e incentivo.
v
Resumo
Esse trabalho mostra a implementação do módulo de manipulação de variáveis e tipos
de dados e o tratamento de exceções para o compilador Jaraki através do uso de dicionários.
O Jaraki é um compilador que executa códigos-fonte da linguagem Java na máquina virtual
do Erlang.
Palavras Chave: Compiladores, Analisadores, Léxico, Sintático, Semântico, Java, Erlang
vi
Abstract
This work presents the implementation of variable and data types handling module
and exceptions handling module for the Jaraki compiler through the use of dictionaries.
The Jaraki is a compiler that run source code of the Java language on the Erlang virtual
machine.
Key-words: Compilers, Analyzers, Lexical, Syntactic, Semantic, Java, Erlang
vii
Sumário
Lista de Tabelas
x
Lista de Figuras
xi
Lista de Códigos
xii
1 Introdução
1.1 Objetivos . . . . . . . . . .
1.1.1 Objetivos Especı́ficos
1.2 Trabalhos Relacionados . . .
1.3 Justificativa . . . . . . . . .
1.4 Metodologia . . . . . . . . .
1.5 Estrutura do Trabalho . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2 Referencial Teórico
2.1 Compiladores . . . . . . . . . . . . .
2.2 Análise Léxica . . . . . . . . . . . . .
2.3 Análise Sintática . . . . . . . . . . .
2.4 Análise Semântica . . . . . . . . . . .
2.5 Tabela de Sı́mbolos e Manipulação de
2.6 Java . . . . . . . . . . . . . . . . . .
2.6.1 Variáveis . . . . . . . . . . . .
2.7 Erlang . . . . . . . . . . . . . . . . .
2.7.1 Caracterı́sticas . . . . . . . .
2.7.2 Variáveis . . . . . . . . . . . .
2.7.3 Tipos de Dados . . . . . . . .
2.7.4 Operadores Aritméticos . . .
2.7.5 Atoms . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. . . .
. . . .
. . . .
. . . .
Erros
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
2
2
2
3
3
3
.
.
.
.
.
.
.
.
.
.
.
.
.
5
5
6
7
8
8
9
9
11
12
13
14
15
16
viii
2.8
Projeto Jaraki . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3 Desenvolvimento
3.1 Analisadores . . . . . . . . . . . . . . .
3.1.1 Leex . . . . . . . . . . . . . . .
3.1.2 Yecc . . . . . . . . . . . . . . .
3.2 Declaração e Atribuição de Variáveis .
3.2.1 Análise Léxica . . . . . . . . . .
3.2.2 Análise Sintática . . . . . . . .
3.2.3 Análise Semântica e Geração de
3.3 Vetores . . . . . . . . . . . . . . . . . .
3.3.1 Análise Léxica . . . . . . . . . .
3.3.2 Análise Sintática . . . . . . . .
3.3.3 Análise Semântica e Geração de
3.4 Matrizes . . . . . . . . . . . . . . . . .
3.4.1 Análise Léxica . . . . . . . . . .
3.4.2 Análise Sintática . . . . . . . .
3.4.3 Análise Semântica e Geração de
16
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
18
18
18
19
19
22
23
24
28
30
31
32
34
35
35
38
.
.
.
.
.
.
.
.
.
.
.
.
42
42
43
43
44
45
48
49
49
51
52
54
55
5 Conclusões
5.1 Trabalhos Futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
56
57
Referências Bibliográficas
58
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
Código .
. . . . .
. . . . .
. . . . .
Código .
. . . . .
. . . . .
. . . . .
Código .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4 Testes
4.1 Processo de Compilação . . . . . . . . . . . . . . . .
4.1.1 Árvore Sintática do compilador Jaraki - JAST
4.1.2 Árvore Sintática do Erlang - AST . . . . . . .
4.2 Teste 1 - Variáveis . . . . . . . . . . . . . . . . . . .
4.3 Teste 2 - Strings . . . . . . . . . . . . . . . . . . . . .
4.4 Teste 3 - Char . . . . . . . . . . . . . . . . . . . . . .
4.5 Teste 4 - Vetores . . . . . . . . . . . . . . . . . . . .
4.5.1 Bubblesort . . . . . . . . . . . . . . . . . . . .
4.5.2 Quicksort . . . . . . . . . . . . . . . . . . . .
4.6 Matrizes . . . . . . . . . . . . . . . . . . . . . . . . .
4.7 Erros . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.8 Resumo do Tempo dos Testes . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
ix
A Códigos de Testes
A.1 Estrutura dos Diretórios . . . . . . . . . . . . . . . . . . . . . . . . . . . .
60
60
x
Lista de Tabelas
2.1
2.2
Precedência de Operadores Aritméticos . . . . . . . . . . . . . . . . . . . .
Operadores Aritméticos . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
15
3.1
3.2
3.3
Bloco de Definições . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Sı́mbolos Terminais e Não-Terminais . . . . . . . . . . . . . . . . . . . . .
Leex - Bloco de Definições do Vetor . . . . . . . . . . . . . . . . . . . . . .
22
24
30
4.1
Resumo dos tempos de testes . . . . . . . . . . . . . . . . . . . . . . . . .
55
xi
Lista de Figuras
2.1
2.2
2.3
2.4
2.5
Compilador . . . . . . . . . . . . . . . . .
Fases de um Compilador . . . . . . . . . .
Interação do Analisador léxico e Sintático .
Árvore Sintática − id1 = id2 + id3 ∗ 60 . .
Compilador Jaraki . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
6
7
7
16
3.1
3.2
3.3
3.4
3.5
3.6
3.7
3.8
3.9
3.10
3.11
Analisador Léxico Leex . . . . . . .
Dicionário de Processos . . . . . . .
Declaração de Variáveis . . . . . . .
Módulo ST . . . . . . . . . . . . .
Fluxograma - Inserção de Variáveis
Atribuição . . . . . . . . . . . . . .
Módulo Vector . . . . . . . . . . .
Declarações de Array . . . . . . . .
Array de Array . . . . . . . . . . .
Módulo Matrix . . . . . . . . . . .
Fluxo Matrix . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
19
21
25
26
27
27
34
35
39
40
41
4.1
4.2
4.3
4.4
4.5
4.6
4.7
4.8
4.9
4.10
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Tempo
Compilação em Java - Cálculo Média .
Execução em Java - Cálculo Média . .
Compilação em Erlang - Cálculo Média
Execução em Erlang - Cálculo Média .
Compilação em Java - String . . . . . .
Execução em Java - String . . . . . . .
Compilação em Erlang - String . . . .
Execução em Erlang - String . . . . . .
Compilação em Java - Char . . . . . .
Execução em Java - Char . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
45
46
46
46
47
47
48
48
48
49
de
de
de
de
de
de
de
de
de
de
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
xii
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
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
Compilação em Erlang - Char . . . . . . . .
Execução em Erlang - Char . . . . . . . . .
Compilação e Execução em Java - bubblesort
Compilação em Erlang - bubblesort . . . . .
Execução em Erlang - bubblesort . . . . . . .
Compilação e Execução em Java - quicksort
Compilação em Erlang - quicksort . . . . . .
Execução em Erlang - quicksort . . . . . . .
Compilação Java - Matrizes . . . . . . . . .
Execução em Java - Matrizes . . . . . . . . .
Compilação em Erlang - Matrizes . . . . . .
Execução em Erlang - Matrizes . . . . . . .
Compilação Java - Erros . . . . . . . . . . .
Execução em Java - Erros . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
49
49
50
51
51
51
52
52
53
53
53
54
55
55
xiii
Lista de Códigos
2.6.1 Variáveis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
2.6.2 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
2.6.3 Declaração de Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
2.7.1 Erro de Atribuição Única . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13
2.7.2 Atribuição a uma nova variável . . . . . . . . . . . . . . . . . . . . . . . .
14
2.7.3 Representação de Caracteres . . . . . . . . . . . . . . . . . . . . . . . . . .
15
2.7.4 Representação de Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15
2.7.5 Atoms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
3.2.1 Código Java - Declaração e Atribuição de Variáveis . . . . . . . . . . . . .
20
3.2.2 EBNF - Declaração de Variáveis e Atribuição . . . . . . . . . . . . . . . .
20
3.2.3 Estrutura do arquivo jaraki lexer.xrl . . . . . . . . . . . . . . . . . . . . . .
22
3.2.4 Lista de Tokens Gerados pelo Leex
. . . . . . . . . . . . . . . . . . . . . .
23
3.2.5 Estrutura do arquivo jaraki parser.yrl . . . . . . . . . . . . . . . . . . . . .
24
3.2.6 Árvore sintática . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
25
3.2.7 Código Erlang - Declaração e Atribuição de Variáveis . . . . . . . . . . . .
28
3.3.1 Código Java - Declaração e Atribuição de Variáveis envolvendo Vetores . .
29
3.3.2 EBNF - Vetores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
3.3.3 Módulo Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30
3.3.4 Lista de Tokens Gerados pelo Leex
. . . . . . . . . . . . . . . . . . . . . .
31
3.3.5 Estrutura do arquivo jaraki parser.yrl . . . . . . . . . . . . . . . . . . . . .
32
3.3.6 Árvore Sintática . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
33
3.3.7 Código Erlang - Vetor . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
36
3.4.1 Código Java - Matriz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
36
xiv
3.4.2 EBNF - Matrizes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
37
3.4.3 Lista de Tokens Gerados pelo Leex
. . . . . . . . . . . . . . . . . . . . . .
37
3.4.4 Estrutura do arquivo jaraki parser.yrl . . . . . . . . . . . . . . . . . . . . .
38
3.4.5 Árvore Sintática . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
39
3.4.6 Código Erlang - Matrix . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
41
4.1.1 Código Java - Cálculo Média . . . . . . . . . . . . . . . . . . . . . . . . . .
43
4.1.2 JAST - Cálculo Média . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
44
4.1.3 AST do Erlang - Cálculo Média . . . . . . . . . . . . . . . . . . . . . . . .
45
4.1.4 Código Erlang - Cálculo Média . . . . . . . . . . . . . . . . . . . . . . . .
46
4.3.1 Código Java - String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
47
4.4.1 Código Java - Char . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
48
4.5.1 Código Java - Método de ordenação bolha
. . . . . . . . . . . . . . . . . .
50
4.5.2 Código Java - Algoritmo Quicksort . . . . . . . . . . . . . . . . . . . . . .
52
4.7.1 Código Java - Erro de Variáveis . . . . . . . . . . . . . . . . . . . . . . . .
54
Capı́tulo 1
Introdução
Apesar de programadores vincularem variáveis a locais de memória, existem muitos aspectos que permeiam esse conceito. Variáveis são abstrações para as células de memória
da máquina em uma linguagem e pode ser caracterizada por 6 atributos (nome, endereço,
valor, tipo, tempo de vida e escopo) [Sebesta2011]. De acordo com Deitel, uma variável
é uma posição de memória do computador onde um valor pode ser armazenado para uso
posterior em um programa [Deitel2010].
É crucial que uma linguagem suporte uma variedade de tipos de dados e estruturas.
Os tipos de dados de uma linguagem são uma parte grande daquilo que determina o seu
estilo e seu uso. Juntamente com as estruturas de controle, formam o coração de uma
linguagem [Sebesta2011].
A maioria das especificações das linguagens de programação não descreve como um
compilador deveria responder aos erros; tal tarefa é designada ao projetista do compilador.
Cada erro encontrado durante as fases de compilação precisarão ser tratados de tal forma
que a compilação possa continuar, permitindo que sejam detectados erros posteriores no
programa fonte [Aho2007].
Java é uma linguagem que foi anunciada em 1995 pela Sun atualmente utilizada para
desenvolver aplicativos de grande porte, aprimorar a funcionalidade de servidores da web,
fornecer aplicativos para dispositivos voltados para o consumo popular e para muitos outros
propósitos. Sua programação é orientada a objetos, permitindo implementar um design
orientado a objetos como um sistema funcional no mundo e disponibiliza concorrência por
Objetivos
2
meio da linguagem e das APIs [Deitel2010].
Erlang é uma linguagem de programação funcional projetada para sistemas complexos
tolerantes a falhas, desenvolvido originalmente no Laboratório de Ciência da computação
da empresa sueca Ericsson Telecom. Por meio de um conjunto de processos paralelos que
podem interagir apenas por troca de mensagens, modela facilmente a programação paralela.
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].
O compilador Jaraki tem como principal objetivo compilar arquivos em Java na máquina
virtual do Erlang implementando partes inerentes a linguagem Java em Erlang.
1.1
Objetivos
Implementar o módulo de manipulação de variáveis e o tratamento de exceções para o
compilador Jaraki.
1.1.1
Objetivos Especı́ficos
Os objetivos especı́ficos são:
• Manipular nomes, vinculações e escopos;
• Implementar tipos de dados;
• Implementar expressões e sentenças de atribuição;
• Implementar tratamento de exceções e tratamento de eventos.
1.2
Trabalhos Relacionados
Outros trabalhos também implementam compiladores, como por exemplo uma simples
calculadora [Upadhyaya2011], que utiliza as ferramentas LEX e YACC para as análises
Justificativa
3
léxica e sintática em linguagem C e também grandes projetos como Reia e Jython. A
linguagem Jython [Jython2012] é uma implementação Java de Python que permite escrever
códigos em Python e rodar dentro da JVM (Java Virtual Machine), enquanto que Reia
[Reia2012] é uma linguagem de script como o Ruby para a máquina virtual do Erlang.
1.3
Justificativa
Diferente da linguagem Java, em Erlang não existem variáveis globais e a atribuição
a uma variável é única (single assignment), ou seja, uma vez que se atribui um valor a
mesma, este valor não pode ser alterado. Verifica-se então que matrizes, vetores e algumas atribuições em Java, por exemplo, tornam-se um desafio quando vinculadas a uma
linguagem funcional.
Apesar de a maioria dos sistemas de computadores serem capazes de detectar certas
condições de erros [Sebesta2011], cada fase de compilação pode apresentar erros especı́ficos
que devem ser retornados e tratados buscando a não interrupção da compilação.
1.4
Metodologia
Para o desenvolvimento deste trabalho foram utilizados arquivos de testes envolvendo
cada etapa necessária para a implementação. Cada teste foi construı́do de forma incremental, abrangendo todas as etapas de compilação: análise léxica, sintática, semântica e
geração de código em Erlang.
1.5
Estrutura do Trabalho
Além do capı́tulo atual, este trabalho está dividido em mais 4 capı́tulos. O Capı́tulo
2, referencial teórico, aborda os assuntos inerentes a estrutura de um compilador (análise
léxica, sintática, semântica e geração de código) e as linguagens envolvidas: Java e Erlang.
O Capı́tulo 3, Desenvolvimento, descreve todas as etapas de implementação do compilador
Jaraki dentro do escopo definido, expondo estruturas desenvolvidas e abordagens utiliza-
Estrutura do Trabalho
4
das para o mesmo. O objetivo do Capı́tulo 4, Testes, é exemplificar, por meio de testes
realizados, a estrutura de código gerado em Erlang a partir de códigos fontes Java. Por
fim, o último capı́tulo refere-se à conclusão e trabalho futuros.
Capı́tulo 2
Referencial Teórico
2.1
Compiladores
Um compilador é um programa que mapeia um programa escrito em uma determinada
linguagem e o traduz em um programa equivalente em outra linguagem, tendo como etapa
importante relatar erros ao usuário, como representado na Figura 2.1. Esse processo de
compilação é dividido em duas partes: análise e sı́ntese [Aho2007].
Figura 2.1: Compilador
Na análise, o programa fonte é dividido em partes constituintes instituindo uma estrutura gramatical. O compilador utiliza essa estrutura para criar uma representação
intermediária do mesmo. É nessa fase que o compilador detecta se o programa fonte é sintaticamente formado ou semanticamente correto, devendo emitir mensagens informativas
ao usuário. A fase de análise também coleta informações sobre o programa fonte e as armazena em uma estrutura de dados chamada tabela de sı́mbolos, que é passado juntamente
Análise Léxica
6
com a representação intermediária à etapa de sı́ntese [Aho2007].
A sı́ntese constrói partes do programa alvo a partir da representação intermediária e as
informações contidas na tabela de sı́mbolos [Aho2007].
Segundo Aho, conceitualmente, um compilador opera em fases e, na prática, algumas
podem ser agrupadas e a representação intermediária entre elas não precisa ser explicitamente construı́da. Uma tı́pica decomposição de um compilador é mostrada na Figura 2.2.
A tabela de sı́mbolos, que armazena informações sobre o programa fonte (identificadores e
atributos), é usada em todas as fases de compilação.
Figura 2.2: Fases de um Compilador
2.2
Análise Léxica
Um analisador léxico é essencialmente um “casamenteiro” de padrões. Ele busca uma
subcadeia de uma dada cadeia de caracteres que casa com um padrão dado, coleta caracteres em agrupamentos lógicos e atribui códigos internos aos agrupamentos de acordo com
sua estrutura. Os agrupamentos de caracteres são chamados lexemas e os códigos internos
de sı́mbolos (tokens). Os analisadores léxicos extraem lexemas de uma dada cadeia de
entrada e produzem os respectivos sı́mbolos [Sebesta2011].
Nessa fase, o programa fonte é mapeado e os caracteres lidos são agrupados num fluxo
de tokens, no qual cada token representa uma sequência de caracteres logicamente coesiva,
Análise Sintática
7
como, por exemplo, um identificador e palavra-chave. Cada token é passado para o parser
(analisador sintático) a medida que o mesmo envia um comando “obter o próximo token”,
essa relação pode ser observada na Figura 2.3 [Aho2007].
Figura 2.3: Interação do Analisador léxico e Sintático
2.3
Análise Sintática
A análise hierárquica é chamada de análise gramatical ou análise sintática. Envolve
o agrupamento dos tokens do programa fonte em frases gramaticais, que são usadas pelo
compilador, a fim de sintetizar a saı́da. Essas frases gramaticais do programa fonte são
geralmente representadas por uma árvore gramatical como mostra a Figura 2.4 [Aho2007].
Figura 2.4: Árvore Sintática − id1 = id2 + id3 ∗ 60
Os analisadores das linguagens de programação constroem árvores de análise para os
programas de dados. A informação necessária para construir a árvore é criada durante
o processo de análise. As árvores de análise e as derivações incluem toda a informação
sintática necessária para um processador da linguagem. Observa-se duas metas nessa fase:
Análise Semântica
8
verificar o programa de entrada para determinar se ele está sintaticamente correto e produzir uma árvore de análise completa ou pelo menos esborçar sua estrutura [Sebesta2011].
2.4
Análise Semântica
A semântica de um programa é o seu “significado”, contrastando com sua sintaxe ou
estrutura. É ela que determina o seu comportamento durante a execução [Louden2004].
A análise semântica pode ser dividida em duas categorias. A primeira é a análise de um
programa requerida pelas regras de linguagem de programação, para verificar sua correção
e garantir sua execução. A segunda categoria é aquela efetuada por um compilador para
melhorar a eficiência de execução do programa traduzido. Esse tipo de análise é, em geral,
incluı́do nas discussões sobre “otimização”, ou técnicas de melhoria de código [Louden2004].
É nessa fase também que são verificados erros semânticos no programa fonte e capturados informações de tipo para a fase de geração de código. Na verificação de tipos, o
compilador checa se cada operador recebe os operandos que são permitidos pela especificação da linguagem fonte. Por exemplo, muitas definições nas linguagens de programação
requerem que o compilador relate um erro a cada vez que um número real seja usado como
ı́ndice de um array [Aho2007].
2.5
Tabela de Sı́mbolos e Manipulação de Erros
Uma tabela de sı́mbolos é uma estrutura de dados contendo um registro para cada
identificador (funções, variáveis, constantes e tipos de dados). Ela interage com quase todas
as fases de compilação (análise léxica, sintática e semântica), onde os mesmos fornecem
identificadores à tabela. As fases de otimização e geração de código utilizam a informação
da tabela para efetuar escolhas apropriadas para o código objeto.
Uma das funções mais importantes de um compilador é sua resposta a erros. Os erros
estáticos (ou em tempo de compilação) devem ser detectados e reportados durante quase
todas as fases de compilação. É importante que o compilador possa gerar mensagens de
erros inteligı́veis e concluir a compilação após cada erro [Louden2004].
Java
9
As fases de análise sintática e semântica tratam usualmente de uma ampla fatia dos erros
detectáveis pelo compilador. Erros de sintaxe ocorrem quando caracteres remanescentes
na entrada não formam qualquer token da linguagem. Os erros, onde o fluxo de tokens
viole as regras estruturais (sintaxe) da linguagem, são determinados pela fase de análise
sintática.
2.6
Java
A linguagem Java evoluiu a partir das linguagens C e C++ e foi desenvolvida por
James Gosling. É uma linguagem de programação orientada a objetos mais amplamente
utilizada do mundo. Uma das razões para a sua popularidade é a sua independência de
plataforma, tornando-a diferente da maioria das outras linguagens de programação, que
exigem diferentes compiladores para diferentes sistemas operacionais [Hubbard2004].
A linguagem Java compila seu código-fonte para uma linguagem genérica bytecode, que
é executada pela máquina cliente usando um programa chamado de Java Virtual Machine
(JVM). O sistema JVM é um interpretador, ou seja, traduz e executa cada instrução em
bytecode separadamente, sempre que ela é necessária [Hubbard2004].
Java tem um conjunto de classes predefinidas reutilizáveis. Essas classes são agrupadas em pacotes e, coletivamente, são chamados de bibliotecas de classes Java ou Java
Application Programming Interface (Java API) [Java2012].
2.6.1
Variáveis
Pode ser caracterizada por um conjunto de propriedades, ou atributos. Uma variável
é uma posição de memória onde seu valor pode ser armazenado para uso posterior do
programa. Variáveis em Java devem ser declaradas especificando um nome e um tipo de
dados antes de serem usadas. Sentenças de atribuição tem como objetivo modificar o valor
de uma variável, seu valor pode mudar durante a execução de um programa [Deitel2010].
Os nomes de variável number1 e number2 (Código 2.6.1) correspondem às posições de
memória do computador. Cada variável tem um nome, tipo, tamanho (em Bytes), valor,
escopo e tempo de vida. O nome é uma cadeia de caracteres que identifica um entidade
Java
1
2
3
4
5
6
7
10
// Declaraç~
ao
int
number1;
float number2;
// Atribuiç~
ao
number1 = 1;
number1 = 1 * 60 + 2;
Código 2.6.1: Variáveis
do programa. Linguagens como C, C++ e Java fazem distinção entre nomes em fontes
maiúsculas e minúsculas. Algumas palavras não podem ser usadas como nomes. Essas,
chamadas de palavras reservadas, têm significado predefinidos como, por exemplo, os tipos
int e float [Sebesta2011].
Uma variável pode ser definida também como local ou global, dependendo do seu escopo.
Se o escopo de uma variável está relacionado a um método, seu tempo de vida estará
limitado desde o ı́nicio da chamada até o fim da execução deste método. Dado uma
declaração de variável dentro do corpo de uma classe, seu tempo de vida estará relacionado
em toda execução do programa.
O maioria dos programas realiza cálculos aritméticos e a linguagem Java aplica os
operadores aritméticos em expressões aritméticas em uma sequência precisa determinada
pelas seguintes regras de precedência de operadores, que são geralmente as mesmas seguidas
em álgebra como mostra a Tabela 2.1 [Deitel2010]:
1. Operações de multiplicação, divisão e módulos são aplicadas primeiro. Se uma expressão contiver várias dessas operações, elas serão aplicadas da esquerda para a
direita. Os operadores de multiplicação, divisão e módulo têm o mesmo nı́vel de
precedência.
2. As operações de adição e subtração são aplicadas em seguida. Se uma expressão
contiver várias dessas operações, os operadores serão aplicados da esquerda para a
direita. Os operadores de adição e subtração têm o mesmo nı́vel de precedência.
Em Java, strings (sequência de caracteres tratadas como uma única unidade) são representadas por meio da utilização da classe String, ou seja, uma string é um objeto
da classe String (Código 2.6.2).
Os grupos de variáveis que contém valores do mesmo
tipo são chamados de array. Eles também são objetos, portanto, considerados tipos por
Erlang
11
Operadores Aritméticos
Operador
Operação
Ordem de Avaliação(precedência)
∗
Multiplicação Avaliado primeiro. Se houver vários operadores desse tipo,
/
Divisão
eles são avaliados da esquerda para a direita.
%
Resto
+
Adição
Avaliado em seguida. Se houver vários operadores desse tipo,
−
Subtração
eles são avaliados da esquerda para a direita.
=
Atribuição
Avaliado por último.
Tabela 2.1: Precedência de Operadores Aritméticos
1
public class ExemploString {
2
3
public static void main(String[] args) {
4
5
6
String s = new String();
s = "UEA";
7
8
9
System.out.println(s);
10
11
12
}
}
Código 2.6.2: Strings
referência [Deitel2010]. O número de posição de um array é chamado de ı́ndice. Como
os outros objetos, os arrays são criados com a palavra-chave new. Possui duas formas de
declaração:
1
2
int[] c = new int[12];
3
int[] n = {10, 20, 30, 40, 50};
Código 2.6.3: Declaração de Array
A primeira declaração cria um objeto array que contém 12 elementos int e armazena
a referência do array na variável c. Na segunda, o array é inicializado com 5 elementos
separados por vı́rgula com valores de ı́ndice de 0 − 4.
2.7
Erlang
Em meados da década de 1980, o laboratório de ciência da computação da Ericsson
teve a tarefa de investigar as linguagens de programação adequadas para programar a
Erlang
12
próxima geração de produtos de telecomunicações. Eles concluı́ram que ainda faltavam
caracterı́sticas necessárias para sistemas tão complexos e por isso criaram sua própria
linguagem, o Erlang [Cesarini2009].
2.7.1
Caracterı́sticas
Erlang possui as seguintes caracterı́sticas [Cesarini2009]:
• Linguagem Declarativa
Erlang é uma linguagem declarativa. Linguagens declarativas trabalham com o principio de tentar descrever o que deve ser calculado, em vez de dizer como um valor
é calculado. Descrevem em alto nı́vel uma aplicação onde os programadores não
especificam sequências de operações como em uma linguagem imperativa.
• Concorrência
Erlang 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.
• Propriedades de tempo Real
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 collection 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.
• Robustez
Tem um conjunto simples, mas poderoso, 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.
Erlang
13
• Sistema Distribuı́do
Erlang tem suporte à programação de sistemas distribuı́dos 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.
• Integração
Erlang pode facilmente fazer uso de programas escritos em outras linguagens de
programação e podem ser interligados ao sistema de uma maneira tal que pareça ao
programador ter sido escrito em Erlang.
2.7.2
Variáveis
Variáveis são usadas para armazenar valores de simples ou compostos tipos de dados.
Em Erlang, elas sempre iniciam com letra maiúscula, seguidas por letras maiúsculas e
minúsculas, inteiros e underscores. Não devem conter outros tipos de caractere especial.
Variáveis em Erlang diferem da maioria das variáveis de linguagens convencionais de
programação.
A principal diferença é que uma vez atribuı́do determinado valor a
uma variável, ele não poderá ser alterado. Isso é chamado de atribuição única (single
assignment) [Cesarini2009]. Um erro de atribuição única ocorre quando uma variável Var
recebe mais de uma vez determinado valor, como observado no Código 2.7.1.
1
%erl
2
3
4
1>Var = 2.
5
6
2
7
8
9
2>Var = Var * Var.
**exception erro: no match of right hand side value 4
Código 2.7.1: Erro de Atribuição Única
Se for preciso computar ou manipular o valor de uma variável, é necessário armazenar
os resultados em uma nova variável (Código 2.7.2):
Outra caracterı́stica das variáveis é que não é necessário declará-las. A razão para que
isso ocorra é que Erlang tem um sistema de tipos dinâmico. Os tipos e a viabilidade da
Erlang
14
1
2
%erl
3
4
5
1>Var = 2.
2
6
7
8
2>NovaVar = Var * Var.
9
4
Código 2.7.2: Atribuição a uma nova variável
operação são determinados em tempo de execução.
2.7.3
Tipos de Dados
Caracterı́sticas de alguns tipos de dados em Erlang:
Integer
Integers em Erlang são usados para representar números inteiros. Eles podem ser
positivos ou negativos e serem expressos em outra base diferente de 10. A noção de tamanho
máximo de inteiros em Erlang não existe, ou seja, números arbitrariamente longos também
podem ser representados.
Float e Boolean
Floats em Erlang são usados para representar números reais com ponto flutuante.
Não existem tipos distintos de valores booleanos ou caracteres em Erlang. Em vez de
um tipo booleano, os atoms true e false são usados em conjunto com operadores booleanos.
Caracteres e Strings
Caracteres são representados por inteiros, e strings são representados por lista de inteiros. A representação de um caractere é dado precedido pelo sı́mbolo $, como mostrado
no Código 2.7.3.
Não existe o tipo de dados string em Erlang. Strings são indicadas por lista de valores
ASCII e representadas com o uso da notação de aspas duplas (“ ”). Então , a string
“Hello World” é na verdade a lista [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]. Uma
string vazia (“ ”) é equivalente a uma lista vazia [ ]. O Código 2.7.4 mostra o uso dessa
representação.
Erlang
1
2
%erl
3
4
5
1>$A.
6
7
8
15
65
2>$A + 32.
9
10
97
11
12
13
3>$a.
97
Código 2.7.3: Representação de Caracteres
1
2
3
4
5
6
7
8
9
10
11
12
13
%erl
1>[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100].
"Hello World"
2>[85, 69, 65].
"UEA"
3>[$H, $e, $l, $l, $o, $ , $W, $o, $r, $l, $d].
"Hello World"
Código 2.7.4: Representação de Strings
2.7.4
Operadores Aritméticos
Operações com inteiros e floats em Erlang incluem adição, subtração, multiplicação e
divisão. Operações envolvendo inteiros resultam somente em inteiros, exceto em casos de
divisão de ponto flutuante, onde o resultado é um float. Usando o operador div, o resultado
é um inteiro sem o resto da divisão. Os operadores podem ser observados na Tabela 2.2.
Operadores Aritméticos
Tipo
Descrição
Tipo de Dados
+
Adição
Integer|Float
−
Subtração
Integer|Float
∗
Multiplicação
Integer|Float
/
Divisão por Ponto Flutuante
Integer|Float
div
Divisao por inteiro
Integer
rem
Resto
Integer
Tabela 2.2: Operadores Aritméticos
Projeto Jaraki
2.7.5
16
Atoms
Em Erlang, atoms são usados para representar diferentes valores de constantes não
númericas. Atoms iniciam com letras minúsculas ou são delimitados por aspas simples.
Seu valor é o nome que lhe foi atribuı́do, como observado no Código 2.7.5.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
%erl
1>abc.
abc
2>‘usando espacos’.
‘usando espacos’
3>‘Uea’.
‘Uea’
4>‘1+2+3’.
‘1+2+3’
Código 2.7.5: Atoms
2.8
Projeto Jaraki
O projeto consiste no desenvolvimento de um compilador cujo fluxo de entrada são
códigos-fontes em Java que passam por todos os processos de compilação (análise léxica,
sintática, semântica, tratamento de erros), gerando códigos na linguagem funcional Erlang
(Figura 2.5).
Figura 2.5: Compilador Jaraki
O projeto abrange o suporte às seguintes implementações:
• Tratamento de variáveis e erros;
Projeto Jaraki
17
• Estruturas de controle;
• Subprogramas, módulos e pacotes;
• Orientação a objetos.
Dentro do escopo deste trabalho, o projeto Jaraki abrange o suporte a variáveis (dicionário, atribuição, controle de escopo, vetores e matrizes) e o tratamento de erros de
compilação e verificações de tipos de dados.
Capı́tulo 3
Desenvolvimento
Este capı́tulo apresenta as etapas de desenvolvimento do compilador chamado Jaraki, ferramentas utilizadas nas análises, códigos-fontes, decisões de projetos e outras estruturas de
implementação.
3.1
Analisadores
Primeiramente, para o desenvolvimento tomou-se como base na geração da análise léxica
e sintática, respectivamente, as ferramentas Leex e Yecc, inerentes a linguagem Erlang, que
auxiliam nessas fases de construção da árvore sintática.
Leex e Yecc são implementações da linguagem Erlang do Lex (gerador de análise léxica
ou tokenizer ) e Yacc (Yet Another Compiler Compiler ), respectivamente.
3.1.1
Leex
O gerador de análise léxica Leex, semelhante a Lex ou Flex, lê e converte uma entrada
para um fluxo de tokens a ser analisado pelo parser. É um arquivo de extensão .xrl,
constituı́do em três partes:
• Definitions: definição de tokens da linguagem;
• Rules: extrai o token e a linha que o corresponde para ser utilizado posteriormente
na análise sintática e semântica;
• Erlang Code: bloco que realiza verificações referentes à linguagem Erlang, por exemplo o unescape, que tem como função transformar os caracteres especiais que foram
“escapados” nos caracteres que eles de fato representam.
Declaração e Atribuição de Variáveis
19
A Figura 3.1 ilustra o processo da análise do fluxo de entrada até a geração de tokens
da linguagem. Primeiramente é necessário mapear as especificações de todos os padrões
da linguagem (leex.xrl) e, por meio do arquivo gerado em Erlang, obter a sequência de
tokens.
Figura 3.1: Analisador Léxico Leex
3.1.2
Yecc
A árvore sintática é gerada através do Yecc e dos tokens obtidos na análise léxica.
É um gerador de parser LALR-1 (lookahead LR) para o Erlang, similar ao Yacc. Assume
uma definição de gramática BNF como entrada, e produz um código Erlang para o parser
[Erlang2012b].
O arquivo Yecc de extensão .yrl, assim como o Leex, é dividido em blocos:
• Nonterminals: Identifica os sı́mbolos que derivam outras sentenças;
• Terminals: Identifica os sı́mbolos terminais;
• Rootsymbol : Indica o inı́cio da primeira sentença a ser analisada;
• Erlang Code: Mesma função que é apresentada no analisador léxico.
3.2
Declaração e Atribuição de Variáveis
Para a fase inicial de implementação, no escopo de declaração e atribuição de variáveis,
definiu-se alguns códigos-fonte na linguagem Java.
Declaração e Atribuição de Variáveis
1
2
3
4
5
6
7
8
9
10
11
20
public class JarakiVar
{
public static void main(String [] Args)
{
int a, b = 4;
int c;
float d;
c = 2;
c = a / b * a + b;
}
}
Código 3.2.1: Código Java - Declaração e Atribuição de Variáveis
O Código 3.2.1 contém declarações e atribuições simples de variáveis. Este código
servirá como base nessa seção para exemplificar todo o processo do compilador Jaraki até
a geração de código.
É necessário observar que, para geração de código em Erlang, esses tipos de declarações
e atribuições se tornam um desafio, visto que as variáveis iniciam com letras minúsculas
e a atribuição não é única. Essas e outros tipos de verificações também tiveram que ser
analisadas para a correta construção do compilador.
A gramática envolvendo a declaração e atribuição pode ser descrita em notação EBNF
como observado no Código 3.2.2. A linha 1 define uma regra com declaração de uma ou
mais variáveis, podendo ter ou não atribuição. A atribuição tem um conjunto de regras
que definem a estrutura de uma atribuição simples ou mesmo de expressões aritméticas
sendo que determinadas regras tratam da precedência de operadores (linha 7 a 14).
1
<local_variable_declaration_statement>
::=
2
3
4
<variable_list>
::= "identifier" ["=" <element_value> ]
5
6
<element_value_pair>
::= "identifier" "=" <element_value> ";"
13
14
15
<element_value>
<bool_expr>
<comparation_expr>
<add_expr>
<mult_expr>
<modulus_expr>
<unary_expr>
<literal>
::=
::=
::=
::=
::=
::=
::=
::=
16
17
<type>
::= "int_t" | "long_t" | "float_t" | "double_t" | "char_t" |
"boolean_t" | "string_t"
7
8
9
10
11
12
18
(<type> | "identifier")
<variable_list> {"," <variable_list>} ";"
<bool_expr>
<comparation_expr> | <comparation_expr> <bool_op> <bool_expr>
<add_expr> | <add_expr> <comparation_op> <comparation_expr>
<mult_expr> | <mult_expr> <add_op> <add_expr>
<modulus_expr> | <modulus_expr> <mult_op> <mult_expr>
<unary_expr> | <unary_expr> <modulus_op> <modulus_expr>
<add_op> <literal> | <literal> | <bool_op> <literal>
"integer" | "float" | "identifier"
Código 3.2.2: EBNF - Declaração de Variáveis e Atribuição
Declaração e Atribuição de Variáveis
21
Dicionário de Processos
Essa implementação só foi possı́vel por meio da criação de um dicionário. Dicionários
representam uma coleção de elementos onde cada elemento possui uma chave e um valor,
seu valor pode ser acessado através de uma chave única. Essa necessidade surgiu para armazenamento do tipo, nome, escopo e valor das variáveis, visto que, durante a compilação,
devem ser analisados alguns aspectos, dado as limitações da linguagem Erlang dentro desse
contexto, dentre eles:
• Uma nova chave no dicionário, para a variável, deve ser criada após cada leitura de
declaração;
• O tipo se torna parte essencial da verificação de erros;
• Tratar a variável caso ela seja local ou global;
• Manipular variável ao receber mais de uma atribuição.
Dentre algumas abordagens pesquisadas para a implementação do dicionário, utilizouse o dicionário de processos da linguagem Erlang. Ele mantém os dados durante toda a
execução do programa. Esse dicionário é uma tabela hash, ou seja, não é preciso varrer
todo a lista de chaves para encontrarmos um valor. O esquema da Figura 3.2 mostra que a
chave e o valor de cada elemento são armazenados como tuplas. Essa abordagem é utilizada
tanto na análise semântica quanto no código gerado.
Figura 3.2: Dicionário de Processos
Os comandos básicos de manipulação do dicionário de processos, respectivamente de
inserção, captura e eliminação dos dados de uma chave são:
• put(Chave, Valor);
• get(Chave);
• erase(Chave).
Declaração e Atribuição de Variáveis
3.2.1
22
Análise Léxica
A implementação da análise léxica foi desenvolvida com a utilização da ferramenta
Leex e como já mencionado, os tokens são construı́dos no bloco de definições. A Tabela 3.1
ilustra os tokens criados na declaração e atribuição de variáveis do Código 3.2.1.
Definição
Integer
Float
Digit
Identifier
AttributionOp
AddOp
MultOp
Comma
Semicolon
Lexema
int
float
[0 − 9]
[a − zA − Z] [a − zA − Z0 − 9] ∗
=
( \+ | -)
( \* | /)
,
;
Tabela 3.1: Bloco de Definições
As regras referentes a essas atribuições (Código 3.2.3) retornam tuplas para
a
análise
sintática.
Basicamente,
a
estrutura
{token, {int_t, TokenLine,
list_to_atom(TokenChars)}} indica o nome, a linha e o atom correspondente a esse token
que a análise sintática receberá como entrada.
A função list_to_atom(), própria do Erlang, converte cadeias de caracteres em atoms.
1
2
Definitions.
...
3
4
Rules.
5
6
7
{Integer}
{Float}
: {token, {int_t,
: {token, {float_t,
TokenLine, list_to_atom(TokenChars)}}.
TokenLine, list_to_atom(TokenChars)}}.
{Digit}+
{Identifier}
: {token, {integer,
: {token, {identifier,
TokenLine, list_to_integer(TokenChars)}}.
TokenLine, list_to_atom(TokenChars)}}.
8
9
10
11
12
13
14
15
{AttributionOp} : {token, {list_to_atom(TokenChars), TokenLine}}.
{AddOp}
: {token, {add_op, TokenLine, list_to_atom(TokenChars)}}.
{MultOp}
: {token, {mult_op, TokenLine, list_to_atom(TokenChars)}}.
16
17
18
{Comma}
{Semicolon}
19
20
Erlang Code.
...
: {token, {list_to_atom(TokenChars), TokenLine}}.
: {token, {list_to_atom(TokenChars), TokenLine}}.
Código 3.2.3: Estrutura do arquivo jaraki lexer.xrl
Declaração e Atribuição de Variáveis
23
Após a definição de todos os blocos da análise léxica (Definições, Regras e Código
Erlang), abrangendo todos os lexemas da linguagem Java, podem ser obtidos a lista de
tokens geradas pela ferramenta Leex, como mostra o esquema do Código 3.2.4.
1
2
3
[{public,1,public}, {class,1,class}, {identifier,1,’Hello’},
{’{’,2}, ... , {’{’,4},
28
29
30
{int_t,5,int},
{identifier,5,a},
{’,’,5},
{identifier,5,b},
{’=’,5},
{integer,5,4},
{’;’,5},
{int_t,6,int},
{identifier,6,c},
{’;’,6},
{float_t,7,float},
{identifier,7,d},
{’;’,7},
{identifier,8,c},
{’=’,8},
{integer,8,2},
{’;’,8},
{identifier,9,c},
{’=’,9},
{identifier,9,a},
{mult_op,9,’/’},
{identifier,9,b},
{mult_op,9,’*’},
{identifier,9,a},
{add_op,9,’+’},
{identifier,9,b},
{’;’,9},
31
32
{’}’,10},{’}’,11}]
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Código 3.2.4: Lista de Tokens Gerados pelo Leex
3.2.2
Análise Sintática
Com a lista de tokens apresentada no Código 3.2.4, são reconhecidos os sı́mbolos terminais e os que definem novas regras são constituı́dos sı́mbolos não-terminais como estruturado na Tabela 3.2.
As regras (Código 3.2.5), assim como na análise léxica, também definem tuplas ou listas
de tuplas que caracterizam a árvore sintática (obtida pelo Yecc). Um não-terminal, como
exemplo a variable_list, pode gerar mais de uma regra.
A função unwrap() captura o terceiro elemento de uma tupla, por exemplo, na linha
5 a função recebe como parâmetro o token do primeiro elemento (identifier ). No caso de
uma declaração com atribuição, a tupla {identifier, Linha ,b} será contruı́da como
Declaração e Atribuição de Variáveis
Terminals
int t
float t
add op
mult op
integer
identifier
’,’ ’;’ ’=’
24
NonTerminals
local variable declaration statement
element value
type
variable list
element value pair
add expr mult expr
literal
Tabela 3.2: Sı́mbolos Terminais e Não-Terminais
retorno da análise léxica. Então, essa função retornará o identificador b. Essas e outras
implementações são desenvolvidas no bloco do Erlang Code, na criação de uma estrutura
mais simplificada e objetiva da árvore que será lida na análise semântica.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
local_variable_declaration_statement -> type variable_list ’;’ :
{var_declaration, {var_type, ’$1’}, {var_list, ’$2’}}.
variable_list -> identifier ’=’ element_value :
[{{var, unwrap(’$1’)}, {var_value, ’$3’}}].
variable_list -> identifier ’=’ element_value ’,’ variable_list :
[{{var, unwrap(’$1’)}, {var_value, ’$3’}} | ’$5’].
variable_list -> identifier :
[{{var, unwrap(’$1’)},{var_value, undefined}}].
variable_list -> identifier
’,’ variable_list :
[{{var, unwrap(’$1’)}, {var_value, undefined}} | ’$3’].
...
literal -> integer
: ’$1’.
literal -> identifier
: {var, line(’$1’), unwrap(’$1’)}.
Erlang Code.
unwrap({_, _, Value})
-> Value.
Código 3.2.5: Estrutura do arquivo jaraki parser.yrl
A árvore sintática gerada a partir da linguagem Java (Código 3.2.1) é a saı́da do Yecc e
sua estrutura é construı́da no bloco de regras. Essas regras precisam ser bem definidas, por
isso as tuplas tiveram grande utilidade especificando cada bloco da estrutura da linguagem
como observado no Código 3.2.6. A árvore sintática é nada mais que uma lista de tuplas
constituı́das a partir das regras e do gerador LALR-1.
3.2.3
Análise Semântica e Geração de Código
Após a análise léxica e sintática, é na fase de análise semântica que são verificados
os erros referentes à manipulação de variáveis e, a partir da análise semântica, é obtido
Declaração e Atribuição de Variáveis
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
25
[{class,
{1,{name,’Hello’},
{parent,null},
{body,[{method, {3, {return,{3,void}}, {name,main},
{modifiers,[public,static]},
[{3,{var_type,{3,{array,’String’}}},{parameter,’Args’}}],
{block,4,
[{var_declaration,
{var_type,{5,int}},
{var_list, [{{var,a},{var_value,undefined}},
{{var,b},{var_value,{integer,5,4}}}]}},
{var_declaration,
{var_type,{6,int}},
{var_list,[{{var,c},{var_value,undefined}}]}},
{var_declaration,
{var_type,{7,float}},
{var_list,[{{var,d},{var_value,undefined}}]}},
{8,attribution,{var,c},{var_value,{integer,8,2}}},
{9,attribution, {var,c}, {var_value, {op,9,’+’,
{op,9,’/’, {var,9,a}, {op,9,’*’,{var,9,b},{var,9,a}}},{var,9,b}}}}]}}}]}}}]
Código 3.2.6: Árvore sintática
também o código em Erlang. Na verdade, cria-se a estrutura da árvore sintática do Erlang
(AST) com a ajuda do módulo próprio do Erlang chamado erl_syntax. Então, o objetivo
é que haja todas as informações para a construção dessa árvore.
Primeiramente, para a análise semântica, dado um corpo de uma função, percorre-se
toda a estrutura da árvore sintática criada com o intuito de identificar tuplas de declarações
de variáveis, como pode ser visto na Figura 3.3. Essas tuplas foram criadas na análise
sintática com a identificação var_declaration, contendo também informações como linha,
tipo e uma ou mais variáveis que foram declaradas. É importante observar que podem
também ocorrer declarações envolvendo atribuições.
Figura 3.3: Declaração de Variáveis
Declaração e Atribuição de Variáveis
26
Módulo ST
Vários arquivos foram utilizados para a melhor manipulação e criação da árvore. O módulo de manipulação de variáveis, que possue relação direta com dicionário de processos, é
chamado de módulo ST (Figura 3.4). Sendo uma tupla definida como declaração de variá-
Figura 3.4: Módulo ST
veis (var_declaration), quando a árvore sintática criada é percorrida, é feita a inserção
da chave no dicionário por meio da função insert_var_list. A função get_declared
verifica se a variável já foi declarada e, caso já tenha sido, gera-se uma exceção, como
mostra o fluxograma da Figura 3.5. Para o tratamento de erros foi criado o módulo jaraki_exception. Esse módulo é chamado em quase todas as funções relacionadas às
variáveis para checagem de tipos. Nele é acoplado o retorno das verificações como saı́da
para o compilador caso ocorra erros como os listados abaixo:
• Variáveis não declaradas;
• Variáveis já declaradas;
• Incompatibilidade de Tipos.
Padrões
Tuplas identificadas como atribuição passam pelo módulo chamado gen_erl_code, especificamente na função match_statement, que casa esse tipo de padrão, ou seja, essa
Declaração e Atribuição de Variáveis
27
Figura 3.5: Fluxograma - Inserção de Variáveis
função tem como parâmetro esse tipo de estrutura de tupla. Como exemplo, a Figura 3.6
mostra uma atribuição simples onde a tupla obtida pela árvore sintática é de atribuição.
Neste exemplo também observa-se que no código gerado para o Erlang também é
utilizado o módulo ST, ou seja, é contruı́do a AST desse módulo para manipularmos as
atribuições em tempo de execução desse programa. A Figura 3.6 mostra a função cre-
Figura 3.6: Atribuição
ate_attribution, que retorna a AST do Erlang e seu valor também é convertido para a
AST por meio da função match_attr_expr.
Após essas e as demais construções da AST, o correspondente em Erlang do Código 3.2.1
é apresentado no Código 3.2.7.
Vetores
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
28
-module(jarakivar).
-compile(export_all).
main({{array, ’String’}, V_Args}) ->
st:new(),
st:get_new_stack({’JarakiVar’,
{main, [{array, ’String’}]}}),
st:put_value({{’JarakiVar’,
{main, [{array, ’String’}]}},
"Args"},
{{array, ’String’}, V_Args}),
begin
st:put_value({{’JarakiVar’,
{main, [{array, ’String’}]}},
"b"},
{int, trunc(4)})
end,
st:put_value({{’JarakiVar’,
{main, [{array, ’String’}]}},
"c"},
{int, trunc(2)}),
st:put_value({{’JarakiVar’,
{main, [{array, ’String’}]}},
"c"},
{int,
trunc(st:get_value({’JarakiVar’,
{main, [{array, ’String’}]}},
"a")
/
(st:get_value({’JarakiVar’,
{main, [{array, ’String’}]}},
"b")
*
st:get_value({’JarakiVar’,
{main, [{array, ’String’}]}},
"a"))
+
st:get_value({’JarakiVar’,
{main, [{array, ’String’}]}},
"b"))}),
st:destroy().
Código 3.2.7: Código Erlang - Declaração e Atribuição de Variáveis
3.3
Vetores
Para a implementação de vetores foram considerados declarações instanciadas e com
array inicializado. O Código 3.3.1 mostra um exemplo do uso de vetores na linguagem
Java e servirá como base nessa seção.
Várias questões foram estudadas em relação a essa implementação, como diferenciar as
estruturas de declaração, atribuição, ı́ndice do vetor, entre outros, mas o principal desafio
foi a passagem de vetor por referência, que será detalhada adiante na análise semântica.
A notação EBNF para as regras dessa gramática são descritas no Código 3.3.2. A linha
1 define regras de declaração de vetores para os casos de arrays instanciados (linha 8) e
Vetores
1
2
3
4
5
6
public class JarakiVetor
{
public static void main(String[] args) {
int[] vet1 = {1, 2, 3};
int[] vet2 = new int[3];
vet2[2] = vet1[1] + 1;
7
8
9
29
}
}
Código 3.3.1: Código Java - Declaração e Atribuição de Variáveis envolvendo Vetores
arrays inicializados (linha 9). O acesso ao array está vinculado na atribuição e é descrito
como um literal. Sua estrutura, na linha 26, pode ser chamada dentro de outros escopos,
como por exemplo um print em Java.
1
2
3
<local_variable_declaration_statement> ::=
<type> "[" "]" <array_declaration_list> {"," <array_declaration_list>} ";" |
<type> <array_declaration_list2> {"," <array_declaration_list2>} ";"
4
5
6
<array_declaration_list>
<array_declaration_list2>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
::= "identifier" [ "=" ("{" <array_initializer> "}" | <new_stmt>] )
::= "identifier" "[" "]" ["=" ( <new_stmt> | "{" <array_initializer> "}")]
<new_stmt>
<array_initializer>
::= "new" type "[" ("integer" | "identifier" | length_stmt ) "]"
::= <literal> [ "," <array_initializer> ]
<element_value_pair>
::= "identifier" "[" <element_value> "]" "=" <element_value> ";"
<element_value>
<bool_expr>
<comparation_expr>
<add_expr>
<mult_expr>
<modulus_expr>
<unary_expr>
<literal>
::=
::=
::=
::=
::=
::=
::=
::=
<type>
::= "int_t" | "long_t" | "float_t" | "double_t" |
"char_t" | "boolean_t" | "string_t"
<length_stmt>
<array_access>
::= "identifier" "." "length"
::= "identifier" "[" <element_value> "]"
<bool_expr>
<comparation_expr> | <comparation_expr> <bool_op> <bool_expr>
<add_expr> | <add_expr> <comparation_op> <comparation_expr>
<mult_expr> | <mult_expr> <add_op> <add_expr>
<modulus_expr> | <modulus_expr> <mult_op> <mult_expr>
<unary_expr> | <unary_expr> <modulus_op> <modulus_expr>
<add_op> <literal> | <literal> | <bool_op> <literal>
"integer" | "float" | "identifier" | <array_access>
Código 3.3.2: EBNF - Vetores
Módulo Array
O módulo do Erlang Array foi a abordagem utilizada na implementação dos vetores. O
array, criado em Erlang, pode ter tamanho fixo ou pode crescer automaticamente, conforme
for necessário. Também pode ser definido um valor padrão para todos os ı́ndices do vetor
[Erlang2012a].
Vetores
30
1
2
%erl
3
4
5
1> A1 = array:new(2, {default, 0}).
{array,2,0,0,10}
6
7
8
2> A2 = array:set(0, 1, A1).
{array,2,0,0,{1,0,0,0,0,0,0,0,0,0}}
9
10
3> A3 = array:set(1, 2, A2).
{array,2,0,0,{1,2,0,0,0,0,0,0,0,0}}
11
12
13
14
15
16
4> A4 = array:set(2, 3, A3).
** exception error: bad argument
in function array:set/3
5> A4 = array:get(1, A3).
Código 3.3.3: Módulo Array
Para exemplificar, os principais comandos do módulo Array são mostrados no Código 3.3.3, onde A1 é um vetor de 3 posições (0 - 2), inicializado com valor 0. Para a
atribuição foi utilizado a função set() com os parâmetros posição, valor e o nome do vetor
criado, respectivamente. A função get() é utilizada para a captura de um ı́ndice de um
vetor. Quando ocorre um acesso a uma posição fora dos limites do vetor é retornado uma
mensagem de erro.
3.3.1
Análise Léxica
As principais definições do Código 3.3.1 relacionadas a estrutura do vetor podem ser
observadas na Tabela 3.3. As regras permanecem quase as mesmas, acrescentando-se apenas o OpenBrackets, CloseBrackets e New. Alguns lexemas devem ser escapados, como
o sı́mbolo “[”, por pertencer à linguagem.
Definição
Integer
OpenBrackets
CloseBrackets
Digit
Identifier
AttributionOp
AddOp
New
Lexema
int
\[
\]
[0 − 9]
[a − zA − Z] [a − zA − Z0 − 9] ∗
=
( \+ | -)
new
Tabela 3.3: Leex - Bloco de Definições do Vetor
Vetores
31
Os tokens gerados por meio da ferramenta Leex para o Código 3.3.1 podem ser consultados no Código 3.3.4. Os sı́mbolos da linguagem fonte, na sua maioria, são retornados
para a análise léxica como a tupla {Sı́mbolo, Linha}.
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
[{public,1,public},
...
{int_t,4,int},
{’[’,4}, {’]’,4}, {identifier,4,vet1},
{’=’,4}, {’{’,4},
{integer,4,1},
{’,’,4},
{integer,4,2},
{’,’,4},
{integer,4,3},
{’}’,4}, {’;’,4},
{int_t,5,int},
{’[’,5}, {’]’,5}, {identifier,5,vet2},
{’=’,5},
{new,5,new},
{int_t,5,int},
{’[’,5}, {integer,5,3}, {’]’,5},
{’;’,5},
{identifier,7,vet2},
{’[’,7},{integer,7,2}, {’]’,7},
{’=’,7},
{identifier,7,vet1},
{’[’,7},
{integer,7,1},
{’]’,7},
{add_op,7,’+’},
{integer,7,1},
{’;’,7}, {’}’,8}, {’}’,9}]
Código 3.3.4: Lista de Tokens Gerados pelo Leex
3.3.2
Análise Sintática
Apesar da inicial dificuldade em se definir as regras e a melhor forma de descrição
das tuplas para o vetor, foram desenvolvidas no Yecc as regras, algumas expostas no
Código 3.3.5. Várias regras foram definidas para o vetor para abranger um grande número
de códigos compiláveis do Java para o Erlang. Declarações do tipo int[ ] vetor também
podem ser escritas como int vetor[ ] porém, com significados diferentes. As regras
também dão suporte ao vetor.length(), que retorna o tamanho do vetor, oriundo do Java
A função make_array_type especifica que o tipo é também de um vetor, essencial para a
posterior checagem de tipos na análise semântica. No acesso a um vetor, o ı́ndice pode ser
um element_value como indicado nas linha 25, ou seja, podem ser colocados expressões
matemáticas, estas que já foram tratadas em algumas regras da análise sintática explicado
Vetores
32
1
2
3
Rules
...
4
5
6
local_variable_declaration_statement -> type ’[’ ’]’ array_declaration_list’;’:
{var_declaration,
{var_type, make_array_type(’$1’)},
{var_list, ’$4’}}.
7
8
9
10
11
array_declaration_list -> identifier:
[{{var, unwrap(’$1’)},{var_value, undefined}}].
12
13
14
array_declaration_list -> identifier ’,’ array_declaration_list:
[{{var, unwrap(’$1’)}, {var_value, undefined}} | ’$3’].
15
16
array_declaration_list -> identifier ’=’ ’{’ array_initializer ’}’:
[{{var, unwrap(’$1’)}, {var_value, {array_initializer, ’$4’}}}].
17
18
19
array_declaration_list -> identifier ’=’ new_stmt:
[{{var, unwrap(’$1’)}, {var_value, ’$3’}}].
20
21
22
new_stmt -> ’new’ type ’[’ integer ’]’:
{new, array, {type, ’$2’}, {index, ’$4’}}.
23
24
25
26
27
array_access ->
identifier ’[’ element_value ’]’ :
{{var, line(’$1’), unwrap(’$1’)}, {index, ’$3’}}.
28
29
30
element_value -> bool_expr
...
literal
-> array_access
: ’$1’.
31
32
33
Erlang code
34
make_array_type({Line, PrimitiveType}) -> {Line, {array, PrimitiveType}}.
: ’$1’.
Código 3.3.5: Estrutura do arquivo jaraki parser.yrl
na sessão anterior. A árvore sintática gerada pelo Yecc, no Código 3.3.6, contém a lista de
tuplas construı́das dado essas regras.
A atribuição do array é identificada como array_attribution, ou seja, ele recebe uma
expressão. Como já foi dito, a regra element_value deriva de expressões aritméticas e,
como pode ser observado, nas regras, um array também é derivado da mesma. Isso possibila
que ocorram qualquer tipo de expressões envolvendo vetores e variáveis (Ex: a = vet[i],
vet[2] = a + 2).
3.3.3
Análise Semântica e Geração de Código
Como já foi descrito, a primeira estrutura a ser verificada é o armazenamento de variáveis no dicionário. Na análise semântica, essa implementação segue o mesmo fluxo já
explicado na sessão anterior.
Em relação ao vetor, o tratamento em termos de geração de código se altera um pouco
Vetores
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
33
[{class,
...
{block,3,
{var_declaration,
{var_type,{4,{array,int}}},
{var_list,
[{{var,vet1},
{var_value,
{array_initializer,
[{array_element,{integer,4,1}},
{array_element,{integer,4,2}},
{array_element,{integer,4,3}}]}}}]}},
{var_declaration,
{var_type,{5,{array,int}}},
{var_list,
[{{var,vet2},
{var_value,
{new,array,
{type,{5,int}},
{index,{integer,5,3}}}}}]}},
{7,array_attribution,
{{var,vet2},{index,{integer,7,2}}},
{var_value,
{op,7,’+’,
{{var,7,vet1},{index,{integer,7,1}}},
{integer,7,1}}}}]}}}]}}}]
Código 3.3.6: Árvore Sintática
já que o vetor deve ser passado por referência, ou seja, em tempo de execução o tratamento
para o tipo de variável vetor é diferente. Algumas considerações foram feitas e a abordagem
escolhida foi a criação de um módulo chamado Vector.
Módulo Vector
O módulo Vector acopla o módulo Array do Erlang. Por meio desse módulo é criado
um “endereço de memória” para cada vetor criado. Diferente das variáveis, o retorno da
busca de um vetor no módulo ST deve retornar esse endereço.
A Figura 3.7 mostra o fluxo de criação de um vetor instanciado com 10 posições. Quando
ele é criado, seu valor é o retorno do módulo Array do Erlang, ou seja, um array fixo com
10 posições inicializado com valores 0.
A primeira etapa dessa abordagem é atualizar um contador de vetores, onde isso é
feito para cada novo vetor. Basicamente, esse contador faz com que cada vetor tenha um
número ligado a ele, que é único. Depois que isso ocorre, o vetor é inserido no dicionário de
processos por meio do módulo ST com uma estrutura diferente da variável, onde sua chave
é uma tupla {array_dict, Endereço} e seu valor o array que veio como parâmetro na
função new_vector. O retorno dessa função é o endereço, ou seja, um número. Em tempos
Matrizes
34
Figura 3.7: Módulo Vector
de execução isso se torna essencial para a passagem do vetor como parâmetro como ocorre
na linguagem Java.
Quando o vetor for do tipo inicializado, primeiramente devem ser feitas todas as inserções para estrutura array do Erlang e só depois é criado o array por meio da função
new_vector.
Declaração de Array
A análise sintática retorna uma única ou uma lista de tuplas identificadas como
{var_declaration, ... } quando uma variável é declarada, assim como o vetor. Essa
lista de variáveis podem conter uma atribuição, um array instanciado, array inicializado
ou os três. O que diferencia cada um é a identificação do seu valor na tupla. O esquema na
(Figura 3.8) mostra como a função pode ser desmembrada apenas com essa identificação.
Como o objetivo ao final da análise semântica é criar os elementos da AST, não se
utiliza o módulo Array do Erlang nessa fase, mas foi criado o elemento AST do módulo
Vector que, em tempo de execução estará vinculado a esse módulo.
O código gerado em Erlang dentro desse escopo é mostrado no Código 3.3.7.
3.4
Matrizes
Para a implementação de matriz também foram considerados as matrizes instanciadas
e inicializadas, assim como, a atribuição envolvendo matrizes, vetores e variáveis. O Có-
Matrizes
35
Figura 3.8: Declarações de Array
digo 3.4.1 mostra os dois tipos de declaração e uma atribuição simples com matrizes. Ele
será utilizado como código-base nessa seção.
As matrizes também são passadas por referência e também utiliza a abordagem do
módulo Array do Erlang. Como a matriz é um vetor de vetor, a EBNF da sua gramática
(Código 3.4.2) é similar ao que foi desenvolvido para o array. Praticamente, as alterações
foram feitas em relação ao tipo, a inicialização da matriz e algumas considerações inerentes
à estrutura da matriz.
3.4.1
Análise Léxica
Para a análise léxica, as principais definições já foram descritas nas sessões anteriores.
A lista de tokens gerados, com base no Código 3.4.1, pela ferramenta Leex são descritas
no Código 3.4.3. As linhas 3 a 14 mostram os tokens gerados para a mat1 e, para a matriz
instanciada, mat2, nas linhas 15 a 22.
3.4.2
Análise Sintática
Para a análise sintática foram definidos também, assim como vetor, regras que tivessem
suporte para declarações como int[][] matriz ou int matriz[][], matriz.length(),
estruturas de acesso a matriz e atribuições envolvendo o mesmo. O tipo da matriz também
é tratada para verificações do módulo jaraki_exception na análise semântica. As regras
oriundas do Yecc são vistas no Código 3.4.4. É importante observar que a estrutura é bem
similar ao vetor, onde também é passado um ı́ndice para a análise semântica, sendo que
Matrizes
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
36
-module(jarakivetor).
-compile(export_all).
-import(vector, [new/1, get_vector/1]).
main({{array, ’String’}, V_args}) ->
st:new(),
st:get_new_stack({’JarakiVetor’,
{main, [{array, ’String’}]}}),
st:put_value({{’JarakiVetor’,
{main, [{array, ’String’}]}},
"args"},
{{array, ’String’}, V_args}),
begin
begin
st:put_value({{’JarakiVetor’,
{main, [{array, ’String’}]}},
"vet1"},
{{array, int}, vector:new(array:from_list([1, 2, 3]))})
end
end,
begin
st:put_value({{’JarakiVetor’,
{main, [{array, ’String’}]}},
"vet2"},
{int, vector:new_vector(3)})
end,
st:put_value({{’JarakiVetor’,
{main, [{array, ’String’}]}},
"vet2"},
{{array, int},
vector:set_vector(2,
vector:access_vector(1,
st:get_value({’JarakiVetor’,
{main,
[{array,’String’}]}},
"vet1"))
+ 1,
st:get_value({’JarakiVetor’,
{main, [{array, ’String’}]}},
"vet2"))}),
st:destroy().
Código 3.3.7: Código Erlang - Vetor
1
2
3
4
5
6
7
8
public class JarakiMatriz {
public static void main(String[] args) {
int[][] mat1 = {{1, 2}, {3, 4}};
int[][] mat2 = new int[2][2];
mat1[0][0] = 9;
}
}
Código 3.4.1: Código Java - Matriz
a tupla {index, {row, LinhaMatriz}, {column, ColunaMatriz} } identifica o ı́ndice
com a linha e coluna da matriz.
Tendo como considerações as regras definidas, a árvore gerada está descrita no Código 3.4.5.
Para cada linha da matriz é criado uma tupla identificada como {ma-
Matrizes
37
1
2
3
<local_variable_declaration_statement> ::=
<type> "[" "]" "[" "]" <array_declaration_list> {"," <array_declaration_list>} ";" |
<type> <array_declaration_list3> {"," <array_declaration_list3>} ";"
4
5
6
<array_declaration_list> ::= "identifier" [ "=" ("{" <array_initializer> "}" | <new_stmt> )]
<array_declaration_list2>::= "identifier" "[" "]" "[" "]" ["=" ( <new_stmt> | "{" <array_initializer> "}")
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<new_stmt>
::= "new" type "[" ("integer" | "identifier" ) "]" "[" ("integer" | "identifier" ) "]"
<array_initializer> ::= (<literal> | "{" <array_initializer> "}") {"," <array_initializer>}
<element_value_pair>::= "identifier" "[" <element_value> "]" "[" <element_value> "]" "=" <element_value> ";
<element_value>
<bool_expr>
<comparation_expr>
<add_expr>
<mult_expr>
<modulus_expr>
<unary_expr>
<literal>
::=
::=
::=
::=
::=
::=
::=
::=
<type>
::= "int_t" | "long_t" | "float_t" | "double_t" |
"char_t" | "boolean_t" | "string_t"
<length_stmt>
<array_access>
::= "identifier" "." "length"
::= "identifier" "[" <element_value> "]" "[" <element_value> "]"
23
24
25
26
<bool_expr>
<comparation_expr> | <comparation_expr> <bool_op> <bool_expr>
<add_expr> | <add_expr> <comparation_op> <comparation_expr>
<mult_expr> | <mult_expr> <add_op> <add_expr>
<modulus_expr> | <modulus_expr> <mult_op> <mult_expr>
<unary_expr> | <unary_expr> <modulus_op> <modulus_expr>
<add_op> <literal> | <literal> | <bool_op> <literal>
"integer" | "float" | "identifier" | <array_access>
Código 3.4.2: EBNF - Matrizes
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
[{public,1,public},
...
{int_t,3,int},
{’[’,3}, {’]’,3},
{’[’,3}, {’]’,3},
{identifier,3,mat1}, {’=’,3},
{’{’,3}, {’{’,3},
{integer,3,1}, {’,’,3},
{integer,3,2}, {’}’,3},
{’,’,3},
{’{’,3},
{integer,3,3}, {’,’,3},
{integer,3,4}, {’}’,3},
{’}’,3},
{’;’,3},
{int_t,4,int},
{’[’,4}, {’]’,4},
{’[’,4}, {’]’,4},
{identifier,4,mat2},
{’=’,4},
{new,4,new}, {int_t,4,int},
{’[’,4}, {integer,4,2}, {’]’,4},
{’[’,4}, {integer,4,2}, {’]’,4},
{’;’,4},
{identifier,6,mat1},
{’[’,6}, {integer,6,0}, {’]’,6},
{’[’,6}, {integer,6,0}, {’]’,6},
{’=’,6}, {integer,6,9}, {’;’,6},
{’}’,7},
{’}’,8}]
Código 3.4.3: Lista de Tokens Gerados pelo Leex
Matrizes
1
2
3
4
5
6
7
8
38
Rules
local_variable_declaration_statement -> type ’[’ ’]’ ’[’ ’]’ array_declaration_list’;’:
{var_declaration,
{var_type, make_matrix_type(’$1’)},
{var_list, ’$6’}}.
array_declaration_list -> identifier:
[{{var, unwrap(’$1’)},{var_value, undefined}}].
9
10
11
array_declaration_list -> identifier ’,’ array_declaration_list:
[{{var, unwrap(’$1’)}, {var_value, undefined}} | ’$3’].
12
13
14
array_declaration_list -> identifier ’=’ ’{’ array_initializer ’}’:
[{{var, unwrap(’$1’)}, {var_value, {array_initializer, ’$4’}}}].
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
array_declaration_list -> identifier ’=’ new_stmt:
[{{var, unwrap(’$1’)}, {var_value, ’$3’}}].
new_stmt -> ’new’ type ’[’ integer ’]’ ’[’ integer ’]’:
{new, array, {type, ’$2’}, {index, {row, ’$4’}, {column, ’$7’}}}.
element_value_pair ->
identifier ’[’ element_value ’]’ ’[’ element_value ’]’’=’
element_value ’;’:
{line(’$1’), array_attribution,
{{var, unwrap(’$1’)},{index, {row, ’$3’}, {column, ’$6’}} },
{var_value, ’$9’}}.
array_access ->
identifier ’[’ element_value ’]’ ’[’ element_value ’]’ :
{{var, line(’$1’), unwrap(’$1’)},
{index, {row, ’$3’}, {column, ’$6’} } }.
element_value -> bool_expr
...
literal
-> array_access
: ’$1’.
: ’$1’.
Erlang code
make_matrix_type({Line, PrimitiveType}) -> {Line, {matrix, PrimitiveType}}.
Código 3.4.4: Estrutura do arquivo jaraki parser.yrl
trix_element, ...} com seus respectivos elementos (array_element).
3.4.3
Análise Semântica e Geração de Código
Para a implementação dessa estrutura de matrizes também foi utilizado o módulo array
do Erlang, apesar de que ele trata apenas vetores. A Figura 3.9, mostra a abordagem
utilizada onde é criado um vetor principal que indica as linhas da matriz e, para cada
posição da linha, é criado um vetor indicando as colunas.
Para tratar e criar esse array de array foi criado um módulo chamado Matrix.
Matrizes
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
39
[{class,
...
[{var_declaration,
{var_type,{3,{matrix,int}}},
{var_list,
[{{var,mat1},
{var_value,
{array_initializer,
[{matrix_element,
[{array_element,{integer,3,1}},
{array_element,{integer,3,2}}]},
{matrix_element,
[{array_element,{integer,3,3}},
{array_element,{integer,3,4}}]}]}}}]}},
{var_declaration,
{var_type,{4,{matrix,int}}},
{var_list,
[{{var,mat2},
{var_value,
{new,array,
{type,{4,int}},
{index,{row,{integer,4,2}},{column,{integer,4,2}}}}}}]}},
{6,array_attribution,
{{var,mat1},{index,{row,{integer,6,0}},{column,{integer,6,0}}}},
{var_value,{integer,6,9}}}]}}}]}}}]
Código 3.4.5: Árvore Sintática
Figura 3.9: Array de Array
Módulo Matrix
O módulo Matrix fornece todo o suporte para o código gerado, criando vetores, linhas
e colunas, dado a ordem da matriz. O exemplo da Figura 3.10 mostra um código gerado a
partir do código Java que instancia um vetor de ordem 2. Nota-se que a análise semântica
gera o vı́nculo com o módulo Matrix por meio da função creation_matrix. Fundamentalmente, essa função cria um vetor linha e um de coluna com valores iniciais iguais a zero.
Matrizes
40
Captura-se o tamanho do vetor linha para ser criado um vetor coluna para cada posição.
Após isso ocorrer, com o mesmo fluxo já explicado para o vetor, é obtido um “endereço de
memória” para essa matriz.
Figura 3.10: Módulo Matrix
O fluxo da Figura 3.11 mostra que, como a matriz de ordem 2 dada como exemplo
contém posições 0 e 1, o tamanho da linha é definido com valor 1. Posteriormente, com
o vetor de colunas já criado, atualiza-se o vetor linha na posição 0 com esse vetor. Caso
a posição da linha ainda não seja igual ao tamanho da linha, é chamada a mesma função recursivamente com a posição de linha agora incrementada. Nessa posição também
atualiza-se o vetor linha, mas agora na posição 1.
O módulo Matrix também atualiza valores em tempos de execução para casos de atribuição, dando todo o suporte básico das estruturas de matriz da linguagem Java. O
Código 3.4.6 foi gerado em Erlang tendo como base o código em Java dado como base
nessa seção e apresenta algumas dessas estruturas do módulo Matrix.
Matrizes
41
Figura 3.11: Fluxo Matrix
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
-module(jarakimatriz).
-compile(export_all).
-import(matrix, [new_matrix/1, creation_matrix/2]).
main({{array, ’String’}, V_args}) ->
st:new(),
st:get_new_stack({’JarakiMatriz’,
{main, [{array, ’String’}]}}),
st:put_value({{’JarakiMatriz’,
{main, [{array, ’String’}]}},
"args"},
{{array, ’String’}, V_args}),
begin
begin
st:put_value({{’JarakiMatriz’,
{main, [{array, ’String’}]}},
"mat1"},
{{matrix, int},
matrix:new_matrix(array:from_list([array:from_list([1, 2]),
array:from_list([3, 4])]))})
end
end,
begin
st:put_value({{’JarakiMatriz’,
{main, [{array, ’String’}]}},
"mat2"},
{int, matrix:creation_matrix(2, 2)})
end,
st:put_value({{’JarakiMatriz’,
{main, [{array, ’String’}]}},
"mat1"}, {{matrix, int},
matrix:set_matrix(0, 0, 9,
st:get_value({’JarakiMatriz’,
{main, [{array, ’String’}]}},
"mat1"))}),
st:destroy().
Código 3.4.6: Código Erlang - Matrix
Capı́tulo 4
Testes
Neste capı́tulo constam alguns arquivos testes em java, a árvore gerada pelo compilador
Jaraki, a árvore abstrata do Erlang do código gerado e a comparação dos tempos de compilação e execução das linguagens envolvidas inerentes às principais implementações dentro
do escopo deste trabalho.
4.1
Processo de Compilação
Para os processos de compilação e execução de cada teste contido neste capı́tulo foram
utilizados o sistema operacional GNU/Linux 11.04 (natty), Kernel 2.6.38-15-generic e as
seguintes configurações de hardware:
• Computador: Hp Pavilion dv4
• Processador: Core i5 2.27GHz
• Memória: 4GB
O comando time foi utilizado na compilação e execução de ambas as linguagens. Na
compilação de testes da linguagem Java, foi usado o comando javac e, para execução, o
comando java. Em Erlang foi criado um script para a compilação e outro para a execução
dos testes gerados, onde para cada teste deve ser escrito o comando ./jkc caminhoArquivo
e na execução ./jk caminhoArquivo .
As versões dos softwares de cada linguagem são descritas abaixo:
• Java: 1.6.0 24
• Erlang: V5.8.4
Processo de Compilação
43
O Código 4.1.1 apresenta a estrutura base da utilização de variáveis em um código em
Java, nele é calculado a média de duas notas. Como a variável média foi declarada como
inteiro, o resultado dessa operação também deverá ser inteiro.
1
2
3
4
5
6
public class Variaveis {
public static void main(String args[]){
int media, nota1, nota2;
nota1 = 7;
nota2 = 8;
media = (nota1 + nota2)/2;
7
8
9
10
System.out.println(media);
}
}
Código 4.1.1: Código Java - Cálculo Média
4.1.1
Árvore Sintática do compilador Jaraki - JAST
A árvore criada a partir da análise sintática da ferramenta Yecc para o compilador
Jaraki pode ser vista no Código 4.1.2. A JAST foi construı́da a partir das regras da análise
sintática para representar o parser do código em Java.
A árvore foi criada seguindo o modelo da árvore sintática do Erlang, ou seja, a JAST
é constituı́da por uma lista de tuplas que representam as principais construções como, por
exemplo, a declaração e a atribuição de variáveis.
A estrutura de declaração inicia na linha 13, definida na tupla var_declaration, e para
a mesma são capturados o tipo e todas as variáveis dessa declaração, uma lista de variáveis.
A tupla que define a atribuição da média inicia na linha 21 e a tupla {var_value,..} contém
toda a operação, que é construı́da dessa forma para facilitar o processo de análise semântica.
4.1.2
Árvore Sintática do Erlang - AST
Parte da AST (Árvore Sintática do Erlang) pode ser visualizada no Código 4.1.3. Por
meio dessa árvore pode ser construı́do a estrutura similar do código Java em Erlang. A
estrutura, nesse caso de declaração, contruı́da na análise semântica como suporte a declaração de variáveis no Erlang é a implementação do dicionário de processos do Erlang. A
linha 6, por exemplo, contém a tupla {remote,17,{atom,17,st},{atom,17,put_value}},
Teste 1 - Variáveis
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
44
[{class,
{1,
{name,’Variaveis’},
{parent,null},
{body,
[{method,
{2,
{return,{2,void}},
{name,main},
{modifiers,[public,static]},
[{2,{var_type,{2,{array,’String’}}},{parameter,args}}],
{block,2,
[{var_declaration,
{var_type,{3,int}},
{var_list,
[{{var,media},{var_value,undefined}},
{{var,nota1},{var_value,undefined}},
{{var,nota2},{var_value,undefined}}]}},
{4,attribution,{var,nota1},{var_value,{integer,4,7}}},
{5,attribution,{var,nota2},{var_value,{integer,5,8}}},
{6,attribution,
{var,media},
{var_value,
{op,6,’/’,
{op,6,’+’,{var,6,nota1},{var,6,nota2}},
{integer,6,2}}}},
{8,println,[{identifier,8,media}]}]}}}]}}}]
Código 4.1.2: JAST - Cálculo Média
que gera o código que chama o módulo ST e, por meio da função put_value, armazena a
variável no dicionário, assim como, seu tipo, escopo e valor.
O Código 4.1.4 foi gerado pelo compilador Jaraki e contém a estrutura do código fonte
em Java com a utilização do dicionário de processos. Como o resultado do cálculo da média
será um valor float, o elemento trunc(linha 19) arrendonda o valor obtido.
Observando o código gerado, podemos visualizar a manipulação das variáveis por meio
do módulo ST onde cada variável declarada tem seu valor inserido (st:put_value) por
meio de uma chave e um valor. A chave, por exemplo para a variável nota1 (linhas 10 a
12), é a tupla {Escopo, NomeVariavel} e o seu valor Tipo, ValorVariavel.
4.2
Teste 1 - Variáveis
Os tempos de compilação e execução, respectivamente, do código de cálculo da média
em Java são observados na Figura 4.1 e Figura 4.2.
O código gerado em Erlang também apresenta o mesmo resultado, porém com tempos
de compilação (Figura 4.3) e execução (Figura 4.4) diferentes.
Teste 2 - Strings
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
45
[{attribute,1,file,{"variaveis.erl",1}},
...,
{string,14,"nota2"}]},
{tuple,15,[{atom,15,int},{call,15,{atom,15,trunc},[{integer,15,8}]}]}]},
{call,17,
{remote,17,{atom,17,st},{atom,17,put_value}},
[{tuple,17,
[{tuple,17,
[{atom,17,’Variaveis’},
{tuple,18,
[{atom,18,main},
{cons,18,
{tuple,18,[{atom,18,array},{atom,18,’String’}]},
{nil,18}}]}]},
{string,18,"media"}]},
{tuple,19,
[{atom,19,int},
{call,19,
{atom,19,trunc},
[{op,22,’/’,
{op,21,’+’,
{call,19,
{remote,19,{atom,19,st},{atom,19,get_value}},
[{tuple,19,
[{atom,19,’Variaveis’},
{tuple,20,
[{atom,20,main},
{cons,20,
{tuple,20,[{atom,20,array},{atom,20,’String’}]},
{nil,20}}]}]},
{string,20,"nota1"}]},
{call,21,
{remote,21,{atom,21,st},{atom,21,get_value}},
[{tuple,21,
[{atom,21,’Variaveis’},
{tuple,22,
[{atom,22,main},
{cons,22,
{tuple,22,[{atom,22,array},{atom,22,’String’}]},
{nil,22}}]}]},
{string,22,"nota2"}]}},
{integer,22,2}}]}]}]},
...,
{call,26,{remote,26,{atom,26,st},{atom,26,destroy}},[]}]}]},
{eof,27}]
Código 4.1.3: AST do Erlang - Cálculo Média
Figura 4.1: Tempo de Compilação em Java - Cálculo Média
4.3
Teste 2 - Strings
O Código 4.3.1 mostra um arquivo teste que utiliza o tipo de variável String. Esse
código contém concatenação de strings e imprime as strings “UEA”e “UEA String”.
Teste 2 - Strings
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
46
-module(variaveis).
-compile(export_all).
main({{array, ’String’}, V_args}) ->
st:new(),
st:get_new_stack({’Variaveis’, {main, [{array, ’String’}]}}),
st:put_value({{’Variaveis’,
{main, [{array, ’String’}]}}, "args"},
{{array, ’String’}, V_args}),
st:put_value({{’Variaveis’,
{main, [{array, ’String’}]}}, "nota1"},
{int, trunc(7)}),
st:put_value({{’Variaveis’,
{main, [{array, ’String’}]}}, "nota2"},
{int, trunc(8)}),
st:put_value({{’Variaveis’,
{main, [{array, ’String’}]}}, "media"},
{int, trunc((st:get_value({’Variaveis’,
{main, [{array, ’String’}]}}, "nota1")
+ st:get_value({’Variaveis’,
{main, [{array, ’String’}]}}, "nota2")) / 2)}),
io:format("~p~n",
[st:get_value({’Variaveis’,
{main, [{array, ’String’}]}}, "media")]),
st:destroy().
Código 4.1.4: Código Erlang - Cálculo Média
Figura 4.2: Tempo de Execução em Java - Cálculo Média
Figura 4.3: Tempo de Compilação em Erlang - Cálculo Média
Figura 4.4: Tempo de Execução em Erlang - Cálculo Média
Teste 2 - Strings
1
2
public class String1 {
public static void main(String[] args) {
String u = "U";
String e = "E";
String a = "A";
String uea, teste;
uea = u + e + a;
teste = uea + " " + "String";
System.out.println(uea);
System.out.println(teste);
}
3
4
5
6
7
8
9
10
11
12
13
47
}
Código 4.3.1: Código Java - String
Observa-se na Figura 4.5 o tempo de compilação em java de 0,706s. A Figura 4.6 mostra
a saı́da, resultado da concatenação de três variáveis e também uma concatenação direta
envolvendo strings, com tempo de execução de 0,079s.
Figura 4.5: Tempo de Compilação em Java - String
Figura 4.6: Tempo de Execução em Java - String
O código em Erlang, apresentado no apêndice, contém a estrutura criada, como suporte a strings com a utilização do dicionário de processos implementado. O sı́mbolo ++
representa a concatenação de strings em Erlang.
A Figura 4.7 mostra o resultado da compilação que gera o arquivo em Erlang, cujo
tempo é de 0,302s. Na execução desse arquivo, Figura 4.8, a mesma saı́da do algoritmo é
gerada com tempo de execução de 0,169s.
Teste 3 - Char
48
Figura 4.7: Tempo de Compilação em Erlang - String
Figura 4.8: Tempo de Execução em Erlang - String
4.4
Teste 3 - Char
O Código 4.4.1 contém uma estrutura simples, referente ao uso do tipo char em java
tendo como saı́da as duas variáveis declaradas como char.
1
2
3
4
5
public class Char1 {
public static void main(String args []){
char a = ’A’;
char b = ’B’;
System.out.println("Char: " + a);
System.out.println("Char: " + b);
6
7
8
9
10
}
}
Código 4.4.1: Código Java - Char
A Figura 4.9 mostra o tempo de compilação em Java de 0,724s e, na Figura 4.10,
podemos observar um tempo de execução de 0,074s.
Figura 4.9: Tempo de Compilação em Java - Char
Teste 4 - Vetores
49
Figura 4.10: Tempo de Execução em Java - Char
O código gerado em Erlang, apresentado no apêndice, contém a declaração com atribuição do char seguindo a estrutura da definição do char em Erlang, onde por exemplo,
para a variável “a” é armazenado no dicionário “$A”, ou seja, seu código em ASCII .
A Figura 4.11 apresenta o tempo de compilação em Erlang de 0,316s e na execução do
código gerado em Erlang foi obtido o mesma saı́da em Java com tempo de execução de
0,155s.
Figura 4.11: Tempo de Compilação em Erlang - Char
Figura 4.12: Tempo de Execução em Erlang - Char
4.5
Teste 4 - Vetores
Como exemplos para o suporte a vetores, 2 testes foram implementados em java.
4.5.1
Bubblesort
O primeiro código implementa o algoritmo do método de ordenação bolha, bubblesort,
como mostra o Código 4.5.1, que percorre um vetor de inteiros comparando elementos
Teste 4 - Vetores
50
adjacentes.
1
2
3
4
5
public class BubbleSort {
public static int[] bubbleSort(int[] vetor){
int aux;
int temp;
for (int i = 0; i < vetor.length; i++) {
for (int j = 0; j < vetor.length - 1; j++) {
temp = j+1;
if (vetor[j] > vetor[temp]) {
aux = vetor[j];
vetor[j] = vetor[temp];
vetor[temp] = aux;
}
}
}
return vetor;
6
7
8
9
10
11
12
13
14
15
16
17
18
}
19
20
21
public static void main(String[] args) {
int[] vetor = {5,2,1,4,3};
vetor = bubbleSort(vetor);
22
23
24
for (int i = 0; i < vetor.length; i++) {
System.out.print(vetor[i]+" ");
}
25
26
27
}
}
Código 4.5.1: Código Java - Método de ordenação bolha
O tempo de compilação e execução desse código em Java pode ser verificado na Figura 4.13, assim como a saı́da dos elementos ordenados para o vetor de 5 elementos dado
como entrada.
Figura 4.13: Tempo de Compilação e Execução em Java - bubblesort
O Código em Erlang gerado a partir do código bubblesort em Java está no apêndice
deste trabalho e apresenta a mesma saı́da de Java sendo que o tempo de compilação em
Erlang é de 0.690s(Figura 4.14) e tempo de execução de 0,77s (Figura 4.15).
Teste 4 - Vetores
51
Figura 4.14: Tempo de Compilação em Erlang - bubblesort
Figura 4.15: Tempo de Execução em Erlang - bubblesort
4.5.2
Quicksort
O segundo código desenvolvido na linguagem Java (Código 4.5.2) implementa o algoritmo de ordenação de vetores quicksort, que adota a estratégia de divisão e conquista.
O código em Java apresentou o tempo de compilação e execução para a entrada de
12 elementos e um vetor de 0,686s e 0,072s, respectivamente, como pode ser visto na
Figura 4.16.
Figura 4.16: Tempo de Compilação e Execução em Java - quicksort
Para o algoritmo quicksort em Erlang também foi obtido a mesma saı́da como em Java,
mas com um tempo de compilação(Figura 4.17) e execução(Figura 4.18) de aproxidamente
0,382s e 0,156, respectivamente.
Matrizes
1
2
3
4
5
6
7
8
9
10
public class QuickSort {
public static int[] quick_sort(int[] v,int ini, int fim) {
int meio;
if (ini < fim) {
meio = partition(v, ini, fim);
v= quick_sort(v, ini, meio);
v = quick_sort(v, meio + 1, fim);
}
return v;
}
11
12
13
public static int partition(int []v, int ini, int fim) {
int pivo, topo;
pivo = v[ini];
topo = ini;
for (int i = ini + 1; i < fim; i++) {
if (v[i] < pivo) {
v[topo] = v[i];
v[i] = v[topo + 1];
topo++;
}
}
v[topo] = pivo;
return topo;
}
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void main(String[] args) {
int[] vet = {8,5,2,7,1,10,4,3,6,50,9,25};
int[] vet1;
vet1 = quick_sort(vet, 0, vet.length);
27
28
29
30
31
32
for(int i=0; i< vet.length; i++)
System.out.print(vet1[i] + " ");
33
34
35
52
}
}
Código 4.5.2: Código Java - Algoritmo Quicksort
Figura 4.17: Tempo de Compilação em Erlang - quicksort
Figura 4.18: Tempo de Execução em Erlang - quicksort
4.6
Matrizes
O teste apresentado nesta seção é a de um algoritmo de multiplicação de matrizes
de ordem 4, onde primeiramente são inicializados dois vetores randomicamente (o código
Matrizes
53
completo, em Java e Erlang, foi inserido no apêndice deste trabalho).
O tempo de compilação em Java para esse algoritmo foi de 0,724s (Figura 4.19) e o de
execução 0,080s.
A Figura 4.20 mostra o tempo de execução, as duas matrizes geradas randomicamente
e a matriz resultado da multiplicação das mesmas.
Figura 4.19: Tempo de Compilação Java - Matrizes
Figura 4.20: Tempo de Execução em Java - Matrizes
O código em Erlang gerado possui tempo de compilação de 0,397s como mostra a
Figura 4.21.
Figura 4.21: Tempo de Compilação em Erlang - Matrizes
Erros
54
Na Figura 4.22 verifica-se que o tempo de execução em Erlang foi de 0,169 e a saı́da
deste algoritmo foi similar ao de Java, sendo que as matrizes bases para a multiplicação,
devido serem randomicamente geradas, tiveram resultados diferentes.
Figura 4.22: Tempo de Execução em Erlang - Matrizes
4.7
Erros
O Código 4.7.1 mostra um exemplo de erros envolvendo variáveis com variáveis já
decladadas e também variáveis não declaradas.
1
public class TestandoErro {
2
3
4
public static void main(String[] args) {
int a;
a = 10;
int a;
5
6
7
System.out.println(a);
d = d + 1;
8
9
10
11
}
}
Código 4.7.1: Código Java - Erro de Variáveis
Ao compilarmos o código, a linguagem Java tem como retorno dois erros (Figura 4.23)
e por isso não gera código executável.
Resumo do Tempo dos Testes
55
Figura 4.23: Tempo de Compilação Java - Erros
Após todos os tratamentos com a utilização do dicionário de processos, em Erlang,
também é gerado relatório similar que contém os erros verificados no código, observados
na Figura 4.24, e ele também não gera código executável
Figura 4.24: Tempo de Execução em Java - Erros
4.8
Resumo do Tempo dos Testes
A Tabela 4.1 mostra o resumo do tempo de compilação e execução de cada teste mostrado neste capı́tulo para a linguagem Java e também para o Erlang.
Testes
Variáveis
Strings
Char
Vetores - bubblesort
Vetores - quicksort
Matrizes
Tempos Java (s)
Compilação Execução
0,729
0,078
0,706
0,079
0,724
0,074
0,690
0,077
0,686
0,072
0,724
0,080
Tempos Erlang (s)
Compilação Execução
0,291
0,227
0,302
0,169
0,316
0,155
0,340
0,165
0,382
0,156
0,397
0,169
Tabela 4.1: Resumo dos tempos de testes
Capı́tulo 5
Conclusões
Neste trabalho foi desenvolvido o suporte a variáveis e erros para o compilador Jaraki tendo
como desafio portar a linguagem orientada a objetos Java na linguagem funcional Erlang.
Para a análise léxica e sintática, foram utilizadas as ferramentas Leex e Yecc, inerentes
à linguagem Erlang, que auxiliaram nas fases de construção da árvore sintática. Com o
estudo da árvore sintática do Erlang (AST), definiu-se as estruturas para a geração de
código em Erlang. Em meio a esse processo, a árvore sintática para o compilador Jaraki
foi criado (JAST), onde o mesmo foi essencial para a implementação da análise semântica.
Dentro do escopo deste projeto, as principais dificuldades encontradas foram em relação à manipulação de variáveis e abordagens envolvendo arrays. Para as variáveis, foi
implementado o dicionário de processos da linguagem Erlang com o intuito de armazenar o
escopo, tipo e outras caracterı́sticas ligadas a esse conceito, tanto para a análise semântica
como geração de código. O dicionário elaborado tornou-se imprescindı́vel também para o
tratamento e retorno do relatório de erros. Essas estruturas também foram necessárias no
código gerado e, por isso, foram encapsuladas por meio de módulos. Outros módulos criados
envolvem vetores e matrizes, tendo como parte fundamental a implementação da passagem
por referência dos mesmos com a finalidade de obter estrutura similar à linguagem Java.
Alguns testes foram apresentados abrangendo as principais estruturas desenvolvidas
(tipos, declarações, atribuições, escopo, vetores, matrizes e erros), assim como os tempos
de compilação e execução tanto para a linguagem Java como para a linguagem Erlang.
Observa-se que os tempos de compilação, em Erlang, foram menores para todos os testes
implementados comparados com a linguagem Java e para os tempos de execução a linguagem Java possui valores com um tempo menor. Estes resultados obtidos são considerados
relevantes, visto que as estruturas e módulos implementados foram necessários dadas as
Trabalhos Futuros
57
caracterı́sticas e conceitos diferentes em relação a variáveis referentes a cada linguagem.
5.1
Trabalhos Futuros
Apesar da maioria das estruturas envolvendo variáveis terem sido desenvolvidas, algumas implementações podem ser realizadas como trabalhos futuros. Por exemplo, a manipulação de variáveis globais e também outros tratamentos envolvendo erros. As variáveis
globais devem ser tratadas em conjunto com a implementação da orientação a objetos do
compilador Jaraki com o intuito de alcançar uma abordagem mais próxima da que já foi
desenvolvida na linguagem Java. A referência feita à manipulação de erros está vinculada
à criação de mais arquivos de teste que simulem possı́veis erros e também ao tratamento
de eventos.
Referências Bibliográficas
[Aho2007] Aho, A. V. (2007). Compiladores: Princı́pios, técnicas e ferramentas. Person,
segunda edição.
[Armstrong2007] Armstrong, J. (2007). Pragmatic Programming in Erlang. Pragmatic
Bookshelf.
[Cesarini2009] Cesarini, F., T. S. (2009). Erlang Programming. O’Reilly.
[Deitel2010] Deitel, P. (2010). Java Como Programar. Pearson Prentice Hall, oitava edição.
[Erlang2012a] Erlang
(2012a).
Array.
Disponı́vel
na
url:
na
url:
<http://www.erlang.org/doc/man/array.html/>. Acesso em: 15/11/2012.
[Erlang2012b] Erlang
(2012b).
Yecc.
Disponı́vel
<http://www.erlang.org/doc/man/yecc.html/>. Acesso em: 05/10/2012.
[Hubbard2004] Hubbard, J. (2004). Coleção Schaum - Programação com Java. Bookman,
segunda edição.
[Java2012] Java (2012).
The Java Language Specification. Disponı́vel na url:
<http://docs.oracle.com/javase/specs/jls/se7/html/index.html/>.
Acesso
em:
05/11/2012.
[Jython2012] Jython
(2012).
The
Jython
Project.
Disponı́vel
na
url:
<http://www.jython.org/>. Acesso em: 25/02/2012.
[Louden2004] Louden, K. C. (2004). Compiladores: princı́pios e práticas. Thomson Pioneira.
REFERÊNCIAS BIBLIOGRÁFICAS
[Reia2012] Reia (2012).
59
Reia Language. Disponı́vel na url: <http://reia-lang.org/>.
Acesso em: 25/02/2012.
[Sebesta2011] Sebesta, R. W. (2011). Conceitos de Linguagens de Programação. Bookman,
nona edição.
[Upadhyaya2011] Upadhyaya, M. (2011). Simple calculator compiler using lex and yacc.
Apêndice A
Códigos de Testes
Os códigos-fontes mencionados no desenvolvimento deste trabalho e outros estão presentes
no CD que acompanha esta monografia.
A.1
Estrutura dos Diretórios
• /jaraki
– /ebin - bytecodes dos .erl
– /include - definição de constantes
∗ jaraki define.hrl
– /src - código-fonte do compilador
∗ ast.erl - interface com o analisador sintático
∗ core.erl - interface para converter jAST em eAST
∗ file lib.erl - biblioteca para suportar manipulação de arquivos
∗ gen ast.erl - funções auxiliares para montar nós da eAST
∗ gen erl code.erl - principal módulo do analisador semântico
∗ helpers.erl - funções auxiliares diversas
∗ jaraki.erl - interface com usuário para compilar .java
∗ jaraki exception.erl - módulo que trata erros de compilação
∗ jaraki lexer.erl - analisador léxico gerado
∗ jaraki lexer.xrl - código fonte para o leex gerar o analisador léxico
∗ jaraki parser.erl - analisador sintático gerado
Estrutura dos Diretórios
61
∗ jaraki parser.yrl - código fonte para o yecc gerar o analisador sintático
∗ jaraki utils.erl - funções para auxiliar o desenvolvimento do Jaraki
∗ loop.erl - biblioteca para suportar laços
∗ matrix.erl - biblioteca para suportar matrizes
∗ oo lib.erl - biblioteca para suportar orientação a objetos
∗ random lib.erl - biblioteca para suportar o uso da classe Random
∗ st.erl - funções para gerenciar a tabela de sı́mbolos
∗ vector.erl - biblioteca para suportar vetores
– /java src - testes utilizados durante o desenvolvimento
– jk - script para executar .erl gerado
– jkc - script para compilar .java
– Makefile - Makefile para compilar o compilador
• /testes
– MatrizTeste.java - código em java que efetua a multiplicação de 2 matrizes
– string1.erl - código em Erlang que apresenta a manipulação de variáveis do tipo
String
– char1.erl - código em Erlang que apresenta a manipulação de variáveis do tipo
char
– bubblesort.erl - código em Erlang gerado que implementa o método de ordenação
bolha
– quicksort.erl - código em Erlang gerado que implementa o algoritmo quicksort
– matrizteste.erl - código em Erlang gerado que efetua a multiplicação de 2 matrizes
• /videos - simulação de testes
• /pdf
– apresentacao tcc.pdf - apresentação de defesa da monografia
– tese.pdf - monografia
Download