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:
var
msg1, msg2 : string;
begin
msg1 := ‘Message 1!’;
msg2 := ‘Message 2!’;
writeln (msg1);
writeln (msg2);
end.
O POJ gera o seguinte JASM:
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:
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:
var
i: integer;
begin
i := 10;
repeat
writeln (‘i=’, i);
i := i + 1;
until i = 20;
end.
O POJ gera o seguinte JASM:
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:
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:
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:
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:
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:
var
i: integer;
begin
for i := 10 to 19 do
begin
writeln (‘inc i=’, i);
end;
end.
O POJ gera o seguinte JASM:
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.