Pascal rodando na JVM
Tags: compiler, go, braziliandevs, learning
Published: 2024-01-30 22:20:48

Compiladores é um dos temas do qual tenho muito interesse. Inclusive estou publicando alguns projetos antigos meus nesta série [aqui](https://dev.to/alexgarzao/series/25859).

Porém, como [comentei em uma publicação desta série](https://dev.to/alexgarzao/ambiente-para-desenvolvimento-e-execucao-de-aplicacoes-escritas-em-hololinguagem-17nf#:~:text=Mas%2C%20para%20tentar%20compensar%20a%20falta%20de%20detalhes%20aqui%2C%20logo%20que%20poss%C3%ADvel%20vou%20iniciar%20um%20projeto%20open%20source%20de%20um%20compilador.%20J%C3%A1%20adianto%20que%20este%20novo%20projeto%20ser%C3%A1%20em%20Go%20e%20irei%20utilizar%20um%20gerador%20de%20parsers%20bem%20conhecido.), vou iniciar uma abordagem mais prática onde irei construir um compilador "_from scratch_", passo-a-passo, de código aberto, tanto para consolidar alguns temas abordados bem como para termos algo concreto.



## Sobre o desafio

Compiladores são, por natureza, projetos que eu considero desafiadores e complexos. E para termos um objetivo claro e, dentro do possível, simples para este projeto, o que vamos fazer é implementar uma nova solução para o trabalho de faculdade que comentei [nesta publicação](https://dev.to/alexgarzao/compilador-para-um-subset-de-pascal-27b1): rodar um _subset_ de [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) na [JVM](https://en.wikipedia.org/wiki/Java_virtual_machine). Para tal, iremos criar um compilador que entende este _subset_ de Pascal e gera o arquivo [class](https://en.wikipedia.org/wiki/Java_class_file) para ser executado na JVM.

Como a minha criatividade para criar nomes de projetos é baixa, vou batizar o projeto de POJ (Pascal On the JVM). Se alguém tiver um nome bacana, eu aceito :-)



## Visão macro do projeto

Na solução anterior (mais detalhes [aqui](https://dev.to/alexgarzao/compilador-para-um-subset-de-pascal-27b1)) o compilador gerava o arquivo class diretamente. Porém, para tentar simplificar a nossa solução, vou abstrair a geração do arquivo class utilizando um montador [Java Assembly](https://www.eg.bucknell.edu/~cs360/java-assembler/examples.html) qualquer (Figura abaixo). Com isso o nosso compilador (POJ) será responsável por ler um programa contendo código Pascal e gerar o assembly Java equivalente enquanto o montador será responsável por ler este assembly e gerar o arquivo class para ser executado na JVM.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/imojbfhnmluzgu9nupny.png)

Entendo que seja uma proposta válida no momento, mas só após alguns testes com montadores assembly Java será possível validar esta proposta. Caso não seja encontrado um montador que atenda as necessidades do projeto, teremos que partir para gerar o arquivo class diretamente. 

Assim como no projeto anterior, para facilitar o desenvolvimento do compilador eu optei por utilizar um [gerador de _parsers_](https://en.wikipedia.org/wiki/Compiler-compiler). Desde 2003 sou fã do [ANTLR](https://www.antlr.org/), que além de fornecer uma integração interessante entre a [análise léxica e sintática](https://www.scaler.com/topics/phases-of-compiler/), também utiliza [EBNF](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form) para especificar a gramática da linguagem. Além disso, ANTLR gera código para diversas linguagens, entre elas Go :-)

A Figura abaixo contém as etapas necessárias para criarmos o nosso compilador (POJ). A partir da gramática da linguagem Pascal, o ANTLR gera o parser em Golang. A partir deste código Go gerado iremos instrumentar as regras necessárias, tanto na análise léxica bem como na sintática. Após isso utilizaremos o compilador do Go para gerar o binário do POJ.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d3rje0qvzmv2a46z5nv7.png)



## _Subset_ aceito

Quando falamos em _subset_ de Pascal entendam um programa Pascal válido, mas com um conjunto reduzido de instruções e operações:

- Declaração de variáveis utilizando os tipos básicos: integer, real, boolean e string;
- Estruturas condicionais: If/Else;
- Estruturas de repetição: For, While, Repeat;
- Entrada e saída de dados via console: Write, WriteLn, Read, ReadLn;
- Operações matemáticas com precedência de operadores: soma, subtração, divisão, multiplicação e módulo;
- Declaração de blocos de código: procedures e funções.



## Exemplos de programas aceitos

Abaixo é possível ver o clássico “Hello world!” em Pascal:

```
program Hello;
begin
  writeln ('Hello world!');
end.
```

Abaixo temos o cálculo do fatorial, de forma recursiva:

```
program fatorial;

var numero : integer;

function fatorial(n : integer) : integer;
begin
    if n<0 then fatorial := 0
    else begin
        if n<=1 then fatorial := 1
        else fatorial := n * fatorial(n-1);
    end;
end;

begin
    write('Introduza numero inteiro: ');
    readln(numero);
    writeln;
    writeln('O fatorial de ', numero, ' e: ', fatorial(numero));
end.
```

Abaixo temos um exemplo com entrada e saída de dados:

```
program NameAndAge;
var
  MyName: String;
  MyAge : Byte;
begin
  Write('What is your name? '); Readln(MyName);
  Write('How old are you? '); Readln(MyAge);
  Writeln;
  Writeln('Hello ', MyName);
  Writeln('You are ', MyAge, ' years old');
end.
```

Para quem tiver interesse de ver a execução destes programas, sugiro utilizar o [Free Pascal Compiler](https://www.freepascal.org/) no momento. Nas próximas semanas poderemos utilizar o POJ para tal :-)



## Por que refazer?

Uma dúvida comum talvez seja: por que refazer? Por que não melhorar o projeto anterior?

Bom, eu até encontrei o código antigo, de 1999, do projeto de faculdade. Mas esta versão utiliza o JavaCC e Java, tecnologias que praticamente só utilizei naquele período. Aprender Java e ajustar um código Java de 1999 não está nos meus objetivos. E como pretendo manter e evoluir este projeto, optei por reescrevê-lo em tecnologias que tenho mais interesse como ANTLR e Go.



## Por que JVM?

Outra dúvida comum talvez seja: porque foi escolhida a JVM? Em 1999 foi utilizada a especificação 1.1 da JVM, e garanto que havia pouca documentação na época. O motivo da escolha da JVM foi porque, dentre as VMs da época, a JVM era [_stack based machine_](https://en.wikipedia.org/wiki/Stack_machine), o que simplifica (e muito) a geração do _bytecode_. Isso porque, além de necessitar de poucas operações, todas as operações da máquina virtual operam sobre a pilha. Já VMs [_register based_](https://en.wikipedia.org/wiki/Register_machine) necessitam de um conjunto maior de operações e precisam lidar com a alocação de registradores (mesmo que estes sejam virtuais). Para quem tiver interesse de ler mais sobre, [aqui](https://www.codeproject.com/Articles/461052/Stack-Based-vs-Register-Based-Virtual-Machine-Arch) tem uma publicação bem interessante sobre este tópico. Isso sem falar que a JVM já é utilizada por várias linguagens além do Java, como Clojure, Lisp, Scala e Kotlin, isso só citando algumas das mais conhecidas.