/*
* tuProlog - Copyright (C) 2001-2002 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.List;
import java.util.Map;
/**
* This class represents a variable term.
* Variables are identified by a name (which must starts with
* an upper case letter) or the anonymous ('_') name.
*
* @see Term
*/
public class Var extends Term {
final static String ANY = "_";
// the name identifying the var
private String name;
private String completeName;
/** link is used for unification process */
private Term link;
/** timestamp is used for fix variables order */
private long timestamp;
/** id of ExecCtx owners of this variable, useful for renaming */
private int id;
/**
* Creates a variable identified by a name.
*
* The name must starts with an upper case letter or the underscore. If an underscore is
* specified as a name, the variable is anonymous.
*
* @param n is the name
* @throws InvalidTermException if n is not a valid Prolog variable name
*/
public Var(String n) {
link = null;
id = -1; //no execCtx owners
if (n.equals(ANY)) {
name = null;
completeName = null;
} else if (Character.isUpperCase(n.charAt(0)) || (n.startsWith(ANY))) {
name = n;
completeName = n;
} else {
throw new InvalidTermException("Illegal variable name: " + n);
}
}
/**
* Creates an anonymous variable.
*
* This is equivalent to build a variable with name "_".
*/
public Var() {
name = null;
completeName = null;
link = null;
id = ORIGINAL;
timestamp = 0;
}
/**
* Creates a internal engine variable.
*
* @param n is the name
* @param id is the id of ExecCtx
* @param alias code to discriminate external vars
* @param time is timestamp
*/
private Var(String n, int id, int alias, long time) {
name = n;
timestamp = time;
link = null;
if(id < 0) id = ORIGINAL;
rename(id,alias);
}
/* Identify kind of renaming */
final static int ORIGINAL = -1;
final static int PROGRESSIVE = -2;
/**
* Rename variable (assign completeName).
*/
void rename(int idExecCtx, int count) {
id = idExecCtx;
if (idExecCtx > -1) {
completeName = name + "_e" + idExecCtx;
}
if (id == ORIGINAL) completeName = name;
if (id == PROGRESSIVE) completeName = "_"+count;
}
/**
* Gets a copy of this variable.
*
* If the variable is not present in the list passed as argument,
* a copy of this variable is returned and added to the list. If instead
* a variable with the same time identifier is found in the list,
* then the variable in the list is returned.
*/
@Override
Term copy(Map<Var, Var> vMap, int idExecCtx) {
Term tt = getTerm();
if (tt == this) {
Var v = (Var) vMap.get(this);
if (v == null) {
// No occurrence of v before
v = new Var(name, idExecCtx, 0, timestamp);
vMap.put(this, v);
}
return v;
} else
return tt.copy(vMap, idExecCtx);
}
/** Gets a copy of this variable. */
@Override
Term copy(Map<Var, Var> vMap, Map<Var, Var> substMap) {
Var v;
Object temp = vMap.get(this);
if (temp == null) {
v = new Var(null, Var.PROGRESSIVE, vMap.size(), timestamp);//name,Var.PROGRESSIVE,vMap.size(),timestamp);
vMap.put(this,v);
} else
v = (Var) temp;
Term t = getTerm();
if (t instanceof Var) {
Object tt = substMap.get(t);
if (tt == null) {
substMap.put((Var) t, v);
v.link = null;
} else
v.link = (tt != v) ? (Var)tt : null;
}
if (t instanceof Struct)
v.link = t.copy(vMap, substMap);
if (t instanceof Number)
v.link = t;
return v;
}
/** De-unify the variable. */
@Override
public void free() {
link = null;
}
/** De-unify the variables in the list. */
public static void free(List<Var> varsUnified) {
for (Var v : varsUnified)
v.free();
}
/** Gets the name of the variable. */
public String getName() {
if (name != null) {
return completeName;
} else {
//return ANY+timestamp;
return ANY;
}
}
/** Gets the name of the variable. */
public String getOriginalName() {
if (name != null) {
return name;
} else {
//return ANY+timestamp;
return ANY + hashCode();
}
}
/**
* Gets the term which is referred by the variable.
*
* For unbound variable it is the variable itself, while
* for bound variable it is the bound term.
*/
@Override
public Term getTerm() {
Term tt = this;
Term t = link;
while (t != null ) {
tt = t;
if (t instanceof Var)
t = ((Var) t).link;
else
break;
}
return tt;
}
/**
* Gets the term which is direct referred by the variable.
*/
Term getLink() {
return link;
}
/** Set the term which is direct bound. */
void setLink(Term l) {
link = l;
}
/** Set the timestamp. */
void setTimestamp(long t) {
timestamp = t;
}
//
@Override
public boolean isEmptyList() {
Term t=getTerm();
if (t==this) {
return false;
} else {
return t.isEmptyList();
}
}
@Override
public boolean isAtomic() {
Term t=getTerm();
if (t==this) {
return false;
} else {
return t.isAtomic();
}
}
@Override
public boolean isCompound() {
Term t=getTerm();
if (t==this) {
return false;
} else {
return t.isCompound();
}
}
@Override
public boolean isAtom() {
Term t=getTerm();
if (t==this) {
return false;
} else {
return t.isAtom();
}
}
@Override
public boolean isList() {
Term t = getTerm();
if (t == this)
return false;
else
return t.isList();
}
@Override
public boolean isGround(){
Term t=getTerm();
if (t==this) {
return false;
} else {
return t.isGround();
}
}
//
/** Tests if this variable is ANY. */
public boolean isAnonymous() {
return name == null;
}
/** Tests if this variable is bound. */
public boolean isBound() {
return link != null;
}
/**
* Finds variable occurrence in a Struct, doing occur-check.
* (It was called findIn.)
*/
private boolean occurCheck(List<Var> vl, Struct t) {
int arity = t.getArity();
for (int c = 0; c < arity; c++) {
Term at = t.getTerm(c);
if (at instanceof Struct) {
if (occurCheck(vl, (Struct) at))
return true;
} else
if (at instanceof Var) {
Var v = (Var) at;
if (v.link == null)
vl.add(v);
if (this == v)
return true;
}
}
return false;
}
//
/** Resolve the occurrence of variables in a Term. */
@Override
long resolveTerm(long count) {
Term tt=getTerm();
if (tt != this) {
return tt.resolveTerm(count);
} else {
timestamp = count;
return count++;
}
}
//
/**
* Variable unification.
* <p>
* First, verify the Term eventually already unified with the same Var
* if the Term exist, unify var with that term, in order to handle situation
* as (A = p(X) , A = p(1)) which must produce X/1.
* <p>
* If instead the var is not already unified, then:
* <p>
* if the Term is a var bound to X, then try unification with X
* so for example if A=1, B=A then B is unified to 1 and not to A
* (note that it's coherent with chronological backtracking:
* the eventually backtracked A unification is always after
* backtracking of B unification.
* <p>
* if are the same Var, unification must succeed, but without any new
* bindings (to avoid cycles for extends in A = B, B = A)
* <p>
* if the term is a number, then it's a success and new link is created
* (retractable by means of a code)
* <p>
* if the term is a compound, then occur check test is executed:
* the var must not appear in the compound ( avoid X=p(X),
* or p(X,X)=p(Y,f(Y)) ); if occur check is ok
* then it's success and a new link is created (retractable by a code)
*/
@Override
boolean unify(List<Var> vl1, List<Var> vl2, Term t) {
Term tt = getTerm();
if(tt == this) {
t = t.getTerm();
if (t instanceof Var) {
if (this == t) {
try {
vl1.add(this);
} catch (NullPointerException e) {
/* vl1==null means nothing interesting for the caller */
}
return true;
}
} else
if (t instanceof Struct) {
// occur-check
if (occurCheck(vl2, (Struct)t)) {
return false;
}
} else
if (!(t instanceof Number)) {
return false;
}
link = t;
try {
vl1.add(this);
} catch (NullPointerException e) {
/* vl1==null mean nothing interesting for the caller */
}
//System.out.println("VAR "+name+" BOUND to "+link+" - time: "+time+" - mark: "+mark);
return true;
} else {
return (tt.unify(vl1, vl2, t));
}
}
@Override
public boolean isGreater(Term t) {
Term tt = getTerm();
if (tt == this) {
t = t.getTerm();
if (!(t instanceof Var)) return false;
return timestamp > ((Var)t).timestamp;
}
else {
return tt.isGreater(t);
}
}
@Override
public boolean isEqual(Term t) {
Term tt = getTerm();
if(tt == this) {
t = t.getTerm();
return (t instanceof Var && timestamp == ((Var)t).timestamp);
} else {
return tt.isEqual(t);
}
}
/**
* Gets the string representation of this variable.
*
* For bounded variables, the string is <Var Name>/<bound Term>.
*/
@Override
public String toString() {
Term tt = getTerm();
if (name != null) {
if (tt == this){
return completeName;
} else {
return (completeName + " / " + tt.toString());
}
} else {
if (tt == this) {
return ANY + hashCode();
} else {
return tt.toString();
}
}
}
/**
* Gets the string representation of this variable, providing
* the string representation of the linked term in the case of
* bound variable.
*/
public String toStringFlattened() {
Term tt = getTerm();
if (name != null) {
if (tt == this) {
return completeName;
} else {
return tt.toString();
}
} else {
if (tt == this) {
return ANY + hashCode();
} else {
return tt.toString();
}
}
}
}