package smallstep; import common.ProofGuessException; import common.ProofModel; import common.ProofNode; import common.ProofRule; import common.ProofRuleException; import common.ProofStep; import common.interpreters.AbstractInterpreterProofModel; import expressions.Expression; /** * Small step version of the {@link ProofModel}. * * @author Benedikt Meurer * @version $Id$ */ public class SmallStepProofModel extends AbstractInterpreterProofModel { // // Constructor // /** * Allocates a new small step proof model, used to * prove that the <code>expression</code> evaluates * to a certain value. * * @param expression the {@link Expression} for the * proof. */ SmallStepProofModel(Expression expression) { super(new SmallStepProofNode(expression)); // tell the view that stores should be displayed // if the supplied expression contains any refe- // rences or memory locations setMemoryEnabled(expression.containsReferences()); } // // Accessors // /** * {@inheritDoc} * * @see common.ProofModel#getRules() */ @Override public ProofRule[] getRules() { return SmallStepProofRule.getRules(); } // // Actions // /** * {@inheritDoc} * * @see common.ProofModel#guess(common.ProofNode) */ @Override public void guess(ProofNode node) throws ProofGuessException { // guess the remaining steps for the node ProofStep[] remainingSteps = remaining(node); // check if the node is already completed if (remainingSteps.length == 0) { throw new IllegalStateException("Cannot prove an already proven node"); } // try to prove using the guessed rule try { // apply the last rule for the evaluated steps to the node apply((SmallStepProofRule)remainingSteps[remainingSteps.length - 1].getRule(), (SmallStepProofNode)node); } catch (ProofRuleException exception) { // failed to guess throw new ProofGuessException(node); } } /** * {@inheritDoc} * * @see common.ProofModel#prove(common.ProofRule, common.ProofNode) */ @Override public void prove(ProofRule rule, ProofNode node) throws ProofRuleException { // verify that the rule is a small step rule if (!(rule instanceof SmallStepProofRule)) { throw new IllegalArgumentException("The rule must be a small step proof rule"); } // verify that the node is valid for the model if (!this.root.isNodeRelated(node)) { throw new IllegalArgumentException("The node is invalid for the model"); } // apply the rule to the specified node apply((SmallStepProofRule)rule, (SmallStepProofNode)node); } /** * Returns the remaining {@link ProofStep}s required to prove the specified * <code>node</code>. This method is used to guess the next step, see the * {@link #guess(ProofNode)} method for further details, and in the user * interface, to highlight the next expression. * * @param node the {@link ProofNode} for which to return the remaining * steps required to prove the <code>node</code>. * * @return the remaining {@link ProofStep}s required to prove the * <code>node</code>, or an empty array if the <code>node</code> * is already proven or the evaluation is stuck. * * @throws IllegalArgumentException if the <code>node</code> is invalid * for this model. */ public ProofStep[] remaining(ProofNode node) { // verify that the node is valid for the model if (!this.root.isNodeRelated(node)) { throw new IllegalArgumentException("The node is invalid for the model"); } // evaluate the next step for the node SmallStepEvaluator evaluator = new SmallStepEvaluator(node.getExpression(), ((SmallStepProofNode)node).getStore()); // determine the evaluated/completed steps ProofStep[] evaluatedSteps = evaluator.getSteps(); ProofStep[] completedSteps = node.getSteps(); // generate the remaining steps ProofStep[] remainingSteps = new ProofStep[evaluatedSteps.length - completedSteps.length]; System.arraycopy(evaluatedSteps, completedSteps.length, remainingSteps, 0, remainingSteps.length); return remainingSteps; } // // Internals // /** * {@inheritDoc} * * @see common.AbstractProofModel#addUndoableTreeEdit(common.AbstractProofModel.UndoableTreeEdit) */ @Override protected void addUndoableTreeEdit(UndoableTreeEdit edit) { // perform the redo of the edit edit.redo(); // add to the undo history super.addUndoableTreeEdit(edit); } /** * Applies the specified <code>rule</code> to the specified <code>node</code>. * * @param rule the {@link SmallStepProofRule} to apply. * @param node the {@link SmallStepProofNode} to which to apply the * <code>rule</code> (must be part of this model). * * @throws ProofRuleException if the <code>rule</code> cannot be applied to the * <code>node</code>. */ private void apply(SmallStepProofRule rule, final SmallStepProofNode node) throws ProofRuleException { // evaluate the expression and determine the proof steps SmallStepEvaluator evaluator = new SmallStepEvaluator(node.getExpression(), node.getStore()); Expression expression = evaluator.getExpression(); ProofStep[] evaluatedSteps = evaluator.getSteps(); // determine the completed steps for the node final ProofStep[] completedSteps = node.getSteps(); // check if the node is already completed if (completedSteps.length >= evaluatedSteps.length) { throw new IllegalStateException("Cannot prove an already proven node"); } // verify the completed steps int n; for (n = 0; n < completedSteps.length; ++n) { if (completedSteps[n].getRule() != evaluatedSteps[n].getRule()) throw new IllegalStateException("Completed steps don't match evaluated steps"); } // check if the rule is valid, accepting regular meta-rules for EXN rules int m; for (m = n; m < evaluatedSteps.length; ++m) { if (evaluatedSteps[m].getRule() == rule || evaluatedSteps[m].getRule() == rule.getExnRule()) break; } // check if rule is invalid if (m >= evaluatedSteps.length) { throw new ProofRuleException(node, rule); } // determine the new step(s) for the node final ProofStep[] newSteps = new ProofStep[m + 1]; System.arraycopy(evaluatedSteps, 0, newSteps, 0, m + 1); // check if the node is finished (the last // step is an application of an axiom rule) if (newSteps[m].getRule().isAxiom()) { // create the child node for the node final SmallStepProofNode child = new SmallStepProofNode(expression, evaluator.getStore()); // add the undoable edit addUndoableTreeEdit(new UndoableTreeEdit() { public void redo() { // apply the new steps and add the child node.setSteps(newSteps); node.add(child); nodesWereInserted(node, new int[] { node.getIndex(child) }); nodeChanged(node); } public void undo() { // remove the child and revert the steps int[] indices = { node.getIndex(child) }; node.remove(child); nodesWereRemoved(node, indices, new Object[] { child }); node.setSteps(completedSteps); nodeChanged(node); } }); } else { // add the undoable edit addUndoableTreeEdit(new UndoableTreeEdit() { public void redo() { // apply the new steps node.setSteps(newSteps); nodeChanged(node); } public void undo() { // revert to the previous steps node.setSteps(completedSteps); nodeChanged(node); } }); } } }