DEV Community

Alex Sandro Garzão
Alex Sandro Garzão

Posted on

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

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.
Enter fullscreen mode Exit fullscreen mode

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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>;
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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)