ADD: closure capture direct parent environment.

main
bog 2023-09-12 14:32:14 +02:00
parent f7b470c0b1
commit d5fb25046e
10 changed files with 107 additions and 3 deletions

View File

@ -50,3 +50,25 @@
(* 3 (- n 2)))) (* 3 (- n 2))))
(assert-eq? 15 ( (h) 7 )) (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))

View File

@ -358,7 +358,15 @@ 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()));

View File

@ -157,8 +157,12 @@ 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
if (entry->is_object) && entry->scope < scope())
{
program.push_instr(OPCODE_LOAD_CLOSURE, entry->addr);
}
else if (entry->is_object)
{ {
program.push_instr(OPCODE_LOAD_OBJ, entry->addr); program.push_instr(OPCODE_LOAD_OBJ, entry->addr);
} }

View File

@ -33,4 +33,15 @@ namespace grino
assert(m_native); assert(m_native);
return m_native(args); 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;
}
} }

View File

@ -23,9 +23,13 @@ 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);
std::shared_ptr<Value> env(size_t addr) const;
void set_env(size_t addr, std::shared_ptr<Value> value);
private: private:
native_t m_native; native_t m_native;
std::shared_ptr<Program> m_prog; std::shared_ptr<Program> m_prog;
std::unordered_map<size_t, std::shared_ptr<Value>> m_env;
}; };
} }

View File

@ -10,6 +10,7 @@ namespace grino
Frame frame; Frame frame;
frame.pc = m_pc; frame.pc = m_pc;
frame.sp = m_sp; frame.sp = m_sp;
frame.function = nullptr;
frame.program = &program; frame.program = &program;
m_frames.push_back(frame); m_frames.push_back(frame);
} }
@ -30,10 +31,35 @@ 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();
auto fun_val = Value::make_function(prog_val->loc(), prog); 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(); size_t addr = m_heap.size();
set_heap(addr, fun_val); set_heap(addr, fun_val);
@ -142,6 +168,7 @@ namespace grino
Frame frame; Frame frame;
frame.pc = m_pc; frame.pc = m_pc;
frame.sp = m_sp; frame.sp = m_sp;
frame.function = fun;
frame.program = fun->program().get(); frame.program = fun->program().get();
m_frames.push_back(frame); m_frames.push_back(frame);

View File

@ -15,6 +15,7 @@ namespace grino
size_t pc; size_t pc;
size_t sp; size_t sp;
Program* program; Program* program;
std::shared_ptr<Function> function;
std::unordered_map<size_t, std::shared_ptr<Value>> locals; std::unordered_map<size_t, std::shared_ptr<Value>> locals;
}; };

View File

@ -64,6 +64,28 @@ namespace grino
return value; 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 std::shared_ptr<Program> Value::as_program() const
{ {
return m_program_val; return m_program_val;

View File

@ -26,6 +26,9 @@ namespace grino
static std::shared_ptr<Value> make_program(Loc const& loc, static std::shared_ptr<Value> make_program(Loc const& loc,
std::shared_ptr<Program> val); 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); explicit Value(Loc const& loc);
virtual ~Value() = default; virtual ~Value() = default;

View File

@ -11,6 +11,8 @@
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), \