1 //tabstop=4
2 //*****************************************************************************/
3 // Project: jpl
4 //
5 // File:    $Id$
6 // Date:    $Date$
7 // Author:  Fred Dushin <fadushin@syr.edu>
8 //
9 //
10 // Description:
11 //
12 //
13 // -------------------------------------------------------------------------
14 // Copyright (c) 2004 Paul Singleton
15 // Copyright (c) 1998 Fred Dushin
16 //                    All rights reserved.
17 //
18 // This library is free software; you can redistribute it and/or
19 // modify it under the terms of the GNU Library Public License
20 // as published by the Free Software Foundation; either version 2
21 // of the License, or (at your option) any later version.
22 //
23 // This library is distributed in the hope that it will be useful,
24 // but WITHOUT ANY WARRANTY; without even the implied warranty of
25 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26 // GNU Library Public License for more details.
27 //*****************************************************************************/
28 package jpl;
29 
30 import java.util.Hashtable;
31 import java.util.Iterator;
32 import java.util.Map;
33 import jpl.fli.DoubleHolder;
34 import jpl.fli.Int64Holder;
35 import jpl.fli.IntHolder;
36 import jpl.fli.Prolog;
37 import jpl.fli.StringHolder;
38 import jpl.fli.term_t;
39 
40 //----------------------------------------------------------------------/
41 // Term
42 /**
43  * Term is the abstract base class for
44  * Compound, Atom, Variable, Integer and Float, which comprise a Java-oriented concrete syntax for Prolog.
45  * You cannot create instances of Term directly; rather, you should create
46  * instances of Term's concrete subclasses.
47  * Alternatively, use textToTerm() to construct a Term from its conventional
48  * Prolog source text representation.
49  *
50  * <hr><i>
51  * Copyright (C) 2004  Paul Singleton<p>
52  * Copyright (C) 1998  Fred Dushin<p>
53  *
54  * This library is free software; you can redistribute it and/or
55  * modify it under the terms of the GNU Library Public License
56  * as published by the Free Software Foundation; either version 2
57  * of the License, or (at your option) any later version.<p>
58  *
59  * This library is distributed in the hope that it will be useful,
60  * but WITHOUT ANY WARRANTY; without even the implied warranty of
61  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
62  * GNU Library Public License for more details.<p>
63  * </i><hr>
64  * @author  Fred Dushin <fadushin@syr.edu>
65  * @version $Revision$
66  */
67 public abstract class Term {
68 	//==================================================================/
69 	//  Attributes
70 	//==================================================================/
71 
72 	//==================================================================/
73 	//  Constructors
74 	//==================================================================/
75 
76 	/**
77 	 * This default constructor is provided in order for subclasses
78 	 * to be able to define their own default constructors.
79 	 */
Term()80 	protected Term() {
81 	}
82 
83 	//==================================================================/
84 	//  Methods (abstract, common)
85 	//==================================================================/
86 
87 	/**
88 	 * returns the ano-th (1+) argument of a (Compound) Term
89 	 * throws a JPLException for any other subclass
90 	 *
91 	 * @return the ano-th argument of a (Compound) Term
92 	 */
arg(int ano)93 	public abstract Term arg(int ano);
94 
95 	/**
96 	 * returns, as a Term[], the arguments of a Compound
97 	 * returns an empty Term[] from an Atom, Integer or Float
98 	 * throws a JPLException from a Variable
99 	 *
100 	 * @return the arguments of a Compound as a Term[
101 	 */
args()102 	public abstract Term[] args();
103 
104 	/**
105 	 * Tests whether this Term's functor has (String) 'name' and 'arity'
106 	 * Returns false if called inappropriately
107 	 *
108 	 * @return whether this Term's functor has (String) 'name' and 'arity'
109 	 */
hasFunctor(String name, int arity)110 	public abstract boolean hasFunctor(String name, int arity);
111 
112 	/**
113 	 * Tests whether this Term's functor has (int) 'name' and 'arity'
114 	 * Returns false if called inappropriately
115 	 *
116 	 * @return whether this Term's functor has (int) 'name' and 'arity'
117 	 */
hasFunctor(int value, int arity)118 	public abstract boolean hasFunctor(int value, int arity);
119 	/**
120 	 * Tests whether this Term's functor has (double) 'name' and 'arity'
121 	 * Returns false if called inappropriately
122 	 *
123 	 * @return whether this Term's functor has (double) 'name' and 'arity'
124 	 */
hasFunctor(double value, int arity)125 	public abstract boolean hasFunctor(double value, int arity);
126 
127 	/**
128 	 * returns, as a String, the name of a Compound, Atom or Variable
129 	 * throws a JPLException from an Integer or Float
130 	 *
131 	 * @return the name of a Compound, Atom or Variable
132 	 */
name()133 	public String name() {
134 		throw new JPLException("jpl." + this.typeName() + ".name() is undefined");
135 	};
136 
137 	/**
138 	 * returns, as an int, the arity of a Compound, Atom, Integer or Float
139 	 * throws a JPLException from a Variable
140 	 *
141 	 * @return the arity of a Compound, Atom, Integer or Float
142 	 */
arity()143 	public int arity() {
144 		throw new JPLException("jpl." + this.typeName() + ".arity() is undefined");
145 	};
146 
147 	/**
148 	 * returns the value (as an int) of an Integer or Float
149 	 * throws a JPLException from a Compound, Atom or Variable
150 	 *
151 	 * @return the value (as an int) of an Integer or Float
152 	 */
intValue()153 	public int intValue() {
154 		throw new JPLException("jpl." + this.typeName() + ".intValue() is undefined");
155 	}
156 	/**
157 	 * returns the value (as a long) of an Integer or Float
158 	 * throws a JPLException from a Compound, Atom or Variable
159 	 *
160 	 * @return the value (as a long) of an Integer or Float
161 	 */
longValue()162 	public long longValue() {
163 		throw new JPLException("jpl." + this.typeName() + ".longValue() is undefined");
164 	}
165 	/**
166 	 * returns the value (as a float) of an Integer or Float
167 	 * throws a JPLException from a Compound, Atom or Variable
168 	 *
169 	 * @return the value (as a float) of an Integer or Float
170 	 */
floatValue()171 	public float floatValue() {
172 		throw new JPLException("jpl." + this.typeName() + ".floatValue() is undefined");
173 	}
174 
175 	/**
176 	 * returns the value (as a double) of an Integer or Float
177 	 * throws a JPLException from any other subclass
178 	 *
179 	 * @return the value (as an double) of an Integer or Float
180 	 */
doubleValue()181 	public double doubleValue() {
182 		throw new JPLException("jpl." + this.typeName() + ".doubleValue() is undefined");
183 	}
184 
185 	//==================================================================/
186 	//  Methods (common)
187 	//==================================================================/
188 
189 	/**
190 	 * returns the type of this term, as one of jpl.fli.Prolog.COMPOUND, .ATOM, .VARIABLE, .INTEGER, .FLOAT etc
191 	 *
192 	 * @return the type of this term, as one of jpl.fli.Prolog.COMPOUND, .ATOM, .VARIABLE, .INTEGER, .FLOAT etc
193 	 */
type()194 	public abstract int type();
195 
196 	/**
197 	 * returns the name of the type of this term, as one of "Compound", "Atom", "Variable", "Integer", "Float" etc
198 	 *
199 	 * @return the name of the type of this term, as one of "Compound", "Atom", "Variable", "Integer", "Float" etc
200 	 */
typeName()201 	public abstract String typeName();
202 
203 	/**
204 	 * whether this Term represents an atom
205 	 *
206 	 * @return whether this Term represents an atom
207 	 */
isAtom()208 	public boolean isAtom() {
209 		return this instanceof Atom;
210 	}
211 
212 	/**
213 	 * whether this Term represents a compound term
214 	 *
215 	 * @return whether this Term represents a compound atom
216 	 */
isCompound()217 	public boolean isCompound() {
218 		return this instanceof Compound;
219 	}
220 
221 	/**
222 	 * whether this Term represents an atom
223 	 *
224 	 * @return whether this Term represents an atom
225 	 */
isFloat()226 	public boolean isFloat() {
227 		return this instanceof Float;
228 	}
229 
230 	/**
231 	 * whether this Term represents an atom
232 	 *
233 	 * @return whether this Term represents an atom
234 	 */
isInteger()235 	public boolean isInteger() {
236 		return this instanceof Integer;
237 	}
238 
239 	/**
240 	 * whether this Term is a variable
241 	 *
242 	 * @return whether this Term is a variable
243 	 */
isVariable()244 	public boolean isVariable() {
245 		return this instanceof Variable;
246 	}
247 
248 	/**
249 	 * whether this Term is a 'jfalse' structure, i.e. @(false)
250 	 *
251 	 * @return whether this Term is a 'jfalse' structure, i.e. @(false)
252 	 */
isJFalse()253 	public boolean isJFalse() {
254 		return false; // overridden in Compound, where it might sometimes be true
255 	}
256 
257 	/**
258 	 * whether this Term is a 'jtrue' structure, i.e. @(true)
259 	 *
260 	 * @return whether this Term is a 'jtrue' structure, i.e. @(true)
261 	 */
isJTrue()262 	public boolean isJTrue() {
263 		return false; // overridden in Compound, where it might sometimes be true
264 	}
265 
266 	/**
267 	 * whether this Term is a 'jnull' structure, i.e. @(null)
268 	 *
269 	 * @return whether this Term is a 'jnull' structure, i.e. @(null)
270 	 */
isJNull()271 	public boolean isJNull() {
272 		return false; // overridden in Compound, where it might sometimes be true
273 	}
274 
275 	/**
276 	 * whether this Term is a 'jvoid' structure, i.e. @(void)
277 	 *
278 	 * @return whether this Term is a 'jvoid' structure, i.e. @(void)
279 	 */
isJVoid()280 	public boolean isJVoid() {
281 		return false; // overridden in Compound, where it might sometimes be true
282 	}
283 
284 	/**
285 	 * whether this Term is a 'jobject' structure, i.e. @(Tag)
286 	 *
287 	 * @return whether this Term is a 'jobject' structure, i.e. @(Tag)
288 	 */
isJObject()289 	public boolean isJObject() {
290 		return false; // overridden in Compound, where it might sometimes be true
291 	}
292 
293 	/**
294 	 * whether this Term is a 'jref' structure, i.e. @(Tag) or @(null)
295 	 *
296 	 * @return whether this Term is a 'jref' structure, i.e. @(Tag) or @(null)
297 	 */
isJRef()298 	public boolean isJRef() {
299 		return false; // overridden in Compound, where it might sometimes be true
300 	}
301 
jrefToObject()302 	public abstract Object jrefToObject();
303 
304 	// objectToJRef(Object)
305 	/**
306 	 * returns a new Term instance which represents the given object
307 	 */
objectToJRef(Object obj)308 	public static Term objectToJRef(Object obj) {
309 		return new Compound( "@", new Term[]{new Atom(Prolog.object_to_tag(obj))});
310 	}
311 
putParams(Term[] ps)312 	public Term putParams(Term[] ps) {
313 		IntHolder next = new IntHolder();
314 		next.value = 0;
315 		Term t2 = this.putParams1(next, ps);
316 		if (next.value != ps.length) {
317 			throw new JPLException("Term.putParams: more actual params than formal");
318 		}
319 		return t2;
320 	}
321 
putParams(Term plist)322 	public Term putParams(Term plist) {
323 		Term[] ps = plist.toTermArray();
324 		return putParams(ps);
325 	}
326 
putParams1(IntHolder next, Term[] ps)327 	protected Term putParams1(IntHolder next, Term[] ps) {
328 		switch (this.type()) {
329 			case Prolog.COMPOUND :
330 				return new Compound(this.name(), putParams2(this.args(), next, ps));
331 			case Prolog.ATOM :
332 				if (this.name().equals("?")) {
333 					if (next.value >= ps.length) {
334 						throw new JPLException("Term.putParams: fewer actual params than formal params");
335 					}
336 					return ps[next.value++];
337 				} // else drop through to default
338 			default :
339 				return this;
340 		}
341 	}
342 
putParams2(Term[] ts, IntHolder next, Term[] ps)343 	static protected Term[] putParams2(Term[] ts, IntHolder next, Term[] ps) {
344 		int n = ts.length;
345 		Term[] ts2 = new Term[n];
346 		for (int i = 0; i < n; i++) {
347 			ts2[i] = ts[i].putParams1(next, ps);
348 		}
349 		return ts2;
350 	}
351 
352 	/**
353 	 * the length of this list, iff it is one, else an exception is thrown
354 	 *
355 	 * @throws JPLException
356 	 * @return the length (as an int) of this list, iff it is one
357 	 */
listLength()358 	public int listLength() {
359 		if (this.hasFunctor(".", 2)) {
360 			return 1 + this.arg(2).listLength();
361 		} else if (this.hasFunctor("[]", 0)) {
362 			return 0;
363 		} else {
364 			throw new JPLException("Term.listLength: term is not a list");
365 		}
366 	}
367 
368 	/** returns an array of terms which are the successive members of this list, if it is a list, else throws an exception
369 	 *
370 	 * @throws JPLException
371 	 * @return an array of terms which are the successive members of this list, if it is a list
372 	 */
toTermArray()373 	public Term[] toTermArray() {
374 		try {
375 			int len = this.listLength();
376 			Term[] ts = new Term[len];
377 			Term t = this;
378 
379 			for (int i = 0; i < len; i++) {
380 				ts[i] = t.arg(1);
381 				t = t.arg(2);
382 			}
383 			return ts;
384 		} catch (JPLException e) {
385 			throw new JPLException("Term.toTermArray: term is not a proper list");
386 		}
387 	}
388 
389 	//==================================================================/
390 	//  Methods (deprecated)
391 	//==================================================================/
392 
393 	/**
394 	 * Returns a debug-friendly representation of a Term
395 	 *
396 	 * @return  a debug-friendly representation of a Term
397 	 * @deprecated
398 	 */
debugString()399 	public abstract String debugString();
400 
401 	/**
402 	 * Returns a debug-friendly representation of a list of Terms
403 	 *
404 	 * @return  a debug-friendly representation of a list of Terms
405 	 * @deprecated
406 	 */
debugString(Term arg[])407 	public static String debugString(Term arg[]) {
408 		String s = "[";
409 
410 		for (int i = 0; i < arg.length; ++i) {
411 			s += arg[i].debugString();
412 			if (i != arg.length - 1) {
413 				s += ", ";
414 			}
415 		}
416 		return s + "]";
417 	}
418 
419 	//==================================================================/
420 	//  Converting JPL Terms to Prolog terms
421 	//
422 	// To convert a Term to a term_t, we need to traverse the Term
423 	// structure and build a corresponding Prolog term_t object.
424 	// There are some issues:
425 	//
426 	// - Prolog term_ts rely on the *consecutive* nature of term_t
427 	//   references.  In particular, to build a compound structure
428 	//   in the Prolog FLI, one must *first* determine the arity of the
429 	//   compound, create a *sequence* of term_t references, and then
430 	//   put atoms, functors, etc. into those term references.  We
431 	//   do this in these methods by first determining the arity of the
432 	//   Compound, and then by "put"-ing a type into a term_t.
433 	//   The "put" method is implemented differently in each of Term's
434 	//   five subclasses.
435 	//
436 	// - What if we are trying to make a term_t from a Term, but the
437 	//   Term has multiple instances of the same Variable?  We want
438 	//   to ensure that _one_ Prolog variable will be created, or else
439 	//   queries will give incorrect answers.  We thus pass a Hashtable
440 	//   (var_table) through these methods.  The table contains term_t
441 	//   instances, keyed on Variable instances.
442 	//==================================================================/
443 
put( term_t term)444 	public void put( term_t term){
445 		put( new Hashtable(), term);
446 	}
447 	/**
448 	 * Cache the reference to the Prolog term_t here.
449 	 *
450 	 * @param   varnames_to_vars  A Map from variable names to JPL Variables.
451 	 * @param   term              A (previously created) term_t which is to be
452 	 *                            put with a Prolog term-type appropriate to the Term type
453 	 *                            (e.g., Atom, Variable, Compound, etc.) on which the method is
454 	 *                            invoked.)
455 	 */
put(Map varnames_to_vars, term_t term)456 	protected abstract void put(Map varnames_to_vars, term_t term);
457 
458 	/**
459 	 * This static method converts an array of Terms to a *consecutive*
460 	 * sequence of term_t objects.  Note that the first term_t object
461 	 * returned is a term_t class (structure); the succeeding term_t
462 	 * objects are consecutive references obtained by incrementing the
463 	 * *value* field of the term_t.
464 	 *
465 	 * @param   varnames_to_vars  Map from variable names to JPL Variables.
466 	 * @param   args              An array of jpl.Term references.
467 	 * @return                    consecutive term_t references (first of which is
468 	 *                            a structure)
469 	 */
putTerms(Map varnames_to_vars, Term[] args)470 	protected static term_t putTerms(Map varnames_to_vars, Term[] args) {
471 
472 		// first create a sequence of term_ts.  The 0th term_t
473 		// will be a jpl.fli.term_t.  Successive Prolog term_t
474 		// references will reside in the Prolog engine, and
475 		// can be obtained by term0.value+i.
476 		//
477 		term_t term0 = Prolog.new_term_refs(args.length);
478 
479 		// for each new term reference, construct a Prolog term
480 		// by putting an appropriate Prolog type into the reference.
481 		//
482 		long ith_term_t = term0.value;
483 		for (int i = 0; i < args.length; ++i, ++ith_term_t) {
484 			term_t term = new term_t();
485 			term.value = ith_term_t;
486 			args[i].put(varnames_to_vars, term); // each subclass defines its own put()
487 		}
488 
489 		return term0;
490 	}
491 
492 	// experiment: for jni_jobject_to_term_byval/2 in jpl.c
putTerm( Object obj, term_t termref)493 	public static void putTerm( Object obj, term_t termref){
494 		if (obj instanceof Term){
495 			((Term)obj).put(termref);
496 		} else {
497 			throw new JPLException("not a Term");
498 		}
499 	}
500 
501 	//==================================================================/
502 	//  Converting Prolog terms to JPL Terms
503 	//
504 	// Converting back from term_ts to Terms is simple, since
505 	// the (simplified) Term representation is canonical (there is only one
506 	// correct structure for any given Prolog term).
507 	//
508 	// One problem concerns variable bindings.  We illustrate
509 	// with several examples.  First, consider the Prolog fact
510 	//
511 	//     p( f(X,X)).
512 	//
513 	// And the query
514 	//
515 	//     ?- p( Y).
516 	//
517 	// A solution should be
518 	//
519 	//     y = f(X,X)
520 	//
521 	// and indeed, if this query is run, the term_t to which Y will
522 	// be unified is a compound, f(X,X).  The problem is, how do
523 	// we know, in converting the term_ts to Terms in the compound f/2
524 	// whether we should create one Variable or two?  This begs the
525 	// question, how do we _identify_ Variables in JPL?  The answer
526 	// to the latter question is, by reference; two Variable (Java)
527 	// references refer to the same variable iff they are, in memory,
528 	// the same Variable object.  That is, they satisfy the Java == relation.
529 	// (Note that this condition is _not_ true of the other Term types.)
530 	//
531 	// Given this design decision, therefore, we should create a
532 	// single Variable instance and a Compound instance whose two arg
533 	// values refer to the same Variable object.  We therefore need to keep
534 	// track, in converting a term_t to a Term (in particular, in
535 	// converting a term_t whose type is variable to a Variable), of
536 	// which Variables have been created.  We do this by using the vars
537 	// Hashtable, which gets passed recursively though the from_term_t
538 	// methods; this table holds the Variable instances that have been
539 	// created, keyed by the unique and internal-to-Prolog string
540 	// representation of the variable (I'm not sure about this...).
541 	//==================================================================/
542 
543 	/**
544 	 * This method calls from_term_t on each term in the n consecutive term_ts.
545 	 * A temporary jpl.term_t "holder" (byref) structure must be created
546 	 * in order to extract type information from the Prolog engine.
547 	 *
548 	 * @param   vars_to_Vars      A Map from Prolog variables to jpl.Variable instances
549 	 * @param   n         The number of consecutive term_ts
550 	 * @param   term0     The 0th term_t (structure); subsequent
551 	 *                    term_ts are not structures.
552 	 * @return            An array of converted Terms
553 	 */
554 	/*
555 	protected static Term[] from_term_ts(Map vars_to_Vars, int n, term_t term0) {
556 
557 		// create an (uninitialised) array of n Term references
558 		Term[] terms = new Term[n];
559 
560 		// for each term_t (from 0...n-1), create a term_t
561 		// (temporary) structure and dispatch the translation
562 		// to a Term to the static from_term_t method of the Term
563 		// class.  This will perform (Prolog) type analysis on the
564 		// term_t and call the appropriate static method to create
565 		// a Term of the right type (e.g., Atom, Variable, List, etc.)
566 		//
567 		long ith_term_t = term0.value;
568 		for (int i = 0; i < n; ++i, ++ith_term_t) {
569 			term_t term = new term_t();
570 			term.value = ith_term_t;
571 
572 			terms[i] = Term.from_term_t(vars_to_Vars, term);
573 		}
574 
575 		return terms;
576 	}
577 	*/
578 
579 	/**
580 	 * We discover the Prolog type of the term, then forward the
581 	 * call to the appropriate subclass
582 	 *
583 	 * @param   vars  A Map from Prolog variables to jpl.Variable instances
584 	 * @param   term  The Prolog term (in a term_t holder) to convert
585 	 * @return        The converted Term subtype instance.
586 	 */
getTerm1(Map vars_to_Vars, term_t term)587 	protected static Term getTerm1(Map vars_to_Vars, term_t term) {
588 		int type = Prolog.term_type(term);
589 
590 		switch (type) {
591 			case Prolog.VARIABLE :
592 				return Variable.getTerm(vars_to_Vars, term);
593 			case Prolog.ATOM :
594 				return Atom.getTerm(vars_to_Vars, term);
595 			case Prolog.STRING :
596 				return Atom.getString(vars_to_Vars, term);
597 			case Prolog.INTEGER :
598 				return Integer.getTerm(vars_to_Vars, term);
599 			case Prolog.FLOAT :
600 				return Float.getTerm(vars_to_Vars, term);
601 			case Prolog.COMPOUND :
602 				return Compound.getTerm(vars_to_Vars, term);
603 			default :
604 				// should never happen...
605 				throw new JPLException("Term.from_term_t: unknown term type=" + type);
606 		}
607 	}
608 
getTerm(Map vars_to_Vars, term_t term)609 	protected static Term getTerm(Map vars_to_Vars, term_t term) {
610 		StringHolder hString;
611 		IntHolder hInt;
612 		Int64Holder hInt64;
613 		// int type = Prolog.term_type(term);
614 		switch (Prolog.term_type(term)) {
615 		case Prolog.VARIABLE:
616 			for (Iterator i = vars_to_Vars.keySet().iterator(); i.hasNext();) {
617 				term_t varX = (term_t) i.next(); // a previously seen Prolog variable
618 				if (Prolog.compare(varX, term) == 0) { // identical Prolog variables?
619 					return (Term) vars_to_Vars.get(varX); // return the associated JPL Variable
620 				}
621 			}
622 			// otherwise, the Prolog variable in term has not been seen before
623 			Variable Var = new Variable(); // allocate a new (sequentially named) Variable to represent it
624 			Var.term_ = term; // this should become redundant...
625 			vars_to_Vars.put(term, Var); // use Hashtable(var,null), but only need set(var)
626 			return Var;
627 		case Prolog.ATOM: // return Atom.getTerm(vars_to_Vars, term);
628 			hString = new StringHolder();
629 			Prolog.get_atom_chars(term, hString); // ignore return val; assume success...
630 			return new Atom(hString.value);
631 		case Prolog.STRING: // return Atom.getString(vars_to_Vars, term);
632 			hString = new StringHolder();
633 			Prolog.get_string_chars(term, hString); // ignore return val; assume success...
634 			return new Atom(hString.value);
635 		case Prolog.INTEGER: // return Integer.getTerm(vars_to_Vars, term);
636 			hInt64 = new Int64Holder();
637 			Prolog.get_integer(term, hInt64); // assume it succeeds...
638 			return new jpl.Integer(hInt64.value);
639 		case Prolog.FLOAT: // return Float.getTerm(vars_to_Vars, term);
640 			DoubleHolder hFloatValue = new DoubleHolder();
641 			Prolog.get_float(term, hFloatValue); // assume it succeeds...
642 			return new jpl.Float(hFloatValue.value);
643 		case Prolog.COMPOUND: // return Compound.getTerm(vars_to_Vars, term);
644 			hString = new StringHolder();
645 			hInt = new IntHolder();
646 			Prolog.get_name_arity(term, hString, hInt); // assume it succeeds
647 			Term args[] = new Term[hInt.value];
648 			// term_t term1 = Prolog.new_term_refs(hArity.value);
649 			for (int i = 1; i <= hInt.value; i++) {
650 				term_t termi = Prolog.new_term_ref();
651 				Prolog.get_arg(i, term, termi);
652 				args[i - 1] = Term.getTerm(vars_to_Vars, termi);
653 			}
654 			return new Compound(hString.value, args);
655 		default:
656 			// should never happen...
657 			throw new JPLException("Term.from_term_t: unknown term type=" + Prolog.term_type(term));
658 		}
659 	}
660 
getTerm( term_t term)661 	protected static Term getTerm( term_t term){
662 		return getTerm( new Hashtable(), term);
663 	}
664 
665 	//==================================================================/
666 	//  Computing Substitutions
667 	//
668 	// Once a solution has been found, the Prolog term_t references
669 	// will have been instantiated and will refer to new terms.  To compute
670 	// a substitution, we traverse the (original) Term structure, looking
671 	// at the term_t reference in the Term.  The only case we really care
672 	// about is if the (original) Term is a Variable; if so, the term_t
673 	// back in the Prolog engine may be instantiated (non Variable parts
674 	// of the original Term cannot change or become uninstantiated).  In
675 	// this case, we can store this term in a Hashtable, keyed by the
676 	// Variable with which the term was unified.
677 	//==================================================================/
678 
679 	//------------------------------------------------------------------/
680 	// getSubst
681 	/**
682 	 * This method computes a substitution from a Term.  The bindings
683 	 * Hashtable stores Terms, keyed by Variables.  Thus, a
684 	 * substitution is as it is in mathematical logic, a sequence
685 	 * of the form \sigma = {t_0/x_0, ..., t_n/x_n}.  Once the
686 	 * substitution is computed, the substitution should satisfy
687 	 *
688 	 *   \sigma T = t
689 	 *
690 	 * where T is the Term from which the substitution is computed,
691 	 * and t is the term_t which results from the Prolog query.<p>
692 	 *
693 	 * A second Hashtable, vars, is required; this table holds
694 	 * the Variables that occur (thus far) in the unified term.
695 	 * The Variable instances in this table are guaranteed to be
696 	 * unique and are keyed on Strings which are Prolog internal
697 	 * representations of the variables.
698 	 *
699 	 * @param   bindings  table holding Term substitutions, keyed on
700 	 * Variables.
701 	 * @param   vars  A Hashtable holding the Variables that occur
702 	 * thus far in the term; keyed by internal (Prolog) string rep.
703 	 */
getSubst(Map varnames_to_Terms, Map vars_to_Vars)704 	protected abstract void getSubst(Map varnames_to_Terms, Map vars_to_Vars);
705 
706 	//------------------------------------------------------------------/
707 	// getSubsts
708 	/**
709 	 * Just calls computeSubstitution for each Term in the array.
710 	 *
711 	 * @param   varnames_to_Terms  a Map from variable names to Terms
712 	 * @param   vars_to_Vars       a Map from Prolog variables to JPL Variables
713 	 * @param   arg                a list of Terms
714 	 */
getSubsts(Map varnames_to_Terms, Map vars_to_Vars, Term[] args)715 	protected static void getSubsts(Map varnames_to_Terms, Map vars_to_Vars, Term[] args) {
716 		for (int i = 0; i < args.length; ++i) {
717 			args[i].getSubst(varnames_to_Terms, vars_to_Vars);
718 		}
719 	}
720 
721 	//------------------------------------------------------------------/
722 	// terms_equals
723 	/**
724 	 * This method is used (by Compound.equals) to determine the Terms in two Term arrays
725 	 * are pairwise equal, where two Terms are equal if they satisfy
726 	 * the equals predicate (defined differently in each Term subclass).
727 	 *
728 	 * @param   t1  an array of Terms
729 	 * @param   t2  another array of Terms
730 	 * @return  true if all of the Terms in the (same-length) arrays are pairwise equal
731 	 */
terms_equals(Term[] t1, Term[] t2)732 	protected static boolean terms_equals(Term[] t1, Term[] t2) {
733 		if (t1.length != t2.length) {
734 			return false;
735 		}
736 
737 		for (int i = 0; i < t1.length; ++i) {
738 			if (!t1[i].equals(t2[i])) {
739 				return false;
740 			}
741 		}
742 		return true;
743 	}
744 
745 	//------------------------------------------------------------------/
746 	// toString
747 	/**
748 	 * Converts a list of Terms to a String.
749 	 *
750 	 * @param   args    An array of Terms to convert
751 	 * @return  String representation of a list of Terms
752 	 */
toString(Term[] args)753 	public static String toString(Term[] args) {
754 		String s = "";
755 
756 		for (int i = 0; i < args.length; ++i) {
757 			s += args[i].toString();
758 			if (i != args.length - 1) {
759 				s += ", ";
760 			}
761 		}
762 
763 		return s;
764 	}
765 
766 }
767 
768 //345678901234567890123456789012346578901234567890123456789012345678901234567890
769