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