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)
#####################
-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+,)
--- /dev/null
+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 ]
+
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
printfn "%s" msg
[]
- let eval ast =
- let env = Eval.makeEnv ()
+ let eval env ast =
try
Some(Eval.eval env ast)
with
|> 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) =
[<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
--- /dev/null
+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 ()