Trabalho nº 3

Propaganda
Trabalho nº 3
DESENVOLVIMENTO DE UM INTERPRETADOR
DESCRIÇÃO
Pretende-se implementar um interpretador para a linguagem de programação Java+- apresentada no
enunciado do 2º trabalho. Recorde-se que esta linguagem é um subconjunto da linguagem Java em
que é possível utilizar variáveis e constantes dos tipos lógico e inteiro (de 32 bits) com sinal. Podem
também ser utilizadas variáveis dos tipos array de inteiros e de valores lógicos. A linguagem aceita
expressões aritméticas e lógicas e operações relacionais, construções condicionais do tipo if-else, e
construções while. Os programas da linguagem Java+- são constituídos por uma única classe (a classe
principal) contendo um único método (o método main).
É possível passar parâmetros, que deverão ser literais inteiros, a um programa Java+- através da linha
de comandos. Supondo que o nome dado ao argumento do método main() é “args”, os seus valores
podem ser recuperados através da construção “Integer.parseInt(args[...])”, e o número de parâmetros
pode ser obtido através da expressão “args.length”. A construção “System.out.println(...)” permite
imprimir valores inteiros ou lógicos.
O significado de um programa em Java+- será o mesmo que o seu significado em Java. Por exemplo, o
seguinte programa calcularia o máximo divisor comum de dois inteiros, e apresentaria esse valor no
écran:
class gcd {
public static void main(String[] args) {
int[] x;
x = new int[2];
x[0] = Integer.parseInt(args[0]);
x[1] = Integer.parseInt(args[1]);
if (x[0] == 0)
System.out.println(x[1]);
else
while (x[1] > 0) {
if (x[0] > x[1])
x[0] = x[0] - x[1];
else
x[1] = x[1] - x[0];
}
Compiladores 2012-2013 | Trabalho nº 3
1
System.out.println(x[0]);
}
}
A gramática da linguagem foi também apresentada no 2º trabalho, e reproduz-se de seguida.
TOKENS E GRAMÁTICA INICIAL DA LINGUAGEM JAVA+ID = [A-Za-z_][A-Za-z0-9_]*
INTLIT = [0-9]+ | “0x” [0-9a-fA-F]+
BOOLLIT = “true” | “false”
INT = “int”
BOOL = “boolean”
NEW = “new”
IF = “if”
ELSE = “else”
WHILE = “while”
PRINT = “System.out.println”
PARSEINT = “Integer.parseInt”
CLASS = “class”
PUBLIC = “public”
STATIC = “static”
VOID = “void”
STRING = “String”
DOTLENGTH = “.length”
OCURV = “(”
CCURV = “)”
OBRACE = “{”
CBRACE = “}”
OSQUARE = “[”
CSQUARE = “]”
OP1 = “&&” | “||”
OP2 = “<” | “>” | “==” | “!=” | “<=” | “>=”
OP3 = “+” | “-”
OP4 = “*” | “/” | “%”
Compiladores 2012-2013 | Trabalho nº 3
2
NOT = “!”
ASSIGN = “=”
SEMIC = “;”
COMMA = “,”
Start → Program EOF
Program → CLASS ID OBRACE PUBLIC STATIC VOID ID OCURV STRING OSQUARE
CSQUARE ID CCURV OBRACE { VarDecl } { Statement } CBRACE CBRACE
VarDecl → Type ID { COMMA ID } SEMIC
Type → INT | BOOL | INT OSQUARE CSQUARE | BOOL OSQUARE CSQUARE
Statement → OBRACE { Statement } CBRACE
Statement → IF OCURV Exp CCURV Statement ELSE Statement
Statement → WHILE OCURV Exp CCURV Statement
Statement → PRINT OCURV Exp CCURV SEMIC
Statement → ID ASSIGN Exp SEMIC
Statement → ID OSQUARE Exp CSQUARE ASSIGN Exp SEMIC
Exp → Exp (OP1 | OP2 | OP3 | OP4) Exp
Exp → Exp OSQUARE Exp CSQUARE
Exp → INTLIT | ID | BOOLLIT
Exp → NEW INT OSQUARE Exp CSQUARE
Exp → NEW BOOL OSQUARE Exp CSQUARE
Exp → OCURV Exp CCURV
Exp → Exp DOTLENGTH | NOT Exp
Exp → PARSEINT OCURV ID OSQUARE Exp CSQUARE CCURV
IMPLEMENTAÇÃO
O interpretador deve ser implementado em Java utilizando as ferramentas JJTree e JavaCC. A
gramática inicial dada já deverá ter sido modificada de modo a eliminar ambiguidades, a reflectir a
precedência dos operadores em Java, e a permitir a análise descendente com LOOKAHEAD=1.
O interpretador deverá chamar-se Javapm, ler o programa a processar do stdin e os seus argumentos
através da linha de comandos, e emitir o resultado para o stdout. Caso o ficheiro gcd.jpm contenha o
exemplo dado na primeira página deste enunciado, a invocação
java Javapm 12 8 < gcd.jpm ↵
deverá interpretar o programa gcd.jpm passando-lhe os parâmetros 12 e 8, e imprimir no écran:
4
Compiladores 2012-2013 | Trabalho nº 3
3
Para efeitos de verificação, o interpretador deve fornecer ainda as seguintes opções:
•
•
-t : imprime a árvore de expressões construída durante a análise do programa
-s : imprime o conteúdo da tabela de símbolos após a interpretação do programa
Estas opções são passadas na linha de comandos, antes dos parâmetros. Mais concretamente:
java Javapm -t 12 8 < gcd.jpm ↵
deverá imprimir a árvore de expressões e terminar, sem proceder à interpretação do programa. Neste
caso, os restantes parâmetros (se existirem) devem ser ignorados.
Por outro lado,
java Javapm -s 12 8 < gcd.jpm ↵
deverá proceder à interpretação do programa e imprimir a tabela de símbolos, separada da saída do
programa por uma linha em branco, do seguinte modo:
4
=== Symbol table ===
Name
Type
Init
Values
----
----
----
------
args
args[]
true
2
12
8
x
int[]
true
2
4
0
O formato das linhas correspondentes às variáveis é o seguinte:
•
•
•
•
Name – o identificador da variável, tal como aparecer no programa a interpretar, e pela
mesma ordem.
Type – o tipo da variável. Os valores possíveis são “int”, “bool”, “int[]”, “bool[]” ou
“args[]”. O (pseudo) tipo “args[]” é usado para indicar o identificador que é usado no
programa para aceder aos parâmetros passados na linha de comandos.
Init – “true” se a variável tiver sido inicializada durante a interpretação do programa,
“false” caso contrário.
Values – O(s) valor(es) da variável, se esta tiver sido inicializada. Se a variável for do tipo
inteiro ou booleano, é simplesmente o seu valor. Se for do tipo array, o primeiro valor é a
dimensão do array e os restantes são os elementos do array.
As colunas desta tabela devem ser separadas pelo caracter “\t”. O analisador deve aceitar (e ignorar)
comentários iniciados por // (sem incluir o caracter de mudança de linha!) e do tipo /* */, bem como
detectar a existência de quaisquer erros de sintaxe ou de semântica no ficheiro de entrada.
ÁRVORES DE INSTRUÇÕES E DE EXPRESSÕES
De modo a permitir a validação dos trabalhos no Mooshak, as árvores de expressões e de instruções
geradas durante a análise sintática devem respeitar as seguintes orientações:
•
•
•
Nó raiz: Program
Declaração de variáveis: VarDecl
Statements: Seq If While Print Sta Stl
Compiladores 2012-2013 | Trabalho nº 3
4
•
•
Operadores: Or And Eq Neq Lt Gt Leq Geq Add Sub Mul Div Rem Not Length Lda
NewInt NewBool ParseArgs
Terminais: Id IntLit BoolLit Type
Um ficheiro com a árvore correspondente ao programa apresentado na primeira página é fornecido
juntamente com este enunciado.
TRATAMENTO DE ERROS
Quaisquer erros sintáticos detectados durante a análise deverão ser tratados como foi especificado no
enunciado do 2º trabalho. Relativamente aos erros semânticos, o interpretador deverá detectar os
seguintes tipos de erros:
main() method is not called main
<symbol> already defined
cannot find symbol <symbol>
<symbol> not initialized
incompatible type (got <type>, required int)
incompatible type (got <type>, required bool)
incompatible type (got <type>, required int or bool)
incompatible type (got <type>, required int[])
incompatible type (got <type>, required bool[])
incompatible type (got <type>, required args[])
incompatible type (got <type>, required int[] or bool[])
incompatible type (got <type>, required int[] or bool[] or args[])
operator <op> cannot be applied to <type>
operator <op> cannot be applied to <type>,<type>
onde:
•
•
•
<type> representa um dos tipos “int”, “bool”, “int[]”, “bool[]” ou “args[]”
<symbol> representa um identificador
<op> representa um dos 14 operadores da linguagem (incluindo o operador unário NOT,
“!”)
Caso algum destes erros seja detectado durante a interpretação do programa, o interpretador deverá
terminar imediatamente com uma mensagem de erro no seguinte formato:
Semantic error: <msg>
onde <msg> representa uma das mensagens acima. Sempre que a uma expressão corresponder mais
do que um erro semântico (por exemplo, a tentativa de indexação de uma expressão do tipo int com
um valor booleano), deverá ser reportado o erro relativo à primeira expressão envolvida (neste caso, o
facto de não ser possível indexar um valor do tipo int, ou seja: “incompatible type (got int, required
int[] or bool[])”).
Compiladores 2012-2013 | Trabalho nº 3
5
O tratamento de erros de execução, como por exemplo, a indexação de um array com um valor
negativo ou a divisão por zero, está para além dos objectivos deste trabalho.
SUBMISSÃO DO TRABALHO
O trabalho deverá ser validado no Mooshak, usando o concurso COMP1213_T3.
O relatório deve ser submetido na tutoria da disciplina e não deve conter mais de 6 páginas. Do
relatório devem constar:
1. A gramática, usada para implementar o interpretador, gerada pelo JJDoc;
2. Uma descrição da estratégia de implementação adoptada, detalhando aspectos como o formato da
tabela de símbolos, o método de avaliação das expressões, o acesso aos parâmetros, etc.
3. Outras descrições do interpretador que achar pertinentes;
4. Caso não implemente toda a funcionalidade pedida, deverá detalhar os aspectos não
implementados.
NOTAS
1. A tarefa A do concurso COMP1213_T3 contém todos os testes parciais incluídos nas outras tarefas;
2. Todos os trabalhos serão alvo de apresentação e discussão.
Compiladores 2012-2013 | Trabalho nº 3
6
Download