type t = {
  vars : (string, value) Hashtbl.t;
  parent : t option;
}

and value =
  | Int of int
  | Float of float
  | String of string
  | Function of string list * expr
  | External of string
  | Nop (* return of system operations (will be deprecated) *)

and expr = Ast.t

module Value = struct
  module Type = Ast.Type
  type t = value

  let to_string = function
    | Int n -> string_of_int n
    | Float n -> string_of_float n
    | String s -> "\"" ^ s ^ "\""
    | Function (vars, _) ->
      Printf.sprintf "function with %d arguments" @@ List.length vars
    | External f -> "external " ^ f
    | Nop -> "nop"

  let typeof = function
    | Int _ -> Type.Int
    | Float _ -> Type.Float
    | String _ -> Type.String
    | Function _ -> Type.Function
    | External _ -> Type.External
    | Nop -> failwith "Value.typeof"

  let promote = function
    | Int n -> Float (float n)
    | _ -> failwith "Value.promote"
end

let init_global () = {
  vars = Hashtbl.create 100;
  parent = None;
}

let make parent = {
  vars = Hashtbl.create 100;
  parent = Some parent;
}

exception Not_found

let rec get e name =
  match Hashtbl.find_opt e.vars name with
  | None -> begin match e.parent with
      | None -> raise Not_found
      | Some p -> get p name
    end
  | Some value -> value

let get_opt e name =
  try Some (get e name)
  with Not_found -> None

let set e name value =
  Hashtbl.replace e.vars name value

let add_seq e seq =
  Hashtbl.add_seq e.vars seq