/*
* Kodkod -- Copyright (c) 2005-present, Emina Torlak
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package kodkod.ast;
import static kodkod.ast.operator.ExprCastOperator.CARDINALITY;
import static kodkod.ast.operator.ExprCastOperator.SUM;
import static kodkod.ast.operator.ExprCompOperator.EQUALS;
import static kodkod.ast.operator.ExprCompOperator.SUBSET;
import static kodkod.ast.operator.ExprOperator.CLOSURE;
import static kodkod.ast.operator.ExprOperator.DIFFERENCE;
import static kodkod.ast.operator.ExprOperator.INTERSECTION;
import static kodkod.ast.operator.ExprOperator.JOIN;
import static kodkod.ast.operator.ExprOperator.OVERRIDE;
import static kodkod.ast.operator.ExprOperator.PRODUCT;
import static kodkod.ast.operator.ExprOperator.REFLEXIVE_CLOSURE;
import static kodkod.ast.operator.ExprOperator.TRANSPOSE;
import static kodkod.ast.operator.ExprOperator.UNION;
import static kodkod.ast.operator.Multiplicity.LONE;
import static kodkod.ast.operator.Multiplicity.NO;
import static kodkod.ast.operator.Multiplicity.ONE;
import static kodkod.ast.operator.Multiplicity.SOME;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import kodkod.ast.operator.ExprCastOperator;
import kodkod.ast.operator.ExprCompOperator;
import kodkod.ast.operator.ExprOperator;
import kodkod.ast.operator.Multiplicity;
import kodkod.ast.visitor.ReturnVisitor;
import kodkod.util.collections.Containers;
/**
* A relational expression. Unless otherwise noted,
* all methods in this class throw a NullPointerException when given
* null arguments.
*
* @specfield arity: int
* @invariant arity > 0
*
* @author Emina Torlak
*/
public abstract class Expression extends Node {
/** The universal relation: contains all atoms in a {@link kodkod.instance.Universe universe of discourse}. */
public static final Expression UNIV = new ConstantExpression("univ", 1);
/** The identity relation: maps all atoms in a {@link kodkod.instance.Universe universe of discourse} to themselves. */
public static final Expression IDEN = new ConstantExpression("iden", 2);
/** The empty relation: contains no atoms. */
public static final Expression NONE = new ConstantExpression("none", 1);
/** The integer relation: contains all atoms {@link kodkod.instance.Bounds bound} to integers */
public static final Expression INTS = new ConstantExpression("ints", 1);
/**
* Constructs a leaf expression
* @ensures no this.children'
*/
Expression() { }
/**
* Returns the join of this and the specified expression. The effect
* of this method is the same as calling this.compose(JOIN, expr).
* @return this.compose(JOIN, expr)
*/
public final Expression join(Expression expr) {
return compose(JOIN,expr);
}
/**
* Returns the product of this and the specified expression. The effect
* of this method is the same as calling this.compose(PRODUCT, expr).
* @return this.compose(PRODUCT, expr)
*/
public final Expression product(Expression expr) {
return compose(PRODUCT,expr);
}
/**
* Returns the union of this and the specified expression. The effect
* of this method is the same as calling this.compose(UNION, expr).
* @return this.compose(UNION, expr)
*/
public final Expression union(Expression expr) {
return compose(UNION,expr);
}
/**
* Returns the difference of this and the specified expression. The effect
* of this method is the same as calling this.compose(DIFFERENCE, expr).
* @return this.compose(DIFFERENCE, expr)
*/
public final Expression difference(Expression expr) {
return compose(DIFFERENCE,expr);
}
/**
* Returns the intersection of this and the specified expression. The effect
* of this method is the same as calling this.compose(INTERSECTION, expr).
* @return this.compose(INTERSECTION, expr)
*/
public final Expression intersection(Expression expr) {
return compose(INTERSECTION,expr);
}
/**
* Returns the relational override of this with the specified expression. The effect
* of this method is the same as calling this.compose(OVERRIDE, expr).
* @return this.compose(OVERRIDE, expr)
*/
public final Expression override(Expression expr) {
return compose(OVERRIDE,expr);
}
/**
* Returns the composition of this and the specified expression, using the
* given binary operator.
* @requires op in ExprOperator.BINARY
* @return {e: Expression | e.left = this and e.right = expr and e.op = this }
*/
public final Expression compose(ExprOperator op, Expression expr) {
return new BinaryExpression(this, op, expr);
}
/**
* Returns the union of the given expressions. The effect of this method is the
* same as calling compose(UNION, exprs).
* @return compose(UNION, exprs)
*/
public static Expression union(Expression...exprs) {
return compose(UNION, exprs);
}
/**
* Returns the union of the given expressions. The effect of this method is the
* same as calling compose(UNION, exprs).
* @return compose(UNION, exprs)
*/
public static Expression union(Collection<? extends Expression> exprs) {
return compose(UNION, exprs);
}
/**
* Returns the intersection of the given expressions. The effect of this method is the
* same as calling compose(INTERSECTION, exprs).
* @return compose(INTERSECTION, exprs)
*/
public static Expression intersection(Expression...exprs) {
return compose(INTERSECTION, exprs);
}
/**
* Returns the intersection of the given expressions. The effect of this method is the
* same as calling compose(INTERSECTION, exprs).
* @return compose(INTERSECTION, exprs)
*/
public static Expression intersection(Collection<? extends Expression> exprs) {
return compose(INTERSECTION, exprs);
}
/**
* Returns the product of the given expressions. The effect of this method is the
* same as calling compose(PRODUCT, exprs).
* @return compose(PRODUCT, exprs)
*/
public static Expression product(Expression...exprs) {
return compose(PRODUCT, exprs);
}
/**
* Returns the product of the given expressions. The effect of this method is the
* same as calling compose(PRODUCT, exprs).
* @return compose(PRODUCT, exprs)
*/
public static Expression product(Collection<? extends Expression> exprs) {
return compose(PRODUCT, exprs);
}
/**
* Returns the override of the given expressions. The effect of this method is the
* same as calling compose(OVERRIDE, exprs).
* @return compose(OVERRIDE, exprs)
*/
public static Expression override(Expression...exprs) {
return compose(OVERRIDE, exprs);
}
/**
* Returns the override of the given expressions. The effect of this method is the
* same as calling compose(OVERRIDE, exprs).
* @return compose(OVERRIDE, exprs)
*/
public static Expression override(Collection<? extends Expression> exprs) {
return compose(OVERRIDE, exprs);
}
/**
* Returns the composition of the given expressions using the given operator.
* @requires exprs.length = 2 => op.binary(), exprs.length > 2 => op.nary()
* @return exprs.length=1 => exprs[0] else {e: Expression | e.children = exprs and e.op = this }
*/
public static Expression compose(ExprOperator op, Expression...exprs) {
switch(exprs.length) {
case 0 : throw new IllegalArgumentException("Expected at least one argument: " + Arrays.toString(exprs));
case 1 : return exprs[0];
case 2 : return new BinaryExpression(exprs[0], op, exprs[1]);
default : return new NaryExpression(op, Containers.copy(exprs, new Expression[exprs.length]));
}
}
/**
* Returns the composition of the given expressions using the given operator.
* @requires exprs.size() = 2 => op.binary(), exprs.size() > 2 => op.nary()
* @return exprs.size()=1 => exprs.iterator().next() else {e: Expression | e.children = exprs.toArray() and e.op = this }
*/
public static Expression compose(ExprOperator op, Collection<? extends Expression> exprs) {
switch(exprs.size()) {
case 0 : throw new IllegalArgumentException("Expected at least one argument: " + exprs);
case 1 : return exprs.iterator().next();
case 2 :
final Iterator<? extends Expression> itr = exprs.iterator();
return new BinaryExpression(itr.next(), op, itr.next());
default :
return new NaryExpression(op, exprs.toArray(new Expression[exprs.size()]));
}
}
/**
* Returns the transpose of this. The effect of this method is the same
* as calling this.apply(TRANSPOSE).
* @return this.apply(TRANSPOSE)
*/
public final Expression transpose() {
return apply(TRANSPOSE);
}
/**
* Returns the transitive closure of this. The effect of this method is the same
* as calling this.apply(CLOSURE).
* @return this.apply(CLOSURE)
*/
public final Expression closure() {
return apply(CLOSURE);
}
/**
* Returns the reflexive transitive closure of this. The effect of this
* method is the same
* as calling this.apply(REFLEXIVE_CLOSURE).
* @return this.apply(REFLEXIVE_CLOSURE)
*/
public final Expression reflexiveClosure() {
return apply(REFLEXIVE_CLOSURE);
}
/**
* Returns the expression that results from applying the given unary operator
* to this.
* @requires op.unary()
* @return {e: Expression | e.expression = this && e.op = this }
* @throws IllegalArgumentException this.arity != 2
*/
public final Expression apply(ExprOperator op) {
return new UnaryExpression(op, this);
}
/**
* Returns the projection of this expression onto the specified columns.
* @return {e: Expression | e = project(this, columns) }
* @throws IllegalArgumentException columns.length < 1
*/
public final Expression project(IntExpression... columns) {
return new ProjectExpression(this, columns);
}
/**
* Returns the cardinality of this expression. The effect of this method is the
* same as calling this.apply(CARDINALITY).
* @return this.apply(CARDINALITY)
*/
public final IntExpression count() {
return apply(CARDINALITY);
}
/**
* Returns the sum of the integer atoms in this expression. The effect of this method is the
* same as calling this.apply(SUM).
* @return this.apply(SUM)
*/
public final IntExpression sum() {
return apply(SUM);
}
/**
* Returns the cast of this expression to an integer expression,
* that represents either the cardinality of this expression (if op is CARDINALITY)
* or the sum of the integer atoms it contains (if op is SUM).
* @return {e: IntExpression | e.op = op && e.expression = this}
*/
public final IntExpression apply(ExprCastOperator op) {
return new ExprToIntCast(this, op);
}
/**
* Returns the formula 'this = expr'. The effect of this method is the same
* as calling this.compare(EQUALS, expr).
* @return this.compare(EQUALS, expr)
*/
public final Formula eq(Expression expr) {
return compare(EQUALS, expr);
}
/**
* Returns the formula 'this in expr'. The effect of this method is the same
* as calling this.compare(SUBSET, expr).
* @return this.compare(SUBSET, expr)
*/
public final Formula in(Expression expr) {
return compare(SUBSET, expr);
}
/**
* Returns the formula that represents the comparison of this and the
* given expression using the given comparison operator.
* @return {f: Formula | f.left = this && f.right = expr && f.op = op}
*/
public final Formula compare(ExprCompOperator op, Expression expr) {
return new ComparisonFormula(this, op, expr);
}
/**
* Returns the formula 'some this'. The effect of this method is the same as calling
* this.apply(SOME).
* @return this.apply(SOME)
*/
public final Formula some() {
return apply(SOME);
}
/**
* Returns the formula 'no this'. The effect of this method is the same as calling
* this.apply(NO).
* @return this.apply(NO)
*/
public final Formula no() {
return apply(NO);
}
/**
* Returns the formula 'one this'. The effect of this method is the same as calling
* this.apply(ONE).
* @return this.apply(ONE)
*/
public final Formula one() {
return apply(ONE);
}
/**
* Returns the formula 'lone this'. The effect of this method is the same as calling
* this.apply(LONE).
* @return this.apply(LONE)
*/
public final Formula lone() {
return apply(LONE);
}
/**
* Returns the formula that results from applying the specified multiplicity to
* this expression. The SET multiplicity is not allowed.
* @return {f: Formula | f.multiplicity = mult && f.expression = this}
* @throws IllegalArgumentException mult = SET
*/
public final Formula apply(Multiplicity mult) {
return new MultiplicityFormula(mult, this);
}
/**
* Returns the arity of this expression.
* @return this.arity
*/
public abstract int arity();
/**
* Accepts the given visitor and returns the result.
* @see kodkod.ast.Node#accept(kodkod.ast.visitor.ReturnVisitor)
*/
public abstract <E, F, D, I> E accept(ReturnVisitor<E, F, D, I> visitor);
}