/*
* 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.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.regex.Pattern;
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.FunctionSymbol;
import de.uni_freiburg.informatik.ultimate.logic.LetTerm;
import de.uni_freiburg.informatik.ultimate.logic.Logics;
import de.uni_freiburg.informatik.ultimate.logic.NonRecursive;
import de.uni_freiburg.informatik.ultimate.logic.QuantifiedFormula;
import de.uni_freiburg.informatik.ultimate.logic.Rational;
import de.uni_freiburg.informatik.ultimate.logic.Sort;
import de.uni_freiburg.informatik.ultimate.logic.Term;
import de.uni_freiburg.informatik.ultimate.logic.TermVariable;
import de.uni_freiburg.informatik.ultimate.smtinterpol.proofcheck.LemmaLAConverter.FactorWrapper;
/**
* Terms in SMT-LIB syntax must be converted to Isabelle syntax before passing
* them to Isabelle. This is what happens here.
*
* @author Christian Schilling
*/
class TermConverter extends NonRecursive {
// appendable
private Appendable mAppendable;
// printing numeric types
private boolean mPrintTypes;
// use more convenient output?
private final boolean mPrettyOutput;
// map for interpreted functions
private final HashMap<String, IFunction> mFunTrans;
// maps for uninterpreted functions to compatible name
private final HashMap<FunctionSymbol, String> mFunction2compFunction;
// list and stack for :named annotations
private LinkedList<NamedWrapper> mNamedList;
private ArrayDeque<NamedWrapper> mNamedStack;
// lambda and conjunction in parentheses for pretty printing modes
private final String mAndInPar;
private final String mLambda;
// lambda variable prefix
private static final String LAMBDA_VAR_PREFIX = "x";
/**
* @param appendable appendable to write the term to
* @param prettyOutput true iff output file is printed in a more convenient
* human-readable way
*/
public TermConverter(final Appendable appendable, final Logics logics,
final boolean prettyOutput) {
mAppendable = appendable;
mPrettyOutput = prettyOutput;
mAndInPar = mPrettyOutput ? ") \\<and> (" : ") & (";
mPrintTypes = false;
mFunction2compFunction = new HashMap<FunctionSymbol, String>();
mLambda = mPrettyOutput ? "((\\<lambda>" : "((%";
// fill function
mFunTrans = new HashMap<String, IFunction>((int)(25 / 0.75) + 1);
fillMap();
}
// [start] general conversion //
/**
* This method converts an SMT-LIB term to an Isabelle term and
* appends it to the internal string memory.
*
* The term 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.
*
* @param term the SMT-LIB term
*/
public void convert(final Term term) {
enqueueWalker(new TermConverterWalker(term));
run();
}
/**
* 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(final String string) {
mString = string;
}
@Override
public void walk(final NonRecursive converter) {
writeString(mString);
}
}
/**
* This walker adds nested :named annotations in the correct order to the
* list.
*/
private class NamedWalker implements NonRecursive.Walker {
final int mStackSize;
public NamedWalker(int stackSize) {
mStackSize = stackSize;
}
@Override
public void walk(final NonRecursive converter) {
if (mNamedStack != null) {
for (int i = mNamedStack.size() - mStackSize; i >= 0; --i) {
assert (!mNamedStack.isEmpty());
final NamedWrapper wrapper = mNamedStack.pop();
mNamedList.add(wrapper);
}
}
}
}
/**
* This walker converts a term (in SMT-LIB syntax) to an
* Isabelle-compatible format.
*
* NOTE: All conversion outputs are in reversed order due to stack usage.
*
* @param term the SMT-LIB term
*/
private class TermConverterWalker extends NonRecursive.TermWalker {
/**
* @param term the term
*/
public TermConverterWalker(final Term term) {
super(term);
}
/**
* This method converts a constant term.
* A constant is always a number.
*
* NOTE: the sorts are not always correct, since a numeral
* is defined to be of sort Int, but in mixed logics it can be of sort
* Real. Isabelle automatically infers type conversion.
*
* @param converter non-recursive converter
* @param term constant term
*/
@Override
public void walk(final NonRecursive converter,
final ConstantTerm term) {
final String termName = term.getSort().getName();
if (termName == "Int") {
writeString("(");
enqueueWalker(new StringWalker("::int)"));
enqueueWalker(new StringWalker(term.toString()));
} else if (termName == "Real") {
// decimal
if (term.getValue() instanceof BigDecimal) {
final BigDecimal number = (BigDecimal)term.getValue();
final String string = number.toString();
int scale = number.scale();
final boolean fraction;
/*
* pretty printing drops all unnecessary fractions and
* zeroes
*/
if (mPrettyOutput) {
if (scale > 0) {
final int point = string.length() - scale;
int i = point + 1;
for ( ; i < string.length(); ++i) {
if (string.charAt(i) != '0') {
break;
}
}
if (i == string.length()) {
fraction = false;
} else {
fraction = true;
}
if (fraction) {
writeString("((");
// if the integer part is zero, drop it as well
if (string.charAt(0) != '0') {
assert (point > 2);
writeString(string.substring(0,
point - 1));
}
writeString(string.substring(i));
} else {
writeString("(");
writeString(string.substring(0, point - 1));
}
} else {
writeString("(");
writeString(string);
fraction = false;
}
} else {
// only ignore a single decimal zero
if (scale > 1) {
writeString("((");
final int point = string.length() - scale;
writeString(string.substring(0, point - 1));
writeString(string.substring(point));
fraction = true;
} else if ((scale == 1) && (string.endsWith("0"))) {
/*
* integer-valued reals have the form 'x.0'
* this is always ignored
*/
writeString("(");
writeString(string.substring(0,
string.length() - 2));
fraction = false;
} else {
writeString("(");
writeString(string);
fraction = false;
}
}
/*
* NOTE: For fractions only one of the numbers must be
* typed as real.
*/
writeString("::real");
if (fraction) {
writeString(") / 1");
while (--scale >= 0) {
writeString("0");
}
}
writeString(")");
} else {
// integer, but sort real
assert (term.getValue() instanceof BigInteger);
writeString("(");
writeString(term.getValue().toString());
writeString("::real)");
}
} else {
throw new IllegalArgumentException(
"The constant type is not supported.");
}
}
/**
* This method converts an annotated term.
*
* Annotations are completely ignored and only the sub-term is pushed
* to the stack.
*
* @param converter non-recursive converter
* @param term annotated term
*/
@Override
public void walk(final NonRecursive converter,
final AnnotatedTerm term) {
final Term subterm = term.getSubterm();
// assertion mode, store :named bindings
if (mNamedStack != null) {
final Annotation[] annotations = term.getAnnotations();
boolean found = false;
for (int i = 0; i < annotations.length; ++i) {
final Annotation annot = annotations[i];
if (annot.getKey().equals(":named")) {
assert (annot.getValue() instanceof String);
mNamedStack.push(new NamedWrapper(
((String)annot.getValue()), subterm));
// afterwards take in reverse order from the stack
if (!found) {
found = true;
enqueueWalker(
new NamedWalker(mNamedStack.size()));
}
}
}
}
enqueueWalker(new TermConverterWalker(subterm));
}
/**
* This method converts an application term.
*
* Internally it is a huge case split for every possible term that
* calls the specific handler.
*
* It currently works for:
*
* Bool functions:
* true, false, and, or, =>, not, xor, ite
*
* equality functions:
* =, distinct
*
* LA functions:
* <=, <, >=, >, +, - (unary and binary), *, /, mod, div, abs
* divisible, to_int, to_real, is_int
*
* CC functions:
* uninterpreted functions
*
* @param converter non-recursive converter
* @param term application term
*/
@Override
public void walk(final NonRecursive converter,
final ApplicationTerm term) {
final FunctionSymbol function = term.getFunction();
// intern function
if (function.isIntern()) {
mFunTrans.get(function.getName()).convert(term);
} else {
// non-intern function
toNoninternFunction(function, term.getParameters());
}
}
/**
* This method converts a let term.
*
* @param converter non-recursive converter
* @param term let term
*/
@Override
public void walk(final NonRecursive converter, final LetTerm term) {
final TermVariable[] variables = term.getVariables();
final Term[] values = term.getValues();
assert ((values.length == variables.length)
&& (values.length > 0));
writeString("(let ");
enqueueWalker(new StringWalker(")"));
enqueueWalker(new TermConverterWalker(term.getSubTerm()));
enqueueWalker(new StringWalker(" in "));
for (int i = variables.length - 1; i >= 1; --i) {
enqueueWalker(new TermConverterWalker(values[i]));
enqueueWalker(new StringWalker(" = "));
enqueueWalker(new TermConverterWalker(variables[i]));
enqueueWalker(new StringWalker("; "));
}
enqueueWalker(new TermConverterWalker(values[0]));
enqueueWalker(new StringWalker(" = "));
enqueueWalker(new TermConverterWalker(variables[0]));
}
/**
* This method converts a quantified term.
*
* Currently not implemented, since SMTInterpol does not support it.
*
* <quantifiers>
* This method has to be implemented when quantifiers can be handled.
*
* @param converter non-recursive converter
* @param term quantified term
*/
@Override
public void walk(final NonRecursive converter,
final QuantifiedFormula term) {
throw new UnsupportedOperationException(
"Quantifiers are currently not supported.");
}
/**
* This method converts a term variable.
* A term variable is used in 'let' and quantified formulae.
*
* <quantifiers>
* This method might have to be changed when quantifiers can be
* handled.
*
* @param converter non-recursive converter
* @param term term variable
*/
@Override
public void walk(final NonRecursive converter,
final TermVariable term) {
writeString(toCompatibleString(term.getName()));
}
}
// [end] general conversion //
// [start] specific function conversion //
/**
* This method fills the rule map with the rules.
* For each rule a conversion object is added to a hash map, which handles
* the conversion individually.
*
* Here the rules could be changed or new ones added if necessary.
*/
private void fillMap() {
// Boolean constants
mFunTrans.put("true", new ConstantFunction("True"));
mFunTrans.put("false", new ConstantFunction("False"));
// Boolean functions
mFunTrans.put("and", new AssociativeFunction(
mPrettyOutput ? " \\<and> " : " & "));
mFunTrans.put("or", new AssociativeFunction(
mPrettyOutput ? " \\<or> " : " | "));
/*
* NOTE: implication is right-associative for both
* SMT-LIB and Isabelle, so no change here
*/
mFunTrans.put("=>", new AssociativeFunction(
mPrettyOutput ? " \\<longrightarrow> " : " --> "));
mFunTrans.put("not", new UnaryFunction(
mPrettyOutput ? "\\<not>" : "~"));
/*
* NOTE: xor is not a native function in Isabelle,
* but the additional theory defines it
*/
mFunTrans.put("xor", new AssociativeFunction(
mPrettyOutput ? " \\<oplus> " : " xor "));
mFunTrans.put("ite", new IteFunction());
// (in)equality functions
mFunTrans.put("=", new ChainableFunction(
" = "));
mFunTrans.put("distinct", new DistinctFunction(
mPrettyOutput ? " \\<noteq> " : " ~= "));
// LA specific functions
mFunTrans.put("<=", new ChainableFunction(
mPrettyOutput ? " \\<le> " : " <= "));
mFunTrans.put("<", new ChainableFunction(" < "));
mFunTrans.put(">=", new ChainableFunction(
mPrettyOutput ? " \\<ge> " : " >= "));
mFunTrans.put(">", new ChainableFunction(" > "));
mFunTrans.put("+", new AssociativeFunction(" + "));
// NOTE: '-' can have two meanings, depending on the parameters
mFunTrans.put("-", new MinusFunction());
mFunTrans.put("*", new AssociativeFunction(" * "));
// NOTE: Isabelle automatically converts the type from 'int' to 'real'
mFunTrans.put("/", new AssociativeFunction(" / "));
// NOTE: Isabelle and SMT-LIB have different semantics for mod.
mFunTrans.put("mod", new ModFunction());
// NOTE: Isabelle and SMT-LIB have different semantics for div.
mFunTrans.put("div", new AssociativeFunction(" SMTdiv "));
mFunTrans.put("abs",
mPrettyOutput ? new AbsFunction() : new UnaryFunction("abs"));
mFunTrans.put("divisible", new DivisibleFunction());
mFunTrans.put("to_int", new UnaryFunction("to_int "));
// NOTE: 'to_real' also exists as shorthand 'real'
mFunTrans.put("to_real", new UnaryFunction("to_real "));
mFunTrans.put("is_int", new UnaryFunction("is_int "));
}
/**
* This interface is used for the translation of interpreted functions.
*/
private interface IFunction {
/**
* @param function the function
*/
public abstract void convert(final ApplicationTerm function);
}
/**
* This abstract class is used for the translation with several function
* names.
*/
private abstract class AFunction implements IFunction {
// function name
protected final String mName;
/**
* @param name the function name
*/
public AFunction(final String name) {
mName = name;
}
/**
* @param function the function
*/
@Override
public abstract void convert(final ApplicationTerm function);
}
/**
* This class translates the Boolean constants.
*/
private class ConstantFunction extends AFunction {
/**
* @param name the function name
*/
public ConstantFunction(final String name) {
super(name);
}
/**
* This method converts a Boolean constant ('true' and 'false').
*
* {@inheritDoc}
*/
@Override
public void convert(final ApplicationTerm function) {
writeString(mName);
}
}
/**
* This class translates an associative function.
*/
private class AssociativeFunction extends AFunction {
/**
* @param name the function name
*/
public AssociativeFunction(final String name) {
super(name);
}
/**
* This method converts an associative function. This means:
* The function may have arbitrarily many parameters in Isabelle.
* The function must be binary in principle.
* Example: '+'
*
* {@inheritDoc}
*/
@Override
public void convert(final ApplicationTerm function) {
final Term[] parameters = function.getParameters();
assert (parameters.length > 1);
writeString("(");
enqueueWalker(new StringWalker(")"));
for (int i = parameters.length - 1; i > 0; --i) {
enqueueWalker(new TermConverterWalker(parameters[i]));
enqueueWalker(new StringWalker(mName));
}
enqueueWalker(new TermConverterWalker(parameters[0]));
}
}
/**
* This class translates an chainable function.
*/
private class ChainableFunction extends AFunction {
/**
* @param name the function name
*/
public ChainableFunction(final String name) {
super(name);
}
/**
* This method converts a chainable function. This means:
* The function may only have a constant number of parameters in
* Isabelle.
* The function must be binary in principle.
* Example: '='
*
* The translation uses lambda abstraction to not write complicated
* terms several times (this is even necessary to avoid duplicate
* :named annotations).
*
* {@inheritDoc}
*/
@Override
public void convert(final ApplicationTerm function) {
final Term[] parameters = function.getParameters();
assert (parameters.length > 1);
if (parameters.length == 2) {
writeString("(");
enqueueWalker(new StringWalker(")"));
enqueueWalker(new TermConverterWalker(parameters[1]));
enqueueWalker(new StringWalker(mName));
enqueueWalker(new TermConverterWalker(parameters[0]));
} else {
writeString(mLambda);
for (int i = 1; i <= parameters.length; ++i) {
writeString(" ");
writeString(LAMBDA_VAR_PREFIX);
writeString(Integer.toString(i));
}
writeString(". (");
String andInPar = "";
for (int i = 1; i < parameters.length; ) {
writeString(andInPar);
andInPar = mAndInPar;
writeString(LAMBDA_VAR_PREFIX);
writeString(Integer.toString(i));
writeString(mName);
writeString(LAMBDA_VAR_PREFIX);
writeString(Integer.toString(++i));
}
writeString(")) ");
enqueueWalker(new StringWalker(")"));
for (int i = parameters.length - 1; i > 0; --i) {
enqueueWalker(new TermConverterWalker(parameters[i]));
enqueueWalker(new StringWalker(" "));
}
enqueueWalker(new TermConverterWalker(parameters[0]));
}
}
}
/**
* This class translates an unary function.
*/
private class UnaryFunction extends AFunction {
/**
* @param name the function name
*/
public UnaryFunction(final String name) {
super(name);
}
/**
* This method converts a unary function. This means:
* The function has exactly one argument in Isabelle.
* Parentheses are added to handle all cases equally.
* Examples: 'not, unary -, abs'
*
* NOTE: Isabelle interprets '~~p = p' as '~(~(p = p))',
* so parentheses are needed
*
* {@inheritDoc}
*/
@Override
public void convert(final ApplicationTerm function) {
final Term[] parameters = function.getParameters();
assert (parameters.length == 1);
writeString("(");
writeString(mName);
enqueueWalker(new StringWalker(")"));
enqueueWalker(new TermConverterWalker(parameters[0]));
}
}
/**
* This class translates the distinct function.
*/
private class DistinctFunction extends AFunction {
/**
* @param name the function name
*/
public DistinctFunction(final String name) {
super(name);
}
/**
* This method converts the distinct function. This means:
* The function is not transitive, so every possible combination must
* be generated.
* Example:
* '(distinct (a b c))' means '(a != b) && (a != c) && (b != c)'
*
* The translation uses lambda abstraction to not write complicated
* terms several times (this is even necessary to avoid duplicate
* :named annotations).
*
* {@inheritDoc}
*/
@Override
public void convert(final ApplicationTerm function) {
final Term[] parameters = function.getParameters();
assert (parameters.length > 1);
if (parameters.length == 2) {
writeString("(");
enqueueWalker(new StringWalker(")"));
enqueueWalker(new TermConverterWalker(parameters[1]));
enqueueWalker(new StringWalker(mName));
enqueueWalker(new TermConverterWalker(parameters[0]));
} else {
writeString(mLambda);
for (int i = 1; i <= parameters.length; ++i) {
writeString(" ");
writeString(LAMBDA_VAR_PREFIX);
writeString(Integer.toString(i));
}
writeString(". (");
String andInPar = "";
for (int i = 1; i < parameters.length; ++i) {
for (int j = i; j < parameters.length; ) {
writeString(andInPar);
andInPar = mAndInPar;
writeString(LAMBDA_VAR_PREFIX);
writeString(Integer.toString(i));
writeString(mName);
writeString(LAMBDA_VAR_PREFIX);
writeString(Integer.toString(++j));
}
}
writeString(")) ");
enqueueWalker(new StringWalker(")"));
for (int i = parameters.length - 1; i > 0; --i) {
enqueueWalker(new TermConverterWalker(parameters[i]));
enqueueWalker(new StringWalker(" "));
}
enqueueWalker(new TermConverterWalker(parameters[0]));
}
}
}
/**
* This class translates the minus function.
*/
private class MinusFunction implements IFunction {
// converter for exactly one parameter
private final UnaryFunction mUnary;
// converter for more than one parameters
private final AssociativeFunction mAssociative;
/**
* constructor
*/
public MinusFunction() {
mUnary = new UnaryFunction("- ");
mAssociative = new AssociativeFunction(" - ");
}
/**
* This method converts the minus ('-') function.
* It can be either a sign or an operator, depending on the parameter
* count.
*
* {@inheritDoc}
*/
@Override
public void convert(final ApplicationTerm function) {
final Term[] parameters = function.getParameters();
assert (parameters.length > 0);
// negative sign
if (parameters.length == 1) {
mUnary.convert(function);
} else {
// associative minus operator
mAssociative.convert(function);
}
}
}
/**
* This class translates the ite (if-then-else) function.
*/
private class IteFunction implements IFunction {
/**
* This method converts the if-then-else function. This means:
* The function has exactly three arguments in Isabelle.
*
* {@inheritDoc}
*/
@Override
public void convert(final ApplicationTerm function) {
final Term[] parameters = function.getParameters();
assert (parameters.length == 3); // NOCHECKSTYLE
writeString("(if ");
enqueueWalker(new StringWalker(")"));
enqueueWalker(new TermConverterWalker(parameters[2]));
enqueueWalker(new StringWalker(" else "));
enqueueWalker(new TermConverterWalker(parameters[1]));
enqueueWalker(new StringWalker(" then "));
enqueueWalker(new TermConverterWalker(parameters[0]));
}
}
/**
* This class translates the modulo function.
*/
private class ModFunction implements IFunction {
/**
* This method converts the modulo function. This means:
* The function has exactly two arguments in Isabelle.
* Example: 'mod'
*
* {@inheritDoc}
*/
@Override
public void convert(final ApplicationTerm function) {
final Term[] parameters = function.getParameters();
assert (parameters.length == 2);
writeString("(");
enqueueWalker(new StringWalker(")"));
enqueueWalker(new TermConverterWalker(parameters[1]));
enqueueWalker(new StringWalker(" SMTmod "));
enqueueWalker(new TermConverterWalker(parameters[0]));
}
}
/**
* This class translates the divisible function.
*/
private class DivisibleFunction implements IFunction {
/**
* This method handles the special conversion of the 'divisible'
* predicate.
* This is necessary due to syntax anomalies in SMT-LIB.
*
* {@inheritDoc}
*/
@Override
public void convert(final ApplicationTerm function) {
final Term[] parameters = function.getParameters();
final BigInteger[] indices = function.getFunction().getIndices();
assert ((parameters.length == 1) && (indices.length == 1));
writeString("(");
writeString(indices[0].toString());
writeString(" dvd ");
enqueueWalker(new StringWalker(")"));
enqueueWalker(new TermConverterWalker(parameters[0]));
}
}
/**
* This class translates the abs (absolute value) function with pretty
* printing.
*/
private class AbsFunction implements IFunction {
/**
* This method handles the special conversion of the 'abs' function
* with pretty printing, which is written '\<bar> expression \<bar>'.
*
* {@inheritDoc}
*/
@Override
public void convert(final ApplicationTerm function) {
final Term[] parameters = function.getParameters();
assert (parameters.length == 1);
writeString(" \\<bar>");
enqueueWalker(new StringWalker("\\<bar> "));
enqueueWalker(new TermConverterWalker(parameters[0]));
}
}
/**
* This method converts non-intern functions, i.e., functions not defined
* in SMT-LIB.
* Prefix notation is used.
*
* @param function non-intern function symbol
* @param parameters parameters of the function
*/
private void toNoninternFunction(final FunctionSymbol function,
Term[] parameters) {
if (parameters.length > 0) {
writeString("(");
writeSortString(function);
enqueueWalker(new StringWalker(")"));
for (int i = parameters.length - 1; i >= 0; --i) {
enqueueWalker(new TermConverterWalker(parameters[i]));
enqueueWalker(new StringWalker(" "));
}
} else {
writeSortString(function);
}
}
/**
* This method writes a function string.
* The parameter and return sorts are also written if globally enabled.
*
* @param functionSymbol the function symbol
*/
private void writeSortString(final FunctionSymbol functionSymbol) {
// non-theory Isabelle-interpreted functions
String functionName = mFunction2compFunction.get(functionSymbol);
// only print types the first time and also store compatible names
if (functionName != null) {
writeString(functionName);
return;
}
functionName = toCompatibleString(functionSymbol.getName());
if (mPrintTypes) {
final String arrow = mPrettyOutput ? "\\<Rightarrow>" : "=>";
final StringBuilder builder = new StringBuilder();
builder.append('(');
builder.append(functionName);
builder.append("::");
for (final Sort paramSort : functionSymbol.getParameterSorts()) {
builder.append(getSingleSortString(paramSort));
builder.append(arrow);
}
builder.append(
getSingleSortString(functionSymbol.getReturnSort()));
builder.append(')');
functionName = builder.toString();
}
writeString(functionName);
mFunction2compFunction.put(functionSymbol, functionName);
}
/**
* This method returns the Isabelle name for a given sort.
*
* @param sort the sort
* @return the Isabelle name of the sort
*/
public String getSingleSortString(final Sort sort) {
final String sortString = sort.getName();
if (sortString == "Int") {
return "int";
} else if (sortString == "Real") {
return "real";
} else if (sortString == "Bool") {
return "bool";
} else {
assert (!sort.isInternal());
return "'a";
}
}
/**
* This method converts user-defined strings to Isabelle compatible
* strings.
*
* @param functionSymbol the function symbol
* @return the modified function symbol
*/
String toCompatibleString(final String functionSymbol) {
final char[] origin = functionSymbol.toCharArray();
final StringBuilder builder = new StringBuilder();
builder.append("smt_");
final Pattern valid = Pattern.compile("[a-zA-Z0-9]");
for (int i = 0; i < origin.length; ++i) {
final String next = Character.toString(origin[i]);
if (valid.matcher(next).matches()) {
builder.append(next);
} else {
builder.append('_');
builder.append(next.codePointAt(0));
}
}
return builder.toString();
}
// [end] specific function conversion //
// [start] additional functionalities //
/**
* This method is called to indicate the start or end of the proof mode.
*/
public void switchProofMode() {
mNamedList = null;
mNamedStack = null;
mFunction2compFunction.clear();
}
/**
* This method converts an assertion and returns the :named definitions.
*
* @param assertion the assertion
* @return a list with the :named definitions
*/
public LinkedList<NamedWrapper> convertAssertion(final Term assertion) {
mNamedList = new LinkedList<NamedWrapper>();
mNamedStack = new ArrayDeque<NamedWrapper>();
mPrintTypes = true;
convert(assertion);
mPrintTypes = false;
return mNamedList;
}
/**
* This method supports temporarily writing to a different output
* appendable. Afterwards the old appendable is used again.
*
* @param term the SMT-LIB term
* @param appendable the temporary appendable
*/
public void convertToAppendable(final Term term,
final Appendable appendable) {
final Appendable tmp = mAppendable;
mAppendable = appendable;
convert(term);
mAppendable = tmp;
}
/**
* This method writes to another appendable and prints (numeric) types.
* It is used by the LA lemma.
*
* @param term the SMT-LIB term
* @param appendable the lemma appendable
*/
public void convertWithTypes(final Term term,
final Appendable appendable) {
mPrintTypes = true;
mNamedStack = null;
convertToAppendable(term, appendable);
mPrintTypes = false;
}
/**
* This method provides writing rational numbers without converting them to
* decimal numbers (which could be non-terminating).
*
* This is only needed in the conversion of real arithmetic lemma, so it
* directly converts the data structure used there. The result is written
* to the appendable and looks like:
* 'f1 * x1 + ... + fn * xn + c', where the fi are factors, the xi are
* arbitrary terms seen as variables, and c is a constant. If an fi is 1 or
* the c is 0, it is ignored.
*
* @param constant the constant
* @param collection data structure of LA lemma
* @param appendable lemma appendable
*/
public void convertFactorMap(Rational constant,
final Collection<Map.Entry<Term, FactorWrapper>> collection,
final Appendable appendable) {
assert (!collection.isEmpty());
final Appendable tmp = mAppendable;
mAppendable = appendable;
writeString("(");
// variables
String plus = "";
for (final Map.Entry<Term, FactorWrapper> tuple : collection) {
writeString(plus);
plus = " + ";
Rational rational = tuple.getValue().mFactor;
final boolean isNegative = rational.isNegative();
if (isNegative) {
rational = rational.negate();
}
if (rational.isIntegral()) {
if (isNegative) {
writeString("- ");
}
writeString(rational.numerator().toString());
writeString(" * ");
convert(tuple.getKey());
} else {
if (isNegative) {
writeString("- ");
}
writeString("(");
writeString(rational.numerator().toString());
writeString(" / ");
writeString(rational.denominator().toString());
writeString(") * ");
convert(tuple.getKey());
}
}
// constant
if (!constant.equals(Rational.ZERO)) {
final boolean isNegative = constant.isNegative();
if (isNegative) {
constant = constant.negate();
}
writeString(" + ");
if (constant.isIntegral()) {
if (isNegative) {
writeString("- ");
}
writeString(constant.numerator().toString());
} else {
if (isNegative) {
writeString("- ");
}
writeString("(");
writeString(constant.numerator().toString());
writeString(" / ");
writeString(constant.denominator().toString());
writeString(")");
}
}
writeString(")");
mAppendable = tmp;
}
public String getFunctionName(final FunctionSymbol functionSymbol) {
String name = mFunction2compFunction.get(functionSymbol);
if (name == null) {
name = toCompatibleString(functionSymbol.getName());
}
assert (name != null);
return name;
}
// [end] additional functionalities //
// [start] stack 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(final String string) {
try {
mAppendable.append(string);
} catch (final IOException e) {
throw new RuntimeException("Appender throws IOException", e);
}
}
// [end] stack related //
}