fsharp: step 3: Improved Env to work with chains. Implemented the def! special form.
authorPeter Stephens <code@diligentsoftware.com>
Mon, 16 Mar 2015 12:09:10 +0000 (07:09 -0500)
committerPeter Stephens <code@diligentsoftware.com>
Mon, 16 Mar 2015 12:09:10 +0000 (07:09 -0500)
fsharp/Makefile
fsharp/env.fs [new file with mode: 0644]
fsharp/eval.fs
fsharp/step2_eval.fs
fsharp/step3_env.fs [new file with mode: 0644]

index 071e66c..82c1d56 100644 (file)
@@ -4,7 +4,8 @@ DEBUG =
 
 TESTS =
 
-SOURCES_BASE = types.fs core.fs tokenizer.fs reader.fs eval.fs printer.fs readline.fs
+SOURCES_BASE = types.fs core.fs tokenizer.fs reader.fs env.fs eval.fs \
+               printer.fs readline.fs
 SOURCES_LISP =
 SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
 
@@ -12,7 +13,7 @@ TERMINAL_SOURCES = terminal.cs
 
 #####################
 
-SRCS = step0_repl.fs step1_read_print.fs step2_eval.fs
+SRCS = step0_repl.fs step1_read_print.fs step2_eval.fs step3_env.fs
 
 FSFLAGS = $(if $(strip $(DEBUG)),--debug+,)
 CSFLAGS = $(if $(strip $(DEBUG)),-debug+,)
diff --git a/fsharp/env.fs b/fsharp/env.fs
new file mode 100644 (file)
index 0000000..6a95ac2
--- /dev/null
@@ -0,0 +1,45 @@
+module Env
+
+    open Types
+
+    type Env = System.Collections.Generic.Dictionary<string, Node>
+    type EnvChain = Env list
+
+    let errSymbolNotFound s = EvalError(sprintf "'%s' not found" s)
+    let errNoEnvironment () = EvalError("no environment")
+
+    let makeEmpty () = Env()
+
+    let ofList lst =
+        let env = makeEmpty ()
+        let accumulate (e : Env) (k, v) = e.Add(k, v); e
+        List.fold accumulate env lst
+
+    let set (env : EnvChain) key node =
+        match env with
+        | head::_ -> head.[key] <- node
+        | _ -> raise <| errNoEnvironment ()
+
+    let rec find (chain : EnvChain) key =
+        match chain with
+        | [] -> None
+        | env::rest ->
+            match env.TryGetValue(key) with
+            | true, v -> Some(v)
+            | false, _ -> find rest key
+
+    let get chain key =
+        match find chain key with
+        | Some(v) -> v
+        | None -> raise <| errSymbolNotFound key
+
+    let makeRootEnv () =
+        let wrap tag name func = name, Func({ Tag = tag; Name = name; F = func })
+        let env =
+            [ wrap 1 "+" Core.add;
+              wrap 2 "-" Core.subtract;
+              wrap 3 "*" Core.multiply;
+              wrap 4 "/" Core.divide ]
+            |> ofList
+        [ env ]
+
index 34ba5d9..5cd1b59 100644 (file)
@@ -5,31 +5,28 @@ module Eval
     type Env = Map<string, Node>
 
     let errFuncExpected () = EvalError("expected function")
-    let errNotFound s = EvalError(sprintf "'%s' not found" s)
-    
-    let wrap tag name func =
-        name, Func({ Tag = tag; Name = name; F = func })
-
-    let makeEnv () =
-        [ wrap 1 "+" Core.add;
-          wrap 2 "-" Core.subtract;
-          wrap 3 "*" Core.multiply;
-          wrap 4 "/" Core.divide ]
-        |> Map.ofList
-
-    let lookup (env : Env) sym =
-        match env.TryFind sym with
-        | Some(f) -> f
-        | None -> raise <| errNotFound sym
+    let errNodeExpected () = EvalError("expected node")
+    let errSymbolExpected () = EvalError("expected symbol")
 
     let rec eval_ast env = function
-        | Symbol(sym) -> lookup env sym
+        | Symbol(sym) -> Env.get env sym
         | List(lst) -> lst |> List.map (eval env) |> List
         | Vector(arr) -> arr |> Array.map (eval env) |> Vector
         | Map(map) -> map |> Map.map (fun k v -> eval env v) |> Map
         | node -> node
 
+    and def env = function
+        | symb::node::[] -> 
+            match symb with
+            | Symbol(sym) -> 
+                let node = eval env node 
+                Env.set env sym node
+                node
+            | _ -> raise <| errSymbolExpected ()
+        | _ -> raise <| Core.errArity () 
+
     and eval env = function
+        | List(Symbol("def!")::rest) -> def env rest
         | List(_) as node ->
             let resolved = node |> eval_ast env
             match resolved with
index 93b56ac..9977317 100644 (file)
@@ -9,8 +9,7 @@ module REPL
             printfn "%s" msg
             []
 
-    let eval ast =
-        let env = Eval.makeEnv ()
+    let eval env ast =
         try
             Some(Eval.eval env ast)
         with
@@ -23,10 +22,10 @@ module REPL
         |> Printer.pr_str
         |> printfn "%s"
 
-    let rep input =
+    let rep env input =
         read input
         |> Seq.ofList
-        |> Seq.choose (fun form -> eval form)
+        |> Seq.choose (fun form -> eval env form)
         |> Seq.iter (fun value -> print value)
 
     let getReadlineMode (args : string array) =
@@ -38,8 +37,9 @@ module REPL
     [<EntryPoint>]
     let rec main args =
         let mode = getReadlineMode args
+        let env = Env.makeRootEnv ()
         match Readline.read "user> " mode with
         | null -> 0
         | input -> 
-            rep input
+            rep env input
             main args
diff --git a/fsharp/step3_env.fs b/fsharp/step3_env.fs
new file mode 100644 (file)
index 0000000..beb6a26
--- /dev/null
@@ -0,0 +1,47 @@
+module REPL
+    open System
+
+    let read input =
+        try
+            Reader.read_str input
+        with
+        | Types.ReaderError(msg) ->
+            printfn "%s" msg
+            []
+
+    let eval env ast =
+        try
+            Some(Eval.eval env ast)
+        with
+        | Types.EvalError(msg) ->
+            printfn "%s" msg
+            None
+
+    let print v =
+        v
+        |> Printer.pr_str
+        |> printfn "%s"
+
+    let rep env input =
+        read input
+        |> Seq.ofList
+        |> Seq.choose (fun form -> eval env form)
+        |> Seq.iter (fun value -> print value)
+
+    let getReadlineMode (args : string array) =
+        if args.Length > 0 && args.[0] = "--raw" then
+            Readline.Mode.Raw
+        else
+            Readline.Mode.Terminal
+
+    [<EntryPoint>]
+    let main args =
+        let mode = getReadlineMode args
+        let env = Env.makeRootEnv ()
+        let rec loop () =
+            match Readline.read "user> " mode with
+            | null -> 0
+            | input ->
+                rep env input
+                loop ()
+        loop ()