/// <summary>
        /// Evalues an AST in the environment.
        ///
        /// TODO: refactor to move to <see cref="OpenLisp.Core.StaticClasses.CoreNameSpace"/>.
        /// </summary>
        /// <param name="abstractSyntaxTree"></param>
        /// <param name="environment"></param>
        /// <returns></returns>
        public static OpenLispVal EvalAst(OpenLispVal abstractSyntaxTree, Env environment)
        {
            var key = abstractSyntaxTree as OpenLispSymbol;

            if (key != null)
            {
                return(environment.Get(key));
            }

            var list = abstractSyntaxTree as OpenLispList;

            if (list == null)
            {
                var map = abstractSyntaxTree as OpenLispHashMap;
                if (map == null)
                {
                    return(abstractSyntaxTree);
                }
                var newDictionary =
                    map.Value.ToDictionary(
                        entry => entry.Key, entry => Eval(entry.Value, environment));
                return(new OpenLispHashMap(newDictionary));
            }

            OpenLispList oldList = list;
            OpenLispList newList = abstractSyntaxTree.ListQ() ? new OpenLispList()
                : (OpenLispList) new OpenLispVector();

            foreach (OpenLispVal movedValue in oldList.Value)
            {
                newList.Conj(Eval(movedValue, environment));
            }
            return(newList);
        }
        /// <summary>
        /// Evaluate an <see cref="OpenLispVal"/> inside an <seealso cref="Env"/>.
        ///
        /// The core namespace is defined in <seealso cref="OpenLisp.Core.StaticClasses.CoreNameSpace"/>.
        ///
        /// TODO: refactor the switch over treeHeadSymbol.  All symbols of the core language should be defined in the same place.
        /// </summary>
        /// <param name="originalAbstractSyntaxTree"></param>
        /// <param name="environment"></param>
        /// <returns></returns>
        public static OpenLispVal Eval(OpenLispVal originalAbstractSyntaxTree, Env environment)
        {
            while (true)
            {
                //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true));
                if (!originalAbstractSyntaxTree.ListQ())
                {
                    return(EvalAst(originalAbstractSyntaxTree, environment));
                }

                // apply list
                OpenLispVal expanded = MacroExpand(originalAbstractSyntaxTree, environment);
                if (!expanded.ListQ())
                {
                    return(expanded);
                }

                OpenLispList abstractSyntaxTree = (OpenLispList)expanded;
                if (abstractSyntaxTree.Size == 0)
                {
                    return(abstractSyntaxTree);
                }

                var treeHead = abstractSyntaxTree[0];

                var    symbol         = treeHead as OpenLispSymbol;
                String treeHeadSymbol = symbol?.ToString() ?? "__<*fn*>__";

                // Let's get alchemical in our metaphors:
                OpenLispVal caputPrimus;    // The First Head.  Here's a vector: [1 lol 2 3 apple].  caputPrimus should be: 1.
                OpenLispVal caputSecundus;  // The Second Head.  Here's a list: `(1 lol 2 3 apple).  caputSecundus should be: lol.
                OpenLispVal solutio;

                switch (treeHeadSymbol)
                {
                // TODO: extract this switch out of the REPL and consolidate in the core NS.
                case "def!":
                    caputPrimus   = abstractSyntaxTree[1];
                    caputSecundus = abstractSyntaxTree[2];
                    solutio       = Eval(caputSecundus, environment);
                    environment.Set((OpenLispSymbol)caputPrimus, solutio);
                    return(solutio);

                case "let*":
                    caputPrimus   = abstractSyntaxTree[1];
                    caputSecundus = abstractSyntaxTree[2];
                    OpenLispSymbol key;
                    OpenLispVal    value;
                    Env            letEnvironment = new Env(environment); // TODO: explain ramifications to memory allocation and protection by creating a new Env object this way.
                    for (int i = 0; i < ((OpenLispList)caputPrimus).Size; i += 2)
                    {
                        key   = (OpenLispSymbol)((OpenLispList)caputPrimus)[i];
                        value = ((OpenLispList)caputPrimus)[i + 1];
                        letEnvironment.Set(key, Eval(value, letEnvironment));
                    }
                    originalAbstractSyntaxTree = caputSecundus;
                    environment = letEnvironment;
                    break;

                case "quote":
                    return(abstractSyntaxTree[1]);

                case "quasiquote":
                    originalAbstractSyntaxTree = QuasiQuote(abstractSyntaxTree[1]);
                    break;

                case "defmacro!":
                    caputPrimus   = abstractSyntaxTree[1];
                    caputSecundus = abstractSyntaxTree[2];
                    solutio       = Eval(caputSecundus, environment);
                    ((OpenLispFunc)solutio).Macro = true;
                    environment.Set(((OpenLispSymbol)caputPrimus), solutio);
                    return(solutio);

                case "macroexpand":
                    caputPrimus = abstractSyntaxTree[1];
                    return(MacroExpand(caputPrimus, environment));

                case "try*":
                    try
                    {
                        return(Eval(abstractSyntaxTree[1], environment));
                    }
                    catch (Exception caught)
                    {
                        if (abstractSyntaxTree.Size <= 2)
                        {
                            throw caught;
                        }

                        OpenLispVal openLispException;

                        caputSecundus = abstractSyntaxTree[2];
                        OpenLispVal caputSecundusHead = ((OpenLispList)caputSecundus)[0];
                        if (((OpenLispSymbol)caputSecundusHead).ToString() != "catch*")
                        {
                            throw caught;
                        }

                        var exception = caught as OpenLispException;
                        openLispException = exception != null
                                ? (OpenLispVal)exception.Value
#if TRACE
                                : new OpenLispString(caught.StackTrace);
#elif !TRACE
                                            : new OpenLispString("Stack Trace not yet available in OS.");
#endif
                        return(Eval(((OpenLispList)caputSecundus)[2],
                                    new Env(environment, ((OpenLispList)caputSecundus).Slice(1, 2),
                                            new OpenLispList(openLispException))));
                    }