Geraç ˜ao de C´odigo para Smallpascal 1. Já estudamos como

Propaganda
Geração de Código para Smallpascala
1. Já estudamos como SableCC faz uso do “design pattern visitor” para
construir compiladores modulares. Vimos também os principais
componentes da máquina virtual Java e a sintaxe da linguagem
Jasmin, um assembler para um subconjunto do máquina virtual Java.
Vamos agora estudar como código Jasmin pode ser gerado a partir de
uma linguagem de expressões simples chamada Smallpascal.
public class CodeGenerator extends DepthFirstAdapter
private Hashtable table = new Hashtable();
private String source;
private String class name;
PrintWriter out;
2. O gerador de código estende DepthFirstAdapter
implementando a interpretação que gerará código Jasmin a partir de
Smallpascal.
a Baseado
na implementaç˜
ao de Samllpascal, por Fidel Viegas.
brainycreatures.co.uk/download/smallpascal.asp)
1
(http://www.
3. A variável table armazenará os identificadores declarados no
programa sendo compilado. Programas em Smallpascal são “case
insensitive”, ou seja, não é feita distinção entre letras maiúsculas e
minúsculas.
4. A variável source armazena o nome do arquivo original e
class name armazena o nome do arquivo original sem a extensão
pas. Esta string define o nome da classe Java gerada. Finalmente
out guarda o “stream” com o texto Jasmin gerado pelo compilador.
O nome do arquivo gerado é o valor de class name concatenado
a .j.
public CodeGenerator(String source)
...
5. O construtor “default” recebe uma string contendo o nome do
arquivo Smallpascal sendo compilado. A partir desta string os
campos (atributos) source, class name e out são instanciados
apropriadamente.
2
public void inAProgram(AProgram node)
out.println(".source " + source);
out.println(".class public " + class_name);
out.println(".super java/lang/Object");
out.println(".method public <init>()V");
out.println(" aload_0");
out.println("
invokenonvirtual java/lang/Object/<init>()V");
out.println(" return");
out.println(".end method");
6. A máquina virtual Java espera que toda classe tenha pelo menos um
construtor. Todo programa Smallpascal é mapeado para uma classe
na JVM. Desta maneira é necessário gerar o construtor default para a
classe gerada a partir do programa Smallpascal sendo compilado. O
construtor default chama o construtor de java.lang.Object.
Isso é feito na ação associada ao header do programa junto com as
diretivas .source, .class e .super de Jasmin.
3
public void outASingleIdentifierList
(ASingleIdentifierList node)
String key = node.getIdentifier().getText().toUpperCase();
String var_name = node.getIdentifier().getText();
String var_image = class_name + "/"+ var_name;
out.println(".field public static "+ var_name + " I = 0");
table.put(key, var_image);
7. Declarações de variáveis são mapeadas para declarações de campos
(atributos) em Jasmin. Todas as variáveis em Samllpascal são int.
Lembre-se que variáveis em Jasmin são representadas pelo nome da
classe mais o nome da variável. Guardamos então este nome
completo da variável na tabela de sı́mbolos para que este nome
completo possa ser descoberto quando a variável for utilizada numa
expressão.
4
public void inABody(ABody node)
out.println();
out.println(
".method public static main([Ljava/lang/String;)V");
out.println(" .limit stack 5");
out.println(" .limit locals 1");
8. Um programa em Smallpascal tem a seguinte organização:
program = program_heading declarations body dot ;
O corpo (“body”) de um programa em Smallpascal é então mapeado
na função main da classe sendo gerada para o programa. Como em
Smallpascal não existem variáveis locais, a pilha é limitada a
valores, o que é suficiente para cumprir os requisitos da JVM. Da
mesma maneira o número de variáveis locais é limitado a
correspondendo aos parâmetros args[] passados ao método main.
5
public void outABody(ABody node)
out.println(" return");
out.println(".end method");
out.flush();
9. Na ação out da produção Body deve ser gerado código para que a
função main retone, dado que todos os métodos, que são
procedimentos, na máquina virtual devem terminar com “return”. A
diretiva adequada deve também ser gerada e finalmente o código do
programa é escrito no disco.
6
public void outAAssignmentStatement
(AAssignmentStatement node)
String key =
node.getIdentifier().getText().toUpperCase();
String var_image = (String) table.get(key);
out.println(" putstatic " + var_image + " I");
10. Este é o código gerado para uma atribuição em Smallpascal. Primeiro
o nome completo do identificador é obtido da tabela de sı́mbolos.
Assume-se que o resultado da avaliação da expressão sendo atribuı́da
a variável esteja no topo da pilha de operandos. A instrução
putstatic copia o valor do topo da pilha para a variável que é do
tipo int, como todas as variáveis em Smallpascal.
7
public void inAWritelnStatement(AWritelnStatement node)
out.println(
"getstatic java/lang/System/out Ljava/io/PrintStream;");
public void outAWritelnStatement(AWritelnStatement node)
out.println(
"invokevirtual java/io/PrintStream/println(I)V");
11. O comando writeln de Smallpascal é implementado através das
ações in e out da produção AWritelnStatement. Na ação in
o objeto java.lang.System.out é empilhado e na ação out o
método println da classe PrintStream é invocado.
8
public void outAPlusExpression(APlusExpression node)
out.println(" iadd");
public void outAMinusExpression(AMinusExpression node)
out.println(" isub");
// ...
public void outANumberFactor(ANumberFactor node)
String value = node.getNumber().getText();
push(value);
12. As operações aritméticas são traduzidas simplesmente para suas
contrapartes na JVM. O processo de empilhamento dos operandos é
feito pelo método push, da classe CodeGenerator, que deve
primeiro verificar qual inteiro está sendo empilhado de forma a
utilizar a instrução adequada. O método push, que pode sinalizar
uma exceção se o inteiro for mal-formado, é invocado quando um
fator está sendo processado. Isso porque neste exemplo não está
sendo utilizada a árvore de sintaxe abstrata.
9
private void push(String value)
// ...
// ...
switch(int_value)
case 5: // gera iconst_X se X estiver entre 0 e 5
out.println(" iconst_" + value);
break;
default:
// instrução para empilhar um byte
if ((int_value >= -128) && (int_value <= 127))
out.println(" bipush " + value);
// instrução para empilhar short integer
else if ((int_value >= -32768) &&
(int_value <= 32767))
out.println(" sipush " + value);
// instrução para empilhar integer
else
out.println(" ldc " + value);
break;
// ...
10
public void outAIdentifierFactor(AIdentifierFactor node)
String key = node.getIdentifier().getText().
toUpperCase();
String var_image = (String) table.get(key);
out.println(" getstatic " + var_image + " I");
13. Uma instrução getstatic deve ser utilizada para se recuperar o
valor de uma variável no lado direito de uma expressão.
11
Download