package code.type.typechecker;

import code.type.*;
import code.type.visitor.TypeVisitor;
import static code.type.TypeFactory.*;

/**
 * A typechecker that applies the TypeVariable substitution to its
 * first argument in the context of an Environment.
 *
 * The first argument to each visit method is the TypeExpression to
 * which the substitution must be applied.  The environment is passed
 * to every visit method as the second argument.  The type expression
 * after the application of the substitution is returned by the visit
 * method.
 *
 * @author Pravin Damle
 * @since 12/5/04
 */
public class ApplySubstTypeVisitor 
    implements TypeVisitor<TypeExpression,Object> {

    private final Environment env ;

    public ApplySubstTypeVisitor (Environment env) { this.env = env ; }

    public TypeExpression visit(TypeVariable var, Object o) {
        TypeExpression y = env.getSubst(var.name);
        if (y == null) {
            return var;
        }
        else {
            // Question: should we invoke the apply substitution typechecker again?
            // Answer: Yes. since we are trying to return a "complete" substitution
            //return y ;
            return y.accept(this,env);
        }
    }

    public TypeExpression visit(TypeConstructor tc, Object o) {
        return tc ;
    }

    public TypeExpression visit(FunctionType function, Object o) {
        return makeFunctionType(function.domain.accept(this,o),
				function.range.accept(this,o));

    }

    public TypeExpression visit(TypeConstructorApplication tc, Object o) {
        TypeExpression[] newargs = new TypeExpression[tc.arguments.length];
        for (int i = 0; i < (tc.arguments).length; i++)
            newargs[i] = tc.arguments[i].accept(this,o);
        return makeApplicationType(tc.typeConstructor, newargs);
    }

}

