Para quem não está acompanhando o POJ (Pascal on the JVM) é um compilador que transforma um subset de Pascal para JASM (Java Assembly) de forma que possamos usar a JVM como ambiente de execução.
Na última postagem foi abordado contextos (do parser) e sentenças aninhadas. Nesta publicação vamos falar sobre as alterações necessárias para possibilitar a leitura de dados da entrada padrão (stdin), isso utilizando a função read/readln do Pascal.
Como estamos compilando para a JVM faz-se necessário detalhar o funcionamento de vários pontos desta incrível máquina virtual. Com isso, em vários momentos eu detalho o funcionamento interno da JVM bem como algumas das suas instruções (opcodes).
Lendo dados de stdin (entrada padrão)
Standard input (stdin) é o stream do qual um programa lê seus dados de entrada. Até o momento tínhamos suporte ao stdout (saída padrão) apenas.
Neste commit foi implementado um programa em Java para entendermos como a JVM lida com stdin:
public class InputData {
public static String name;
public static int age;
public static void main(String[] args) {
name = System.console().readLine();
age = Integer.parseInt(System.console().readLine());
System.out.println("You entered string " + name);
}
Quando desassemblamos o arquivo class obtemos o assembly abaixo. Trechos irrelevantes foram omitidos, bem como o trecho original (em Java) que deu origem ao assembly foi inserido com ";;":
1: public class InputData {
2: ;; public static String name;
3: public static name java/lang/String
4:
5: ;; public static int age;
6: public static age I
7:
8: public static main([java/lang/String)V {
9: ;; name = System.console().readLine();
10: invokestatic java/lang/System.console()java/io/Console
11: invokevirtual java/io/Console.readLine()java/lang/String
12: putstatic InputData.name java/lang/String
13:
14: ;; age = Integer.parseInt(System.console().readLine());
15: invokestatic java/lang/System.console()java/io/Console
16: invokevirtual java/io/Console.readLine()java/lang/String
17: invokestatic java/lang/Integer.parseInt(java/lang/String)I
18: putstatic InputData.age I
19:
20: ;; System.out.println("You entered string " + name);
21: getstatic java/lang/System.out java/io/PrintStream
22: getstatic InputData.name java/lang/String
23: invokedynamic makeConcatWithConstants(java/lang/String)java/lang/String {
invokestatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants(java/lang/invoke/MethodHandles$Lookup, java/lang/String, java/lang/invoke/MethodType, java/lang/String, [java/lang/Object)java/lang/invoke/CallSite
["You entered string "]
}
24:
25: invokevirtual java/io/PrintStream.println(java/lang/String)V
26:
27: return
}
}
Com este exemplo foi possível identificar que para ler dados da stdin era necessário utilizar a instrução System.console().readLine() (linhas 11 e 16). E como readLine() retorna uma string, para lermos números era necessário converter com o uso da função Integer.parseInt (linha 17).
Dito isso, a partir do programa Pascal abaixo:
program NameAndAge;
var
myname: string;
myage: integer;
begin
write('What is your name? '); readln(myname);
write('How old are you? '); readln(myage);
writeln;
writeln('Hello ', myname);
writeln('You are ', myage, ' years old');
end.
O POJ foi ajustado para gerar o seguinte JASM:
// Code generated by POJ 0.1
public class name_and_age {
;; var myname: string;
public static myname java/lang/String
;; var myage: integer;
public static myage I
;; procedure main
public static main([java/lang/String)V {
;; write('What is your name? ');
getstatic java/lang/System.out java/io/PrintStream
ldc "What is your name? "
invokevirtual java/io/PrintStream.print(java/lang/String)V
;; readln(myname);
invokestatic java/lang/System.console()java/io/Console
invokevirtual java/io/Console.readLine()java/lang/String
putstatic name_and_age.myname java/lang/String
;; write('How old are you? ');
getstatic java/lang/System.out java/io/PrintStream
ldc "How old are you? "
invokevirtual java/io/PrintStream.print(java/lang/String)V
;; readln(myage);
invokestatic java/lang/System.console()java/io/Console
invokevirtual java/io/Console.readLine()java/lang/String
invokestatic java/lang/Integer.parseInt(java/lang/String)I
putstatic name_and_age.myage I
;; writeln;
getstatic java/lang/System.out java/io/PrintStream
invokevirtual java/io/PrintStream.println()V
;; writeln('Hello ', myname);
getstatic java/lang/System.out java/io/PrintStream
ldc "Hello "
invokevirtual java/io/PrintStream.print(java/lang/String)V
getstatic java/lang/System.out java/io/PrintStream
getstatic name_and_age.myname java/lang/String
invokevirtual java/io/PrintStream.print(java/lang/String)V
getstatic java/lang/System.out java/io/PrintStream
invokevirtual java/io/PrintStream.println()V
;; writeln('You are ', myage, ' years old');
getstatic java/lang/System.out java/io/PrintStream
ldc "You are "
invokevirtual java/io/PrintStream.print(java/lang/String)V
getstatic java/lang/System.out java/io/PrintStream
getstatic name_and_age.myage I
invokevirtual java/io/PrintStream.print(I)V
getstatic java/lang/System.out java/io/PrintStream
ldc " years old"
invokevirtual java/io/PrintStream.print(java/lang/String)V
getstatic java/lang/System.out java/io/PrintStream
invokevirtual java/io/PrintStream.println()V
return
}
}
Este commit implementa as alterações necessárias no parser do POJ.
Aqui está o PR completo.
Próximos passos
Na próxima publicação vamos concluir um dos objetivos deste projeto: cálculo do fatorial de forma recursiva.
Código completo do projeto
O repositório com o código completo do projeto e a sua documentação está aqui.
Top comments (0)