public IEnumerable <IDebugEvent> Solve (Compiled.Goal [] goalDefs) { // Instantiate the goals. var queryArgumentInstantiator = new ArgumentInstantiator(); var goals = InstantiateGoals (goalDefs, queryArgumentInstantiator); frame = new Frame (goals, null, new BoundVariableSet (), queryArgumentInstantiator.Variables); // ReSharper disable ConditionIsAlwaysTrueOrFalse while (frame != null) // ReSharper restore ConditionIsAlwaysTrueOrFalse { if (frame.GoalsProven == frame.Goals.Length) { // No more goals to prove or it's a fact. System.Diagnostics.Debug.Assert (frame.Goals.All (g => g.CurrentFrame != null)); if (frame.Parent == null) { yield return new Solution (frame); foreach (var e in GoToLastChoicePoint ()) yield return e; } else { // go on and prove the next parent goal. frame = frame.Parent; frame.GoalsProven++; } continue; } var goal = frame.Goals [frame.GoalsProven]; // Check that the same goal is not on the stack to prevent infinite loops. if (AlreadyOnTheStack (goal)) { foreach (var e in Backtrack ()) yield return e; } else { var newFrame = goal.GetNextFrame (); if (newFrame == null) { foreach (var e in Backtrack ()) yield return e; } else { yield return new Enter (newFrame); frame = newFrame; // decend the tree } } } }
IEnumerable <IDebugEvent> Backtrack () { // Current frame's current goal does not unify with any (more) clauses. if (frame.GoalsProven == 0) { if (frame.Parent != null) yield return new Leave (frame); frame = frame.Parent; // ascend the tree; frame may become null at this point. } else { frame.CurrentGoal.Restart(); foreach (var e in GoToLastChoicePoint ()) yield return e; } }
IEnumerable <IDebugEvent> GoToLastChoicePoint () { while (frame.Goals.Length > 0) { frame.GoalsProven--; var lastProvenGoal = frame.CurrentGoal; frame = lastProvenGoal.CurrentFrame; } yield return new Leave (frame); frame = frame.Parent; // ascend the tree; frame CANNOT become null at this point. }