JavaでREPL、JavaScriptを利用して計算機インタプリタへ。

「JavaでREPLを作る。」のREPLBaseを改良。
JavaScriptを利用して計算機インタプリタを作る。

![replCalc1.png](https://qiita-image-store.s3.amazonaws.com/0/23378/bca25ca4-690a-693c-85ae-5efba7a44658.png)

JavaからJavaScript実行はScriptEngineManagerとScriptEngineを使用します。

```Java:JavaScript実行
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("JavaScript");
scriptEngine.eval("print(eval(¥"1+1¥"))")
```

REPLBase.java
ベースクラス化しました。
起動メッセージ、プロンプト等を派生側で設定可能にしました。
compile()メソッドを抽象メソッドにし、派生側で実装するようにしました。

```Java: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()メソッドを使用していますので、計算機ロジックを組む必要はありません。
画像を見ていただければ分かりますが、変数を使うことも可能です。

```Java: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コマンドと変わりませんが。。。
![replCalc2_js.png](https://qiita-image-store.s3.amazonaws.com/0/23378/e1f66a56-6576-4f73-d50b-c2537e08ffda.png)
![replCalc3_js.png](https://qiita-image-store.s3.amazonaws.com/0/23378/e922ef3e-adbe-0e35-ff40-1e5cde19d5a3.png)
![replCalc_js4.png](https://qiita-image-store.s3.amazonaws.com/0/23378/383c0feb-eb0e-279a-5e08-9c8d9f048f83.png)