(*
  *)

type Id = string;

(** Representing types for mini-Java **)

datatype Basic = Bool | Int | Real;

datatype Type
  = BasicType of Basic
  | ArrayType of Basic
  | ObjType of Id
  | VoidType;


(* A slot of type (`x TC) is an option *)
(* type. The parser places NONE there  *)
(* and the type-checker fills it in    *)
(* with (SOME x) when "x" is known     *)

type 'x TC = 'x option;

(******** Representing Programs *******)

datatype Op                 (* infix operators *)
  = ADD | SUB | MUL | DIV        (* Arithmetic *)
  | AND | OR                     (* logical    *)
  | EQ | NE | LT | LE | GT | GE  (* relational *)

datatype Constant         (* Literal constants *)
  = Cint of int
  | Creal of string
  | Cbool of bool

datatype Exp
  = Literal of Constant                 (* 5, 6.3, true *)
  | Binop of Op * Exp * Exp             (* x + 3        *)
  | Relop of Op * Exp * Exp             (* x < 7.7      *)
  | Not of Exp                          (* ! x          *)
  | ArrayElm of Exp * Exp * (Basic TC)  (* x[3]         *)
  | ArrayLen of Exp                     (* x.length()   *)
  | Call of Exp * Id *(Id TC)* Exp list (* x.f(1,z)     *)
  | NewArray of Basic * Exp             (* new int[3]   *)
  | NewObject of Id                     (* new point()  *)
  (* Coerce is used only in type checking               *)
  | Coerce of Exp


datatype Stmt
  = Block of Stmt list       (* {x:5; print(2)}           *)
  | Assign of Exp option * Id * Exp option * Exp
                             (* p.x[2]=5  p.x=5  x=5      *)
  | CallStmt of Exp * Id * (Id TC)* Exp list
                             (* x.f(1,z)                  *)
  | If of Exp * Stmt * Stmt  (* if (p<2) x=5 else x=6     *)
  | While of Exp * Stmt      (* while (p) s               *)
  | PrintE of Exp            (* System.out.println(x)     *)
  | PrintT of string         (* System.out.println("zbc") *)
  | Return of Exp option;    (* return (x+3)              *)

datatype VarDecl = VarDecl of Type * Id * Exp option;

datatype Formal = Formal of Type * Id;

datatype MetDecl = MetDecl of Type * Id *
                   Formal list * VarDecl list *
                   Stmt list;

datatype VarDecl
  = VarDecl of Type * Id * Exp

datatype ClassDec
  = ClassDec of Id * Id * VarDecl list * MetDecl list;


datatype Program = Program of ClassDec list;


datatype CTab
   = Node of
        Id *                            (* Class Name      *)
        (Id * Type)list *               (* Class Variables *)
        (Id * Type list * Type)list *   (* Class Methods   *)
        CTab ref *                      (* Parent Class    *)
        CTab list                       (* Sub Classes     *)
   | NullClass;

val root = Node("object",[],[],ref NullClass,[]);


exception NoParent of string;
fun newClass name vars methods parent NullClass = NullClass
  | newClass name vars methods parent (n as Node(nm,vs,ms,p,subs)) =
     if parent=nm
        then let val p1 = ref n
                 val new = Node(name,vars,methods,p1,[])
                 val newP = Node(nm,vs,ms,p,new::subs)
                 val _ = p1 := newP
             in newP end
        else Node(nm,vs,ms,p,map (newClass name vars methods parent) subs)

val t1 = newClass "point" [] [] "object" root;

val t2 = newClass "colorpoint" [] [] "point" t1;

fun basiceq (x,y) =
case (x,y) of
  (Real,Real) => true
| (Int,Int) => true
| (Bool,Bool) => true
| (_,_) => false

fun typeeq (x,y) =
case (x,y) of
  (BasicType x,BasicType y) => basiceq(x,y)
| (ArrayType x,ArrayType y) => basiceq(x,y)
| (ObjType x,ObjType y) => x=y
| (VoidType,VoidType) => true
| (_,_) => false

fun defines name NullClass = false
  | defines name (Node(n,vs,ms,p,ss)) =
      if name=n then true else List.exists (defines name) ss;

fun useTree NullClass (x,y) = false
  | useTree (Node(nm,vs,ms,p,ss)) (x,y) =
     if nm = y
        then List.exists (defines x) ss
        else List.exists (fn t => useTree t (x,y)) ss

fun subtype classH (x,y) =
  case (x,y) of
    (x,ObjType "object") => true
  | (BasicType Int,ObjType "numeric") => true
  | (BasicType Real,ObjType "numeric") => true
  | (ObjType x,ObjType y) => useTree classH (x,y)
  | (_,_) => typeeq(x,y)




(* 
*)