// Copyright 1998,1999 Zanshin Inc.
// The contents of this file are subject to the Zanshin Public License Version
// 1.0 (the "License"); you may not use this file except in compliance with the
// License. You should have received a copy of the License with Latte; see
// the file COPYING. You may also obtain a copy of the License at
// .
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
// for the specific language governing rights and limitations under the
// License.
//
// The Original Code is Latte.
//
// The Initial Developer of the Original Code is Zanshin, Inc.
#include
Latte_Closure::Latte_Closure(const Latte_List::const_iterator ¶ms,
const Latte_List::const_iterator ¶ms_end,
const Latte_List::const_iterator &body,
const Latte_List::const_iterator &body_end,
Latte_Activation &activation) :
m_env(new Latte_BindingEnv(activation.env())),
m_activation(&activation),
m_body(new Latte_List(body, body_end)),
m_rest_offset(-1), m_positional_params(0),
m_macro(0)
{
Refcounter rest_param(0);
// Build the binding environment for calls to this closure.
// Start with ordinary positional parameters.
for (Latte_List::const_iterator i = params; i != params_end; ++i) {
const Refcounter &obj = *i;
Latte_VarRef *varref = obj->as_varref();
Latte_Param *param = obj->as_param();
if (param && param->is_rest())
rest_param = Refcounter(param);
else if (varref) {
m_env->extend(varref->name());
++m_positional_params;
}
// silently ignore other kinds of objects
}
// Now the named params
for (Latte_List::const_iterator i = params; i != params_end; ++i) {
const Refcounter &obj = *i;
Latte_Param *param = obj->as_param();
if (param && param->is_named())
m_env->extend(param->name());
}
if (rest_param.get())
m_rest_offset = m_env->extend(rest_param->name());
}
Latte_Closure::Latte_Closure(const Latte_Closure &other) :
m_env(other.m_env),
m_activation(other.m_activation),
m_body(other.m_body),
m_rest_offset(other.m_rest_offset),
m_positional_params(other.m_positional_params),
m_macro(other.m_macro)
{
}
Refcounter
Latte_Closure::apply(const Latte_Wstate &ws,
const Latte_FileLoc &loc,
const Latte_List::const_iterator &args,
const Latte_List::const_iterator &args_end,
Latte_Activation &activation) const
{
Refcounter new_activation(new
Latte_Activation(*m_env,
*m_activation));
Refcounter rest_list((m_rest_offset >= 0) ?
(new Latte_List) :
0);
long n = 0;
for (Latte_List::const_iterator i = args; i != args_end; ++i) {
const Refcounter &obj = *i;
Latte_Assignment *assignment = obj->as_assignment();
if (assignment) {
try {
Refcounter val = assignment->nested_obj();
new_activation->lookup(assignment->name()) = val;
} catch (const Latte_BindingEnv::NotFound &err) {
throw IllegalAssignment(*this, err, loc);
};
} else if (n >= m_positional_params) {
if (m_rest_offset >= 0) {
rest_list->push_back(obj);
++n;
} // else silently drop it
} else {
new_activation->lookup(0, n) = obj;
++n;
}
}
if (m_rest_offset >= 0)
new_activation->lookup(0, m_rest_offset) =
Refcounter(rest_list.get());
Refcounter result = latte_false();
for (Latte_List::const_iterator i = m_body->begin(); i != m_body->end(); ) {
const Refcounter &obj = *i++;
if (!((i == m_body->end()) || obj->side_effects())) {
Latte_Tangible *tangible = obj->as_tangible();
throw Useless(*this, (tangible ? tangible->fileloc() : loc));
}
result = obj->eval(*new_activation);
}
return Latte_WsNode::wrap(*(m_macro ? result->eval(activation) : result),
ws);
}
void
Latte_Closure::visit(Latte_Visitor &visitor)
{
visitor.visit_closure(*this);
}
const shstring &
Latte_Closure::name() const
{
static const shstring n = "closure";
return n;
}