diff --git a/doc/grammar.bnf b/doc/grammar.bnf index a7e193b..044280f 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -3,4 +3,6 @@ EXPR ::= bool | ident | VARDECL +| FUNCALL VARDECL ::= opar decl ident EXPR cpar +FUNCALL ::= opar ident EXPR* cpar diff --git a/lib/core.cpp b/lib/core.cpp new file mode 100644 index 0000000..374fcb4 --- /dev/null +++ b/lib/core.cpp @@ -0,0 +1,19 @@ +#include "../src/Loader.hpp" + +extern "C" void lib(grino::Loader& loader) +{ + loader.add_native("dump", [](auto args){ + std::string sep; + std::stringstream ss; + + for (auto arg: args) + { + ss << sep << arg->string(); + sep = " "; + } + + std::cout << ss.str() << std::endl; + + return grino::Value::make_nil(); + }); +} diff --git a/meson.build b/meson.build index 8a8e406..9641ad9 100644 --- a/meson.build +++ b/meson.build @@ -6,8 +6,11 @@ project('grino', 'cpp_std=c++17' ]) +grino_libdir = get_option('prefix') / get_option('libdir') / 'grino' + conf = configuration_data() conf.set('version', meson.project_version()) +conf.set('libdir', grino_libdir) configure_file(input: 'src/config.in.hpp', output: 'config.hpp', @@ -24,11 +27,23 @@ grino_src = static_library('grino', 'src/Program.cpp', 'src/VM.cpp', 'src/Value.cpp', + 'src/Function.cpp', 'src/SymTable.cpp', + 'src/Loader.cpp', ]) grino_dep = declare_dependency(link_with: grino_src) +shared_library('grino_core', + sources: [ + 'lib/core.cpp' + ], + dependencies: [ + grino_dep + ], + install: true, + install_dir: grino_libdir) + executable('grino', sources: [ 'src/main.cpp' diff --git a/src/Compiler.cpp b/src/Compiler.cpp index 74beece..e5f414c 100644 --- a/src/Compiler.cpp +++ b/src/Compiler.cpp @@ -28,6 +28,16 @@ namespace grino } } break; + case NODE_FUNCALL: { + + for (size_t i=0; isize(); i++) + { + compile(node->child(i).lock(), program); + } + + program.push_instr(OPCODE_CALL, node->size() - 1); + } break; + case NODE_BOOL: { std::string repr = node->repr(); auto value = Value::make_bool(repr == "true"); @@ -58,7 +68,14 @@ namespace grino m_logger.log(LOG_ERROR, node->loc(), ss.str()); } - program.push_instr(OPCODE_LOAD_LOCAL, entry->addr); + if (entry->is_object) + { + program.push_instr(OPCODE_LOAD_OBJ, entry->addr); + } + else + { + program.push_instr(OPCODE_LOAD_LOCAL, entry->addr); + } } break; default: diff --git a/src/Function.cpp b/src/Function.cpp new file mode 100644 index 0000000..02af6dc --- /dev/null +++ b/src/Function.cpp @@ -0,0 +1,20 @@ +#include "Function.hpp" +#include "Value.hpp" + +namespace grino +{ + /*explicit*/ Function::Function(native_t native) + : m_native { native } + { + } + + /*virtual*/ Function::~Function() + { + } + + value_t Function::call(args_t args) + { + assert(m_native); + return m_native(args); + } +} diff --git a/src/Function.hpp b/src/Function.hpp new file mode 100644 index 0000000..1ac67ae --- /dev/null +++ b/src/Function.hpp @@ -0,0 +1,27 @@ +#ifndef grino_FUNCTION_HPP +#define grino_FUNCTION_HPP + +#include "commons.hpp" + +namespace grino +{ + class Value; + + using value_t = std::shared_ptr; + using args_t = std::vector; + using native_t = std::function; + + class Function + { + public: + explicit Function(native_t native); + virtual ~Function(); + + value_t call(args_t args); + + private: + native_t m_native; + }; +} + +#endif diff --git a/src/Loader.cpp b/src/Loader.cpp new file mode 100644 index 0000000..1bb1555 --- /dev/null +++ b/src/Loader.cpp @@ -0,0 +1,45 @@ +#include "Loader.hpp" +#include "Function.hpp" +#include "src/config.in.hpp" +#include + +namespace grino +{ + /*explicit*/ Loader::Loader(VM& vm, SymTable& sym_table) + : m_vm { vm } + , m_sym_table { sym_table } + { + } + + /*virtual*/ Loader::~Loader() + { + } + + void Loader::load_libraries() + { + for (auto entry: std::filesystem::directory_iterator(GRINO_LIBDIR)) + { + if (entry.path().has_extension() + && entry.path().extension() == ".so") + { + load_library(entry.path()); + } + } + } + + void Loader::load_library(std::filesystem::path path) + { + void* handle = dlopen(path.string().c_str(), RTLD_NOW); + typedef void(*libfun)(Loader&); + libfun f = (libfun) dlsym(handle, "lib"); + + f(*this); + } + + void Loader::add_native(std::string const& name, native_t native) + { + size_t addr = m_vm.heap_size(); + m_vm.set_heap(addr, grino::Value::make_native_function(native)); + m_sym_table.declare_object(grino::Loc {"???", 1}, name, addr); + } +} diff --git a/src/Loader.hpp b/src/Loader.hpp new file mode 100644 index 0000000..4f99d94 --- /dev/null +++ b/src/Loader.hpp @@ -0,0 +1,26 @@ +#ifndef grino_LOADER_HPP +#define grino_LOADER_HPP + +#include "commons.hpp" +#include "VM.hpp" +#include "SymTable.hpp" + +namespace grino +{ + class Loader + { + public: + explicit Loader(VM& vm, SymTable& sym_table); + virtual ~Loader(); + + void load_libraries(); + void load_library(std::filesystem::path path); + void add_native(std::string const& name, native_t native); + + private: + VM& m_vm; + SymTable& m_sym_table; + }; +} + +#endif diff --git a/src/Node.hpp b/src/Node.hpp index 7b7fb52..30d2bfa 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -8,6 +8,7 @@ G(NODE_MODULE), \ G(NODE_BOOL), \ G(NODE_VARDECL), \ + G(NODE_FUNCALL), \ G(NODE_IDENT), \ G(NODE_OPAR), \ G(NODE_CPAR), \ diff --git a/src/Parser.cpp b/src/Parser.cpp index 68d81a2..33600a4 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -117,7 +117,12 @@ namespace grino std::shared_ptr Parser::parse_expr() { - if (type_is(NODE_OPAR)) + if (type_is({NODE_OPAR, NODE_IDENT})) + { + return parse_funcall(); + } + + if (type_is({NODE_OPAR, NODE_DECL})) { return parse_vardecl(); } @@ -151,4 +156,21 @@ namespace grino return node; } + + std::shared_ptr Parser::parse_funcall() + { + consume(NODE_OPAR); + + auto node = make_node(NODE_FUNCALL); + node->add_child(consume(NODE_IDENT)); + + while (!type_is(NODE_CPAR)) + { + node->add_child(parse_expr()); + } + + consume(NODE_CPAR); + + return node; + } } diff --git a/src/Parser.hpp b/src/Parser.hpp index fe89d0a..54891a5 100644 --- a/src/Parser.hpp +++ b/src/Parser.hpp @@ -35,6 +35,7 @@ namespace grino std::shared_ptr parse_module(); std::shared_ptr parse_expr(); std::shared_ptr parse_vardecl(); + std::shared_ptr parse_funcall(); }; } diff --git a/src/SymTable.cpp b/src/SymTable.cpp index a220e7c..9d0df11 100644 --- a/src/SymTable.cpp +++ b/src/SymTable.cpp @@ -23,11 +23,19 @@ namespace grino SymEntry entry; entry.addr = addr; entry.name = name; - entry.is_global = false; + entry.is_object = false; m_entries.push_back(entry); } + void SymTable::declare_object(Loc const& loc, + std::string const& name, + size_t addr) + { + declare(loc, name, addr); + m_entries.back().is_object = true; + } + std::optional SymTable::find(std::string const& name) { for (size_t i=0; i find(std::string const& name); std::string string() const; diff --git a/src/VM.cpp b/src/VM.cpp index dc76f03..ebb054f 100644 --- a/src/VM.cpp +++ b/src/VM.cpp @@ -48,6 +48,34 @@ namespace grino m_pc++; } break; + case OPCODE_LOAD_OBJ: { + size_t addr = *instr.param; + auto ref = Value::make_ref(addr); + push(program.push_constant(ref)); + m_pc++; + } break; + + case OPCODE_CALL: { + size_t const N = *instr.param; + + std::vector> args; + + for (size_t i=0; ias_ref(); + auto fun = heap(ref)->as_function(); + + auto ret = fun->call(args); + + push(program.push_constant(ret)); + + m_pc++; + + } break; + default: std::cerr << "cannot execute unknown opcode " << OpcodeTypeStr[instr.opcode] @@ -69,6 +97,29 @@ namespace grino return ss.str(); } + + std::shared_ptr VM::local(size_t addr) const + { + return m_frames.back().locals.at(addr); + } + + void VM::set_local(size_t addr, + std::shared_ptr value) + { + m_frames.back().locals[addr] = value; + } + + std::shared_ptr VM::heap(size_t addr) const + { + return m_heap.at(addr); + } + + void VM::set_heap(size_t addr, + std::shared_ptr value) + { + m_heap[addr] = value; + } + void VM::push(size_t addr) { if (m_sp >= STACK_SIZE) @@ -97,16 +148,4 @@ namespace grino size_t addr = m_stack[m_sp - 1]; return addr; } - - std::shared_ptr VM::local(size_t addr) const - { - return m_frames.back().locals.at(addr); - } - - void VM::set_local(size_t addr, - std::shared_ptr value) - { - m_frames.back().locals[addr] = value; - } - } diff --git a/src/VM.hpp b/src/VM.hpp index 86b9ca8..49845da 100644 --- a/src/VM.hpp +++ b/src/VM.hpp @@ -21,12 +21,23 @@ namespace grino explicit VM(Logger& logger); virtual ~VM(); + size_t heap_size() const { return m_heap.size(); } + void run(Program& program); std::string string() const; + + std::shared_ptr local(size_t addr) const; + void set_local(size_t addr, + std::shared_ptr value); + + std::shared_ptr heap(size_t addr) const; + void set_heap(size_t addr, + std::shared_ptr value); private: Logger& m_logger; std::array m_stack; + std::unordered_map> m_heap; std::vector m_frames; size_t m_sp; /* stack pointer */ @@ -36,9 +47,8 @@ namespace grino void push(size_t addr); size_t pop(); size_t top(); - std::shared_ptr local(size_t addr) const; - void set_local(size_t addr, - std::shared_ptr value); + + }; } diff --git a/src/Value.cpp b/src/Value.cpp index 4061f63..fa34411 100644 --- a/src/Value.cpp +++ b/src/Value.cpp @@ -17,13 +17,31 @@ namespace grino return value; } + /*static*/ + std::shared_ptr Value::make_native_function(native_t val) + { + auto value = std::make_shared(); + value->m_type = TYPE_FUNCTION; + value->m_function_val = std::make_shared(val); + return value; + } + + /*static*/ std::shared_ptr Value::make_ref(size_t val) + { + auto value = std::make_shared(); + value->m_type = TYPE_REF; + value->m_ref_val = val; + return value; + } + std::string Value::string() const { switch (m_type) { case TYPE_NIL: return ""; case TYPE_BOOL: return *m_bool_val ? "true" : "false"; - + case TYPE_FUNCTION: return ""; + case TYPE_REF: return "&" + std::to_string(*m_ref_val); default: std::cerr << "cannot stringify value " << TypeTypeStr[m_type] << std::endl; @@ -39,6 +57,7 @@ namespace grino { case TYPE_NIL: return true; case TYPE_BOOL: return *m_bool_val == *other.m_bool_val; + case TYPE_REF: return *m_ref_val == *other.m_ref_val; default: std::cerr << "cannot compare equality with value " diff --git a/src/Value.hpp b/src/Value.hpp index 88fe4f2..87bae81 100644 --- a/src/Value.hpp +++ b/src/Value.hpp @@ -3,6 +3,7 @@ #include "commons.hpp" #include "types.hpp" +#include "Function.hpp" namespace grino { @@ -11,12 +12,16 @@ namespace grino public: static std::shared_ptr make_nil(); static std::shared_ptr make_bool(bool val); + static std::shared_ptr make_native_function(native_t val); + static std::shared_ptr make_ref(size_t val); explicit Value() = default; virtual ~Value() = default; TypeType type() const { return m_type; } bool as_bool() const { return *m_bool_val; } + std::shared_ptr as_function() const { return m_function_val; } + size_t as_ref() const { return *m_ref_val; } std::string string() const; bool equals(Value const& other) const; @@ -24,6 +29,8 @@ namespace grino private: TypeType m_type = TYPE_NIL; std::optional m_bool_val; + std::shared_ptr m_function_val; + std::optional m_ref_val; }; } diff --git a/src/config.in.hpp b/src/config.in.hpp index dcba94a..acfdfe0 100644 --- a/src/config.in.hpp +++ b/src/config.in.hpp @@ -1,6 +1,9 @@ #ifndef grino_CONFIG_IN_HPP #define grino_CONFIG_IN_HPP +#include + #define GRINO_VERSION "@version@" +#define GRINO_LIBDIR std::filesystem::path {"@libdir@"} #endif diff --git a/src/main.cpp b/src/main.cpp index e05604d..09578bd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,7 @@ #include "VM.hpp" #include "Logger.hpp" #include "src/SymTable.hpp" +#include "Loader.hpp" int main(int argc, char** argv) { @@ -74,7 +75,12 @@ int main(int argc, char** argv) std::cout << "--- ast ---" << std::endl; std::cout << ast->string() << std::endl; } + grino::SymTable sym_table {logger}; + grino::VM vm {logger}; + + grino::Loader loader {vm, sym_table}; + loader.load_libraries(); grino::Compiler compiler {logger, sym_table}; grino::Program program; @@ -86,8 +92,6 @@ int main(int argc, char** argv) std::cout << program.string() << std::endl; } - grino::VM vm {logger}; - vm.run(program); if (debug_mode) diff --git a/src/opcodes.hpp b/src/opcodes.hpp index d20c591..9ad6f2c 100644 --- a/src/opcodes.hpp +++ b/src/opcodes.hpp @@ -8,7 +8,11 @@ G(OPCODE_LOAD_CONST), \ G(OPCODE_POP), \ G(OPCODE_LOAD_LOCAL), \ - G(OPCODE_STORE_LOCAL) + G(OPCODE_STORE_LOCAL), \ + G(OPCODE_LOAD_OBJ), \ + G(OPCODE_STORE_OBJ), \ + G(OPCODE_CALL), \ + G(OPCODE_RET), namespace grino { diff --git a/src/types.hpp b/src/types.hpp index 0350d8d..d989f6d 100644 --- a/src/types.hpp +++ b/src/types.hpp @@ -5,7 +5,9 @@ #define TYPES(G) \ G(TYPE_NIL), \ - G(TYPE_BOOL) + G(TYPE_BOOL), \ + G(TYPE_FUNCTION), \ + G(TYPE_REF) namespace grino { diff --git a/tests/Parser.cpp b/tests/Parser.cpp index d0914ed..72ac39e 100644 --- a/tests/Parser.cpp +++ b/tests/Parser.cpp @@ -39,3 +39,12 @@ TEST_CASE_METHOD(ParserTest, "Parser_vardecl") test_parse("MODULE(VARDECL(IDENT[hello],IDENT[world]))", "($ hello world)"); } + +TEST_CASE_METHOD(ParserTest, "Parser_funcall") +{ + test_parse("MODULE(FUNCALL(IDENT[hello]))", + "(hello)"); + + test_parse("MODULE(FUNCALL(IDENT[f],BOOL[false],BOOL[true]))", + "(f false true)"); +}