fsharp: step 4: Added list and comparison functions.
[jackhill/mal.git] / ocaml / step4_if_fn_do.ml
1 module T = Types.Types
2
3 let repl_env = Env.make (Some Core.ns)
4
5 let rec eval_ast ast env =
6 match ast with
7 | T.Symbol s -> Env.get env ast
8 | T.List { T.value = xs; T.meta = meta }
9 -> T.List { T.value = (List.map (fun x -> eval x env) xs);
10 T.meta = meta }
11 | T.Vector { T.value = xs; T.meta = meta }
12 -> T.Vector { T.value = (List.map (fun x -> eval x env) xs);
13 T.meta = meta }
14 | T.Map { T.value = xs; T.meta = meta }
15 -> T.Map {T.meta = meta;
16 T.value = (Types.MalMap.fold
17 (fun k v m
18 -> Types.MalMap.add (eval k env) (eval v env) m)
19 xs
20 Types.MalMap.empty)}
21 | _ -> ast
22 and eval ast env =
23 match ast with
24 | T.List { T.value = [(T.Symbol { T.value = "def!" }); key; expr] } ->
25 let value = (eval expr env) in
26 Env.set env key value; value
27 | T.List { T.value = [(T.Symbol { T.value = "let*" }); (T.Vector { T.value = bindings }); body] }
28 | T.List { T.value = [(T.Symbol { T.value = "let*" }); (T.List { T.value = bindings }); body] } ->
29 (let sub_env = Env.make (Some env) in
30 let rec bind_pairs = (function
31 | sym :: expr :: more ->
32 Env.set sub_env sym (eval expr sub_env);
33 bind_pairs more
34 | _::[] -> raise (Invalid_argument "let* bindings must be an even number of forms")
35 | [] -> ())
36 in bind_pairs bindings;
37 eval body sub_env)
38 | T.List { T.value = ((T.Symbol { T.value = "do" }) :: body) } ->
39 List.fold_left (fun x expr -> eval expr env) T.Nil body
40 | T.List { T.value = [T.Symbol { T.value = "if" }; test; then_expr; else_expr] } ->
41 if Types.to_bool (eval test env) then (eval then_expr env) else (eval else_expr env)
42 | T.List { T.value = [T.Symbol { T.value = "if" }; test; then_expr] } ->
43 if Types.to_bool (eval test env) then (eval then_expr env) else T.Nil
44 | T.List { T.value = [T.Symbol { T.value = "fn*" }; T.Vector { T.value = arg_names }; expr] }
45 | T.List { T.value = [T.Symbol { T.value = "fn*" }; T.List { T.value = arg_names }; expr] } ->
46 Types.fn
47 (function args ->
48 let sub_env = Env.make (Some env) in
49 let rec bind_args a b =
50 (match a, b with
51 | [T.Symbol { T.value = "&" }; name], args -> Env.set sub_env name (Types.list args);
52 | (name :: names), (arg :: args) ->
53 Env.set sub_env name arg;
54 bind_args names args;
55 | [], [] -> ()
56 | _ -> raise (Invalid_argument "Bad param count in fn call"))
57 in bind_args arg_names args;
58 eval expr sub_env)
59 | T.List _ ->
60 (match eval_ast ast env with
61 | T.List { T.value = ((T.Fn { T.value = f }) :: args) } -> f args
62 | _ -> raise (Invalid_argument "Cannot invoke non-function"))
63 | _ -> eval_ast ast env
64
65 let read str = Reader.read_str str
66 let print exp = Printer.pr_str exp true
67 let rep str env = print (eval (read str) env)
68
69 let rec main =
70 try
71 Core.init Core.ns;
72 ignore (rep "(def! not (fn* (a) (if a false true)))" repl_env);
73 while true do
74 print_string "user> ";
75 let line = read_line () in
76 try
77 print_endline (rep line repl_env);
78 with End_of_file -> ()
79 | Invalid_argument x ->
80 output_string stderr ("Invalid_argument exception: " ^ x ^ "\n");
81 flush stderr
82 done
83 with End_of_file -> ()