ADD: closure capture direct parent environment.
parent
f7b470c0b1
commit
d5fb25046e
|
@ -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))
|
10
lib/core.cpp
10
lib/core.cpp
|
@ -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()));
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
27
src/VM.cpp
27
src/VM.cpp
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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), \
|
||||||
|
|
Loading…
Reference in New Issue