DEV Community

Alex Sandro Garzão
Alex Sandro Garzão

Posted on • Updated on

Compilador para um subset de Pascal

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 e gerar um arquivo class válido de forma a poder executá-lo na JVM. 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.
Enter fullscreen mode Exit fullscreen mode

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

Para facilitar o desenvolvimento do compilador, optei por utilizar um gerador de parsers. Apesar de já ter utilizado os clássicos Lex/YACC e Flex/Bison em outros momentos, eu sentia falta de uma integração maior entre a análise léxica e sintática nestes geradores. Além disso, estes geradores utilizavam BNF, e eu gostaria de utilizar EBNF para especificar a gramática da minha linguagem (subset de Pascal).

Com isso optei por utilizar o 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.
Enter fullscreen mode Exit fullscreen mode

A partir deste programa o compilador então traduzia para o seu equivalente em Assembly Java, 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
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)