Files
olisp/lib/eval.ml
2025-10-12 21:33:57 +03:00

101 lines
3.0 KiB
OCaml

open Ast;;
open InterpreterStdlib;;
let default_env: environment = [Hashtbl.create 1024];;
let env_add env s v =
match env with
| [] -> ()
| e1 :: _ -> Hashtbl.add e1 s v
let env_new_lexical env =
let h = Hashtbl.create 16 in
h :: env
let add_builtin s f =
env_add default_env s (LBuiltinFunction (s, f))
let () = add_builtin "+" iadd
let () = add_builtin "-" isub
let () = add_builtin "car" car
let () = add_builtin "cdr" cdr
let make_env () = [Hashtbl.copy (List.hd default_env)]
(* the type annotations are unnecessary, but help constrain us from a
potentially more general function here *)
let rec eval_sym (env: environment) (s: string) =
match env with
| [] -> raise (Invalid_argument (Printf.sprintf "eval_sym: symbol %s has no value in current scope" s))
| e :: rest ->
match Hashtbl.find_opt e s with
| None -> eval_sym rest s
| Some v -> v
let rec eval_one env v =
match v with
| LInt x -> LInt x
| LDouble x -> LDouble x
| LString s -> LString s
| LSymbol s -> eval_sym env s
| LNil -> LNil
| LCons (func, args) -> eval_call env func args
| LBuiltinFunction (n, f) -> LBuiltinFunction (n, f)
| LFunction (n, l, f) -> LFunction (n, l, f)
| LQuoted v -> v
(* Evaluate a list of values, without evaluating the resulting
function or macro call. Since macros and functions inherently
look similar, they share a lot of code, which is extracted here *)
and eval_list env l =
match l with
| LNil -> LNil
| LCons (a, b) -> LCons (eval_one env a, eval_list env b)
| _ -> raise (Invalid_argument "eval_list: cannot process non-list")
and eval_body env body =
match body with
| LNil -> LNil
| LCons (form, LNil) -> eval_one env form
| LCons (form, next) ->
ignore (eval_one env form); eval_body env next
| _ -> LNil
and eval_apply env func args =
let lexical_env = env_new_lexical env in
let rec bind_one s a =
match a with
| LNil -> raise (Invalid_argument "not enough arguments supplied to function")
| LCons (value, resta) ->
env_add lexical_env s value; resta
| _ -> raise (Invalid_argument "invalid argument list")
and bind_args l a =
match l with
| LNil -> (match a with
| LNil -> ()
| _ -> raise (Invalid_argument "too many arguments supplied to function"))
| LCons (LSymbol sym, LSymbol restl)->
env_add lexical_env restl (bind_one sym a)
| LCons (LSymbol sym, restl) ->
bind_args restl (bind_one sym a)
| _ -> raise (Invalid_argument "Failure while binding arguments")
in match func with
| LFunction (_, l, b) ->
bind_args l args;
eval_body lexical_env b
| _ -> LNil
and eval_call env func args =
match eval_one env func with
| LBuiltinFunction (_, f) -> f env (eval_list env args)
| LFunction (n, l, b) -> eval_apply env (LFunction (n, l, b)) (eval_list env args)
| v -> raise (Invalid_argument
(Printf.sprintf "eval_apply: cannot call non-function object %s" (dbg_print_one v)));;
let eval_all vs =
let env = make_env () in
let ev v = eval_one env v in
List.map ev vs