簡易Truffle言語に変数を実装する

Truffleで足し算してみたけど、やっぱちょっと変数を実装しておきたい。
Truffleでの言語実装を最小手数ではじめる - きしだのはてな
ということで、変数を追加するんだけど、ここでは変数定義はせずに埋め込み変数みたいなものを実装してみます。

変数登録

今回はMathRootNodeでのexecuteのときに変数を登録します。変数名はFrameSlotで表します。FrameSlotはFrameDescriptorからとってきます。このとき、FrameSlotに変数の値の型も設定しておきます。

static class MathRootNode extends RootNode {
    ...

    @Override
    public Object execute(VirtualFrame frame) {
        setup(frame);
        return body.executeGeneric(frame);
    }

    void setup(VirtualFrame frame) {
        final FrameDescriptor desc = frame.getFrameDescriptor();
        FrameSlot slotAa = desc.findOrAddFrameSlot("aa");
        desc.setFrameSlotKind(slotAa, FrameSlotKind.Long);
        frame.setLong(slotAa, 123);            
    }
}

言語実装するときは、これを変数割り当てノードなどで実装します。

変数呼び出し

変数呼び出しノードを作ります。変数呼び出しではVirtualFrameにFrameSlotを指定してgetLongすればいいんですが、例外をにぎりつぶす便利メソッドgetLongSafeがあるのでこれを使います。

@NodeField(name = "slot", type = FrameSlot.class)
public static abstract class VariableNode extends MathNode {
    abstract FrameSlot getSlot();
    
    @Specialization
    long readLong(VirtualFrame vf) {
        return FrameUtil.getLongSafe(vf, getSlot());
    }       
}

変数呼び出しノードの登録

最後にMathLangでのパース時にこの変数ノードを使うようにします。整数のパースに失敗したら変数ということにしておきます。

MathNode parseNode(FrameDescriptor frame, String value) {
    try {
        return LongNode.of(value);
    } catch (NumberFormatException ex) {
        return VariableNodeGen.create(frame.findOrAddFrameSlot(value));
    }
}


parseメソッドも書き換えておきます。

FrameDescriptor frame = new FrameDescriptor();

MathNode node = parseNode(frame, nums[nums.length - 1]);
for (int i = nums.length - 2; i >= 0; --i) {
    node = MathNodesFactory.AddNodeGen.create(parseNode(frame, nums[i]), node);
}
MathRootNode root = new MathRootNode(this, frame, node);

実行

ということで変数を使ってみます。

public static void main(String[] args) {
    String exp = "12+34+aa";
    Context cont = Context.create("mathlang");
    System.out.println(cont.eval("mathlang", exp));
}


できました!

$ ./me
169