private IEnumerable <ExecutionMessage> SLet(List <SExpression> args, Environment e) { Environment local = new Environment(e); //TODO BUG: (let () ...) parses the name name bindings that are the pair <EmptyList,NULL> if (args[0] is SPair) { SPair nameBindings = (SPair)args[0]; if (!nameBindings.isProperList()) { throw new Exception("Can't use impro per list in a let"); } List <SExpression> names = nameBindings.flatten(); for (int i = 0; i < names.Count - 1; i++) { String name = ((SID)((SPair)names[i]).getHead()).identifier; SExpression val = ((SPair)((SPair)names[i]).getTail()).getHead(); //Cycle through the yields until done var elmEnum = evaluate(val, e).GetEnumerator(); SExpression elm = null; while (elmEnum.MoveNext()) { ExecutionMessage current = elmEnum.Current; if (current.status == ExecStatus.DONE) { elm = current.returnVal; break; } yield return(current); } local.addVal(name, elm); } } else if (args[0] is SEmptyList) { } else { throw new Exception("Name bindings section of let must be a list"); } //Lets can have an arbitrary number of statements after the name bindings, execute them for stateful effects ExecutionMessage msg = null; for (int i = 1; i < args.Count; i++) { foreach (var tmsg in evaluate(args[i], local)) { msg = tmsg; if (msg.status == ExecStatus.DONE) { continue; } yield return(msg); } } //yield final message yield return(msg); }
public IEnumerable <ExecutionMessage> evaluate(SExpression expr, Environment e) { if (expr is SID) { yield return(new ExecutionMessage(ExecStatus.DONE, e.lookup((SID)expr))); yield break; } else if (!(expr is SPair)) { yield return(new ExecutionMessage(ExecStatus.DONE, expr)); yield break; } SPair exprL = (SPair)expr; if (!exprL.isProperList()) { throw new Exception("Not a proper list!"); } //We need to get the value of the head, but that could be another step of execution that can be interrupted //So, yield the value if it's not done. It it is, continue on List <SExpression> elms = exprL.flatten(); var subcall = evaluate(elms[0], e).GetEnumerator(); SExpression head = null; while (subcall.MoveNext()) { ExecutionMessage current = subcall.Current; if (current.status == ExecStatus.DONE) { head = current.returnVal; break; } else { yield return(current); } } if (!(head is SApplicable)) { throw new Exception("SExpression not applicable!"); } //args are going to be body. But because this is a proper list, the last element is going to be a empty list we want to drop elms.RemoveAt(0); //drop head elms.RemoveAt(elms.Count - 1); // remove empty list at end SApplicable appHead = (SApplicable)head; //Convert arguments to primatives if (appHead.fixedArgCount) { if (elms.Count != appHead.argCount) // make sure expected num arguments matches num arguments { throw new Exception(String.Format("Expected {0} arguments, recieved {1}", appHead.argCount, elms.Count)); } } //Convert arguments to usable values, the goofy escape is so that specific primatives (define, lambda, let, if) are skipped //preEval is always true for user created functions if (appHead.preEval) { for (int i = 0; i < elms.Count; i++) { var elmEnum = evaluate(elms[i], e).GetEnumerator(); SExpression elm = null; while (elmEnum.MoveNext()) { ExecutionMessage current = elmEnum.Current; if (current.status == ExecStatus.DONE) { elm = current.returnVal; break; } yield return(current); } elms[i] = elm; } } //Actually CALL the dang thing if (appHead is SPrimitive) { SPrimitive prim = (SPrimitive)appHead; foreach (var msg in prim.func(elms, e)) { yield return(msg); } yield break; } //Therefore is SFunc SFunc lambda = (SFunc)appHead; //bind names to variables to create new subenvironment if (lambda.fixedArgCount) { //if there is fixed number of args, pair each evaluated argument with its name for (int i = 0; i < elms.Count; i++) { lambda.env.setLocalVal(lambda.names[i].identifier, elms[i]); } } else { //if there are not, match the generic identifier with the list of args (need to convert to SPair w/tail first) lambda.env.setLocalVal(lambda.arglist.identifier, new SPair(elms, true)); } callStack.Add(expr);//append current function to call stack yield return(new ExecutionMessage(ExecStatus.PENDING_STEP, expr)); foreach (var msg in evaluate(lambda.body, lambda.env)) { yield return(msg); } callStack.RemoveAt(callStack.Count - 1); }