module Evaluator open AST open System (* Represents a variable environment *) type Env = Map (* prettyprint * Prints out Blub expressions all nice looking. * The moral equivalent of toString in Java. *) let rec prettyprint (e: Expr) : string = match e with | Num n -> string n | EString s -> s | Variable v -> "Variable(" + v + ")" | Assignment (e1, e2) -> "Assignment(" + (prettyprint e1) + ", " + (prettyprint e2) + ")" | Plus (e1, e2) -> "Plus(" + (prettyprint e1) + ", " + (prettyprint e2) + ")" | Print e -> "Print(" + (prettyprint e) + ")" | Sequence es -> let pe = es |> List.map prettyprint "Sequence(" + String.Join(",", pe) + ")" (* eval * The Blub interpreter! * Always takes in an expression and an environment, and * always returns an expression and an updated environment. * * Blub does not statically check data types; it does * perform dynamic checks in some places. * Blub is "closed" under all Blub operations, * meaning that a Blub expression will never return * something that isn't a Blub expression. *) let rec eval (e: Expr)(env: Env) : Expr * Env = match e with | Num n -> e, env | EString s -> e, env | Print e -> let er, env1 = eval e env printfn "%s" (prettyprint er) er, env1 | Variable v -> if env.ContainsKey v then let value = env[v] value, env else printfn $"ERROR: Undefined variable '{v}'." exit 1 | Assignment (lhs,rhs) -> match lhs with | Variable v -> let rhsr, env1 = eval rhs env let env2 = env1.Add (v, rhsr) rhsr, env2 | _ -> printfn "ERROR: The left side of an assignment must be a variable." exit 1 | Plus (lhs, rhs) -> let lhsr, env1 = eval lhs env let rhsr, env2 = eval rhs env1 match lhsr, rhsr with | Num n1, Num n2 -> Num (n1 + n2), env2 | _ -> printfn "ERROR: Operands for addition must evaluate to numeric data." exit 1 | Sequence es -> match es with | [] -> printfn "ERROR: Empty sequence is not allowed." exit 1 | [e] -> eval e env | e::es2 -> let _, env1 = eval e env let s = Sequence es2 eval s env1