iアプリでScheme(Lisp)処理系を作る 第二回

動機については前回を参照ください。
今回はとりあえず足し算ができるように、ペア・シンボル・整数・手続きを表すクラスを作成します。

ペアを作る

Eclipseのdojaプラグインでテンプレクラスを作成してからPairクラスを作成しておきます。
ペア(Pair)にはcar(カー)とcdr(クダー)の2つの部分があります。
nilの中身はとりあえず自分自身にしてみました。nullの方がよかったかな?

/**
 * ペア
 *  carとcdrとを持つ
 */
public class Pair {
	// 空リスト
	public static final Pair nil = new Pair() {
		{
			car = cdr = this;
		}
		public String toString() {
			return "()";
		}
	};

	public Object car, cdr;

	Pair(){}

	public Pair(Object x, Object y) {
		car = x;
		cdr = y;
	}
}

シンボルを作る

同じ名前でも環境が違えば異なるオブジェクトを作る方針です。

/**
 * シンボル
 *  評価されると場所に束縛された値や構文を返す。
 *  ソースに生で書いてあるシンボルは識別子とも呼ばれる。
 *  可視な束縛は環境にまとめておく。
 */
public class Symbol {
	public String name;
	public Object value;

	public Symbol(String n) {
		name = n;
	}
}

整数を作る

JavaのIntegerクラスを流用します。

手続きを作る

/**
 * 手続き
 */
public class Procedure {

	// 発動
	public Object invoke(Pair args) throws Exception {
		throw new Exception("無実装である");
	}

	// +
	public static final Procedure add = new Procedure() {
		public Object invoke(Pair args) throws Exception {
			int num = 0;
			for(;;) {
				if(!(args.car instanceof Integer))
					throw new ArithmeticException(" + に非Integer型が与えられた");
				num += ((Integer)args.car).intValue(); // hashcode?
				if((args = (Pair)args.cdr) == Pair.nil) break;
			}
			return new Integer(num);
		}
	};
}

環境を作る

シンボルをまとめておく環境を用意します。
ついでにScheme式の評価もこのクラスにやらせます。

import java.util.Hashtable;

/**
 * 環境
 *    識別子は,構文の型の名前に,または値を格納できる場所の
 *  名前になり得る。構文の型の名前になっている識別子は,構
 *  文キーワード(syntactic keyword) と呼ばれ,その構文に束
 *  縛(bound) されていると言われる。場所の名前になってい
 *  る識別子は,変数(variable) と呼ばれ,その場所に束縛さ
 *  れていると言われる。プログラムの中のある地点で有効なす
 *  べての可視な束縛からなる集合は,その地点で有効な環境
 *  (environment) として知られる。変数が束縛されている場所
 *  に格納されている値は,その変数の値と呼ばれる。
 *
 * ・evalもここでやります。
 */
public class Env {
	Hashtable bindings = new Hashtable();

	public Symbol intern(String name) {
		Symbol sym = (Symbol)bindings.get(name);
		if(sym == null) {
			sym = new Symbol(name);
			bindings.put(name, sym);
		}
		return sym;
	}

	public Object eval(Object expr) throws Exception {
		// TODO: 評価順はランダム
		if(expr instanceof Pair) {
			Pair p = (Pair)expr;
			// 全引数を不定の順番で評価
			for(;;) {
				p.car = eval(p.car);
				if((p = (Pair)p.cdr) == Pair.nil) break;
			}
			// 手続きを取り出し、発動する
			Procedure func = (Procedure)((Pair)expr).car;
			return func.invoke((Pair)((Pair)expr).cdr);
		}

		if(expr instanceof Symbol)
			return ((Symbol)expr).value;

		return expr;
	}
}

はじめての足し算

これでようやく準備が整いました。用意したadd手続きで早速足し算をしてみます。

  • StartApp.java(部分)
   MainCanvas() {
        Env env = new Env();
        // (+ 1 2)
        Pair expr = new Pair(env.intern("+"), new Pair(new Integer(1), new Pair(new Integer(2), Pair.nil)));
        env.intern("+").value = Procedure.add;
        try {
		System.out.println(env.eval(expr).toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
        
    }

Dojaエミュレータを起動してコンソールに 3 がでれば成功です。