/*
* tuProlog - Copyright (C) 2001-2007 aliCE team at deis.unibo.it
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package alice.tuprolog;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
/**
* This class defines the Theory Manager who manages the clauses/theory often
* referred to as the Prolog database. The theory (as a set of clauses) are
* stored in the ClauseDatabase which in essence is a HashMap grouped by
* functor/arity.
* <p/>
* The TheoryManager functions logically, as prescribed by ISO Standard 7.5.4
* section. The effects of assertions and retractions shall not be undone if
* the program subsequently backtracks over the assert or retract call, as
* prescribed by ISO Standard 7.7.9 section.
* <p/>
* To use the TheoryManager one should primarily use the methods assertA,
* assertZ, consult, retract, abolish and find.
* <p/>
*
* rewritten by:
* @author ivar.orstavik@hist.no
*
* @see Theory
*/
public class TheoryManager {
private ClauseDatabase dynamicDBase;
private ClauseDatabase staticDBase;
private Prolog engine;
private PrimitiveManager primitiveManager;
private Stack<Struct> startGoalStack;
Theory lastConsultedTheory;
void initialize(Prolog vm) {
dynamicDBase = new ClauseDatabase();
staticDBase = new ClauseDatabase();
lastConsultedTheory = new Theory();
engine = vm;
primitiveManager = engine.getPrimitiveManager();
}
/**
* Inserting of a clause at the head of the database.
*/
void assertA(Struct clause, boolean dyn, String libName, boolean backtrackable) {
ClauseInfo d = new ClauseInfo(toClause(clause), libName);
String key = d.getHead().getPredicateIndicator();
if (dyn) {
dynamicDBase.addFirst(key, d);
if (staticDBase.containsKey(key)) {
engine.warn("A static predicate with signature " + key + " has been overriden.");
}
} else
staticDBase.addFirst(key, d);
engine.spy("INSERTA: " + d.getClause() + "\n");
}
/**
* Inserting of a clause at the end of the database.
*/
void assertZ(Struct clause, boolean dyn, String libName, boolean backtrackable) {
ClauseInfo d = new ClauseInfo(toClause(clause), libName);
String key = d.getHead().getPredicateIndicator();
if (dyn) {
dynamicDBase.addLast(key, d);
if (staticDBase.containsKey(key)) {
engine.warn("A static predicate with signature " + key + " has been overriden.");
}
} else
staticDBase.addLast(key, d);
engine.spy("INSERTZ: " + d.getClause() + "\n");
}
/**
* Removing from database the first clause with head unifying with clause.
*/
ClauseInfo retract(Struct cl) {
Struct clause = toClause(cl);
Struct struct = ((Struct) clause.getArg(0));
LinkedList<ClauseInfo> family = dynamicDBase.get(struct.getPredicateIndicator());
if (family == null)
return null;
for (Iterator<ClauseInfo> it = family.iterator(); it.hasNext();) {
ClauseInfo d = it.next();
if (clause.match(d.getClause())) {
it.remove();
engine.spy("DELETE: " + d.getClause() + "\n");
return new ClauseInfo(d.getClause(), null);
}
}
return null;
}
/**
* Removing from database all the clauses corresponding to the
* predicate indicator passed as a parameter.
*/
boolean abolish(Struct pi) {
String key = pi.toStringWithoutApices();
LinkedList<ClauseInfo> abolished = dynamicDBase.abolish(key);
if (abolished != null)
engine.spy("ABOLISHED: " + key + " number of clauses = " + abolished.size() + "\n");
return true;
}
/**
* Returns a family of clauses with functor and arity equals
* to the functor and arity of the term passed as a parameter.
*/
List<ClauseInfo> find(Term headt) {
if (headt instanceof Struct) {
String key = ((Struct) headt).getPredicateIndicator();
List<ClauseInfo> list = dynamicDBase.getPredicates(key);
if (list.isEmpty())
list = staticDBase.getPredicates(key);
return list;
}
if (headt instanceof Var) {
String m = "Cannot find a family of clauses corresponding " +
"to a variable. Pass a Struct instead.";
throw new IllegalArgumentException(m);
}
return new LinkedList<ClauseInfo>();
}
/**
* Consults a theory.
*
* @param theory theory to add
* @param dynamicTheory if it is true, then the clauses are marked as dynamic
* @param libName if it not null, then the clauses are marked to belong to the specified library
*/
void consult(Theory theory, boolean dynamicTheory, String libName) throws InvalidTheoryException {
startGoalStack = new Stack<Struct>();
// iterate all clauses in theory and assert them
try {
for (Iterator<Term> it = theory.iterator(engine); it.hasNext();) {
Struct d = (Struct) it.next();
if (!runDirective(d))
assertZ(d, dynamicTheory, libName, true);
}
} catch (InvalidTermException e) {
throw new InvalidTheoryException(e.getMessage());
}
if (libName == null)
//lastConsultedTheory.append(theory);
lastConsultedTheory = theory;
}
/**
* Binds clauses in the database with the corresponding
* primitive predicate, if any.
*/
void rebindPrimitives() {
for (ClauseInfo d : dynamicDBase)
for (AbstractSubGoalTree e : d.getBody()) {
Term t = ((SubGoalElement) e).getValue();
primitiveManager.identifyPredicate(t);
}
}
/**
* Clears the clause database.
*/
void clear() {
dynamicDBase = new ClauseDatabase();
}
/**
* Remove all the clauses of library theory.
*/
void removeLibraryTheory(String libName) {
for (Iterator<ClauseInfo> allClauses = staticDBase.iterator(); allClauses.hasNext();) {
ClauseInfo d = allClauses.next();
if (d.libName != null && libName.equals(d.libName))
allClauses.remove();
}
}
private boolean runDirective(Struct c) {
if ("':-'".equals(c.getName()) || ":-".equals(c.getName()) && c.getArity() == 1 && c.getTerm(0) instanceof Struct) {
Struct dir = (Struct) c.getTerm(0);
return primitiveManager.evalAsDirective(dir);
} else
return false;
}
/**
* Gets a clause from a generic Term.
*/
private Struct toClause(Struct t) {
// TODO bad, slow way of cloning. requires approximately twice the time necessary
t = (Struct) Term.createTerm(t.toString(), this.engine.getOperatorManager());
if (!t.isClause())
t = new Struct(":-", t, new Struct("true"));
primitiveManager.identifyPredicate(t);
return t;
}
void solveTheoryGoal() {
Struct s = null;
while (!startGoalStack.empty()) {
s = (s == null) ?
(Struct) startGoalStack.pop() :
new Struct(",", (Struct) startGoalStack.pop(), s);
}
if (s != null) {
try {
engine.solve(s);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* Add a goal eventually defined by last parsed theory.
*/
void addStartGoal(Struct g) {
startGoalStack.push(g);
}
/**
* Saves the database on a output stream.
*/
boolean save(OutputStream os, boolean onlyDynamic) {
try {
new DataOutputStream(os).writeBytes(getTheory(onlyDynamic));
return true;
} catch (IOException e) {
return false;
}
}
/**
* Gets current theory.
*
* @param onlyDynamic if true, fetches only dynamic clauses
*/
public String getTheory(boolean onlyDynamic) {
StringBuilder buffer = new StringBuilder();
for (ClauseInfo d : dynamicDBase)
buffer.append(d.toString(engine.getOperatorManager())).append("\n");
if (!onlyDynamic)
for (ClauseInfo d : staticDBase)
buffer.append(d.toString(engine.getOperatorManager())).append("\n");
return buffer.toString();
}
/**
* Gets last consulted theory.
*
* @return last theory
*/
Theory getLastConsultedTheory() {
return lastConsultedTheory;
}
}