// Path: src/stone/ast/ASTree.java
// public abstract class ASTree implements Iterable<ASTree> {
//     public abstract ASTree child(int i);
//     public abstract int numChildren();
//     public abstract Iterator<ASTree> children();
//     public abstract String location();
//     public Iterator<ASTree> iterator() { return children(); }
// }
// 
// Path: src/stone/ast/TypeTag.java
// public class TypeTag extends ASTList {
//     public static final String UNDEF = "<Undef>";
//     public TypeTag(List<ASTree> c) { super(c); }
//     public String type() {
//         if (numChildren() > 0)
//             return ((ASTLeaf)child(0)).token().getText();
//         else
//             return UNDEF;
//     }
//     public String toString() { return ":" + type(); }
// }


import stone.ast.ASTree;
import stone.ast.TypeTag;

    public TypeInfo type() { return this; }
    public boolean match(TypeInfo obj) {
        return type() == obj.type();
    }
    public boolean subtypeOf(TypeInfo superType) {
        superType = superType.type();
        return type() == superType || superType == ANY;
    }
    public void assertSubtypeOf(TypeInfo type, TypeEnv env, ASTree where)
        throws TypeException
    {
        if (!subtypeOf(type))
            throw new TypeException("type mismatch: cannot convert from "
                                    + this + " to " + type, where);
    }
    public TypeInfo union(TypeInfo right, TypeEnv tenv) {
        if (match(right))
            return type();
        else
            return ANY;
    }
    public TypeInfo plus(TypeInfo right, TypeEnv tenv) {
        if (INT.match(this) && INT.match(right))
            return INT;
        else if (STRING.match(this) || STRING.match(right))
            return STRING;
        else
            return ANY;
    }
// Path: src/stone/ast/ASTree.java
// public abstract class ASTree implements Iterable<ASTree> {
//     public abstract ASTree child(int i);
//     public abstract int numChildren();
//     public abstract Iterator<ASTree> children();
//     public abstract String location();
//     public Iterator<ASTree> iterator() { return children(); }
// }
// 
// Path: src/stone/ast/TypeTag.java
// public class TypeTag extends ASTList {
//     public static final String UNDEF = "<Undef>";
//     public TypeTag(List<ASTree> c) { super(c); }
//     public String type() {
//         if (numChildren() > 0)
//             return ((ASTLeaf)child(0)).token().getText();
//         else
//             return UNDEF;
//     }
//     public String toString() { return ":" + type(); }
// }


// Path: src/chap14/TypeInfo.java
import stone.ast.ASTree;
import stone.ast.TypeTag;


    public TypeInfo type() { return this; }
    public boolean match(TypeInfo obj) {
        return type() == obj.type();
    }
    public boolean subtypeOf(TypeInfo superType) {
        superType = superType.type();
        return type() == superType || superType == ANY;
    }
    public void assertSubtypeOf(TypeInfo type, TypeEnv env, ASTree where)
        throws TypeException
    {
        if (!subtypeOf(type))
            throw new TypeException("type mismatch: cannot convert from "
                                    + this + " to " + type, where);
    }
    public TypeInfo union(TypeInfo right, TypeEnv tenv) {
        if (match(right))
            return type();
        else
            return ANY;
    }
    public TypeInfo plus(TypeInfo right, TypeEnv tenv) {
        if (INT.match(this) && INT.match(right))
            return INT;
        else if (STRING.match(this) || STRING.match(right))
            return STRING;
        else
            return ANY;
    }
    public static TypeInfo get(TypeTag tag) throws TypeException {