/**
* Author: Georg Hofferek <georg.hofferek@iaik.tugraz.at>
*/
package at.iaik.suraq.sexp;
import java.io.IOException;
import java.io.Serializable;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import at.iaik.suraq.exceptions.NotATokenListException;
import at.iaik.suraq.exceptions.ParseError;
import at.iaik.suraq.main.SuraqOptions;
import at.iaik.suraq.parser.SExpParser;
import at.iaik.suraq.smtlib.formula.Formula;
import at.iaik.suraq.smtlib.formula.PropositionalVariable;
/**
* This class represents s-expressions. It consists of a list of subexpressions.
*
* @author Georg Hofferek <georg.hofferek@iaik.tugraz.at>
*
*/
public class SExpression implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* the line number in the source file corresponding to this expression
*/
protected int lineNumber = -1;
/**
* the column number in the source file corresponding to this expression
*/
protected int columnNumber = -1;
/**
* The parents of this (non-Token) S-expression.
*/
private final List<SExpression> children;
/**
* Constructs a new <code>SExpression</code>.
*
* @param parents
* the subexpressions.
*/
public SExpression(List<? extends SExpression> children) {
this.children = new ArrayList<SExpression>(children);
// this.children.addAll(parents);
}
/**
* Constructs a new <code>SExpression</code>.
*
* @param parents
* the subexpressions.
*/
public SExpression(SExpression[] children) {
ArrayList<SExpression> tmp = new ArrayList<SExpression>();
for (SExpression child : children)
tmp.add(child);
this.children = new ArrayList<SExpression>(tmp);
}
/**
* Constructs a new, empty <code>SExpression</code>.
*/
public SExpression() {
this.children = new ArrayList<SExpression>(new ArrayList<SExpression>());
}
/**
* Constructs a new <code>SExpression</code>, with only one child.
*
* @param child
* the only child of this s-expression.
*/
public SExpression(SExpression child) {
if (child == null)
throw new RuntimeException(
"empty child found! null is not allowed!");
ArrayList<SExpression> tmp = new ArrayList<SExpression>();
tmp.add(child);
this.children = new ArrayList<SExpression>(tmp);
}
/**
* Convenience method to construct an <code>SExpression</code> with exactly
* two parents. Constructs a new <code>SExpression</code>.
*
* @param first
* the first child
* @param second
* the second child
*/
public SExpression(SExpression first, SExpression second) {
if (first == null)
throw new RuntimeException(
"empty child found! null is not allowed!");
if (second == null)
throw new RuntimeException(
"empty child found! null is not allowed!");
ArrayList<SExpression> tmp = new ArrayList<SExpression>();
tmp.add(first);
tmp.add(second);
this.children = new ArrayList<SExpression>(tmp);
}
/**
* Convenience method to construct an <code>SExpression</code> with exactly
* three parents. Constructs a new <code>SExpression</code>.
*
* @param first
* the first child
* @param second
* the second child
* @param third
* the third child
*/
public SExpression(SExpression first, SExpression second, SExpression third) {
if (first == null)
throw new RuntimeException(
"empty child found! null is not allowed!");
if (second == null)
throw new RuntimeException(
"empty child found! null is not allowed!");
if (third == null)
throw new RuntimeException(
"empty child found! null is not allowed!");
ArrayList<SExpression> tmp = new ArrayList<SExpression>();
tmp.add(first);
tmp.add(second);
tmp.add(third);
this.children = new ArrayList<SExpression>(tmp);
}
/**
*
* Constructs a new <code>SExpression</code> by parsing the given String. If
* <code>string</code> contains a single expression (or token), then this
* single expression (token) is returned. If it contains a sequence of
* expressions (tokens) then this sequence will be embedded into a root
* expression.
*
* @param string
* the <code>String</code> to be parsed.
* @return the parsed expression, or <code>null</code> if parsing was
* unsuccessful;
*/
public static SExpression fromString(String string) {
SExpParser parser = new SExpParser(string);
try {
parser.parse();
} catch (ParseError exc) {
return null;
}
if (!parser.wasParsingSuccessfull())
return null;
List<SExpression> children = parser.getRootExpr().getChildren();
if (children.size() == 1)
return children.get(0);
else
return new SExpression(children);
}
/**
* @return the size of this s-expression, i.e., the number of parents. 0, if
* empty.
*/
public int size() {
return children.size();
}
/**
* @return <code>true</code> if this s-expression is empty,
* <code>false</code> otherwise.
*/
public boolean isEmpty() {
return (children.size() == 0);
}
/**
* Adds the given s-expression to the end of the list of parents of this
* one.
*
* @param sexp
* the new child to add
*/
public void addChild(SExpression sexp) {
if (sexp == null)
throw new RuntimeException(
"empty child found! null is not allowed!");
children.add(sexp);
}
/**
* Inserts the given <code>newChild</code> into this
* <code>SExpression</code> so that it follows the given
* <code>precedingChild</code>. If <code>precedingChild</code> is not a
* child of this expression, nothing happens.
*
* @param newChild
* @param precedingChild
*/
public void insertChildAfter(SExpression newChild,
SExpression precedingChild) {
if (!children.contains(precedingChild)) {
if (SuraqOptions.getInstance().isVerbose())
System.out
.println("Tried to insert an SExpression after a child that was not found!");
return;
}
int position = children.indexOf(precedingChild);
children.add(new Token("dummy"));
for (int index = children.size() - 1; index > position + 1; index--) {
children.set(index, children.get(index - 1));
}
children.set(position + 1, newChild);
}
/**
* Replaces the given s-expression in the parents of this one, at the
* specified position.
*
* @param sexp
* the new child to add.
* @param position
* the position of the child to replace.
*/
public void replaceChild(SExpression sexp, int position) {
if (sexp == null)
throw new RuntimeException(
"empty child found! null is not allowed!");
// parents.set(position, sexp);
children.add(position, sexp);
children.remove(position + 1);
}
/**
* Adds the given s-expression to the parents of this one, at the specified
* position.
*
* @param sexp
* the new child to add.
* @param position
* the position of the new child.
*/
public void addChild(SExpression sexp, int position) {
if (sexp == null)
throw new RuntimeException(
"empty child found! null is not allowed!");
children.add(position, sexp);
}
/**
* Removes the child with the specified index.
*
* @param index
* index of the element to remove.
*/
public void removeChild(int index) {
children.remove(index);
}
/**
* Returns the list of parents. This is the live list, not a copy.
*
* @return the list of parents (not a copy).
*/
public List<SExpression> getChildren() {
return children;
}
/**
* Converts this s-expression into a list of <code>Token</code>s. Only
* direct parents will be considered. If this expression is empty, an empty
* list will be returned. If one or more parents are not <code>Token</code>
* s, an exception is thrown.
*
* @return A list of all <code>Token</code>s that are (direct) parents of
* this expression.
* @throws NotATokenListException
* if one or more parents are not <code>Token</code>s.
*/
public List<Token> toTokenList() throws NotATokenListException {
List<Token> list = new ArrayList<Token>();
if (isEmpty())
return list;
assert (children != null);
for (int count = 0; count < children.size(); count++) {
if (!(children.get(count) instanceof Token))
throw new NotATokenListException(children.get(count).toString()
+ " is not a Token!");
else
list.add((Token) children.get(count));
}
return list;
}
/**
* Returns a pretty-printed string representation of this s-expression.
*
* @return a pretty-printed string representation of this s-expression.
*/
@Override
public String toString() {
// does not terminate
if (isEmpty())
return "()";
if (size() == 1)
return "(" + children.get(0).toString() + ")";
StringBuffer buffer = new StringBuffer();
buffer.append("(\n ");
for (SExpression child : children) {
buffer.append((child instanceof Token ? child.toString() + "\n"
: child.toString() + ((child.size() > 1) ? "" : "\n"))
.replace("\n", "\n "));
}
if (buffer.substring(buffer.length() - 2).equals(" "))
buffer = buffer.delete(buffer.length() - 2, buffer.length());
else
buffer.append("\n");
buffer.append(")\n");
return buffer.toString();
}
/**
* Recursively copies this s-expression.
*
* @return a deep copy of this s-expression
*/
public SExpression deepCopy() {
SExpression copy = new SExpression();
for (SExpression child : children) {
copy.addChild(child.deepCopy());
}
return copy;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof SExpression))
return false;
if (obj instanceof Token && !(this instanceof Token))
return false;
SExpression other = ((SExpression) obj);
if (children.size() != other.children.size())
return false;
for (int count = 0; count < children.size(); count++)
if (!children.get(count).equals(other.children.get(count)))
return false;
return true;
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int code = 0;
for (int count = 0; count < children.size(); count++) {
code = code ^ children.get(count).hashCode();
}
return code;
}
/**
* @return the <code>lineNumber</code>
*/
public int getLineNumber() {
return lineNumber;
}
/**
* @param <code>lineNumber</code> the new value for <code>lineNumber</code>
*/
public void setLineNumber(int lineNumber) {
this.lineNumber = lineNumber;
}
/**
* @return the <code>columnNumber</code>
*/
public int getColumnNumber() {
return columnNumber;
}
/**
* @param <code>columnNumber</code> the new value for
* <code>columnNumber</code>
*/
public void setColumnNumber(int columnNumber) {
this.columnNumber = columnNumber;
}
/**
* Creates an SExpression for a function declaration. Produces something of
* the form <code>(declare-fun name () type)</code>. If
* <code>numParams > 0</code> the corresponding number of <code>Value</code>
* parameters will be added. E.g. for <code>numParams==2</code>:
* <code>(declare-fun name (Value Value) type)</code>.
*
* @param name
* the name of the function
* @param type
* the type of the function
* @param numParams
* the number of parameters (all of type <code>Value</code>).
* Negative values will be treated as zero.
* @return an <code>SExpression</code> declaring the specified function.
*/
public static SExpression makeDeclareFun(Token name, SExpression type,
int numParams) {
if (type == null)
throw new RuntimeException("null 'type' is not allowed!");
SExpression result = new SExpression();
result.addChild(SExpressionConstants.DECLARE_FUN);
result.addChild(name);
if (numParams > 0) {
SExpression params = new SExpression();
for (int count = 0; count < numParams; count++)
params.addChild(SExpressionConstants.VALUE_TYPE);
result.addChild(params);
} else
result.addChild(SExpressionConstants.EMPTY);
result.addChild(type);
return result;
}
/**
* Creates an SExpression for an assert statement for a control signal. Is
* used to check if formula is a valid implementation for a control signal.
*
* @param controlSignal
* the control signal for which a assert statement is generated
* @param controlFormula
* the formula that implements the signal
* @return an <code>SExpression</code> for the assert statement
*/
public static SExpression makeControlAssert(
PropositionalVariable controlSignal, Formula controlFormula) {
SExpression eqFormulaExp = new SExpression(SExpressionConstants.EQUAL,
SExpression.fromString(controlSignal.toString()),
controlFormula.toSmtlibV2());
SExpression result = new SExpression(SExpressionConstants.ASSERT,
eqFormulaExp);
return result;
}
/**
* Checks an expression to be a valid proof type.
*
* @param expr
* the expression to check
* @return an <code>Boolean</code> value declaring iff expr is a valid proof
* type
*/
public static Boolean isValidProofType(Token expr) {
for (SExpression proofType : SExpressionConstants.PROOF_TYPES) {
if (proofType.equals(expr))
return true;
}
return false;
}
/**
* Checks an expression to be a valid formula type.
*
* @param expr
* the expression to check
* @return an <code>Boolean</code> value declaring iff expr is a valid
* formula type
*/
public static Boolean isValidFormulaType(Token expr) {
if (SExpressionConstants.AND.equals(expr))
return true;
if (SExpressionConstants.EQUAL.equals(expr))
return true;
if (SExpressionConstants.IMPLIES.equals(expr)
|| SExpressionConstants.IMPLIES_ALT.equals(expr))
return true;
if (SExpressionConstants.NOT.equals(expr))
return true;
if (SExpressionConstants.OR.equals(expr))
return true;
if (SExpressionConstants.XOR.equals(expr))
return true;
if (SExpressionConstants.ITE.equals(expr))
return true;
return false;
}
/**
* Replaces all the tokens in this expression (and sub-expressions) with
* their associated values in the given map.
*
* @param replacements
*/
public void replace(Map<Token, SExpression> replacements) {
for (int count = 0; count < children.size(); count++) {
SExpression child = children.get(count);
if (child instanceof Token) {
for (Token token : replacements.keySet()) {
if (child.equals(token)) {
children.set(count, replacements.get(token));
break;
}
}
} else
child.replace(replacements);
}
}
/**
* @return
*/
public boolean isAssert() {
if (children.size() != 2)
return false;
if (!children.get(0).equals(SExpressionConstants.ASSERT))
return false;
return true;
}
/**
* @return
*/
public boolean isDeclareFun() {
if (children.size() < 4)
return false;
if (!children.get(0).equals(SExpressionConstants.DECLARE_FUN))
return false;
return true;
}
/**
*
*/
public void changeControlAndNoDepDefs() {
if (!isDeclareFun())
return;
List<SExpression> childrenCopy = new ArrayList<SExpression>(
children.size());
for (int count = 0; count < children.size(); count++) {
SExpression child = children.get(count);
if (child.equals(SExpressionConstants.CONTROL_TYPE)) {
childrenCopy.add(SExpressionConstants.BOOL_TYPE);
continue;
}
if (child.equals(SExpressionConstants.NO_DEPENDENCE))
continue;
childrenCopy.add(child);
}
children.clear();
children.addAll(childrenCopy);
}
/**
* writes this expression to <code>writer</code>.
*
* @param writer
* @throws IOException
*/
public void writeTo(Writer writer) throws IOException {
writer.write("(");
for (SExpression child : children) {
writer.write(" ");
child.writeTo(writer);
}
writer.write(")");
}
}