本章要達到的目的:擴展Stone語言,使它能在程序中調用Java語言中的static方法java
Java語言提供了名爲原生方法的功能,用於調用C語言等其餘一些語言寫成的函數。咱們將爲Stone語言添加相似的功能,讓它可以調用由Java語言寫成的函數。數組
原生函數將由Arguments類的eval方法調用。
代碼清單8.1是用於改寫Arguments類的eval方法的修改器。
這個名爲NativeArgEx的修改器標有extendsArgumentsEx一句,它修改的是Arguments類。
ArgumentsEx是第7天代碼清單7.7中定義的另外一個修改器。NativeArgEx修改器與ArgumentsEx修改器都有用於修改Arguments類,它將在後者的基礎上對該類做進一步修改。app
這裏的修改器繼承了另外一個修改器。函數
經過此次修改,Arguments類eval方法將首先判斷參數value是否爲NativeFunction對象。參數value是一個由函數調用表達式的函數名獲得的對象。eval方法以前返回的老是Function對象。若是參數是一個NativeFunction對象,eval方法將在計算實參序列並保存至數組args以後,調用NativeFunction對象的invoke來執行目標函數。若是參數不是NativeFunction對象,解釋器將執行一般的函數調用。
它將經過super來調用原先由ArgumentsEx修改器添加的eval方法。ui
package chap8; import java.util.List; import stone.StoneException; import stone.ast.ASTree; import javassist.gluonj.*; import chap6.Environment; import chap6.BasicEvaluator.ASTreeEx; import chap7.FuncEvaluator; @Require(FuncEvaluator.class) @Reviser public class NativeEvaluator { @Reviser public static class NativeArgEx extends FuncEvaluator.ArgumentsEx { public NativeArgEx(List<ASTree> c) { super(c); } public Object eval(Environment callerEnv, Object value) { if (!(value instanceof NativeFunction)) return super.eval(callerEnv, value); NativeFunction func = (NativeFunction) value; int nparams = func.numOfParameters(); if (size() != nparams) throw new StoneException("bad number of arguments", this); Object[] args = new Object[nparams]; int num = 0; for (ASTree a : this) { ASTreeEx ae = (ASTreeEx) a; args[num++] = ae.eval(callerEnv); } return func.invoke(args, this); } } }
代碼清單8.2是NativeFunction類。若是函數是一個原生函數,程序將在開始執行前建立NativeFunction類的對象,將由函數名與相應對象組成的名值對添加至環境中。該類的invoke方法將以參數args爲參數調用Java語言的static方法。this
Method對象的invoke方法用於執行它表示的Java語言方法。invoke的第1個參數是執行該方法的對象。若是被執行的是一個static方法,該參數則爲null。invoke的第2個參數用於保存傳遞給方法的實參序列lua
package chap8; import java.lang.reflect.Method; import stone.StoneException; import stone.ast.ASTree; public class NativeFunction { protected Method method; protected String name; protected int numParams; public NativeFunction(String n, Method m) { name = n; method = m; numParams = m.getParameterTypes().length; } public String toString() { return "<native:" + hashCode() + ">"; } public int numOfParameters() { return numParams; } public Object invoke(Object[] args, ASTree tree) { try { return method.invoke(null, args); } catch (Exception e) { throw new StoneException("bad native function call: " + name, tree); } } }
代碼清單8.3中的程序會在執行前建立NativeFunction對象,並添加至環境中。其中,Natives類的environment方法將在調用後返回一個含有原生函數的環境。code
append方法可以向環境添加一個由參數指定的static方法做爲原生函數。它的第3個參數是須要添加的static方法的類,第四個參數是該方法的名稱,從第五個參數開始是該方法的參數類型。若是新增的方法不含參數,則僅需向append方法傳入前4個參數。orm
代碼清單8.3向環境添加了print函數、read函數、1ength函數、toInt函數以及currentTime函數。關於這些原生函數的用途,請參見Natives類中的同名static方法。對象
代碼清單8.4與代碼清單8.5分別是解釋器程序及其啓動程序。代碼清單8.4中的解釋器將首先調用Natives類的environment方法,建立一個包含原生函數的環境。
package chap8; import java.lang.reflect.Method; import javax.swing.JOptionPane; import stone.StoneException; import chap6.Environment; public class Natives { public Environment environment(Environment env) { appendNatives(env); return env; } protected void appendNatives(Environment env) { append(env, "print", Natives.class, "print", Object.class); append(env, "read", Natives.class, "read"); append(env, "length", Natives.class, "length", String.class); append(env, "toInt", Natives.class, "toInt", Object.class); append(env, "currentTime", Natives.class, "currentTime"); } protected void append(Environment env, String name, Class<?> clazz, String methodName, Class<?> ... params) { Method m; try { m = clazz.getMethod(methodName, params); } catch (Exception e) { throw new StoneException("cannot find a native function: " + methodName); } env.put(name, new NativeFunction(methodName, m)); } // native methods public static int print(Object obj) { System.out.println(obj.toString()); return 0; } public static String read() { return JOptionPane.showInputDialog(null); } public static int length(String s) { return s.length(); } public static int toInt(Object value) { if (value instanceof String) return Integer.parseInt((String)value); else if (value instanceof Integer) return ((Integer)value).intValue(); else throw new NumberFormatException(value.toString()); } private static long startTime = System.currentTimeMillis(); public static int currentTime() { return (int)(System.currentTimeMillis() - startTime); } }
package chap8; import stone.ClosureParser; import stone.ParseException; import chap6.BasicInterpreter; import chap7.NestedEnv; public class NativeInterpreter extends BasicInterpreter { public static void main(String[] args) throws ParseException { run(new ClosureParser(),new Natives().environment(new NestedEnv())); } }
package chap8; import javassist.gluonj.util.Loader; import chap7.ClosureEvaluator; public class NativeRunner { public static void main(String[] args) throws Throwable { Loader.run(NativeInterpreter.class, args, NativeEvaluator.class, ClosureEvaluator.class); } }
def fib (n) { if n < 2 { n } else { fib (n - 1) + fib (n - 2) } } t = currentTime() fib 15 print currentTime() - t + " msec"