package code.type.typechecker;

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

import java.util.Hashtable;

/**
 * This class renames type variables occurring in a type expression.
 * A single type variable in the given expression is renamed only once.
 * All subsequent occurances of the type var name use the previously assigned new name.
 *
 * @author Pravin Damle
 * @since 12/2/04
 */
public class RenameTypeVarVisitor
    implements TypeVisitor<TypeExpression,Object> {

    private Hashtable<String,TypeVariable> table
	= new Hashtable<String,TypeVariable>();
    private final Environment env;

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

    /**
     * If we have not generated a new name for var, then create a fresh type
     * variable name for it. Otherwise, we return the variable type with
     * the name assigned previously. We make use of a hash table to keep
     * track of whether variables have been assigned.
     *
     * @param var - the type variable
     * @param o   - not used
     * @return a new variable type with a fresh name or one previously created through this function.
     */
    public TypeExpression visit(TypeVariable var, Object o) {
        if (table.containsKey(var.name)) {
            //we already have done this. so just use it.
            TypeVariable tv = table.get(var.name);
            return tv;
        } else {
            // we have not renamed it yet. generate a new name
            String name = env.generateName();
            TypeVariable newvar = (TypeVariable) makeTypeVariable(name);
            table.put(var.name, newvar);
            return newvar;
        }
    }

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

    public TypeExpression visit(FunctionType fun, Object o) {

        TypeExpression domain = fun.domain.accept(this, o);
        TypeExpression range = fun.range.accept(this, o);

        return makeFunctionType(domain, range);
    }

    public TypeExpression visit(TypeConstructorApplication app, Object o) {

        TypeExpression[] args = new TypeExpression[app.arguments.length];

        for (int i = 0; i < app.arguments.length; ++i)
            args[i] = app.arguments[i].accept(this, o);

        return makeApplicationType(app.typeConstructor, args);
    }

}

