ADD: closure capture direct parent environment.
parent
f7b470c0b1
commit
d5fb25046e
|
@ -50,3 +50,25 @@
|
|||
(* 3 (- n 2))))
|
||||
|
||||
(assert-eq? 15 ( (h) 7 ))
|
||||
|
||||
;; closure
|
||||
($ (i init)
|
||||
($ counter (- init 1))
|
||||
(-> ()
|
||||
(set! counter (+ counter 1))
|
||||
counter))
|
||||
|
||||
($ j (i 0))
|
||||
($ k (i 16))
|
||||
|
||||
(assert-eq? 0 (j))
|
||||
(assert-eq? 16 (k))
|
||||
(assert-eq? 17 (k))
|
||||
(assert-eq? 1 (j))
|
||||
(assert-eq? 2 (j))
|
||||
(assert-eq? 18 (k))
|
||||
(assert-eq? 19 (k))
|
||||
(assert-eq? 3 (j))
|
||||
(assert-eq? 4 (j))
|
||||
(assert-eq? 5 (j))
|
||||
(assert-eq? 20 (k))
|
|
@ -358,8 +358,16 @@ 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()));
|
||||
});
|
||||
|
|
|
@ -157,8 +157,12 @@ namespace grino
|
|||
ss << "undefined variable '" << ident << "'";
|
||||
m_logger.log<compile_error>(LOG_ERROR, node->loc(), ss.str());
|
||||
}
|
||||
|
||||
if (entry->is_object)
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -33,4 +33,15 @@ namespace grino
|
|||
assert(m_native);
|
||||
return m_native(args);
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> Function::env(size_t addr) const
|
||||
{
|
||||
return m_env.at(addr);
|
||||
}
|
||||
|
||||
void Function::set_env(size_t addr, std::shared_ptr<Value> value)
|
||||
{
|
||||
m_env[addr] = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,9 +23,13 @@ namespace grino
|
|||
std::shared_ptr<Program> program() const;
|
||||
value_t call(args_t args);
|
||||
|
||||
std::shared_ptr<Value> env(size_t addr) const;
|
||||
void set_env(size_t addr, std::shared_ptr<Value> value);
|
||||
|
||||
private:
|
||||
native_t m_native;
|
||||
std::shared_ptr<Program> m_prog;
|
||||
std::unordered_map<size_t, std::shared_ptr<Value>> m_env;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
27
src/VM.cpp
27
src/VM.cpp
|
@ -10,6 +10,7 @@ namespace grino
|
|||
Frame frame;
|
||||
frame.pc = m_pc;
|
||||
frame.sp = m_sp;
|
||||
frame.function = nullptr;
|
||||
frame.program = &program;
|
||||
m_frames.push_back(frame);
|
||||
}
|
||||
|
@ -30,10 +31,35 @@ 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();
|
||||
|
||||
auto fun_val = Value::make_function(prog_val->loc(), prog);
|
||||
|
||||
// closure
|
||||
if (m_frames.size() > 0)
|
||||
{
|
||||
for (auto const& entry: m_frames.at(m_frames.size() - 1).locals)
|
||||
{
|
||||
fun_val->as_function()
|
||||
->set_env(entry.first,
|
||||
Value::make_copy(fun_val->loc(), entry.second));
|
||||
}
|
||||
}
|
||||
|
||||
size_t addr = m_heap.size();
|
||||
set_heap(addr, fun_val);
|
||||
|
||||
|
@ -142,6 +168,7 @@ namespace grino
|
|||
Frame frame;
|
||||
frame.pc = m_pc;
|
||||
frame.sp = m_sp;
|
||||
frame.function = fun;
|
||||
frame.program = fun->program().get();
|
||||
m_frames.push_back(frame);
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace grino
|
|||
size_t pc;
|
||||
size_t sp;
|
||||
Program* program;
|
||||
std::shared_ptr<Function> function;
|
||||
std::unordered_map<size_t, std::shared_ptr<Value>> locals;
|
||||
};
|
||||
|
||||
|
|
|
@ -64,6 +64,28 @@ namespace grino
|
|||
return value;
|
||||
}
|
||||
|
||||
/*static*/
|
||||
std::shared_ptr<Value> Value::make_copy(Loc const& loc,
|
||||
std::shared_ptr<Value> val)
|
||||
{
|
||||
switch (val->type())
|
||||
{
|
||||
case TYPE_INT: {
|
||||
return Value::make_int(loc, val->as_int());
|
||||
} break;
|
||||
|
||||
case TYPE_REF: {
|
||||
return Value::make_ref(loc, val->as_ref());
|
||||
} break;
|
||||
|
||||
default:
|
||||
std::cerr << "cannot copy unknown value " <<
|
||||
TypeTypeStr[val->type()] << std::endl;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Program> Value::as_program() const
|
||||
{
|
||||
return m_program_val;
|
||||
|
|
|
@ -26,6 +26,9 @@ namespace grino
|
|||
static std::shared_ptr<Value> make_program(Loc const& loc,
|
||||
std::shared_ptr<Program> val);
|
||||
|
||||
static std::shared_ptr<Value> make_copy(Loc const& loc,
|
||||
std::shared_ptr<Value> val);
|
||||
|
||||
explicit Value(Loc const& loc);
|
||||
virtual ~Value() = default;
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
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