#use "ass13.ml";;

(* let a = 1 in b *)
print_endline (expr2string (Let ("a", (Num 1), (Var "b"))));;

(* a + 3 *)
print_endline (expr2string (BinopAp (Add, Var "a", Num 3)));;

(* -a *)
print_endline (expr2string (MonopAp (Neg, Var "a")));;

let show_freevars e =
    let free = match freevars e with
          [] -> ""
        | [x] -> x
        | h::t -> List.fold_left (fun a b -> a ^ ", " ^ b) h t
    in
    print_endline ("\nFree variables in:\n" ^ (expr2string e)
                   ^ "\nare: [" ^ free ^ "]")
;;

(* Write the expressions of assignment series 1 in our data types *)
let l arg body = Fun (arg, body);;
let app func arg = FunAp (func, arg);;

let ass1a =
    let c = Var "c" in
    let bcc = l "b" (l "c" c) in
    let a = Var "a" in
    let aaa = l "a" (app a a) in
    let aaa_aaa = app aaa aaa in
    let v = Var "v" in
    let uvw = l "u" (app (l "v" (l "w" v)) (l "a" a)) in
    (app (app bcc aaa_aaa) uvw);;

let ass1b =
    let u = Var "u" in
    let v = Var "v" in
    let w = Var "w" in
    let uvw = l "u" (l "v" (l "w" (app (app u v) w))) in
    let x = Var "x" in
    let y = Var "y" in
    let z = Var "z" in
    let xyz = l "x" (l "y" (l "z" (app x (app y z)))) in
    let a = Var "a" in
    let b = Var "b" in
    let c = Var "c" in
    (app (app (app (app uvw xyz) a) b) c);;

(* [a] *)
show_freevars (Fun ("b", (BinopAp (Add, Var "a", Num 3))));;

(* [] *)
show_freevars (Fun ("a", (BinopAp (Add, Var "a", Num 3))));;

let l arg body = Fun (arg, body);;
let app f e = FunAp (f, e);;

(* [] *)
show_freevars ass1a;;

(* [a, b, c] *)
show_freevars ass1b;;

let show_eval e =
    print_endline ("\nEvaluation of:\n" ^ (expr2string e)
                   ^ "\nis:\n" ^ (expr2string (eval e)))
;;

(* -5 *)
show_eval (BinopAp (Sub, Num 4, Num 9));;

(* false *)
show_eval (BinopAp (Lt, Num 6, Num 5));;

(* true *)
show_eval (BinopAp (Or, Bool true, Bool false));;

(* fun u -> fun w -> fun a -> a *)
show_eval ass1a;;

(* (a (b c)) *)
show_eval ass1b;;

(* fun y' -> fun z -> ((y y') z) *)
let x = Var "x" in
let y = Var "y" in
let z = Var "z" in
show_eval (app (l "x" (l "y" (l "z" (app (app x y) z)))) y);;

(* c + b *)
show_eval (Let ("a", Var "c", (BinopAp (Add, Var "a", Var "b"))));;
