示例#1
0
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;
}
示例#2
0
// 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());
  }
}