REPL(Read-eval-print loop)を作る。
Kotlinソースのコードリーディングで、JLineというコンソール入出力ハンドリングライブラリを知りました。
JLine:http://jline.sourceforge.net/
Kotlin GitHub:https://github.com/JetBrains/kotlin
KotlinのREPLに使用されています。
JLineでやっていることをコーディングした記憶があり、ちょっとショック。
やはり、知らないという事は悪なのです。
今回はKotlinは使っていません。Javaで作りました。
とはいえ、REPLの構文解析/構文実行等は作っていません。言語仕様も決めていませんので。
起動、コマンド(Kotlin REPLの:〜)、構文入力受け付け的な所まで。
KotlinのREPLを真似してみました。
package replbase;
import java.io.IOException;
import java.io.PrintWriter;
import jline.ConsoleReader;
public class REPLBase {
private final String COMMAND = ":";
private final String PROMPT_READLINE = ">>> ";
private final String PROMPT_INCOMPLETE = "... ";
private ConsoleReader consoleReader;
enum STATUS {
READLINE,
INCOMPLETE,
QUIT
}
private STATUS status = STATUS.READLINE;
public static void main(String[] args) {
REPLBase replBase = new REPLBase();
replBase.start();
}
private void start() {
try(PrintWriter printwriter = new PrintWriter(System.out);) {
consoleReader = new ConsoleReader(System.in, printwriter);
consoleReader.printString("Welcome to REPLBase.(JRE " + System.getProperty("java.runtime.version") + ")\n");
consoleReader.printString("Type :help for help, :quit for quit\n");
while(status != STATUS.QUIT) {
String input = readLine();
analyze(input);
}
}
catch(IOException exception) {
System.err.printf(exception.toString());
}
}
private String readLine() throws IOException{
String prompt = status == STATUS.READLINE ? PROMPT_READLINE : PROMPT_INCOMPLETE;
String input = consoleReader.readLine(prompt);
return input;
}
private void analyze(String input) throws IOException {
if((status != STATUS.INCOMPLETE) &&
(input.startsWith(COMMAND) == true)) {
command(input);
}
else {
compile(input);
}
}
private void compile(String input) {
if(input.endsWith("}") != true) {
status = STATUS.INCOMPLETE;
}
else {
status = STATUS.READLINE;
}
}
private void command(String input) throws IOException {
String command = input.replaceFirst(COMMAND, "");
switch(command) {
case "help":
consoleReader.printString("Available commands:\n" +
":help show this help\n" +
":quit exit the interpreter\n");
break;
case "quit":
status = STATUS.QUIT;
break;
default:
consoleReader.printString("Unknown command\n" +
"Type :help for help\n");
break;
}
}
}
これをベースに拡張出来ます。
REPLBaseをベースクラスにすれば色々使えます。
start()メソッド内のConsoleReaderがJLineです。
readline()メソッドでプロンプト表示/入力待ち/入力ライン受け取りです。
受け取った結果をanalyze()内でコマンド処理(command())と構文解析以降処理(compile())に振り分けています。
command()メソッドを拡張すれば、コマンドを追加/変更出来ます。
compile()メソッドを拡張すれば、REPLの本体を実装出来ます。
analyze()/command()/compile()を派生側で実装出来るようにすることで拡張可能になります。