はじめに
前回記事の「オブジェクト生成に対応する」でクラスのロードができるようになりました。
ロードできるようになったクラスを利用して、そのクラスのスタティックメソッド呼び出しに対応したいと思います。
スタティックメソッド呼び出し対応でやりたいこと
スタティックメソッド呼び出し対応でやりたいことを確認します。
例えば下のようなプログラムがあります。
1行目でIntegerクラスを変数integerClassへロードします。
2行目でそのクラスのtoHexStringスタティックメソッドを呼び出し、その戻り値を出力するようにしたいと思います。
toHexStringスタティックメソッドは引数の整数を16進数表現の文字列にして返します。
例の引数は255なので出力はffになります。
var integerClass = loadClass("java.lang.Integer")
println(integerClass.toHexString(255))
実装の仕方
インタプリタ(Interpreter)での実装の仕方について考えていきます。
字句解析(Lexer)と構文解析(Parser)の変更はありません。
インタプリタ(Interpreter)の実装の仕方
以前の記事、「メソッド呼び出しに対応する」で、インスタンスメソッド呼び出しには対応しました。
スタティックメソッドも構文はインスタンスメソッドと同じなので、対応する箇所は、インスタンスメソッドでの対応と同じ箇所へ行います。
実装の仕方は、例えばv.method()のようなメソッド呼び出しがあったときの、vの型が何かを参照して、
それがインスタンスメソッドの呼び出しか、スタティックメソッドの呼び出しかを切り分けます。
切り分け方は、vの型がClass<?>ならスタティックメソッド呼び出しとみなし、
それ以外の型なら、インスタンスメソッド呼び出しとみなします。
それ以外の型とは、例えばStringやIntegerなどです。
Javaで実装してみる
実装にうつります。
インタプリタ(Interpreter)について、変更したところみてみます。
Interpreter.java
Interpreter.javaの実装です。
func()メソッドの変更です。
func()メソッドは引数のvalueが、関数のように呼び出し可能であることを保証します。
// Updateの下へif文を入れる変更をしました。
先の例v.method()のvのあたるのがd.leftです。
d.left.getClass()でvの型を取得します。
vの型がClass<?>であったら、それをスタティックメソッド呼び出しとみなし、そうでなければインスタンスメソッド呼び出しとみなします。
if文では、MethodFunc型変数のmfのフィールドへ、何を代入するかを決定します。
mfのフィールドへ正しく値を準備できれば、スタティックメソッド呼び出しとインスタンスメソッド呼び出しの違いを意識することなく、
MethodFuncクラスのinvokeメソッドで、メソッド呼び出しを実行でるからです。
if文が真の場合、すなわちスタティックメソッド呼び出しの場合は、mf.class_にd.left自身を代入しています。
d.left自身が型を表しているためです。
mf.targetにはnullを代入しています。
nullを代入するのは、スタティックメソッド呼び出しなため、メソッド呼び出しにインスタンスが必要ないためです。
if文が偽の場合、すなわちインスタンスメソッド呼び出しの場合は、以前のメソッド呼び出しで実装したものと同じままです。
public Func func(Object value) throws Exception {
if (value instanceof Func) {
return (Func) value;
} else if (value instanceof Dotted) {
Dotted d = (Dotted) value;
MethodFunc mf = new MethodFunc();
mf.name = d.right.value;
// Update
Class<?> c = d.left.getClass();
if (c == Class.class.getClass()) {
mf.class_ = (Class<?>) d.left;
mf.target = null;
} else {
mf.class_ = c;
mf.target = d.left;
}
return mf;
} else if (value instanceof Variable) {
Variable v = (Variable) value;
return func(v.value);
} else {
throw new Exception("Not a function");
}
}
主な変更箇所は以上です。
以上の実装を使って下のプログラム
var integerClass = loadClass("java.lang.Integer")
println(integerClass.toHexString(255))
を実行し、255の16進数表現ffを出力します。
public static void main(String[] args) throws Exception {
String text = "";
text += "var integerClass = loadClass(\"java.lang.Integer\")";
text += "println(integerClass.toHexString(255))";
List<Token> tokens = new Lexer().init(text).tokenize();
List<Token> blk = new Parser().init(tokens).block();
new Interpreter().init(blk).run();
// --> ff
}
実装は以上です。
ありがとうございました。
おわりに
ソースの全文はこちらで公開しています。
Calc
https://github.com/quwahara/Calc/tree/article-20-static-method-invocation/Calc/src/main/java
続きの記事があります。
スクリプトをファイルから読み込んで実行する
http://qiita.com/quwahara/items/bac43cd1df11b025e46a