diff --git a/examples/rec.fk b/examples/rec.fk new file mode 100644 index 0000000..2528653 --- /dev/null +++ b/examples/rec.fk @@ -0,0 +1,13 @@ +;; RECURSIVE LAMBDA +;; ================ +(assert= 720 ((-> (n) + (if (= n 0) 1 + (* n (self (- n 1))))) 6)) + +;; RECURSIVE NAMED LAMBDA +;; ====================== +($ fac (-> (n) + (if (= n 0) 1 + (* n (fac (- n 1)))))) + +(assert= 120 (fac 5)) \ No newline at end of file diff --git a/libstd/macro.cpp b/libstd/macro.cpp index 1d22d90..44206b9 100644 --- a/libstd/macro.cpp +++ b/libstd/macro.cpp @@ -34,7 +34,9 @@ namespace fkstd std::string ident = node->child(1)->repr(); auto rhs = node->child(2); + compiler.push_decl(ident); compiler.compile_prog(rhs, program); + compiler.pop_decl(); auto entry = compiler.sym()->declare_local(ident, addr, diff --git a/src/Compiler.cpp b/src/Compiler.cpp index fdd5a24..73c2962 100644 --- a/src/Compiler.cpp +++ b/src/Compiler.cpp @@ -53,6 +53,12 @@ namespace fk case NODE_LAMBDA: { auto params = node->child(0); auto body = node->child(1); + std::string func_name = "self"; + + if (m_decl_stack.empty() == false) + { + func_name = m_decl_stack.back(); + } m_sym->enter_scope(node); @@ -62,6 +68,8 @@ namespace fk m_sym->declare_local(ident, i, node->loc()); } + m_sym->declare_local(func_name, params->size(), node->loc()); + Compiler compiler {m_sym}; for (auto e: m_macros) { @@ -69,6 +77,7 @@ namespace fk } auto program = compiler.compile(body); + program->add(OP_RET); m_sym->leave_scope(); @@ -107,18 +116,21 @@ namespace fk if (auto entry = m_sym->find(node->child(0)->repr()); entry && entry->is_global() == false) { - size_t arity = entry->node()->child(0)->size(); - if (arity != node->size() - 1) + if (entry->node()) { - std::stringstream ss; - ss << "arity mismatch: " << node->child(0)->repr() - << " expect '" - << arity - << "' arguments, got '" - << node->size() - 1 - << "'"; + size_t arity = entry->node()->child(0)->size(); + if (arity != node->size() - 1) + { + std::stringstream ss; + ss << "arity mismatch: " << node->child(0)->repr() + << " expect '" + << arity + << "' arguments, got '" + << node->size() - 1 + << "'"; - node->loc().error(LOG_ERROR, ss.str()); + node->loc().error(LOG_ERROR, ss.str()); + } } prog->add(OP_CALL_REF, node->size() - 1); @@ -190,4 +202,16 @@ namespace fk } break; } } + + void Compiler::push_decl(std::string const& ident) + { + m_decl_stack.push_back(ident); + } + + void Compiler::pop_decl() + { + assert(m_decl_stack.empty() == false); + m_decl_stack.pop_back(); + } + } diff --git a/src/Compiler.hpp b/src/Compiler.hpp index 7d69bda..c5cf4ec 100644 --- a/src/Compiler.hpp +++ b/src/Compiler.hpp @@ -31,8 +31,12 @@ namespace fk void compile_prog(std::shared_ptr node, std::shared_ptr prog); + void push_decl(std::string const& ident); + void pop_decl(); + private: std::shared_ptr m_sym; + std::vector m_decl_stack; std::unordered_map> m_macros; diff --git a/src/SymTable.cpp b/src/SymTable.cpp index f3f1ce3..967abdf 100644 --- a/src/SymTable.cpp +++ b/src/SymTable.cpp @@ -56,7 +56,11 @@ namespace fk for (auto& entry: m_entries) { - if (entry.name() == name + if (entry.name() == name && entry.is_global()) + { + result = entry; + } + else if (entry.name() == name && entry.scope() <= m_scope && ((entry.parent() == nullptr && m_parents.empty()) || entry.parent() == m_parents.back()) diff --git a/src/VM.cpp b/src/VM.cpp index e4f3868..7c07da8 100644 --- a/src/VM.cpp +++ b/src/VM.cpp @@ -107,6 +107,11 @@ namespace fk store_local(i, args[i]); } + auto self = std::make_shared(TYPE_REF, + static_cast(ref), + ref_val->loc()); + store_local(args.size(), self); + m_pc = 0; } break;