let rec inspect_ast_expr (expr : Ast.expr) = match expr with
    | Id(id) -> Printf.sprintf "Id(%s)" id
    | This -> "This"
    | Null -> "Null"
    | NewObj(the_type, args) -> Printf.sprintf("NewObj(%s, %s)") the_type (inspect_str_list inspect_ast_expr args)
    | Anonymous(the_type, args, body) -> Printf.sprintf("Anonymous(%s, %s, %s)") the_type (inspect_str_list inspect_ast_expr args) (inspect_str_list inspect_ast_func_def body)
    | Literal(l) -> Printf.sprintf "Literal(%s)" (inspect_ast_lit l)
    | Invoc(receiver, meth, args) -> Printf.sprintf "Invocation(%s, %s, %s)" (inspect_ast_expr receiver) meth (inspect_str_list inspect_ast_expr args)
    | Field(receiver, field) -> Printf.sprintf "Field(%s, %s)" (inspect_ast_expr receiver) field
    | Deref(var, index) -> Printf.sprintf "Deref(%s, %s)" (inspect_ast_expr var) (inspect_ast_expr var)
    | Unop(an_op, exp) -> Printf.sprintf "Unop(%s, %s)" (inspect_ast_op an_op) (inspect_ast_expr exp)
    | Binop(left, an_op, right) -> Printf.sprintf "Binop(%s, %s, %s)" (inspect_ast_op an_op) (inspect_ast_expr left) (inspect_ast_expr right)
    | Refine(fname, args, totype) -> Printf.sprintf "Refine(%s,%s,%s)" fname (inspect_str_list inspect_ast_expr args) (inspect_opt _id totype)
    | Assign(the_var, the_expr) -> Printf.sprintf "Assign(%s, %s)" (inspect_ast_expr the_var) (inspect_ast_expr the_expr)
    | Refinable(the_var) -> Printf.sprintf "Refinable(%s)" the_var
and inspect_ast_var_def (var : Ast.var_def) = match var with
    | (the_type, the_var) -> Printf.sprintf "(%s, %s)" the_type the_var
and inspect_ast_stmt (stmt : Ast.stmt) = match stmt with
    | Decl(the_def, the_expr) -> Printf.sprintf "Decl(%s, %s)" (inspect_ast_var_def the_def) (inspect_opt inspect_ast_expr the_expr)
    | If(clauses) -> Printf.sprintf "If(%s)" (inspect_str_list inspect_ast_clause clauses)
    | While(pred, body) -> Printf.sprintf "While(%s, %s)" (inspect_ast_expr pred) (inspect_str_list inspect_ast_stmt body)
    | Expr(the_expr) -> Printf.sprintf "Expr(%s)" (inspect_ast_expr the_expr)
    | Return(the_expr) -> Printf.sprintf "Return(%s)" (inspect_opt inspect_ast_expr the_expr)
    | Super(args) -> Printf.sprintf "Super(%s)" (inspect_str_list inspect_ast_expr args)
and inspect_ast_clause ((opt_expr, body) : Ast.expr option * Ast.stmt list) =
    Printf.sprintf "(%s, %s)" (inspect_opt inspect_ast_expr opt_expr) (inspect_str_list inspect_ast_stmt body)
and inspect_ast_class_section (sect : Ast.class_section) = match sect with
    | Publics  -> "Publics"
    | Protects -> "Protects"
    | Privates -> "Privates"
    | Refines  -> "Refines"
    | Mains    -> "Mains"
and inspect_ast_func_def (func : Ast.func_def) =
    Printf.sprintf "{ returns = %s, host = %s, name = %s, static = %B, formals = %s, body = %s, section = %s, inklass = %s, uid = %s }"
    (inspect_opt _id func.returns)
    (inspect_opt _id func.host)
    func.name
    func.static
    (inspect_str_list inspect_ast_var_def func.formals)
    (inspect_str_list inspect_ast_stmt func.body)
    (inspect_ast_class_section func.section)
    func.inklass
    func.uid