diff --git a/examples/block.fk b/examples/block.fk new file mode 100644 index 0000000..4259685 --- /dev/null +++ b/examples/block.fk @@ -0,0 +1,24 @@ +($ a 2) + +(: + (assert= 2 a) + ($ a 3) + (assert= 3 a) + + (: + ($ a 7) + (assert= 7 a) + (assert-static-fail ($ a 2)) + ) + + (assert= 3 a) +) + +(assert= 2 a) + +($ b (: 2 6 9)) + +(assert= 9 b) + +($ c (: 1 2 (: 3 4 (: 5 6 7)))) +(assert= 7 c) \ No newline at end of file diff --git a/examples/vardecl.fk b/examples/vardecl.fk new file mode 100644 index 0000000..a9923e3 --- /dev/null +++ b/examples/vardecl.fk @@ -0,0 +1,10 @@ +(assert-static-fail a) +($ a 12) +(assert-static-fail ($ a 3)) + +($ b 13) +($ c a) + +(assert= 12 a) +(assert= 13 b) +(assert= 12 c) diff --git a/libstd/lib.cpp b/libstd/lib.cpp index 1290d99..645275d 100644 --- a/libstd/lib.cpp +++ b/libstd/lib.cpp @@ -8,4 +8,6 @@ extern "C" void lib(Module& mod) mod.register_function("println", fkstd::println); mod.register_macro("assert-static-fail", fkstd::assert_static_fail); + mod.register_macro(":", fkstd::block); + mod.register_macro("$", fkstd::decl); } diff --git a/libstd/macro.cpp b/libstd/macro.cpp index 4e541c3..744a4a1 100644 --- a/libstd/macro.cpp +++ b/libstd/macro.cpp @@ -22,4 +22,44 @@ namespace fkstd ss << "assertion failed"; node->loc().error(LOG_ERROR, ss.str()); } + + void decl(Compiler& compiler, + std::shared_ptr node, + std::shared_ptr program) + { + static addr_t addr = 0; + addr++; + + std::string ident = node->child(1)->repr(); + auto rhs = node->child(2); + + compiler.compile_prog(rhs, program); + + compiler.sym()->declare_local(ident, + addr, + node->loc()); + + program->add(OP_STORE_LOCAL, addr); + } + + void block(Compiler& compiler, + std::shared_ptr node, + std::shared_ptr program) + { + compiler.sym()->enter_scope(); + + for (size_t i=1; isize(); i++) + { + compiler.compile_prog(node->child(i), program); + + if (i != node->size() - 1) + { + program->add(OP_POP); + } + } + + compiler.sym()->leave_scope(); + + // std::cout << compiler.sym()->string() << std::endl; + } } diff --git a/libstd/macro.hpp b/libstd/macro.hpp index 880e304..70a88be 100644 --- a/libstd/macro.hpp +++ b/libstd/macro.hpp @@ -7,6 +7,15 @@ namespace fkstd void assert_static_fail(Compiler& compiler, std::shared_ptr node, std::shared_ptr program); + + void decl(Compiler& compiler, + std::shared_ptr node, + std::shared_ptr program); + + void block(Compiler& compiler, + std::shared_ptr node, + std::shared_ptr program); + } #endif diff --git a/src/Compiler.cpp b/src/Compiler.cpp index 8af5c0f..ad062c6 100644 --- a/src/Compiler.cpp +++ b/src/Compiler.cpp @@ -72,10 +72,12 @@ namespace fk if (!entry->is_global()) { - throw std::runtime_error { "not implemented yet" }; + prog->add(OP_LOAD_LOCAL, entry->addr()); + } + else + { + prog->add(OP_LOAD_GLOBAL, entry->addr()); } - - prog->add(OP_LOAD_GLOBAL, entry->addr()); } break; diff --git a/src/Compiler.hpp b/src/Compiler.hpp index 2fb6c8e..e443e94 100644 --- a/src/Compiler.hpp +++ b/src/Compiler.hpp @@ -17,6 +17,8 @@ namespace fk explicit Compiler(std::shared_ptr sym); virtual ~Compiler(); + std::shared_ptr sym() const { return m_sym; } + void add_macro(std::string const& name, std::shared_ptr macro); diff --git a/src/Parser.cpp b/src/Parser.cpp index 9ca24d3..110ec2e 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -1,5 +1,6 @@ #include "Parser.hpp" #include "src/Loc.hpp" +#include "src/Node.hpp" namespace fk { @@ -48,7 +49,12 @@ namespace fk std::shared_ptr Parser::parse_expr() { - if (type_any({NODE_INT, NODE_FLOAT, NODE_BOOL, NODE_STRING})) + if (type_any({ + NODE_INT, + NODE_FLOAT, + NODE_BOOL, + NODE_STRING, + NODE_IDENT})) { return consume(); } diff --git a/src/SymEntry.cpp b/src/SymEntry.cpp index 73cf759..3f6a69d 100644 --- a/src/SymEntry.cpp +++ b/src/SymEntry.cpp @@ -5,10 +5,14 @@ namespace fk /*explicit*/ SymEntry::SymEntry(std::string const& name, addr_t addr, bool is_global, + int scope, + std::shared_ptr parent, Loc const& loc) : m_name { name } , m_addr { addr } , m_is_global { is_global } + , m_scope { scope } + , m_parent { parent } , m_loc { loc } { } @@ -20,7 +24,7 @@ namespace fk std::string SymEntry::string() const { std::stringstream ss; - ss << m_name << "\t" << m_addr << "\t" << m_is_global; + ss << m_name << "\t" << m_addr << "\t" << m_is_global << "\t" << m_scope; return ss.str(); } } diff --git a/src/SymEntry.hpp b/src/SymEntry.hpp index 73f92b7..dd9ae38 100644 --- a/src/SymEntry.hpp +++ b/src/SymEntry.hpp @@ -6,21 +6,28 @@ namespace fk { + class Node; + class SymEntry { public: explicit SymEntry(std::string const& name, addr_t addr, bool is_global, + int scope, + std::shared_ptr parent, Loc const& loc); virtual ~SymEntry(); std::string name() const { return m_name; } addr_t addr() const { return m_addr; } bool is_global() const { return m_is_global; } + int scope() const { return m_scope; } + std::shared_ptr parent() const { return m_parent; } Loc loc() const { return m_loc; } void set_global(bool global) { m_is_global = global; } + void set_addr(addr_t addr) { m_addr = addr; } std::string string() const; @@ -28,6 +35,8 @@ namespace fk std::string m_name; addr_t m_addr; bool m_is_global = false; + int m_scope; + std::shared_ptr m_parent; Loc m_loc; }; } diff --git a/src/SymTable.cpp b/src/SymTable.cpp index ab1a49b..0f07fb4 100644 --- a/src/SymTable.cpp +++ b/src/SymTable.cpp @@ -17,7 +17,7 @@ namespace fk { auto entry = find(name); - if (entry) + if (entry && entry->scope() == m_scope) { std::stringstream ss; ss << "cannot declare existing variable '" @@ -27,7 +27,7 @@ namespace fk entry->loc().error(LOG_ERROR, ss.str()); } - m_entries.push_back(SymEntry {name, addr, false, loc}); + m_entries.push_back(SymEntry {name, addr, false, m_scope, nullptr, loc}); } void SymTable::declare_global(std::string const& name, @@ -40,15 +40,20 @@ namespace fk std::optional SymTable::find(std::string const& name) { + std::optional result; + for (auto& entry: m_entries) { - if (entry.name() == name) + if (entry.name() == name + && entry.scope() <= m_scope + && (result == std::nullopt || + entry.scope() > result->scope())) { - return entry; + result = entry; } } - return std::nullopt; + return result; } std::string SymTable::string() const @@ -56,6 +61,9 @@ namespace fk std::stringstream ss; ss << "======== SymTable ========\n"; + ss << std::setw(8) << std::left; + ss << "name\taddr\tglobal\tscope" << std::endl; + for (auto const& entry: m_entries) { ss << entry.string() << std::endl; @@ -63,4 +71,14 @@ namespace fk return ss.str(); } + + void SymTable::enter_scope() + { + m_scope++; + } + + void SymTable::leave_scope() + { + m_scope--; + } } diff --git a/src/SymTable.hpp b/src/SymTable.hpp index 2ffcdb3..4fcfb7c 100644 --- a/src/SymTable.hpp +++ b/src/SymTable.hpp @@ -26,8 +26,12 @@ namespace fk std::string string() const; + void enter_scope(); + void leave_scope(); + private: std::vector m_entries; + int m_scope; }; } diff --git a/src/VM.cpp b/src/VM.cpp index ee59e8d..51e8a3c 100644 --- a/src/VM.cpp +++ b/src/VM.cpp @@ -30,8 +30,10 @@ namespace fk for (size_t i=0; iget_const(pop())); + frame().program->get_const(addr)); } auto ref = std::get(frame().program @@ -44,6 +46,21 @@ namespace fk m_pc++; } break; + case OP_LOAD_LOCAL: { + auto value = frame().locals[instr.param]; + assert(value); + push(frame().program->add(value)); + + m_pc++; + } break; + + case OP_STORE_LOCAL: { + auto value = frame().program->get_const(top()); + assert(value); + frame().locals[instr.param] = value; + m_pc++; + } break; + case OP_LOAD_GLOBAL: { auto ref = std::make_shared(TYPE_REF, (size_t) instr.param, @@ -89,6 +106,16 @@ namespace fk return addr; } + std::shared_ptr VM::load_local(addr_t addr) + { + return frame().locals[addr]; + } + + void VM::store_local(addr_t addr, std::shared_ptr constant) + { + frame().locals[addr] = constant; + } + std::string VM::string() const { std::stringstream ss; diff --git a/src/VM.hpp b/src/VM.hpp index 78a7089..15b9af3 100644 --- a/src/VM.hpp +++ b/src/VM.hpp @@ -11,6 +11,7 @@ namespace fk struct Frame { std::shared_ptr program; + std::unordered_map> locals; }; class VM @@ -19,7 +20,7 @@ namespace fk explicit VM(); virtual ~VM(); - Frame frame() const { return m_frames.back(); } + Frame& frame() { return m_frames.back(); } void mount(std::shared_ptr program); void run(); @@ -28,6 +29,9 @@ namespace fk void store_global(addr_t addr, global_t value); size_t store_global(global_t value); + std::shared_ptr load_local(addr_t addr); + void store_local(addr_t addr, std::shared_ptr constant); + std::string string() const; private: diff --git a/src/opcodes.hpp b/src/opcodes.hpp index 6e18880..1c29b02 100644 --- a/src/opcodes.hpp +++ b/src/opcodes.hpp @@ -2,7 +2,7 @@ #define fk_OPCODES_HPP #define OPCODES(G) G(OP_LOAD_CONST), G(OP_POP), G(OP_CALL_NATIVE), \ - G(OP_LOAD_GLOBAL) + G(OP_LOAD_GLOBAL), G(OP_LOAD_LOCAL), G(OP_STORE_LOCAL) #include "commons.hpp" diff --git a/tests/SymTable.cpp b/tests/SymTable.cpp index 5aa9960..8f5b9e9 100644 --- a/tests/SymTable.cpp +++ b/tests/SymTable.cpp @@ -14,16 +14,5 @@ protected: TEST_CASE_METHOD(SymTableTest, "SymTable_declare_var") { - REQUIRE(std::nullopt == m_sym.find("hello")); - m_sym.declare_local("hello", 404, m_loc); - auto entry = m_sym.find("hello"); - - REQUIRE(std::nullopt != entry); - REQUIRE("hello" == entry->name()); - REQUIRE(404 == entry->addr()); - REQUIRE(false == entry->is_global()); - - REQUIRE_THROWS_AS(m_sym.declare_local("hello", 407, m_loc), - fk::symbol_error); }