継続可能なインタプリタ

スピードとかはぜんぜん考えてませんけど、nagasimaさんが言っていたツリーをたどる、
継続可能なインタプリタを作ってみました。
足し算しかできないけどこんな感じなのかなっと。

import java.util.Hashtable;

class t{
	public static void main(String argv[]) {
		Atom exp = new Add(new Number(5), new Add(new CC(new Number(1)), new Number(2)));
		System.out.println(exp = Atom.e(exp));
		System.out.println(exp = Atom.e(exp));
	}
}
class Atom {
	static Atom e(Atom a) {
		Context context = new Context();
		try {
			return a.eval(context);
		} catch (CCException e) {
			e.cc.context = context;
			e.cc.root = a;
			return e.cc;
		}
	}
	Atom eval(Context c) throws CCException { return null;}
}

class Number extends Atom {
	int data;
	Number(int i) { data = i;}
	Atom eval(Context c) throws CCException { return this;}
	public String toString() {
		return ""+data;
	}
}
class Add extends Atom {
	Atom l;
	Atom r;
	Add(Atom l, Atom r) {
		this.l = l;
		this.r = r;
	}
	Atom eval(Context c) throws CCException  {
		switch(c.pc) {
		case 0: c.child = new Context(); c.pc++;
		case 1: c.env.put("x", l.eval(c.child));
				c.child = new Context(); c.pc++;
		case 2: c.env.put("y", r.eval(c.child)); c.pc++;
		}
		return new Number(
			((Number)c.env.get("x")).data + 
			((Number)c.env.get("y")).data);
	}
}

class Context {
	int pc = 0;
	Context child;
	Hashtable env = new Hashtable();
}

class CCException extends Exception {
	CC2 cc;
	CCException(CC2 cc) {
		this.cc = cc;
	}
}

class CC extends Atom {
	Atom data;
	CC(Atom data) { this.data = data; }
	Atom eval(Context c) throws CCException  {
		switch(c.pc) {
		case 0: c.pc++; throw new CCException(new CC2());
		}
		return data;
	}
	
}
class CC2 extends Atom{
	Atom root;
	Context context;
	Atom eval(Context c) throws CCException {
		return root.eval(context);
	}
}