// h18.9/29 (鈴)
package tiny_prolog;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/** Prolog 処理系の (構文解析より後段の) 内部データ構造と手続き。
*
* Prolog プログラムは,
* 述語クラス Pred の各インスタンスが保持する節のリストとして表現される。
* 大域的なシンボルテーブル等はない。
*
* プログラムを「実行」するメソッドは,Goals#iterator() である。
* 環境クラス Env のインスタンスなどの動的データ構造は,
* 直接,間接にこのメソッドを遂行する過程で作られ,使われる。
*/
public class Core
{
/** 汎用の2項組。
* 基本的なデータ型としてフィールド first, second への直接参照を許す。
* second は Lisp の cdr に相当する。
* 連結リストの最後の要素の second の値は普通は null である。
* 文字列表現は Prolog のリストに準ずる。
*/
public static class Pair {
public First first;
public Second second;
public Pair (First first, Second second) {
this.first = first;
this.second = second;
}
@Override
public String toString() {
return "[" + toStringWithoutParens() + "]";
}
/** 両端のカッコを省いた文字列表現 */
protected String toStringWithoutParens() {
if (second == null) {
return str(first);
} else if (second instanceof Pair) {
return (str(first) + ", " +
((Pair) second).toStringWithoutParens());
} else {
return str(first) + " | " + str(second);
}
}
}
/** 引数の文字列値を返す便宜関数。
* null には "[]" を返す。
* 空文字列,大文字か数字か負号で始まる文字列,空白を含む文字列は,
* 前後に ' を付ける。
*/
public static String str(Object x) {
if (x == null) return "[]";
else if (x instanceof String) {
String s = (String) x;
int n = s.length();
if (n == 0) return "''";
else {
char c = s.charAt(0);
if (Character.isUpperCase(c) || Character.isDigit(c)
|| c == '-') {
return "'" + s + "'";
} else {
for (int i = 0; i < n; i++) {
c = s.charAt(i);
if (Character.isWhitespace(c)) {
return "'" + s + "'";
}
}
return s;
}
}
}
return x.toString();
}
/** 変数。名前だけ保持する。変数値はここには保持しない。
*/
public static class Var {
private final String name;
public Var (String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
/** 述語。
* 述語の定義として節 (clause) のリストを保持する。
* ここで節とは Goal と IBody の Pair とする。
*/
public static class Pred {
private final ArrayList> defs;
private final String name;
public Pred (String name) {
this.name = name;
defs = new ArrayList> ();
}
@Override
public String toString() {
return name;
}
}
/** 節の本体 */
private static interface IBody {}
/** 節の本体としてのコールバック */
public static interface ICallback extends IBody {
public boolean call(CallbackEnv env);
}
/** ゴール,つまり述語項。
* 述語に対する節の定義を主要なメソッドとする。
*/
public static class Goal {
private final Pred pred;
private final Object[] args;
/** 述語と項の配列を引数として述語項を構築する。
* カット演算子以外,両引数とも null であってはならない。
* ただし,項の配列の各要素は null であってもよい (Prolog の
* 空リスト扱いされる)。
*/
public Goal (Pred pred, Object[] args) {
this.pred = pred;
this.args = args;
}
/** 自身を頭部,引数を本体とした節を定義する。
* 本体がないときは null を与える。
* si はラテン語で「もしも」を意味する。
*/
public void si(Goals body) {
pred.defs.add(new Pair (this, body));
}
/** コールバックを定義する。
*/
public void calls(ICallback callback) {
assert callback != null;
pred.defs.add(new Pair (this, callback));
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder ();
buf.append(str(pred));
buf.append('(');
boolean f = true;
for (Object x: args) {
if (f) {
f = false;
} else {
buf.append(", ");
}
buf.append(str(x));
}
buf.append(')');
return buf.toString();
}
}
/** カット演算子 */
public static class Cut extends Goal {
private Cut () {
super(null, null);
}
@Override
public String toString() {
return "!";
}
}
/** カット演算子の唯一のインスタンス */
public static final Cut CUT = new Cut ();
/** Goal のリスト。
* Pair の具体型であり,第1引数として Goal インスタンス,
* 第2引数として null または Goals インスタンスをとる。
*/
public static class Goals extends Pair
implements IBody, Iterable
{
public Goals (Goal goal, Goals rest) {
super(goal, rest);
assert goal != null;
}
/** 解決結果の環境を与えるイテレータを構築する。 */
public Iterator iterator() {
return new Resolver (this);
}
/** 文字列表現。Pair の具体型だが前後のカッコを省く。*/
@Override
public String toString() {
return toStringWithoutParens();
}
}
/** 環境 */
public static final class Env {
private final HashMap> table;
private Env () {
table = new HashMap> ();
}
private void put(Var x, Pair