Observações

 

 

 

 

                O compilador fornecido define várias classes utilizadas para representar envio de mensagem:

abstract class MessageSend  extends Expr  { ... }

class MessageSendToVariable extends MessageSend { ... }

class MessageSendToSelf     extends MessageSend { ... }

class MessageSendToSuper    extends MessageSend { ... }

class MessageSendStatement     extends Statement {

      MessageSend  messageSend;

      public void gen( PrintWriter out ) {

         pw.print(“”);

         messageSend.gen(out);

         out.println(“;”);

      }

    ...

}

 É melhor fazer MessageSend herdar de Expr do que de Statement. Vocês descobrirão porque na hora de gerar código: um envio de mensagem, quando é um “Statement”, exige um “;” no final e como expressão, não. Então é melhor fazer o mais complexo, MessageSend como Statement, utilizar o mais simples, MessageSend como Expr.

 

 

 

            Para criar um objeto da classe A, fazemos

    a = new A();

 

Para construir a ASA da expressão ``new A()´´, utilize uma classe que representa a criação de um objeto. Esta classe deve ter uma única variável de instância, um ponteiro para a classe ─ construtores em Krakatoa não admitem parâmetros. Note que esta classe deve herdar de Expr.

 

 

 

 

          Na tabela de símbolos, nãonecessidade de colocar os métodos ou variáveis de instância. Coloque apenas as variáveis locais, parâmetros (primeiro nível)  e classes (segundo nível). Isto está feito na classe SymbolTable do compilador fornecido neste site.

            Os métodos deverão ser procurados por um método searchMethod da classe ClassDec (que representa uma classe de Krakatoa). Quando houver um envio de mensagem, como em

       x.m();

deve-se procurar por método “m” na classe de “x”. Assim:   

          classOf_x.searchMethod(“m”);

Onde classOf_x foi obtido por uma busca na tabela de símbolos pela classe “A”, assim:

        classOf_x = (ClassDec ) symbolTable.getInGlobal(“A”);

Assume-se que x tenha sido declarado como

        A x;

e que método getInGlobal da tabela de símbolos procure por um símbolo apenas nas classes da tabela (isto é verdade para a classe SymbolTable fornecida na página da disciplina).

            O mesmo raciocínio se aplica a variáveis de instância.

 

 

 

A classe que representa uma variável local é

 

public class Variable {

    private String name;

    private Type type;

}

            Como type é do tipo Type, esta variável de instância pode apontar para objetos de Type e suas subclasses, o que inclui ClassDec. Assim, o tipo de uma variável pode ser “integer” (objeto de Type), “boolean” (objeto de Type)  ou uma classe (objeto de ClassDec). Naturalmente, ClassDec deve herdar de Type para que isto seja possível.

 

 

 

 

            O construtor de ClassDec deve ter um único parâmetro, o nome da classe. Assim, pode-se criar um objeto de ClassDec tão logo saibamos o nome da classe. Isto é necessário pois o objeto que representa a classe deve logo ser inserido na Tabela de Símbolos, pois uma classe pode declarar um objeto dela mesma:

 

class A {

   private A x;

   ...

end

 

Assim, ao encontar o “x”, haverá uma busca na tabela de símbolos e será encontrado o objeto de ClassDec que representa a classe A, que foi inserido tão logo o nome da classe se tornou disponível. A mesma observação vale para MethodDec: podemos ter chamadas recursivas !

 

 

 

 

 

            Para comparar strings em Java, utilize compareTo:

         if ( methodId.compareTo(“new”) == 0 )

            ...

 

Assim, ao encontrar um comando

                       x = y;

o compilador deve procurar por x na tabela de símbolos de tal forma que o objeto de AssignmentStatement correspondente a esta atribuição tenha um ponteiro para o objeto Variable representando x. Este objeto é o que foi criado na declaração de x. O mesmo se aplica a y. Você deve fazer algum assim:

 

   // lexer.getStringValue() retorna “x”

   Variable left = st.getInLocal( lexer.getStringValue() );

   if ( left == null ) error.show(“...”);

   return new AssignmentStatement( left, expr() );

 

Duas estratégias ERRADAS são dadas abaixo.

 

1) representar x como String. A classe AssignmentStatement seria

   class AssignmentStatement {

      private String    leftSide;

      private Expr      rightSide;

      ...

   }

 

2) representar x como uma variável, mas criar esta variável ao encontrar x :

   // lexer.getStringValue() retorna “x”

   Variable left = new Variable( lexer.getStringValue() );

   return new AssignmentStatement( left, expr() ); 

 

 

            Os únicos lugares onde deve-se criar objetos de Variable, InstanceVariable e Parameter é na declaração das variáveis correspondentes. E nunca se deve representar variáveis por Strings --- utilize objetos de Variable e suas subclasses. O mesmo se aplica a ClassDec e ao tipo de variáveis.

 

 

 

 

            Os compiladores sinalizam uma exceção depois de mostrar um erro. Esta exceção deve ser (é) capturada em um bloco try no método compile da classe Compiler. A impressão da pilha de chamadas (com e.printStackTrace()) deve ser utilizada apenas na fase de depuração do compilador e não deve estar presente no compilador entregue ao professor.

 

 

 

           Seria interessante que vocês lessem um artigo sobre o compilador Green. Ele fala sobre algumas de minhas experiências na construção do compilador de Green.

 

 

            Não deve haver uma classe IdList no compilador. A produção IdList na gramática é utilizada para duas coisas diferentes: análise de variáveis de instância e variáveis locais.

 

 

 

            Interessante, mas ninguém até agora notou uma terrível inconsistência no nome dos métodos dos analisadores sintáticos que eu apresentei como exemplos. Lembram dos nomes dos métodos ? São expr, ifStat, whileStat, varDec, etc. Absurdamente errado. Estes nomes são substantivos. Métodos designam ações e portanto devem ter nomes verbais. Os nomes corretos seriam analyzeExpr, analyzeIfStat, etc, dizendo "analise uma expressão", ... Naturalmente, utizamos expr no lugar de analyzeExpr por um motivo de economia de digitação. Mas no vosso trabalho vocês não devem repetir este erro em outras classes que não o analisador sintático. O guia de programação recomenda que vocês coloquem nomes significativos nos métodos e isto significa colocar nomes verbais.

 

 

            Peguem o máximo possível de código dos trabalhos exemplo que deixei na página de Construção de Compiladores do semestre anterior. E peguem o máximo do compilador Green que agora está disponível nesta página:
           Todo o compilador
           O analisador sintático
           O analisador léxico
           A árvore de sintaxe abstrata

            Alguns vão achar difícil de entender o compilador, pois ele possui muitas outras coisas além do que vocês precisam. Mas ainda assim vale a pena vê-lo. Veja também a página da linguagem Green.

 

 

    Algumas dicas para fazer o trabalho:

  • Faça ClassDec (classe da ASA para uma classe de Simples) herdar de Type;
  • A ASA deve ser facílima de fazer. Se você não achar fácil, você não entendeu como fazê-la. Ela deve ser feita olhando-se a estrutura de um programa em Simples, NÃO a gramática Simples;
  • devem existir classes da ASA InstanceVariable, Parameter, Variable e MethodDec para variáveis de instância, parâmetros, ... e também InstanceVariableList, ParameterList, VariableList, MethodList para lista de variáveis de instância, ... Cada uma destas listas deve ter um método que gera código. Este método chama o método de gerar código de cada um dos elementos da lista. Há exemplos em abundância na ASA de Green. As classes
    “[A-Za-z]+List” são quase todas iguais;
  • você obrigatoriamente usará uma variável de instância currentClass da classe Compiler que aponta para o objeto que representa a classe corrente. Utilize esta variável para inserir uma lista de variáveis de instância na classe  ou uma lista de métodos (privados ou públicos);
  • tenha também uma variável de instância currentMethod na classe Compiler;
  • análise de declaração de variáveis e  métodos é chato de fazer, pois exigem uma lista de variáveis e uma lista de métodos. E deve existir uma lista de métodos públicos e outra lista de métodos privados. Veja como fiz no compilador Green e faça algo parecido. Ou veja os compiladores exemplo. Não perca tempo. Qualquer dúvida procure o professor.

 

 

 

            Utilize a classe PW para fazer a tabulação do código gerado corretamente. Faça assim: antes de usar pw, inicialize-a uma única vez com o objeto PrintWriter utilizado para saída
      pw.set(out);
pw possui métodos print e println que automaticamente indentam o que você imprime com  pw.print ou pw.println. Se você quiser aumentar a indentação, utilize pw.add(). Para diminuir, utilize pw.sub(). Teste o seguinte código
       PrintWriter out = new PrintWriter( ... );
       pw.set(out);  // faça isso uma única vez antes do início da geração de código
       pw.add();
       pw.print("if");
       pw.out.println( "  a > b");   // código sem indentação, pois foi escrito com pw.out
       pw.print("then");
       pw.add();    //  comandos dentro do then devem ser indentados
       pw.println("a = b;");
       pw.sub();    // diminui a indentação: acabou o then
       pw.println("endif");
       pw.sub();
       pw.println("Texto normal, não indentado");

Esta classe está no diretório AST do analisador sintático fornecido na página Material de Aula.

 

 

            Por curiosidade, mostrarei a geração de código de Green. A partir de uma classe Green A, do arquivo A.g, o compilador produz uma interface A.gi e arquivos em Java _A.java e $co$A.java. Este dois últimos contém o código compilado de A.g. São produzidos também arquivos contendo interfaces, um arquivo para cada método declarado na classe A. O código compilado é complexo porque ele contém suporte à biblioteca de reflexão introspectiva de Green. Esta biblioteca permite ao programa descobrir que classes o compõem, quais os métodos de cada classe ou objeto, os parâmetros de dado método, o tipo de cada variável de instância de certa classe, etc.

 

 

            Veja o site Java & Internet Glossary. Há muitas coisas interessantes lá. E possivelmente muitas dicas que vocês deverão empregar neste curso. Em particular, veja o significado das mensagens de erro dadas por compiladores Java e JVM.