Esempio n. 1
0
        // Name changed from the guide to highlight that the outcome is complicated.
        static MalVal Quasiquote(MalVal ast)
        {
            if (!is_pair(ast))
            {
                // Case 1.
                // If is_pair of ast is false: return a new list containing: a symbol named "quote" and ast.
                MalList qList = new MalList();

                qList.Add(new MalSym("quote"));
                qList.Add(ast);

                //qList.Conj(new MalSym("quote"), ast);
                return(qList);
            }
            else
            {
                MalSequence astSeq = (MalSequence)ast;
                MalVal      a0     = astSeq[0];

                // Case 2:  if the first element of ast is a symbol named "unquote": return the second element of ast.
                if (a0 is MalSym a0Sym && a0Sym.getName() == "unquote")
                {
                    // (qq (uq form)) -> form
                    return(astSeq[1]);
                }
Esempio n. 2
0
        static MalVal eval_ast(MalVal ast, Env env)
        {
            // TODO - handle malHashMap.
            // Switch on the type of the ast.
            switch ((Object)ast)
            {
            case MalSym malSym:
                return(env.Get(malSym));

            case MalList malList:
                // Return a new list that is the result of calling EVAL on each of the members of the list.
                MalList derivedList = new MalList();
                for (int i = 0; i < malList.Count(); i++)
                {
                    derivedList.Add(EVAL(malList.Get(i), env));
                }
                return(derivedList);

            case MalVector malVec:
                // Return a new vector that is the result of calling EVAL on each of the members of the vector.
                MalVector derivedVec = new MalVector();
                for (int i = 0; i < malVec.Count(); i++)
                {
                    derivedVec.Add(EVAL(malVec.Get(i), env));
                }
                return(derivedVec);

            case MalHashMap malHashMap:
                throw new MalInternalError("Can't evaluate a HashMap yet '" + malHashMap.ToString() + "'");

            default:
                // It's not a symbol or a list.
                return(ast);
            }
        }
Esempio n. 3
0
        static MalVal EvalIf(MalList astList, Env env)
        {
            // If has the syntax (if <cond> <true-branch> <optional-false-branch>)
            if (astList.Count() < 3 || astList.Count() > 4)
            {
                throw new MalEvalError("'if' should have a condition, true branch and optional false branch");
            }
            // Evaluate the Cond part of the if.
            MalVal cond = EVAL(astList[1], env);

            if (cond is MalNil || cond is MalFalse)
            {
                // Cond is nil or false. Eval the 'false' branch, if any.
                if (astList.Count() == 4)
                {
                    return(EVAL(astList[3], env));
                }
                else
                {
                    return(new MalNil());
                }
            }
            else
            {
                // Eval the 'true' branch.
                return(EVAL(astList[2], env));
            }
        }
Esempio n. 4
0
File: types.cs Progetto: nboyd/mal
 //
 // General functions
 //
 public static bool _equal_Q(MalVal a, MalVal b)
 {
     Type ota = a.GetType(), otb = b.GetType();
     if (!((ota == otb) ||
         (a is MalList && b is MalList))) {
         return false;
     } else {
         if (a is MalInt) {
             return ((MalInt)a).getValue() ==
                 ((MalInt)b).getValue();
         } else if (a is MalSymbol) {
             return ((MalSymbol)a).getName() ==
                 ((MalSymbol)b).getName();
         } else if (a is MalString) {
             return ((MalString)a).getValue() ==
                 ((MalString)b).getValue();
         } else if (a is MalList) {
             if (((MalList)a).size() != ((MalList)b).size()) {
                 return false;
             }
             for (int i=0; i<((MalList)a).size(); i++) {
                 if (! _equal_Q(((MalList)a)[i], ((MalList)b)[i])) {
                     return false;
                 }
             }
             return true;
         } else {
             return a == b;
         }
     }
 }
Esempio n. 5
0
 public MalFunc(MalVal ast, Env e, MalList fparams, Func <MalList, MalVal> fn)
 {
     this.ast     = ast;
     this.env     = e;
     this.fparams = fparams;
     this.fn      = fn;
 }
Esempio n. 6
0
 public MalFunc(MalVal ast, Mal.env.Env env, MalList fparams,
                Func <MalList, MalVal> fn)
 {
     this.fn      = fn;
     this.ast     = ast;
     this.env     = env;
     this.fparams = fparams;
 }
Esempio n. 7
0
 public MalFunc(MalVal ast, Env e, MalSequence fparams, Func <MalList, MalVal> fn)
 {
     this.ast     = ast;
     this.env     = e;
     this.fparams = fparams;
     this.fn      = fn;
     IsCore       = false;
 }
Esempio n. 8
0
        // EVAL is handled by two functions - EVAL which decides whether or not the ast is a special
        // form or not and eval_ast which evaluates the remaining symbols, lists, etc.
        // EVAL is tail-call optimised, as per Mal guide step 5

        static MalVal eval_ast(MalVal ast, Env env)
        {
            // Switch on the type of the ast.
            switch ((Object)ast)
            {
            case MalSym malSym:
                return(env.Get(malSym));

            case MalList malList:
                // Return a new list that is the result of calling EVAL on each of the members of the list.
                MalList derivedList = new MalList();
                for (int i = 0; i < malList.Count(); i++)
                {
                    derivedList.Add(EVAL(malList.Get(i), env));
                }
                return(derivedList);

            case MalVector malVec:
                // Return a new vector that is the result of calling EVAL on each of the members of the vector.
                MalVector derivedVec = new MalVector();
                for (int i = 0; i < malVec.Count(); i++)
                {
                    derivedVec.Add(EVAL(malVec.Get(i), env));
                }
                return(derivedVec);

            case MalHashMap malHashMap:
                // Return a new hash-map which consists of key-value pairs where the key is a key from the hash-map
                // and the value is the result of calling EVAL on the corresponding value.
                if (malHashMap.Count() % 2 != 0)
                {
                    throw new MalEvalError("Hashmap requires an even number of elements forming key value pairs '" + malHashMap.ToString(true) + "'");
                }

                MalHashMap derivedHashMap = new MalHashMap();
                // Work through successive key value pairs.
                // Note - C#-Mal creates a dictionary, loads it and then passes the result to a Hashmap c'tor that takes a Dict.
                for (int i = 0; i < malHashMap.Count(); i += 2)
                {
                    MalVal key = malHashMap.Get(i);
                    if (key is MalString || key is MalKeyword)
                    {
                        derivedHashMap.Add(key);
                        derivedHashMap.Add(EVAL(malHashMap.Get(i + 1), env));
                    }
                    else
                    {
                        throw new MalEvalError("Expecting a keyword or string as a HashMap key but got '" + key.ToString(true) + "'");
                    }
                }
                return(derivedHashMap);

            default:
                // It's not a symbol or a list.
                return(ast);
            }
        }
Esempio n. 9
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) + "'");
     }
 }
Esempio n. 10
0
 public MalVal Reset(MalVal newVal)
 {
     // some interesting error cases here, e.g. (reset a (list a)).
     if (newVal == this)
     {
         throw new MalEvalError("Can't reset atom '" + this.ToString(true) + "' to itself");
     }
     myAtom = newVal;
     return(newVal);
 }
Esempio n. 11
0
 public bool Contains(MalVal target)
 {
     for (var i = 0; i < MyElements.Count; i++)
     {
         if (EQ(MyElements[i], target) == malTrue)
         {
             return(true);
         }
     }
     return(false);
 }
Esempio n. 12
0
 public MalAtom(MalVal atom)
 {
     if (atom == null)
     {
         throw new MalInternalError("Attempt to create null Atom");
     }
     if (((object)atom is MalNil) || ((object)atom is MalTrue) || ((object)atom is MalNil))
     {
         throw new MalEvalError("'" + atom.ToString(true) + "' cannot be an Atom");
     }
     myAtom = atom;
 }
Esempio n. 13
0
        static MalVal EvalDef(MalList astList, Env env)
        {
            // Evaluate all elements of list using eval_ast, retun the final eval'd element
            // Should be something like (def a b). Set() symbol a to be the result of evaluating b.
            if (astList.Count() != 3 || !(astList[1] is MalSym symbol))
            {
                throw new MalEvalError("'def' should be followed by a symbol and a value");
            }
            MalVal result = EVAL(astList[2], env);

            env.Set(symbol.getName(), result);
            return(result);
        }
Esempio n. 14
0
 // The converse of read_str - return a string rep of a MAL object.
 public static string pr_str(MalVal ast, bool printReadably)
 {
     // Mal Guide says switch on the ast type, but we use virtuals instead.
     if (ast != null)
     {
         return(ast.ToString(printReadably));
     }
     else
     {
         // The MAL guide doesn't do this check but it stops comment lines from crashing.
         return("");
     }
 }
Esempio n. 15
0
        // The guts of the REPL. It extendsthe C#-Mal reference REPL
        // to handle (1) multi-line forms and (2) multi-form lines.
        public void RunREPL()
        {
            while (true)
            {
                try
                {
                    // Read standard input.
                    string line = ReadLine.Readline("JKL> ");

                    // Exit if EOF or ctrl-Z.
                    if (line == null)
                    {
                        break;
                    }

                    // Go round again if empty line (user hit return).
                    if (line == "")
                    {
                        continue;
                    }

                    // Tokenise the input and load the tokens into a Reader object.
                    MalReader.Reader rdr = new MalReader.Reader(MalReader.Tokenizer(line));

                    // Loop until all tokens on the line have been consumed. This handles lines
                    // like (a b) (c d) - where Mal REPL would silently ignore (c d)
                    while (rdr.Peek() != null)
                    {
                        // Parse input to create a Mal abstract syntax tree. The parser
                        // attempts to reload the reader if it runs out of tokens mid form.
                        MalVal ast = READMULTILINE(rdr);

                        // Evaluate the ast and print the result.
                        Console.WriteLine(PRINT(EVAL(ast, myEnv)));
                    }
                }
                catch (MalParseError e)
                {
                    Console.WriteLine(e.Message);
                }
                catch (MalEvalError e)
                {
                    Console.WriteLine(e.Message);
                }
                catch (MalInternalError e)
                {
                    Console.WriteLine(e.Message);
                }
            }
        }
Esempio n. 16
0
        static MalVal eval_ast(MalVal ast, Dictionary <string, MalFunc> replEnv)
        {
            // TODO - handle vectors
            // Switch on the type of the ast.
            switch ((Object)ast)
            {
            case MalSym malSym:
                // Lookup the symbol in the environment and return the value or raise an error.
                string malsymstring = malSym.ToString(true);
                Console.WriteLine("eval_ast - searching for symbol: '" + malsymstring + "'");

                if (replEnv.TryGetValue(malsymstring, out MalFunc func))
                {
                    Console.WriteLine("eval_ast - found function '" + func.ToString(true) + "'");
                    return(func);
                }
                else
                {
                    throw new MalEvalError("Undefined symbol '" + malSym.ToString(true) + "'");
                }

            case MalList malList:
                MalList derivedList = new MalList();
                Console.WriteLine("eval_ast - handling MalList");
                for (int i = 0; i < malList.Count(); i++)
                {
                    derivedList.Add(EVAL(malList.Get(i), replEnv));
                }
                return(derivedList);

            case MalVector malVec:
                MalVector derivedVec = new MalVector();
                Console.WriteLine("eval_ast - handling MalVector");
                // TODO - return a new list that is the result of calling EVAL on each of the members of the list.
                for (int i = 0; i < malVec.Count(); i++)
                {
                    derivedVec.Add(EVAL(malVec.Get(i), replEnv));
                }
                return(derivedVec);

            case MalHashMap malHashMap:
                throw new MalEvalError("INTERNAL - can't evaluate a HashMap yet '" + malHashMap.ToString() + "'");

            default:
                // It's not a symbol or a list.
                return(ast);
            }
        }
Esempio n. 17
0
        //
        // General functions
        //

        public static bool _equal_Q(MalVal a, MalVal b)
        {
            Type ota = a.GetType(), otb = b.GetType();

            if (!((ota == otb) ||
                  (a is MalList && b is MalList)))
            {
                return(false);
            }
            else
            {
                if (a is MalInt)
                {
                    return(((MalInt)a).getValue() ==
                           ((MalInt)b).getValue());
                }
                else if (a is MalSymbol)
                {
                    return(((MalSymbol)a).getName() ==
                           ((MalSymbol)b).getName());
                }
                else if (a is MalString)
                {
                    return(((MalString)a).getValue() ==
                           ((MalString)b).getValue());
                }
                else if (a is MalList)
                {
                    if (((MalList)a).size() != ((MalList)b).size())
                    {
                        return(false);
                    }
                    for (int i = 0; i < ((MalList)a).size(); i++)
                    {
                        if (!_equal_Q(((MalList)a)[i], ((MalList)b)[i]))
                        {
                            return(false);
                        }
                    }
                    return(true);
                }
                else
                {
                    return(a == b);
                }
            }
        }
Esempio n. 18
0
        static MalVal EvalLet(MalList astList, Env env)
        {
            // Let* syntax is (let* <bindings-list> <result>), e.g. (let (p ( + 2 3) q (+ 2 p)) (+ p q)).
            // Bindings can refer to earlier bindings in the new let environment or to symbols
            // in the outer environment.

            // Confirm the structure of the let* form.
            if (astList.Count() != 3)
            {
                throw new MalEvalError("'let' should have two arguments (bindings-list and result), but instead had " + (astList.Count() - 1));
            }
            // Extract the first parameter - the bindings list. E.g. (p (+ 2 3) q (+ 2 p))
            if (!(astList[1] is MalList bindingsList))
            {
                throw new MalEvalError("'let' should be followed by a non-empty bindings list and a result form");
            }
            if (bindingsList.Count() <= 1 || bindingsList.Count() % 2 != 0)
            {
                throw new MalEvalError("'let' bindings list should have an even number of entries");
            }

            // Create a new Env - the scope of the Let form.
            Env LetEnv = new Env(env);

            // Process each pair of entries in the bindings list.
            for (int i = 0; i < bindingsList.Count(); i += 2)
            {
                // The first element should be a 'key' symbol. E.g. 'p'.
                if (!(bindingsList[i] is MalSym bindingKey))
                {
                    throw new MalEvalError("'let' expected symbol but got: '" + bindingsList[i].ToString(true) + "'");
                }
                // The second element is the value of the key symbol in Let's environment, E.g. (+ 2 3)
                MalVal val = EVAL(bindingsList[i + 1], LetEnv);
                // This expression can refer to earlier let bindings.

                // Now, store the new value in the environment.
                LetEnv.Set(bindingKey.getName(), val);
                // Note - this can silently redefine built-in functions e.g. if something like '+' is used.
            }

            // Using the populated Let* environment, evaluate and return the result form.
            // The Let* environment itself is discarded, unshadowing symbols in outer environments.
            return(EVAL(astList[2], LetEnv));
        }
Esempio n. 19
0
        static MalVal EvalMalFunc(MalVal ast, Env env)
        {
            // Try to evaluate an ast as a MalFunc.
            MalList evaledList = (MalList)eval_ast(ast, env);

            MalVal  listFirst = evaledList.First();
            MalList listRest  = evaledList.Rest();

            if (listFirst is MalFunc func)
            {
                // Ast *is* a function, so call it.
                return(func.Apply(listRest));
            }
            else
            {
                throw new MalEvalError("Can't use '" + listFirst.ToString(true) + "' as a function.");
            }
        }
Esempio n. 20
0
 // -------------------- Quoting and macros ---------------------------------------------
 // Helper function for quasiquotes. Return true if the arg is a non-empty list.
 static Boolean is_pair(MalVal ast)
 {
     if (ast is MalList malList)
     {
         if (malList.Count() > 0)
         {
             return(true);
         }
     }
     if (ast is MalVector malVec)
     {
         if (malVec.Count() > 0)
         {
             return(true);
         }
     }
     return(false);
 }
Esempio n. 21
0
        //
        // General functions
        //

        public static bool _equal_Q(MalVal a, MalVal b) {
            Type ota = a.GetType(), otb = b.GetType();
            if (!((ota == otb) ||
                (a is MalList && b is MalList))) {
                return false;
            } else {
                if (a is MalInt) {
                    return ((MalInt)a).getValue() ==
                        ((MalInt)b).getValue();
                } else if (a is MalSymbol) {
                    return ((MalSymbol)a).getName() ==
                        ((MalSymbol)b).getName();
                } else if (a is MalString) {
                    return ((MalString)a).getValue() ==
                        ((MalString)b).getValue();
                } else if (a is MalList) {
                    if (((MalList)a).size() != ((MalList)b).size()) {
                        return false;
                    }
                    for (int i=0; i<((MalList)a).size(); i++) {
                        if (! _equal_Q(((MalList)a)[i], ((MalList)b)[i])) {
                            return false;
                        }
                    }
                    return true;
                } else if (a is MalHashMap) {
                    var akeys = ((MalHashMap)a).getValue().Keys;
                    var bkeys = ((MalHashMap)b).getValue().Keys;
                    if (akeys.Count != bkeys.Count) {
                        return false;
                    }
                    foreach (var k in akeys) {
                        if (!_equal_Q(((MalHashMap)a).getValue()[k],
                                      ((MalHashMap)b).getValue()[k])) {
                            return false;
                        }
                    }
                    return true;
                } else {
                    return a == b;
                }
            }
        }
Esempio n. 22
0
        static MalVal EVAL(MalVal ast, Dictionary <string, MalFunc> replEnv)
        {
            switch ((Object)ast)
            {
            case MalList mList:
                // Ast is a list.
                // TODO - should this also do vectors and hashmaps?
                if (mList.Count() <= 0)
                {
                    // Empty list, return unchanged.
                    Console.WriteLine("EVAL - empty list: " + Printer.pr_str(mList, true));
                    return(ast);
                }
                else
                {
                    // ast is a non-empty list, so evaluate it.
                    Console.WriteLine("EVAL - non-empty list: " + Printer.pr_str(mList, true));

                    // Evaluate the List.
                    MalList evaledList = (MalList)eval_ast(ast, replEnv);

                    MalVal  listFirst = evaledList.First();
                    MalList listRest  = evaledList.Rest();

                    switch ((Object)listFirst)
                    {
                    case MalFunc func:
                        Console.WriteLine("EVAL - List head is: '" + Printer.pr_str(listFirst, true) + "'. Rest elements: ");
                        // Take the first item of the evaluated list and call it as function using the rest of the evaluated list as its arguments

                        return(func.Apply(listRest));

                    //return null;
                    default:
                        throw new MalEvalError("Can't use '" + listFirst.ToString(true) + "' as a function.");
                    }
                }

            default:
                // If ast is not a list (e.g. a vector), return the result of calling eval_ast on it.
                return(eval_ast(ast, replEnv));
            }
        }
Esempio n. 23
0
        // Helper functions for EVAL -----------------------------------------------------------

        // Helper function for quasiquotes. Return true if the arg is a non-empty list.
        static Boolean is_pair(MalVal ast)
        {
            // This name might make sense in the sense of a cons cell / dotted pair.
            if (ast is MalList malList)
            {
                if (malList.Count() > 0)
                {
                    return(true);
                }
            }
            if (ast is MalVector malVec)
            {
                if (malVec.Count() > 0)
                {
                    return(true);
                }
            }
            return(false);
        }
Esempio n. 24
0
        // Evaluate the ast in the supplied environment.
        // NOTE - I've taken off the '*' and '!' chars from the special forms.
        static MalVal EVAL(MalVal ast, Env env)
        {
            if (ast is MalList astList)
            {
                if (astList.Count() <= 0)
                {
                    // Empty list, return unchanged.
                    return(ast);
                }
                else
                {
                    // ast is a non-empty list. [0] should be a special form or a function name.
                    if (!(astList[0] is MalSym sym))
                    {
                        // Something like ([ 1 2]) perhaps.
                        throw new MalEvalError("Expected function name or special form but got '" + astList[0] + "'");
                    }

                    switch (sym.getName())
                    {
                    case "def": return(EvalDef(astList, env));

                    case "let": return(EvalLet(astList, env));

                    case "do":  return(EvalDo(astList, env));

                    case "if":  return(EvalIf(astList, env));

                    case "fn":  return(EvalFnDef(astList, env));

                    default:
                        // Ast isn't a special form, so it should be a function.
                        return(EvalMalFunc(ast, env));
                    }
                }
            }
            else
            {
                // If ast is not a list (e.g. a vector), return the result of calling eval_ast on it.
                return(eval_ast(ast, env));
            }
        }
Esempio n. 25
0
            // Non-destructively remove a target item from a supplied list.
            // This has CL-like semantics, do only removes the first target encountered.
            public MalSequence Remove(MalVal target)
            {
                // Create a list to which the non-target items will be copied.
                MalSequence RemoveSeq;
                bool        removedP = false;

                // Instantiate a sequence of the same type.
                if (this is MalList)
                {
                    RemoveSeq = new MalList();
                }
                else if (this is MalVector)
                {
                    RemoveSeq = new MalVector();
                }
                else
                {
                    throw new MalEvalError("remove expected list or vector but got '" + this.ToString() + "'");
                }
                for (var i = 0; i < MyElements.Count; i++)
                {
                    if (removedP)
                    {
                        // target already removed so always okay to keep this one.
                        RemoveSeq.Add(MyElements[i]);
                    }
                    else
                    {
                        if (EQ(MyElements[i], target) == malTrue)
                        {
                            // This is the target to remove, so don't copy it.
                            removedP = true;
                        }
                        else
                        {
                            RemoveSeq.Add(MyElements[i]);
                        }
                    }
                }
                return(RemoveSeq);
            }
Esempio n. 26
0
        // Name changed from the guide to highlight that the outcome is complicated.
        // TODO logic here is broken - see the above for the right if nesting.
        static MalVal ProcessQuasiquote(MalVal ast)
        {
            if (!is_pair(ast))
            {
                // Case 1.
                // If is_pair of ast is false: return a new list containing: a symbol named "quote" and ast.
                MalList qList = new MalList();

                qList.Add(new MalSym("quote"));
                qList.Add(ast);
                return(qList);
            }
            else
            {
                MalList astList = (MalList)ast;
                MalVal  a0      = astList[0];

                // Case 2:  if the first element of ast is a symbol named "unquote": return the second element of ast.
                if (a0 is MalSym a0Sym && a0Sym.getName() == "unquote")
                {
                    return(astList[1]);
                }
Esempio n. 27
0
        // Read a MalSequence, checking that it starts and terminates correctly.
        // Named read_list to follow the ref, but has been genericized to handle vectors as well.
        static public MalSeqBase read_list(Reader reader, MalSeqBase sequence, char start, char end)
        {
            // Check that we are in fact at the start of a list.
            string token = reader.Next();

            if (token[0] != start)
            {
                // Parse error - probably internal if the list code is correct.
                throw new MalInternalError("Sequence expected '" + start + "' but got: " + token);
            }

            // Use read_form to get the list's contents, accumulating them into the list.
            while (true)
            {
                token = reader.Peek();

                if (token != null)
                {
                    // We are in the list or at the end.
                    if (token[0] == end)
                    {
                        // Reached valid end of list. Consume the end char.
                        reader.Next();
                        // And we are done.
                        break;
                    }
                    // Mutually recurse to read the next list element.
                    MalVal newVal = read_form(reader);
                    sequence.Add(newVal);
                }
                else
                {
                    // The input has finished but the list hasn't. Try to get more input.
                    reader.LoadMoreTokens(start, end);
                }
            }

            return(sequence);
        }
Esempio n. 28
0
 public MalException(string value) :base(value) {
     this.value = new MalString(value);
 }
Esempio n. 29
0
 String PRINT(MalVal exp)
 {
     return(Printer.pr_str(exp, true));
 }
Esempio n. 30
0
            // Support the built-in '=' function. False until explicitly proven otherwise.
            // Should be used in preference to == for internal MalVal equality checks.
            public static MalVal EQ(MalVal a, MalVal b)
            {
                if (a.GetType() != b.GetType())
                {
                    // TODO - allow equality comparisons between ints and floats.
                    return(malFalse);
                }
                // If here, they are of the same Mal type. Do they have the same value?
                switch ((object)a)
                {
                case MalSym aSym:
                    if (aSym.getName() == ((MalSym)b).getName())
                    {
                        return(malTrue);
                    }
                    break;

                case MalNum aNumk:
                    if (aNumk.Unbox() == ((MalNum)b).Unbox())
                    {
                        return(malTrue);
                    }
                    break;

                case MalString aString:
                    if (aString.unbox() == ((MalString)b).unbox())
                    {
                        return(malTrue);
                    }
                    break;

                case MalKeyword aKeyWord:
                    if (aKeyWord.unbox() == ((MalKeyword)b).unbox())
                    {
                        return(malTrue);
                    }
                    break;

                case MalNil aNil:
                    if (aNil == ((MalNil)b))
                    {
                        return(malTrue);
                    }
                    break;

                case MalTrue aTrue:
                    if (aTrue == ((MalTrue)b))
                    {
                        return(malTrue);
                    }
                    break;

                case MalFalse aFalse:
                    if (aFalse == ((MalFalse)b))
                    {
                        return(malTrue);
                    }
                    break;

                case MalSeqBase aSeq:
                    // Sequences must be the same length, and each element must be the same.
                    MalSeqBase bSeq = (MalSeqBase)b;
                    if (aSeq.Count() != bSeq.Count())
                    {
                        // They are not of equal length.
                        return(malFalse);
                    }
                    for (var i = 0; i < aSeq.Count(); i++)
                    {
                        // At least one of the elements is not equal.
                        if (EQ(aSeq[i], bSeq[i]) == malFalse)
                        {
                            return(malFalse);
                        }
                    }
                    return(malTrue);

                case MalAtom aAtom:
                    // The atoms must be the same object. Two atoms containing the same value are not equal.
                    // TODO check whether this should be true if dereferencing them should be used instead. This isn't specified in the tests.
                    if (aAtom == ((MalAtom)b))
                    {
                        return(malTrue);
                    }
                    break;

                default:
                    throw new MalInternalError("Can't yet compare '" + a.GetType() + "' with '" + b.GetType() + "'");
                }
                return(malFalse);
            }
Esempio n. 31
0
 public MalAtom(MalVal value) { this.value = value; }
Esempio n. 32
0
 public MalVal setValue(MalVal value) { return this.value = value; }
Esempio n. 33
0
 public MalFunc(MalVal ast, Mal.env.Env env, MalList fparams,
                    Func<MalList, MalVal> fn) {
     this.fn = fn;
     this.ast = ast;
     this.env = env;
     this.fparams = fparams;
 }
Esempio n. 34
0
 public MalMalError(MalVal exceptionVal) : base(exceptionVal.ToString(true))
 {
     myException = exceptionVal;
 }
Esempio n. 35
0
 public MalVal setMeta(MalVal m) { meta = m; return this; }
Esempio n. 36
0
 public void Add(MalVal newVal)
 {
     MyElements.Add(newVal);
 }
Esempio n. 37
0
 public MalVal setValue(MalVal value)
 {
     return(this.value = value);
 }
Esempio n. 38
0
 //string Message;
 public MalException(MalVal value) {
     this.value = value;
 }