「JavaでREPLを作る。」のREPLBaseを改良。
JavaScriptを利用して計算機インタプリタを作る。
JavaからJavaScript実行はScriptEngineManagerとScriptEngineを使用します。
JavaScript実行
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("JavaScript");
scriptEngine.eval("print(eval(¥"1+1¥"))")
REPLBase.java
ベースクラス化しました。
起動メッセージ、プロンプト等を派生側で設定可能にしました。
compile()メソッドを抽象メソッドにし、派生側で実装するようにしました。
REPLBase.java
package replbase;
import java.io.IOException;
import java.io.PrintWriter;
import jline.ConsoleReader;
abstract public class REPLBase {
protected String COMMAND = ":";
protected String PROMPT_READLINE = ">>> ";
protected String PROMPT_INCOMPLETE = "... ";
protected String START_MESSAGE = "Welcome to REPLBase.(JRE " + System.getProperty("java.runtime.version") + ")\n" +
"Type :help for help, :quit for quit\n";
private ConsoleReader consoleReader;
enum STATUS {
READLINE,
INCOMPLETE,
QUIT
}
private STATUS status = STATUS.READLINE;
public void start() {
try(PrintWriter printwriter = new PrintWriter(System.out);) {
consoleReader = new ConsoleReader(System.in, printwriter);
if((START_MESSAGE != null) && (START_MESSAGE.isEmpty() == false)) {
printString(START_MESSAGE);
}
while(status != STATUS.QUIT) {
String input = readLine();
analyze(input);
}
}
catch(IOException exception) {
System.err.println(exception);
}
}
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);
}
}
abstract protected void compile(String input);
private void command(String input) {
String command = input.replaceFirst(COMMAND, "");
switch(command) {
case "help":
printString("Available commands:\n" +
":help show this help\n" +
":quit exit the interpreter\n");
break;
case "quit":
status = STATUS.QUIT;
break;
default:
printString("Unknown command\n" +
"Type :help for help\n");
break;
}
}
public void printString(String message) {
try {
if((message != null) &&
(message.isEmpty() == false)) {
consoleReader.printString(message);
}
}
catch(IOException exception) {
System.err.println(exception);
}
}
}
REPLCalc.java
計算機インタプリタ実装クラス。
JavaScriptのeval()メソッドを使用していますので、計算機ロジックを組む必要はありません。
画像を見ていただければ分かりますが、変数を使うことも可能です。
REPLCalc.java
package replcalc;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import replbase.REPLBase;
public class REPLCalc extends REPLBase {
private final ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
private final ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("JavaScript");
public static void main(String[] args) {
REPLCalc replCalc = new REPLCalc();
replCalc.PROMPT_READLINE = "[Calc] ";
replCalc.START_MESSAGE = "Welcome to Calc.(Used JavaScript Engine)\n" +
"Type :help for help, :quit for quit\n";
replCalc.start();
}
@Override
public void compile(String input) {
try {
scriptEngine.eval("print(eval(\"" + input + "\"))");
}
catch(ScriptException exception) {
printString(exception.getMessage() + "\n");
}
}
}
compile()メソッドを「scriptEngine.eval(input);」に変更すると、JavaScriptインタプリタになります。
if文や関数定義・実行等が可能です。
インタプリタ内からJavaをコールも可能です。
JDKに用意されているjjsコマンドと変わりませんが。。。