410 lines
14 KiB
C++
410 lines
14 KiB
C++
/* @file Lambda.cpp */
|
|
|
|
#include "Lambda.hpp"
|
|
#include "exprtype.hpp"
|
|
#include "pretty_expression.hpp"
|
|
#include "pretty_variable.hpp"
|
|
#include "xo/reflect/TypeDescr.hpp"
|
|
#include "xo/reflect/function/FunctionTdx.hpp"
|
|
#include "xo/indentlog/print/vector.hpp"
|
|
#include "xo/indentlog/print/pretty_vector.hpp"
|
|
#include <map>
|
|
#include <sstream>
|
|
|
|
namespace xo {
|
|
using xo::reflect::TypeDescr;
|
|
using xo::reflect::TypeDescrBase;
|
|
using xo::reflect::FunctionTdxInfo;
|
|
using std::stringstream;
|
|
|
|
namespace scm {
|
|
TypeDescr
|
|
Lambda::assemble_lambda_td(const std::vector<rp<Variable>> & argv,
|
|
TypeDescr return_td)
|
|
{
|
|
assert(return_td != nullptr);
|
|
|
|
std::vector<TypeDescr> arg_td_v;
|
|
{
|
|
arg_td_v.reserve(argv.size());
|
|
|
|
for (const auto & arg : argv) {
|
|
arg_td_v.push_back(arg->valuetype());
|
|
}
|
|
}
|
|
|
|
auto function_info
|
|
= FunctionTdxInfo(return_td,
|
|
arg_td_v,
|
|
false /*!is_noexcept*/);
|
|
|
|
TypeDescr lambda_td
|
|
= TypeDescrBase::require_by_fn_info(function_info);
|
|
|
|
return lambda_td;
|
|
}
|
|
|
|
TypeDescr
|
|
Lambda::assemble_lambda_td(const std::vector<rp<Variable>> & argv,
|
|
TypeDescr explicit_return_td,
|
|
const rp<Expression> & body)
|
|
{
|
|
if (!body)
|
|
return nullptr;
|
|
|
|
if (explicit_return_td && body->valuetype() && (explicit_return_td != body->valuetype())) {
|
|
throw std::runtime_error(tostr("explicit lambda return type T1 conflicts with lambda body T2",
|
|
xtag("T1", explicit_return_td),
|
|
xtag("T2", body->valuetype())));
|
|
}
|
|
|
|
// TODO: unify(explicit_return_td, body->valuetype())
|
|
|
|
TypeDescr return_td = explicit_return_td ? explicit_return_td : body->valuetype();
|
|
|
|
return assemble_lambda_td(argv, return_td);
|
|
}
|
|
|
|
std::string
|
|
Lambda::assemble_type_str(TypeDescr lambda_td) {
|
|
assert(lambda_td);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << lambda_td->fn_retval()->short_name()
|
|
<< "(";
|
|
|
|
for (std::size_t i = 0, n = lambda_td->n_fn_arg(); i < n; ++i) {
|
|
if (i > 0)
|
|
ss << ",";
|
|
ss << lambda_td->fn_arg(i)->short_name();
|
|
}
|
|
ss << ")";
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
rp<Lambda>
|
|
Lambda::make(const std::string & name,
|
|
TypeDescr lambda_td,
|
|
const rp<LocalEnv> & env,
|
|
const rp<Expression> & body)
|
|
{
|
|
return new Lambda(name, lambda_td, env, body);
|
|
}
|
|
|
|
rp<Lambda>
|
|
Lambda::make_from_env(const std::string & name,
|
|
const rp<LocalEnv> & env,
|
|
TypeDescr explicit_return_td,
|
|
const rp<Expression> & body)
|
|
{
|
|
TypeDescr lambda_td = assemble_lambda_td(env->argv(), explicit_return_td, body);
|
|
|
|
rp<Lambda> retval
|
|
= new Lambda(name,
|
|
lambda_td,
|
|
env,
|
|
body);
|
|
|
|
/* need two-phase construction b/c pointer cycle */
|
|
env->assign_origin(retval.get());
|
|
|
|
return retval;
|
|
}
|
|
|
|
rp<Lambda>
|
|
Lambda::make(const std::string & name,
|
|
const std::vector<rp<Variable>> & argv,
|
|
const rp<Expression> & body,
|
|
const rp<Environment> & parent_env)
|
|
{
|
|
rp<LocalEnv> env = LocalEnv::make(argv, parent_env);
|
|
|
|
TypeDescr explicit_return_td = nullptr;
|
|
|
|
return make_from_env(name, env, explicit_return_td, body);
|
|
} /*make*/
|
|
|
|
std::set<std::string>
|
|
Lambda::calc_free_variables() const
|
|
{
|
|
std::set<std::string> retval
|
|
= body_->get_free_variables();
|
|
|
|
/* but remove formals. */
|
|
for (const auto & var : local_env_->argv())
|
|
retval.erase(var->name());
|
|
|
|
return retval;
|
|
} /*calc_free_variables*/
|
|
|
|
std::map<std::string, rp<Variable>>
|
|
Lambda::regularize_layer_vars()
|
|
{
|
|
/* regularize local_env+body: make sure exactly one instance
|
|
* (i.e. with object identity) of a Variable appears
|
|
* within one layer of a lambda body.
|
|
*
|
|
* Here 'layer' means excluding appearance in any nested lambdas
|
|
* (i.e. whether or not such appearance would resolve to the same
|
|
* memory location).
|
|
*
|
|
* Motivation is to unify Variables that would use the same
|
|
* binding_path to resolve their runtime location.
|
|
*/
|
|
std::map<std::string, rp<Variable>> var_map;
|
|
|
|
for (const auto & arg : local_env_->argv()) {
|
|
/* each arg name can appear at most once
|
|
* in a particular lambda's parameter list
|
|
*/
|
|
assert(var_map.find(arg->name()) == var_map.end());
|
|
|
|
var_map[arg->name()] = arg;
|
|
}
|
|
|
|
this->body_
|
|
= (body_->xform_layer
|
|
([&var_map](bp<Expression> x) -> rp<Expression>
|
|
{
|
|
if (x->extype() == exprtype::variable) {
|
|
bp<Variable> var = Variable::from(x);
|
|
|
|
auto ix = var_map.find(var->name());
|
|
if (ix == var_map.end()) {
|
|
/* add to var_map, copy to ensure Variable
|
|
* not shared with any other layer
|
|
*/
|
|
|
|
var_map[var->name()] = Variable::copy(var);
|
|
|
|
return var.get();
|
|
} else {
|
|
/* substitute already-encountered var_map[] member */
|
|
return ix->second.get();
|
|
}
|
|
} else {
|
|
return x.get();
|
|
}
|
|
}));
|
|
|
|
return var_map;
|
|
} /*regularize_layer_vars*/
|
|
|
|
void
|
|
Lambda::complete_assembly_from_body() {
|
|
if (body_) {
|
|
TypeDescr explicit_return_td = nullptr;
|
|
TypeDescr lambda_td
|
|
= assemble_lambda_td(this->local_env_->argv(), explicit_return_td, body_);
|
|
|
|
if (lambda_td)
|
|
this->type_str_ = assemble_type_str(lambda_td);
|
|
|
|
this->layer_var_map_ = this->regularize_layer_vars();
|
|
|
|
this->free_var_set_ = this->calc_free_variables();
|
|
|
|
std::map<std::string, bp<Lambda>> nested_lambda_map;
|
|
{
|
|
this->body_->visit_layer
|
|
([&nested_lambda_map]
|
|
(bp<Expression> expr)
|
|
{
|
|
if (expr->extype() == exprtype::lambda) {
|
|
bp<Lambda> lm = Lambda::from(expr);
|
|
|
|
nested_lambda_map[lm->name()] = lm.get();
|
|
}
|
|
});
|
|
}
|
|
this->nested_lambda_map_ = std::move(nested_lambda_map);
|
|
|
|
/* establish the set of captured local vars.
|
|
* These are any formal parameters that appear free in
|
|
* any layer of a nested lambda.
|
|
*/
|
|
std::set<std::string> captured_var_set;
|
|
{
|
|
for (const auto & ix : nested_lambda_map_) {
|
|
std::set<std::string> nested_free_var_set
|
|
= ix.second->get_free_variables();
|
|
|
|
for (const auto & jx : nested_free_var_set) {
|
|
/* check whether variable *jx is one of this lambda's
|
|
* formals
|
|
*/
|
|
auto bind = this->local_env_->lookup_local_binding(jx);
|
|
|
|
if (bind.i_link_ == 0) {
|
|
/* yup, it's a formal parameter of this lambda */
|
|
captured_var_set.insert(jx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this->captured_var_set_ = std::move(captured_var_set);
|
|
|
|
/* in particular:
|
|
* - establish binding path (intrusively) for each variable
|
|
* assigns Variable::path_
|
|
*/
|
|
this->body_->attach_envs(local_env_);
|
|
}
|
|
}
|
|
|
|
Lambda::Lambda(const std::string & name,
|
|
TypeDescr lambda_td,
|
|
const rp<LocalEnv> & local_env,
|
|
const rp<Expression> & body)
|
|
: FunctionInterface(exprtype::lambda, lambda_td),
|
|
name_{name},
|
|
body_{body},
|
|
local_env_{local_env}
|
|
{
|
|
#ifdef OBSOLETE
|
|
stringstream ss;
|
|
ss << "double";
|
|
ss << "(";
|
|
for (std::size_t i = 0, n = this->n_arg(); i < n; ++i) {
|
|
if (i > 0)
|
|
ss << ",";
|
|
ss << "double";
|
|
}
|
|
ss << ")";
|
|
#endif
|
|
|
|
if (lambda_td)
|
|
this->type_str_ = assemble_type_str(lambda_td);
|
|
|
|
/* ensure variables are unique within layer for this lambda */
|
|
this->layer_var_map_ = this->regularize_layer_vars();
|
|
|
|
this->free_var_set_ = this->calc_free_variables();
|
|
|
|
std::map<std::string, bp<Lambda>> nested_lambda_map;
|
|
{
|
|
this->body_->visit_layer
|
|
([&nested_lambda_map]
|
|
(bp<Expression> expr)
|
|
{
|
|
if (expr->extype() == exprtype::lambda) {
|
|
bp<Lambda> lm = Lambda::from(expr);
|
|
|
|
nested_lambda_map[lm->name()] = lm.get();
|
|
}
|
|
});
|
|
}
|
|
this->nested_lambda_map_ = std::move(nested_lambda_map);
|
|
|
|
/* establish the set of captured local vars.
|
|
* These are any formal parameters that appear free in
|
|
* any layer of a nested lambda.
|
|
*/
|
|
std::set<std::string> captured_var_set;
|
|
{
|
|
for (const auto & ix : nested_lambda_map_) {
|
|
std::set<std::string> nested_free_var_set
|
|
= ix.second->get_free_variables();
|
|
|
|
for (const auto & jx : nested_free_var_set) {
|
|
/* check whether variable *jx is one of this lambda's
|
|
* formals
|
|
*/
|
|
auto bind = this->local_env_->lookup_local_binding(jx);
|
|
|
|
if (bind.i_link_ == 0) {
|
|
/* yup, it's a formal parameter of this lambda */
|
|
captured_var_set.insert(jx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this->captured_var_set_ = std::move(captured_var_set);
|
|
|
|
/* in particular:
|
|
* - establish binding path (intrusively) for each variable
|
|
* assigns Variable::path_
|
|
*/
|
|
this->body_->attach_envs(local_env_);
|
|
|
|
} /*ctor*/
|
|
|
|
void
|
|
Lambda::attach_envs(bp<Environment> p) {
|
|
local_env_->assign_parent(p);
|
|
|
|
/** establish a binding path for each variable **/
|
|
}
|
|
|
|
void
|
|
Lambda::display(std::ostream & os) const {
|
|
os << "<Lambda"
|
|
<< xtag("name", name_)
|
|
<< xtag("argv", local_env_->argv())
|
|
<< xtag("body", body_)
|
|
<< ">";
|
|
} /*display*/
|
|
|
|
std::uint32_t
|
|
Lambda::pretty_print(const ppindentinfo & ppii) const
|
|
{
|
|
return ppii.pps()->pretty_struct(ppii, "Lambda",
|
|
refrtag("name", name_),
|
|
refrtag("argv", local_env_->argv()),
|
|
refrtag("body", body_));
|
|
}
|
|
|
|
// ----- Lambda Access -----
|
|
|
|
rp<LambdaAccess>
|
|
LambdaAccess::make(const std::string & name,
|
|
const std::vector<rp<Variable>> & argv,
|
|
const rp<Expression> & body,
|
|
const rp<Environment> & parent_env)
|
|
{
|
|
TypeDescr explicit_return_td = nullptr;
|
|
TypeDescr lambda_td = assemble_lambda_td(argv, explicit_return_td, body);
|
|
rp<LocalEnv> env = LocalEnv::make(argv, parent_env);
|
|
|
|
rp<LambdaAccess> retval
|
|
= new LambdaAccess(name,
|
|
lambda_td,
|
|
env,
|
|
body);
|
|
|
|
/* need two-phase construction b/c pointer cycle */
|
|
env->assign_origin(retval.get());
|
|
|
|
return retval;
|
|
}
|
|
|
|
rp<LambdaAccess>
|
|
LambdaAccess::make_empty()
|
|
{
|
|
return new LambdaAccess("" /*name*/,
|
|
nullptr /*lambda_td*/,
|
|
nullptr /*local_env*/,
|
|
nullptr /*body*/);
|
|
}
|
|
|
|
LambdaAccess::LambdaAccess(const std::string & name,
|
|
TypeDescr lambda_td,
|
|
const rp<LocalEnv> & local_env,
|
|
const rp<Expression> & body)
|
|
: Lambda(name, lambda_td, local_env, body)
|
|
{}
|
|
|
|
void
|
|
LambdaAccess::assign_body(const rp<Expression> & body) {
|
|
this->body_ = body;
|
|
|
|
this->complete_assembly_from_body();
|
|
}
|
|
} /*namespace scm*/
|
|
} /*namespace xo*/
|
|
|
|
/* end Lambda.cpp */
|