2024-08-26 01:58:03 +02:00
|
|
|
open Value
|
|
|
|
|
|
|
|
|
|
module Hashtbl = struct
|
|
|
|
|
include Stdlib.Hashtbl
|
|
|
|
|
|
|
|
|
|
let contains tbl key = find_opt tbl key |> Option.is_some
|
|
|
|
|
end
|
|
|
|
|
|
2024-08-28 23:58:55 +02:00
|
|
|
type environment = Value.environment
|
2024-08-28 17:21:36 +02:00
|
|
|
|
2024-08-26 01:58:03 +02:00
|
|
|
module Env = struct
|
|
|
|
|
type t = environment
|
|
|
|
|
|
2024-08-28 17:21:36 +02:00
|
|
|
let make () : t = { globals = ref (Hashtbl.create 0); locals = [] }
|
2024-08-26 01:58:03 +02:00
|
|
|
|
|
|
|
|
let enter (env : t) : t =
|
|
|
|
|
let tbl = Hashtbl.create 0 in
|
|
|
|
|
{ env with locals = tbl :: env.locals }
|
|
|
|
|
|
2024-08-28 17:21:36 +02:00
|
|
|
let push_frame (env : t) : t = { env with locals = [] } |> enter
|
|
|
|
|
|
|
|
|
|
(* let exit (env : t) : t =
|
|
|
|
|
assert (not (List.is_empty env.locals));
|
|
|
|
|
{ env with locals = List.tl env.locals } *)
|
2024-08-26 01:58:03 +02:00
|
|
|
|
|
|
|
|
let define_global (env : t) (name : string) (value : lox_value) =
|
2024-08-28 17:21:36 +02:00
|
|
|
Hashtbl.replace !(env.globals) name value
|
2024-08-26 01:58:03 +02:00
|
|
|
|
2024-08-28 17:21:36 +02:00
|
|
|
let get_global (env : t) (name : string) : lox_value option = Hashtbl.find_opt !(env.globals) name
|
2024-08-26 01:58:03 +02:00
|
|
|
|
|
|
|
|
(* Check if `name` is defined in either locals or globals *)
|
|
|
|
|
let is_defined (env : t) (name : string) : bool =
|
|
|
|
|
let _is_defined tbl = Hashtbl.contains tbl name in
|
2024-08-28 17:21:36 +02:00
|
|
|
List.fold_left (fun acc tbl -> acc || _is_defined tbl) false (!(env.globals) :: env.locals)
|
2024-08-26 01:58:03 +02:00
|
|
|
|
|
|
|
|
let define (env : t) (name : string) (value : lox_value) : bool =
|
|
|
|
|
if List.is_empty env.locals then (
|
|
|
|
|
define_global env name value;
|
|
|
|
|
true)
|
|
|
|
|
else
|
|
|
|
|
let tbl = List.hd env.locals in
|
|
|
|
|
if Hashtbl.contains tbl name then false
|
|
|
|
|
else (
|
|
|
|
|
Hashtbl.add tbl name value;
|
|
|
|
|
true)
|
|
|
|
|
|
|
|
|
|
let update (env : t) (name : string) (value : lox_value) =
|
|
|
|
|
let rec _update tbls =
|
|
|
|
|
match tbls with
|
|
|
|
|
| [] -> false
|
|
|
|
|
| tbl :: tail ->
|
|
|
|
|
if Hashtbl.contains tbl name then (
|
|
|
|
|
Hashtbl.replace tbl name value;
|
|
|
|
|
true)
|
|
|
|
|
else _update tail
|
|
|
|
|
in
|
2024-08-28 17:21:36 +02:00
|
|
|
if _update env.locals then () else Hashtbl.replace !(env.globals) name value
|
2024-08-26 01:58:03 +02:00
|
|
|
|
|
|
|
|
let get (env : t) (name : string) : lox_value option =
|
|
|
|
|
let rec _get tbls =
|
|
|
|
|
match tbls with
|
|
|
|
|
| [] -> None
|
|
|
|
|
| tbl :: tail -> (
|
|
|
|
|
match Hashtbl.find_opt tbl name with Some x -> Some x | None -> _get tail)
|
|
|
|
|
in
|
|
|
|
|
let val_opt = _get env.locals in
|
2024-08-28 17:21:36 +02:00
|
|
|
match val_opt with Some x -> Some x | None -> Hashtbl.find_opt !(env.globals) name
|
2024-08-26 01:58:03 +02:00
|
|
|
end
|