package at.iaik.suraq.parser; import java.io.Serializable; 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 at.iaik.suraq.exceptions.IncomparableTermsException; import at.iaik.suraq.exceptions.InvalidIndexGuardException; import at.iaik.suraq.exceptions.InvalidParametersException; import at.iaik.suraq.exceptions.InvalidValueConstraintException; import at.iaik.suraq.exceptions.ParseError; import at.iaik.suraq.exceptions.SuraqException; import at.iaik.suraq.sexp.SExpression; import at.iaik.suraq.sexp.SExpressionConstants; import at.iaik.suraq.sexp.Token; import at.iaik.suraq.smtlib.Z3Proof; import at.iaik.suraq.smtlib.formula.AndFormula; import at.iaik.suraq.smtlib.formula.ArrayIte; import at.iaik.suraq.smtlib.formula.ArrayProperty; import at.iaik.suraq.smtlib.formula.ArrayRead; import at.iaik.suraq.smtlib.formula.ArrayTerm; import at.iaik.suraq.smtlib.formula.ArrayVariable; import at.iaik.suraq.smtlib.formula.ArrayWrite; import at.iaik.suraq.smtlib.formula.DomainIte; 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.FormulaTerm; import at.iaik.suraq.smtlib.formula.FunctionMacro; import at.iaik.suraq.smtlib.formula.ImpliesFormula; import at.iaik.suraq.smtlib.formula.NotFormula; import at.iaik.suraq.smtlib.formula.OrFormula; import at.iaik.suraq.smtlib.formula.PropositionalConstant; import at.iaik.suraq.smtlib.formula.PropositionalFunctionMacro; import at.iaik.suraq.smtlib.formula.PropositionalFunctionMacroInstance; import at.iaik.suraq.smtlib.formula.PropositionalIte; import at.iaik.suraq.smtlib.formula.PropositionalTerm; import at.iaik.suraq.smtlib.formula.PropositionalVariable; import at.iaik.suraq.smtlib.formula.Term; import at.iaik.suraq.smtlib.formula.TermFunctionMacro; import at.iaik.suraq.smtlib.formula.TermFunctionMacroInstance; 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.smtlib.formula.XorFormula; public abstract class SMTLibParser extends Parser implements Serializable { /** * */ private static final long serialVersionUID = 1L; /** * maps containing proof references */ protected final Map<Token, Z3Proof> proofs = new HashMap<Token, Z3Proof>(); protected final Map<Token, Formula> formulas = new HashMap<Token, Formula>(); protected final Map<Token, Term> terms = new HashMap<Token, Term>(); protected final List<PropositionalVariable> tseitinVariables = new ArrayList<PropositionalVariable>(); private static final int GLOBAL_PARTITION = -1; /** * constants for let expression types */ public static final char REF_PROOF = '@'; public static final char REF_FORMULA = '$'; public static final char REF_TERM = '?'; /** * The list of control variables found during parsing */ protected List<PropositionalVariable> controlVariables = new ArrayList<PropositionalVariable>(); /** * The list of Boolean variables found during parsing */ protected Set<PropositionalVariable> boolVariables = new HashSet<PropositionalVariable>(); /** * The list of domain variables found during parsing */ protected Set<DomainVariable> domainVariables = new HashSet<DomainVariable>(); /** * The list of array variables found during parsing */ protected Set<ArrayVariable> arrayVariables = new HashSet<ArrayVariable>(); /** * The list of uninterpreted functions found during parsing */ protected Set<UninterpretedFunction> functions = new HashSet<UninterpretedFunction>(); /** * The set of variables on which control logic may <em>not</em> depend */ protected Set<Token> noDependenceVariables = new HashSet<Token>(); /** * The function macros found during parsing, indexed by name tokens */ protected Map<Token, FunctionMacro> macros = new HashMap<Token, FunctionMacro>(); /** * The root of the s-expression to be parsed. */ protected SExpression rootExpr = null; /** * A map of current local variables while parsing a function macro. */ protected Map<Token, SExpression> currentLocals = null; /** * The set of universally quantified variables in current scope. */ protected Collection<DomainVariable> currentUVars = null; protected Map<Token, Formula> letFormulaDefinitions = new HashMap<Token, Formula>(); protected Map<Token, Term> letTermDefinitions = new HashMap<Token, Term>(); /** * Parses a given s-expression into a formula. * * @param expression * the expression to parse. * @return the formula resulting from the given expression. * @throws ParseError * if parsing fails. */ public Formula parseFormulaBody(SExpression expression) throws ParseError { if (isLet(expression)) return handleLet(expression); if (expression.toString().charAt(0) == SMTLibParser.REF_FORMULA) { // resolve reference with LUT assert (expression instanceof Token); Token pureKey = Token.generate(expression.toString().substring(1)); Formula formula = this.formulas.get(pureKey); if (formula == null) throw new ParseError(expression, "could not find a matching formula-LUT-entry!"); return formula; } if (isPropositionalConstant(expression)) { PropositionalConstant constant = null; if (expression.equals(SExpressionConstants.TRUE)) constant = PropositionalConstant.create(true); else if (expression.equals(SExpressionConstants.FALSE)) constant = PropositionalConstant.create(false); else throw new ParseError(expression, "Unexpected Error parsing propositional constant!"); return constant; } SExpression type = isLocalVariable(expression); // takes precedence over // global variables. if (type != null) { if (!(type.equals(SExpressionConstants.BOOL_TYPE) || type .equals(SExpressionConstants.CONTROL_TYPE))) throw new ParseError(expression, "Found non-Boolean local variable where Bool sort was expected: " + expression.toString()); int partition = getPartitionBoolVariable(expression); return PropositionalVariable.create((Token) expression, partition); } if (isPropositionalVariable(expression)) { int partition = getPartitionBoolVariable(expression); return PropositionalVariable.create((Token) expression, partition); } if (isTseitinVariable(expression)) { PropositionalVariable var = PropositionalVariable .create((Token) expression); if (!tseitinVariables.contains(var)) tseitinVariables.add(var); return var; } if (isFormulaLetKey(expression)) { Formula value = letFormulaDefinitions.get(expression); assert (value != null); return value; } Token operator = isBooleanCombination(expression); if (operator != null) { if (operator.equals(SExpressionConstants.NOT)) { if (expression.getChildren().size() != 2) throw new ParseError(expression, "Expected exactly 1 expression after 'not'."); Formula negatedFormula = parseFormulaBody(expression .getChildren().get(1)); return NotFormula.create(negatedFormula); } if (operator.equals(SExpressionConstants.AND)) { if (expression.getChildren().size() < 2) throw new ParseError(expression, "Expected at least 1 expression after 'and'."); List<Formula> formulaList = new ArrayList<Formula>(expression .getChildren().size() - 1); for (SExpression child : expression.getChildren().subList(1, expression.getChildren().size())) { formulaList.add(parseFormulaBody(child)); } return AndFormula.generate(formulaList); } if (operator.equals(SExpressionConstants.OR)) { if (expression.getChildren().size() < 2) throw new ParseError(expression, "Expected at least 1 expression after 'or'."); List<Formula> formulaList = new ArrayList<Formula>(expression .getChildren().size() - 1); for (SExpression child : expression.getChildren().subList(1, expression.getChildren().size())) { formulaList.add(parseFormulaBody(child)); } return OrFormula.generate(formulaList); } if (operator.equals(SExpressionConstants.XOR)) { if (expression.getChildren().size() < 2) throw new ParseError(expression, "Expected at least 1 expression after 'xor'."); List<Formula> formulaList = new ArrayList<Formula>(expression .getChildren().size() - 1); for (SExpression child : expression.getChildren().subList(1, expression.getChildren().size())) { formulaList.add(parseFormulaBody(child)); } return XorFormula.generate(formulaList); } if (operator.equals(SExpressionConstants.IMPLIES) || operator.equals(SExpressionConstants.IMPLIES_ALT)) { if (expression.getChildren().size() != 3) throw new ParseError(expression, "Expected 2 arguments for '=>'."); Formula leftSide = parseFormulaBody(expression.getChildren() .get(1)); Formula rightSide = parseFormulaBody(expression.getChildren() .get(2)); return ImpliesFormula.create(leftSide, rightSide); } if (operator.equals(SExpressionConstants.ITE)) { if (expression.getChildren().size() != 4) throw new ParseError(expression, "Expected 3 arguments for 'ite'."); Formula condition = parseFormulaBody(expression.getChildren() .get(1)); Formula thenBranch = parseFormulaBody(expression.getChildren() .get(2)); Formula elseBranch = parseFormulaBody(expression.getChildren() .get(3)); return PropositionalIte.create(condition, thenBranch, elseBranch); } throw new ParseError(expression, "Unexpected internal parse error!"); } if (isEquality(expression)) { assert (expression.getChildren().size() >= 3); boolean equal; if (expression.getChildren().get(0) .equals(SExpressionConstants.EQUAL)) equal = true; else if (expression.getChildren().get(0) .equals(SExpressionConstants.DISTINCT)) equal = false; else throw new ParseError(expression, "Unexpected internal parse error!"); List<Term> termList = new ArrayList<Term>(expression.getChildren() .size() - 1); for (SExpression child : expression.getChildren().subList(1, expression.getChildren().size())) { termList.add(parseTerm(child)); } try { return EqualityFormula.create(termList, equal); } catch (IncomparableTermsException exc) { throw new ParseError(expression, "Incomparable terms in equality.", exc); } } if (isArrayProperty(expression)) { if (expression.getChildren().size() != 3) throw new ParseError(expression, "Expected 2 arguments for 'forall' expression."); assert (expression.getChildren().get(0) .equals(SExpressionConstants.FORALL)); SExpression uVarsExpression = expression.getChildren().get(1); try { currentUVars = parseUVars(uVarsExpression); SExpression property = expression.getChildren().get(2); Formula indexGuard; Formula valueConstraint; if (property.getChildren().size() <= 2) { // not an implication indexGuard = PropositionalConstant.create(true); valueConstraint = parseFormulaBody(property); } else if (!property.getChildren().get(0) .equals(SExpressionConstants.IMPLIES) && !property.getChildren().get(0) .equals(SExpressionConstants.IMPLIES_ALT)) { // also not an implication indexGuard = PropositionalConstant.create(true); valueConstraint = parseFormulaBody(property); } else { // we have an implication if (property.getChildren().size() != 3) throw new ParseError(property, "Malformed array property!"); assert (property.getChildren().get(0) .equals(SExpressionConstants.IMPLIES) || property .getChildren().get(0) .equals(SExpressionConstants.IMPLIES_ALT)); indexGuard = parseFormulaBody(property.getChildren().get(1)); valueConstraint = parseFormulaBody(property.getChildren() .get(2)); } try { return ArrayProperty.create(currentUVars, indexGuard, valueConstraint); } catch (InvalidIndexGuardException exc) { throw new ParseError(property, "Malformed index guard.", exc); } catch (InvalidValueConstraintException exc) { throw new ParseError(property, "Malformed value constraint.", exc); } } finally { currentUVars = null; } } UninterpretedFunction function = isUninterpredFunctionInstance(expression); if (function != null) { if (!(function.getType().equals(SExpressionConstants.BOOL_TYPE))) throw new ParseError( expression, "Non-Boolean uninterpreted function encountered, where sort Boolean was expected: " + function.getName().toString()); if (function.getNumParams() != expression.getChildren().size() - 1) throw new ParseError(expression, "Function '" + function.getName() + "' expects " + function.getNumParams() + " parameters."); List<DomainTerm> parameters = new ArrayList<DomainTerm>(); for (int count = 0; count < function.getNumParams(); count++) { Term term = parseTerm(expression.getChildren().get(count + 1)); if (!(term instanceof DomainTerm)) throw new ParseError(expression.getChildren() .get(count + 1), "Parameter is not a domain term."); parameters.add((DomainTerm) term); } try { int partition = getPartitionUninterpretedFunction(function); return UninterpretedPredicateInstance.create(function, parameters, partition); } catch (SuraqException exc) { throw new RuntimeException( "Unexpected situation while parsing uninterpreted function instance."); } } FunctionMacro macro = isMacroInstance(expression); if (macro != null) { if (!macro.getType().equals(SExpressionConstants.BOOL_TYPE)) throw new ParseError(expression, "Bool macro expected. Received type: " + macro.getType().toString()); List<SExpression> paramExpressions = macro.getNumParams() == 0 ? new ArrayList<SExpression>() : expression.getChildren().subList(1, expression.getChildren().size()); if (paramExpressions.size() != macro.getNumParams()) throw new ParseError(expression, "Expected " + macro.getNumParams() + "parameters for macro " + macro.getName().toString() + ", got " + paramExpressions.size() + " instead."); Map<Token, Term> paramMap = new HashMap<Token, Term>(); assert (paramExpressions.size() == macro.getNumParams()); for (int count = 0; count < paramExpressions.size(); count++) { Term paramTerm = parseTerm(paramExpressions.get(count)); if (!paramTerm.getType().equals(macro.getParamType(count))) throw new ParseError(paramExpressions.get(count), "Wrong parameter type. Expected " + macro.getParamType(count).toString() + ", got " + paramTerm.getType().toString() + " instead."); paramMap.put(macro.getParam(count), paramTerm); } try { return PropositionalFunctionMacroInstance.create( (PropositionalFunctionMacro) macro, paramMap); } catch (InvalidParametersException exc) { throw new RuntimeException( "Unexpected condition while creating function-macro instance.", exc); } } // we have something we cannot handle if (expression instanceof Token) throw new ParseError(expression, "Undeclared identifier: " + expression.toString()); else { System.err .println("We could not handle an Expression and it was no Token."); System.err.println("It was a:" + expression.getClass().getName() + " and its content was: " + (expression.toString().length() >= 30 ? expression .toString().substring(0, 30) + "..." : expression .toString())); System.err.println("Full Exception coming soon..."); throw new ParseError(expression, "Error parsing formula body."); } } /** * Determines the assert partition of a <code>PropositionalVariable</code>, * based on previous assert-partition information stored in * <code>boolVariables</code>. * * @param expression * the expression to check * @return the variables assert partition */ private int getPartitionBoolVariable(SExpression expression) { for (PropositionalVariable var : boolVariables) if (var.equals(PropositionalVariable.create((Token) expression))) { Set<Integer> partitions = var.getPartitionsFromSymbols(); return partitions.iterator().next(); } return SMTLibParser.GLOBAL_PARTITION; } /** * Determines the assert partition of a <code>DomainVariable</code>, based * on previous assert-partition information stored in * <code>domainVariables</code>. * * @param expression * the expression to check * @return the variables assert partition */ private int getPartitionDomainVariable(SExpression expression) { for (DomainVariable var : domainVariables) if (var.equals(DomainVariable.create((Token) expression))) { Set<Integer> partitions = var.getPartitionsFromSymbols(); return partitions.iterator().next(); } return SMTLibParser.GLOBAL_PARTITION; } /** * Determines the assert partition of a <code>ArrayVariable</code>, based on * previous assert-partition information stored in * <code>arrayVariables</code>. * * @param expression * the expression to check * @return the variables assert partition */ private int getPartitionArrayVariable(SExpression expression) { for (ArrayVariable var : arrayVariables) if (var.equals(ArrayVariable.create((Token) expression))) { Set<Integer> partitions = var.getPartitionsFromSymbols(); return partitions.iterator().next(); } return SMTLibParser.GLOBAL_PARTITION; } /** * Determines the assert partition of an <code>UninterpretedFunction</code>, * based on previous assert-partition information stored in * <code>uninterpretedFunctions</code>. * * @param function * the function to check * @return the <code>function</code>'s assert partition */ private int getPartitionUninterpretedFunction(UninterpretedFunction function) { for (UninterpretedFunction fun : functions) if (fun.equals(function)) { Set<Integer> partitions = fun.getAssertPartitionFromSymbols(); return partitions.iterator().next(); } return SMTLibParser.GLOBAL_PARTITION; } /** * Parses the given expression as a term. * * @param expression * the expression to parse * @return the term resulting from parsing. * @throws ParseError * if parsing fails */ protected Term parseTerm(SExpression expression) throws ParseError { if (isTermLetKey(expression)) { Term value = letTermDefinitions.get(expression); assert (value != null); return value; } if (expression.toString().charAt(0) == SMTLibParser.REF_TERM) { // resolve reference with LUT assert (expression instanceof Token); Token pureKey = Token.generate(expression.toString().substring(1)); Term term = this.terms.get(pureKey); if (term == null) throw new ParseError(expression, "could not find a matching term-LUT-entry!"); return term; } if (isUVar(expression)) { // Takes precedence over other variable types int partition = getPartitionDomainVariable(expression); return DomainVariable.create((Token) expression, partition); } SExpression type = isLocalVariable(expression); // takes precedence over // global variables. if (type != null) { if (type.equals(SExpressionConstants.ARRAY_TYPE)) { int partition = getPartitionArrayVariable(expression); return ArrayVariable.create((Token) expression, partition); } if (type.equals(SExpressionConstants.VALUE_TYPE)) { int partition = getPartitionDomainVariable(expression); return DomainVariable.create((Token) expression, partition); } if (type.equals(SExpressionConstants.BOOL_TYPE) || type.equals(SExpressionConstants.CONTROL_TYPE)) { int partition = getPartitionBoolVariable(expression); return PropositionalVariable.create((Token) expression, partition); } // In case we have a type that should not exist: throw new RuntimeException( "Unexpected type while handling local variable: " + type.toString()); } if (isIteTerm(expression)) { if (expression.getChildren().size() != 4) throw new ParseError(expression, "Expected 3 parameters for 'ite' expression."); Formula condition = parseFormulaBody(expression.getChildren() .get(1)); Term thenBranch = parseTerm(expression.getChildren().get(2)); Term elseBranch = parseTerm(expression.getChildren().get(3)); if (thenBranch instanceof ArrayTerm && elseBranch instanceof ArrayTerm) return ArrayIte.create(condition, (ArrayTerm) thenBranch, (ArrayTerm) elseBranch); if (thenBranch instanceof DomainTerm && elseBranch instanceof DomainTerm) return DomainIte.create(condition, (DomainTerm) thenBranch, (DomainTerm) elseBranch); if (thenBranch instanceof PropositionalTerm && elseBranch instanceof PropositionalTerm) return FormulaTerm.create((PropositionalIte.create(condition, (PropositionalTerm) thenBranch, (PropositionalTerm) elseBranch))); throw new ParseError(expression, "Incompatible types in 'ite' expression"); } if (isArrayVariable(expression)) { int partition = getPartitionArrayVariable(expression); return ArrayVariable.create(expression.toString(), partition); } if (isArrayWrite(expression)) { if (expression.getChildren().size() != 4) throw new ParseError(expression, "Expected 3 parameters for 'store' expression."); Term arrayTerm = parseTerm(expression.getChildren().get(1)); if (!(arrayTerm instanceof ArrayTerm)) throw new ParseError(expression.getChildren().get(1), "First parameter of 'store' must be an array term."); Term indexTerm = parseTerm(expression.getChildren().get(2)); if (!(indexTerm instanceof DomainTerm)) throw new ParseError(expression.getChildren().get(2), "Second parameter of 'store' must be a domain term."); Term valueTerm = parseTerm(expression.getChildren().get(3)); if (!(valueTerm instanceof DomainTerm)) throw new ParseError(expression.getChildren().get(3), "Third parameter of 'store' must be a domain term."); return ArrayWrite.create((ArrayTerm) arrayTerm, (DomainTerm) indexTerm, (DomainTerm) valueTerm); } if (isDomainVariable(expression)) { int partition = getPartitionDomainVariable(expression); return DomainVariable.create(expression.toString(), partition); } UninterpretedFunction function = isUninterpredFunctionInstance(expression); if (function != null) { // if (function.getType().equals(SExpressionConstants.BOOL_TYPE)) // throw new ParseError(expression, // "Boolean uninterpreted function encountered, where Term was expected: " // + function.getName().toString()); if (function.getNumParams() != expression.getChildren().size() - 1) throw new ParseError(expression, "Function '" + function.getName() + "' expects " + function.getNumParams() + " parameters."); List<DomainTerm> parameters = new ArrayList<DomainTerm>(); for (int count = 0; count < function.getNumParams(); count++) { Term term = parseTerm(expression.getChildren().get(count + 1)); if (!(term instanceof DomainTerm)) throw new ParseError(expression.getChildren() .get(count + 1), "Parameter is not a domain term."); parameters.add((DomainTerm) term); } try { int partition = getPartitionUninterpretedFunction(function); if (function.getType().equals(SExpressionConstants.VALUE_TYPE)) return UninterpretedFunctionInstance.create(function, parameters, partition); else { assert (function.getType() .equals(SExpressionConstants.BOOL_TYPE)); return UninterpretedPredicateInstance.create(function, parameters, partition); } } catch (SuraqException exc) { throw new RuntimeException( "Unexpected situation while parsing uninterpreted function instance."); } } if (isArrayRead(expression)) { if (expression.getChildren().size() != 3) throw new ParseError(expression, "Expected 2 parameters for 'select' expression."); Term arrayTerm = parseTerm(expression.getChildren().get(1)); if (!(arrayTerm instanceof ArrayTerm)) throw new ParseError(expression.getChildren().get(1), "First parameter of 'select' must be an array term."); Term indexTerm = parseTerm(expression.getChildren().get(2)); if (!(indexTerm instanceof DomainTerm)) throw new ParseError(expression.getChildren().get(2), "Second parameter of 'select' must be a domain term."); return ArrayRead.create((ArrayTerm) arrayTerm, (DomainTerm) indexTerm); } if (isPropositionalConstOrVar(expression)) { if (expression.equals(SExpressionConstants.TRUE)) return PropositionalConstant.create(true); else if (expression.equals(SExpressionConstants.FALSE)) return PropositionalConstant.create(false); int partition = getPartitionBoolVariable(expression); PropositionalVariable variable = PropositionalVariable.create( (Token) expression, partition); if (!boolVariables.contains(variable) && !controlVariables.contains(variable)) throw new RuntimeException( "Unexpected situation while handling variable " + variable.toString()); return variable; } FunctionMacro macro = isMacroInstance(expression); if (macro != null) { List<SExpression> paramExpressions = expression.getChildren() .subList(1, expression.getChildren().size()); if (paramExpressions.size() != macro.getNumParams()) throw new ParseError(expression, "Expected " + macro.getNumParams() + "parameters for macro " + macro.getName().toString() + ", got " + paramExpressions.size() + " instead."); Map<Token, Term> paramMap = new HashMap<Token, Term>(); assert (paramExpressions.size() == macro.getNumParams()); for (int count = 0; count < paramExpressions.size(); count++) { Term paramTerm = parseTerm(paramExpressions.get(count)); if (!paramTerm.getType().equals(macro.getParamType(count))) throw new ParseError(paramExpressions.get(count), "Wrong parameter type. Expected " + macro.getParamType(count).toString() + ", got " + paramTerm.getType().toString() + " instead."); paramMap.put(macro.getParam(count), paramTerm); } try { if (macro.getType().equals(SExpressionConstants.BOOL_TYPE)) { assert (macro instanceof PropositionalFunctionMacro); return FormulaTerm .create(PropositionalFunctionMacroInstance.create( (PropositionalFunctionMacro) macro, paramMap)); } else { assert (macro instanceof TermFunctionMacro); return TermFunctionMacroInstance.create( (TermFunctionMacro) macro, paramMap); } } catch (InvalidParametersException exc) { throw new RuntimeException( "Unexpected condition while creating function-macro instance.", exc); } } // as a last resort, try interpreting the expression as a formula // this will throw a parse error, if it fails. Formula formula = parseFormulaBody(expression); return FormulaTerm.create(formula); } /** * Parses the list of universally quantified variables. * * @param uVarsExpression * the first argument of a <code>forall</code> expression * @return the collection of universally quantified variables. */ protected Collection<DomainVariable> parseUVars(SExpression uVarsExpression) throws ParseError { Set<DomainVariable> uVars = new HashSet<DomainVariable>(); if (uVarsExpression.isEmpty()) throw new ParseError(uVarsExpression, "Empty variable list."); for (SExpression child : uVarsExpression.getChildren()) { if (child.getChildren().size() != 2) throw new ParseError(child, "Invalid quantified variable"); if (!child.getChildren().get(1) .equals(SExpressionConstants.VALUE_TYPE)) throw new ParseError(child.getChildren().get(1), "Invalid type of quantified variable: " + child.getChildren().get(1).toString()); if (!(child.getChildren().get(0) instanceof Token)) throw new ParseError(child.getChildren().get(0), "Expected variable name."); int partition = getPartitionDomainVariable(child.getChildren().get( 0)); if (!uVars.add(DomainVariable.create((Token) child.getChildren() .get(0), partition))) { throw new ParseError(child.getChildren().get(0), "Duplicate variable in quantifier scope: " + child.getChildren().get(0).toString()); } } return uVars; } /** * Checks if the given expression is an if-then-else term * * @param expression * the expression to check. * @return <code>true</code> if the first child of <code>expression</code> * is a <code>Token</code> and it equals the ITE operator. */ protected boolean isIteTerm(SExpression expression) { if (expression instanceof Token) return false; if (expression.getChildren().size() < 1) return false; if (expression.getChildren().get(0).equals(SExpressionConstants.ITE)) return true; return false; } /** * Checks if the given expression is a macro instance. If so, the * corresponding macro is returned. * * @param expression * the expression to check. * @return the macro instantiated by this expression, or <code>null</code> * if this is not a macro instance */ protected FunctionMacro isMacroInstance(SExpression expression) { if (expression instanceof Token) { // Could be a macro without parameters return macros.get(expression); } else { // Could be a macro with parameters if (expression.getChildren().size() < 2) return null; if (!(expression.getChildren().get(0) instanceof Token)) return null; assert (expression.getChildren().get(0) instanceof Token); Token macroName = (Token) expression.getChildren().get(0); return macros.get(macroName); } } /** * Checks whether the given expression is an array property. For more * meaningful parse errors, everything starting with a <code>forall</code> * token is considered an array property here. * * @param expression * the expression to check. * @return <code>true</code> if the given expression starts with a * <code>forall</code> token. */ protected boolean isArrayProperty(SExpression expression) { if (expression instanceof Token) return false; if (expression.getChildren().size() < 1) return false; SExpression firstChild = expression.getChildren().get(0); if (!(firstChild instanceof Token)) return false; if (firstChild.equals(SExpressionConstants.FORALL)) return true; return false; } /** * Checks whether the given expression is an array read. * * @param expression * the expression to check. * @return <code>true</code> if the first child of <code>expression</code> * is the <code>select</code> token, <code>false</code> otherwise. */ protected boolean isArrayRead(SExpression expression) { if (expression instanceof Token) return false; if (expression.getChildren().size() < 1) return false; if (!expression.getChildren().get(0) .equals(SExpressionConstants.SELECT)) return false; else return true; } /** * Checks whether the given expression is an array write. * * @param expression * the expression to check. * @return <code>true</code> if the first child of <code>expression</code> * is the <code>store</code> token, <code>false</code> otherwise. */ protected boolean isArrayWrite(SExpression expression) { if (expression instanceof Token) return false; if (expression.getChildren().size() < 1) return false; if (!expression.getChildren().get(0).equals(SExpressionConstants.STORE)) return false; else return true; } /** * Checks whether the given expression is an equality instance. * * @param expression * the expression to check. * @return <code>true</code> if the given expression is an equality * expression, <code>false</code> otherwise. */ protected boolean isEquality(SExpression expression) { if (expression instanceof Token) return false; if (expression.getChildren().size() < 3) return false; if (!(expression.getChildren().get(0) instanceof Token)) return false; assert (expression.getChildren().get(0) instanceof Token); Token operator = (Token) expression.getChildren().get(0); if (operator.equals(SExpressionConstants.EQUAL) || operator.equals(SExpressionConstants.DISTINCT)) return true; return false; } /** * Checks if the given expression is a Boolean combination (excluding * equality). If so, its operator is returned. Otherwise, <code>null</code> * is returned. * * @param expression * the expression to check. * @return the operator used, if the given expression is a Boolean * combination (except equality). <code>null</code> otherwise. * */ protected Token isBooleanCombination(SExpression expression) { if (expression instanceof Token) return null; if (expression.getChildren().size() < 2) return null; if (!(expression.getChildren().get(0) instanceof Token)) return null; assert (expression.getChildren().get(0) instanceof Token); Token operator = (Token) expression.getChildren().get(0); if (operator.equals(SExpressionConstants.AND) || operator.equals(SExpressionConstants.OR) || operator.equals(SExpressionConstants.XOR) || operator.equals(SExpressionConstants.NOT) || operator.equals(SExpressionConstants.IMPLIES) || operator.equals(SExpressionConstants.IMPLIES_ALT) || operator.equals(SExpressionConstants.ITE)) return operator; return null; } /** * Checks if the given expression is a propositional variable. * * @param expression * the expression to check * @return <code>true</code> if the given expression is a propositional * variable, <code>false</code> otherwise. */ protected boolean isPropositionalVariable(SExpression expression) { if (!(expression instanceof Token)) return false; Token token = (Token) expression; PropositionalVariable variable = PropositionalVariable.create(token); if (boolVariables.contains(variable) || controlVariables.contains(variable)) return true; else return false; } /** * Checks if the given expression is a Tseitin variable. * * @param expression * the expression to check * @return <code>true</code> if the given expression is a Tseitin variable, * <code>false</code> otherwise. */ protected boolean isTseitinVariable(SExpression expression) { if (!(expression instanceof Token)) return false; Token token = (Token) expression; PropositionalVariable variable = PropositionalVariable.create(token); if (expression.toString().startsWith("k!")) { if (boolVariables.contains(variable) || controlVariables.contains(variable)) assert (false); // System.out.println("INFO: Identified Tseitin variable: " + // token); return true; } return false; } /** * Checks if the given expression is a propositional constant. * * @param expression * the expression to check. * @return <code>true</code> if the given expression is a propositional * constant, <code>false</code> otherwise. */ protected boolean isPropositionalConstant(SExpression expression) { return (expression.equals(SExpressionConstants.TRUE) || expression .equals(SExpressionConstants.FALSE)); } /** * Checks if the given expression is a current local variable. Returns the * type of the variable, or <code>null</code> if no such variable exists. * * @param expression * the expression to check * @return the type of the local variable or <code>null</code> if it does * not exist. */ protected SExpression isLocalVariable(SExpression expression) { if (currentLocals == null) return null; return currentLocals.get(expression); } /** * Checks if the given expression is a propositional variable or constant. * * @param expression * the expression to check. * @return <code>true</code> if the given expression is a propositional * variable or constant, <code>false</code> otherwise. */ protected boolean isPropositionalConstOrVar(SExpression expression) { if (!(expression instanceof Token)) return false; if (expression.equals(SExpressionConstants.TRUE)) return true; if (expression.equals(SExpressionConstants.FALSE)) return true; PropositionalVariable variable = PropositionalVariable .create((Token) expression); if (boolVariables.contains(variable)) // || controlVariables.contains(variable)) return true; else return false; } /** * Checks whether the given expression is a domain variable * * @param expression * the expression to check * @return <code>true</code> if the given expression is a domain variable, * <code>false</code> otherwise. */ protected boolean isDomainVariable(SExpression expression) { if (!(expression instanceof Token)) return false; return domainVariables.contains(DomainVariable .create((Token) expression)); } /** * Checks whether the given expression is an array variable * * @param expression * the expression to check * @return <code>true</code> if the given expression is an array variable, * <code>false</code> otherwise. */ protected boolean isArrayVariable(SExpression expression) { if (!(expression instanceof Token)) return false; return arrayVariables .contains(ArrayVariable.create((Token) expression)); } /** * Checks whether the given expression is an uninterpreted function * instance. If so, the function is returned. * * @param expression * the expression to check * @return if the given expression is an uninterpreted function instance, * the function is returned. Otherwise <code>null</code> is * returned. */ protected UninterpretedFunction isUninterpredFunctionInstance( SExpression expression) { if (expression instanceof Token) return null; if (expression.getChildren().size() < 2) return null; if (!(expression.getChildren().get(0) instanceof Token)) return null; Token name = (Token) expression.getChildren().get(0); for (UninterpretedFunction function : functions) { if (name.equals(function.getName())) return function; } return null; } /** * Checks if the given expression is a universally quantified variable (in * current scope). * * @param expression * the expression to check * @return <code>true</code> if the expression is a universally quantified * variable, <code>false</code> otherwise. */ protected boolean isUVar(SExpression expression) { if (currentUVars == null) return false; if (!(expression instanceof Token)) return false; return (this.currentUVars.contains(DomainVariable .create((Token) expression))); } /** * Checks whether the given expression is an let instance. * * @param expression * the expression to check. * @return <code>true</code> if the given expression is an let expression, * <code>false</code> otherwise. */ protected boolean isLet(SExpression expression) { if (expression instanceof Token) return false; if (expression.getChildren().size() != 3) return false; if (!(expression.getChildren().get(0) instanceof Token)) // let return false; assert (expression.getChildren().get(0) instanceof Token); if (!(expression.getChildren().get(0).equals(SExpressionConstants.LET))) return false; for (SExpression child : expression.getChildren().get(1).getChildren()) { if (child.size() != 2) return false; if (!(child.getChildren().get(0) instanceof Token)) return false; } return true; } /** * Checks whether the given expression is a key defined by a let expression. * * @param expression * the expression to check. * @return <code>true</code> if the given expression is a key defined by a * let expression, <code>false</code> otherwise. */ protected boolean isFormulaLetKey(SExpression expression) { if (!(expression instanceof Token)) return false; return letFormulaDefinitions.containsKey(expression); } /** * Checks whether the given expression is a key defined by a let expression. * * @param expression * the expression to check. * @return <code>true</code> if the given expression is a key defined by a * let expression, <code>false</code> otherwise. */ protected boolean isTermLetKey(SExpression expression) { if (!(expression instanceof Token)) return false; return letTermDefinitions.containsKey(expression); } /** * Returns a copy of the list of control variables. * * @return a copy of the <code>controlVariables</code> */ public List<PropositionalVariable> getControlVariables() { return new ArrayList<PropositionalVariable>(controlVariables); } /** * Returns a copy of the list of Boolean variables. * * @return a copy of the <code>boolVariables</code> */ public List<PropositionalVariable> getBoolVariables() { return new ArrayList<PropositionalVariable>(boolVariables); } /** * Returns a copy of the list of Tseitin variables. * * @return a copy of the <code>boolVariables</code> */ public List<PropositionalVariable> getTseitinVariables() { return new ArrayList<PropositionalVariable>(tseitinVariables); } /** * Returns a copy of the list of domain variables. * * @return a copy of the <code>domainVariables</code> */ public List<DomainVariable> getDomainVariables() { return new ArrayList<DomainVariable>(domainVariables); } /** * Returns a copy of the list of array variables. * * @return a copy of the <code>arrayVariables</code> */ public List<ArrayVariable> getArrayVariables() { return new ArrayList<ArrayVariable>(arrayVariables); } /** * Returns a copy of the list of variables on which control logic may * <em>not</em> depend. * * @return a copy of the list of variables on which control logic may * <em>not</em> depend. */ public List<Token> getNoDependenceVariables() { return new ArrayList<Token>(noDependenceVariables); } /** * Returns a copy of the list of uninterpreted functions. * * @return a copy of the <code>functions</code> */ public List<UninterpretedFunction> getFunctions() { return new ArrayList<UninterpretedFunction>(functions); } /** * Returns a copy of the list of function macros. * * @return a copy of the <code>macros</code> */ public List<FunctionMacro> getMacros() { return new ArrayList<FunctionMacro>(macros.values()); } /** * Handles a let expression that defines Tseitin variables. * * @param expr * @return * @throws ParseError */ protected final Formula handleLet(SExpression expr) throws ParseError { assert (expr.size() == 3); assert (expr.getChildren().get(0) instanceof Token); assert (expr.getChildren().get(0).equals(SExpressionConstants.LET)); Set<Token> currentLetTokens = new HashSet<Token>(); for (SExpression defineExpr : expr.getChildren().get(1).getChildren()) { assert (defineExpr.size() == 2); assert (defineExpr.getChildren().get(0) instanceof Token); Token key = (Token) defineExpr.getChildren().get(0); if (isTerm(defineExpr.getChildren().get(1))) { Term value = parseTerm(defineExpr.getChildren().get(1)); letTermDefinitions.put(key, value); } else { Formula value = parseFormulaBody(defineExpr.getChildren() .get(1)); letFormulaDefinitions.put(key, value); } currentLetTokens.add(key); } // String formulaString = expr.getChildren().get(2).toString(); // // for (Token token : letFormulaDefinitions.keySet()) // formulaString = formulaString.replaceAll(token.toString() // + "[\\t\\n\\x0B\\f\\r(]", letFormulaDefinitions.get(token) // .toString()); // // SExpression tmpExpr = SExpression.fromString(formulaString); // SExpression tmpExpr = expr.getChildren().get(2); // tmpExpr.replace(letFormulaDefinitions); Formula result = parseFormulaBody(expr.getChildren().get(2)); for (Token key : currentLetTokens) { letFormulaDefinitions.remove(key); letTermDefinitions.remove(key); } return result; } /** * * @param expression * @return <code>true</code> iff the given expression is a term and not a * formula. */ protected boolean isTerm(SExpression expression) { if (isDomainVariable(expression)) return true; UninterpretedFunction function = isUninterpredFunctionInstance(expression); if (function != null) { if (function.getType().equals(SExpressionConstants.VALUE_TYPE)) return true; } if (isArrayRead(expression)) return true; if (isArrayWrite(expression)) return true; if (isArrayVariable(expression)) return true; return false; } }