(* Abstract Syntax Tree and functions for printing it *)

(* This code was original from MicroC (by way of ast.ml) and modified by:
Keir Lauritzen, Leon Song, and Guy Yardeni*)

type op = Add | Sub | Mult | Div | Equal | Neq | Less | Leq | Greater | Geq |
          And | Or

type uop = Neg | Not

type typ = Int | String_t | Void | Bool | Char | Charp | Charpp | Intp | Float | Array_f | Array_s | Action_t | S_arr_t of typ * int

type bind = typ * string

type expr =
    | Int_Lit of int
    | Bool_Lit of bool
    | String_Lit of string
    | Float_Lit of float
    | Array_F_Lit of (string * float) list
    | Array_S_Lit of (string * string) list
    | Retrieve_Float of string * expr
    | Retrieve_String of string * expr
    | Array_Assign_Float of string * expr * expr
    | Array_Assign_String of string * expr * expr
    | Char_Lit of char
    | Id of string
    | Binop of expr * op * expr
    | Unop of uop * expr
    | Assign of string * expr
    | Assign_array of string * int * expr
    | Call of string * expr list
    | Noexpr
    | Assign_func of string * string
    | Assign_func_array of string * int * string

type stmt =
    | Block of stmt list
    | Expr of expr
    | Return of expr
    | If of expr * stmt * stmt
    | For of expr * expr * expr * stmt
    | While of expr * stmt


type func_decl = {
    typ : typ;
    fname : string;
    formals : bind list;
    locals : bind list;
    body : stmt list;
}

type program = {
    var_decls : bind list;
    func_decls : func_decl list;
}

let rec string_of_typ = function
    | Int -> "int"
    | Bool -> "bool"
    | Void -> "void"
    | Char -> "char"
    | String_t -> "string"
    | Charp -> "char*"
    | Charpp -> "char**"
    | Intp -> "int*"
    | Float -> "float"
    | Array_f -> "array float"
    | Array_s -> "array string"
    | Action_t -> "function pointer"

let string_of_op = function
    | Add -> "+"
    | Sub -> "-"
    | Mult -> "*"
    | Div -> "/"
    | Equal -> "=="
    | Neq -> "!="
    | Less -> "<"
    | Leq -> "<="
    | Greater -> ">"
    | Geq -> ">="
    | And -> "&&"
    | Or -> "||"

let string_of_uop = function
    | Neg -> "-"
    | Not -> "!"

let rec string_of_expr = function
    | Int_Lit(l) -> string_of_int l
    | Bool_Lit(true) -> "true"
    | Bool_Lit(false) -> "false"
    | String_Lit(str) -> "" ^ str
    | Float_Lit(l) -> string_of_float l
    | Char_Lit(c) -> Char.escaped c
    | Id(s) -> s
    | Binop(e1, o, e2) ->
        string_of_expr e1 ^ " " ^ string_of_op o ^ " " ^ string_of_expr e2
    | Unop(o, e) -> string_of_uop o ^ string_of_expr e
    | Assign(v, e) -> v ^ " = " ^ string_of_expr e
    | Call(f, el) ->
        f ^ "(" ^ String.concat ", " (List.map string_of_expr el) ^ ")"
    | Noexpr -> ""
    | _ -> "No_match"
