// eq: indicates whether its (two) arguments are equal. private PLObject eq_fun(PLObject a, PLObject b) { if ((a is PLAtom) && (b is PLAtom)) { // Two atoms compare by value return(BoolToExpr(a.Equals(b))); } else { // Two empty lists are equal if ((a is PLList) && (((PLList)a).Count == 0) && (b is PLList) && (((PLList)b).Count == 0)) { return(BoolToExpr(true)); } } return(BoolToExpr(false)); }
// Convert a ProtoLisp expression to a native C# bool value private bool ExprToBool(PLObject expr) { // Anything that is not "t" is false return((expr is PLStringAtom) && (expr.Equals("t"))); }
public PLObject Eval(PLObject expr, PLEnvironment localEnv, Stream traceStream) { if (traceStream != null) { StreamWriteLine(traceStream, "** Evaluating: " + ExpressionToString(expr)); } if (expr is PLAtom) { PLAtom atomExpr = (PLAtom)expr; if (atomExpr is PLNumberAtom) { // Numbers eval to themselves return(atomExpr); } else { if (atomExpr.Equals("t")) { // "t" (True) evals to itself return(atomExpr); } else if (atomExpr.Equals("nil")) { // Return whatever our native representation for false is return(BoolToExpr(false)); } else { PLObject retval = Lookup(atomExpr.ToString(), localEnv); if (retval == null) { throw new Exception("The symbolic name \"" + atomExpr + "\" is not bound."); } return(retval); } } } else if (expr is PLList) { PLList listExpr = ((PLList)expr); // The empty list evaluates to itself if (listExpr.Count == 0) { return(listExpr); } PLObject carVal = car_fun(listExpr); // Check for special-case primitives if (carVal is PLStringAtom) { if (carVal.Equals("cond")) { if (listExpr.Count < 2) { throw new Exception("Must pass at least one argument to the 'cond' primitive"); } return(cond_fun(cdr_fun(listExpr), localEnv, traceStream)); } else if (carVal.Equals("quote")) { if (listExpr.Count != 2) { throw new Exception("Incorrect number of arguments passed to the 'quote' primitive"); } // Return the second portion of the list return(listExpr[1]); } else if (carVal.Equals("define")) { if (listExpr.Count != 3) { throw new Exception("Incorrect number of arguments passed to the 'define' primitive"); } if (!(listExpr[1] is PLStringAtom)) { throw new Exception("The first argument passed to the 'define' primitive was not a string"); } return(define_fun(((PLStringAtom)listExpr[1]).ToString(), Eval(listExpr[2], localEnv, traceStream))); } else if (carVal.Equals("lambda")) { if (listExpr.Count != 3) { throw new Exception("Incorrect number of arguments passed to the 'lambda' primitive"); } if (!(listExpr[1] is PLList)) { throw new Exception("The first argument to the 'lambda' primitive was not a list"); } return(lambda_fun((PLList)listExpr[1], listExpr[2], localEnv)); } else if (carVal.Equals("defun")) { // Syntactic sugar for (define <name> (lambda ... if (listExpr.Count != 4) { throw new Exception("Incorrect number of arguments passed to the 'defun' primitive"); } if (!(listExpr[1] is PLStringAtom)) { throw new Exception("The first argument passed to the 'defun' primitive was not a simple name"); } if (!(listExpr[2] is PLList)) { throw new Exception("The second argument passed to the 'defun' argument was not a list"); } return(define_fun(((PLStringAtom)listExpr[1]).ToString(), lambda_fun((PLList)listExpr[2], listExpr[3], localEnv))); } } // Assume a general function invocation. Evaluate all the // arguments and invoke. PLList args = EvalPLList(cdr_fun(listExpr), localEnv, traceStream); return(Apply(listExpr[0], args, localEnv, traceStream)); } else { throw new Exception("Unrecognized type passed to Eval()"); } }
// Apply the specified argument list to the function indicated // by the provided expression. The function expression may simply // be the symbolic name of a function, or it may be an evaluatable // expression in its own right. private PLObject Apply(PLObject func, PLList args, PLEnvironment localEnv, Stream traceStream) { if (traceStream != null) { StreamWriteLine(traceStream, "** Evaluating the expression \"" + ExpressionToString(func) + "\" as a function"); } if (func is PLStringAtom) { // Function is named by a symbol if (func.Equals("atom")) { if (args.Count != 1) { throw new Exception("Incorrect number of arguments passed to the 'atom' primitive"); } return(atom_fun(args[0])); } else if (func.Equals("eq")) { if (args.Count != 2) { throw new Exception("Incorrect number of arguments passed to the 'eq' primitive"); } return(eq_fun(args[0], args[1])); } else if (func.Equals("car")) { if (args.Count != 1) { throw new Exception("Incorrect number of arguments passed to the 'car' primitive"); } if (!(args[0] is PLList)) { throw new Exception("The argument passed to the 'car' primitive was not a list"); } return(car_fun((PLList)args[0])); } else if (func.Equals("cdr")) { if (args.Count != 1) { throw new Exception("Incorrect number of arguments passed to the 'cdr' primitive"); } if (!(args[0] is PLList)) { throw new Exception("The argument passed to the 'cdr' primitive was not a list"); } return(cdr_fun((PLList)args[0])); } else if (func.Equals("cons")) { if (args.Count != 2) { throw new Exception("Incorrect number of arguments passed to the 'cons' primitive"); } if (!(args[1] is PLList)) { throw new Exception("The second argument passed to the 'cons' primitive was not a list"); } return(cons_fun(args[0], (PLList)args[1])); } else if (func.Equals("+")) { if (args.Count != 2) { throw new Exception("Incorrect number of arguments pass to the '+' primitive"); } if (!(args[0] is PLNumberAtom) || !(args[1] is PLNumberAtom)) { throw new Exception("Both arguments to the '+' primitive must be numbers"); } return(plus_fun((PLNumberAtom)args[0], (PLNumberAtom)args[1])); } else if (func.Equals("-")) { if (args.Count != 2) { throw new Exception("Incorrect number of arguments pass to the '-' primitive"); } if (!(args[0] is PLNumberAtom) || !(args[1] is PLNumberAtom)) { throw new Exception("Both arguments to the '-' primitive must be numbers"); } return(minus_fun((PLNumberAtom)args[0], (PLNumberAtom)args[1])); } else if (func.Equals("*")) { if (args.Count != 2) { throw new Exception("Incorrect number of arguments pass to the '*' primitive"); } if (!(args[0] is PLNumberAtom) || !(args[1] is PLNumberAtom)) { throw new Exception("Both arguments to the '*' primitive must be numbers"); } return(mult_fun((PLNumberAtom)args[0], (PLNumberAtom)args[1])); } else if (func.Equals("/")) { if (args.Count != 2) { throw new Exception("Incorrect number of arguments pass to the '/' primitive"); } if (!(args[0] is PLNumberAtom) || !(args[1] is PLNumberAtom)) { throw new Exception("Both arguments to the '/' primitive must be numbers"); } return(div_fun((PLNumberAtom)args[0], (PLNumberAtom)args[1])); } // The name does not indicate a built-in primitive. // Look up the function in our environment PLObject funObj = Lookup(func.ToString(), localEnv); if (!(funObj is PLClosure)) { throw new Exception("The symbolic name \"" + func + "\" is not bound to a function"); } // Run the function! return(Execute((PLClosure)funObj, args, traceStream)); } else { // The function is an expression, not just a name. This expression // had better evaluate to a closure. PLObject funObj = Eval(func, localEnv, traceStream); if (!(funObj is PLClosure)) { throw new Exception("Expression \"" + ExpressionToString(func) + "\" does not evaluate to a function"); } // Run this function expression return(Execute((PLClosure)funObj, args, traceStream)); } }
public PLObject GetExpression() { string token = GetToken(); if (token == null) { return(null); } if (token.Equals("'")) { // The "'" character means "quote", and preserve // the next expression. PLList retval = new PLList(); retval.Add(new PLStringAtom("quote")); PLObject nextExpr = GetExpression(); if (nextExpr == null) { throw new Exception("'quote' was not followed by a complete expression"); } retval.Add(nextExpr); return(retval); } if (!token.Equals("(")) { // Not the beginning of a list; the expression is // simply this token. See if it's a number or a // plain string. try { return(new PLNumberAtom(token)); } catch (Exception) { // Just treat it as a regular string return(new PLStringAtom(token)); } } else { // We have the beginning of a list, which can contain // any number of expressions. Keep building our list // until we encounter the closing paren. PLList retval = new PLList(); PLObject nextExpr = GetExpression(); while (!nextExpr.Equals(")")) { retval.Add(nextExpr); nextExpr = GetExpression(); if (nextExpr == null) { throw new Exception("Incomplete expression (unbalanced parens?"); } } return(retval); } }