Estruturas de repetição: repeat, while e for

Rmag Breaking News

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.

Leave a Reply

Your email address will not be published. Required fields are marked *