package de.skuzzle.polly.core.parser.ast.declarations.types.unification; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import de.skuzzle.polly.core.parser.ast.declarations.types.ListType; import de.skuzzle.polly.core.parser.ast.declarations.types.MapType; import de.skuzzle.polly.core.parser.ast.declarations.types.MissingType; import de.skuzzle.polly.core.parser.ast.declarations.types.ProductType; 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; public abstract class AbstractUnifier implements Unifier { protected final boolean subTypeIncl; public AbstractUnifier(boolean subTypeIncl) { this.subTypeIncl = subTypeIncl; } /** * Tests for structural equality of the given type expression in the context of this * unifier instance. * * @param first First type to check. * @param second Second type to check. * @return A substitution for the type variables in first and second or * <code>null</code> if unification was not successful. */ @Override public Substitution unify(Type first, Type second) { final Map<TypeVar, Type> subst = new HashMap<TypeVar, Type>(); if (!unifyInternal(first, second, subst)) { return null; } return new Substitution(subst); } /** * Tests for structural equality of the two given type expressions. * * @param first First type. * @param second Second type. * @return Whether the first type is an instance of the second type */ @Override public boolean tryUnify(Type first, Type second) { return this.unifyInternal(first, second, new HashMap<TypeVar, Type>()); } /** * <p>Finds the representative of the equivalence class that <code>s</code> is in. If * <code>s</code> was not yet assigned to a equivalence class, it is made the * representative of a new class.</p> * * @param s Type which' equivalence class's type will be resolved. * @return The representative type of the equivalence class that <code>s</code> is in. */ protected abstract Type find(Type s); /** * Unions the two types into a single equivalence class. If the representative * <code>x</code> of the equivalence class of <code>m</code> is no {@link TypeVar}, * <code>x</code> is made the representative of the equivalence class that represents * the union of <code>m</code> and <code>n</code>. Otherwise, the representative * <code>y</code> of the equivalence class of <code>n</code> is made the * representative of the united equivalence class. * * @param m Type to union. * @param n Type to union. */ protected abstract void union(Type s, Type t, Map<TypeVar, Type> subst); protected boolean unifyInternal(Type m, Type n, Map<TypeVar, Type> subst) { final Type s = this.find(m); final Type t = this.find(n); if (this.subTypeIncl && s.isA(t) || s == t) { return true; } else if (s instanceof MissingType || t instanceof MissingType) { this.union(s, t, subst); return true; } else if (s instanceof MapType && t instanceof MapType) { this.union(s, t, subst); final MapType mc1 = (MapType) s; final MapType mc2 = (MapType) t; return this.unifyInternal(mc1.getSource(), mc2.getSource(), subst) && this.unifyInternal(mc1.getTarget(), mc2.getTarget(), subst); } else if (s instanceof ListType && t instanceof ListType) { this.union(s, t, subst); final ListType l1 = (ListType) s; final ListType l2 = (ListType) t; return this.unifyInternal(l1.getSubType(), l2.getSubType(), subst); } else if (s instanceof ProductType && t instanceof ProductType) { this.union(s, t, subst); final ProductType p1 = (ProductType) s; final ProductType p2 = (ProductType) t; if (p1.getTypes().size() != p2.getTypes().size()) { return false; } final Iterator<Type> p2It = p2.getTypes().iterator(); for (final Type p1Type : p1.getTypes()) { if (!this.unifyInternal(p1Type, p2It.next(), subst)) { return false; } } return true; } else if (s instanceof TypeVar || t instanceof TypeVar) { this.union(s, t, subst); return true; } else { return false; } } }