兩週自制腳本語言-第8天 關聯Java語言

第8天 關聯Java語言

本章要達到的目的:擴展Stone語言,使它能在程序中調用Java語言中的static方法java

8.1 原生函數

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

代碼清單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類。若是函數是一個原生函數,程序將在開始執行前建立NativeFunction類的對象,將由函數名與相應對象組成的名值對添加至環境中。該類的invoke方法將以參數args爲參數調用Java語言的static方法。this

Method對象的invoke方法用於執行它表示的Java語言方法。invoke的第1個參數是執行該方法的對象。若是被執行的是一個static方法,該參數則爲null。invoke的第2個參數用於保存傳遞給方法的實參序列lua

代碼清單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中的程序會在執行前建立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方法,建立一個包含原生函數的環境。

8.2 編寫使用原生函數的程序

代碼清單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 計算斐波那契數列所需的時間

def fib (n) {
    if n < 2 {
        n
    } else {
        fib (n - 1) + fib (n - 2)
    }
}
t = currentTime()
fib 15
print currentTime() - t + " msec"
相關文章
相關標籤/搜索