aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShawn O. Pearce <sop@google.com>2011-06-02 09:28:18 -0700
committerShawn O. Pearce <sop@google.com>2011-06-03 10:12:35 -0700
commitf39dc418888a620115b2e2d3773b93623ba7b495 (patch)
tree9da300b20d11e7e531c7587db5f7c4d9e8419688
parentfba28633841da9ede153a35da8db9a384bd06883 (diff)
downloadprolog-cafe-f39dc418888a620115b2e2d3773b93623ba7b495.tar.gz
Rewrite Compiler command line handling
Enable performing more than one translation at a time, and support passing in the path for the output directory.
-rw-r--r--Makefile2
-rw-r--r--src/compiler/CompileException.java16
-rw-r--r--src/compiler/Compiler.java424
-rw-r--r--src/compiler/Makefile2
-rw-r--r--src/lang/BufferingPrologControl.java148
5 files changed, 406 insertions, 186 deletions
diff --git a/Makefile b/Makefile
index 909acb7..cb2e57b 100644
--- a/Makefile
+++ b/Makefile
@@ -51,7 +51,7 @@ compiler:
plcafe:
$(JAVAC) $(JAVACOPTS) src/lang/*.java src/builtin/*.java \
target/generated-sources/prologcafe-builtin/com/googlecode/prolog_cafe/builtin/*.java \
- src/compiler/pl2am/*.java src/compiler/am2j/*.java src/compiler/Compiler.java
+ src/compiler/pl2am/*.java src/compiler/am2j/*.java src/compiler/*.java
$(JAR) $(JAROPTS) plcafe.jar com/googlecode/prolog_cafe
plj:
diff --git a/src/compiler/CompileException.java b/src/compiler/CompileException.java
new file mode 100644
index 0000000..f9d515c
--- /dev/null
+++ b/src/compiler/CompileException.java
@@ -0,0 +1,16 @@
+package com.googlecode.prolog_cafe.compiler;
+
+/** Indicates compiling did not succeed. */
+public class CompileException extends Exception {
+ public CompileException(String message) {
+ super(message);
+ }
+
+ public CompileException(Throwable cause) {
+ super(cause.getMessage(), cause);
+ }
+
+ public CompileException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/compiler/Compiler.java b/src/compiler/Compiler.java
index c0d94a6..bfd3b9d 100644
--- a/src/compiler/Compiler.java
+++ b/src/compiler/Compiler.java
@@ -1,8 +1,19 @@
package com.googlecode.prolog_cafe.compiler;
-import com.googlecode.prolog_cafe.lang.*;
+import com.googlecode.prolog_cafe.lang.BufferingPrologControl;
+import com.googlecode.prolog_cafe.lang.ListTerm;
+import com.googlecode.prolog_cafe.lang.Prolog;
+import com.googlecode.prolog_cafe.lang.PrologException;
+import com.googlecode.prolog_cafe.lang.SymbolTerm;
+import com.googlecode.prolog_cafe.lang.Term;
+
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.LinkedList;
/**
- * The <code>Compiler</code> class provides methods for
+ * The <code>Compiler</code> class provides methods for
* translating Prolog programs into Java programs.
*
* The <code>Compiler</code> class supports the following compiler options.
@@ -22,7 +33,7 @@ import java.io.File;
* <ul>
* <li>From Command line<br>
* <pre>
- * % java -cp $PLCAFEDIR/plcafe.jar com.googlecode.prolog_cafe.compiler.Compiler:$CLASSPATH $PLCAFEDIR/examples/prolog/list.pl
+ * % java -cp $PLCAFEDIR/plcafe.jar com.googlecode.prolog_cafe.compiler.Compiler:$CLASSPATH $PLCAFEDIR/examples/prolog/list.pl
* Prolog Cafe X.X.X (YYY)
* Copyright(C) 1997-200X M.Banbara and N.Tamura
* % ls
@@ -40,20 +51,20 @@ import java.io.File;
* </pre>
* <pre>
* % javac -classpath $PLCAFEDIR/plcafe.jar:$CLASSPATH T.java
- * % java -classpath $PLCAFEDIR/plcafe.jar:$CLASSPATH T $PLCAFEDIR/examples/prolog/list.pl
+ * % java -classpath $PLCAFEDIR/plcafe.jar:$CLASSPATH T $PLCAFEDIR/examples/prolog/list.pl
* % ls
* PRED_append_3.java PRED_nrev_2.java PRED_range_3.java
* </pre>
* </ul>
*
- * It is noted that
+ * It is noted that
* our Prolog-to-Java translator is originally witten in Prolog, and then bootstrapped.
* Please see the following two Prolog programs in details.
* <ul>
* <li><code>$PLCAFEDIR/src/compiler/pl2am.pl</code><br>
* Translates a Prolog program into a WAM-based intermediate code.
* <li><code>$PLCAFEDIR/src/compiler/am2j.pl</code><br>
- * Translates a WAM-based intermediate code generated by <code>pl2am.pl</code>
+ * Translates a WAM-based intermediate code generated by <code>pl2am.pl</code>
* into Java programs.
* </ul>
*
@@ -62,216 +73,261 @@ import java.io.File;
* @version 1.2
*/
public class Compiler {
- /** Version information */
- public static String VERSION = "Prolog Cafe 1.2.5 (mantis)";
- /** Copyright information */
- public static String COPYRIGHT = "Copyright(C) 1997-2009 M.Banbara and N.Tamura";
-
- /** Compiler option for eliminating disjunctions. Its initial value is <code>true</code> */
- protected boolean eliminateDisjunctions = true;
- /** Compiler option for arithmetic compilation. Its initial value is <code>true</code> */
- protected boolean arithmeticCompilation = true;
- /** Compiler option for inline expansion. Its initial value is <code>true</code> */
- protected boolean inlineExpansion = true;
- /** Compiler option for optimising recursive call. Its initial value is <code>true</code> */
- protected boolean optimiseRecursiveCall = true;
- /** Compiler option for second-level indexing. Its initial value is <code>true</code> */
- protected boolean switchOnHash = true;
- /** Non-standard option. Compiler option for closure generation. Its initial value is <code>false</code> */
- protected boolean generateClosure = false;
-
- /**
- * Translates a Prolog program into a WAM-based intermediate code.
+ public static enum Option {
+ eliminateDisjunctions("ed", true),
+ arithmeticCompilation("ac", true),
+ inlineExpansion("ie", true),
+ optimiseRecursiveCall("rc", true),
+ switchOnHash("idx", true),
+ generateClosure("clo", false);
+
+ final SymbolTerm symbol;
+ final boolean onByDefault;
+
+ Option(String symbol, boolean onByDefault) {
+ this.symbol = SymbolTerm.makeSymbol(symbol);
+ this.onByDefault = onByDefault;
+ }
+ }
+
+ /** Prolog context running the compiler/translater tools. */
+ private BufferingPrologControl pcl;
+ private EnumSet<Option> options;
+
+ /** Initialize a new compiler instance. */
+ public Compiler() {
+ pcl = new BufferingPrologControl();
+ options = EnumSet.noneOf(Option.class);
+ enableDefaultOptions();
+ }
+
+ /**
+ * Translates a Prolog program into a WAM-based intermediate code.
*
* @param _prolog an input Prolog file
- * @param _wam an output file for WAM-based intermediate code.
- * @return <code>true</code> if succeeds, otherwise <code>false</code>.
+ * @param _wam an output file for WAM-based intermediate code.
*/
- public boolean prologToWAM(String _prolog, String _wam) {
- try {
- if (! fileExists(_prolog)) {
- System.out.println("**ERROR: file " + _prolog + " does not exist");
- return false;
- }
- if (fileExists(_wam)) {
- System.out.println("**ERROR: file " + _wam + " already exists");
- return false;
- }
+ public void prologToWAM(String _prolog, String _wam) throws CompileException {
+ if (! fileExists(_prolog))
+ throw new CompileException(new FileNotFoundException(_prolog));
+
// Create arguments
Term prolog = SymbolTerm.makeSymbol(_prolog);
Term wam = SymbolTerm.makeSymbol(_wam);
Term op = Prolog.Nil;
- if (eliminateDisjunctions)
- op = new ListTerm(SymbolTerm.makeSymbol("ed"), op);
- if (arithmeticCompilation)
- op = new ListTerm(SymbolTerm.makeSymbol("ac"), op);
- if (inlineExpansion)
- op = new ListTerm(SymbolTerm.makeSymbol("ie"), op);
- if (optimiseRecursiveCall)
- op = new ListTerm(SymbolTerm.makeSymbol("rc"), op);
- if (switchOnHash)
- op = new ListTerm(SymbolTerm.makeSymbol("idx"), op);
- if (generateClosure)
- op = new ListTerm(SymbolTerm.makeSymbol("clo"), op);
- BlockingPrologControl ctl = new BlockingPrologControl();
- return ctl.execute("com.googlecode.prolog_cafe.compiler.pl2am", "pl2am",
- new ListTerm(prolog, new ListTerm(wam, new ListTerm(op, Prolog.Nil))));
- } catch (Exception e){
- e.printStackTrace();
- }
- return false;
+ for (Option opt : options)
+ op = new ListTerm(opt.symbol, op);
+
+ ListTerm args = new ListTerm(prolog, new ListTerm(wam, new ListTerm(op, Prolog.Nil)));
+ try {
+ if (!pcl.execute("com.googlecode.prolog_cafe.compiler.pl2am", "pl2am", args))
+ throw new CompileException("Unknown Error");
+ } catch (PrologException err) {
+ throw new CompileException("Error compiling "+_prolog, err);
+ }
}
- /**
- * Translates WAM-based intermediate code into Java programs.
+ /**
+ * Translates WAM-based intermediate code into Java source.
*
- * @param _wam an input file for WAM-based intermediate code.
- * @param _dir a destination directory for java files. The directory must already exist.
- * @return <code>true</code> if succeeds, otherwise <code>false</code>.
+ * @param _wam an input file for WAM-based intermediate code.
+ * @param _dir a destination directory for java files.
* @see #prologToWAM(String, String)
*/
- public boolean wamToJava(String _wam, String _dir) {
- try {
- if (! fileExists(_wam)) {
- System.out.println("**ERROR: file " + _wam + " does not exist");
- return false;
- }
- if (! fileExists(_dir)) {
- System.out.println("**ERROR: directory " + _dir + " does not exist");
- return false;
- }
+ public void wamToJavaSource(String _wam, String _dir) throws CompileException {
+ if (! fileExists(_wam))
+ throw new CompileException(new FileNotFoundException(_wam));
+ if (! fileExists(_dir) && !new File(_dir).mkdirs())
+ throw new CompileException(new FileNotFoundException(_dir));
+
// Create arguments
Term wam = SymbolTerm.makeSymbol(_wam);
Term dir = SymbolTerm.makeSymbol(_dir);
- BlockingPrologControl ctl = new BlockingPrologControl();
- return ctl.execute("com.googlecode.prolog_cafe.compiler.am2j", "am2j",
- new ListTerm(wam, new ListTerm(dir, Prolog.Nil)));
- } catch (Exception e){
- e.printStackTrace();
- }
- return false;
+ ListTerm args = new ListTerm(wam, new ListTerm(dir, Prolog.Nil));
+ try {
+ if (!pcl.execute("com.googlecode.prolog_cafe.compiler.am2j", "am2j", args))
+ throw new CompileException("Unknown Error");
+ } catch (PrologException err) {
+ throw new CompileException("Error converting "+_wam, err);
+ }
}
- /**
- * Translates a Prolog program into Java programs.
+ /**
+ * Translates a Prolog program into Java source files.
*
* @param prolog an input Prolog file
* @param dir a destination directory for java files. The directory must already exist.
- * @return <code>true</code> if succeeds, otherwise <code>false</code>.
* @see #prologToWAM(String, String)
- * @see #wamToJava(String, String)
+ * @see #wamToJavaSource(String, String)
*/
- public boolean prologToJava(String prolog, String dir) {
- String wam = prolog + ".am";
- if (! prologToWAM(prolog, wam))
- return false;
- if (! wamToJava(wam, dir))
- return false;
- try {
- File f = new File(wam);
- if (f.exists())
- f.delete();
- } catch (SecurityException e) {}
- return true;
+ public void prologToJavaSource(String prolog, String dir) throws CompileException {
+ File tmp;
+ try {
+ tmp = File.createTempFile("PrologCafe_", ".am");
+ } catch (IOException e) {
+ throw new CompileException("Cannot create temporary file", e);
+ }
+ try {
+ prologToWAM(prolog, tmp.getPath());
+ wamToJavaSource(tmp.getPath(), dir);
+ } finally {
+ if (!tmp.delete() && tmp.exists())
+ tmp.deleteOnExit();
+ }
}
- public static void main(String argv[]) {
- try {
- System.err.println("\n" + VERSION);
- System.err.println(COPYRIGHT);
- if (argv.length != 1) {
- usage();
- System.exit(999);
- }
- Compiler comp = new Compiler();
- if (! comp.prologToJava(argv[0], "."))
- System.exit(1);
- System.exit(0);
- } catch (Exception e){
- e.printStackTrace();
- System.exit(1);
- }
- }
+ public static void main(String argv[]) throws Exception {
+ Compiler comp = new Compiler();
+ String out = ".";
+ String amdir = null;
+ boolean stackTrace = false;
+ LinkedList<String> plsrc = new LinkedList<String>();
+ int argi = 0;
+ for (; argi < argv.length; argi++) {
+ String a = argv[argi];
+ if (a.equals("--")) {
+ argi++;
+ break;
+ }
- protected static boolean fileExists(String _file) {
- try {
- File file = new File(_file);
- return file.exists();
- } catch (SecurityException e) {}
- return false;
+ if (a.equals("-O")) {
+ comp.enableDefaultOptions();
+
+ } else if (a.equals("-O:none")) {
+ comp.options.clear();
+
+ } else if (a.startsWith("-O:")) {
+ String optname = a.substring("-O:".length());
+ Option opt = findOptionByName(optname);
+ if (opt != null)
+ comp.enable(opt);
+
+ } else if (a.equals("-s")) {
+ if (++argi == argv.length)
+ usage();
+ out = argv[argi];
+
+ } else if (a.equals("-am")) {
+ if (++argi == argv.length)
+ usage();
+ amdir = argv[argi];
+
+ } else if (a.equals("-h") || a.equals("--help") || a.equals("-help")) {
+ usage();
+
+ } else if (a.equals("--show-stack-trace")) {
+ stackTrace = true;
+
+ } else if (a.startsWith("-")) {
+ System.err.println("error: Unsupported flag '" + a + "'");
+ usage();
+
+ } else {
+ plsrc.add(a);
+ }
+ }
+ if (argi < argv.length)
+ plsrc.addAll(Arrays.asList(argv).subList(argi, argv.length));
+ if (plsrc.isEmpty())
+ usage();
+
+ banner();
+ for (String pl : plsrc) {
+ System.err.println("Translating " + pl);
+
+ try {
+ if (amdir != null) {
+ String base;
+ if (pl.endsWith(".pl"))
+ base = pl.substring(0, pl.length() - 3);
+ else
+ base = pl;
+ File am = new File(new File(amdir), base + ".am");
+ am.getParentFile().mkdirs();
+
+ comp.prologToWAM(pl, am.getPath());
+ comp.wamToJavaSource(am.getPath(), out);
+ } else {
+ comp.prologToJavaSource(pl, out);
+ }
+ } catch (CompileException err) {
+ if (stackTrace)
+ err.printStackTrace();
+ else
+ System.err.println("error: " + err.getMessage());
+ System.exit(1);
+ }
+ }
}
- /** Shows usage */
- protected static void usage() {
- String s = "Usage:\n";
- s += "java -cp $PLCAFEDIR/plcafe.jar";
- s += " com.googlecode.prolog_cafe.compiler.Compiler prolog_file\n";
- System.out.println(s);
+ private static Option findOptionByName(String optname) {
+ for (Option opt : Option.values()) {
+ if (opt.toString().equalsIgnoreCase(optname))
+ return opt;
+ if (opt.symbol.name().equalsIgnoreCase(optname))
+ return opt;
+ }
+ System.err.println("error: Unsupported option '" + optname + "'");
+ System.exit(1);
+ throw new RuntimeException("System.exit(1)");
}
- /**
- * Returns the boolean value of <code>eliminateDisjunctions</code>.
- * @see #eliminateDisjunctions
- */
- public boolean getEliminateDisjunctions() { return eliminateDisjunctions; }
- /**
- * The <code>eliminateDisjunctions</code> field is set to <code>b</code>.
- * @see #eliminateDisjunctions
- */
- public void setEliminateDisjunctions(boolean b) { eliminateDisjunctions = b; }
+ private static void usage() {
+ System.err.print("usage: ");
+ System.err.print("java ");
+ System.err.print(Compiler.class.getName());
+ System.err.print(" [options]");
+ System.err.print(" prolog_source...");
+ System.err.println();
+ banner();
- /**
- * Returns the boolean value of <code>arithmeticCompilation</code>.
- * @see #arithmeticCompilation
- */
- public boolean getArithmeticCompilation() { return arithmeticCompilation; }
- /**
- * The <code>arithmeticCompilation</code> field is set to <code>b</code>.
- * @see #arithmeticCompilation
- */
- public void setArithmeticCompilation(boolean b) { arithmeticCompilation = b; }
+ String optfmt = " %-20s %s";
+ System.err.format(optfmt, "-s <directory>", "where to place generated source files");
+ System.err.println();
+ System.err.format(optfmt, "-am <directory>", "save WAM intermediate files");
+ System.err.println();
- /**
- * Returns the boolean value of <code>inlineExpansion</code>.
- * @see #inlineExpansion
- */
- public boolean getInlineExpansion() { return inlineExpansion; }
- /**
- * The <code>inlineExpansion</code> field is set to <code>b</code>.
- * @see #inlineExpansion
- */
- public void setInlineExpansion(boolean b) { inlineExpansion = b; }
+ System.err.format(optfmt, "-O", "enable all optimizations");
+ System.err.println();
+ System.err.format(optfmt, "-O:none", "disable all optimizations");
+ System.err.println();
- /**
- * Returns the boolean value of <code>optimiseRecursiveCall</code>.
- * @see #optimiseRecursiveCall
- */
- public boolean getOptimiseRecursiveCall() { return optimiseRecursiveCall; }
- /**
- * The <code>optimiseRecursiveCall</code> field is set to <code>b</code>.
- * @see #optimiseRecursiveCall
- */
- public void setOptimiseRecursiveCall(boolean b) { optimiseRecursiveCall = b; }
+ // Special options not related to building Prolog programs.
+ System.err.println();
+ System.err.format(optfmt, "-h, --help", "display this message");
+ System.err.println();
+ System.err.format(optfmt, "--show-stack-trace", "show Java stack trace on failure");
+ System.err.println();
- /**
- * Returns the boolean value of <code>switchOnHash</code>.
- * @see #switchOnHash
- */
- public boolean getSwitchOnHash() { return switchOnHash; }
- /**
- * The <code>switchOnHash</code> field is set to <code>b</code>.
- * @see #switchOnHash
- */
- public void setSwitchOnHash(boolean b) { switchOnHash = b; }
+ System.exit(1);
+ }
- /**
- * Returns the boolean value of <code>generateClosure</code>.
- * @see #generateClosure
- */
- public boolean getGenerateClosure() { return generateClosure; }
- /**
- * The <code>generateClosure</code> field is set to <code>b</code>.
- * @see #generateClosure
- */
- public void setGenerateClosure(boolean b) { generateClosure = b; }
+ private static void banner() {
+ System.err.println("Prolog Cafe");
+ System.err.println("Copyright(C) 1997-2009 M.Banbara and N.Tamura");
+ System.err.println();
+ }
+
+ private static boolean fileExists(String _file) {
+ try {
+ return new File(_file).exists();
+ } catch (SecurityException e) {}
+ return false;
+ }
+
+ public boolean isEnabled(Option opt) { return options.contains(opt); }
+ public void enable(Option opt) { options.add(opt); }
+ public void disable(Option opt) { options.remove(opt); }
+ public void setEnabled(Option opt, boolean on) {
+ if (on)
+ enable(opt);
+ else
+ disable(opt);
+ }
+
+ private void enableDefaultOptions() {
+ for (Option opt : Option.values())
+ if (opt.onByDefault)
+ options.add(opt);
+ }
}
diff --git a/src/compiler/Makefile b/src/compiler/Makefile
index c6d2c3d..20ef2fd 100644
--- a/src/compiler/Makefile
+++ b/src/compiler/Makefile
@@ -42,7 +42,7 @@ JAVA = java
plc: pl2am.plc am2j.plc
compiler:pl2am.am am2j.am
- $(JAVAC) $(JAVACOPTS) pl2am/*.java am2j/*.java Compiler.java
+ $(JAVAC) $(JAVACOPTS) pl2am/*.java am2j/*.java *.java
$(JAR) $(JAROPTS) compiler.jar com/googlecode/prolog_cafe/compiler
plj: pl2am.plj am2j.plj
diff --git a/src/lang/BufferingPrologControl.java b/src/lang/BufferingPrologControl.java
new file mode 100644
index 0000000..3e1f423
--- /dev/null
+++ b/src/lang/BufferingPrologControl.java
@@ -0,0 +1,148 @@
+package com.googlecode.prolog_cafe.lang;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Executes Prolog on the current thread, buffering all solutions.
+ * <p>
+ * Whenever a solution is found for the predicate the arguments are deep-copied
+ * and buffered in a result collection.
+ */
+public class BufferingPrologControl extends PrologControl {
+ private int resLimit;
+ private List resBuffer;
+
+ private boolean resSingle;
+ private Term[] resTemplate;
+
+ /**
+ * Determine if the predicate completes successfully.
+ *
+ * @param pkg package the functor is declared in. Typically "user".
+ * @param functor a prolog predicate to execute.
+ * @param args arguments to pass.
+ * @return true if the function has at least one solution; false otherwise.
+ */
+ public boolean execute(String pkg, String functor, Term... args) {
+ return once(pkg, functor, args) != null;
+ }
+
+ /**
+ * Execute a function and return one solution.
+ *
+ * @param pkg package the functor is declared in. Typically "user".
+ * @param functor a prolog predicate to execute.
+ * @param arg argument to pass in, and template to return the result with. If
+ * this is an {@link VariableTerm} the value will be saved and returned
+ * on success. If this is a structure or list containing variables, the
+ * variables will be materialized in the result.
+ * @return a deep copy of {@code arg} for the first solution; null on failure.
+ */
+ public Term once(String pkg, String functor, Term arg) {
+ setPredicate(pkg, functor, arg);
+ setResultTemplate(arg);
+ return (Term) (run(1) ? resBuffer.get(0) : null);
+ }
+
+ /**
+ * Execute a function and return one solution.
+ *
+ * @param pkg package the functor is declared in. Typically "user".
+ * @param functor a prolog predicate to execute.
+ * @param args argument to pass in, and template to return the result with. If
+ * this is an {@link VariableTerm} the value will be saved and returned
+ * on success. If this is a structure or list containing variables, the
+ * variables will be materialized in the result.
+ * @return a deep copy of {@code args} for the first solution; null on
+ * failure.
+ */
+ public Term[] once(String pkg, String functor, Term... args) {
+ setPredicate(pkg, functor, args);
+ setResultTemplate(args);
+ return (Term[]) (run(1) ? resBuffer.get(0) : null);
+ }
+
+ /**
+ * Execute a function and return all solutions.
+ *
+ * @param pkg package the functor is declared in. Typically "user".
+ * @param functor a prolog predicate to execute.
+ * @param arg argument to pass in, and template to return the result with. If
+ * this is an {@link VariableTerm} the value will be saved and returned
+ * on success. If this is a structure or list containing variables, the
+ * variables will be materialized in the result.
+ * @return a deep copy of {@code arg} for each solution found. Empty list if
+ * there are no solutions.
+ */
+ public List<Term> all(String pkg, String functor, Term arg) {
+ setPredicate(pkg, functor, arg);
+ setResultTemplate(arg);
+ run(Integer.MAX_VALUE);
+ return resBuffer;
+ }
+
+ /**
+ * Execute a function and return all solutions.
+ *
+ * @param pkg package the functor is declared in. Typically "user".
+ * @param functor a prolog predicate to execute.
+ * @param args argument to pass in, and template to return the result with. If
+ * this is an {@link VariableTerm} the value will be saved and returned
+ * on success. If this is a structure or list containing variables, the
+ * variables will be materialized in the result.
+ * @return a deep copy of {@code args} for each solution found. Empty list if
+ * there are no solutions.
+ */
+ public List<Term[]> all(String pkg, String functor, Term... args) {
+ setPredicate(pkg, functor, args);
+ setResultTemplate(args);
+ run(Integer.MAX_VALUE);
+ return resBuffer;
+ }
+
+ private void setResultTemplate(Term t) {
+ resTemplate = new Term[] {t};
+ resSingle = true;
+ }
+
+ private void setResultTemplate(Term[] t) {
+ resTemplate = t;
+ resSingle = false;
+ }
+
+ /**
+ * Execute until the limit is reached.
+ *
+ * @param newLimit maximum number of results. Set to 1 if only a single
+ * attempt is required.
+ * @return true if at least one result was discovered; false if there are no
+ * solutions to the predicate.
+ */
+ private boolean run(int newLimit) {
+ resLimit = newLimit;
+ resBuffer = new ArrayList(Math.min(newLimit, 16));
+
+ executePredicate();
+ return 0 < resBuffer.size();
+ }
+
+ @Override
+ public boolean isEngineStopped() {
+ return resLimit <= resBuffer.size();
+ }
+
+ @Override
+ protected void success() {
+ Term[] r = new Term[resTemplate.length];
+ for (int i = 0; i < resTemplate.length; i++) {
+ r[i] = engine.copy(resTemplate[i]);
+ }
+ resBuffer.add(resSingle ? r[0] : r);
+ }
+
+ @Override
+ protected void fail() {
+ resLimit = 0;
+ }
+}