View Javadoc

1   /*
2    * #%L
3    * prolobjectlink-jpi-jtrolog
4    * %%
5    * Copyright (C) 2012 - 2018 WorkLogic Project
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as
9    * published by the Free Software Foundation, either version 2.1 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 Lesser Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Lesser Public
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/lgpl-2.1.html>.
20   * #L%
21   */
22  /*
23   * tuProlog - Copyright (C) 2001-2002  aliCE team at deis.unibo.it
24   *
25   * This library is free software; you can redistribute it and/or
26   * modify it under the terms of the GNU Lesser General Public
27   * License as published by the Free Software Foundation; either
28   * version 2.1 of the License, or (at your option) any later version.
29   *
30   * This library is distributed in the hope that it will be useful,
31   * but WITHOUT ANY WARRANTY; without even the implied warranty of
32   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
33   * Lesser General Public License for more details.
34   *
35   * You should have received a copy of the GNU Lesser General Public
36   * License along with this library; if not, write to the Free Software
37   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
38   */
39  package jTrolog.engine;
40  
41  import jTrolog.errors.*;
42  import jTrolog.lib.BasicLibrary;
43  import jTrolog.lib.Library;
44  import jTrolog.lib.BuiltIn;
45  import jTrolog.parser.Parser;
46  import jTrolog.terms.*;
47  import jTrolog.terms.Number;
48  
49  import java.io.Serializable;
50  import java.io.BufferedReader;
51  import java.io.InputStreamReader;
52  import java.io.PrintStream;
53  import java.io.IOException;
54  import java.util.*;
55  import java.lang.reflect.InvocationTargetException;
56  import java.lang.reflect.Method;
57  
58  /**
59   * The class representing a jTrolog Prolog machine.
60   */
61  @SuppressWarnings({ "rawtypes", "unchecked","serial", "unused" })
62  public class Prolog implements Serializable {
63  
64  	public static final String VERSION = "2.1";// jTrolog version
65  	public static final int OP_LOW = 1;
66  	public static final int OP_HIGH = 1200;
67  
68  	private LibraryAndTheoryManager libraryAndTheoryManager; // Rule and Fact
69  																// Database
70  																// Manager
71  	private OperatorTable opTable; // Table mapping operators
72  
73  	private PrintStream currentPS = System.out; // the current printstream
74  	private Struct currentQuery; // the last query
75  	private Engine currentEngine; // the last initialized engine
76  	public static final Prolog defaultMachine = new DefaultProlog();
77  	public static final int FX = 0;
78  	public static final int FY = 1;
79  	public static final int XFX = 2;
80  	public static final int XFY = 3;
81  	public static final int YFX = 4;
82  	public static final int XF = 5;
83  	public static final int YF = 6;
84  
85  	/**
86  	 * Builds a prolog engine with default libraries: BasicLibrary, ISOLibrary,
87  	 * IOLibrary
88  	 */
89  	public Prolog() {
90  		this(new String[] { "jTrolog.lib.BasicLibrary", "jTrolog.lib.ISOLibrary", "jTrolog.lib.IOLibrary" });
91  	}
92  
93  	/**
94  	 * Builds a prolog engine with default libraries: BasicLibrary, ISOLibrary,
95  	 * IOLibrary + the extraLib
96  	 * 
97  	 * @param extraLib
98  	 *            additional library to be loaded
99  	 */
100 	public Prolog(String extraLib) {
101 		this();
102 		try {
103 			loadLibrary(extraLib);
104 		} catch (InvalidLibraryException e) {
105 			warn(e.toString());
106 		}
107 	}
108 
109 	/**
110 	 * Builds a prolog engine using _only_ the specified libraries as parameters
111 	 * 
112 	 * @param libNames
113 	 *            the (class) names of the libraries to be loaded
114 	 */
115 	public Prolog(String[] libNames) {
116 		opTable = new OperatorTable();
117 		libraryAndTheoryManager = new LibraryAndTheoryManager(this);
118 		try {
119 			loadLibrary(new BuiltIn(this));
120 		} catch (InvalidLibraryException e) {
121 			warn(e.toString());
122 		}
123 
124 		if (libNames == null)
125 			return;
126 		for (int i = 0; i < libNames.length; i++) {
127 			try {
128 				loadLibrary(libNames[i]);
129 			} catch (InvalidLibraryException e) {
130 				warn(e.toString());
131 			}
132 		}
133 	}
134 
135 	/**
136 	 * Starts a command line interface with jTrolog Prolog engine. Builtin-,
137 	 * Basic-, ISO- and IO-Libraries are loaded. In the future, args could be
138 	 * made to look for URLs with theories?
139 	 */
140 	public static void main(String[] args) throws IOException {
141 		Prolog vm = new Prolog();
142 		System.out.println("jTrolog - Java Trondheim Prolog - v.2.1");
143 		System.out.print("?- ");
144 		BufferedReader consoleIn = new BufferedReader(new InputStreamReader(System.in));
145 		String input = "";
146 		String tmp;
147 		while ((tmp = consoleIn.readLine()) != null) {
148 			input += tmp;
149 			try {
150 				if (input.trim().endsWith(".")) {
151 					Solution x = vm.solve(input);
152 					System.out.println("\nresult: " + x);
153 					System.out.println(x.bindingsToString());
154 				} else if (input.trim().length() == 0) {
155 					Solution x = vm.solveNext();
156 					System.out.println("\nresult: " + x);
157 					System.out.println(x.bindingsToString());
158 				}
159 			} catch (Throwable throwable) {
160 				System.out.println("Prolog error (but don't be alarmed):\n" + throwable.getMessage());
161 			} finally {
162 				input = "";
163 				System.out.print("?- ");
164 			}
165 		}
166 	}
167 
168 	/*******************************************************************************************************************
169 	 * The Rule and Fact Database System The Library System
170 	 */
171 	public void assertZ(Clause toBeAsserted) throws PrologException {
172 		String index = toBeAsserted.head.predicateIndicator;
173 		if (staticDBContainsPredicate(index))
174 			throw new PrologException("permission_error(modify, static_procedure, " + index + ")");
175 		libraryAndTheoryManager.assertZ(toBeAsserted, index);
176 	}
177 
178 	public void assertA(Clause toBeAsserted) throws PrologException {
179 		String index = toBeAsserted.head.predicateIndicator;
180 		if (staticDBContainsPredicate(index))
181 			throw new PrologException("permission_error(modify, static_procedure, " + index + ")");
182 		libraryAndTheoryManager.assertA(toBeAsserted, index);
183 	}
184 
185 	public Struct retract(Struct sarg0) throws PrologException {
186 		String index = ((Struct) sarg0.getArg(0)).predicateIndicator;
187 		if (staticDBContainsPredicate(index))
188 			throw new PrologException("permission_error(modify, static_procedure, " + index + ")");
189 		Struct struct = libraryAndTheoryManager.retract(sarg0, index);
190 		if (struct != null)
191 			warn("DELETE: " + struct + "\n");
192 		return struct;
193 	}
194 
195 	private boolean staticDBContainsPredicate(String key) {
196 		return libraryAndTheoryManager.isLibraryRule(key) || hasPrimitive(key);
197 	}
198 
199 	public void abolish(String predicateIndicator) throws PrologException {
200 		if (staticDBContainsPredicate(predicateIndicator))
201 			throw new PrologException("permission_error(modify, static_procedure, " + predicateIndicator + ")");
202 		List l = libraryAndTheoryManager.abolish(predicateIndicator);
203 	}
204 
205 	public void setDynamicPredicateIndicator(String predicateIndicator) throws PrologException {
206 		if (staticDBContainsPredicate(predicateIndicator))
207 			throw new PrologException("permission_error(modify, static_procedure, " + predicateIndicator + ")");
208 		libraryAndTheoryManager.setDynamic(predicateIndicator);
209 	}
210 
211 	public List find(String predIndicator) throws PrologException {
212 		List rulesFromDatabase = libraryAndTheoryManager.find(predIndicator);
213 		if (rulesFromDatabase == null)
214 			throw new PrologException("The predicate " + predIndicator + " is unknown.");
215 		return rulesFromDatabase;
216 	}
217 
218 	public Iterator dynamicPredicateIndicators() {
219 		return libraryAndTheoryManager.dynamicPredicateIndicators();
220 	}
221 
222 	/**
223 	 * clears all dynamic predicates and sets the new newTheory
224 	 * 
225 	 * @deprecated use clearTheory() + addTheory(newTheory) instead
226 	 * @param newTheory
227 	 *            to be set
228 	 * @throws PrologException
229 	 *             if newTheory is not valid
230 	 */
231 	public void setTheory(String newTheory) throws PrologException {
232 		libraryAndTheoryManager.clear();
233 		addTheory(newTheory);
234 	}
235 
236 	/**
237 	 * removes all dynamic predicates
238 	 */
239 	public void clearTheory() {
240 		libraryAndTheoryManager.clear();
241 	}
242 
243 	/**
244 	 * @param theory
245 	 *            to be added to the existing set of theories in the database.
246 	 * @throws PrologException
247 	 *             if the new theory is not valid
248 	 */
249 	public void addTheory(String theory) throws PrologException {
250 		libraryAndTheoryManager.consult(theory);
251 	}
252 
253 	/**
254 	 * @return the current theory in the Prolog machine (only dynamic)
255 	 */
256 	public String getTheory() {
257 		return libraryAndTheoryManager.getTheory(true);
258 	}
259 
260 	/**
261 	 * @return the last theory to be consulted or attempted consulted as text
262 	 */
263 	public String getLastConsultedTheory() {
264 		return libraryAndTheoryManager.getLastConsultedTheory();
265 	}
266 
267 	/**
268 	 * Loads a library. If a library with the same name is already present, a
269 	 * warning event is notified. Then, the current instance of that library is
270 	 * discarded, and the new instance gets loaded.
271 	 * 
272 	 * @param className
273 	 *            of the Java class containing the Library to be loaded
274 	 * @return the reference to the Library just loaded
275 	 * @throws InvalidLibraryException
276 	 *             if name is not a valid library
277 	 */
278 	public synchronized Library loadLibrary(String className) throws InvalidLibraryException {
279 		try {
280 			return loadLibrary((Library) Class.forName(className).newInstance());
281 		} catch (Exception e) {
282 			throw new InvalidLibraryException(className);
283 		}
284 	}
285 
286 	/**
287 	 * Loads a specific instance of a library If a library of the same class is
288 	 * already present, a warning event is notified. Then, the current instance
289 	 * of that library is discarded, and the new instance gets loaded.
290 	 * 
291 	 * @param lib
292 	 *            the (Java class) name of the library to be loaded
293 	 * @throws InvalidLibraryException
294 	 *             if name is not a valid library
295 	 */
296 	public Library loadLibrary(Library lib) throws InvalidLibraryException {
297 		String name = lib.getName();
298 		if (getLibrary(name) != null)
299 			throw new InvalidLibraryException("library " + name + " already loaded.");
300 
301 		lib.setEngine(this);
302 
303 		addPrimitives(lib);
304 		try {
305 			return libraryAndTheoryManager.consultLib(lib);
306 		} catch (PrologException e) {
307 			throw new InvalidLibraryException(lib.getName(), e);
308 		}
309 	}
310 
311 	/**
312 	 * @param name
313 	 *            of the library to be unloaded
314 	 * @throws InvalidLibraryException
315 	 *             if no loaded Library has the given name
316 	 */
317 	public void unloadLibrary(String name) throws InvalidLibraryException {
318 		Library library = getLibrary(name);
319 		if (library == null)
320 			throw new InvalidLibraryException(name);
321 		Library unloaded = libraryAndTheoryManager.unconsultLib(library);
322 		removePrimitives(unloaded);
323 	}
324 
325 	public Library getLibrary(String name) {
326 		return libraryAndTheoryManager.getLibrary(name);
327 	}
328 
329 	/**
330 	 * @return the names of the libraries currently loaded
331 	 */
332 	public Iterator getCurrentLibraries() {
333 		return libraryAndTheoryManager.getCurrentLibraries();
334 	}
335 
336 	/**
337 	 * @param ps
338 	 *            the engine printstream.
339 	 */
340 	public void setPrintStream(PrintStream ps) {
341 		currentPS = ps;
342 	}
343 
344 	/**
345 	 * @return the engine printstream.
346 	 */
347 	public PrintStream getPrintStream() {
348 		return currentPS;
349 	}
350 
351 	/*******************************************************************************************************************
352 	 * The Operator System
353 	 */
354 
355 	/**
356 	 * Gets the list of the operators currently defined
357 	 * 
358 	 * @return the list of the operators
359 	 */
360 	public synchronized Iterator getCurrentOperators() {
361 		return opTable.getAllOperators();
362 	}
363 
364 	public int getOperatorPriority(String name, int type) {
365 		return opTable.getOperatorPriority(name, type);
366 	}
367 
368 	public void opNew(String name, int type, int i) {
369 		opTable.addOperator(name, type, i);
370 	}
371 
372 	public void opNew(String name, String type, int i) {
373 		if (type.equalsIgnoreCase("fx"))
374 			opTable.addOperator(name, FX, i);
375 		if (type.equalsIgnoreCase("fy"))
376 			opTable.addOperator(name, FY, i);
377 		if (type.equalsIgnoreCase("xfx"))
378 			opTable.addOperator(name, XFX, i);
379 		if (type.equalsIgnoreCase("xfy"))
380 			opTable.addOperator(name, XFY, i);
381 		if (type.equalsIgnoreCase("yfx"))
382 			opTable.addOperator(name, YFX, i);
383 		if (type.equalsIgnoreCase("yf"))
384 			opTable.addOperator(name, YF, i);
385 		if (type.equalsIgnoreCase("xf"))
386 			opTable.addOperator(name, XF, i);
387 
388 	}
389 
390 	/*******************************************************************************************************************
391 	 * To solve, that is the problem
392 	 */
393 
394 	/**
395 	 * Solves a query
396 	 * 
397 	 * @param st
398 	 *            the string representing the goal to be demonstrated
399 	 * @return the result of the demonstration
400 	 * @see SolutionManager
401 	 */
402 	public synchronized Solution solve(String st) throws Throwable {
403 		Parser p = new Parser(st, this);
404 		Term t = p.nextTerm(true);
405 		if (!(t instanceof Struct)) // Var or Number is considered true, since
406 									// they are not false?
407 			return new Solution(t);
408 
409 		try {
410 			Struct g = (Struct) t;
411 			if (getPrimitiveExp(g) != null)
412 				return new Solution(new BindingsTable().evalExpression(this, g));
413 			return solve(g);
414 		} catch (InvocationTargetException e) {
415 			Throwable cause = e;
416 			while (cause instanceof InvocationTargetException)
417 				cause = cause.getCause();
418 			throw cause;
419 		}
420 	}
421 
422 	/**
423 	 * Solves a query
424 	 * 
425 	 * @param g
426 	 *            the term representing the goal to be demonstrated
427 	 * @return the result of the demonstration
428 	 * @see SolutionManager
429 	 */
430 	public synchronized Solution solve(Struct g) throws Throwable {
431 		onSolveBegin(g);
432 		currentQuery = (Struct) BuiltIn.convertTermToClauseBody(g);
433 		currentEngine = new Engine(this, BuiltIn.convertTermToClauseBody2(currentQuery));
434 		BindingsTable result = currentEngine.runFirst();
435 		onSolveEnd();
436 		return SolutionManager.prepareSolution(currentQuery, result);
437 	}
438 
439 	/**
440 	 * Gets next solution
441 	 * 
442 	 * @return the result of the demonstration
443 	 * @throws NoMorePrologSolutions
444 	 *             if no more solutions are present
445 	 * @see SolutionManager
446 	 */
447 	public synchronized Solution solveNext() throws Throwable {
448 		if (currentEngine == null || !currentEngine.hasAlternatives())
449 			throw new NoMorePrologSolutions();
450 		BindingsTable result = currentEngine.run(Engine.BACK);
451 		onSolveEnd();
452 		return SolutionManager.prepareSolution(currentQuery, result);
453 	}
454 
455 	public synchronized void onSolveBegin(Term g) {
456 		for (Iterator it = getCurrentLibraries(); it.hasNext();)
457 			((Library) it.next()).onSolveBegin(g);
458 	}
459 
460 	public synchronized void onSolveEnd() {
461 		for (Iterator it = getCurrentLibraries(); it.hasNext();)
462 			((Library) it.next()).onSolveEnd();
463 	}
464 
465 	/**
466 	 * Asks for the presence of open alternatives to be explored in current
467 	 * demostration process.
468 	 * 
469 	 * @return true if open alternatives are present
470 	 */
471 	public synchronized boolean hasOpenAlternatives() throws Throwable {
472 		return currentEngine.hasAlternatives();
473 	}
474 
475 	/**
476 	 * Matches the structure of the two original terms. OBS: Variables in the
477 	 * original terms are not resolved. If this is desired, then the terms
478 	 * passed in should be clonedAndResolved first.
479 	 * 
480 	 * OBS2: no unification of variables is made.
481 	 * 
482 	 * @param t0
483 	 *            first term to be matched
484 	 * @param t1
485 	 *            second term to be matched
486 	 * @return true if the structure of the two terms match
487 	 */
488 	public static synchronized boolean match(Term t0, Term t1) {
489 		if (t0 instanceof Var || t1 instanceof Var)
490 			return true;
491 
492 		if (t0 instanceof Number && t1 instanceof jTrolog.terms.Number)
493 			return BasicLibrary.number_equality_2((Number) t0, (Number) t1);
494 
495 		if (t0 instanceof StructAtom && t1 instanceof StructAtom)
496 			return t0.equals(t1);
497 
498 		if (t0 instanceof Struct && t1 instanceof Struct) {
499 			Struct s0 = (Struct) t0;
500 			Struct s1 = (Struct) t1;
501 			if (s0.arity != s1.arity)
502 				return false;
503 			for (int i = 0; i < s1.arity; i++) {
504 				if (!match(s0.getArg(i), s1.getArg(i)))
505 					return false;
506 			}
507 			return true;
508 		}
509 		return false;
510 	}
511 
512 	/*******************************************************************************************************************
513 	 * The Primitives system
514 	 */
515 	private HashMap directives = new HashMap();
516 	private HashMap expressions = new HashMap();
517 
518 	public boolean hasPrimitive(String predicateIndiciator) {
519 		return directives.containsKey(predicateIndiciator) || expressions.containsKey(predicateIndiciator);
520 	}
521 
522 	public boolean hasPrimitiveExp(String predicateIndiciator) {
523 		return expressions.containsKey(predicateIndiciator);
524 	}
525 
526 	/**
527 	 * @return the primitive matching the predicate indicator signature of the
528 	 *         struct passed as argument.
529 	 */
530 	final PrimitiveInfo getPrimitive(Struct struct) {
531 		return (PrimitiveInfo) directives.get(struct.predicateIndicator);
532 	}
533 
534 	final PrimitiveInfo getPrimitiveExp(Struct struct) {
535 		return (PrimitiveInfo) expressions.get(struct.predicateIndicator);
536 	}
537 
538 	private void addPrimitives(Library src) {
539 		List prims = getPrimitives(src);
540 		for (int i = 0; i < prims.size(); i++) {
541 			PrimitiveInfo p = (PrimitiveInfo) prims.get(i);
542 			if (p.method.getReturnType() == Term.class)
543 				expressions.put(p.key, p);
544 			else
545 				directives.put(p.key, p);
546 		}
547 	}
548 
549 	private void removePrimitives(Library src) {
550 		List prims = getPrimitives(src);
551 		for (int i = 0; i < prims.size(); i++) {
552 			PrimitiveInfo p = (PrimitiveInfo) prims.get(i);
553 			directives.remove(p.key);
554 			expressions.remove(p.key);
555 		}
556 	}
557 
558 	/**
559 	 * 1. Every method in the library that matches the following criteria: -
560 	 * return type either boolean, void or Term, - the first parameter is
561 	 * BindingsTable, and - the method name ends with "_N" where N = number of
562 	 * parameters - 1. are added as Primitives to the Prolog machine that loads
563 	 * the Library.
564 	 * 
565 	 * 2. For each primitive added, the getSynomym(name) is also queried. For
566 	 * each synonyms, a copy of the primitive is added to the machine using the
567 	 * synonym name.
568 	 * 
569 	 * @param library
570 	 *            that is scanned for primitives
571 	 * @return a list of PrimitiveInfo
572 	 */
573 	private static List getPrimitives(Library library) {
574 		ArrayList result = new ArrayList();
575 
576 		Method[] mlist = library.getClass().getMethods();
577 		methodLoop: for (int i = 0; i < mlist.length; i++) {
578 			Method m = mlist[i];
579 			String mName = m.getName();
580 			Class[] params = m.getParameterTypes();
581 
582 			Class retType = m.getReturnType();
583 			if (!(retType == boolean.class || retType == Term.class || retType == void.class))
584 				continue;
585 
586 			int index = mName.lastIndexOf('_');
587 			if (index == -1)
588 				continue;
589 
590 			// retrieve and check arg number
591 			int arity = Integer.parseInt(mName.substring(index + 1, mName.length()));
592 			if (params.length - 1 != arity)
593 				continue;
594 
595 			for (int j = 1; j < arity; j++) {
596 				if (!Term.class.isAssignableFrom(params[j]))
597 					continue methodLoop;
598 			}
599 
600 			String rawName = mName.substring(0, index);
601 			result.add(new PrimitiveInfo(library, m, rawName, arity));
602 
603 			// adding synonyms
604 			String[] synonyms = library.getSynonym(rawName);
605 			if (synonyms != null) {
606 				for (int j = 0; j < synonyms.length; j++)
607 					result.add(new PrimitiveInfo(library, m, synonyms[j], arity));
608 			}
609 		}
610 		return result;
611 	}
612 
613 	/*******************************************************************************************************************
614 	 * The Warning system
615 	 */
616 	List warnings = new LinkedList();
617 
618 	public synchronized void resetWarningList() {
619 		warnings.clear();
620 	}
621 
622 	public List getAndResetWarnings() {
623 		List tmp = warnings;
624 		warnings = new LinkedList();
625 		return tmp;
626 	}
627 
628 	/**
629 	 * @param m
630 	 *            adds the warning message to the warnings list
631 	 */
632 	public void warn(String m) {
633 		warnings.add(m);
634 	}
635 
636 	/*******************************************************************************************************************
637 	 * The Flag system
638 	 */
639 	private HashMap flags = new HashMap();
640 
641 	public void defineFlag(String name, Struct valueList, Term defValue, boolean modifiable) {
642 		flags.put(name, new Flag(name, valueList, defValue, modifiable));
643 	}
644 
645 	public Flag getFlag(String name) {
646 		return (Flag) flags.get(name);
647 	}
648 
649 	public Term getFlagValue(String name) {
650 		Flag flag = (Flag) flags.get(name);
651 		return flag == null ? null : flag.getValue();
652 	}
653 
654 	public Term getPrologFlagList() {
655 		Struct flist = Term.emptyList;
656 		for (Iterator it = flags.values().iterator(); it.hasNext();) {
657 			Flag fl = (Flag) it.next();
658 			Term at0 = new Struct("flag", new Term[] { new StructAtom(fl.getName()), fl.getValue() });
659 			flist = new Struct(".", new Term[] { at0, flist });
660 		}
661 		return flist;
662 	}
663 
664 	public static boolean evalPrimitive(PrimitiveInfo prim, Object[] primitive_args) throws Throwable {
665 		Method method = prim.method;
666 		try {
667 			if (method.getReturnType() == void.class) {
668 				method.invoke(prim.source, primitive_args);
669 				return true;
670 			}
671 			return ((Boolean) method.invoke(prim.source, primitive_args)).booleanValue();
672 		} catch (IllegalArgumentException e) {
673 			Class[] expectedArgs = method.getParameterTypes();
674 			for (int i = 1; i < primitive_args.length; i++) {
675 				Term actual = (Term) primitive_args[i];
676 				Class expectedClass = expectedArgs[i];
677 				if (expectedClass.isAssignableFrom(actual.getClass()))
678 					continue; // nothing wrong with this one, check next param
679 				if (actual instanceof Var)
680 					throw new PrologException("instantiation_error"); // expected
681 																		// anything
682 																		// but a
683 																		// Var
684 				String expected = expectedClass.getName();
685 				expected = expected.substring(expected.lastIndexOf('.') + 1);
686 				throw new PrologException("type_error(" + expected + ", " + actual + ")");
687 			}
688 			throw new PrologException("WTF: Bug in system.");
689 		}
690 	}
691 }