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.