ADD: function capture env (closure).
parent
83f1cc97b9
commit
8b5b243cab
|
@ -4,11 +4,15 @@
|
|||
(:
|
||||
($ a 4)
|
||||
(assert-eq? 4 a)
|
||||
(set! a 42)
|
||||
(assert-eq? 42 a)
|
||||
|
||||
(:
|
||||
($ a 7)
|
||||
(assert-eq? 7 a)
|
||||
(set! a 32)
|
||||
(assert-eq? 32 a)
|
||||
)
|
||||
|
||||
(assert-eq? 4 a)
|
||||
(assert-eq? 42 a)
|
||||
)
|
|
@ -72,3 +72,24 @@
|
|||
(assert-eq? 4 (j))
|
||||
(assert-eq? 5 (j))
|
||||
(assert-eq? 20 (k))
|
||||
|
||||
($ m 1)
|
||||
($ (n)
|
||||
($ counter 0)
|
||||
(-> ()
|
||||
(set! counter (* 2 m))
|
||||
(set! m (+ 1 m))
|
||||
counter))
|
||||
|
||||
($ o (n))
|
||||
($ p (n))
|
||||
|
||||
(assert-eq? 2 (o))
|
||||
(assert-eq? 4 (o))
|
||||
(assert-eq? 6 (o))
|
||||
|
||||
(assert-eq? 2 (p))
|
||||
(assert-eq? 4 (p))
|
||||
(assert-eq? 6 (p))
|
||||
|
||||
(assert-eq? 1 m)
|
|
@ -358,16 +358,8 @@ extern "C" void lib(grino::Loader& loader)
|
|||
if (entry)
|
||||
{
|
||||
compiler.compile(expr, program, sym);
|
||||
|
||||
if (entry->scope == compiler.scope()-1)
|
||||
{
|
||||
program.push_instr(grino::OPCODE_STORE_CLOSURE, entry->addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
program.push_instr(grino::OPCODE_STORE_LOCAL, entry->addr);
|
||||
}
|
||||
}
|
||||
|
||||
program.push_value(grino::Value::make_nil(node->loc()));
|
||||
});
|
||||
|
|
|
@ -160,11 +160,6 @@ namespace grino
|
|||
ss << "undefined variable '" << ident << "'";
|
||||
m_logger.log<compile_error>(LOG_ERROR, node->loc(), ss.str());
|
||||
}
|
||||
else if (entry->is_object == false
|
||||
&& entry->scope < scope())
|
||||
{
|
||||
program.push_instr(OPCODE_LOAD_CLOSURE, entry->addr);
|
||||
}
|
||||
else if (entry->is_object)
|
||||
{
|
||||
program.push_instr(OPCODE_LOAD_OBJ, entry->addr);
|
||||
|
|
|
@ -36,6 +36,11 @@ namespace grino
|
|||
return m_native(args);
|
||||
}
|
||||
|
||||
bool Function::has_env(size_t addr) const
|
||||
{
|
||||
return m_env.find(addr) != std::end(m_env);
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> Function::env(size_t addr) const
|
||||
{
|
||||
return m_env.at(addr);
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace grino
|
|||
std::shared_ptr<Program> program() const;
|
||||
value_t call(args_t args);
|
||||
|
||||
bool has_env(size_t addr) const;
|
||||
std::shared_ptr<Value> env(size_t addr) const;
|
||||
void set_env(size_t addr, std::shared_ptr<Value> value);
|
||||
|
||||
|
|
108
src/VM.cpp
108
src/VM.cpp
|
@ -31,18 +31,6 @@ namespace grino
|
|||
|
||||
switch (instr.opcode)
|
||||
{
|
||||
case OPCODE_LOAD_CLOSURE: {
|
||||
auto val = m_frames.back().function->env(*instr.param);
|
||||
push(program().push_constant(val));
|
||||
m_pc++;
|
||||
} break;
|
||||
|
||||
case OPCODE_STORE_CLOSURE: {
|
||||
auto val = program().constant(pop());
|
||||
m_frames.back().function->set_env(*instr.param, val);
|
||||
m_pc++;
|
||||
} break;
|
||||
|
||||
case OPCODE_MK_FUN: {
|
||||
auto prog_val = program().constant(pop());
|
||||
auto prog = prog_val->as_program();
|
||||
|
@ -109,13 +97,105 @@ namespace grino
|
|||
case OPCODE_STORE_LOCAL: {
|
||||
size_t addr = *instr.param;
|
||||
auto value = program().constant(top());
|
||||
set_local(addr, value);
|
||||
bool found = false;
|
||||
|
||||
if (auto itr=m_frames.back().locals.find(addr);
|
||||
itr != std::end(m_frames.back().locals))
|
||||
{
|
||||
m_frames.back().locals[addr] = value;
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto fun = m_frames.back().function;
|
||||
|
||||
if (fun && fun->has_env(addr))
|
||||
{
|
||||
fun->set_env(addr, Value::make_copy(value->loc(), value));
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i=0; i<m_frames.size(); i++)
|
||||
{
|
||||
size_t j = m_frames.size() - 1 - i;
|
||||
|
||||
if (auto itr=m_frames[j].locals.find(addr);
|
||||
itr != std::end(m_frames[j].locals))
|
||||
{
|
||||
if (fun)
|
||||
{
|
||||
std::cout << "store INIT" << std::endl;
|
||||
fun->set_env(itr->first,
|
||||
Value::make_copy(itr->second->loc(),
|
||||
itr->second));
|
||||
}
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
m_frames.back().locals[addr] = value;
|
||||
}
|
||||
|
||||
m_pc++;
|
||||
} break;
|
||||
|
||||
case OPCODE_LOAD_LOCAL: {
|
||||
size_t addr = *instr.param;
|
||||
push(program().push_constant(local(addr)));
|
||||
bool found = false;
|
||||
|
||||
if (auto itr=m_frames.back().locals.find(addr);
|
||||
itr != std::end(m_frames.back().locals))
|
||||
{
|
||||
push(program().push_constant(itr->second));
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto fun = m_frames.back().function;
|
||||
|
||||
if (fun && fun->has_env(addr))
|
||||
{
|
||||
push(program().push_constant(fun->env(addr)));
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i=0; i<m_frames.size(); i++)
|
||||
{
|
||||
size_t j = m_frames.size() - 1 - i;
|
||||
|
||||
if (auto itr2=m_frames[j].locals.find(addr);
|
||||
itr2 != std::end(m_frames[j].locals))
|
||||
{
|
||||
push(program().push_constant(itr2->second));
|
||||
|
||||
if (fun)
|
||||
{
|
||||
fun->set_env(itr2->first,
|
||||
Value::make_copy(itr2->second->loc(),
|
||||
itr2->second));
|
||||
}
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
}
|
||||
|
||||
assert(found);
|
||||
|
||||
m_pc++;
|
||||
} break;
|
||||
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
G(OPCODE_STORE_LOCAL), \
|
||||
G(OPCODE_LOAD_OBJ), \
|
||||
G(OPCODE_STORE_OBJ), \
|
||||
G(OPCODE_LOAD_CLOSURE), \
|
||||
G(OPCODE_STORE_CLOSURE), \
|
||||
G(OPCODE_CALL), \
|
||||
G(OPCODE_BRF), \
|
||||
G(OPCODE_BR), \
|
||||
|
|
Loading…
Reference in New Issue