/**
* Author: Georg Hofferek <georg.hofferek@iaik.tugraz.at>
*/
package at.iaik.suraq.parser;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import at.iaik.suraq.exceptions.ParseError;
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.ArrayVariable;
import at.iaik.suraq.smtlib.formula.DomainVariable;
import at.iaik.suraq.smtlib.formula.Formula;
import at.iaik.suraq.smtlib.formula.PropositionalVariable;
import at.iaik.suraq.smtlib.formula.Term;
import at.iaik.suraq.smtlib.formula.UninterpretedFunction;
/**
* @author Georg Hofferek <georg.hofferek@iaik.tugraz.at>
*
*/
public class ProofParser extends SMTLibParser {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* The proof that results from parsing.
*/
protected Z3Proof rootProof = null;
/**
*
* Constructs a new <code>ProofParser</code>.
*
* @param root
* the root expression to parse.
* @param domainVars
* proof domain variables
* @param propsitionalVars
* proof propositional variables
* @param arrayVars
* proof array variables
* @param uninterpretedFunctions
* proof uninterpreted Functions
*/
public ProofParser(SExpression root, Set<DomainVariable> domainVars,
Set<PropositionalVariable> propsitionalVars,
Set<ArrayVariable> arrayVars,
Set<UninterpretedFunction> uninterpretedFunctions) {
this.boolVariables = propsitionalVars;
this.arrayVariables = arrayVars;
this.domainVariables = domainVars;
this.functions = uninterpretedFunctions;
this.rootExpr = root;
}
/**
*
* @return a (deep) copy of the root expression of this parser.
*/
public SExpression getRootExpr() {
return rootExpr.deepCopy();
}
/**
* Parses the root s-expression into a proof, which can then be retrieved
* using <code>getRootProof</code>.
*
* @see at.iaik.suraq.parser.Parser#parse()
*/
@Override
public void parse() throws ParseError {
int start = 0;
// chillebold: workaround:
// because proof looks like: ((set-logic QF_UF)(proof (let (($x131....
if (rootExpr.getChildren().get(0).getChildren().get(0)
.equals(SExpressionConstants.SET_LOGIC_QF_UF)) {
rootExpr = rootExpr.getChildren().get(0).getChildren().get(1);
// chillebold: startcounter changed from 0 to 1 in that case
start = 1;
}
for (int count = start; count < rootExpr.getChildren().size(); count++) {
SExpression expression = rootExpr.getChildren().get(count);
if (expression instanceof Token)
throw new ParseError(expression.getLineNumber(),
expression.getColumnNumber(), expression.toString(),
"Unexpected Token.");
if (expression.getChildren().size() == 0)
throw new ParseError(expression.getLineNumber(),
expression.getColumnNumber(), expression.toString(),
"Unexpected empty expression.");
assert (expression.getChildren().size() >= 1);
// at this point, we expect a proof expression or an let expression
if (!(expression.getChildren().get(0) instanceof Token)) {
/*
* if(expression.getChildren().get(0).equals(SExpressionConstants
* .SET_LOGIC_QF_UF)) {
* System.out.println("Expected 'proof' or 'let' expression. Got: "
* + expression.getChildren().get(0).getClass().getName()+
* " \nwith value: "
* +expression.getChildren().get(0).toString());
* System.out.println
* ("use next: "+expression.getChildren().get(1
* ).getClass().getName());
*
* System.out.println("We will take the next element given:");
* expression = expression.getChildren().get(1); // this is now
* a (PROOF
*
* //System.out.println(expression.toString().substring(0,200)+"..."
* );
*
* // also leave out the PROOF... //expression =
* expression.getChildren().get(1); // this is now the first LET
*
*
* } else {
*/
System.err
.println("Expected 'proof' or 'let' expression. Got: "
+ expression.getChildren().get(0).getClass()
.getName() + " \nwith value: "
+ expression.getChildren().get(0).toString());
throw new ParseError(expression.getLineNumber(),
expression.getColumnNumber(), expression.toString(),
"Expected 'proof' or 'let' expression.");
// }
}
if (isLet(expression)) {
handleLetInProof(expression);
continue;
}
else if (isProof(expression)) {
handleProof(expression);
continue;
}
// we got something unexpected, if we reach this point.
throw new ParseError(expression.getLineNumber(),
expression.getColumnNumber(), expression.toString(),
"Expected 'proof' or 'let' expression.");
}
parsingSuccessfull = true;
}
/**
* Handles an let expression. I.e., if <code>rootProof</code> is still
* <code>null</code>, it will be initialized to the result of parsing this
* assert statement's body. If <code>rootProof</code> already is non-
* <code>null</code>, a conjunction of its current value an the parsed body
* will be made.
*
* @param expression
* the assert expression to parse.
*/
private void handleLetInProof(SExpression expression) throws ParseError {
Token key = (Token) expression.getChildren().get(1).getChildren()
.get(0).getChildren().get(0);
SExpression entryExpr = expression.getChildren().get(1).getChildren()
.get(0).getChildren().get(1);
insertLUTEntry(key, entryExpr);
SExpression scopeExpr = expression.getChildren().get(2);
Z3Proof scope = parseBody(scopeExpr);
if (rootProof == null)
rootProof = scope;
else {
throw new RuntimeException("Found more than one proof, aborting.");
}
}
/**
* Handles an proof expression. I.e., if <code>rootProof</code> is still
* <code>null</code>, it will be initialized to the result of parsing this
* assert statement's body. If <code>rootProof</code> already is non-
* <code>null</code>, a conjunction of its current value an the parsed body
* will be made.
*
* @param expression
* the assert expression to parse.
*/
private void handleProof(SExpression expression) throws ParseError {
assert (expression.getChildren().get(0) instanceof Token);
Token proofType = (Token) expression.getChildren().get(0);
int numChildren = expression.getChildren().size();
assert (numChildren >= 2);
int subProofsCount = numChildren - 2; // 0
List<Z3Proof> subProofs = new ArrayList<Z3Proof>();
if (subProofsCount > 0)
for (int i = 1; i <= subProofsCount; i++) {
subProofs.add(parseBody(expression.getChildren().get(i)));
}
SExpression consequentExpr = expression.getChildren().get(
numChildren - 1);
Formula consequent = null;
try {
consequent = parseFormulaBody(consequentExpr);
} catch (ParseError ex) {
System.err
.println("Exception in ProofParser:handleProof on parseFormulaBody: "
+ ex.getMessage());
ex.printStackTrace();
System.exit(0); // TODO: remove System.exit(0)
throw ex;
}
Z3Proof proof = new Z3Proof(proofType, subProofs, consequent);
// for (Z3Proof subProof : subProofs)
// subProof.addParent(proof);
if (rootProof == null)
rootProof = proof;
else {
throw new RuntimeException("Found more than one proof, aborting.");
}
}
/**
* Inserts an expression into a lookup-table.
*
* @param entryExpr
* the expression to be inserted into the lookup-table (proofs,
* formulas or terms).
* @throws ParseError
*/
private void insertLUTEntry(Token key, SExpression entryExpr)
throws ParseError {
Token pureKey = Token.generate(key.toString().substring(1));
char typeCharEntry = entryExpr.toString().charAt(0);
char typeCharKey = key.toString().charAt(0);
if (typeCharKey == SMTLibParser.REF_PROOF) {
if (typeCharEntry == SMTLibParser.REF_PROOF)
throw new ParseError(entryExpr,
"no assignment of proof to another reference of proof");
Z3Proof entry = parseBody(entryExpr);
this.proofs.put(pureKey, entry);
} else if (typeCharKey == SMTLibParser.REF_FORMULA) {
if (typeCharEntry == SMTLibParser.REF_FORMULA)
throw new ParseError(entryExpr,
"no assignment of formula to another reference of formula");
Formula entry = parseFormulaBody(entryExpr);
// if (!Util.isLiteral(entry))
// entry = new TseitsinVariable(key, entry);
this.formulas.put(pureKey, entry);
} else if (typeCharKey == SMTLibParser.REF_TERM) {
if (typeCharEntry == SMTLibParser.REF_TERM)
throw new ParseError(entryExpr,
"no assignment of term to another reference of term");
Term entry = parseTerm(entryExpr);
this.terms.put(pureKey, entry);
} else
throw new ParseError(entryExpr, "unknown expression type found");
}
/**
* Checks whether the given expression is an proof instance.
*
* @param expression
* the expression to check.
* @return <code>true</code> if the given expression is an proof expression,
* <code>false</code> otherwise.
*/
private boolean isProof(SExpression expression) {
if (expression instanceof Token) {
if (SMTLibParser.REF_PROOF == expression.toString().charAt(0))
return true;
else
return false;
}
if (!(expression.getChildren().size() >= 2))
return false;
if (!(expression.getChildren().get(0) instanceof Token))
return false;
assert (expression.getChildren().get(0) instanceof Token);
Token proofType = (Token) expression.getChildren().get(0);
if (!SExpression.isValidProofType(proofType))
return false;
return true;
}
/**
* Checks whether the given expression is an let instance, with a key that
* starts either with $, @, or ?.
*
* @param expression
* the expression to check.
* @return <code>true</code> if the given expression is an let expression,
* <code>false</code> otherwise.
*/
@Override
protected boolean isLet(SExpression expression) {
if (!super.isLet(expression))
return false;
Token key = (Token) expression.getChildren().get(1).getChildren()
.get(0).getChildren().get(0); // $x5
if (key.toString().charAt(0) != SMTLibParser.REF_PROOF
&& key.toString().charAt(0) != SMTLibParser.REF_FORMULA
&& key.toString().charAt(0) != SMTLibParser.REF_TERM)
return false;
return true;
}
/**
* Parses a given s-expression into a proof. The s-expression can either be
* of a proof or a let-assignment.
*
* @param expression
* the expression to parse.
* @return the proof resulting from the given expression.
* @throws ParseError
* if parsing fails.
*/
private Z3Proof parseBody(SExpression expression) throws ParseError {
if (isLet(expression))
return parseLet(expression);
else if (isProof(expression))
return (parseProofBody(expression));
else
throw new ParseError(expression,
"parseBody can only parse Let-Expression or Proof!");
}
/**
* Handles an let expression.
*
* @param expression
* the let expression to parse.
* @return the proof resulting from parsing.
*/
private Z3Proof parseLet(SExpression expression) throws ParseError {
Token key = (Token) expression.getChildren().get(1).getChildren()
.get(0).getChildren().get(0);
SExpression entryExpr = expression.getChildren().get(1).getChildren()
.get(0).getChildren().get(1);
insertLUTEntry(key, entryExpr);
SExpression scopeExpr = expression.getChildren().get(2);
Z3Proof scope = parseBody(scopeExpr);
return scope;
}
/**
* Parses a given s-expression into a <code>Z3Proof</code>.
*
* @param expression
* the expression to parse.
* @return the formula resulting from the given expression.
* @throws ParseError
* if parsing fails.
*/
private Z3Proof parseProofBody(SExpression expression) throws ParseError {
if (expression.toString().charAt(0) == SMTLibParser.REF_PROOF) {
// resolve reference with LUT
assert (expression instanceof Token);
Token pureKey = Token.generate(expression.toString().substring(1));
Z3Proof proof = this.proofs.get(pureKey);
if (proof == null)
throw new ParseError(expression,
"could not find a matching proof-LUT-entry!");
return proof;
} else {
assert (expression.getChildren().get(0) instanceof Token);
Token proofType = (Token) expression.getChildren().get(0);
int numChildren = expression.getChildren().size();
int subProofsCount = numChildren - 2; // first child is proofType,
// last one is consequent
List<Z3Proof> subProofs = new ArrayList<Z3Proof>();
if (subProofsCount > 0)
for (int i = 1; i <= subProofsCount; i++) {
subProofs.add(parseBody(expression.getChildren().get(i)));
}
SExpression consequentExpr = expression.getChildren().get(
numChildren - 1);
Formula consequent = parseFormulaBody(consequentExpr);
Z3Proof result = new Z3Proof(proofType, subProofs, consequent);
// for (Z3Proof subProof : subProofs)
// subProof.addParent(result);
return result;
}
}
/**
* Returns the root proof that resulted from parsing, or <code>null</code>
* if parsing was not successful.
*
* @return the proof that resulted from parsing, or <code>null</code> if
* parsing was not successful.
*/
public Z3Proof getRootProof() {
if (!wasParsingSuccessfull())
return null;
return rootProof;
}
}