第8天 關(guān)聯(lián)Java語(yǔ)言
本章要達(dá)到的目的:擴(kuò)展Stone語(yǔ)言请毛,使它能在程序中調(diào)用Java語(yǔ)言中的static方法
8.1 原生函數(shù)
Java語(yǔ)言提供了名為原生方法的功能,用于調(diào)用C語(yǔ)言等其他一些語(yǔ)言寫(xiě)成的函數(shù)鲤氢。我們將為Stone語(yǔ)言添加類(lèi)似的功能茴她,讓它能夠調(diào)用由Java語(yǔ)言寫(xiě)成的函數(shù)宇整。
原生函數(shù)將由Arguments類(lèi)的eval方法調(diào)用档叔。
代碼清單8.1是用于改寫(xiě)Arguments類(lèi)的eval方法的修改器桌粉。
這個(gè)名為NativeArgEx的修改器標(biāo)有extendsArgumentsEx一句,它修改的是Arguments類(lèi)衙四。
ArgumentsEx是第7天代碼清單7.7中定義的另一個(gè)修改器铃肯。NativeArgEx修改器與ArgumentsEx修改器都有用于修改Arguments類(lèi),它將在后者的基礎(chǔ)上對(duì)該類(lèi)作進(jìn)一步修改传蹈。
這里的修改器繼承了另一個(gè)修改器押逼。
通過(guò)這次修改,Arguments類(lèi)eval方法將首先判斷參數(shù)value是否為NativeFunction對(duì)象惦界。參數(shù)value是一個(gè)由函數(shù)調(diào)用表達(dá)式的函數(shù)名得到的對(duì)象挑格。eval方法之前返回的總是Function對(duì)象。如果參數(shù)是一個(gè)NativeFunction對(duì)象表锻,eval方法將在計(jì)算實(shí)參序列并保存至數(shù)組args之后恕齐,調(diào)用NativeFunction對(duì)象的invoke來(lái)執(zhí)行目標(biāo)函數(shù)。如果參數(shù)不是NativeFunction對(duì)象瞬逊,解釋器將執(zhí)行通常的函數(shù)調(diào)用。
它將通過(guò)super來(lái)調(diào)用原先由ArgumentsEx修改器添加的eval方法仪或。
代碼清單8.1 NativeEvaluator.java
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類(lèi)确镊。如果函數(shù)是一個(gè)原生函數(shù),程序?qū)⒃陂_(kāi)始執(zhí)行前創(chuàng)建NativeFunction類(lèi)的對(duì)象范删,將由函數(shù)名與相應(yīng)對(duì)象組成的名值對(duì)添加至環(huán)境中蕾域。該類(lèi)的invoke方法將以參數(shù)args為參數(shù)調(diào)用Java語(yǔ)言的static方法。
Method對(duì)象的invoke方法用于執(zhí)行它表示的Java語(yǔ)言方法。invoke的第1個(gè)參數(shù)是執(zhí)行該方法的對(duì)象旨巷。如果被執(zhí)行的是一個(gè)static方法巨缘,該參數(shù)則為null。invoke的第2個(gè)參數(shù)用于保存?zhèn)鬟f給方法的實(shí)參序列
代碼清單8.2 NativeFunction.java
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中的程序會(huì)在執(zhí)行前創(chuàng)建NativeFunction對(duì)象采呐,并添加至環(huán)境中若锁。其中,Natives類(lèi)的environment方法將在調(diào)用后返回一個(gè)含有原生函數(shù)的環(huán)境斧吐。
append方法能夠向環(huán)境添加一個(gè)由參數(shù)指定的static方法作為原生函數(shù)又固。它的第3個(gè)參數(shù)是需要添加的static方法的類(lèi),第四個(gè)參數(shù)是該方法的名稱(chēng)煤率,從第五個(gè)參數(shù)開(kāi)始是該方法的參數(shù)類(lèi)型仰冠。如果新增的方法不含參數(shù),則僅需向append方法傳入前4個(gè)參數(shù)蝶糯。
代碼清單8.3向環(huán)境添加了print函數(shù)洋只、read函數(shù)、1ength函數(shù)昼捍、toInt函數(shù)以及currentTime函數(shù)木张。關(guān)于這些原生函數(shù)的用途,請(qǐng)參見(jiàn)Natives類(lèi)中的同名static方法端三。
代碼清單8.4與代碼清單8.5分別是解釋器程序及其啟動(dòng)程序舷礼。代碼清單8.4中的解釋器將首先調(diào)用Natives類(lèi)的environment方法,創(chuàng)建一個(gè)包含原生函數(shù)的環(huán)境郊闯。
8.2 編寫(xiě)使用原生函數(shù)的程序
代碼清單8.3 Natives.java
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);
}
}
代碼清單8.4 NativeInterpreter.java
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()));
}
}
代碼清單8.5 NativeRunner.java
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);
}
}
代碼清單8.6 計(jì)算斐波那契數(shù)列所需的時(shí)間
def fib (n) {
if n < 2 {
n
} else {
fib (n - 1) + fib (n - 2)
}
}
t = currentTime()
fib 15
print currentTime() - t + " msec"