import java.util.*;
class TypeChecker implements Visitor{
    Map<String,KlipType> vars= new HashMap<String,KlipType>();


    public void  visit(Num n){
	n.myType=KlipType.zahl;
    }
    public  void  visit(Var v){
        KlipType typ = vars.get(v.x);
	if (typ==null) 
	    throw new RuntimeException("variable not declared: "+v.x);
	v.myType=typ;
    }
    public  void  visit(Assign as){
	KlipType typ = vars.get(as.l.x);
	as.r.welcome(this);
	if (typ==null){
	    vars.put(as.l.x,as.r.myType);
	    as.l.welcome(this);
	}else{
	    if (typ!=as.r.myType)
         throw new RuntimeException("incompatible type in assignment: "+typ+" "+as.r.myType);
	}
	as.myType=KlipType.unit;
    }

    public  void checkArith(Klip l,Klip r){
      l.welcome(this);
      if (l.myType != KlipType.zahl) 
         throw new RuntimeException("wrong type for mult: "+l.myType);
      r.welcome(this);
      if (r.myType != KlipType.zahl) 
         throw new RuntimeException("wrong type for mult: "+r.myType);
    }

  public  void  visit(Mul e){
      checkArith(e.l,e.r);
      e.myType=KlipType.zahl;
    }
  public  void  visit(Not e){
      e.l.welcome(this);
      if (e.l.myType != KlipType.wahrheit) 
         throw new RuntimeException("wrong type for not: "+e.l.myType);
      e.myType=KlipType.wahrheit;
    }
  public  void  visit(Eq e){
      checkArith(e.l,e.r);
      e.myType=KlipType.wahrheit;
    }
  public  void  visit(Div e){
      checkArith(e.l,e.r);
      e.myType=KlipType.zahl;
    }
  public  void  visit(Add e){
      checkArith(e.l,e.r);
      e.myType=KlipType.zahl;
    }
  public  void  visit(Sub e){
      checkArith(e.l,e.r);
      e.myType=KlipType.zahl;
    }
  public  void  visit(While w){
      w.cond.welcome(this);
      if (w.cond.myType != KlipType.wahrheit) 
         throw new RuntimeException("wrong type for while condition: "+w.cond.myType);
      w.body.welcome(this);
      w.myType=KlipType.unit;
    }
    public  void  visit(Seq xs){
	for (Klip x:xs.klips)x.welcome(this);
	xs.myType=xs.klips.get(xs.klips.size()-1).myType;
    }

}
