ref: 2e9df13b3aae992797d8b5ed2a189a4154c201fe
dir: /m9.ml/
(* Martian Scheme Copyright 2020, McKay Marston This is a project for me to 1) Get more familiar with OCaml. 2) Try to provide a natively supported scheme for Plan9. It is heavily inspired by s9fes (http://www.t3x.org/s9fes), and the make a lisp project (https://github.com/kanaka/mal) *) module T = Types.Types let repl_env = Env.make (Some Core.base) let rec eval_ast ast env = match ast with | T.Symbol s -> Env.get env ast | T.List { T.value = xs; T.meta } -> T.List { T.value = List.map (fun x -> eval x env) xs; T.meta } | T.Vector { T.value = xs; T.meta } -> T.Vector { T.value = List.map (fun x -> eval x env) xs; T.meta } | _ -> ast and eval ast env = match ast with | T.List { T.value = [] } -> ast | T.List { T.value = [ T.Symbol { T.value = "define" }; key; expr ] } -> let value = eval expr env in Env.set env key value; value | T.List { T.value = [ T.Symbol { T.value = "let" }; T.Vector { T.value = bindings }; body ] } | T.List { T.value = [ T.Symbol { T.value = "let" }; T.List { T.value = bindings }; body ] } -> let sub_env = Env.make (Some env) in let rec bind_pairs = function | sym :: expr :: more -> Env.set sub_env sym (eval expr sub_env); bind_pairs more | [ _ ] -> raise (Invalid_argument "let missing body") | [] -> () in bind_pairs bindings; eval body sub_env | T.List { T.value = [ T.Symbol { T.value = "lambda" }; T.Vector { T.value = arg_names }; expr ] } | T.List { T.value = [ T.Symbol { T.value = "lambda" }; T.List { T.value = arg_names }; expr ] } -> Types.proc (function args -> let sub_env = Env.make (Some env) in let rec bind_args a b = match a, b with | [ T.Symbol { T.value = "&" }; name ], args -> Env.set sub_env name (Types.list args) | name :: names, arg :: args -> Env.set sub_env name arg; bind_args names args | [], [] -> () | _ -> raise (Invalid_argument "wrong parameter count") in bind_args arg_names args; eval expr sub_env) | T.List { T.value = T.Symbol { T.value = "begin" } :: body } -> List.fold_left (fun x expr -> eval expr env) T.Nil body | T.List { T.value = [ T.Symbol { T.value = "if" }; cond; then_expr; else_expr ] } -> if Types.to_bool (eval cond env) then eval then_expr env else eval else_expr env | T.List { T.value = [ T.Symbol { T.value = "if" }; cond; then_expr ] } -> if Types.to_bool (eval cond env) then eval then_expr env else T.Nil | T.List _ -> (match eval_ast ast env with | T.List { T.value = T.Proc { T.value = f } :: args } -> f args | _ -> raise (Invalid_argument "not a function")) | _ -> eval_ast ast env ;; let nameplate = "Martian9 Scheme v0.1" let read str = Reader.read_str str let print exp = Printer.print exp true let rep str env = print (eval (read str) env) let rec main = try Core.init Core.base; print_endline nameplate; while true do print_string "m9> "; let line = read_line () in try print_endline (rep line repl_env) with | End_of_file -> () | Invalid_argument x -> output_string stderr ("Invalid argument: " ^ x ^ "\n"); flush stderr done with | End_of_file -> () ;;