Para quem não está acompanhando, o POJ (Pascal on the JVM) é um compilador que transforma um subset de Pascal para JASM (Java Assembly) de forma que possamos usar a JVM como ambiente de execução.
Na última postagem eu tinha mais algumas funcionalidades para comentar, mas para facilitar a compreensão achei mais prudente separar em mais de uma postagem.
Como estamos compilando para a JVM faz-se necessário detalhar o funcionamento de várias pontos desta incrível máquina virtual. Com isso em vários momentos eu detalho o funcionamento interno da JVM bem como algumas das suas instruções.
Declaração de variáveis
Apesar de importante, declaração de variáveis em Pascal não era a minha próxima meta, mas sim estruturas de controle como for, while e repeat. Mas, para podermos exemplificar e corretamente implementar estas estruturas, a declaração de variáveis torna-se essencial.
Após todo o aprendizado no desenvolvimento do POJ (todas as postagens estão aqui) foi relativamente fácil alterar o parser bem como gerar o assembly para declarar as variáveis em Java Assembly.
A partir do programa Pascal abaixo:
program VarDeclarations;
var
msg1, msg2 : string;
begin
msg1 := 'Message 1!';
msg2 := 'Message 2!';
writeln (msg1);
writeln (msg2);
end.
O POJ gera o seguinte JASM:
// Code generated by POJ 0.1
public class var_declarations {
public static main([java/lang/String)V {
;; msg1 := 'Message 1!';
ldc "Message 1!"
astore 1
;; msg2 := 'Message 2!';
ldc "Message 2!"
astore 2
;; writeln (msg1);
getstatic java/lang/System.out java/io/PrintStream
aload 1
invokevirtual java/io/PrintStream.print(java/lang/String)V
getstatic java/lang/System.out java/io/PrintStream
invokevirtual java/io/PrintStream.println()V
;; writeln (msg2);
getstatic java/lang/System.out java/io/PrintStream
aload 2
invokevirtual java/io/PrintStream.print(java/lang/String)V
getstatic java/lang/System.out java/io/PrintStream
invokevirtual java/io/PrintStream.println()V
return
}
}
Cabe aqui salientar que, para facilitar a compreensão, no exemplo acima as linhas iniciando com ";;" contém o trecho em Pascal que originou o JASM.
Em relação aos exemplos anteriores agora estamos utilizando duas novas instruções da JVM:
- astore <N>: retira da pilha uma string e armazena no slot de variáveis N
- aload <N>: carrega na pilha a string existente no slot de variáveis N
A alteração mais significativa é que agora o POJ gera uma tabela de símbolos compartilhada durante todo o processo de compilação.
Para quem tiver interesse, o PR que implementa a declaração de variáveis está aqui.
Estrutura de controle Repeat
De posse de todo o conhecimento obtido até aqui, e com os pré-requisitos para implementarmos as estruturas de repetição de Pascal (sentença if, operadores relacionais e booleanos), optamos por iniciar com a estrutura "Repeat Until" por ser a mais simples dentre as estruturas de repetição.
Abaixo temos a sintaxe do Repeat:
1: repeat
2: <bloco código>
3: until <condição>;
O POJ implementa o seguinte comportamento:
- Na linha 1, ao reconhecer o token repeat, gera e emite um label (L1) que indica onde inicia o laço de repetição
- Na linha 2 o bloco de código é traduzido para JASM
- Na linha 3 a condição é avaliada e, caso seja válida (true) a execução salta para L1
Vamos a um exemplo que imprime os números de 10 até 19. A partir do programa Pascal abaixo:
program RepeatExample;
var
i: integer;
begin
i := 10;
repeat
writeln ('i=', i);
i := i + 1;
until i = 20;
end.
O POJ gera o seguinte JASM:
// Code generated by POJ 0.1
public class repeat_example {
public static main([java/lang/String)V {
;; i := 10;
sipush 10
istore 1
L1: ;; repeat
;; writeln ('i=', i);
getstatic java/lang/System.out java/io/PrintStream
ldc "i="
invokevirtual java/io/PrintStream.print(java/lang/String)V
getstatic java/lang/System.out java/io/PrintStream
iload 1
invokevirtual java/io/PrintStream.print(I)V
getstatic java/lang/System.out java/io/PrintStream
invokevirtual java/io/PrintStream.println()V
;; i := i + 1;
iload 1
sipush 1
iadd
istore 1
;; until i = 20;
iload 1
sipush 20
if_icmpne L2
iconst 1
goto L3
L2: iconst 0
L3: ifeq L1
return
}
}
Neste JASM não foram utilizadas novas instruções da JVM. Mais detalhes sobre estas instruções podem ser obtidas na postagem anterior.
Para quem tiver interesse o PR que implementa a estrutura Repeat está aqui.
Estrutura de controle While
Dando sequências às estruturas de controle, a próxima estrutura implementada foi a While.
A estrutura While pode ser vista como uma variação da Repeat, onde no caso do Repeat o teste é executado no final enquanto que no While o teste é executado no início. Repeat sempre executa o bloco de código pelo menos uma vez, enquanto que o While executa 0 ou mais vezes.
Abaixo temos a sintaxe do While:
1: while <condição> do
2: begin
3: <bloco código>
4: end;
O POJ implementa o seguinte comportamento:
- Na linha 1, ao reconhecer o token while, executa os seguintes passos:
- gera e emite um label (L1) que indica onde está o condicional do while
- gera um label (L2) que indica o término do while
- avalia a condição e, caso seja inválida (false) a execução salta para L2
- A linha 2 apenas indica o início do bloco
- Na linha 3 o bloco de código é traduzido para JASM
- Na linha 4 são executados os seguintes passos:
- a execução salta para L1
- emite o label L2
Vamos a um exemplo. A partir do programa Pascal abaixo:
program WhileExample;
var
i: integer;
begin
i := 10;
while i < 20 do
begin
writeln ('i=', i);
i := i + 1;
end;
end.
O POJ gera o seguinte JASM:
// Code generated by POJ 0.1
public class while_example {
public static main([java/lang/String)V {
;; i := 10;
sipush 10
istore 1
L1: ;; while i < 20 do
iload 1
sipush 20
if_icmpge L3
iconst 1
goto L4
L3: iconst 0
L4: ifeq L2
;; writeln ('i=', i);
getstatic java/lang/System.out java/io/PrintStream
ldc "i="
invokevirtual java/io/PrintStream.print(java/lang/String)V
getstatic java/lang/System.out java/io/PrintStream
iload 1
invokevirtual java/io/PrintStream.print(I)V
getstatic java/lang/System.out java/io/PrintStream
invokevirtual java/io/PrintStream.println()V
;; i := i + 1;
iload 1
sipush 1
iadd
istore 1
;; end; (do while)
goto L1
L2: return
}
}
Em comparação com os exemplos anteriores, não foram utilizadas novas instruções da JVM.
Para quem tiver interesse o PR que implementa a estrutura "While" está aqui.
Estrutura de controle For
Por ser considerada a mais complexa das estruturas de controle citadas nesta publicação (repeat, while e for), propositadamente o For foi deixado por último.
Abaixo temos a sintaxe do For:
1: for <var> := <início> to <fim> do
2: begin
3: <bloco código>
4: end;
O POJ implementa o seguinte comportamento:
- Na linha 1, ao reconhecer o token for, são executados os seguintes passos:
- inicializa <var> com o valor <início>
- gera e emite um label (L1) que indica onde está o condicional do for
- gera um label (L2) que indica o término do for
- avalia se <var> é <= a <fim> e, caso seja inválida (false) a execução salta para o label L2
- A linha 2 apenas indica o início do bloco
- Na linha 3 o bloco de código é traduzido para JASM
- Na linha 4 são executados os seguintes passos:
- incrementa <var> em 1 unidade
- a execução salta para o label L1
- emite o label L2
Para a sua correta implementação o for engloba as seguintes tarefas:
- inicializar a variável definida no for
- incrementar/decrementar esta variável automaticamente no final do laço
- validar no início da sentença se o bloco deve ser executado
Vamos ao exemplo. A partir do programa Pascal abaixo:
program ForExample;
var
i: integer;
begin
for i := 10 to 19 do
begin
writeln ('inc i=', i);
end;
end.
O POJ gera o seguinte JASM:
// Code generated by POJ 0.1
public class for_example {
public static main([java/lang/String)V {
;; i := 10
sipush 10
istore 1
L1: ;; i <= 19 ?
iload 1
sipush 19
if_icmpgt L2
;; writeln ('inc i=', i);
getstatic java/lang/System.out java/io/PrintStream
ldc "inc i="
invokevirtual java/io/PrintStream.print(java/lang/String)V
getstatic java/lang/System.out java/io/PrintStream
iload 1
invokevirtual java/io/PrintStream.print(I)V
getstatic java/lang/System.out java/io/PrintStream
invokevirtual java/io/PrintStream.println()V
;; i := i + 1
iload 1
sipush 1
iadd
istore 1
;; end; (do for)
goto L1
L2: return
}
}
Para quem tiver interesse o PR que implementa a estrutura For está aqui.
Maiores informações
O repositório com o código completo do projeto e a sua documentação está aqui.
Top comments (0)