iアプリでScheme(Lisp)処理系を作る 第二回
動機については前回を参照ください。
今回はとりあえず足し算ができるように、ペア・シンボル・整数・手続きを表すクラスを作成します。
ペアを作る
Eclipseのdojaプラグインでテンプレクラスを作成してからPairクラスを作成しておきます。
ペア(Pair)にはcar(カー)とcdr(クダー)の2つの部分があります。
nilの中身はとりあえず自分自身にしてみました。nullの方がよかったかな?
- Pair.java
/** * ペア * 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; } }
シンボルを作る
同じ名前でも環境が違えば異なるオブジェクトを作る方針です。
- Symbol.java
/** * シンボル * 評価されると場所に束縛された値や構文を返す。 * ソースに生で書いてあるシンボルは識別子とも呼ばれる。 * 可視な束縛は環境にまとめておく。 */ public class Symbol { public String name; public Object value; public Symbol(String n) { name = n; } }
整数を作る
JavaのIntegerクラスを流用します。
手続きを作る
- Procedure.java
/** * 手続き */ 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式の評価もこのクラスにやらせます。
- Env.java
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 がでれば成功です。