diff --git a/examples/closure.fk b/examples/closure.fk new file mode 100644 index 0000000..1dafd0b --- /dev/null +++ b/examples/closure.fk @@ -0,0 +1,16 @@ +($ mk-counter + (-> (init) + ($ counter (- init 1)) + (-> () + (! counter (+ counter 1)) + counter))) + +($ a (mk-counter 5)) +($ b (mk-counter 32)) + +(assert= 5 (a)) +(assert= 32 (b)) +(assert= 33 (b)) +(assert= 6 (a)) +(assert= 7 (a)) +(assert= 34 (b)) \ No newline at end of file diff --git a/libstd/macro.cpp b/libstd/macro.cpp index 4ed4fd7..671296a 100644 --- a/libstd/macro.cpp +++ b/libstd/macro.cpp @@ -9,9 +9,18 @@ namespace fkstd { auto ident = node->child(1)->repr(); compiler.compile_prog(node->child(2), program); - + std::cout << "push " << node->child(2)->string() << std::endl; auto entry = compiler.sym()->find(ident); - program->add(OP_STORE_LOCAL, entry->addr()); + + if (entry) + { + program->add(OP_STORE_LOCAL, entry->addr()); + } + else + { + auto entry = compiler.sym()->find_global(ident); + program->add(OP_STORE_CLOSURE, entry->addr()); + } } void assert_static_fail(Compiler& compiler, diff --git a/src/Compiler.cpp b/src/Compiler.cpp index 73c2962..98362d9 100644 --- a/src/Compiler.cpp +++ b/src/Compiler.cpp @@ -146,6 +146,17 @@ namespace fk case NODE_IDENT: { auto entry = m_sym->find(node->repr()); + if (!entry) + { + auto entry = m_sym->find_global(node->repr()); + + if (entry) + { + prog->add(OP_LOAD_CLOSURE, entry->addr()); + break; + } + } + if (!entry) { std::stringstream ss; diff --git a/src/Constant.cpp b/src/Constant.cpp index d9a03eb..21e7c50 100644 --- a/src/Constant.cpp +++ b/src/Constant.cpp @@ -14,6 +14,12 @@ namespace fk { } + std::shared_ptr Constant::clone() + { + auto res = std::make_shared(m_type, m_value, m_loc); + return res; + } + std::string Constant::string() const { switch (m_type) @@ -23,6 +29,7 @@ namespace fk case TYPE_BOOL: return std::get(m_value) ? "true" : "false"; case TYPE_STRING: return std::get(m_value); case TYPE_REF: return std::to_string(std::get(m_value)); + case TYPE_PROGRAM: return ""; default: { std::stringstream ss; diff --git a/src/Constant.hpp b/src/Constant.hpp index 0276a85..7843985 100644 --- a/src/Constant.hpp +++ b/src/Constant.hpp @@ -25,6 +25,8 @@ namespace fk explicit Constant(Type type, constant_t value, Loc const& loc); virtual ~Constant(); + std::shared_ptr clone(); + Type type() const { return m_type; } constant_t value() const { return m_value; } Loc loc() const { return m_loc; } diff --git a/src/Lambda.cpp b/src/Lambda.cpp index fa239bf..cf0c829 100644 --- a/src/Lambda.cpp +++ b/src/Lambda.cpp @@ -11,4 +11,23 @@ namespace fk /*virtual*/ Lambda::~Lambda() { } + + bool Lambda::has_env(addr_t addr) const + { + return m_env.find(addr) != std::end(m_env); + } + + std::shared_ptr + Lambda::get_env(addr_t addr) const + { + assert(has_env(addr)); + return m_env.at(addr); + } + + void + Lambda::add_env(addr_t addr, std::shared_ptr constant) + { + m_env[addr] = constant; + } + } diff --git a/src/Lambda.hpp b/src/Lambda.hpp index 9d3036b..58afe7b 100644 --- a/src/Lambda.hpp +++ b/src/Lambda.hpp @@ -4,6 +4,7 @@ #include "commons.hpp" #include "SymTable.hpp" #include "Program.hpp" +#include namespace fk { @@ -18,8 +19,13 @@ namespace fk std::shared_ptr program() const { return m_program; } std::shared_ptr sym() const { return m_sym; } + bool has_env(addr_t addr) const; + std::shared_ptr get_env(addr_t addr) const; + void add_env(addr_t addr, std::shared_ptr constant); + private: std::shared_ptr m_program; + std::unordered_map> m_env; size_t m_arity; std::shared_ptr m_sym = std::make_shared(); }; diff --git a/src/SymTable.cpp b/src/SymTable.cpp index 5e12842..ea029fc 100644 --- a/src/SymTable.cpp +++ b/src/SymTable.cpp @@ -89,6 +89,43 @@ namespace fk return idx; } + std::optional SymTable::find_global(std::string const& name) + { + auto idx = find_idx_global(name); + if (!idx) { return std::nullopt; } + + return m_entries[*idx]; + } + + std::optional SymTable::find_idx_global(std::string const& name) + { + std::optional result; + + size_t i=0; + std::optional idx; + + for (auto& entry: m_entries) + { + if (entry.name() == name && entry.is_global()) + { + result = entry; + idx = i; + } + else if (entry.name() == name + && entry.scope() <= m_scope + && (result == std::nullopt || + entry.scope() > result->scope())) + { + result = entry; + idx = i; + } + + i++; + } + + return idx; + } + void SymTable::set(std::string const& name, addr_t addr) { if (auto idx = find_idx(name); diff --git a/src/SymTable.hpp b/src/SymTable.hpp index 0564110..9353720 100644 --- a/src/SymTable.hpp +++ b/src/SymTable.hpp @@ -14,6 +14,8 @@ namespace fk explicit SymTable(); virtual ~SymTable(); + int scope() const { return m_scope; } + SymEntry& declare_local(std::string const& name, addr_t addr, Loc const& loc); @@ -24,6 +26,10 @@ namespace fk std::optional find(std::string const& name); std::optional find_idx(std::string const& name); + + std::optional find_global(std::string const& name); + std::optional find_idx_global(std::string const& name); + void set(std::string const& name, addr_t addr); std::string string() const; diff --git a/src/VM.cpp b/src/VM.cpp index 6e93633..9dc8521 100644 --- a/src/VM.cpp +++ b/src/VM.cpp @@ -29,6 +29,27 @@ namespace fk switch (instr.opcode) { + case OP_STORE_CLOSURE: { + auto val = frame().program->get_const(top()); + auto lambda = frame().lambda; + lambda->add_env(instr.param, val); + m_pc++; + } break; + + case OP_LOAD_CLOSURE: { + if (frame().lambda && frame().lambda->has_env(instr.param)) + { + auto val = frame().lambda->get_env(instr.param); + addr_t addr = frame().program->add(val); + + push(addr); + m_pc++; + break; + } + + m_pc++; + } break; + case OP_BR: { m_pc = instr.param; } break; @@ -56,6 +77,14 @@ namespace fk addr_t addr = store_global(lambda); auto ref = std::make_shared(TYPE_REF, addr, p->loc()); + if (m_frames.size() > 1) + { + for (auto e: m_frames.back().locals) + { + lambda->add_env(e.first, e.second); + } + } + push(frame().program->add(ref)); m_pc++; @@ -114,6 +143,7 @@ namespace fk ref_val->loc()); Frame frame; + frame.lambda = lambda; frame.program = lambda->program(); frame.ret_addr = m_pc; frame.stack_sz = m_stack.size(); @@ -139,7 +169,9 @@ namespace fk args.insert(std::begin(args), frame().program->get_const(addr)); } + auto ref_val = frame().program->get_const(pop()); + Loc loc = ref_val->loc(); if (instr.param > 0) @@ -148,6 +180,7 @@ namespace fk } auto ref = std::get(ref_val->value()); + auto fun = std::get> (load_global(ref)); @@ -214,6 +247,7 @@ namespace fk Loc const& loc) { Frame frame; + frame.lambda = lambda; frame.program = lambda->program(); frame.ret_addr = m_pc; frame.stack_sz = m_stack.size(); @@ -315,6 +349,7 @@ namespace fk static_cast(ref), loc); Frame frame; + frame.lambda = lambda; frame.program = lambda->program(); frame.ret_addr = m_pc; frame.stack_sz = m_stack.size(); diff --git a/src/VM.hpp b/src/VM.hpp index 2127783..48362f1 100644 --- a/src/VM.hpp +++ b/src/VM.hpp @@ -14,6 +14,7 @@ namespace fk struct Frame { std::shared_ptr program; + std::shared_ptr lambda; addr_t ret_addr; size_t stack_sz; std::unordered_map> locals; diff --git a/src/opcodes.hpp b/src/opcodes.hpp index 9513dcb..014b857 100644 --- a/src/opcodes.hpp +++ b/src/opcodes.hpp @@ -4,7 +4,7 @@ #define OPCODES(G) G(OP_LOAD_CONST), G(OP_POP), G(OP_CALL_NATIVE), \ G(OP_LOAD_GLOBAL), G(OP_LOAD_LOCAL), G(OP_STORE_LOCAL), \ G(OP_MAKE_FUNCTION), G(OP_CALL_REF), G(OP_RET), G(OP_BNE), \ - G(OP_BR) + G(OP_BR), G(OP_LOAD_CLOSURE), G(OP_STORE_CLOSURE) #include "commons.hpp"