/** * Author: Christoph Hillebold <c.hillebold@student.tugraz.at> */ package at.iaik.suraq.parser; import java.io.BufferedReader; import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import at.iaik.suraq.exceptions.IncomparableTermsException; import at.iaik.suraq.exceptions.ParseError; import at.iaik.suraq.exceptions.WrongFunctionTypeException; import at.iaik.suraq.exceptions.WrongNumberOfParametersException; import at.iaik.suraq.main.SuraqOptions; import at.iaik.suraq.proof.VeriTToken; import at.iaik.suraq.proof.VeritProof; import at.iaik.suraq.proof.VeritProofNode; import at.iaik.suraq.resProof.ResProof; import at.iaik.suraq.sexp.Token; import at.iaik.suraq.smtlib.SMTLibObject; import at.iaik.suraq.smtlib.formula.AndFormula; import at.iaik.suraq.smtlib.formula.ArrayVariable; import at.iaik.suraq.smtlib.formula.DomainTerm; import at.iaik.suraq.smtlib.formula.DomainVariable; import at.iaik.suraq.smtlib.formula.EqualityFormula; import at.iaik.suraq.smtlib.formula.Formula; import at.iaik.suraq.smtlib.formula.NotFormula; import at.iaik.suraq.smtlib.formula.OrFormula; import at.iaik.suraq.smtlib.formula.PropositionalVariable; import at.iaik.suraq.smtlib.formula.Term; import at.iaik.suraq.smtlib.formula.UninterpretedFunction; import at.iaik.suraq.smtlib.formula.UninterpretedFunctionInstance; import at.iaik.suraq.smtlib.formula.UninterpretedPredicateInstance; import at.iaik.suraq.util.ImmutableSet; import at.iaik.suraq.util.Util; public class VeriTParser extends Parser { /** * */ private static final long serialVersionUID = 1L; private final BufferedReader reader; /** * two macro buffers: for Formulas and Terms */ private final HashMap<String, Formula> macroBufferFormula = new HashMap<String, Formula>(); private final HashMap<String, Term> macroBufferTerm = new HashMap<String, Term>(); // The constructor stores the objects in these functions. // Therefore we know, which type they are. private final Set<PropositionalVariable> propositionalVariables; private final Set<DomainVariable> domainVariables; private final Set<String> uninterpretedFunctionNames; private final Set<UninterpretedFunction> uninterpretedFunctions; /** * Finally the Proof is stored here. */ private final VeritProof proof = new VeritProof(); // Stacks are used to store variables of several Layers // a Layer is opened, when a ( occurs, and closed on ) /** * name of the macro, which is currently being defined */ private final Stack<String> macroNameStack = new Stack<String>(); /** * name of the command of the current Stack (and, or, =) */ private final Stack<String> commandStack = new Stack<String>(); /** * list of formulas of every stack, content of command */ private final Stack<ArrayList<Formula>> formulaStack = new Stack<ArrayList<Formula>>(); /** * list of terms of every stack, content of command */ private final Stack<ArrayList<Term>> termStack = new Stack<ArrayList<Term>>(); /** * the UninterpretedFunction defined on the current stack */ private final Stack<UninterpretedFunction> uninterpretedStack = new Stack<UninterpretedFunction>(); /** * the current line for debug output or Exceptions */ private int currentLine = 0; /** * The ResProof. If not null, we parse into this ResProof instead of into a * veriT proof. */ private ResProof resProof = null; /** * Map from literal IDs to partitions */ private Map<Integer, Integer> partitions = null; /** * Map from clauses of leaves to partitions */ private Map<ImmutableSet<Integer>, Integer> leafPartitions = null; /** * The VeriTParser needs all those Parameters to know which variable had * which type. The current version will not work if two UF have the same * name but an other count of parameters. In that case, please rename the * function. * * @param reader * a BufferedReader to Parse; it will be parsed line by line * @param oldTopLevelFormula * we extract variables etc. out of this * @param tseitinVars * @param noDependenceVarsCopies * @param noDependenceFunctionsCopies */ public VeriTParser(BufferedReader reader, Formula oldTopLevelFormula, Set<PropositionalVariable> tseitinVars, Collection<List<Term>> noDependenceVarsCopies, Map<Token, List<UninterpretedFunction>> noDependenceFunctionsCopies) { assert (reader != null); this.reader = reader; // extract all variables out of the old Formula Set<SMTLibObject> done = new HashSet<SMTLibObject>(); this.domainVariables = new HashSet<DomainVariable>(); oldTopLevelFormula.getDomainVariables(this.domainVariables, done); done.clear(); this.propositionalVariables = new HashSet<PropositionalVariable>(); oldTopLevelFormula.getPropositionalVariables( this.propositionalVariables, done); done.clear(); this.uninterpretedFunctionNames = new HashSet<String>(); oldTopLevelFormula.getUninterpretedFunctionNames( this.uninterpretedFunctionNames, done); done.clear(); this.uninterpretedFunctions = new HashSet<UninterpretedFunction>(); oldTopLevelFormula.getUninterpretedFunctions( this.uninterpretedFunctions, done); done.clear(); // add the tseitinVars propositionalVariables.addAll(tseitinVars); // look for noDependenceVarsCopies (DomainVariable, // PropositionalVariable) for (List<Term> terms : noDependenceVarsCopies) { for (Term term : terms) { if (term instanceof DomainVariable) this.domainVariables.add((DomainVariable) term); else if (term instanceof PropositionalVariable) this.propositionalVariables .add((PropositionalVariable) term); else if (term instanceof ArrayVariable) throw new RuntimeException( "Didn't expect array variables here."); else throw new RuntimeException( "Unknown type of noDependenceVarCopies"); } } // look for noDependenceFunctionCopies (UF, UP) for (List<UninterpretedFunction> ufs : noDependenceFunctionsCopies .values()) { for (UninterpretedFunction uf : ufs) { uninterpretedFunctions.add(uf); uninterpretedFunctionNames.add(uf.getName().toString()); } } } /** * Constructs a new <code>VeriTParser</code>. * * @param stream * @param inverseLiteralIds * @param resProof */ public VeriTParser(BufferedReader stream, int maxId, ResProof resProof, Map<Integer, Integer> partitions, Map<ImmutableSet<Integer>, Integer> leafPartitions) { this.resProof = resProof; this.uninterpretedFunctionNames = new HashSet<String>(); this.uninterpretedFunctions = new HashSet<UninterpretedFunction>(); this.domainVariables = new HashSet<DomainVariable>(); this.reader = stream; this.propositionalVariables = new HashSet<PropositionalVariable>( 2 * maxId); for (int count = 1; count <= maxId; count++) this.propositionalVariables.add(PropositionalVariable.create("v_" + Integer.toString(count))); this.partitions = new HashMap<Integer, Integer>(partitions); this.leafPartitions = new HashMap<ImmutableSet<Integer>, Integer>( leafPartitions); } private void cleanUp() { try { reader.close(); } catch (IOException e) { throw new RuntimeException(e); } macroBufferFormula.clear(); macroBufferTerm.clear(); propositionalVariables.clear(); domainVariables.clear(); uninterpretedFunctionNames.clear(); uninterpretedFunctions.clear(); macroNameStack.clear(); commandStack.clear(); formulaStack.clear(); termStack.clear(); uninterpretedStack.clear(); } /** * Parses the Proof line by line. Formula Expressions in the proof are * parsed in the method parseConclusion(...) */ @Override public void parse() { try { String line = reader.readLine(); while (line != null) { currentLine++; // count the lines line = line.trim(); if (line.length() == 0) { line = reader.readLine(); continue; } if (currentLine % 10000 == 0) { Util.printToSystemOutWithWallClockTimePrefix(" Now starting to parse line " + Util.largeNumberFormatter.format(currentLine)); if (SuraqOptions.getInstance().getCheckProofWhileParsing()) VeritProofNode.printCheckCountersAndTimers(); } if (currentLine % 100000 == 0) { Util.printMemoryInformation(); } // every line has to start with (set . if (!line.startsWith("(set ")) throw new ParseError(currentLine, line); // Extract the name of the ProofNode int pos_begin = "(set ".length(); int pos_end = line.indexOf(' ', pos_begin); String name = line.substring(pos_begin, pos_end); // extract the content of the ProofNode as 'rest' // rest = (input :conclusion (#1:(and (not #2:(= lambda_....)) // excl. surrounding brackets String rest = line.substring(pos_end + 2, line.length() - 1); // Extract the type of the ProofNode pos_end = rest.indexOf(' ', 0); // input,and, or,resolution, eq_transitive,... // you may add more types in VeriTToken Token type = VeriTToken.parse(rest.substring(0, pos_end)); // find :clauses, :iargs and :conclusion if exists int pos_clauses = rest.indexOf(":clauses ", 0); int pos_iargs = rest.indexOf(":iargs ", 0); int pos_conclusion = rest.indexOf(":conclusion ", 0); // prepare parsing them String clauses = null; String[] parsed_clauses = null; String iargs = null; Integer parsed_iargs = null; ArrayList<VeritProofNode> parsed_clauses2 = null; ArrayList<Formula> conclusions = null; // if :clauses exists, parse them if (pos_clauses > 0) { // the range of the :clauses depends on if a :iargs if found // after int until = (pos_iargs > 0) ? pos_iargs : pos_conclusion; // find the clauses and split them by whitespaces clauses = rest.substring( pos_clauses + ":clauses (".length(), until - 2); // REGEX: \s=A whitespace character: [ \t\n\x0B\f\r] parsed_clauses = clauses.split("\\s"); // find the given clauses in already existing ProofNodes and if (resProof == null) { parsed_clauses2 = new ArrayList<VeritProofNode>(); for (String parsed_clause : parsed_clauses) { VeritProofNode proofNode = proof .getProofNode(parsed_clause); assert (proofNode != null); parsed_clauses2.add(proofNode); } } } // if :iargs exists, parse them if (pos_iargs > 0) { iargs = rest.substring(pos_iargs + ":iargs (".length(), pos_conclusion - 2); parsed_iargs = Integer.parseInt(iargs); } // parse :conclusion // parse conclusion WITH brackets, because they are needed by // the parseConclusion(..)-Formula-Parser String conclusion = rest.substring(pos_conclusion + ":conclusion ".length(), rest.length() - 1); if (conclusion.length() == 0) conclusion = null; else conclusions = parseConclusion(conclusion); // System.out.println("Parsed Conclusions: "+ // conclusions.toString()); // DebugHelper.getInstance().stringtoFile(conclusions.toString(), // "~verit_z"+lines+".tmp"); // Store the ProofNode to the Proof assert (conclusions != null); if (resProof == null) { proof.addProofNode(name, type, conclusions, parsed_clauses2, parsed_iargs, !SuraqOptions .getInstance() .getDontRemoveLemmaSubproofs()); } else { assert (partitions != null); assert (leafPartitions != null); resProof.add(name, type, conclusions, parsed_clauses, partitions, leafPartitions); } // read next line line = reader.readLine(); } parsingSuccessfull = true; } catch (Exception e) { parsingSuccessfull = false; System.err.println("Error parsing line " + currentLine); e.printStackTrace(); throw new RuntimeException(e); } finally { System.out.println("There were " + currentLine + " lines."); cleanUp(); } } /** * This is a non-iterative (sequential) attempt to parse a Formula. This is * done to reduce the stack and to improve performance. Although the code is * more complicated and not that easy to understand. * * The formula can consist of And, Or, Equivalences, Not, * PropositionalVariables, DomainVariables, UninterpretedFunctions and * UninterpretedPredicates. It supports definitions of Function in the Form * #1:(...). These definitions can be used by writing #1. * * @param conclusion * the Formula to Parse * @param lineNumber * LineNumber, used when an Exception is thrown * @return * @throws ParseException * @throws IncomparableTermsException */ private ArrayList<Formula> parseConclusion(String conclusion) throws ParseException, IncomparableTermsException { try { // caches the length of the formula to parse int length = conclusion.length(); // store all conclusions on the TopLayer of the Formula ArrayList<Formula> conclusions = new ArrayList<Formula>(); // if this flag is set, we are currently parsing a macro_name: // -> the number between # and :) boolean macro_name = false; // a StringBuilder to store the current variable name StringBuilder str = new StringBuilder(); // iterate each character for (int i = 0; i < length; i++) { char cur = conclusion.charAt(i); // is this the end of a variable? if (cur == ' ' || cur == ')') { // was this a macro_name? e.g. #12 or #12) if (macro_name) { // find a pre-defined formula, that was #12 Formula formula = macroBufferFormula .get(str.toString()); Term term = macroBufferTerm.get(str.toString()); if (formula != null) { formulaStack.peek().add(formula); } if (term != null) { termStack.peek().add(term); } // we didn't find the macro if (term == null && formula == null) { throw new ParseException("Macro #" + str + " was not declared before (line=" + currentLine + ").", currentLine); } macro_name = false; } // it is a variable/UF/UP or a command else { String tmp = str.toString(); // is it a command? and, or, = if (tmp.equals("and") || tmp.equals("or") || tmp.equals("=") || tmp.equals("not")) { commandStack.pop(); commandStack.push(tmp); } // is it a variable/UF/UP? else if (tmp.length() > 0) { if (uninterpretedFunctionNames.contains(tmp)) { // its an uninterpreted function, save it uninterpretedStack.pop(); uninterpretedStack .push(findUninterpretedFunction(tmp)); } else { // its a variable Term term = findVariable(tmp); if (term instanceof PropositionalVariable) formulaStack.peek().add( (PropositionalVariable) term); // DomainVariable and PropositionalVariable termStack.peek().add(term); } } } } if (cur == ' ') { // variable end handled above } // we got an opening bracket else if (cur == '(') { // extend all Stacks and init them with null or a List formulaStack.push(new ArrayList<Formula>()); termStack.push(new ArrayList<Term>()); commandStack.push(null); macroNameStack.push(null); uninterpretedStack.push(null); } // we got a closing bracket else if (cur == ')') { // find out if this shall be stored to a macro: String macroname = null; macroNameStack.pop(); if (macroNameStack.size() != 0) // stack could be empty macroname = macroNameStack.peek(); // get the command String command = commandStack.pop(); // find out if there was an UF/UP UninterpretedFunction uf = uninterpretedStack.pop(); if (uf != null) { // pops the termStack and the formulaStack this.parseUF(uf, macroname); } // we got a closing bracket, and it is no UF/UP else { // we must have a command for each stack except the // first if (command != null) { // pops and peeks the formulaStack and the termStack this.parseCommand(command); // store macro if this was a macro if (macroname != null) { this.storeMacro(macroname, formulaStack.peek() .get(formulaStack.peek().size() - 1)); } } // just a list of variables else { ArrayList<Formula> formulaElements = formulaStack .pop(); if (commandStack.size() == 0) { // this is the :conclusion layer conclusions.addAll(formulaElements); } else { throw new ParseException( "We have a list of Variables without command.", currentLine); } } } } // this is the beginning of a macro-definition #2: // or a beginning of a macro-usage: #2 else if (cur == '#') { macro_name = true; } // this is the end of a macro-definition (name) else if (cur == ':') { macro_name = false; // store the macroname, so it can be stored when a closing // bracket is found macroNameStack.pop(); macroNameStack.push(str.toString()); } // a normal character else { str.append(cur); continue; } // clear the buffer: str.setLength(0); } return conclusions; } catch (Exception e) { throw new RuntimeException(e); } } /** * parses an UninterpretedFunction * * @param uf * the Uninterpreted Function to parse * @param macroname * null or the name of the UF, how it is stored * @throws WrongNumberOfParametersException * @throws WrongFunctionTypeException */ private void parseUF(UninterpretedFunction uf, String macroname) throws WrongNumberOfParametersException, WrongFunctionTypeException { // generate a new UninterpretedFunctionInstance or // UninterpretedPredicateInstance // with the parameters stored in formulaElements ArrayList<DomainTerm> formulaElements = new ArrayList<DomainTerm>(); for (Term tmp3 : termStack.pop()) formulaElements.add((DomainTerm) tmp3); formulaStack.pop(); Term newTerm = null; if (uf.getType().equalsString("Value")) { newTerm = UninterpretedFunctionInstance.create(uf, formulaElements); if (macroname != null) { // System.out.println("[M] Stored macro #" + macroname); macroBufferTerm.put(macroname, newTerm); } } else if (uf.getType().equalsString("Bool")) { newTerm = UninterpretedPredicateInstance .create(uf, formulaElements); // additionally add to formulaStack formulaStack.peek().add((UninterpretedPredicateInstance) newTerm); if (macroname != null) { // System.out.println("[M] Stored macro #" + macroname); macroBufferFormula.put(macroname, (UninterpretedPredicateInstance) newTerm); macroBufferTerm.put(macroname, newTerm); } } else assert (false); // add UF/UP to termStack termStack.peek().add(newTerm); } /** * Parses a command/expression/? (=, and, or, not) * * @param command * one of ["=", "and", "or", "not"] * @throws ParseException * @throws IncomparableTermsException */ private void parseCommand(String command) throws ParseException, IncomparableTermsException { ArrayList<Formula> formulaElements = formulaStack.pop(); ArrayList<Term> termElements = termStack.pop(); if (command.equals("not")) { if (formulaElements.size() != 1) throw new ParseException("Not has not 1 element. #Elements=" + formulaElements.size(), currentLine); formulaStack.peek().add(NotFormula.create(formulaElements.get(0))); } else if (command.equals("=")) { formulaStack.peek().add(EqualityFormula.create(termElements, true)); } else if (command.equals("and")) { formulaStack.peek().add(AndFormula.generate(formulaElements)); } else if (command.equals("or")) { formulaStack.peek().add(OrFormula.generate(formulaElements)); } else { throw new ParseException("Unknown Command", currentLine); } } /** * Stores a Macro to the Buffer * * @param macroName * name of the Macro * @param formula * Formula, by which the macro is defined */ private void storeMacro(String macroName, Formula formula) { // System.out.println("[M] Stored macro #" + macroName); macroBufferFormula.put(macroName, formula); if (formula instanceof Term) macroBufferTerm.put(macroName, (Term) formula); } /** * finds an UninterpretedFunction by searching in the already existing * variables. * * @param name * @return */ private UninterpretedFunction findUninterpretedFunction(String name) { if (this.uninterpretedFunctionNames.contains(name)) { UninterpretedFunction uf = null; for (UninterpretedFunction tmp : uninterpretedFunctions) { // NOTE: please do not use the same name for different count of // parameters or different types if (tmp.getName().equalsString(name)) { uf = UninterpretedFunction.create(tmp.getName(), tmp.getNumParams(), tmp.getType()); break; } } if (uf != null) return uf; } String err = "Unknown Variable '" + name + "' on parsing. Cannot decide if it is a propositional or domain variable."; System.err.println(err); throw new RuntimeException(err); } /** * find a variable in the already given variables * * @param name * name of the variable * @return */ private Term findVariable(String name) { if (this.domainVariables.contains(DomainVariable.create(name))) { return DomainVariable.create(name); } else if (this.propositionalVariables.contains(PropositionalVariable .create(name))) { return PropositionalVariable.create(name); } String err = "Unknown Variable '" + name + "' on parsing. Cannot decide if it is a propositional or domain variable."; System.err.println(err); throw new RuntimeException(err); } /** * Returns the finally parsed Proof as a VeriTProof. * * @return */ public VeritProof getProof() { return proof; } }