/*
* tuProlog - Copyright (C) 2001-2007 aliCE team at deis.unibo.it
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package alice.tuprolog;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
/**
* Struct class represents both compound prolog term
* and atom term (considered as 0-arity compound).
*/
public class Struct extends Term implements Iterable<Term> {
/** name of the structure */
private String name;
/** arguments array */
private Term[] args;
/** arity **/
private int arity;
/** to speedup hash map operation */
private String predicateIndicator;
/** primitive behavior */
private transient PrimitiveInfo primitive;
/** it indicates if the term is resolved */
private boolean resolved = false;
/**
* Builds a compound term, with a series of arguments.
* If no arguments are passed, builds an atom.
*/
public Struct(String f, Term... args) {
this(f, args.length);
for (int i = 0; i < args.length; i++)
if (args[i] == null)
throw new InvalidTermException("Arguments of a Struct cannot be null");
else
this.args[i] = args[i];
}
/**
* Builds a structure representing an empty list.
*/
public Struct() {
this("[]", 0);
resolved = true;
}
/**
* Builds a list providing head and tail.
*/
public Struct(Term h, Term t) {
this(".", 2);
args[0] = h;
args[1] = t;
}
/**
* Builds a list specifying the elements.
*/
public Struct(Term[] args) {
this(args, 0);
}
private Struct(Term[] args, int index) {
this(".", 2);
if (index < args.length) {
this.args[0] = args[index];
this.args[1] = new Struct(args, index+1);
} else {
// build an empty list
name = "[]";
arity = 0;
this.args = null;
}
}
/**
* Builds a compound, with a linked list of arguments.
*/
Struct(String f, LinkedList<Term> args) {
name = f;
arity = args.size();
if (arity > 0) {
this.args = new Term[arity];
for(int c = 0; c < arity; c++)
this.args[c] = args.removeFirst();
}
predicateIndicator = name + "/" + arity;
resolved = false;
}
private Struct(int arity) {
this.arity = arity;
args = new Term[arity];
}
private Struct(String name,int arity) {
if (name == null)
throw new InvalidTermException("The functor of a Struct cannot be null");
if (name.length() == 0 && arity > 0)
throw new InvalidTermException("The functor of a non-atom Struct cannot be an empty string");
this.name = name;
this.arity = arity;
if (arity > 0)
args = new Term[arity];
predicateIndicator = name + "/" + arity;
resolved = false;
}
/** @deprecated Use Struct#getPredicateIndicator instead. */
String getHashKey() {
return getPredicateIndicator();
}
String getPredicateIndicator() {
return predicateIndicator;
}
/**
* Gets the number of elements of this structure.
*/
public int getArity() {
return arity;
}
/**
* Gets the functor name of this structure.
*/
public String getName() {
return name;
}
/**
* Gets the i-th element of this structure.
*
* No bound check is done
*/
public Term getArg(int index) {
return args[index];
}
/**
* Sets the i-th element of this structure.
*
* (Only for internal service).
*/
void setArg(int index, Term argument) {
args[index] = argument;
}
/**
* Gets the i-th element of this structure.
*
* No bound check is done. It is equivalent to
* <code>getArg(index).getTerm()</code>.
*/
public Term getTerm(int index) {
return args[index].getTerm();
}
// check type services
@Override
public boolean isAtomic() {
return arity == 0;
}
@Override
public boolean isCompound() {
return arity > 0;
}
@Override
public boolean isAtom() {
return (arity == 0 || isEmptyList());
}
@Override
public boolean isList() {
return (name.equals(".") && arity == 2 && args[1].isList()) || isEmptyList();
}
@Override
public boolean isGround() {
for (int i = 0; i < arity; i++)
if (!args[i].isGround())
return false;
return true;
}
/**
* Check is this struct is clause or directive.
*/
public boolean isClause() {
return(name.equals(":-") && arity > 1 && args[0].getTerm() instanceof Struct);
//return(name.equals(":-") && arity == 2 && arg[0].getTerm() instanceof Struct);
}
@Override
public Term getTerm() {
return this;
}
//
/**
* Gets an argument inside this structure, given its name.
*
* @param name name of the structure
* @return the argument or null if not found
*/
public Struct getArg(String name) {
if (arity == 0)
return null;
for (int i = 0; i < args.length; i++)
if (args[i] instanceof Struct) {
Struct s = (Struct) args[i];
if (s.getName().equals(name))
return s;
}
for (int i = 0; i < args.length; i++)
if (args[i] instanceof Struct) {
Struct s = (Struct)args[i];
Struct sol = s.getArg(name);
if (sol != null)
return sol;
}
return null;
}
//
/** Test if a term is greater than other. */
@Override
public boolean isGreater(Term t) {
t = t.getTerm();
if (!(t instanceof Struct)) {
return true;
} else {
Struct ts = (Struct) t;
int tarity = ts.arity;
if (arity > tarity) {
return true;
} else if (arity == tarity) {
if (name.compareTo(ts.name) > 0) {
return true;
} else if (name.compareTo(ts.name) == 0) {
for (int c = 0;c < arity;c++) {
if (args[c].isGreater(ts.args[c])) {
return true;
} else if (!args[c].isEqual(ts.args[c])) {
return false;
}
}
}
}
}
return false;
}
/** Test if a term is equal to other. */
@Override
public boolean isEqual(Term t) {
t = t.getTerm();
if (t instanceof Struct) {
Struct ts = (Struct) t;
if (arity == ts.arity && name.equals(ts.name)) {
for (int c = 0;c < arity;c++) {
if (!args[c].isEqual(ts.args[c])) {
return false;
}
}
return true;
} else {
return false;
}
} else {
return false;
}
}
//
/**
* Gets a copy of this structure.
*
* @param vMap is needed for register occurrence of same variables
*/
@Override
Term copy(Map<Var, Var> vMap, int idExecCtx) {
Struct t = new Struct(arity);
t.resolved = resolved;
t.name = name;
t.predicateIndicator = predicateIndicator;
t.primitive = primitive;
for (int c = 0; c < arity; c++)
t.args[c] = args[c].copy(vMap, idExecCtx);
return t;
}
/**
* Gets a copy of this structure.
*
* @param vMap is needed for register occurrence of same variables
*/
@Override
Term copy(Map<Var, Var> vMap, Map<Var, Var> substMap) {
Struct t = new Struct(arity);
t.resolved = false;
t.name = name;
t.predicateIndicator = predicateIndicator;
t.primitive = null;
for (int c = 0; c < arity; c++)
t.args[c] = args[c].copy(vMap, substMap);
return t;
}
/** Resolve term. */
@Override
long resolveTerm(long count) {
if (resolved)
return count;
else {
LinkedList<Var> vars = new LinkedList<Var>();
return resolveTerm(vars, count);
}
}
/**
* Resolve name of terms.
*
* @param vl list of variables resolved
* @param count start timestamp for variables of this term
* @return next timestamp for other terms
*/
long resolveTerm(LinkedList<Var> vl, long count) {
long newcount = count;
for (int c = 0; c < arity; c++) {
Term term = args[c];
if (term != null) {
//--------------------------------
// we want to resolve only not linked variables:
// so linked variables must get the linked term
term = term.getTerm();
//--------------------------------
if (term instanceof Var) {
Var t = (Var) term;
t.setTimestamp(newcount++);
if (!t.isAnonymous()) {
// searching a variable with the same name in the list
String name = t.getName();
Var found = null;
for (Var vn : vl)
if (name.equals(vn.getName())) {
found = vn;
break;
}
if (found != null)
args[c] = found;
else
vl.add(t);
}
} else
if (term instanceof Struct)
newcount = ((Struct) term).resolveTerm(vl, newcount);
}
}
resolved = true;
return newcount;
}
// services for list structures
/** Is this structure an empty list? */
@Override
public boolean isEmptyList() {
return name.equals("[]") && arity == 0;
}
/**
* Gets the head of this structure, which is supposed to be a list.
*
* <p>
* Gets the head of this structure, which is supposed to be a list.
* If the callee structure is not a list, throws an <code>UnsupportedOperationException</code>
* </p>
*/
public Term listHead() {
if (!isList())
throw new UnsupportedOperationException("The structure " + this + " is not a list.");
return args[0].getTerm();
}
/**
* Gets the tail of this structure, which is supposed to be a list.
*
* <p>
* Gets the tail of this structure, which is supposed to be a list.
* If the callee structure is not a list, throws an <code>UnsupportedOperationException</code>
* </p>
*/
public Struct listTail() {
if (!isList())
throw new UnsupportedOperationException("The structure " + this + " is not a list.");
return (Struct) args[1].getTerm() ;
}
/**
* Gets the number of elements of this structure, which is supposed to be a list.
*
* <p>
* Gets the number of elements of this structure, which is supposed to be a list.
* If the callee structure is not a list, throws an <code>UnsupportedOperationException</code>
* </p>
*/
public int listSize() {
if (!isList())
throw new UnsupportedOperationException("The structure " + this + " is not a list.");
Struct t = this;
int count = 0;
while (!t.isEmptyList()) {
count++;
t = (Struct) t.args[1].getTerm();
}
return count;
}
/**
* Gets an iterator on the elements of this structure,
* which is supposed to be a list.
*
* <p>
* If the callee structure is not a list, throws an
* <code>UnsupportedOperationException</code>
*/
@Override
public Iterator<Term> iterator() {
if (!isList()) {
String message = "The structure " + this + " is not a list.";
throw new UnsupportedOperationException(message);
}
return new ListIterator(this);
}
// hidden services
/**
* Gets a list Struct representation, with the functor as first element.
*/
Struct toList() {
Struct t = new Struct();
for(int c = arity - 1;c >= 0;c--) {
t = new Struct(args[c].getTerm(),t);
}
return new Struct(new Struct(name),t);
}
/**
* Gets a flat Struct from this structure considered as a List.
*
* If this structure is not a list, null object is returned.
*/
Struct fromList() {
Term ft = args[0].getTerm();
if (!ft.isAtom())
return null;
Struct at = (Struct) args[1].getTerm();
LinkedList<Term> al = new LinkedList<Term>();
while (!at.isEmptyList()) {
if (!at.isList())
return null;
al.addLast(at.getTerm(0));
at = (Struct) at.getTerm(1);
}
return new Struct(((Struct) ft).name, al);
}
/**
* Appends an element to this structure supposed to be a list.
*/
public void append(Term t) {
if (isEmptyList()) {
name = ".";
arity = 2;
args = new Term[arity];
args[0] = t; args[1] = new Struct();
} else if (args[1].isList()) {
((Struct) args[1]).append(t);
} else {
args[1] = t;
}
}
/**
* Inserts (as head) an element to this structure supposed to be a list.
*/
void insert(Term t) {
Struct co=new Struct();
co.args[0]=args[0];
co.args[1]=args[1];
args[0] = t;
args[1] = co;
}
//
/**
* Try to unify two terms.
*
* @param t the term to unify
* @param vl1 list of variables unified
* @param vl2 list of variables unified
* @return true if the term is unifiable with this one
*/
@Override
boolean unify(List<Var> vl1, List<Var> vl2, Term t) {
// During unification we need to take note of
// all the variables in the complete struct
t = t.getTerm();
if (t instanceof Struct) {
Struct ts = (Struct) t;
if (arity == ts.arity && name.equals(ts.name)) {
for (int c = 0; c < arity; c++) {
if (!args[c].unify(vl1, vl2, ts.args[c]))
return false;
}
return true;
}
} else
if (t instanceof Var)
return t.unify(vl2, vl1, this);
return false;
}
/* dummy method */
@Override
public void free() {}
//
/**
* Set primitive behavior associated at structure.
*/
void setPrimitive(PrimitiveInfo b) {
primitive = b;
}
/**
* Get primitive behavior associated at structure.
*/
public PrimitiveInfo getPrimitive() {
return primitive;
}
/**
* Check if this term is a primitive struct.
*/
public boolean isPrimitive() {
return primitive != null;
}
//
/**
* Gets the string representation of this structure.
*
* Specific representations are provided for lists and atoms.
* Names starting with upper case letter are enclosed in apices.
*/
@Override
public String toString() {
// empty list case
if (isEmptyList()) return "[]";
// list case
if (name.equals(".") && arity == 2) {
return ("[" + toString0() + "]");
} else if (name.equals("{}")) {
return ("{" + toString0_bracket() + "}");
} else {
String s = (Parser.isAtom(name) ? name : "'" + name + "'");
if (arity > 0) {
s = s + "(";
for (int c = 1;c < arity;c++) {
if (!(args[c - 1] instanceof Var)) {
s = s + args[c - 1].toString() + ",";
} else {
s = s + ((Var)args[c - 1]).toStringFlattened() + ",";
}
}
if (!(args[arity - 1] instanceof Var)) {
s = s + args[arity - 1].toString() + ")";
} else {
s = s + ((Var)args[arity - 1]).toStringFlattened() + ")";
}
}
return s;
}
}
private String toString0() {
Term h = args[0].getTerm();
Term t = args[1].getTerm();
if (t.isList()) {
Struct tl = (Struct) t;
if (tl.isEmptyList()) {
return h.toString();
}
if (h instanceof Var) {
return (((Var)h).toStringFlattened() + "," + tl.toString0());
} else {
return (h.toString() + "," + tl.toString0());
}
} else {
String h0;
String t0;
if (h instanceof Var) {
h0 = ((Var)h).toStringFlattened();
} else {
h0 = h.toString();
}
if (t instanceof Var) {
t0 = ((Var)t).toStringFlattened();
} else {
t0 = t.toString();
}
return (h0 + "|" + t0);
}
}
private String toString0_bracket() {
if (arity == 0) {
return "";
} else if (arity==1 && !((args[0] instanceof Struct) && ((Struct)args[0]).getName().equals(","))){
return args[0].getTerm().toString();
} else {
// comma case
Term head = ((Struct)args[0]).getTerm(0);
Term tail = ((Struct)args[0]).getTerm(1);
StringBuilder buffer = new StringBuilder(head.toString());
while (tail instanceof Struct && ((Struct)tail).getName().equals(",")){
head = ((Struct)tail).getTerm(0);
buffer.append(","+head.toString());
tail = ((Struct)tail).getTerm(1);
}
buffer.append(","+tail.toString());
return buffer.toString();
// return arg[0]+","+((Struct)arg[1]).toString0_bracket();
}
}
private String toStringAsList(OperatorManager op) {
Term h = args[0];
Term t = args[1].getTerm();
if (t.isList()) {
Struct tl = (Struct)t;
if (tl.isEmptyList()){
return h.toStringAsArgY(op,0);
}
return (h.toStringAsArgY(op,0) + "," + tl.toStringAsList(op));
} else {
return (h.toStringAsArgY(op,0) + "|" + t.toStringAsArgY(op,0));
}
}
@Override
String toStringAsArg(OperatorManager op, int prio, boolean x) {
int p = 0;
String v = "";
if (name.equals(".") && arity == 2) {
if (args[0].isEmptyList()) {
return("[]");
} else {
return("[" + toStringAsList(op) + "]");
}
} else if (name.equals("{}")) {
return("{" + toString0_bracket() + "}");
}
if (arity == 2) {
if ((p = op.opPrio(name,"xfx")) >= OperatorManager.OP_LOW) {
return(
(((x && p >= prio) || (!x && p > prio)) ? "(" : "") +
args[0].toStringAsArgX(op,p) +
" " + name + " " +
args[1].toStringAsArgX(op,p) +
(((x && p >= prio) || (!x && p > prio)) ? ")" : ""));
}
if ((p = op.opPrio(name,"yfx")) >= OperatorManager.OP_LOW) {
return(
(((x && p >= prio) || (!x && p > prio)) ? "(" : "") +
args[0].toStringAsArgY(op,p) +
" " + name + " " +
args[1].toStringAsArgX(op,p) +
(((x && p >= prio) || (!x && p > prio)) ? ")" : ""));
}
if ((p = op.opPrio(name,"xfy")) >= OperatorManager.OP_LOW) {
if (!name.equals(",")) {
return(
(((x && p >= prio) || (!x && p > prio)) ? "(" : "") +
args[0].toStringAsArgX(op,p) +
" " + name + " " +
args[1].toStringAsArgY(op,p) +
(((x && p >= prio) || (!x && p > prio)) ? ")" : ""));
} else {
return(
(((x && p >= prio) || (!x && p > prio)) ? "(" : "") +
args[0].toStringAsArgX(op,p) +
//",\n\t"+
","+
args[1].toStringAsArgY(op,p) +
(((x && p >= prio) || (!x && p > prio)) ? ")" : ""));
}
}
}
else if (arity == 1) {
if ((p = op.opPrio(name,"fx")) >= OperatorManager.OP_LOW) {
return(
(((x && p >= prio) || (!x && p > prio)) ? "(" : "") +
name + " " +
args[0].toStringAsArgX(op,p) +
(((x && p >= prio) || (!x && p > prio)) ? ")" : ""));
}
if ((p = op.opPrio(name,"fy")) >= OperatorManager.OP_LOW) {
return(
(((x && p >= prio) || (!x && p > prio)) ? "(" : "") +
name + " " +
args[0].toStringAsArgY(op,p) +
(((x && p >= prio) || (!x && p > prio)) ? ")" : ""));
}
if ((p = op.opPrio(name,"xf")) >= OperatorManager.OP_LOW) {
return(
(((x && p >= prio) || (!x && p > prio)) ? "(" : "") +
args[0].toStringAsArgX(op,p) +
" " + name + " " +
(((x && p >= prio) || (!x && p > prio)) ? ")" : ""));
}
if ((p = op.opPrio(name,"yf")) >= OperatorManager.OP_LOW) {
return(
(((x && p >= prio) || (!x && p > prio)) ? "(" : "") +
args[0].toStringAsArgY(op,p) +
" " + name + " " +
(((x && p >= prio) || (!x && p > prio)) ? ")" : ""));
}
}
v = (Parser.isAtom(name) ? name : "'" + name + "'");
if (arity == 0) {
return v;
}
v = v + "(";
for (p = 1;p < arity;p++) {
v = v + args[p - 1].toStringAsArgY(op,0) + ",";
}
v = v + args[arity - 1].toStringAsArgY(op,0);
v = v + ")";
return v;
}
@Override
public Term iteratedGoalTerm() {
if (name.equals("^") && arity == 2) {
Term goal = getTerm(1);
return goal.iteratedGoalTerm();
} else
return super.iteratedGoalTerm();
}
//
/**
* This class represents an iterator through the arguments
* of a Struct list.
*/
private static class ListIterator implements Iterator<Term> {
Struct list;
ListIterator(Struct t) {
list = t;
}
@Override
public boolean hasNext() {
return !list.isEmptyList();
}
@Override
public Term next() {
if (list.isEmptyList())
throw new NoSuchElementException();
// Using Struct#getTerm(int) instead of Struct#listHead and Struct#listTail
// to avoid redundant Struct#isList calls since it is only possible to get
// a StructIterator on a Struct instance which is already a list.
Term head = list.getTerm(0);
list = (Struct) list.getTerm(1);
return head;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}