package alice.tuprolog; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; /** * A list of clauses belonging to the same family as a goal. A family is * composed by clauses with the same functor and arity. */ public class ClauseStore { private OneWayList<ClauseInfo> clauses; private Term goal; private List<Var> vars; private boolean haveAlternatives; private ClauseStore(Term goal, List<Var> vars) { this.goal = goal; this.vars = vars; clauses = null; } /** * Load a clause family. * @param familyClauses */ public static ClauseStore build(Term goal, List<Var> vars, List<ClauseInfo> familyClauses) { ClauseStore clauseStore = new ClauseStore(goal, vars); clauseStore.clauses = OneWayList.transform(familyClauses); if (clauseStore.clauses == null || !clauseStore.existCompatibleClause()) return null; return clauseStore; } /** * Return the clause to load. */ public ClauseInfo fetch() { if (clauses == null) return null; deunify(vars); if (!checkCompatibility(goal)) return null; ClauseInfo clause = (ClauseInfo) clauses.getHead(); clauses = clauses.getTail(); haveAlternatives = checkCompatibility(goal); return clause; } public boolean haveAlternatives() { return haveAlternatives; } /** * Verify if there is a term in compatibleGoals compatible with goal. * @param goal * @param compGoals * @return true if compatible or false otherwise. */ protected boolean existCompatibleClause() { List<Term> saveUnifications = deunify(vars); boolean found = checkCompatibility(goal); reunify(vars, saveUnifications); return found; } /** * Save bindings of variables to deunify * @param varsToDeunify * @return binding of variables */ private List<Term> deunify(List<Var> varsToDeunify) { List<Term> saveUnifications = new ArrayList<Term>(); // temporarily deunifying variables for (Var v : varsToDeunify) { saveUnifications.add(v.getLink()); v.free(); } return saveUnifications; } /** * Restore previous unifications into variables. * @param varsToReunify * @param saveUnifications */ private void reunify(List<Var> varsToReunify, List<Term> saveUnifications) { int size = varsToReunify.size(); ListIterator<Var> it1 = varsToReunify.listIterator(size); ListIterator<Term> it2 = saveUnifications.listIterator(size); // Only the first occurrence of a variable gets its binding saved; // following occurrences get a null instead. So, to avoid clashes // between those values, and avoid random variable deunification, // the reunification is made starting from the end of the list. while (it1.hasPrevious()) it1.previous().setLink(it2.previous()); } /** * Verify if a clause exists that is compatible with goal. * As a side effect, clauses that are not compatible get * discarded from the currently examined family. * @param goal */ private boolean checkCompatibility(Term goal) { if (clauses == null) return false; ClauseInfo clause = null; do { clause = (ClauseInfo) clauses.getHead(); if (goal.match(clause.getHead())) return true; clauses = clauses.getTail(); } while (clauses != null); return false; } @Override public String toString() { return "clauses: "+clauses+"\n"+ "goal: "+goal+"\n"+ "vars: "+vars+"\n"; } /* * Methods for spyListeners */ public List<ClauseInfo> getClauses() { List<ClauseInfo> l = new ArrayList<ClauseInfo>(); OneWayList<ClauseInfo> t = clauses; while (t != null) { l.add(t.getHead()); t = t.getTail(); } return l; } public Term getMatchGoal() { return goal; } public List<Var> getVarsForMatch() { return vars; } }