/*
* Kodkod -- Copyright (c) 2005-2011, 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.engine.fol2sat;
import kodkod.ast.Variable;
/**
* Represents a variable binding environment as a
* map from a {@link kodkod.ast.Variable variable} to a
* {@link java.lang.Object value}. An environment has
* a (possibly empty) parent environment to which unsuccessful lookups
* are delegated.
*
* @specfield variable: lone Variable
* @specfield value: lone T
* @specfield type: lone E
* @specfield parent: Environment
* @invariant this = parent => no variable
* @author Emina Torlak
*/
public final class Environment<T, E> {
private final Variable variable;
private final T value;
private final E type;
private final Environment<T, E> parent;
private final Object envType; // may be null, but will typically contain Quantifier.ALL or Quantifier.SOME
private boolean negated;
/**
* Constructs the empty environment.
*/
private Environment() {
this.variable = null;
this.value = null;
this.type = null;
this.parent = this;
this.envType = null;
this.negated = false;
}
/**
* Constructs a new environment with the specified parent
* and mapping.
*
* @ensures this.parent' = parent && this.variable' = variable && this.value' = value
*/
private Environment(Environment<T, E> parent, Variable variable, E type, T value, Object quant, boolean negated) {
this.variable = variable;
this.value = value;
this.type = type;
this.parent = parent;
this.envType = quant;
this.negated = negated;
}
/**
* Returns the empty environment.
* @return the empty environment.
*/
public static <T, E> Environment<T, E> empty() {
return new Environment<T, E>();
}
/**
* Returns the parent environment of this, or null if this
* does not have a parent.
*
* @return this.parent
*/
public Environment<T, E> parent() { return parent; }
/**
* Returns a new environment that extends this environment with the specified
* mapping.
* @requires variable != null
* @return e : Environment | e.parent = this && e.variable = variable && e.value = value
*/
public Environment<T, E> extend(Variable variable, E type, T value) { return extend(variable, type, value, null); }
public Environment<T, E> extend(Variable variable, E type, T value, Object envType) {
return new Environment<T, E>(this, variable, type, value, envType, false);
}
public void negate() {
negated = !negated;
}
/**
* Returns this.variable.
* @return this.variable
*/
public Variable variable() {
return this.variable;
}
/**
* Return this.isInt.
* @return this.isInt
*/
public E type() {
return this.type;
}
/**
* Returns this.value.
* @return this.value
*/
public T value() {
return this.value;
}
/**
* Returns this.quant.
* @return this.quant
*/
public Object envType() {
return envType;
}
public boolean isNegated() {
return negated;
}
/**
* Returns true if this is the empty (root) environment;
* otherwise returns false.
* @return this.parent = this
*/
public boolean isEmpty() {
return this==this.parent;
}
/**
* Looks up the given variable in this environment and its
* ancestors. If the variable is not bound in this
* environment or any of its ancestors, null is returned.
* If the variable is bound in multiple environments,
* the first found binding is returned. Note that null
* will also be returned if the variable is bound to null.
* @return variable = this.variable => this.value, this.parent.lookup(variable)
*/
public T lookup(Variable variable) {
Environment<T, E> p = this;
// ok to use == for testing variable equality:
// see kodkod.ast.LeafExpression#equals
while(!p.isEmpty() && p.variable!=variable) {
p = p.parent;
}
return p.value;
}
public E lookupType(Variable variable) {
Environment<T, E> p = this;
// ok to use == for testing variable equality:
// see kodkod.ast.LeafExpression#equals
while(!p.isEmpty() && p.variable!=variable) {
p = p.parent;
}
return p.type;
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return (parent.isEmpty() ? "[]" : parent.toString()) + "["+variable+"="+value+"]";
}
}