Laboratório de Compiladores

Últimas notícias:

12 de Abril

       Ao entregar o segundo trabalho (tradução de Simples para C), coloque a nota que você merece na primeira página do trabalho. Se o trabalho satisfaz todas as exigências que pedi, dê 10.

4 de Abril

            Havia dois testes errados:  ok-sem01.s e ok-ger14.s. Eles foram corrigidos e os testes corretos estão aqui. O teste ok-ger13.s não deve ser utilizado. Corrigi x.bat para que ele não utilize este teste.

 

28 de Março

       Está pronto o programa que analisará o seu compilador e avisará quantos erros ele deixou de apontar nos exemplos e quantos erros ele apontou e que não existem. Para utilizá-lo, faça o seguinte: copie x.bat e degree para o diretório onde estão os 150 testes. Tenha a certeza de que o seu compilador está em um diretório citado no classpath. Descomprima degree.zip e execute x.bat. Ele criará arquivos ok-*.txt e er-*.txt. Agora chame o programa degree da seguinte forma:

     java Degree

Este programa criará um arquivo out.txt. Veja este arquivo com more:

     more out.txt

Em out.txt estarão descritos os erros do seu compilador.

            Naturalmente, assume-se que o seu compilador possa ser chamado pela linha de comando

    java comp Hello.s

onde comp é o nome do compilador. Observe que não pode ser outro nome. Tem que ser comp.

 

            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.

27 de Março

            Esqueçam a observação do dia 20 de Março sobre o segundo trabalho. Como não daria tempo mesmo, achei melhor pedir apenas a tradução de um programa em Simples para C, manualmente. Faça um pequeno programa em Simples e o traduza para C. O programa deve ter:

a) uma herança. Então devem existir pelo menos duas classes;

b) uma redefinição de método na subclasse. Isto é, se B herda de A e A possui método m, B deve ter um método m;

c) um envio de mensagem a super;

d) um envio de mensagem a self;

e) duas variáveis de instância em classes diferentes.

f) envios de mensagem, atribuições, criações de objetos, etc

A data de entrega é 12 de Abril. Se tiverem dificuldade, me procurem. Estou acreditando que vocês não gastarão mais do que duas horas para fazer este trabalho. Avisem-me se estiver mais difícil.

            Alguém tem um CD com o compilador C++ da Borland para me emprestar ? Prefiro a versão 4.52.

            Estou disponibilizando 150 testes para o compilador de Simples. Gostaria que vocês testassem o vosso compilador utilizando estes testes. Provavelmente amanhã, Quinta, disponibilizarei um programa que os auxiliará a testar o compilador com todos os testes. Há testes para o analisador léxico, sintático e semântico. Um teste chamado er-sin04.s contém um erro e testa o analisador sintático. É o quarto teste deste tipo. Da mesma forma, um teste ok-lex03.s testa o analisador léxico, não contém um erro (está ok) e é o terceiro teste deste tipo.

            Todos os grupos deverão marcar uma entrevista comigo até o dia 12 de Abril. Os elementos que não apareceram na primeira entrevista deverão obrigatoriamente vir nesta. Na entrevista testarei os programas com os 150 testes, o que será decisivo para a nota. O grupo deve trazer um disquete com o trabalho pronto para ser executado. Isto é, os arquivos devem estar compilados e nos diretórios corretos. Lembrem-se de que um programa em Simples deve ser compilado pela linha de comando

       java comp Hello.s

onde comp é o nome do compilador e Hello.s o nome do programa a ser compilado. O compilador deve ainda produzir um arquivo chamado result.txt contendo 0 ou 1. Estas duas condições são fundamentais para que eu teste o compilador e sem as quais me será impossível dar a nota. Se necessário, modifiquem o compilador que entregaram para que ele pegue o maior número de erros possível.

           

20 de Março

 

            O segundo trabalho é a construção de um gerador de código para Simples cuja saída é C. A tradução de Simples para C é descrita no artigo “Geração e Otimização de Código Orientado a Objetos”. Este artigo não possui as figuras 1 e 7, que podem ser obtidas aqui.

            Teremos aula na Segunda dia 25 de Março. O número das salas é 157, 191, 172, 159 para os horários de 8-10, 10-12, 14-16 e 16-18, respectivamente.

 

18 de Março

            Por causa de inúmeros pedidos, decidi adiar a data de entrega dos trabalhos para o dia 22 de Março. Possivelmente teremos aula na Segunda feira dia 25 de Março: eu explicarei como gerar código em C para Simples. Mas aguardem outro comunicado amanhã, Terça ou no máximo Quarta feira.

            Alguns grupos não apareceram para a entrevista. Eles receberão nota 1 na primeira parte do trabalho, que valia 3.

            Alguns grupos estão me devendo a listagem da primeira parte do trabalho, que eu pedi na entrevista. Entreguem-na o mais rápido possível. Se eu não pedi, não precisa entregar.

            Esqueçam a organização das classes MessageSend dada no dia 4 de Março (veja abaixo). É melhor fazer MessageSend herdar de Expression. 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. Assim, a organização das classes ficaria

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(“;”);

      }

....

}

 

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.

4 de Março

Pessoal, seria melhor organizar as classes de envios de mensagem assim:

 

abstract class MessageSend  extends Statement { ... }

class MessageSendToVariable extends MessageSend { ... }

class MessageSendToSelf     extends MessageSend { ... }

class MessageSendToSuper    extends MessageSend { ... }

class MessageSendExpr              extends Expression {

      MessageSend  messageSend;

....

}

teríamos então um MessageSendToVariable que substitui o MessageSend anterior. E seria acrescentada uma nova classe abstrata MessageSend, superclasse de todo mundo. Desta forma o código fica melhor organizado. Quem já fez da maneira anterior e está funcionando, pode deixar. Quem não fez, utilize o esquema acima.

            Muita gente fez uma classe ProcHeading. Ela é desnecessária. Coloquem dentro da classe  Method tudo o que está em ProcHeading.

1 de Março

        Imprimam os próximos 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.

27 de Fevereiro

            O trabalho foi adiado para o dia 15 de Março, impreterivelmente.

            Coloque três tipos de envio de mensagem: MessageSend, MessageSendToSelf, MessageSendToSuper. A primeira é para envios de mensagens para variáveis, como “x.m()”. A segunda é para envios de mensagens para “self” ou para ninguém, como “m()”. O terceiro tipo é para envios para super, como “super.m()”. Você possivelmente precisará utilizar uma classe MessageSendExpr que contém um ponteiro para MessageSend. Esta classe deve ser utilizada quando um envio de mensagem for uma expressão, como em “a = x.m()”. Você pode organizar as classes assim:

class MessageSend extends Statement { ... }

class MessageSendToSelf extends MessageSend { ... }

class MessageSendToSuper extends MessageSend {  ... }

class MessageSendExpr extends Expression {

      MessageSend  messageSend;

....

}

 

Ou se você quiser pode inverter as coisas: fazer MessageSend herdar de Expression e criar uma classe MessageSendStatement que contém um ponteiro para MessageSend. Aí todas as instruções que são envios de mensagem, como “x.m();” serão representadas por uma instrução MessageSendStatement.

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

            ...

 

25 de Fevereiro

           Seria interessante que vocês lessem um artigo sobre o compilador Green que estou submetendo ao Simpósio Brasileiro de Linguagens de Programação. 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.

18 de Fevereiro

            O comando loop-end existe na gramática, mas não está definido no texto. Ele funciona da seguinte forma:

loop

...

end

 

Os comandos entre loop e end repetem-se até que um comando break seja executado. É idêntico a

while ( true ) {

...

}

 

de Java.

            Convoco todos os grupos a me mostrar o trabalho até o fim da semana que vem, de preferência o mais cedo possível. Gostaria de conversar com as pessoas, com a listagem do código em mãos, para que eu possa dar sugestões de como adiantar o trabalho, para que ninguém perca mais tempo. Enviem-me um e-mail para marcarmos um horário. Meus horários livres são:

Se vocês me encontrarem no departamento, podem conversar comigo em qualquer horário. Notem que esta convocação é obrigatória e contará como entrevista do trabalho, embora o objetivo principal seja facilitar o vosso trabalho e não avaliar o vosso desempenho. Em tempo: estarei com a listagem dos trabalhos que vocês entregarem na minha sala. Assim, não é necessário tirar outra cópia. E lembrem-se de que vocês podem e devem utilizar o fineprint.

14 de Fevereiro

            Pessoal, tinha esquecido de colocar a gramática Green com o CUP. Ela está incompleta, pois desisti de fazer o Green usando o CUP. É muito complicado para mim.

 

31 de Janeiro

        Quando alguém quiser tirar dúvidas e não me encontrar na minha sala, envie-me um email. Leio-os todos os dias e podemos marcar uma "reunião" para o dia seguinte. Preciso saber das dúvidas das pessoas para que eu possa colocar aqui explicações que ajudaram a todos os grupos.

     A classe ws, descrita abaixo ,  não está boa. Esta classe será utilizada para fazer a saída do compilador. No nosso caso, o código em Java gerado. A saída é feita para uma variável de PrintWriter em métodos, digamos, chamados gen :
   void gen( PrintWriter out ) {
      ws.print("if (");
      ifExpr.gen(out);
      out.println(") {")
      ...
      }

O problema é que estamos misturando uma saída feita por um parâmetro, out, com outra feita por uma classe que funciona com variável global. Inconsistente. O correto seria fazer tudo com parâmetros. Para tanto, deveríamos modificar a classe ws  tornando todos os seus métodos não estáticos. E depois podemos passar dois parâmetros ao método gen, out e um objeto de ws, ou colocar a funcionalidade de PrintWriter dentro da classe ws, o que é muito melhor. Assim, teríamos que simplesmente acrescentar um método a ws que imprime como o print e println de out. Só isso. Ou criar uma classe, fazê-la herdar de PrintWriter  e definir nela novos métodos equivalentes ao add, sub, set, print e println de ws. Eu ficaria imensamente contente se algum grupo fizesse essas modificações.

    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.

30 de Janeiro

      Nas últimas semanas eu estive muitíssimo ocupado e foi difícil de me encontar. Agora estou um pouco mais livre e estarei mais no DC. Os horários mais prováveis de me encontrar são das 11:30 às 12 horas e das 16 às 18 horas. Como o trabalho que tenho a fazer é teórico (não no computador), não posso fazê-lo no DC, tenho que trabalhar em casa.
    Muitas pessoas estão reinventando a roda ao fazer o trabalho. Por exemplo, tem gente fazendo o analisador léxico desde o início quando poderia simplesmente modificar um dos analisadores exemplo do semestre passado. É trabalho para um dia, no máximo. Peguem o máximo possível de código dos trabalhos exemplo que deixei na apostila. 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:


A data da entrega da primeira parte do trabalho foi mudada para o dia 18 de Fevereiro.

28 de Janeiro

    Na produção
           ReadStat ::=  "read"  "("  LeftValue  { LeftValue } ")"
Leia-se
           ReadStat ::=  "read"  "("  LeftValue  { ","  LeftValue } ")"

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
   Leia.readInt(a);
   Leia.readInt(b);
   Leia.readInt(c);
em Java.

 




10 de Janeiro

      Pessoal, 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 ”.


7 de Janeiro

    Temos 17 grupos fazendo o compilador com o CUP e 13 sem. Para equilibrar a relação, dois grupos que estão utilizando o CUP utilizarão o método  recursivo descendente. O primeiro grupo que me procurou e pediu a troca já foi contemplado. Quem quiser mudar do recursivo descendente para CUP deve me enviar um e-mail  o mais rápido possível --- ainda temos uma "vaga".

19 de Dezembro

      A maioria dos grupos ainda não me procurou para pegar o seu peixe. Ou não me comunicaram que peixe pegaram. Assim, permitirei que **alguns** grupos troquem o CUP pelo recursivo descendente para equilibrar a relação
            número de grupos com CUP/número de grupos com recursivo descendente
Há mais grupos, na minha relação, com o CUP do que sem ele. Naturalmente, os grupos que já me deram a lista de integrantes/peixe terão preferência na troca.

18 de Dezembro

    A nova data para a parte I do trabalho é  1 de Março, sendo que até o dia 11 de Fevereiro todos deverão me entregar a listagem do código que já tiverem feito (isto valerá 3 dos 10 pontos do primeiro trabalho). Ao imprimir a listagem, é recomendável utilizar o fineprint para economizar papel e tinta. Se você não tiver este software, me procure.

    A maioria dos grupos ainda não me enviou a listagem dos integrantes. Isto já deveria ter sido feito há muito.
Confira se o seu grupo já foi cadastrado . Tenho os integrantes mas não o peixe de alguns grupos. Se tiver curiosidade, procure informações seu peixe em um destes sites .

    Algumas pessoas me procuraram querendo mudar o trabalho do CUP para Recursivo Descendente. Gostaria que vocês trocassem os peixes enter si de tal forma que metada da turma utilize o CUP e a outra não. Apesar do CUP ser chato, o seu equivalente procedimental, o YACC, é utilizado nas empresas. Então seria muito bom que pelo menos alguns de vocês soubessem utilizá-lo. Poderá ser útil no futuro e teremos uma base de comparação das duas formas de construir um compilador. De qualquer forma, quando as pessoas me mandarem os nomes de todos os grupos com os respectivos peixes, poderei balancear os trabalhos CUP/sem CUP. Pelas informações que tenho, há oito grupos com CUP e dois sem.

    Recomendo fortemente uma segunda leitura da descrição do
trabalho que também contém algumas observações técnicas. Não se esqueça também do Guia de Correção dos Trabalhos .


10 de Dezembro

      Pessoal, o semestre recomeçou oficialmente hoje. Mas tenho a impressão, como seria de se esperar, de que a maioria dos alunos ainda não retornou. Por causa disso, só marcarei novos prazos de entrega dos trabalhos em Janeiro. Eu acho até possível que a UFSCar não considere estas duas semanas no cômputo do semestre. Seria muito razoável.

     A maior parte dos alunos ainda não me comunicou a composição de seus grupos e sequer vieram pegar a senha para o trabalho. As senhas só poderão ser pegas comigo, por e-mail ou pessoalmente. Os grupos poderão trocar as senhas entre si.

      Creio que todos vocês estão animadíssimos para continuar o trabalho de compiladores, com toda a energia acumulada em cem dias de greve. Vamos ao trabalho ! Se tiverem dúvidas, e elas serão muitas,   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.

19 de Setembro
 
       Para os que estão fazendo o compilador com o CUP: como colocar o + e - unários com maior precedência do que os operadores restantes ?
Coloque normalmente a precedência dos operadores e acrescente um operador inexistente UMINUS e UPLUS:

precedence left  OR;
precedence left  XOR;
...    // outros operadores
precedence nonassoc LEFTSHIFT, RIGHTSHIFT;
precedence right NOT, UMINUS, UPLUS, BINNOT, PLUSPLUS, MINUSMINUS;

Na regra com o operador unário, utilize % prec :

Expr ::=
    Expr:left OR         Expr:right {:
      RESULT = new BooleanCompositeExpr(left, Sym.OR, right);
      :}
      |
    Expr:left XOR        Expr:right {:
      RESULT = new BooleanCompositeExpr(left, Sym.XOR, right);
      :}
      |
...  // outras regras
    MINUS      Expr:right   {:
      RESULT = new ArithmeticUnaryExpr( Sym.MINUS, right);
      :} %prec UMINUS
      |
    PLUS       Expr:right   {:
      RESULT = new ArithmeticUnaryExpr( Sym.PLUS, right);
      :} %prec UPLUS
      ;
 
 
20 de Agosto

     Está disponível a lista que associa a senha ao tipo de trabalho . "Recursivo descendente" quer dizer "Análise recursiva descendente e análise léxica feita sem auxílio de ferramentas" e CUP significa "Utilize o CUP e JLex".
    A partir de Quarta feira, as senhas só poderão ser pegas comigo, pessoalmente ou por e-mail.
    Comecem a fazer o trabalho. Lembrem -se de que fazer a análise léxica será fácil. Fazer a análise sintática será fácil. Construir a ASA será razoavelmente fácil. Gerar código em Java será fácil. Mas fazer a análise semântica será difícil, embora vocês tenham perfeitas condições de fazê-la. Não deixem para começar perto do fim do prazo.

Trabalhos    (pdf)
Capa dos Trabalhos    (pdf)
A Linguagem Simples (
ps ) ( pdf )
Guia de Correção dos Trabalhos

O artigo "Geração de Código em C para Simples" será colocado nesta página assim que estiver corrigido.