shithub: martian9

Download patch

ref: cc10399b3b05fc3fcda7f04afe9b34fffa7f743a
parent: b34fba0e5b9e39fc5120f06b6c335033472f81ca
author: smazga <[email protected]>
date: Thu Aug 6 09:33:45 EDT 2020

format and switch building to ocamlc for plan9

--- a/m9.ml
+++ b/m9.ml
@@ -12,37 +12,11 @@
 
 module T = Types.Types
 
-module Env = Map.Make (String
-(*(struct
-  type t = Types.Symbol
-  let compare (Types.Symbol a) (Types.Symbol b) = compare a b
-  end)*))
+let repl_env = Env.make (Some Core.base)
 
-(* replace me *)
-let num_fun f =
-  Types.proc (function
-      | [ T.Number a; T.Number b ] -> T.Number (f a b)
-      | _ -> raise (Invalid_argument "Expected numeric argument"))
-;;
-
-(* replace me *)
-let repl_env =
-  ref
-    (List.fold_left
-       (fun a b -> b a)
-       Env.empty
-       [ Env.add "+" (num_fun ( + ))
-       ; Env.add "-" (num_fun ( - ))
-       ; Env.add "*" (num_fun ( * ))
-       ; Env.add "/" (num_fun ( / ))
-       ])
-;;
-
 let rec eval_ast ast env =
   match ast with
-  | T.Symbol { T.value = s } ->
-    (try Env.find s !env with
-    | Not_found -> raise (Invalid_argument ("Symbol '" ^ s ^ "' not found")))
+  | 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 } ->
@@ -50,10 +24,61 @@
   | _ -> ast
 
 and eval ast env =
-  let result = eval_ast ast env in
-  match result with
-  | T.List { T.value = T.Proc { T.value = f } :: args } -> f args
-  | _ -> result
+  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"
@@ -63,6 +88,7 @@
 
 let rec main =
   try
+    Core.init Core.base;
     print_endline nameplate;
     while true do
       print_string "m9> ";
--- a/mkfile
+++ b/mkfile
@@ -1,13 +1,19 @@
 BIN=m9
 
-$BIN: types.cmx printer.cmx reader.cmx $BIN.cmx
-	ocamlopt -o $target str.cmxa $prereq
+# $BIN: types.cmx env.cmx core.cmx printer.cmx reader.cmx $BIN.cmx
+# 	ocamlopt -o $target str.cmxa $prereq
 
+$BIN: types.cmo env.cmo core.cmo printer.cmo reader.cmo $BIN.cmo
+	ocamlc -o $target str.cma $prereq
+
 %.cmx : %.ml
 	ocamlopt -c $stem.ml
 
+%.cmo : %.ml
+	ocamlc -c $stem.ml
+
 install:V: $BIN
 	cp $prereq ~/bin/$BIN
 
 clean:V:
-	rm -f $BIN *.cmx *.cmi *.o
+	rm -f $BIN *.cmx *.cmi *.cmo
--- a/printer.ml
+++ b/printer.ml
@@ -8,6 +8,7 @@
   | T.Vector { T.meta } -> meta
   | T.Record { T.meta } -> meta
   | _ -> T.Nil
+;;
 
 let rec print obj readable =
   let r = readable in
@@ -20,14 +21,18 @@
   (* | T.Pair { T.value = one, two } -> "(" ^ one ^ " . " ^ two ^ ")" *)
   | T.Pair (p, q) -> "<pair unsupported>"
   | T.Proc p -> "#<proc>"
-  | T.Symbol {T.value = s} -> s
+  | T.Symbol { T.value = s } -> s
   | T.Bytevector bv -> "<bytevector unsupported>"
   | T.Eof_object -> "<eof>"
-  | T.Number n -> string_of_int n
+  | T.Number n ->
+    if Types.is_float n.value
+    then string_of_float n.value
+    else string_of_int (int_of_float n.value)
   | T.Port p -> "<port unsupported>"
   | T.String s -> s (* need to handle escaping and stuff *)
   | T.List { T.value = xs } ->
-     "(" ^ (String.concat " " (List.map (fun s -> print s r) xs)) ^ ")"
-  | T.Vector {T.value = v} -> "#(" ^ (String.concat " " (List.map (fun s -> print s r) v)) ^ ")"
+    "(" ^ String.concat " " (List.map (fun s -> print s r) xs) ^ ")"
+  | T.Vector { T.value = v } ->
+    "#(" ^ String.concat " " (List.map (fun s -> print s r) v) ^ ")"
   | T.Record r -> "<record supported>"
-
+;;
--- a/reader.ml
+++ b/reader.ml
@@ -1,54 +1,79 @@
 module T = Types.Types
 
-let token_re = Str.regexp "~@\\|[][{}()'`~^@]\\|\"\\(\\\\.\\|[^\"]\\)*\"?\\|;.*\\|[^][  \n{}('\"`,;)]*"
+let token_re =
+  Str.regexp "~@\\|[][{}()'`~^@]\\|\"\\(\\\\.\\|[^\"]\\)*\"?\\|;.*\\|[^][  \n{}('\"`,;)]*"
+;;
+
 let string_re = Str.regexp "\"\\(\\\\.\\|[^\\\\\"]\\)*\""
 
-type reader = { form : Types.m9type; tokens : string list }
-type list_reader = { list_form : Types.m9type list; tokens : string list }
+type reader =
+  { form : Types.m9type
+  ; tokens : string list
+  }
 
+type list_reader =
+  { list_form : Types.m9type list
+  ; tokens : string list
+  }
+
 let tokenize str =
   List.map
-    (function Str.Delim x -> x | Str.Text x -> "botch")
-    (List.filter (function Str.Delim x -> true | Str.Text x -> false) (Str.full_split token_re str))
+    (function
+      | Str.Delim x -> x
+      | Str.Text x -> "tokenize botch")
+    (List.filter
+       (function
+         | Str.Delim x -> true
+         | Str.Text x -> false)
+       (Str.full_split token_re str))
+;;
 
 let read_atom token =
   match token with
-  | "null"  -> T.Nil
-  | "true"  -> T.Bool true
-  | "false" -> T.Bool false
-  | _       -> (
-      match token.[0] with
-      | '0' .. '9' -> T.Number (int_of_string token)
-      | '-'        -> (
-          match String.length token with
-          | 1 -> Types.symbol token
-          | _ -> ( match token.[1] with '0' .. '9' -> T.Number (int_of_string token) | _ -> Types.symbol token ) )
-      | '"'        -> T.String token (* TODO: unescape *)
-      | _          -> Types.symbol token )
+  | "null" -> T.Nil
+  | "#t" | "#true" -> T.Bool true
+  | "#f" | "#false" -> T.Bool false
+  | _ ->
+    (match token.[0] with
+    | '0' .. '9' -> Types.number (float_of_string token)
+    | '-' ->
+      (match String.length token with
+      | 1 -> Types.symbol token
+      | _ ->
+        (match token.[1] with
+        | '0' .. '9' -> Types.number (float_of_string token)
+        | _ -> Types.symbol token))
+    | '"' -> T.String token (* TODO: unescape *)
+    | _ -> Types.symbol token)
+;;
 
 let rec read_list eol list_reader =
   match list_reader.tokens with
-  | []              ->
-      print_endline "unexpected EOF";
-      raise End_of_file
+  | [] ->
+    print_endline "unexpected EOF";
+    raise End_of_file
   | token :: tokens ->
-      if Str.string_match (Str.regexp eol) token 0 then
-        { list_form = list_reader.list_form; tokens }
-      else
-        let reader = read_form list_reader.tokens in
-        read_list eol { list_form = list_reader.list_form @ [ reader.form ]; tokens = reader.tokens }
+    if Str.string_match (Str.regexp eol) token 0
+    then { list_form = list_reader.list_form; tokens }
+    else (
+      let reader = read_form list_reader.tokens in
+      read_list
+        eol
+        { list_form = list_reader.list_form @ [ reader.form ]; tokens = reader.tokens })
 
 and read_form all_tokens =
   match all_tokens with
-  | []              -> raise End_of_file
-  | token :: tokens -> (
-      match token with
-      | "("  ->
-          let list_reader = read_list ")" { list_form = []; tokens } in
-          { form = Types.list list_reader.list_form; tokens = list_reader.tokens }
-      | "#|" ->
-          let list_reader = read_list "|#" { list_form = []; tokens } in
-          { form = T.Comment; tokens = list_reader.tokens }
-      | _    -> if token.[0] = ';' then read_form tokens else { form = read_atom token; tokens } )
+  | [] -> raise End_of_file
+  | token :: tokens ->
+    (match token with
+    | "(" ->
+      let list_reader = read_list ")" { list_form = []; tokens } in
+      { form = Types.list list_reader.list_form; tokens = list_reader.tokens }
+    | "#|" ->
+      let list_reader = read_list "|#" { list_form = []; tokens } in
+      { form = T.Comment; tokens = list_reader.tokens }
+    | _ ->
+      if token.[0] = ';' then read_form tokens else { form = read_atom token; tokens })
+;;
 
 let read_str str = (read_form (tokenize str)).form
--- a/types.ml
+++ b/types.ml
@@ -1,5 +1,8 @@
 module rec Types : sig
-  type 'a with_meta = { value : 'a; meta : t }
+  type 'a with_meta =
+    { value : 'a
+    ; meta : t
+    }
 
   and t =
     | List of t list with_meta
@@ -12,7 +15,7 @@
     | Symbol of string with_meta
     | Bytevector of t list
     | Eof_object
-    | Number of int (* needs to handle more than one type *)
+    | Number of float with_meta
     | Port of bool (* not sure how to represent this *)
     | String of string
     | Vector of t list with_meta
@@ -32,12 +35,20 @@
 
 type m9type = Value.t
 
-let list x = Types.List { Types.value = x; meta = Types.Nil }
+let to_bool x =
+  match x with
+  | Types.Nil | Types.Bool false -> false
+  | _ -> true
+;;
 
-let proc x = Types.Proc { Types.value = x; meta = Types.Nil }
+let is_float v =
+  let c = classify_float (fst (Float.modf v)) in
+  c != FP_zero
+;;
 
+let list x = Types.List { Types.value = x; meta = Types.Nil }
+let proc x = Types.Proc { Types.value = x; meta = Types.Nil }
 let symbol x = Types.Symbol { Types.value = x; meta = Types.Nil }
-
 let vector x = Types.Vector { Types.value = x; meta = Types.Nil }
-
 let record x = Types.Record { Types.value = x; meta = Types.Nil }
+let number x = Types.Number { Types.value = x; meta = Types.Bool (is_float x) }