// 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]); }
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; }
// Given a sequence of symbols (binds) and a list of expressions, work through the // sequences, setting each bind in turn to the value of its corresponding expression. // The primary application is setting function arguments (binds) to their values (expressions). // If one of the binds is the Var args character ('&') the next bind is associated with // all of the remaining exprs. Trapped errors include non-symbols being used as binds // or cases where the binds and expr sequences are of incompatible lengths. public Env(Env outer, MalSequence binds, MalSequence exprs) { // A key-MalVal dictionary. data = new Dictionary <string, MalVal>(); // The scope we are created in. Null is taken to mean the outermost REPL's env. this.outer = outer; int bindsProcessed = 0; // (def f (fn (a & b) (list a b))) -- okay // Bind (set) each element (symbol) to the respective elements of the exprs list. for (bindsProcessed = 0; bindsProcessed < binds.Count(); bindsProcessed++) { if (binds[bindsProcessed] == malVarArgsChar) { // E.g. (fn f (a1 a2 & restArgs) (list a1 a2 restargs )) called with (f 1 2 3 4) if (bindsProcessed == binds.Count() - 1) { // malVarArgsChar isn't followed by the name of the varargs parameter. throw new MalEvalError("Expected symbol after & in arg list"); // If there are multiple symbols after '&' all but the first will be trapped as // unbound symbols later on. } if (binds[bindsProcessed + 1] is MalSym symbol) { // Bind the rest of the expressions to the symbol following the varargs char. Set(symbol, exprs.GetRange(bindsProcessed, exprs.Count())); return; } else { // Not sure if this can heppen but just in case. throw new MalEvalError("Expected symbol after & but got '" + binds[bindsProcessed + 1].ToString(true)); } // } else if (binds[bindsProcessed] is MalSym symbol) { // Bind an expression to a binding symbol. if (bindsProcessed >= exprs.Count()) { throw new MalEvalError("Incorrect number of arguments " + exprs.Count()); } Set(symbol, exprs[bindsProcessed]); } else { throw new MalEvalError("expected symbol to bind but got '" + binds[bindsProcessed].ToString(true) + '"'); } } if (bindsProcessed < exprs.Count()) { // We ran out of bindings although there are expressions remaining. throw new MalEvalError(exprs.ToString(true) + " - more expressions (" + exprs.Count() + ") than bindings: " + bindsProcessed); } }