View Javadoc

1   /*
2    * #%L
3    * prolobjectlink-jpi-jlog
4    * %%
5    * Copyright (C) 2019 Prolobjectlink Project
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU General Public License as
9    * published by the Free Software Foundation, either version 3 of the
10   * License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Public
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/gpl-3.0.html>.
20   * #L%
21   */
22  package io.github.prolobjectlink.prolog.jlog;
23  
24  import static io.github.prolobjectlink.prolog.PrologTermType.ATOM_TYPE;
25  import static io.github.prolobjectlink.prolog.PrologTermType.CUT_TYPE;
26  import static io.github.prolobjectlink.prolog.PrologTermType.DOUBLE_TYPE;
27  import static io.github.prolobjectlink.prolog.PrologTermType.FAIL_TYPE;
28  import static io.github.prolobjectlink.prolog.PrologTermType.FALSE_TYPE;
29  import static io.github.prolobjectlink.prolog.PrologTermType.FLOAT_TYPE;
30  import static io.github.prolobjectlink.prolog.PrologTermType.INTEGER_TYPE;
31  import static io.github.prolobjectlink.prolog.PrologTermType.LIST_TYPE;
32  import static io.github.prolobjectlink.prolog.PrologTermType.LONG_TYPE;
33  import static io.github.prolobjectlink.prolog.PrologTermType.MAP_ENTRY_TYPE;
34  import static io.github.prolobjectlink.prolog.PrologTermType.MAP_TYPE;
35  import static io.github.prolobjectlink.prolog.PrologTermType.NIL_TYPE;
36  import static io.github.prolobjectlink.prolog.PrologTermType.OBJECT_TYPE;
37  import static io.github.prolobjectlink.prolog.PrologTermType.STRUCTURE_TYPE;
38  import static io.github.prolobjectlink.prolog.PrologTermType.TRUE_TYPE;
39  import static io.github.prolobjectlink.prolog.PrologTermType.VARIABLE_TYPE;
40  import static ubc.cs.JLog.Foundation.iType.TYPE_LIST;
41  import static ubc.cs.JLog.Foundation.iType.TYPE_PREDICATE;
42  
43  import java.io.IOException;
44  import java.util.ArrayDeque;
45  import java.util.Deque;
46  import java.util.Enumeration;
47  
48  import io.github.prolobjectlink.prolog.AbstractTerm;
49  import io.github.prolobjectlink.prolog.PrologLogger;
50  import io.github.prolobjectlink.prolog.PrologNumber;
51  import io.github.prolobjectlink.prolog.PrologProvider;
52  import io.github.prolobjectlink.prolog.PrologTerm;
53  import ubc.cs.JLog.Foundation.jEquivalenceMapping;
54  import ubc.cs.JLog.Foundation.jKnowledgeBase;
55  import ubc.cs.JLog.Foundation.jPrologFileServices;
56  import ubc.cs.JLog.Foundation.jPrologServices;
57  import ubc.cs.JLog.Parser.pOperatorEntry;
58  import ubc.cs.JLog.Parser.pOperatorRegistry;
59  import ubc.cs.JLog.Parser.pPredicateRegistry;
60  import ubc.cs.JLog.Terms.jCompoundTerm;
61  import ubc.cs.JLog.Terms.jInteger;
62  import ubc.cs.JLog.Terms.jList;
63  import ubc.cs.JLog.Terms.jListPair;
64  import ubc.cs.JLog.Terms.jNullList;
65  import ubc.cs.JLog.Terms.jTerm;
66  import ubc.cs.JLog.Terms.jVariable;
67  
68  /**
69   * @author Jose Zalacain
70   * @since 1.0
71   */
72  abstract class JLogTerm extends AbstractTerm implements PrologTerm {
73  
74  	protected int vIndex;
75  	protected jTerm value;
76  	private PrologTerm vValue;
77  	protected static int vIdexer = 0;
78  
79  	private final jEquivalenceMapping equivalence = new jEquivalenceMapping();
80  
81  	protected final jList adaptList(PrologTerm[] arguments) {
82  		jList pList = jNullList.NULL_LIST;
83  		for (int i = arguments.length - 1; i >= 0; --i) {
84  			pList = new jListPair(fromTerm(arguments[i], jTerm.class), pList);
85  		}
86  		return pList;
87  	}
88  
89  	protected final jCompoundTerm adaptCompound(PrologTerm[] arguments) {
90  		jCompoundTerm compound = new jCompoundTerm(arguments.length);
91  		for (PrologTerm iPrologTerm : arguments) {
92  			compound.addTerm(fromTerm(iPrologTerm, jTerm.class));
93  		}
94  		return compound;
95  	}
96  
97  	protected JLogTerm(int type, PrologProvider provider) {
98  		super(type, provider);
99  	}
100 
101 	protected JLogTerm(int type, PrologProvider provider, jTerm value) {
102 		super(type, provider);
103 		this.value = value;
104 	}
105 
106 	protected JLogTerm(int type, PrologProvider provider, int vIndex) {
107 		this(type, provider, new jVariable());
108 		this.vIndex = vIndex;
109 	}
110 
111 	protected JLogTerm(int type, PrologProvider provider, String name, int vIndex) {
112 		this(type, provider, new jVariable(name));
113 		this.vIndex = vIndex;
114 	}
115 
116 	public final boolean isAtom() {
117 		return type == ATOM_TYPE || type == FAIL_TYPE || type == FALSE_TYPE || type == TRUE_TYPE || type == CUT_TYPE
118 				|| type == NIL_TYPE;
119 	}
120 
121 	public final boolean isNumber() {
122 		return isDouble() || isFloat() || isInteger() || isLong();
123 	}
124 
125 	public final boolean isFloat() {
126 		return type == FLOAT_TYPE;
127 	}
128 
129 	public final boolean isDouble() {
130 		return type == DOUBLE_TYPE;
131 	}
132 
133 	public final boolean isInteger() {
134 		return type == INTEGER_TYPE;
135 	}
136 
137 	public final boolean isLong() {
138 		return type == LONG_TYPE;
139 	}
140 
141 	public final boolean isVariable() {
142 		return type == VARIABLE_TYPE;
143 	}
144 
145 	public final boolean isList() {
146 		return (this instanceof JLogList)
147 
148 				||
149 
150 				(this instanceof JLogEmpty)
151 
152 				||
153 
154 				(this instanceof JLogMap);
155 	}
156 
157 	public final boolean isStructure() {
158 		return (this instanceof JLogStructure) || (this instanceof JLogEntry);
159 	}
160 
161 	public final boolean isNil() {
162 		return this instanceof JLogNil;
163 	}
164 
165 	public final boolean isEmptyList() {
166 		return (this instanceof JLogEmpty)
167 
168 				||
169 
170 				(this instanceof JLogMap
171 
172 						&&
173 
174 						((JLogMap) this).size() == 0);
175 	}
176 
177 	public boolean isEvaluable() {
178 		if (isStructure()) {
179 			String builtins = "builtins";
180 			jKnowledgeBase kb = new jKnowledgeBase();
181 			pOperatorRegistry or = new pOperatorRegistry();
182 			pPredicateRegistry pr = new pPredicateRegistry();
183 			jPrologServices engine = new jPrologServices(kb, pr, or);
184 			engine.setFileServices(new jPrologFileServices());
185 			try {
186 				engine.loadLibrary(builtins);
187 			} catch (IOException e) {
188 				getLogger().error(getClass(), PrologLogger.ERROR_LOADING_BUILT_INS, e);
189 			}
190 			Enumeration<?> e = engine.getOperatorRegistry().enumOperators();
191 			while (e.hasMoreElements()) {
192 				Object object = e.nextElement();
193 				if (object instanceof pOperatorEntry) {
194 					pOperatorEntry entry = (pOperatorEntry) object;
195 					String operator = entry.getName();
196 					String functor = getFunctor();
197 					if (functor.startsWith("'") && functor.endsWith("'")) {
198 						functor = functor.substring(1, functor.length() - 1);
199 					}
200 					if (operator.equals(functor)) {
201 						return true;
202 					}
203 				}
204 			}
205 		}
206 		return false;
207 	}
208 
209 	public final boolean isAtomic() {
210 		return !isCompound();
211 	}
212 
213 	public boolean isCompound() {
214 		return (value != null)
215 
216 				&& (value.type == TYPE_PREDICATE
217 
218 						|| value.type == TYPE_LIST
219 
220 				);
221 	}
222 
223 	public final boolean isTrueType() {
224 		Object object = getObject();
225 		return object != null && object.equals(true);
226 	}
227 
228 	public final boolean isFalseType() {
229 		Object object = getObject();
230 		return object != null && object.equals(false);
231 	}
232 
233 	public final boolean isNullType() {
234 		return isObjectType() && getObject() == null;
235 	}
236 
237 	public final boolean isVoidType() {
238 		return getObject() == void.class;
239 	}
240 
241 	public final boolean isObjectType() {
242 		return getType() == OBJECT_TYPE;
243 	}
244 
245 	public final boolean isReference() {
246 		return isObjectType();
247 	}
248 
249 	public PrologTerm getTerm() {
250 		return toTerm(value.getTerm(), PrologTerm.class);
251 	}
252 
253 	public final boolean unify(PrologTerm term) {
254 		Deque<PrologTerm> stack = new ArrayDeque<PrologTerm>();
255 		boolean match = unify(term, stack);
256 		for (PrologTerm prologTerm : stack) {
257 			((JLogTerm) prologTerm).unbind();
258 		}
259 		stack.clear();
260 		return match;
261 	}
262 
263 	private final boolean unify(PrologTerm term, Deque<PrologTerm> stack) {
264 		return unify((JLogTerm) term, stack);
265 	}
266 
267 	private final boolean unify(JLogTerm otherTerm, Deque<PrologTerm> stack) {
268 
269 		JLogTerm thisTerm = this;
270 
271 		if (thisTerm == otherTerm) {
272 			if (thisTerm.isVariableNotBound()) {
273 				thisTerm.bind(otherTerm);
274 				otherTerm.bind(thisTerm);
275 				stack.push(thisTerm);
276 			}
277 			return true;
278 		}
279 
280 		else if (thisTerm.isVariableBound()) {
281 			return ((JLogTerm) thisTerm.vValue).unify(otherTerm, stack);
282 		}
283 
284 		else if (otherTerm.isVariableBound()) {
285 			return ((JLogTerm) otherTerm.vValue).unify(thisTerm, stack);
286 		}
287 
288 		// current term is a free variable
289 		else if (thisTerm.isVariableNotBound()) {
290 			thisTerm.bind(otherTerm);
291 			stack.push(thisTerm);
292 			return true;
293 		}
294 
295 		// the other term is a free variable
296 		else if (otherTerm.isVariableNotBound()) {
297 			otherTerm.bind(thisTerm);
298 			stack.push(otherTerm);
299 			return true;
300 		}
301 
302 		// if at least term is a number then check equivalence
303 		else if (thisTerm.isNumber() || otherTerm.isNumber()) {
304 			if ((thisTerm.isInteger() || thisTerm.isLong()) && (otherTerm.isInteger() || otherTerm.isLong())) {
305 				int thisInt = fromTerm(thisTerm, jInteger.class).getIntegerValue();
306 				int otherInt = fromTerm(otherTerm, jInteger.class).getIntegerValue();
307 				return thisInt == otherInt;
308 			}
309 			return thisTerm.equals(otherTerm);
310 		}
311 
312 		else {
313 
314 			int thisArity = thisTerm.getArity();
315 			int otherArity = otherTerm.getArity();
316 			String thisFunctor = thisTerm.getFunctor();
317 			String otherFunctor = otherTerm.getFunctor();
318 			if (thisFunctor.equals(otherFunctor) && thisArity == otherArity) {
319 				PrologTerm[] thisArguments = thisTerm.getArguments();
320 				PrologTerm[] otherArguments = otherTerm.getArguments();
321 				if (thisArguments.length == otherArguments.length) {
322 					for (int i = 0; i < thisArguments.length; i++) {
323 						if (thisArguments[i] != null && otherArguments[i] != null) {
324 							JLogTerm thisJLogTerm = (JLogTerm) thisArguments[i];
325 							JLogTerm otherJLogTerm = (JLogTerm) otherArguments[i];
326 							if (!thisJLogTerm.unify(otherJLogTerm, stack)) {
327 								return false;
328 							}
329 						}
330 					}
331 				}
332 				return true;
333 			}
334 		}
335 		return false;
336 
337 	}
338 
339 	/**
340 	 * Check if Variable and bound. A variable bound is synonym of not free variable
341 	 * because this variable have instance value.
342 	 * 
343 	 * @return true if Variable and bound.
344 	 */
345 	public final boolean isVariableBound() {
346 		return isVariable() && vValue != null;
347 	}
348 
349 	/**
350 	 * Check if current variable is not bound. A variable not bound is synonym of
351 	 * free variable because this variable don't have instance value.
352 	 * 
353 	 * @return true if Variable and not bound.
354 	 */
355 	public final boolean isVariableNotBound() {
356 		return isVariable() && vValue == null;
357 	}
358 
359 	/**
360 	 * Binds a variable to a term
361 	 * 
362 	 * @param term
363 	 */
364 	private final void bind(JLogTerm term) {
365 		vValue = term;
366 	}
367 
368 	/** Unbinds a term reseting it to a variable */
369 	private final void unbind() {
370 		vValue = null;
371 	}
372 
373 	public final int compareTo(PrologTerm term) {
374 
375 		int termType = term.getType();
376 
377 		if ((type >> 8) < (termType >> 8)) {
378 			return -1;
379 		} else if ((type >> 8) > (termType >> 8)) {
380 			return 1;
381 		}
382 
383 		switch (type) {
384 		case NIL_TYPE:
385 		case CUT_TYPE:
386 		case FAIL_TYPE:
387 		case TRUE_TYPE:
388 		case FALSE_TYPE:
389 		case ATOM_TYPE:
390 
391 			// alphabetic functor comparison
392 			int result = value.getName().compareTo(term.getFunctor());
393 			if (result < 0) {
394 				return -1;
395 			} else if (result > 0) {
396 				return 1;
397 			}
398 			break;
399 
400 		case FLOAT_TYPE:
401 
402 			float thisFloatValue = ((jFloat) value).getRealValue();
403 			float otherFloatValue = ((PrologNumber) term).getFloatValue();
404 
405 			if (thisFloatValue < otherFloatValue) {
406 				return -1;
407 			} else if (thisFloatValue > otherFloatValue) {
408 				return 1;
409 			}
410 
411 			break;
412 
413 		case LONG_TYPE:
414 
415 			long thisValue = ((jInteger) value).getIntegerValue();
416 			long otherValue = ((PrologNumber) term).getLongValue();
417 
418 			if (thisValue < otherValue) {
419 				return -1;
420 			} else if (thisValue > otherValue) {
421 				return 1;
422 			}
423 
424 			break;
425 
426 		case DOUBLE_TYPE:
427 
428 			double thisDoubleValue = ((jDouble) value).getRealValue();
429 			double otherDoubleValue = ((PrologNumber) term).getDoubleValue();
430 
431 			if (thisDoubleValue < otherDoubleValue) {
432 				return -1;
433 			} else if (thisDoubleValue > otherDoubleValue) {
434 				return 1;
435 			}
436 
437 			break;
438 
439 		case INTEGER_TYPE:
440 
441 			int thisIntergerValue = ((jInteger) value).getIntegerValue();
442 			int otherIntegerValue = ((PrologNumber) term).getIntegerValue();
443 
444 			if (thisIntergerValue < otherIntegerValue) {
445 				return -1;
446 			} else if (thisIntergerValue > otherIntegerValue) {
447 				return 1;
448 			}
449 
450 			break;
451 
452 		case MAP_TYPE:
453 		case LIST_TYPE:
454 		case STRUCTURE_TYPE:
455 		case MAP_ENTRY_TYPE:
456 
457 			PrologTerm thisCompound = this;
458 			PrologTerm otherCompound = term;
459 
460 			if (thisCompound.isEmptyList() && otherCompound.isEmptyList()) {
461 				return 0;
462 			}
463 
464 			// comparison by arity
465 			if (thisCompound.getArity() < otherCompound.getArity()) {
466 				return -1;
467 			} else if (thisCompound.getArity() > otherCompound.getArity()) {
468 				return 1;
469 			}
470 
471 			// alphabetic functor comparison
472 			result = thisCompound.getFunctor().compareTo(otherCompound.getFunctor());
473 			if (result < 0) {
474 				return -1;
475 			} else if (result > 0) {
476 				return 1;
477 			}
478 
479 			// arguments comparison
480 			PrologTerm[] thisArguments = thisCompound.getArguments();
481 			PrologTerm[] otherArguments = otherCompound.getArguments();
482 
483 			for (int i = 0; i < thisArguments.length; i++) {
484 				PrologTerm thisArgument = thisArguments[i];
485 				PrologTerm otherArgument = otherArguments[i];
486 				if (thisArgument != null && otherArgument != null) {
487 					result = thisArgument.compareTo(otherArgument);
488 					if (result != 0) {
489 						return result;
490 					}
491 				}
492 			}
493 			break;
494 
495 		case VARIABLE_TYPE:
496 
497 			JLogTerm thisVariable = this;
498 			JLogTerm otherVariable = ((JLogTerm) term);
499 			if (thisVariable.vIndex < otherVariable.vIndex) {
500 				return -1;
501 			} else if (thisVariable.vIndex > otherVariable.vIndex) {
502 				return 1;
503 			}
504 			break;
505 
506 		default:
507 			return 0;
508 
509 		}
510 
511 		return 0;
512 	}
513 
514 	@Override
515 	public int hashCode() {
516 		final int prime = 31;
517 		int result = 1;
518 		result = prime * result + type;
519 		result = prime * result + ((value == null) ? 0 : value.toString().hashCode());
520 		return result;
521 	}
522 
523 	@Override
524 	public boolean equals(Object obj) {
525 		if (this == obj)
526 			return true;
527 		if (obj == null)
528 			return false;
529 		if (getClass() != obj.getClass())
530 			return false;
531 		JLogTerm other = (JLogTerm) obj;
532 		if (type != other.type)
533 			return false;
534 		if (value == null) {
535 			if (other.value != null)
536 				return false;
537 		} else if (!value.equivalence(other.value, equivalence)) {
538 			return false;
539 		}
540 		return true;
541 	}
542 
543 	@Override
544 	public String toString() {
545 		return value.toString(true);
546 	}
547 
548 }