ADD: function capture env (closure).
parent
83f1cc97b9
commit
8b5b243cab
|
@ -4,11 +4,15 @@
|
||||||
(:
|
(:
|
||||||
($ a 4)
|
($ a 4)
|
||||||
(assert-eq? 4 a)
|
(assert-eq? 4 a)
|
||||||
|
(set! a 42)
|
||||||
|
(assert-eq? 42 a)
|
||||||
|
|
||||||
(:
|
(:
|
||||||
($ a 7)
|
($ a 7)
|
||||||
(assert-eq? 7 a)
|
(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? 4 (j))
|
||||||
(assert-eq? 5 (j))
|
(assert-eq? 5 (j))
|
||||||
(assert-eq? 20 (k))
|
(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)
|
10
lib/core.cpp
10
lib/core.cpp
|
@ -358,15 +358,7 @@ extern "C" void lib(grino::Loader& loader)
|
||||||
if (entry)
|
if (entry)
|
||||||
{
|
{
|
||||||
compiler.compile(expr, program, sym);
|
compiler.compile(expr, program, sym);
|
||||||
|
program.push_instr(grino::OPCODE_STORE_LOCAL, entry->addr);
|
||||||
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()));
|
program.push_value(grino::Value::make_nil(node->loc()));
|
||||||
|
|
|
@ -160,11 +160,6 @@ namespace grino
|
||||||
ss << "undefined variable '" << ident << "'";
|
ss << "undefined variable '" << ident << "'";
|
||||||
m_logger.log<compile_error>(LOG_ERROR, node->loc(), ss.str());
|
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)
|
else if (entry->is_object)
|
||||||
{
|
{
|
||||||
program.push_instr(OPCODE_LOAD_OBJ, entry->addr);
|
program.push_instr(OPCODE_LOAD_OBJ, entry->addr);
|
||||||
|
|
|
@ -36,6 +36,11 @@ namespace grino
|
||||||
return m_native(args);
|
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
|
std::shared_ptr<Value> Function::env(size_t addr) const
|
||||||
{
|
{
|
||||||
return m_env.at(addr);
|
return m_env.at(addr);
|
||||||
|
|
|
@ -25,6 +25,7 @@ namespace grino
|
||||||
std::shared_ptr<Program> program() const;
|
std::shared_ptr<Program> program() const;
|
||||||
value_t call(args_t args);
|
value_t call(args_t args);
|
||||||
|
|
||||||
|
bool has_env(size_t addr) const;
|
||||||
std::shared_ptr<Value> 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);
|
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)
|
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: {
|
case OPCODE_MK_FUN: {
|
||||||
auto prog_val = program().constant(pop());
|
auto prog_val = program().constant(pop());
|
||||||
auto prog = prog_val->as_program();
|
auto prog = prog_val->as_program();
|
||||||
|
@ -109,13 +97,105 @@ namespace grino
|
||||||
case OPCODE_STORE_LOCAL: {
|
case OPCODE_STORE_LOCAL: {
|
||||||
size_t addr = *instr.param;
|
size_t addr = *instr.param;
|
||||||
auto value = program().constant(top());
|
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++;
|
m_pc++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case OPCODE_LOAD_LOCAL: {
|
case OPCODE_LOAD_LOCAL: {
|
||||||
size_t addr = *instr.param;
|
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++;
|
m_pc++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
G(OPCODE_STORE_LOCAL), \
|
G(OPCODE_STORE_LOCAL), \
|
||||||
G(OPCODE_LOAD_OBJ), \
|
G(OPCODE_LOAD_OBJ), \
|
||||||
G(OPCODE_STORE_OBJ), \
|
G(OPCODE_STORE_OBJ), \
|
||||||
G(OPCODE_LOAD_CLOSURE), \
|
|
||||||
G(OPCODE_STORE_CLOSURE), \
|
|
||||||
G(OPCODE_CALL), \
|
G(OPCODE_CALL), \
|
||||||
G(OPCODE_BRF), \
|
G(OPCODE_BRF), \
|
||||||
G(OPCODE_BR), \
|
G(OPCODE_BR), \
|
||||||
|
|
Loading…
Reference in New Issue