Java
REPL

REPL(Read-eval-print loop)を作る。

More than 1 year has passed since last update.

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を真似してみました。

Kotlin REPL起動

replkt1.png

Kotlin REPLコマンド実行

replkt2.png

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()を派生側で実装出来るようにすることで拡張可能になります。