package de.skuzzle.polly.core.parser.ast.declarations.types.unification;
import java.util.HashMap;
import java.util.Map;
import de.skuzzle.polly.core.parser.ast.declarations.types.Substitution;
import de.skuzzle.polly.core.parser.ast.declarations.types.Type;
import de.skuzzle.polly.core.parser.ast.declarations.types.TypeVar;
/**
* Helper class to determine whether two types are structural equal and to resolve
* type variable substitution.
*
* @author Simon Taddiken
*/
final class HashMapTypeUnifier extends AbstractUnifier {
private int classes;
private final Map<Type, Integer> typeToClass;
private final Map<Integer, Type> classToType;
/**
* Creates a new TypeUnifier which can then be used to test for equality of type
* expressions.
* @param subTypeIncl Whether sub type inclusion should be checked when comparing
* primitives.
*/
public HashMapTypeUnifier(boolean subTypeIncl) {
super(subTypeIncl);
this.typeToClass = new HashMap<Type, Integer>();
this.classToType = new HashMap<Integer, Type>();
}
/**
* Re-initializes this unifier. Needs to be done before unifying another set
* of type expressions.
*/
private final void init() {
this.typeToClass.clear();
this.classToType.clear();
this.classes = 0;
}
@Override
public Substitution unify(Type first, Type second) {
this.init();
return super.unify(first, second);
}
@Override
public boolean tryUnify(Type first, Type second) {
this.init();
return super.tryUnify(first, second);
}
@Override
public Type find(Type s) {
final int cls = this.getEquivClass(s);
Type representant = this.classToType.get(cls);
if (representant == null) {
representant = s;
this.classToType.put(cls, representant);
}
return representant;
}
/**
* Gets the equivalence class of the given type. If
* <code>t</code> was not yet assigned to a equivalence class, it is made the
* representative of a new class.
*
* @param t Type which equivalence class should be resolved.
* @return The type's equivalence class.
*/
private int getEquivClass(Type t) {
Integer i = this.typeToClass.get(t);
if (i == null) {
i = this.classes++;
this.typeToClass.put(t, i);
}
return i;
}
@Override
protected void union(Type m, Type n, Map<TypeVar, Type> subst) {
final Type rep_m = this.find(m);
final Type rep_n = this.find(n);
final int equiv = this.getEquivClass(m);
final Type representative = rep_m instanceof TypeVar ? rep_n : rep_m;
final Type other = representative == rep_m ? rep_n : rep_m;
this.makeEquivalent(equiv, representative, other);
if (other instanceof TypeVar) {
subst.put((TypeVar) other, representative);
}
}
/**
* Makes both given types equivalent, that is both gets assigned the given equivalence
* class and the first type is made the new representative of the given class.
*
* @param equivClass Equivalence class to put both types in.
* @param representative First type. Is also made the new representative of the
* equivalence class.
* @param other Second type to put in the equivalence class.
*/
private final void makeEquivalent(int equivClass, Type representative, Type other) {
// make type the new representative of the given class
this.classToType.put(equivClass, representative);
// assign new equivalence class to both types
this.typeToClass.put(representative, equivClass);
this.typeToClass.put(other, equivClass);
}
}