static int add_binding_i(void *data, void *info) { binding_t *b = (binding_t *)data; env_t *env = (env_t *)info; value_t *v; if (b->type->type == t_fn) { // A function, allocate a closure. v = alloc_value(v_closure); closure_params(v) = b->params; closure_body(v) = b->body; closure_env(v) = env; } else { // Body is constant, thunk it. v = thunk_create(env, b->body); } env_add_binding(env, b->name, v); return 1; }
// This is where most of the strict/lazy distinction is. static value_t *e_fncall(env_t *env, expr_t *fn, list_t *args) { value_t *fnv; eli_closure_t c; // Call-by-need (lazy function calls): suspend (thunk-ify) each // argument in the given environment. c.env = env; c.list = list_empty(); list_iterate(args, thunk_list_i, &c); list_reverse(c.list); // Due to C's 'break' being imperfect, use 'goto' for clarity. loop: // Evaluate the function to a closure/data constructor in the given // environment. fnv = e_expr(env, fn); switch (fnv->type) { case v_datacons: // Construct a new data constructor value; we need to do this in // case the value we got from evaluating the "function" is shared. { value_t *dcv = alloc_value(v_datacons); datacons_tag(dcv) = datacons_tag(fnv); datacons_params(dcv) = list_append_new(datacons_params(fnv), c.list); fnv = dcv; } break; case v_closure: { int paramsArgs; // Bind the closure's parameters to the given arguments in a new // environment. At this point the original environment has // served its purpose. env = closure_env(fnv); env_new_scope(&env); paramsArgs = list_zip_with(closure_params(fnv), c.list, e_bind_params_i, env); // See how the number of parameters and arguments relate. switch (paramsArgs) { case -1: // Didn't get enough arguments, so wait for some more by // building a new closure. { value_t *fn_unsaturated = alloc_value(v_closure); closure_params(fn_unsaturated) = list_drop_new(list_length(c.list), closure_params(fnv)); closure_body(fn_unsaturated) = closure_body(fnv); closure_env(fn_unsaturated) = env; fnv = fn_unsaturated; } break; case 0: // Got exactly the right number of arguments. Evaluate the // body in the extended environment. fnv = e_expr(env, closure_body(fnv)); break; case 1: // Got too many arguments for this closure. Assuming // type-correctness, that implies the body of this closure // reduces to a function, so let's try again. Note the // environment has already been updated. fn = closure_body(fnv); c.list = list_drop_new(list_length(closure_params(fnv)), c.list); goto loop; } break; case v_builtin_fn: { int nArgs; int nParams; // See how the number of parameters and arguments relate. nArgs = list_length(c.list); nParams = builtin_num_params(fnv); if (nArgs < nParams) { // Didn't get enough arguments, so wait for some more by // building a new closure-like thing. value_t *fn_unsaturated = alloc_value(v_builtin_fn); builtin_num_params(fn_unsaturated) = nParams - nArgs; builtin_args(fn_unsaturated) = builtin_args(fnv); list_append(&builtin_args(fn_unsaturated), &c.list); builtin_fn(fn_unsaturated) = builtin_fn(fnv); return fn_unsaturated; } else if (nArgs > nParams) { // Got too many arguments. Assuming type-correctness, that // implies the built-in function returns a function closure, // so let's try again. // FIXME error("builtin function application is over-saturated.\n"); return NULL; // value_t *result = builtin_fn(fnv)(list_take_new(builtin_num_params(fnv), c.list)); // fncall_fn(expr) = closure_body(fnv); // c.list = list_drop_new(nParams, c.list); // env = fn_env; // break; /\* Loop *\/ } else { // Got exactly the right number of arguments. return builtin_fn(fnv)(c.list); } } break; default: fprintf(stdout, "e_fncall: expression:\n"); pp_expr(stdout, fn, 2); fprintf(stdout, "\non line %d evaluated to non-function/data constructor value:\n", fn->line_num); print_value(stdout, fnv); error("\n"); break; } } return fnv; }
Co<Expression> ExpressionCallWithNamedParameters::evaluate(Environment &env) const { env.checkTimeout(getSourceLocation()); Co<ExpressionClosure> closure = env[name]; /* std::cout << "getSource(): " << getSource() << std::endl; */ /* std::cout << "getSourceLocation(): " << getSourceLocation().toAnchor() << std::endl; */ /* std::cout << "closure->getSourceLocation(): " << closure->getSourceLocation().toAnchor() << std::endl; */ if (closure.IsNull()) { throw MessagedException(getSourceLocation().toAnchor() + ": No function named " + name + " is defined."); } /* closure->setSource(getSource(), getSourceLocation()); */ Co<ExpressionDefine> define = closure->getDefine(); Co<Environment> closure_env(new Environment(*closure->getEnvironment())); // Before trying to execute this business, let's make certain the user didn't // pass any named parameters that don't apply. Having extras isn't going to // stop us from executing the function, but it probably means the caller has // some different intent. set<string> formal_names = define->getFormalNames(); for (bindings_t::const_iterator i = bindings.begin(); i != bindings.end(); ++i) { if (formal_names.find(i->first) == formal_names.end()) { throw MessagedException(getSourceLocation().toAnchor() + ": I saw that you passed a parameter named " + i->first + " to function " + define->getName() + ", but " + define->getName() + " doesn't accept a parameter named " + i->first + "."); } } // Having cleared the appropriateness of the bindings, let's start evaluating. for (unsigned int i = 0; i < define->getArity(); ++i) { const FormalParameter &formal = define->getFormal(i); bool is_lazy = formal.getEvaluationMode() == FormalParameter::LAZY; // Look up parameter name in enumerated bindings first. If we find an // explicit parameter with that name, use its value. bindings_t::const_iterator match = bindings.find(formal.getName()); Co<ExpressionDefine> parameter_define; Co<ExpressionDefine> parameter_closure; if (match != bindings.end()) { parameter_define = Co<ExpressionDefine>(new ExpressionDefine(formal.getName(), is_lazy ? match->second : match->second->evaluate(env))); parameter_closure = Co<ExpressionClosure>(new ExpressionClosure(parameter_define, is_lazy ? env : Environment())); parameter_closure->setSource(match->second->getSource(), match->second->getSourceLocation()); } else if (!formal.getDefaultValue().IsNull()) { parameter_define = Co<ExpressionDefine>(new ExpressionDefine(formal.getName(), formal.getDefaultValue()->evaluate(env))); parameter_closure = Co<ExpressionClosure>(new ExpressionClosure(parameter_define, Environment())); parameter_closure->setSource(formal.getDefaultValue()->getSource(), formal.getDefaultValue()->getSourceLocation()); // UPDATE: disallow implicits. They work, but their benefit is small and // risk of confusion is too high. // If no such parameter was enumerated, checking for a binding with that // name in the surrounding environment at the time of the call. /* else if (env.isBound(formal.getName())) { */ /* Co<Expression> closure = env[formal.getName()]; */ /* parameter_define = Co<ExpressionDefine>(new ExpressionDefine(formal.getName(), is_lazy ? closure : closure->evaluate(env))); */ /* parameter_closure = Co<ExpressionClosure>(new ExpressionClosure(parameter_define, is_lazy ? env : Environment())); */ /* parameter_closure->setSource(closure->getSource(), closure->getSourceLocation()); */ } else { throw MessagedException(getSourceLocation().toAnchor() + ": Function " + define->getName() + " expects a parameter named " + formal.getName() + ". No such parameter was provided and no variable with that name was defined."); } closure_env->replace(define->getFormal(i).getName(), parameter_closure); } try { return closure->evaluate(*closure_env); } catch (Co<Expression> return_value) { return return_value; } catch (UnlocatedException e) { throw MessagedException(getSourceLocation().toAnchor() + ": " + e.GetMessage()); } }