package nl.utwente.viskell.haskell.expr;
import nl.utwente.viskell.haskell.type.Type;
import nl.utwente.viskell.haskell.type.TypeScope;
/**
* A Binder is the definition side of a local variable, it is used link variable to their binding constructs, such as lambdas
*/
public class Binder {
/** The name of this binder */
private final String name;
/** An internal type used in the type inference process */
private Type inferenceType;
/** An optional type annotation to restrict the type of this binder */
private Type annotation;
/**
* @param name of this Binder
*/
public Binder(String name) {
this.name = name;
this.annotation = null;
}
/**
* @param name of this binder
* @param annotation a type to restrict the potential types of this binder
*/
public Binder(String name, Type annotation){
this.name = name;
this.annotation = annotation;
}
/**
* @return The name (made unique) of this binder, for avoiding name conflicts in code generation
*/
public String getUniqueName() {
return name + "__" + Integer.toHexString(this.hashCode());
}
/**
* @return The base name of this binder
*/
public String getBaseName() {
return this.name;
}
/**
* Refreshes the internal type of the binder for type inference
* @param scope wherein the fresh type is constructed
*/
public Type refreshBinderType(final TypeScope scope) {
if (this.annotation != null) {
this.inferenceType = this.annotation.getFresh(scope);
} else {
this.inferenceType = scope.getVar(this.name);
}
return this.inferenceType;
}
/**
* @return the type for the use site of this binder
* @throws RuntimeException if this function is called before refreshBinderType
*/
public final Type getBoundType() {
if (this.inferenceType == null) {
// technically it is an error in scoping but this will do for now
throw new RuntimeException("Using the type before it is bound, of binder: " + this.name);
}
return this.inferenceType;
}
/**
* Sets the type annotation of the binder and makes its internal type fresh.
* @param type to annotate this binder with.
* @param scope wherein the fresh type is constructed.
*/
public void setFreshAnnotation(Type type, TypeScope scope) {
this.annotation = type;
this.inferenceType = type.getFresh(scope);
}
/**
* Replaces the type annotation and internal type with another type.
* @param type to annotate this binder with.
*/
public void setAnnotationAsType(Type type) {
this.annotation = type;
this.inferenceType = type;
}
/** @return a fresh copy of the annotated type constraint, or null if absent. */
public Type getFreshAnnotationType() {
if (this.annotation != null) {
return this.annotation.getFresh();
}
return null;
}
@Override
public final String toString() {
if (this.inferenceType == null) {
return this.name;
}
return this.name + "::" + this.inferenceType.toString();
}
}