blob: f39e2244698547b329843046e63758eb33d28209 [file] [log] [blame]
package com.googlecode.prolog_cafe.lang;
import java.util.ArrayList;
import java.util.List;
/**
* List.<br>
* The class <code>ListTerm</code> represents a list structure.<br>
*
* <pre>
* % [1,2]
* Term Nil = SymbolTerm.makeSymbol("[]");
* Term n1 = IntegerTerm(1);
* Term n2 = IntegerTerm(2);
* Term t = new ListTerm(n1, new ListTerm(n2, Nil));
*
* Term car = ((ListTerm)t).car();
* Term cdr = ((ListTerm)t).cdr();
* </pre>
*
* Here is sample program for creating a list from <code>1</code> to <code>n</code>.
* <pre>
* public static Term makeList(int n) {
* Term t = SymbolTerm.makeSymbol("[]");
* for (int i=n; i>0; i--) {
* t = new ListTerm(new IntegerTerm(i), t);
* }
* return t;
* }
* </pre>
*
* @author Mutsunori Banbara (banbara@kobe-u.ac.jp)
* @author Naoyuki Tamura (tamura@kobe-u.ac.jp)
* @version 1.0
*/
public class ListTerm extends Term {
/** A functor <code>'.' /2</code>. */
protected static final SymbolTerm SYM_DOT = SymbolTerm.intern(".", 2);
/** Holds the first element of this <code>ListTerm</code>. */
protected Term car;
/**
* Holds the list consisting of all the rest of the elements of
* this <code>ListTerm</code> but the first one.
*/
protected Term cdr;
/**
* Constructs a new Prolog list structure
* such that <code>_car</code> is the first element of this list, and
* <code>_cdr</code> is the list consisting of all the rest of the
* elements of this list but the first one.
*/
public ListTerm(Term _car, Term _cdr) {
car = _car;
cdr = _cdr;
}
/** Returns the value of <code>car</code>.
* @see #car
*/
public Term car() { return car; }
/** Returns the value of <code>cdr</code>.
* @see #cdr
*/
public Term cdr() { return cdr; }
/** Sets the value to <code>car</code>.
* @see #car
*/
public void setCar(Term t) { car = t; }
/** Sets the value to <code>cdr</code>.
* @see #cdr
*/
public void setCdr(Term t) { cdr = t; }
@Override
public int type() {
return TYPE_LIST;
}
/* Term */
@Override
public boolean unify(Term t, Trail trail) {
t = t.dereference();
if (t instanceof VariableTerm) {
((VariableTerm) t).bind(this, trail);
return true;
}
if (! (t instanceof ListTerm))
return false;
return car.unify(((ListTerm)t).car(), trail)
&& cdr.unify(((ListTerm)t).cdr(), trail);
}
/**
* @return the <code>boolean</code> whose value is
* <code>convertible(List.class, type)</code>.
* @see Term#convertible(Class, Class)
*/
@Override
public boolean convertible(Class<?> type) {
return convertible(List.class, type);
}
@Override
protected Term copy(Prolog engine) {
return new ListTerm(car.copy(engine), cdr.copy(engine));
}
@Override
public boolean isGround() {
if (! car.isGround())
return false;
if (! cdr.isGround())
return false;
return true;
}
@Override
public String name() { return SYM_DOT.name(); }
@Override
public Term arg(int nth) {
Term t = this;
int old_nth = nth;
while (t instanceof ListTerm && 0 < nth) {
nth--;
t = ((ListTerm)t).cdr.dereference();
}
if (t instanceof ListTerm)
return ((ListTerm)t).car;
throw new ArrayIndexOutOfBoundsException(old_nth);
}
/** Returns the length of this <code>ListTerm</code>. */
public int length() {
int count = 0;
Term t = this;
while(t instanceof ListTerm) {
count++;
t = ((ListTerm)t).cdr().dereference();
}
return count;
}
/**
* Returns a {@code java.util.List} corresponds to this <code>ListTerm</code>
* according to <em>Prolog Cafe interoperability with Java</em>.
* @return a {@link java.util.List} object equivalent to
* this <code>IntegerTerm</code>.
*/
@Override
public List<?> toJava() {
List<Object> vec = new ArrayList<>();
Term t = this;
while(t instanceof ListTerm) {
vec.add(((ListTerm)t).car().dereference().toJava());
t = ((ListTerm)t).cdr().dereference();
}
return vec;
}
@Override
public String toQuotedString() {
Term x = this;
String s = "[";
for (;;) {
s += ((ListTerm)x).car.dereference().toQuotedString();
x = ((ListTerm)x).cdr.dereference();
if (! (x instanceof ListTerm))
break;
s += ",";
}
if (! Prolog.Nil.equals(x))
s += "|" + x.toQuotedString();
s += "]";
return s;
}
/* Object */
/**
* Checks <em>term equality</em> of two terms.
* The result is <code>true</code> if and only if the argument is an instance of
* <code>ListTerm</code>, and
* all corresponding pairs of elements in the two lists are <em>term-equal</em>.
* @param obj the object to compare with. This must be dereferenced.
* @return <code>true</code> if the given object represents a Prolog list
* equivalent to this <code>ListTerm</code>, false otherwise.
* @see #compareTo
*/
@Override
public boolean equals(Object obj) {
if (! (obj instanceof ListTerm))
return false;
return car.equals(((ListTerm)obj).car().dereference())
&& cdr.equals(((ListTerm)obj).cdr().dereference());
}
@Override
public int hashCode() {
int h = 1;
h = 31*h + SYM_DOT.hashCode();
h = 31*h + car.dereference().hashCode();
h = 31*h + cdr.dereference().hashCode();
return h;
}
/** Returns a string representation of this <code>ListTerm</code>. */
@Override
public String toString() {
Term x = this;
String s = "[";
for (;;) {
s += ((ListTerm)x).car.dereference().toString();
x = ((ListTerm)x).cdr.dereference();
if (! (x instanceof ListTerm))
break;
s += ",";
}
if (! Prolog.Nil.equals(x))
s += "|" + x.toString();
s += "]";
return s;
}
/* Comparable */
/**
* Compares two terms in <em>Prolog standard order of terms</em>.<br>
* It is noted that <code>t1.compareTo(t2) == 0</code> has the same
* <code>boolean</code> value as <code>t1.equals(t2)</code>.
* @param anotherTerm the term to compared with. It must be dereferenced.
* @return the value <code>0</code> if two terms are identical;
* a value less than <code>0</code> if this term is <em>before</em> the <code>anotherTerm</code>;
* and a value greater than <code>0</code> if this term is <em>after</em> the <code>anotherTerm</code>.
*/
@Override
public int compareTo(Term anotherTerm) { // anotherTerm must be dereferenced.
if (anotherTerm instanceof VariableTerm || ((anotherTerm instanceof IntegerTerm) || (anotherTerm instanceof DoubleTerm)) || anotherTerm instanceof SymbolTerm)
return AFTER;
if (anotherTerm instanceof StructureTerm) {
int arity = ((StructureTerm)anotherTerm).arity();
if (2 != arity)
return (2 - arity);
SymbolTerm functor = ((StructureTerm)anotherTerm).functor();
if (! SYM_DOT.equals(functor))
return SYM_DOT.compareTo(functor);
}
Term[] args = new Term[2];
if (anotherTerm instanceof ListTerm) {
args[0] = ((ListTerm)anotherTerm).car();
args[1] = ((ListTerm)anotherTerm).cdr();
} else if (anotherTerm instanceof StructureTerm) {
args = ((StructureTerm)anotherTerm).args();
} else {
return BEFORE;
}
Term tmp = car;
int rc;
for (int i=0; i<2; i++) {
rc = tmp.compareTo(args[i].dereference());
if (rc != EQUAL)
return rc;
tmp = cdr;
}
return EQUAL;
}
}