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); } }
// 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); } }
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); } }
public void ReadForm_ReadsAVector() { var input = "[+ 1 1]"; var reader = Reader.ReadStr(input); var expected = new MalVector(); expected.Add(new MalAtom("+")); expected.Add(new MalAtom("1")); expected.Add(new MalAtom("1")); var result = Reader.ReadForm(reader); result.Should().BeOfType <MalVector>(); result.Should().BeEquivalentTo(expected); }
// 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); }