package typing; /** * A list of type equations. * * @author Benedikt Meurer * @version $Id$ * * @see typing.Equation */ final class EquationList { /** * Allocates a new equation list, which basicly extends * <code>remaining</code> with a new {@link Equation} * <code>first</code>. * * @param first the new {@link Equation}. * @param remaining an existing {@link EquationList} */ EquationList(Equation first, EquationList remaining) { this.first = first; this.remaining = remaining; } /** * Allocates a new {@link EquationList}, which extends this * equation list with a new {@link Equation} for <code>left</code> * and <code>right</code>. * * @param left the left side of the new equation. * @param right the right side of the new equation. * * @return the extended {@link EquationList}. */ EquationList extend(MonoType left, MonoType right) { // allocate an extended equation list return new EquationList(new Equation(left, right), this); } /** * Applies the {@link Substitution} <code>s</code> to all * equations contained within this list. * * @param s the {@link Substitution} to apply. * * @return the resulting list of equations. * * @see Equation#substitute(Substitution) */ EquationList substitute(Substitution s) { // nothing to substitute on the empty list if (this == EMPTY_LIST) return this; // apply the substitution to the first equation Equation first = this.first.substitute(s); // apply the substitution to the remaining equations EquationList remaining = this.remaining.substitute(s); // check if anything changed, otherwise we can reuse // the existing equation list if (first != this.first || remaining != this.remaining) return new EquationList(first, remaining); else return this; } /** * This method implements the unification algorithm. * * It returns the unificator for this type equation * list. * * @return the unificator for this type equation. * * @throws UnificationException if one of the equations contained * within this list could not be unified. */ Substitution unify() throws UnificationException { // an empty type equation list is easy to unify if (this == EMPTY_LIST) return Substitution.EMPTY_SUBSTITUTION; // otherwise, we examine the first equation in the list MonoType left = this.first.getLeft(); MonoType right = this.first.getRight(); // different actions, depending on the exact types if (left instanceof TypeVariable || right instanceof TypeVariable) { // the left or right side of the equation is a type variable TypeVariable tvar = (TypeVariable)(left instanceof TypeVariable ? left : right); MonoType tau = (left instanceof TypeVariable ? right : left); // either tvar equals tau or tvar is not present in tau if (tvar.equals(tau) || !tau.containsFreeTypeVariable(tvar.getName())) { Substitution s1 = new Substitution(tvar.getName(), tau); Substitution s2 = this.remaining.substitute(s1).unify(); return s1.compose(s2); } // FALL-THROUGH: Otherwise it's a type error } else if (left instanceof ArrowType && right instanceof ArrowType) { // cast to ArrowType instances (tau and tau') ArrowType taul = (ArrowType)left; ArrowType taur = (ArrowType)right; // we need to check {tau1 = tau1', tau2 = tau2'} as well EquationList eqns = this.remaining; eqns = new EquationList(new Equation(taul.getT2(), taur.getT2()), eqns); eqns = new EquationList(new Equation(taul.getT1(), taur.getT1()), eqns); // try to unify the new list return eqns.unify(); } else if (left instanceof TupleType && right instanceof TupleType) { // cast to TupleType instances (tau and tau') TupleType taul = (TupleType)left; TupleType taur = (TupleType)right; // check if the arities match if (taul.arity() == taur.arity()) { // determine the sub types MonoType[] typesl = taul.getTypes(); MonoType[] typesr = taur.getTypes(); // check all sub types EquationList eqns = this.remaining; for (int n = 0; n < typesl.length; ++n) eqns = new EquationList(new Equation(typesl[n], typesr[n]), eqns); // try to unify the new list return eqns.unify(); } // FALL-THROUGH: Otherwise it's a type error } else if (left.equals(right)) { // the types equal, just unify the remaining equations then return this.remaining.unify(); } // (left = right) cannot be unified throw new UnificationException(this.first); } /** * Returns the string representation of the equations * contained in this list. This method is mainly useful * for debugging purposes. * * @return the string representation. * * @see Equation#toString() * @see java.lang.Object#toString() */ @Override public String toString() { StringBuilder builder = new StringBuilder(128); builder.append('{'); for (EquationList list = this; list != EMPTY_LIST; list = list.remaining) { if (list != this) builder.append(", "); builder.append(list.first); } builder.append('}'); return builder.toString(); } /** * The empty equation list. */ static final EquationList EMPTY_LIST = new EquationList(); private EquationList() { } // member attributes private Equation first; private EquationList remaining; }