blob: 00ffb2addc4bfd72cecd93074f093d34dd3b8024 [file] [log] [blame]
package com.googlecode.prolog_cafe.lang;
import com.googlecode.prolog_cafe.exceptions.HaltException;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.exceptions.ReductionLimitException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.PushbackReader;
import java.util.Set;
/**
* Tracks current evaluation goal and results.
* <p>
* On success/1 or fail/1 the corresponding methods in this class are invoked,
* allowing the implementation to message the results to the application. During
* any reduction loops the {@link #isEngineStopped()} method is consulted to
* determine if the loop should terminate.
*
* @author Mutsunori Banbara (banbara@kobe-u.ac.jp)
* @author Naoyuki Tamura (tamura@kobe-u.ac.jp)
* @version 1.2
*/
public abstract class PrologControl {
/** Holds a Prolog engine. */
protected Prolog engine;
/** Holds a Prolog goal to be executed. */
protected Operation code;
/** How many operations can be executed before exceeding cost limit. */
private long reductionLimit = 1 << 20;
private long reductionsUsed;
/** Constructs a new <code>PrologControl</code>. */
public PrologControl() {
engine = new Prolog(this);
}
/** Constructs a new <code>PrologControl</code>. */
public PrologControl(PrologMachineCopy pmc) {
engine = new Prolog(this, pmc);
}
public boolean isEnabled(Prolog.Feature f) {
return engine.features.contains(f);
}
public void setEnabled(Prolog.Feature f, boolean on) {
if (on)
engine.features.add(f);
else
engine.features.remove(f);
}
public void setEnabled(Set<Prolog.Feature> f, boolean on) {
if (on)
engine.features.addAll(f);
else
engine.features.removeAll(f);
}
public int getMaxDatabaseSize() {
if (engine.internalDB != null)
return engine.internalDB.maxContents;
return InternalDatabase.DEFAULT_SIZE;
}
public void setMaxDatabaseSize(int size) {
if (engine.internalDB != null)
engine.internalDB.maxContents = size;
else
engine.internalDB = new InternalDatabase(size);
}
public PrologClassLoader getPrologClassLoader() {
if (engine.pcl == null)
engine.pcl = new PrologClassLoader();
return engine.pcl;
}
public void setPrologClassLoader(PrologClassLoader cl) {
engine.pcl = cl;
}
/**
* Registers {@code user_input}, {@code user_output}, and {@code user_error}
* streams.
*/
public void configureUserIO(InputStream in, OutputStream out,
OutputStream err) {
if (engine.streamManager == null) {
engine.streamManager = new HashtableOfTerm(7);
}
if (in != null) {
engine.streamManager.put(
SymbolTerm.intern("user_input"),
new JavaObjectTerm(new PushbackReader(
new BufferedReader(new InputStreamReader(in)),
Prolog.PUSHBACK_SIZE)));
}
if (out != null) {
engine.streamManager.put(
SymbolTerm.intern("user_output"),
new JavaObjectTerm(new PrintWriter(
new BufferedWriter(new OutputStreamWriter(out)),
true)));
}
if (err != null) {
engine.streamManager.put(
SymbolTerm.intern("user_error"),
new JavaObjectTerm(new PrintWriter(
new BufferedWriter(new OutputStreamWriter(err)),
true)));
}
}
/** Sets a goal and its arguments to this Prolog thread.
* An initial continuation goal (a <code>Success</code> object)
* is set to the <code>cont</code> field of goal <code>p</code> as continuation.
*/
public void setPredicate(Predicate p) {
p.cont = Success.SUCCESS;
code = p;
}
/** Sets a goal <code>call(t)</code> to this Prolog thread.
* An initial continuation goal (a <code>Success</code> object)
* is set to the <code>cont</code> field of goal <code>p</code> as continuation.
*/
public void setPredicate(String pkg, String functor, Term... args) {
setPredicate(getPrologClassLoader().predicate(pkg, functor, args));
}
/** Sets a goal <code>call(t)</code> to this Prolog thread.
* An initial continuation goal (a <code>Success</code> object)
* is set to the <code>cont</code> field of <code>call(t)</code> as continuation.
*/
public void setPredicate(Term t) {
setPredicate(Prolog.BUILTIN, "call", t);
}
/**
* Is invoked when the system succeeds to find a solution.<br>
*
* This method is invoked from the initial continuation goal
* (a <code>Success</code> object).
*/
protected abstract void success();
/** Is invoked after failure of all trials. */
protected abstract void fail();
/**
* Check if evaluation should continue.
* <p>
* This method gets invoked on every predicate reduction. If the control class
* wants to halt execution (for example sufficient results were obtained, or a
* limit on running time or reduction count has been reached) the method must
* return true to stop execution.
*
* @return true if the engine is no longer supposed to execute; false if
* another predicate reduction can take place.
*/
public abstract boolean isEngineStopped();
/**
* Execute the predicate on the current thread.
* <p>
* This method does not return until {@link #isEngineStopped()} returns true.
* Implementors of the class are expected to invoke this method to perform
* evaluation, and terminate out of the loop at the proper time based on an
* invocation to {@link #success()} or {@link #fail()}.
*
* @throws PrologException
*/
protected void executePredicate() throws PrologException {
Prolog engine = this.engine;
Operation code = this.code;
long reductionsRemaining = reductionLimit;
try {
engine.init();
do {
if (isEngineStopped()) return;
if (--reductionsRemaining <= 0)
throw new ReductionLimitException(reductionLimit);
code = code.exec(engine);
} while (engine.halt == 0);
if (engine.halt != 1) {
throw new HaltException(engine.halt - 1);
}
} finally {
this.reductionsUsed = reductionLimit - reductionsRemaining;
this.code = code;
SymbolTerm.gc();
}
}
/** @return number of reductions used by execution. */
public long getReductions() {
return reductionsUsed;
}
/** Applies an upper limit on number of reductions. */
public void setReductionLimit(long limit) {
reductionLimit = Math.max(0, limit);
}
/** @param err stack trace to print (or log). */
public void printStackTrace(Throwable err) {
err.printStackTrace();
}
}