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.

 

            Java exige que variáveis sejam iniciadas antes de serem utilizadas. Simples não. Assuma então que todas as variáveis dos programas em Simples sejam iniciadas antes da utilização, mesmo que a linguagem não exija isto. Isto é, assuma que o seguinte trecho de um programa em Simples seja ilegal:

var a, b : integer;

begin

a = b;   // não iniciou o b

...

end

Não é necessário que você confira se as variáveis foram iniciadas ou não.

 

        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 Expression  { ... }

class MessageSendToVariable extends MessageSend { ... }

class MessageSendToSelf     extends MessageSend { ... }

class MessageSendToSuper    extends MessageSend { ... }

class MessageSendStatement     extends Statement {

      MessageSend  messageSend;

      public void gen( PrintWriter out ) {

         ws.print(“”);

         messageSend.gen(out);

         out.println(“;”);

      }

    ...

}

 É melhor fazer MessageSend herdar de Expression 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 Expression.

            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 ObjetCreation ou MessageSendNew (você escolhe o nome). Em qualquer caso, a classe deve herdar de Expression, 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. Coloque apenas as variáveis locais, parâmetros (primeiro nível), variáveis de instância (segundo nível) e classes (terceiro nível). 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 = symbolTable.getInClass(“A”);

Assume-se que x tenha sido declarado como

        var x : A;

e que método getInClass da tabela de símbolos procure por um símbolo apenas nas classes da tabela.

            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 superclassName;

   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 )

            ...

 

 

           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.

            Não deve haver um método IdList no programa. 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.

            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 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. Vocês podem utilizar as classes que representam expressões da ASA de Green, pois as expressões em Green são muito semelhantes ou iguais às de Simples.

    Algumas dicas para fazer o trabalho:

 

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

 

            Na leitura de um valor inteiro (só inteiros podem ser lidos), você deve utilizar um método de Java que faz a leitura. Este método não está pronto. Utilize a classe abaixo

import java.io.*;


class Leia {
   
    public static int readInt() {
        int i = 0;
       
        try {
            BufferedReader br = new BufferedReader( new InputStreamReader(System.in));
            i = Integer.valueOf(br.readLine()).intValue();
        } catch (Exception e ) {
            System.out.println("Read error !!!!");
            System.exit(1);
        }
        return i;
    }
}


A instrução em simples  
        read(a, b, c)
deve ser traduzida para
   a = Leia.readInt();
   b = Leia.readInt();
   c = Leia.readInt();
em Java.

       Vocês podem utilizar o programa Creation para criar automaticamente um arquivo com as declarações de constantes do léxico. Funciona assim: suponha que você tenha feito o analisador sintático e lá tenha utilizado variáveis de uma classe Sym :

    if ( lexer.getToken ().tk != Sym.END )

Você ainda não declarou a classe Sym mas precisará criá-la, o que pode ser feito com o programa Creation. Chame-o com

       C:\>java Creation  Parser.java Sym

onde Parser. java é o nome do seu analisador sintático e Sym é o nome da classe Sym. Este programa criará uma classe Sym em um arquivo “Sym .java” que conterá as declarações de todos os símbolos. Lembre-se de que o analisador léxico deve estar em um outro arquivo que não “ Sym.java ”. O Creation irá sobreescrever qualquer arquivo “ Sym.java ” já existente.

             Lembre-se de que o diretório onde você colocou o programa Creation deve estar na variável “ classpath ”.

            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.