はじめに
前回記事の「オブジェクト生成に対応する」でクラスのロードができるようになりました。
ロードできるようになったクラスを利用して、そのクラスのスタティックメソッド呼び出しに対応したいと思います。
スタティックメソッド呼び出し対応でやりたいこと
スタティックメソッド呼び出し対応でやりたいことを確認します。
例えば下のようなプログラムがあります。
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