Observações

 

            Estarei em minha sala quase todo o tempo, principalmente às tardes. Se alguém não conseguir me encontrar, mande-me um e-mail para marcarmos um horário.

        Imprimam os trabalhos em fonte courier. Assim podemos ver ser a tabulação está correta. Para quem não sabe, letras em courier ocupam sempre o mesmo espaço horizontal, independente do tamanho da letra. O mesmo não ocorre com outras fontes, onde o “i”, por exemplo, ocupa menos espaço do que o “m” e outras letras mais gordinhas.

                Quanto à organização das classes de envio de mensagem, faça assim:

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.

            Note que há um envio de mensagem que não deve ser considerado um envio de mensagem: é a criação de objetos, com new:

    a = A.new();

 

Aqui, devemos criar um objeto de uma classe MessageSendNew (você escolhe o nome). Em qualquer caso, a classe deve herdar de Expr, não de MessageSend. Ou talvez você consiga fazer funcionar herdando de MessageSend.

          Na tabela de símbolos, não há necessidade 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 já 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 Simples). 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

        var x : A;

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 ClassDec (que representa uma classe de Simples), deve ser algo como

 

class ClassDec

   public ClassDec( String name ) {

      this.name = name;

   }

   private String name;

   private ClassDec superclass;

   private InstanceVariableList instanceVariableList;

   private MethodList publicMethodList, privateMethodList;

   // métodos públicos get e set para obter e iniciar as variáveis acima,

   // entre outros métodos

}

Não deve haver variáveis como publicPart ou privatePart. Nem deve haver classes PublicPart e PrivatePart. Nem deve existir classe UnPubPri.

            ClassDec deve herdar de Type para que a classe Variable e suas subclasses funcionem corretamente:

 

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).

            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:

      var x : A;

   ...

end

 

Assim, ao encontar o “x”, haverá uma busca na tabela de símbolos e lá será encontrado o objeto de ClassDec que representa a classe A, que foi inserido lá 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 três coisas diferentes: análise de parâmetros, variáveis de instância e variáveis locais. Em cada caso, o método IdList correspondente à produção IdList teria que criar objetos de Parameter, InstanceVariable e Variable. Então, crie três métodos iguais ao seu IdList correspondentes a cada uma das classes Parameter, InstanceVariable e Variable. Veja no compilador Green como foi feito.

            A expressão “true > false” é semânticamente correta em Simples. A sua tradução para Java resulta no mesmo código, “true > false”. Contudo, Java não considera esta expressão correta, embora devesse considerar. Não se preocupem com este problema. Resolvê-lo seria complicado e então vamos deixar as coisas assim mesmo.

 

            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", ... Eu esqueci de fazer esta observação durante o primeiro curso. 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:

 

            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.