/*
* tuProlog - Copyright (C) 2001-2002 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.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Core engine.
*
* @author Alex Benini
*/
public class Engine {
enum State {
INIT,
GOAL_EVALUATION,
GOAL_SELECTION,
RULE_SELECTION,
BACKTRACK,
FALSE,
TRUE,
TRUE_CP,
HALT
}
private State state;
Term query;
Struct startGoal;
Collection<Var> goalVars;
int nDemoSteps;
ExecutionContext currentContext;
//ClauseStore clauseSelector;
ChoicePointContext currentAlternative;
ChoicePointStore choicePointSelector;
boolean mustStop;
EngineManager manager;
public Engine(EngineManager manager, Term query) {
this.manager = manager;
this.query = query;
mustStop = false;
}
void initialize(ExecutionContext eCtx) {
currentContext = eCtx;
choicePointSelector = new ChoicePointStore();
nDemoSteps = 1;
currentAlternative = null;
}
void mustStop() {
mustStop = true;
}
SolveInfo solve() {
state = State.INIT;
return run();
}
SolveInfo solveNext() {
state = State.BACKTRACK;
return run();
}
/** The Finite State Machine representing the engine's core. */
private SolveInfo run() {
while (state != State.FALSE && state != State.TRUE &&
state != State.TRUE_CP && state != State.HALT) {
if (mustStop) {
state = State.FALSE;
break;
}
String action = state.toString();
switch (state) {
case INIT:
state = init();
break;
case GOAL_EVALUATION:
state = goalEvaluation();
break;
case GOAL_SELECTION:
state = goalSelection();
break;
case RULE_SELECTION:
state = ruleSelection();
break;
case BACKTRACK:
state = backtrack();
break;
}
manager.spy(action, this);
}
List<Var> vars = new ArrayList<Var>();
Struct goal = (Struct) startGoal.copyResult(goalVars, vars);
return new SolveInfo(query, goal, state, vars);
}
/** Initial state of demonstration. */
State init() {
// prepare goal
Map<Var, Var> goalVars = new LinkedHashMap<Var, Var>();
startGoal = (Struct) query.copy(goalVars, 0);
this.goalVars = goalVars.values();
// initialize first executionContext
ExecutionContext eCtx = new ExecutionContext(0);
eCtx.goalsToEval = new SubGoalStore();
eCtx.goalsToEval.load(ClauseInfo.extractBody(startGoal));
eCtx.clause = (Struct) query;
eCtx.depth = 0;
// initialize VM environment
initialize(eCtx);
return State.GOAL_SELECTION;
}
State goalEvaluation() {
if (!currentContext.currentGoal.isPrimitive())
return State.RULE_SELECTION;
State nextState = null;
// get the primitive
PrimitiveInfo primitive = currentContext.currentGoal.getPrimitive();
try {
nextState = primitive.evalAsPredicate(currentContext.currentGoal) ?
State.GOAL_SELECTION : State.BACKTRACK;
} catch (HaltException he) {
nextState = State.HALT;
} catch (Throwable t) {
// TODO Manage exceptions in Prolog
t.printStackTrace();
nextState = State.HALT;
}
// increment the demonstration steps counter
nDemoSteps++;
return nextState;
}
State goalSelection() {
Term curGoal = null;
State nextState = null;
while (curGoal == null) {
curGoal = currentContext.goalsToEval.fetch();
if (curGoal == null) {
// demo termination
if (currentContext.fatherCtx == null) {
// verify ChoicePoint
nextState = (choicePointSelector.existChoicePoint()) ?
State.TRUE_CP : State.TRUE;
break;
}
// Case: removing an execution context
currentContext = currentContext.fatherCtx;
} else {
// Case: identify curGoal
Term goal_app = curGoal.getTerm();
if (!(goal_app instanceof Struct)) {
nextState = State.FALSE;
break;
}
// Code inserted to allow evaluation of meta-clause
// such as p(X) :- X. When evaluating directly terms,
// they are converted to execution of a call/1 predicate.
// This enables the dynamic linking of built-ins for
// terms coming from outside the demonstration context.
if (curGoal != goal_app)
curGoal = new Struct("call", goal_app);
currentContext.currentGoal = (Struct) curGoal;
nextState = State.GOAL_EVALUATION;
}
} // end while
return nextState;
}
State ruleSelection() {
// identify compatibleGoals and
// ascertain whether we come from backtracking.
Struct goal = currentContext.currentGoal;
boolean fromBacktracking = true;
ChoicePointContext alternative = currentAlternative;
ClauseStore clauseStore;
currentAlternative = null;
if (alternative == null) {
// from normal evaluation
fromBacktracking = false;
List<Var> varsList = new ArrayList<Var>();
currentContext.trailingVars = new OneWayList<List<Var>>(varsList, currentContext.trailingVars);
clauseStore = ClauseStore.build(goal, varsList, manager.find(goal));
if (clauseStore == null)
return State.BACKTRACK;
} else
clauseStore = alternative.compatibleGoals;
// choose a rule from those potentially compatible
ClauseInfo clause = clauseStore.fetch();
// build ExecutionContext and ChoicePointContext
ExecutionContext ec = new ExecutionContext(nDemoSteps++);
ExecutionContext curCtx = currentContext;
ec.clause = clause.getClause();
// head and body with refresh variables (clause copied)
clause.performCopy(ec.getId());
ec.headClause = clause.getHeadCopy();
ec.goalsToEval = new SubGoalStore();
ec.goalsToEval.load(clause.getBodyCopy());
// The following block encodes cut functionalities, and hardcodes the
// special treatment that ISO Standard reserves for goal disjunction:
// section 7.8.6.1 prescribes that ;/2 must be transparent to cut.
ec.choicePointAfterCut = choicePointSelector.getPointer();
if (alternative != null) {
ChoicePointContext choicePoint = alternative;
int depth = alternative.executionContext.depth;
ec.choicePointAfterCut = choicePoint.prevChoicePointContext;
Struct currentGoal = choicePoint.executionContext.currentGoal;
while (currentGoal.getName().equals(";") && currentGoal.getArity() == 2) {
if (choicePoint.prevChoicePointContext != null) {
int distance = depth - choicePoint.prevChoicePointContext.executionContext.depth;
while (distance == 0 && choicePoint.prevChoicePointContext != null) {
ec.choicePointAfterCut = choicePoint.prevChoicePointContext.prevChoicePointContext;
choicePoint = choicePoint.prevChoicePointContext;
}
if (distance == 1 && choicePoint.prevChoicePointContext != null) {
ec.choicePointAfterCut = choicePoint.prevChoicePointContext.prevChoicePointContext;
currentGoal = choicePoint.prevChoicePointContext.executionContext.currentGoal;
choicePoint = choicePoint.prevChoicePointContext;
} else
break;
} else
break;
}
}
Struct curGoal = curCtx.currentGoal;
List<Var> unifiedVars = (List<Var>) currentContext.trailingVars.getHead();
curGoal.unify(unifiedVars, unifiedVars, ec.headClause);
ec.haveAlternatives = clauseStore.haveAlternatives();
// cpc creation
if (ec.haveAlternatives && !fromBacktracking) {
ChoicePointContext cpc = new ChoicePointContext();
cpc.compatibleGoals = clauseStore;
// c.saveLastTheoryStatus();
cpc.executionContext = curCtx;
cpc.indexSubGoal = curCtx.goalsToEval.getCurrentGoalId();
cpc.varsToDeunify = currentContext.trailingVars;
choicePointSelector.add(cpc);
}
// cpc destruction
if (!ec.haveAlternatives && fromBacktracking) {
choicePointSelector.removeUnusedChoicePoints();
}
ec.performTailRecursionOptimization(this);
ec.saveParentState();
ec.depth = currentContext.depth + 1;
currentContext = ec;
return State.GOAL_SELECTION;
}
State backtrack() {
ChoicePointContext curChoice = choicePointSelector.fetch();
// verify ChoicePoint
if (curChoice == null) {
Struct goal = currentContext.currentGoal;
manager.warn("The predicate " + goal.getPredicateIndicator() + " is unknown.");
return State.FALSE;
}
currentAlternative = curChoice;
// deunify variables and reload old goal
currentContext = curChoice.executionContext;
Term curGoal = currentContext.goalsToEval.backTo(curChoice.indexSubGoal).getTerm();
if (!(curGoal instanceof Struct))
return State.FALSE;
currentContext.currentGoal = (Struct) curGoal;
// ensure coherence in execution stack
ExecutionContext curCtx = currentContext;
OneWayList<List<Var>> pointer = curCtx.trailingVars;
OneWayList<List<Var>> stopDeunify = curChoice.varsToDeunify;
List<Var> varsToDeunify = (List<Var>) stopDeunify.getHead();
Var.free(varsToDeunify);
varsToDeunify.clear();
SubGoalId fatherIndex;
// bring parent contexts to a previous state in the demonstration
do {
// deunify variables in sibling contexts
while (pointer != stopDeunify) {
Var.free(pointer.getHead());
pointer = pointer.getTail();
}
curCtx.trailingVars = pointer;
if (curCtx.fatherCtx == null)
break;
stopDeunify = curCtx.fatherVarsList;
fatherIndex = curCtx.fatherGoalId;
curCtx = curCtx.fatherCtx;
curGoal = curCtx.goalsToEval.backTo(fatherIndex).getTerm();
if (!(curGoal instanceof Struct))
return State.FALSE;
curCtx.currentGoal = (Struct)curGoal;
pointer = curCtx.trailingVars;
} while (true);
return State.GOAL_EVALUATION;
}
@Override
public String toString() {
try {
return "ExecutionStack: \n" + currentContext + "\n" +
"ChoicePointStore: \n" + choicePointSelector + "\n\n";
} catch(Exception ex) {
return "";
}
}
/*
* Methods for spyListeners
*/
public Term getQuery() {
return query;
}
public int getNumDemoSteps() {
return nDemoSteps;
}
public List<ExecutionContext> getExecutionStack() {
List<ExecutionContext> l = new ArrayList<ExecutionContext>();
ExecutionContext t = currentContext;
while (t != null) {
l.add(t);
t = t.fatherCtx;
}
return l;
}
public ChoicePointStore getChoicePointStore() {
return choicePointSelector;
}
}