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