/*
* 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.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 parent: Environment
* @invariant this = parent => no variable
* @author Emina Torlak
*/
@SuppressWarnings("unchecked")
final class Environment<T> {
private final Variable variable;
private final T value;
private final Environment<T> parent;
/**
* The empty environment; EMPTY is its own parent.
*/
@SuppressWarnings("rawtypes")
static final Environment EMPTY = new Environment();
/**
* Constructs the empty environment.
*/
private Environment() {
this.variable = null;
this.value = null;
this.parent = this;
}
/**
* Constructs a new environment with the specified parent
* and mapping.
*
* @ensures this.parent' = parent && this.variable' = variable && this.value' = value
*/
private Environment(Environment<T> parent, Variable variable, T value) {
this.variable = variable;
this.value = value;
this.parent = parent;
}
/**
* Returns the empty environment.
* @return the empty environment.
*/
public static <T> Environment<T> empty() {
return (Environment<T>) EMPTY;
}
/**
* Returns the parent environment of this, or null if this
* does not have a parent.
*
* @return this.parent
*/
public Environment<T> 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> extend(Variable variable, T value) {
assert variable != null;
return new Environment<T>(this, variable, value);
}
/**
* Returns this.variable.
* @return this.variable
*/
public Variable variable() {
return this.variable;
}
/**
* Returns this.value.
* @return this.value
*/
public T value() {
return this.value;
}
/**
* Returns true if this is the empty environment;
* otherwise returns false.
* @return this.parent = EMPTY
*/
public boolean isEmpty() {
return this==EMPTY;
}
/**
* 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> p = this;
// ok to use == for testing variable equality:
// see kodkod.ast.LeafExpression#equals
while(p!=EMPTY && p.variable!=variable) {
p = p.parent;
}
return p.value;
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return (parent == EMPTY ? "[]" : parent.toString()) + "["+variable+"="+value+"]";
}
}