Compilador para um subset de Pascal
Tags: programming, compiling, computerscience
Published: 2023-12-21 18:44:03

O ano era 1999 (ou 2000, não tenho mais certeza rsrsrs). Foi um dos trabalhos da disciplina de Compilação Avançada, e posso afirmar que foi um dos mais divertidos da minha graduação em Ciência da Computação 🙂

Basicamente o objetivo do projeto era implementar um compilador capaz de ler um _subset_ de [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) e gerar um arquivo [class](https://en.wikipedia.org/wiki/Java_class_file) válido de forma a poder executá-lo na [JVM](https://en.wikipedia.org/wiki/Java_virtual_machine). Por _subset_ entendam um programa Pascal válido, mas com um conjunto reduzido de instruções e operações:

- Declaração de variáveis: tipos básicos apenas;
- Estruturas de repetição: If, For, While, Repeat;
- Entrada e saída de dados: 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.

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

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

Abaixo temos um exemplo em Pascal um pouco mais elaborado onde temos definição de variáveis, entrada e saída de dados:

```
program Example;
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 facilitar o desenvolvimento do compilador, optei por utilizar um [gerador de _parsers_](https://en.wikipedia.org/wiki/Compiler-compiler). Apesar de já ter utilizado os clássicos [Lex](https://en.wikipedia.org/wiki/Lex_(software))/[YACC](https://en.wikipedia.org/wiki/Yacc) e [Flex](https://en.wikipedia.org/wiki/Flex_(lexical_analyser_generator))/[Bison](https://en.wikipedia.org/wiki/GNU_Bison) em outros momentos, eu sentia falta de uma integração maior entre a [análise léxica e sintática](https://www.scaler.com/topics/phases-of-compiler/) nestes geradores. Além disso, estes geradores utilizavam [BNF](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form), e eu gostaria de utilizar [EBNF](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form) para especificar a gramática da minha linguagem (_subset_ de Pascal).

Com isso optei por utilizar o [JavaCC](https://en.wikipedia.org/wiki/JavaCC), um gerador de _parsers_ que, a partir da gramática EBNF que criei, gerava um _parser_ em Java capaz de ler este _subset_ de Pascal e, a cada etapa das análises léxicas e sintáticas, executar ações determinadas por mim. Nestas ações eu inseria código Java que realizava o que eu precisava: gerar a tabela de símbolos (que continham as strings e números existentes no programa em Pascal) e o _bytecode_ equivalente ao programa lido. No final do parser, caso nenhum erro ocorresse, o arquivo class era gerado.

Para exemplificar o processo de tradução que o compilador realizava vamos usar o clássico “Hello world!” em Pascal abaixo:

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

A partir deste programa o compilador então traduzia para o seu equivalente em [Assembly Java](https://www.eg.bucknell.edu/~cs360/java-assembler/examples.html), similar ao exemplo abaixo:

```
class Hello
{ 
  method public static void main (java.lang.String[])
  max_stack 2
  { 
    getstatic java.io.PrintStream java.lang.System.out
    ldc "Hello World!"
    invokevirtual void java.io.PrintStream.println(java.lang.String)
    return
  }
}
```

Posteriormente este assembly era traduzido pelo compilador para o seu formato binário (_bytecode_) e armazenado no arquivo class.

No fim, o trabalho foi concluído com sucesso. Eu tinha uma gramática em JavaCC que gerava um _parser_ capaz de ler qualquer programa em Pascal (desde que respeitando o _subset_ definido) e gerar o arquivo class, arquivo este que era executado diretamente pela JVM. Aliás, fiquei fã da JVM nesta época.