DEV Community

Alex Sandro Garzão
Alex Sandro Garzão

Posted on

Sentenças aninhadas

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 resolvemos alguns bugs importantes, em especial na geração do assembly. Nesta publicação vamos falar sobre como gerar corretamente o assembly para sentenças aninhadas.

Como estamos compilando para a JVM faz-se necessário detalhar o funcionamento de vários 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 (opcodes).

Contextos

Uma das funcionalidades necessárias para lidarmos corretamente com sentenças aninhadas é a possibilidade de termos vários contextos no parser. Isso ocorre porque se o parser assume um contexto apenas, sentenças de controle aninhadas que geram labels e saltos (como if, for, while e repeat) gerariam o endereçamento de saltos incorretamente.

Existem duas formas de lidar com contextos, que são:

  • Parser recursivo
  • Empilhar contextos

Usualmente eu utilizaria a abordagem de parser recursivo. Porém, para podermos implementar um parser recursivo com o ANTLR, pela forma como a gramática foi estruturada no POJ, seria necessário injetar código diretamente na gramática, uma abordagem pouco recomendada. Em função disso optou-se pela abordagem de empilhar contextos.

Como já existia uma implementação de pilha que monitorava os tipos que o parser empilhou/desempilhou na JVM, para não ter que criar mais uma pilha para um tipo específico, optou-se por criar uma stack genérica neste PR. Além de poder aproveitar esta implementação posteriormente, ainda posso refatorar o código antigo e remover a stack específica existente.

Neste commit o parser foi alterado para empilhar/desempilhar corretamente os contextos das funções. Basicamente no início do parser de uma função o contexto é empilhado, e no seu final o contexto é desempilhado.

Sentenças aninhadas

Sentenças de controle como if, for, while e repeat funcionavam corretamente. Porém, caso houvesse sentenças aninhadas, o POJ não guardava contexto e acaba por gerar erroneamente os labels e saltos. Aqui e aqui foi abordado como funciona a geração do assembly para estas sentenças de controle.

Para o exemplo abaixo, que contém um if aninhado dentro de outro, o POJ gerava erroneamente os labels e saltos necessários:

program NestedIfs;
begin
  if (1 > 2) then
    if (2 > 3 ) then
      writeln('1 > 2 and 2 > 3')
    else
      writeln('1 > 2 and 2 <= 3')
  else
    writeln('1 <= 2');
end.
Enter fullscreen mode Exit fullscreen mode

Este bug era conhecido e eu optei por resolvê-lo no momento em que o parser tivesse suporte a contextos.

Neste commit foi criado a estrutura LabelsContext contendo os seguintes labels:

  • Else: necessário para o caso do if com else;
  • NextStatement: contém o label da próxima instrução;
  • IterationStart: indica o teste da estrutura de repetição no caso de while, repeat e for.

Para validar a correta geração do assembly foram criados testes para validar if's aninhados, repeat's aninhados, while's aninhados bem como for's aninhados. Aqui foi criado os testes para validar a geração do assembly no caso de funções recursivas. Além disso foi necessário atualizar o assembly esperado de todos os testes existentes. Por fim, neste PR foi atualizado o parser para utilizar a nova estrutura de contextos.

Aqui está o PR completo destas modificações. Aqui temos o commit contendo as mudanças para o correto funcionamento da sentença if, aqui o commit referente ao repeat, aqui o commit referente ao while e aqui o commit referente ao for.

Próximos passos

Na próxima publicação vamos falar sobre entrada de dados. Agora falta pouco para concluirmos um dos objetivos deste projeto: ler um número da entrada padrão e calcular o seu fatorial.

Código completo do projeto

O repositório com o código completo do projeto e a sua documentação está aqui.

Top comments (0)