JLogTerm.java
/*
* #%L
* prolobjectlink-jpi-jlog
* %%
* Copyright (C) 2019 Prolobjectlink Project
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
package io.github.prolobjectlink.prolog.jlog;
import static io.github.prolobjectlink.prolog.PrologTermType.ATOM_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.CUT_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.DOUBLE_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.FAIL_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.FALSE_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.FLOAT_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.INTEGER_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.LIST_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.LONG_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.MAP_ENTRY_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.MAP_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.NIL_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.OBJECT_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.STRUCTURE_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.TRUE_TYPE;
import static io.github.prolobjectlink.prolog.PrologTermType.VARIABLE_TYPE;
import static ubc.cs.JLog.Foundation.iType.TYPE_LIST;
import static ubc.cs.JLog.Foundation.iType.TYPE_PREDICATE;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Enumeration;
import io.github.prolobjectlink.prolog.AbstractTerm;
import io.github.prolobjectlink.prolog.PrologLogger;
import io.github.prolobjectlink.prolog.PrologNumber;
import io.github.prolobjectlink.prolog.PrologProvider;
import io.github.prolobjectlink.prolog.PrologTerm;
import ubc.cs.JLog.Foundation.jEquivalenceMapping;
import ubc.cs.JLog.Foundation.jKnowledgeBase;
import ubc.cs.JLog.Foundation.jPrologFileServices;
import ubc.cs.JLog.Foundation.jPrologServices;
import ubc.cs.JLog.Parser.pOperatorEntry;
import ubc.cs.JLog.Parser.pOperatorRegistry;
import ubc.cs.JLog.Parser.pPredicateRegistry;
import ubc.cs.JLog.Terms.jCompoundTerm;
import ubc.cs.JLog.Terms.jInteger;
import ubc.cs.JLog.Terms.jList;
import ubc.cs.JLog.Terms.jListPair;
import ubc.cs.JLog.Terms.jNullList;
import ubc.cs.JLog.Terms.jTerm;
import ubc.cs.JLog.Terms.jVariable;
/**
* @author Jose Zalacain
* @since 1.0
*/
abstract class JLogTerm extends AbstractTerm implements PrologTerm {
protected int vIndex;
protected jTerm value;
private PrologTerm vValue;
protected static int vIdexer = 0;
private final jEquivalenceMapping equivalence = new jEquivalenceMapping();
protected final jList adaptList(PrologTerm[] arguments) {
jList pList = jNullList.NULL_LIST;
for (int i = arguments.length - 1; i >= 0; --i) {
pList = new jListPair(fromTerm(arguments[i], jTerm.class), pList);
}
return pList;
}
protected final jCompoundTerm adaptCompound(PrologTerm[] arguments) {
jCompoundTerm compound = new jCompoundTerm(arguments.length);
for (PrologTerm iPrologTerm : arguments) {
compound.addTerm(fromTerm(iPrologTerm, jTerm.class));
}
return compound;
}
protected JLogTerm(int type, PrologProvider provider) {
super(type, provider);
}
protected JLogTerm(int type, PrologProvider provider, jTerm value) {
super(type, provider);
this.value = value;
}
protected JLogTerm(int type, PrologProvider provider, int vIndex) {
this(type, provider, new jVariable());
this.vIndex = vIndex;
}
protected JLogTerm(int type, PrologProvider provider, String name, int vIndex) {
this(type, provider, new jVariable(name));
this.vIndex = vIndex;
}
public final boolean isAtom() {
return type == ATOM_TYPE || type == FAIL_TYPE || type == FALSE_TYPE || type == TRUE_TYPE || type == CUT_TYPE
|| type == NIL_TYPE;
}
public final boolean isNumber() {
return isDouble() || isFloat() || isInteger() || isLong();
}
public final boolean isFloat() {
return type == FLOAT_TYPE;
}
public final boolean isDouble() {
return type == DOUBLE_TYPE;
}
public final boolean isInteger() {
return type == INTEGER_TYPE;
}
public final boolean isLong() {
return type == LONG_TYPE;
}
public final boolean isVariable() {
return type == VARIABLE_TYPE;
}
public final boolean isList() {
return (this instanceof JLogList)
||
(this instanceof JLogEmpty)
||
(this instanceof JLogMap);
}
public final boolean isStructure() {
return (this instanceof JLogStructure) || (this instanceof JLogEntry);
}
public final boolean isNil() {
return this instanceof JLogNil;
}
public final boolean isEmptyList() {
return (this instanceof JLogEmpty)
||
(this instanceof JLogMap
&&
((JLogMap) this).size() == 0);
}
public boolean isEvaluable() {
if (isStructure()) {
String builtins = "builtins";
jKnowledgeBase kb = new jKnowledgeBase();
pOperatorRegistry or = new pOperatorRegistry();
pPredicateRegistry pr = new pPredicateRegistry();
jPrologServices engine = new jPrologServices(kb, pr, or);
engine.setFileServices(new jPrologFileServices());
try {
engine.loadLibrary(builtins);
} catch (IOException e) {
getLogger().error(getClass(), PrologLogger.ERROR_LOADING_BUILT_INS, e);
}
Enumeration<?> e = engine.getOperatorRegistry().enumOperators();
while (e.hasMoreElements()) {
Object object = e.nextElement();
if (object instanceof pOperatorEntry) {
pOperatorEntry entry = (pOperatorEntry) object;
String operator = entry.getName();
String functor = getFunctor();
if (functor.startsWith("'") && functor.endsWith("'")) {
functor = functor.substring(1, functor.length() - 1);
}
if (operator.equals(functor)) {
return true;
}
}
}
}
return false;
}
public final boolean isAtomic() {
return !isCompound();
}
public boolean isCompound() {
return (value != null)
&& (value.type == TYPE_PREDICATE
|| value.type == TYPE_LIST
);
}
public final boolean isTrueType() {
Object object = getObject();
return object != null && object.equals(true);
}
public final boolean isFalseType() {
Object object = getObject();
return object != null && object.equals(false);
}
public final boolean isNullType() {
return isObjectType() && getObject() == null;
}
public final boolean isVoidType() {
return getObject() == void.class;
}
public final boolean isObjectType() {
return getType() == OBJECT_TYPE;
}
public final boolean isReference() {
return isObjectType();
}
public PrologTerm getTerm() {
return toTerm(value.getTerm(), PrologTerm.class);
}
public final boolean unify(PrologTerm term) {
Deque<PrologTerm> stack = new ArrayDeque<PrologTerm>();
boolean match = unify(term, stack);
for (PrologTerm prologTerm : stack) {
((JLogTerm) prologTerm).unbind();
}
stack.clear();
return match;
}
private final boolean unify(PrologTerm term, Deque<PrologTerm> stack) {
return unify((JLogTerm) term, stack);
}
private final boolean unify(JLogTerm otherTerm, Deque<PrologTerm> stack) {
JLogTerm thisTerm = this;
if (thisTerm == otherTerm) {
if (thisTerm.isVariableNotBound()) {
thisTerm.bind(otherTerm);
otherTerm.bind(thisTerm);
stack.push(thisTerm);
}
return true;
}
else if (thisTerm.isVariableBound()) {
return ((JLogTerm) thisTerm.vValue).unify(otherTerm, stack);
}
else if (otherTerm.isVariableBound()) {
return ((JLogTerm) otherTerm.vValue).unify(thisTerm, stack);
}
// current term is a free variable
else if (thisTerm.isVariableNotBound()) {
thisTerm.bind(otherTerm);
stack.push(thisTerm);
return true;
}
// the other term is a free variable
else if (otherTerm.isVariableNotBound()) {
otherTerm.bind(thisTerm);
stack.push(otherTerm);
return true;
}
// if at least term is a number then check equivalence
else if (thisTerm.isNumber() || otherTerm.isNumber()) {
if ((thisTerm.isInteger() || thisTerm.isLong()) && (otherTerm.isInteger() || otherTerm.isLong())) {
int thisInt = fromTerm(thisTerm, jInteger.class).getIntegerValue();
int otherInt = fromTerm(otherTerm, jInteger.class).getIntegerValue();
return thisInt == otherInt;
}
return thisTerm.equals(otherTerm);
}
else {
int thisArity = thisTerm.getArity();
int otherArity = otherTerm.getArity();
String thisFunctor = thisTerm.getFunctor();
String otherFunctor = otherTerm.getFunctor();
if (thisFunctor.equals(otherFunctor) && thisArity == otherArity) {
PrologTerm[] thisArguments = thisTerm.getArguments();
PrologTerm[] otherArguments = otherTerm.getArguments();
if (thisArguments.length == otherArguments.length) {
for (int i = 0; i < thisArguments.length; i++) {
if (thisArguments[i] != null && otherArguments[i] != null) {
JLogTerm thisJLogTerm = (JLogTerm) thisArguments[i];
JLogTerm otherJLogTerm = (JLogTerm) otherArguments[i];
if (!thisJLogTerm.unify(otherJLogTerm, stack)) {
return false;
}
}
}
}
return true;
}
}
return false;
}
/**
* Check if Variable and bound. A variable bound is synonym of not free variable
* because this variable have instance value.
*
* @return true if Variable and bound.
*/
public final boolean isVariableBound() {
return isVariable() && vValue != null;
}
/**
* Check if current variable is not bound. A variable not bound is synonym of
* free variable because this variable don't have instance value.
*
* @return true if Variable and not bound.
*/
public final boolean isVariableNotBound() {
return isVariable() && vValue == null;
}
/**
* Binds a variable to a term
*
* @param term
*/
private final void bind(JLogTerm term) {
vValue = term;
}
/** Unbinds a term reseting it to a variable */
private final void unbind() {
vValue = null;
}
public final int compareTo(PrologTerm term) {
int termType = term.getType();
if ((type >> 8) < (termType >> 8)) {
return -1;
} else if ((type >> 8) > (termType >> 8)) {
return 1;
}
switch (type) {
case NIL_TYPE:
case CUT_TYPE:
case FAIL_TYPE:
case TRUE_TYPE:
case FALSE_TYPE:
case ATOM_TYPE:
// alphabetic functor comparison
int result = value.getName().compareTo(term.getFunctor());
if (result < 0) {
return -1;
} else if (result > 0) {
return 1;
}
break;
case FLOAT_TYPE:
float thisFloatValue = ((jFloat) value).getRealValue();
float otherFloatValue = ((PrologNumber) term).getFloatValue();
if (thisFloatValue < otherFloatValue) {
return -1;
} else if (thisFloatValue > otherFloatValue) {
return 1;
}
break;
case LONG_TYPE:
long thisValue = ((jInteger) value).getIntegerValue();
long otherValue = ((PrologNumber) term).getLongValue();
if (thisValue < otherValue) {
return -1;
} else if (thisValue > otherValue) {
return 1;
}
break;
case DOUBLE_TYPE:
double thisDoubleValue = ((jDouble) value).getRealValue();
double otherDoubleValue = ((PrologNumber) term).getDoubleValue();
if (thisDoubleValue < otherDoubleValue) {
return -1;
} else if (thisDoubleValue > otherDoubleValue) {
return 1;
}
break;
case INTEGER_TYPE:
int thisIntergerValue = ((jInteger) value).getIntegerValue();
int otherIntegerValue = ((PrologNumber) term).getIntegerValue();
if (thisIntergerValue < otherIntegerValue) {
return -1;
} else if (thisIntergerValue > otherIntegerValue) {
return 1;
}
break;
case MAP_TYPE:
case LIST_TYPE:
case STRUCTURE_TYPE:
case MAP_ENTRY_TYPE:
PrologTerm thisCompound = this;
PrologTerm otherCompound = term;
if (thisCompound.isEmptyList() && otherCompound.isEmptyList()) {
return 0;
}
// comparison by arity
if (thisCompound.getArity() < otherCompound.getArity()) {
return -1;
} else if (thisCompound.getArity() > otherCompound.getArity()) {
return 1;
}
// alphabetic functor comparison
result = thisCompound.getFunctor().compareTo(otherCompound.getFunctor());
if (result < 0) {
return -1;
} else if (result > 0) {
return 1;
}
// arguments comparison
PrologTerm[] thisArguments = thisCompound.getArguments();
PrologTerm[] otherArguments = otherCompound.getArguments();
for (int i = 0; i < thisArguments.length; i++) {
PrologTerm thisArgument = thisArguments[i];
PrologTerm otherArgument = otherArguments[i];
if (thisArgument != null && otherArgument != null) {
result = thisArgument.compareTo(otherArgument);
if (result != 0) {
return result;
}
}
}
break;
case VARIABLE_TYPE:
JLogTerm thisVariable = this;
JLogTerm otherVariable = ((JLogTerm) term);
if (thisVariable.vIndex < otherVariable.vIndex) {
return -1;
} else if (thisVariable.vIndex > otherVariable.vIndex) {
return 1;
}
break;
default:
return 0;
}
return 0;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + type;
result = prime * result + ((value == null) ? 0 : value.toString().hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
JLogTerm other = (JLogTerm) obj;
if (type != other.type)
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equivalence(other.value, equivalence)) {
return false;
}
return true;
}
@Override
public String toString() {
return value.toString(true);
}
}