Example #1
0
        // Return the value of a target symbol in this or an outer scope.
        public MalVal Get(MalSym keySymbol)
        {
            Env e = Find(keySymbol);

            if (e == null)
            {
                switch (keySymbol.ToString(true))
                {
                // Some symbols are only valid when used in a particular context.

                case "unquote":
                    throw new MalEvalError("'unquote' used incorrectly (missing macro?)");

                case "quasiquote":
                    throw new MalEvalError("'quasiquote' used incorrectly (missing macro?)");

                case "splice-unquote":
                    throw new MalEvalError("'splice-unquote' used incorrectly (missing macro?)");
                }
                //// If here the symbol simply hasn't been defined or is mis-spelt.
                throw new MalLookupError("Get - Symbol not found '" + keySymbol.ToString(true) + "'");
            }
            else
            {
                if (e.data.TryGetValue(keySymbol.getName(), out MalVal value))
                {
                    return(value);
                }
                else
                {
                    throw new MalInternalError("Get - Find successful but symbol retrieval failed '" + keySymbol.ToString(true) + "'");
                }
            }
        }
Example #2
0
 public void Set(MalSym keySymbol, MalVal value)
 {
     // Takes a symbol key and a mal value and adds them to the environment.
     if (!data.TryAdd(keySymbol.getName(), value))
     {
         // Symbol can shadow an equivalent in an outer scope but cannot be duped.
         throw new MalEvalError("Attempt to redefine '" + keySymbol.ToString(true) + "'");
     }
 }
Example #3
0
        // Return the value of a target symbol in this or an outer scope.
        public MalVal Get(MalSym keySymbol)
        {
            Env e = Find(keySymbol);

            if (e != null && e.data.TryGetValue(keySymbol.getName(), out MalVal value))
            {
                return(value);
            }
            throw new MalEvalError("Symbol not found '" + keySymbol.ToString(true) + "'");
        }
Example #4
0
        public Env(Env outer, MalSym binds, MalSym exprs)
        {
            // TODO - these binds and exprs might not be malsyms
            // A key-MalVal dictionary.
            data = new Dictionary <string, MalVal>();

            // The scope we are created in. Null is taken to mean the REPL itself.
            this.outer = outer;

            // Bind (set) each element (symbol) to the respective elements of the exprs list
            throw new MalInternalError("Env can't bind yet");
        }
Example #5
0
 // Search for and return the environment (scope) that contains a target symbol.
 public Env Find(MalSym keySymbol)
 {
     if (data.ContainsKey(keySymbol.getName()))
     {
         // Symbol exists in the current scope.
         return(this);
     }
     else if (outer != null)
     {
         // Recurse to search for the symbol in an outer scope.
         return(outer.Find(keySymbol));
     }
     else
     {
         // Symbol is not defined.
         return(null);
     }
 }
Example #6
0
        // Return the value of a target symbol in this or an outer scope.
        public MalVal Get(MalSym keySymbol)
        {
            Env e = Find(keySymbol);

            if (e != null && e.data.TryGetValue(keySymbol.getName(), out MalVal value))
            {
                return(value);
            }
            switch (keySymbol.ToString(true))
            {
            case "quote":
                throw new MalEvalError("'quote' used where expression expected");

            case "quasiquote":
                throw new MalEvalError("'quasiquote' used where expression expected");

            case "splice-unquote":
                throw new MalEvalError("'splice-unquote' used where expression expected");

            default:
                throw new MalEvalError("Symbol not found '" + keySymbol.ToString(true) + "'");
            }
        }
Example #7
0
        // Setup the REPL.
        public MalRepl()
        {
            // Load built-in functions into the initial eval environment.
            try
            {
                myEnv = new Env(null);

                // Load core functions.
                foreach (var thing in MalNameSpace)
                {
                    MalSym mSym = new MalSym(thing.Key);

                    if (myEnv.Find(mSym) == null)
                    {
                        myEnv.Set(mSym, thing.Value);
                    }
                    else
                    {
                        // The namespace itself may already have thrown an error but if not.
                        throw new MalInternalError("Attempt to refine symbol: '" + mSym.ToString(true) + "' with '" + thing.Value.ToString(true) + "'");
                    }
                }
                // Load the special eval core function. We have to do it here to access the REPL env.
                myEnv.Set(new MalSym("eval"), new MalFunc(args =>
                {
                    return(EVAL(args[0], myEnv));
                }));

                // some convenient test files
                // "F:\Mal-development\mal-tests\mal-step6-test-01.txt"
                // "F:\Mal-development\mal-tests\mal-code-01.txt"
                // (load-file "F:\Mal-development\mal-tests\mal-code-01.txt")

                // (eval (read-string (slurp "F:\Mal-development\mal-tests\mal-code-01.txt")))

                // Add 'core' functions defined using Mal itself.
                EVAL(READ("(def not (fn (a) (if a false true)))"), myEnv);

                // Establish a gensym mechanism
                EVAL(READ("(def *gensym-counter* (atom 0))"), myEnv);
                EVAL(READ("(def gensym (fn [] (symbol (str \"G__\" (swap *gensym-counter* (fn [x] (+ 1 x)))))))"), myEnv);

                EVAL(READ("(def load-file (fn (f) (eval (read-string (str \"(do \" (slurp f) \"))\")))))"), myEnv);

                EVAL(READ("(defmacro cond (fn (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"), myEnv);

                EVAL(READ("(defmacro or (fn (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let (condvar (gensym)) `(let (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))"), myEnv);
            }
            catch (System.TypeInitializationException e)
            {
                // Typically happens if there is a duplicate symbol in the namespace list
                Console.WriteLine("Unrecoverable error: " + e.InnerException.Message);
            }
            catch (MalLookupError e)
            {
                Console.WriteLine(e.Message);
            }
            catch (MalEvalError e)
            {
                Console.WriteLine(e.Message);
            }
            catch (MalParseError e)
            {
                Console.WriteLine(e.Message);
            }
        }