/* * Copyright (C) 2012-2013 University of Freiburg * * This file is part of SMTInterpol. * * SMTInterpol 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 3 of the License, or * (at your option) any later version. * * SMTInterpol 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 SMTInterpol. If not, see <http://www.gnu.org/licenses/>. */ package de.uni_freiburg.informatik.ultimate.smtinterpol.proofcheck; import java.io.IOException; import java.util.ArrayDeque; import java.util.HashMap; import de.uni_freiburg.informatik.ultimate.logic.AnnotatedTerm; import de.uni_freiburg.informatik.ultimate.logic.Annotation; import de.uni_freiburg.informatik.ultimate.logic.ApplicationTerm; import de.uni_freiburg.informatik.ultimate.logic.ConstantTerm; import de.uni_freiburg.informatik.ultimate.logic.FormulaLet; import de.uni_freiburg.informatik.ultimate.logic.FormulaLet.LetFilter; import de.uni_freiburg.informatik.ultimate.logic.LetTerm; import de.uni_freiburg.informatik.ultimate.logic.NonRecursive; import de.uni_freiburg.informatik.ultimate.logic.QuantifiedFormula; import de.uni_freiburg.informatik.ultimate.logic.Term; import de.uni_freiburg.informatik.ultimate.logic.TermVariable; import de.uni_freiburg.informatik.ultimate.logic.Theory; /** * This class is the front-end for converting an SMTInterpol proof to an * Isabelle proof. * * @author Christian Schilling */ class ProofConverter extends NonRecursive { // result stack private final ArrayDeque<Term> mResultStack; // appendable private final Appendable mAppendable; // true iff only the partial proof is given private final boolean mPartialProof; // term converter private final TermConverter mConverter; // theory private final Theory mTheory; // let handler private final LetHandler mLetHandler; // converters private final ResolutionConverter mResConverter; private final LemmaCCConverter mLemmaCCConverter; private final LemmaLAConverter mLemmaLAConverter; private final LemmaTrichotomyConverter mLemmaTrichotomyConverter; private final SubstitutionConverter mSubstConverter; private final SplitConverter mSplitConverter; private final TautologyConverter mTautologyConverter; private final RewriteConverter mRewriteConverter; // map for fast proof node conversion private final HashMap<String, IProofNode> mProofNode2Method; // map for assertions private HashMap<Term, Integer> mAssertion2index; // prefix lemmata use private static final String LET_LEMMA_PREFIX = "lemma"; // keywords for special rules protected static final String G_ONLY_SUBSTITUTE = "substitute, no print"; /** * @param appendable appendable to write the proof to * @param theory the theory * @param prettyOutput true iff output file is printed in more convenient * human-readable way * @param converter term converter * @param fastProofs true iff fast proofs shall be printed * @param partialProof true iff only the partial proof is given * @param lemmaAppendable appendable for the lemmata */ public ProofConverter(final Appendable appendable, final Theory theory, final boolean prettyOutput, final TermConverter converter, final boolean fastProofs, final boolean partialProof, final Appendable lemmaAppendable) { mAppendable = appendable; mTheory = theory; mPartialProof = partialProof; mResultStack = new ArrayDeque<Term>(); mConverter = converter; mLetHandler = new LetHandler(); final ComputationSimplifier simplifier = new ComputationSimplifier(); mResConverter = new ResolutionConverter(appendable, theory, converter, simplifier, fastProofs, lemmaAppendable); mLemmaCCConverter = new LemmaCCConverter(appendable, theory, converter, simplifier, fastProofs); mLemmaLAConverter = new LemmaLAConverter(appendable, theory, converter, simplifier, fastProofs, lemmaAppendable); mLemmaTrichotomyConverter = new LemmaTrichotomyConverter(appendable, theory, converter, simplifier); mSubstConverter = new SubstitutionConverter(appendable, theory, converter, simplifier); mSplitConverter = new SplitConverter(appendable, theory, converter, simplifier); mTautologyConverter = new TautologyConverter(appendable, theory, converter, simplifier); mRewriteConverter = new RewriteConverter(appendable, theory, converter, simplifier, lemmaAppendable); if (mPartialProof) { mProofNode2Method = new HashMap<String, IProofNode>( (int)(4 / 0.75) + 1); } else { mProofNode2Method = new HashMap<String, IProofNode>( (int)(7 / 0.75) + 1); } fillMap(); } /** * This method fills the proof node map with the proof node converters. * For each proof node a conversion object is added to a hash map, which * handles the conversion individually. * * NOTE: @rewrite and @intern only occur within @eq and have special * treatment there. */ private void fillMap() { mProofNode2Method.put("@res", new ResNode()); mProofNode2Method.put("@lemma", new LemmaNode()); mProofNode2Method.put("@asserted", new AssertedNode()); mProofNode2Method.put("@tautology", new TautologyNode()); if (!mPartialProof) { mProofNode2Method.put("@eq", new EqNode()); mProofNode2Method.put("@split", new SplitNode()); mProofNode2Method.put("@clause", new ClauseNode()); } } // [start] general proof conversion // /** * This method converts the SMTInterpol proof to an Isabelle proof. * * The proof is pushed on a stack and then iteratively split up to its * sub-terms, based on the {@link NonRecursive} and * {@link de.uni_freiburg.informatik.ultimate.logic.NonRecursive.Walker} * classes. * * The proof is abbreviated using let expressions (see * {@link ProofWalker#walk(NonRecursive converter, LetTerm term)} for * details). * * @param proof the proof passed by the solver * @param assertion2index map from assertions to index */ public void convert(Term proof, final HashMap<Term, Integer> assertion2index) { mAssertion2index = assertion2index; mResultStack.clear(); mLetHandler.clear(); mConverter.switchProofMode(); proof = new FormulaLet( new FormulaLet.LetFilter() { @Override public boolean isLettable(Term term) { return ((term instanceof ApplicationTerm) && ((ApplicationTerm)term).getFunction(). getReturnSort().getName() == "@Proof"); } }).let(proof); writeString("proof -\n"); // start conversion enqueueWalker(new ProofWalker(proof, null)); run(); assert ((mResultStack.size() == 1) && (mResultStack.getFirst() == mTheory.mFalse)) : "The proof must result in 'false'."; writeString("thus ?thesis by assumption\nqed\n"); // reset converter to checker mode mConverter.switchProofMode(); } /** * This walker appends a given string to the output when it is taken from * the stack. */ private class StringWalker implements NonRecursive.Walker { // string object private final String mString; /** * @param string the string to be appended */ public StringWalker(String string) { mString = string; } @Override public void walk(NonRecursive converter) { writeString(mString); } } /** * This walker is used for binding a lemma for scoped sub-proofs * (resolution and substitution). The Isabelle translation adds a pair * of scoping parentheses and hence the binding of the last result cannot * be used later on. The solution is to bind the lemma immediately after * the parentheses without remarkable effort. */ private class ScopingLemmaWalker implements NonRecursive.Walker { // string object private final TermVariable mLemmaVar; /** * @param lemmaVar term variable of lemma binding if any, else null */ public ScopingLemmaWalker(TermVariable lemmaVar) { mLemmaVar = lemmaVar; } @Override public void walk(NonRecursive engine) { writeString("note "); writeString(LET_LEMMA_PREFIX); writeString(mLetHandler.getNumberString(mLemmaVar)); writeString(" = this\n"); } } /** * This walker handles the term conversion on the stack. * It discriminates between the term's sub-type. The only possible terms * are application terms starting with an '@' (proof nodes) and let * bindings/variables, which again only abbreviate those application terms * (see {@link #walk(NonRecursive converter, LetTerm term)} for details). * * NOTE: All conversion outputs are in reversed order due to stack usage. */ private class ProofWalker extends NonRecursive.TermWalker { // TermVariable of lemma binding if any, else null private final TermVariable mLemma; /** * @param term the term to be converted next * @param lemma TermVariable of lemma binding if any, else null */ ProofWalker(Term term, TermVariable lemma) { super(term); mLemma = lemma; } /** * A constant term is not expected in a proof. The reason is that the * proof is only unfolded until a fixed level (proof nodes), whereas * the base terms are converted using the {@link TermConverter}. */ @Override public void walk(NonRecursive converter, ConstantTerm term) { throw new IllegalArgumentException( "ConstantTerm is not supported at this level!"); } /** * An annotated term is not expected in a proof. They only occur inside * the proof nodes and so are handled there. */ @Override public void walk(NonRecursive converter, AnnotatedTerm term) { throw new IllegalArgumentException( "AnnotatedTerm is not supported at this level!"); } /** * <quantifiers> * This method has to be implemented when quantifiers can be handled. */ @Override public void walk(NonRecursive converter, QuantifiedFormula term) { throw new UnsupportedOperationException( "Quantifiers are currently not supported."); } /** * This method converts an application term (proof node). Internally it * just calls the specific sub-method. The following proof nodes are * supported: * * - @res (resolution) * - @lemma (lemma, both CC and LA) * - @asserted (assertion from input, normalized in partial proof) * - @tautology (tautology) * - @eq (substitution, including equalities @rewrite and @intern) * - @split (split from a disjunction) * - @clause (clause flattening) * * NOTE: @rewrite and @intern only occur within @eq and have special * treatment there. * * @param converter non-recursive converter * @param term application term */ @Override public void walk(NonRecursive converter, ApplicationTerm term) { final String name = term.getFunction().getName(); final Term[] parameters = term.getParameters(); assert (mProofNode2Method.get(name) != null); mProofNode2Method.get(name).convert( converter, parameters, mLemma); } // [start] let term conversion // /** * This method converts a let term. A let term is a mapping from term * variables (abbreviations) to terms and a scoped sub-term where the * mapping holds. Each abbreviation is considered iteratively. * * To avoid proving sub-proofs that occur more than once for several * times, they are collected by the {@link FormulaLet} and abbreviated. * If a let is found, the sub-proof is pushed to the stack here and * later the result is remembered and associated with the variable * instead of the whole proof. After the result has been computed a * {@link #walk(NonRecursive converter, LetTerm term).LetWalker} * observes it and does the variable binding. * * The translation to Isabelle really treats the abbreviation as a * lemma and so the result of the sub-proof gets an associated name. * Isabelle then only has to look up the proven lemma in the own * variable collection if it occurs later on. * * @param converter non-recursive converter * @param term let term */ @Override public void walk(NonRecursive converter, LetTerm term) { /** * This Walker handles let terms. Only a proof node * (ApplicationTerm starting with an '@') is regarded as a lemma * by the {@link LetFilter} and hence abbreviated. * * See {@link #walk(NonRecursive converter, LetTerm term)} for more * details. */ class LetWalker implements NonRecursive.Walker { // term variable private final TermVariable mVariable; /** * @param variable the term variable the lemma shall be * assigned to */ public LetWalker(TermVariable variable) { mVariable = variable; } /** * The last element on the result stack is assigned to the * lemma name. */ @Override public void walk(NonRecursive converter) { assert (!mResultStack.isEmpty()); final Term lemma = mResultStack.pop(); assert (lemma != null); mLetHandler.add(mVariable, lemma); } } /** * This walker internally translates an equality proof node for * later substitution. Some rewrites are never used and hence are * not translated. To have the rule annotation present in later * substitution steps, the whole proof node is stored for these * cases. */ class EqualityWalker implements NonRecursive.Walker { // rewrite node private final ApplicationTerm mNode; // true iff node is @rewrite, false iff node is @intern private final boolean mIsRewrite; // TermVariable of lemma binding if any, else null private final TermVariable mLemmaVar; /** * @param node rewrite node * @param isRewrite true = @rewrite, false = @intern * @param eqLemmaVar term variable of lemma binding, else null */ public EqualityWalker(final ApplicationTerm node, final boolean isRewrite, final TermVariable eqLemmaVar) { mNode = node; mIsRewrite = isRewrite; mLemmaVar = eqLemmaVar; } @Override public void walk(NonRecursive engine) { final String proof; if (mIsRewrite) { proof = convertRewrite(mNode.getParameters()); } else { proof = convertIntern(mNode.getParameters()); } // the equality was pushed to the result stack assert ((mResultStack.size() > 0) && (mResultStack.getFirst() instanceof ApplicationTerm)); final ApplicationTerm equality = (ApplicationTerm)mResultStack.pop(); // some proofs should not be translated if (proof != G_ONLY_SUBSTITUTE) { // NOPMD startSubProof(false, mLemmaVar); mConverter.convert(equality); writeString("\" by "); writeString(proof); if (mIsRewrite) { mRewriteConverter.writeLemma(); } } else if (mIsRewrite) { mRewriteConverter.forgetLemma(); } // memorize result mResultStack.push(mNode); } } converter.enqueueWalker(new ProofWalker(term.getSubTerm(), mLemma)); final TermVariable[] variables = term.getVariables(); final Term[] values = term.getValues(); assert (variables.length == values.length); for (int i = variables.length - 1; i >= 0; --i) { final TermVariable variable = variables[i]; converter.enqueueWalker(new LetWalker(variable)); // only proof nodes are abbreviated if (values[i] instanceof ApplicationTerm) { final ApplicationTerm aValue = (ApplicationTerm)values[i]; final String name = aValue.getFunction().getName(); // special handling of equality proof nodes if (name == "@rewrite") { converter.enqueueWalker( new EqualityWalker(aValue, true, variable)); } else if (name == "@intern") { converter.enqueueWalker( new EqualityWalker(aValue, false, variable)); } else { converter.enqueueWalker(new ProofWalker(aValue, variable)); } } else { assert (values[i] instanceof LetTerm); converter.enqueueWalker(new ProofWalker(values[i], variable)); } } } /** * This method recalls lemmata that have already been proven. This * avoids reproving the same theorem for several times and can only * occur wherever a proof node is expected, e.g., in a resolution or * substitution node. * * <quantifiers> * This method might have to be modified when quantifiers can be * handled. * * @param converter non-recursive converter * @param variable term variable */ @Override public void walk(NonRecursive converter, TermVariable variable) { final Term lemma = mLetHandler.getLemma(variable); assert (lemma != null); mResultStack.push(lemma); writeString("note "); writeString(LET_LEMMA_PREFIX); writeString(mLetHandler.getNumberString(variable)); writeString("\n"); } // [end] let term conversion // } // [end] general proof conversion // // [start] proof node conversion // /** * This method converts a rewrite proof node (@rewrite). * That is, a trivial equality later used to rewrite a term. * * The result is received by the substitution converter. It is a string * with the rule's proof if the rule should be translated and a special * keyword if the rule should be ignored. The reason why it is not directly * written to the output is that there exist rewrite rules that are * duplicate and hence can be ignored. This is detected by the substitution * converter, which then decides if the rule should be written or not. * * The equality is pushed to the result stack nevertheless and the * substitution converter pops it again to run the substitution. * * @param parameters rewrite parameters * @return string with proof iff rewrite rule was translated, else null */ private String convertRewrite(Term[] parameters) { assert ((parameters.length == 1) && (parameters[0] instanceof AnnotatedTerm)); final AnnotatedTerm rewrite = (AnnotatedTerm)parameters[0]; assert (rewrite.getAnnotations().length == 1); final String annotation = rewrite.getAnnotations()[0].getKey(); assert ((rewrite.getSubterm() instanceof ApplicationTerm) && (((ApplicationTerm)rewrite.getSubterm()).getFunction(). getName() == "=") && (((ApplicationTerm)rewrite.getSubterm()).getParameters().length == 2)); final ApplicationTerm equality = (ApplicationTerm)rewrite.getSubterm(); // proof rule final String proof = mRewriteConverter.convert(equality, annotation); assert (proof != null); // memorize result (can be set to 'False') mResultStack.push(equality); // return the proof as a string (can be a special keyword) return proof; } /** * This method converts an internal rewrite proof node (@intern). * * @param parameters equality parameters * @return the proof string */ private String convertIntern(Term[] parameters) { assert ((parameters.length == 1) && (parameters[0] instanceof ApplicationTerm)); final ApplicationTerm equality = (ApplicationTerm)parameters[0]; assert (equality.getFunction().getName() == "="); // memorize result mResultStack.push(equality); return "auto\n"; } /** * This method is used to insert a lemma binding in sub-proofs. * * @param continueProof true iff the proof is continued * @param lemmaVar term variable of lemma binding if any, else null */ private void startSubProof(final boolean continueProof, final TermVariable lemmaVar) { // lemma binding if (lemmaVar != null) { // NOPMD final String lemmaNumber = mLetHandler.getNumberString(lemmaVar); if (continueProof) { writeString("hence "); } else { writeString("have "); } writeString(LET_LEMMA_PREFIX); writeString(lemmaNumber); writeString(": \""); } else { // no lemma binding if (continueProof) { writeString("hence \""); } else { writeString("have \""); } } } /** * This interface is implemented by all proof node converters. */ private interface IProofNode { /** * @param converter non-recursive converter * @param parameters parameters of the split * @param lemmaVar term variable of lemma binding if any, else null */ void convert(NonRecursive converter, Term[] parameters, TermVariable lemmaVar); } /** * This class converts a resolution proof node (@res). */ private class ResNode implements IProofNode { /** * This method converts a resolution proof node (@res). * * {@inheritDoc} */ @Override public void convert(NonRecursive converter, Term[] parameters, TermVariable lemmaVar) { /** * This walker handles resolution steps. The resolution in * SMTInterpol has n > 2 arguments and is used as a chained * (binary) rule. The result of the binary application is always * the first parameter of the next one, meaning n-1 applications * per whole resolution term. * * Since the arguments themselves can be complicated sub-proofs, * they are computed separately by assumption and then simply the * last two results are taken from the result stack. These are * then used to compute the result of the binary resolution rule. */ class ResolutionWalker implements NonRecursive.Walker { // pivot term private final Term mPivot; /** * @param pivot the pivot term */ public ResolutionWalker(Term pivot) { mPivot = pivot; } @Override public void walk(NonRecursive converter) { assert (mResultStack.size() > 1); final Term second = mResultStack.pop(); final Term first = mResultStack.pop(); assert ((first != null) && (second != null) && (mPivot != null)); // compute resolution result writeString("ultimately\nhave \""); final Term result = mResConverter.convert(first, second, mPivot); // memorize result mResultStack.push(result); } } assert (parameters.length > 1); if (lemmaVar != null) { converter.enqueueWalker(new ScopingLemmaWalker(lemmaVar)); } converter.enqueueWalker(new StringWalker("}\n")); // terms 2..n with pivot annotation for (int i = parameters.length - 1; i > 0; --i) { assert (parameters[i] instanceof AnnotatedTerm); final AnnotatedTerm next = (AnnotatedTerm)parameters[i]; final Annotation[] annotation = next.getAnnotations(); assert ((annotation.length == 1) && (annotation[0].getKey() == ":pivot") && (annotation[0].getValue() instanceof Term)); // resolution with pivot converter.enqueueWalker(new ResolutionWalker( (Term)annotation[0].getValue())); // next term converter.enqueueWalker( new ProofWalker(next.getSubterm(), null)); // combination keyword in Isabelle converter.enqueueWalker(new StringWalker("moreover\n")); } // first term converter.enqueueWalker(new ProofWalker(parameters[0], null)); writeString("{\n"); } } /** * This class converts a lemma proof node (@lemma). */ private class LemmaNode implements IProofNode { /** * This method converts a lemma proof node (@lemma). * * @param converter non-recursive converter * @param parameters lemma parameters * @param lemmaVar term variable of lemma binding if any, else null */ @Override public void convert(NonRecursive converter, Term[] parameters, TermVariable lemmaVar) { assert ((parameters.length == 1) && (parameters[0] instanceof AnnotatedTerm)); final AnnotatedTerm lemma = (AnnotatedTerm)parameters[0]; assert (lemma.getAnnotations().length == 1); final Annotation annotation = lemma.getAnnotations()[0]; final String key = annotation.getKey(); final ApplicationTerm result; // print header with possible lemma binding startSubProof(false, lemmaVar); // CC lemma (result may change due to reordering) if (key == ":CC") { result = mLemmaCCConverter.convert(lemma); } else if (key == ":LA") { // LA lemma assert (lemma.getSubterm() instanceof ApplicationTerm); result = (ApplicationTerm)lemma.getSubterm(); assert ((result.getFunction() == mTheory.mOr) && (annotation.getValue() instanceof Object[])); mLemmaLAConverter.convert(result, (Object[])annotation.getValue()); } else if (key == ":trichotomy") { // trichotomy lemma assert (lemma.getSubterm() instanceof ApplicationTerm); result = mLemmaTrichotomyConverter.convert( (ApplicationTerm)lemma.getSubterm()); } else { throw new IllegalArgumentException( "The lemma is not supported."); } // memorize result mResultStack.push(result); } } /** * This class converts an assertion proof node (@asserted). */ private class AssertedNode implements IProofNode { /** * This method converts an assertion proof node (@asserted). * * @param converter non-recursive converter * @param parameters assertion parameters * @param lemmaVar term variable of lemma binding if any, else null */ @Override public void convert(final NonRecursive converter, final Term[] parameters, final TermVariable lemmaVar) { assert (parameters.length == 1); Term term = parameters[0]; /* * partial proof mode * NOTE: uses auto and adds mod and div definitions */ if (mPartialProof) { assert ((term instanceof AnnotatedTerm) && (((AnnotatedTerm)term).getAnnotations()[0].getKey() == ":input")); final AnnotatedTerm aTerm = (AnnotatedTerm)term; final String name = (String)aTerm.getAnnotations()[0].getValue(); term = ((AnnotatedTerm)term).getSubterm(); startSubProof(false, lemmaVar); mConverter.convert(term); writeString("\" using "); writeString(name); writeString(" unfolding SMTmod_def SMTdiv_def by auto\n"); // memorize result without annotation mResultStack.push(term); } else { // extended proof mode assert (mAssertion2index.get(term) != null); if (lemmaVar == null) { writeString("note "); writeString(ProofChecker.ASSERTION_PREFIX); writeString(Integer.toString(mAssertion2index.get(term))); writeString("\n"); } else { writeString("note "); writeString(LET_LEMMA_PREFIX); writeString(mLetHandler.getNumberString(lemmaVar)); writeString(" = "); writeString(ProofChecker.ASSERTION_PREFIX); writeString(Integer.toString(mAssertion2index.get(term))); writeString("\n"); } // memorize result with annotation (will be split away) mResultStack.push(term); } } } /** * This class converts a tautology proof node (@tautology). */ private class TautologyNode implements IProofNode { /** * This method converts a tautology proof node (@tautology). * * @param converter non-recursive converter * @param parameters tautology parameters */ @Override public void convert(NonRecursive converter, Term[] parameters, TermVariable lemmaVar) { assert ((parameters.length == 1) && (parameters[0] instanceof AnnotatedTerm)); final AnnotatedTerm tautology = (AnnotatedTerm)parameters[0]; assert ((tautology.getAnnotations().length == 1) && (tautology.getSubterm() instanceof ApplicationTerm)); final String annotation = tautology.getAnnotations()[0].getKey(); final ApplicationTerm result = (ApplicationTerm)tautology.getSubterm(); // convert tautology rule startSubProof(false, lemmaVar); mTautologyConverter.convert(result, annotation); // memorize result mResultStack.push(result); } } /** * This class converts a substitution proof node (@eq). */ private class EqNode implements IProofNode { /** * This method converts a substitution proof node (@eq). * * @param converter non-recursive converter * @param parameters substitution parameters * @param lemmaVar term variable of lemma binding if any, else null */ @Override public void convert(NonRecursive converter, Term[] parameters, TermVariable lemmaVar) { /** * This walker handles substitution steps. The substitution in * SMTInterpol has n > 2 arguments and is used as a chained * (binary) rule. The result of the binary application is always * the first parameter of the next one, meaning n-1 applications * per whole substitution term. * * Since the arguments themselves can be complicated sub-proofs, * they are computed separately by assumption and then simply the * last two results are taken from the result stack. These are then * used to compute the result of the binary substitution rule. * * NOTE: Currently, the rewrite steps are immediately available, * so it would be possible to directly translate all the * steps directly without using the stack and walkers. * It was a design decision to still support rewrite rules * from other sources (e.g., from another sub-proof). */ class SubstitutionWalker implements NonRecursive.Walker { // the equality proof node private final Term mEqualityNode; /** * @param equalityNode proof node that justifies an equality */ public SubstitutionWalker(Term equalityNode) { mEqualityNode = equalityNode; } @Override public void walk(NonRecursive converter) { // equality proof node final ApplicationTerm node; // let lemma if term is a TermVariable, else null final TermVariable lemma; /* * The term is an already proven lemma. * Since it can be useless, it is handled similar to a new * proof node. * * For such equality nodes the lemma is stored differently. * Instead of only the result, the whole proof node is * stored in order to have the rule annotation present. */ if (mEqualityNode instanceof TermVariable) { lemma = (TermVariable)mEqualityNode; final Term lemmaTerm = mLetHandler.getLemma(lemma); assert ((lemmaTerm != null) && (lemmaTerm instanceof ApplicationTerm)); node = (ApplicationTerm)lemmaTerm; } else { lemma = null; assert (mEqualityNode instanceof ApplicationTerm); node = (ApplicationTerm)mEqualityNode; } /* * Here the term is an equality proof node * (@rewrite/@intern). An equality can be useless, but * unfortunately this cannot be predicted. This is why the * substitution is applied first and if no change occurred, * the rule is ignored. */ final String name = node.getFunction().getName(); final String proof; // get proof result from equality rule (not written yet) if (name == "@rewrite") { proof = convertRewrite(node.getParameters()); } else if (name == "@intern") { proof = convertIntern(node.getParameters()); } else { throw new IllegalArgumentException("The proof node " + name + "is not supported."); } // the equality was pushed to the result stack assert ((mResultStack.size() > 0) && (mResultStack.getFirst() instanceof ApplicationTerm)); final ApplicationTerm equality = (ApplicationTerm)mResultStack.pop(); // apply substitution and see if there was a change assert ((equality.getFunction().getName() == "=") && (equality.getParameters().length == 2)); assert (mResultStack.size() > 0); final Term first = mResultStack.pop(); // apply substitution final Term result = mSubstConverter.convert(first, equality); assert (result != null); /* * only write the rules if there was a change * and the rule should in general be translated */ if ((result != first) && (proof != G_ONLY_SUBSTITUTE)) { // NOPMD // write equality proof writeString("moreover\n"); // new proof node if (lemma == null) { startSubProof(false, null); mConverter.convert(equality); writeString("\" by "); writeString(proof); // write stored pattern lemma if (name == "@rewrite") { mRewriteConverter.writeLemma(); } } else { // lemma already proven writeString("note "); writeString(LET_LEMMA_PREFIX); writeString(mLetHandler.getNumberString(lemma)); writeString("\n"); // forget stored pattern lemma if (name == "@rewrite") { mRewriteConverter.forgetLemma(); } } /* * write substitution proof * * NOTE: no lemma binding here due to scoping */ writeString("ultimately\nhave \""); mConverter.convert(result); // whole term was substituted if (result == equality.getParameters()[1]) { writeString("\" by (rule HOL.rev_iffD1)\n"); } else { // only parts of the term were substituted writeString("\" by (rule eq)\n"); } } else { // forget stored pattern lemma if (name == "@rewrite") { mRewriteConverter.forgetLemma(); } } // memorize result mResultStack.push(result); } } assert (parameters.length > 1); // lemma binding for scoping if (lemmaVar != null) { converter.enqueueWalker(new ScopingLemmaWalker(lemmaVar)); } /* * NOTE: It can happen that no substitution is written in the end. * In this case the output is just '{\n}\n', so the scoping * parentheses are still written. This is hard to avoid. */ converter.enqueueWalker(new StringWalker("}\n")); for (int i = parameters.length - 1; i > 0; --i) { converter.enqueueWalker(new SubstitutionWalker(parameters[i])); } converter.enqueueWalker(new ProofWalker(parameters[0], null)); writeString("{\n"); } } /** * This class converts a split proof node (@split). */ private class SplitNode implements IProofNode { /** * This method converts a split proof node (@split). * * @param converter non-recursive converter * @param parameters parameters of the split * @param lemmaVar term variable of lemma binding if any, else null */ @Override public void convert(NonRecursive converter, Term[] parameters, TermVariable lemmaVar) { /** * This walker handles @split steps. */ class SplitWalker implements NonRecursive.Walker { // result of the split private final ApplicationTerm mResult; // annotation private final String mAnnotation; // TermVariable of lemma binding if any, else null private final TermVariable mLemmaVar; /** * @param result the result of the split node * @param annotation the annotation * @param splitLemmaVar term variable of lemma, else null */ public SplitWalker(ApplicationTerm result, String annotation, TermVariable splitLemmaVar) { mResult = result; mAnnotation = annotation; mLemmaVar = splitLemmaVar; } @Override public void walk(NonRecursive converter) { assert (mResultStack.getFirst() instanceof ApplicationTerm); final ApplicationTerm negDisjunction = (ApplicationTerm)mResultStack.pop(); startSubProof(true, mLemmaVar); mSplitConverter.convert(negDisjunction, mResult, mAnnotation); // memorize result mResultStack.push(mResult); } } assert ((parameters.length == 2) && (parameters[0] instanceof AnnotatedTerm)); final AnnotatedTerm split = (AnnotatedTerm)parameters[0]; assert (split.getAnnotations().length == 1); final String annotation = split.getAnnotations()[0].getKey(); final Term subterm = split.getSubterm(); assert (parameters[1] instanceof ApplicationTerm); final ApplicationTerm result = (ApplicationTerm)parameters[1]; // catch result converter.enqueueWalker( new SplitWalker(result, annotation, lemmaVar)); // convert sub-term first converter.enqueueWalker(new ProofWalker(subterm, null)); } } /** * This class converts a clause proof node (@clause). * * NOTE: This node is a relic in the SMTInterpol proof, so just unpack it. * Unused code is commented out. */ private class ClauseNode implements IProofNode { /** * This method converts a clause proof node (@clause). * * @param converter non-recursive converter * @param parameters clause parameters * @param lemmaVar term variable of lemma binding if any, else null */ @Override public void convert(NonRecursive converter, Term[] parameters, TermVariable lemmaVar) { // class ClauseWalker implements NonRecursive.Walker { // final Term m_result; // // TermVariable of lemma binding if any, else null // final TermVariable m_lemmaVar; // // /** // * @param result the result of the flattening // */ // public ClauseWalker(Term result, TermVariable clauseLemmaVar) { // m_result = result; // m_lemmaVar = clauseLemmaVar; // } // // @Override // public void walk(NonRecursive engine) { // assert (! m_resultStack.isEmpty()); // final Term origin = m_resultStack.pop(); // // // convert rule // startSubProof(true, m_lemmaVar); // m_converter.convert(m_result); // writeString("\" by auto (* @clause *)\n"); // // // memorize result // m_resultStack.push(m_result); // } // } assert (parameters.length == 2); // converter.enqueueWalker(new ClauseWalker(parameters[1], lemmaVar)); converter.enqueueWalker(new ProofWalker(parameters[0], lemmaVar)); } } // [end] proof node conversion // // [start] output related // /** * This method writes a string to the appendable. * * @param string string that needs not use the stack * @throws RuntimeException thrown if an IOException is caught */ private void writeString(String string) { try { mAppendable.append(string); } catch (final IOException e) { throw new RuntimeException("Appender throws IOException", e); } } // [end] output related // }