diff --git a/doc/grammar.bnf b/doc/grammar.bnf index cf5950d..857a478 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -11,6 +11,9 @@ EXPR ::= | LAMBDA | BLOCK | ARRAY +| IMPORT +| SHORT_IMPORT +| NS VARDECL ::= opar decl ident EXPR cpar FUNDECL ::= opar decl opar ident* cpar BODY cpar FUNCALL ::= opar EXPR EXPR* cpar @@ -19,3 +22,6 @@ PARAMS ::= ident* BODY ::= EXPR* BLOCK ::= opar colon EXPR* cpar ARRAY ::= osquare EXPR* csquare +IMPORT ::= opar import string cpar +SHORT_IMPORT ::= opar decl import ident string cpar +NS ::= ident ns ident diff --git a/examples/mod.gri b/examples/mod.gri new file mode 100644 index 0000000..8549546 --- /dev/null +++ b/examples/mod.gri @@ -0,0 +1,4 @@ +($ @m './mod2.gri') + +(assert= 32 (m::twice 16)) +(assert= 3.14159 m::pi) \ No newline at end of file diff --git a/examples/mod2.gri b/examples/mod2.gri new file mode 100644 index 0000000..de37d38 --- /dev/null +++ b/examples/mod2.gri @@ -0,0 +1,2 @@ +($ (twice n) (* n 2)) +($ pi 3.14159) \ No newline at end of file diff --git a/meson.build b/meson.build index 809474b..417ef6e 100644 --- a/meson.build +++ b/meson.build @@ -32,6 +32,7 @@ grino_src = static_library('grino', 'src/SymTable.cpp', 'src/Loader.cpp', 'src/Addr.cpp', + 'src/Module.cpp', ]) grino_dep = declare_dependency(link_with: grino_src) diff --git a/src/Compiler.cpp b/src/Compiler.cpp index efe71ed..39bef19 100644 --- a/src/Compiler.cpp +++ b/src/Compiler.cpp @@ -1,5 +1,6 @@ #include "Compiler.hpp" #include "Program.hpp" +#include "Module.hpp" #include "SymTable.hpp" #include "src/opcodes.hpp" #include "StaticFunction.hpp" @@ -31,6 +32,25 @@ namespace grino } } break; + case NODE_IMPORT: { + std::string name = node->child(0).lock()->repr(); + name = name.substr(1, name.size() - 2); + + program.push_value(Value::make_string(node->loc(), name)); + program.push_instr(OPCODE_MK_MOD); + } break; + + case NODE_NS: { + auto mod = node->child(0).lock(); + auto var = node->child(1).lock()->repr(); + + compile(mod, program, sym); + + program.push_value(Value::make_string(node->loc(), var)); + + program.push_instr(OPCODE_LOAD_NS); + } break; + case NODE_BODY: { for (size_t i=0; isize(); i++) { diff --git a/src/Lexer.cpp b/src/Lexer.cpp index 9c9d34f..4be67d8 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -7,6 +7,8 @@ namespace grino : m_logger { logger } , m_loc {source_path, 1} { + add_text(NODE_NS, "::", false); + add_text(NODE_IMPORT, "@", false); add_text(NODE_COLON, ":", false); add_text(NODE_OSQUARE, "[", false); add_text(NODE_CSQUARE, "]", false); diff --git a/src/Loader.cpp b/src/Loader.cpp index ac2c0bd..72d156d 100644 --- a/src/Loader.cpp +++ b/src/Loader.cpp @@ -2,6 +2,8 @@ #include "Function.hpp" #include "src/config.in.hpp" #include +#include "Lexer.hpp" +#include "Parser.hpp" namespace grino { @@ -16,6 +18,51 @@ namespace grino { } + std::vector + Loader::dependencies(std::shared_ptr node) + { + std::vector deps; + + std::function)> + f = [&](std::shared_ptr n){ + if (n->type() == NODE_IMPORT) + { + std::string str = n->child(0).lock()->repr(); + deps.push_back(str.substr(1, str.size() - 2)); + } + else + { + for (size_t i=0; isize(); i++) + { + f(n->child(i).lock()); + } + } + }; + + f(node); + return deps; + } + + std::shared_ptr + Loader::user_module(std::filesystem::path path) + { + std::string source; + { + std::ifstream file { path }; + assert(file); + std::string line; + + while (std::getline(file, line)) + { source += line + (file.eof() ? "" : "\n"); } + } + + Logger logger; + Lexer lexer {logger, path}; + Parser parser {logger, lexer}; + + return parser.parse(source); + } + void Loader::load_libraries() { for (auto entry: std::filesystem::directory_iterator(GRINO_LIBDIR)) diff --git a/src/Loader.hpp b/src/Loader.hpp index 7da623f..cf6c01d 100644 --- a/src/Loader.hpp +++ b/src/Loader.hpp @@ -17,6 +17,11 @@ namespace grino VM& vm() const { return m_vm; } + std::vector + dependencies(std::shared_ptr node); + + std::shared_ptr user_module(std::filesystem::path path); + void load_libraries(); void load_library(std::filesystem::path path); diff --git a/src/Module.cpp b/src/Module.cpp new file mode 100644 index 0000000..ea66579 --- /dev/null +++ b/src/Module.cpp @@ -0,0 +1,64 @@ +#include "Module.hpp" +#include "Loader.hpp" +#include "Lexer.hpp" +#include "Parser.hpp" +#include "Compiler.hpp" + +namespace grino +{ + /*explicit*/ Module::Module(std::filesystem::path path, + std::string const& name /*= "" */) + : m_path { path } + , m_name { name == "" ? m_path.filename().stem().string() : name } + , m_program { std::make_shared()} + , m_sym_table { std::make_shared(m_logger)} + , m_vm { std::make_shared(m_logger, *m_program)} + { + } + + /*virtual*/ Module::~Module() + { + } + + void Module::load() + { + std::string source; + { + std::ifstream file { m_path }; + assert(file); + std::string line; + + while (std::getline(file, line)) + { source += line + (file.eof() ? "" : "\n"); } + } + + Lexer lexer {m_logger, m_path}; + Parser parser {m_logger, lexer}; + + auto ast = parser.parse(source); + Addr addr; + Compiler compiler {m_logger, addr}; + m_vm = std::make_shared(m_logger, *m_program); + + Loader loader {*m_vm, compiler, *m_sym_table}; + loader.load_libraries(); + + compiler.compile(ast, *m_program, *m_sym_table); + + m_vm->run(); + } + + std::shared_ptr Module::find(std::string const& name) + { + auto entry = m_sym_table->find_no_scope(name); + assert(entry); + assert(entry->is_object == false); + + return m_vm->local(entry->addr); + } + + std::shared_ptr Module::from_heap(size_t addr) + { + return m_vm->heap(addr); + } +} diff --git a/src/Module.hpp b/src/Module.hpp new file mode 100644 index 0000000..2f11802 --- /dev/null +++ b/src/Module.hpp @@ -0,0 +1,33 @@ +#ifndef grino_MODULE_HPP +#define grino_MODULE_HPP + +#include "commons.hpp" +#include "Program.hpp" +#include "SymTable.hpp" +#include "VM.hpp" + +namespace grino +{ + class Module + { + public: + explicit Module(std::filesystem::path path, std::string const& name=""); + virtual ~Module(); + + std::string name() const { return m_name; } + void load(); + + std::shared_ptr find(std::string const& name); + std::shared_ptr from_heap(size_t addr); + + private: + std::filesystem::path m_path; + std::string m_name; + Logger m_logger; + std::shared_ptr m_program; + std::shared_ptr m_sym_table; + std::shared_ptr m_vm; + }; +} + +#endif diff --git a/src/Node.hpp b/src/Node.hpp index 5657e17..1c48d28 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -23,7 +23,9 @@ G(NODE_BLOCK), \ G(NODE_ARRAY), \ G(NODE_FLOAT), \ - G(NODE_STRING) + G(NODE_STRING), \ + G(NODE_IMPORT), \ + G(NODE_NS) namespace grino { diff --git a/src/Parser.cpp b/src/Parser.cpp index aeb796e..5370476 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -118,11 +118,27 @@ namespace grino std::shared_ptr Parser::parse_expr() { + + if (type_is({NODE_IDENT, NODE_NS})) + { + return parse_ns(); + } + if (type_is(NODE_OSQUARE)) { return parse_array(); } + if (type_is({NODE_OPAR, NODE_DECL, NODE_IMPORT})) + { + return parse_short_import(); + } + + if (type_is({NODE_OPAR, NODE_IMPORT})) + { + return parse_import(); + } + if (type_is({NODE_OPAR, NODE_COLON})) { return parse_block(); @@ -294,4 +310,42 @@ namespace grino return node; } + + std::shared_ptr Parser::parse_import() + { + consume(NODE_OPAR); + auto node = consume(NODE_IMPORT); + node->add_child(consume(NODE_STRING)); + consume(NODE_CPAR); + return node; + } + + std::shared_ptr Parser::parse_ns() + { + auto node = make_node(NODE_NS); + node->add_child(consume(NODE_IDENT)); + consume(NODE_NS); + node->add_child(consume(NODE_IDENT)); + return node; + } + + std::shared_ptr Parser::parse_short_import() + { + consume(NODE_OPAR); + consume(NODE_DECL); + consume(NODE_IMPORT); + auto ident = consume(NODE_IDENT); + auto val = consume(NODE_STRING); + consume(NODE_CPAR); + + auto node = make_node(NODE_VARDECL); + node->add_child(ident); + + auto imp = make_node(NODE_IMPORT); + imp->add_child(val); + node->add_child(imp); + + return node; + } + } diff --git a/src/Parser.hpp b/src/Parser.hpp index 28b3baa..ff94e87 100644 --- a/src/Parser.hpp +++ b/src/Parser.hpp @@ -42,6 +42,9 @@ namespace grino std::shared_ptr parse_body(); std::shared_ptr parse_block(); std::shared_ptr parse_array(); + std::shared_ptr parse_import(); + std::shared_ptr parse_short_import(); + std::shared_ptr parse_ns(); }; } diff --git a/src/SymTable.cpp b/src/SymTable.cpp index d34a65e..f3583b0 100644 --- a/src/SymTable.cpp +++ b/src/SymTable.cpp @@ -57,6 +57,22 @@ namespace grino return entry; } + std::optional + SymTable::find_no_scope(std::string const& name) + { + std::optional entry; + + for (size_t i=0; i find(std::string const& name, size_t scope); + std::optional find_no_scope(std::string const& name); void purge(size_t scope); diff --git a/src/VM.cpp b/src/VM.cpp index 3744d0e..d1e37b8 100644 --- a/src/VM.cpp +++ b/src/VM.cpp @@ -1,5 +1,6 @@ #include "VM.hpp" #include "src/Value.hpp" +#include "Module.hpp" #include "src/opcodes.hpp" #include @@ -32,6 +33,45 @@ namespace grino switch (instr.opcode) { + case OPCODE_MK_MOD: { + auto path = std::filesystem::path(program() + .constant(pop()) + ->as_string()); + + auto mod = std::make_shared(path); + mod->load(); + size_t addr = heap_size(); + + Loc loc {"???", 0}; + set_heap(addr, Value::make_module(loc, mod)); + + auto ref = Value::make_ref(loc, addr); + push(program().push_constant(ref)); + + m_pc++; + } break; + + case OPCODE_LOAD_NS: { + std::string var = program().constant(pop())->as_string(); + size_t ref_val = program().constant(pop())->as_ref(); + auto mod = heap(ref_val); + + auto val = mod->as_module()->find(var); + + if (val->type() == TYPE_REF) + { + size_t heap_addr = val->as_ref(); + auto heap_val = mod->as_module()->from_heap(heap_addr); + size_t addr = heap_size(); + set_heap(addr, heap_val); + val = Value::make_ref(heap_val->loc(), addr); + } + + push(program().push_constant(val)); + + m_pc++; + } break; + case OPCODE_MK_ARRAY: { size_t const N = *instr.param; diff --git a/src/Value.cpp b/src/Value.cpp index 523bfaa..709f133 100644 --- a/src/Value.cpp +++ b/src/Value.cpp @@ -1,6 +1,7 @@ #include "Value.hpp" #include "Program.hpp" #include "src/types.hpp" +#include "Module.hpp" namespace grino { @@ -84,6 +85,10 @@ namespace grino return Value::make_int(loc, val->as_int()); } break; + case TYPE_STRING: { + return Value::make_string(loc, val->as_string()); + } break; + case TYPE_REF: { return Value::make_ref(loc, val->as_ref()); } break; @@ -115,11 +120,26 @@ namespace grino return value; } + /*static*/ + std::shared_ptr Value::make_module(Loc const& loc, + std::shared_ptr val) + { + auto value = std::make_shared(loc); + value->m_type = TYPE_MODULE; + value->m_module_val = val; + return value; + } + std::shared_ptr Value::as_program() const { return m_program_val; } + std::shared_ptr Value::as_module() const + { + return m_module_val; + } + std::string Value::string() const { switch (m_type) @@ -132,6 +152,7 @@ namespace grino case TYPE_FUNCTION: return ""; case TYPE_REF: return "&" + std::to_string(*m_ref_val); case TYPE_PROGRAM: return ""; + case TYPE_MODULE: return "name() + ">"; case TYPE_ARRAY: { std::stringstream ss; ss << "["; @@ -166,6 +187,7 @@ namespace grino return std::fabs(*m_float_val - *other.m_float_val) < FLOAT_APPROX; case TYPE_REF: return *m_ref_val == *other.m_ref_val; case TYPE_PROGRAM: return false; + case TYPE_MODULE: return false; default: std::cerr << "cannot compare equality with value " diff --git a/src/Value.hpp b/src/Value.hpp index 88ef268..3472f7b 100644 --- a/src/Value.hpp +++ b/src/Value.hpp @@ -9,6 +9,7 @@ namespace grino { class Program; + class Module; using val_array_t = std::vector>; @@ -40,6 +41,9 @@ namespace grino static std::shared_ptr make_string(Loc const& loc, std::string const& val); + static std::shared_ptr make_module(Loc const& loc, + std::shared_ptr val); + explicit Value(Loc const& loc); virtual ~Value() = default; @@ -54,6 +58,7 @@ namespace grino val_array_t& as_array() { return *m_array_val; } val_array_t const& as_array() const { return *m_array_val; } std::string const& as_string() const { return *m_string_val; } + std::shared_ptr as_module() const; std::string string() const; bool equals(Value const& other) const; @@ -69,6 +74,7 @@ namespace grino std::optional m_ref_val; std::optional m_array_val; std::optional m_string_val; + std::shared_ptr m_module_val; }; } diff --git a/src/main.cpp b/src/main.cpp index bfdce7e..d669201 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,11 +12,18 @@ #include "Loader.hpp" #include "Addr.hpp" -void run(char** argv, bool debug_mode) + +void run(char* const source_name, bool debug_mode) { std::string source; { - std::ifstream file { argv[optind] }; + if (!std::filesystem::exists(source_name)) + { + std::cerr << "file " << source_name << " doesnt exists" << std::endl; + abort(); + } + + std::ifstream file { source_name }; assert(file); std::string line; @@ -25,7 +32,7 @@ void run(char** argv, bool debug_mode) } grino::Logger logger; - grino::Lexer lexer {logger, argv[optind]}; + grino::Lexer lexer {logger, source_name}; grino::Parser parser {logger, lexer}; auto ast = parser.parse(source); @@ -46,6 +53,11 @@ void run(char** argv, bool debug_mode) grino::Loader loader {vm, compiler, sym_table}; loader.load_libraries(); + for (auto dep: loader.dependencies(ast)) + { + // TODO: import them + } + compiler.compile(ast, program, sym_table); if (debug_mode) @@ -108,13 +120,13 @@ int main(int argc, char** argv) { if (debug_mode) { - run(argv, debug_mode); + run(argv[optind], debug_mode); } else { try { - run(argv, debug_mode); + run(argv[optind], debug_mode); } catch(std::exception const& err) { diff --git a/src/opcodes.hpp b/src/opcodes.hpp index 353e721..d106ed8 100644 --- a/src/opcodes.hpp +++ b/src/opcodes.hpp @@ -17,7 +17,9 @@ G(OPCODE_NOT), \ G(OPCODE_RET), \ G(OPCODE_MK_FUN), \ - G(OPCODE_MK_ARRAY), + G(OPCODE_MK_ARRAY), \ + G(OPCODE_MK_MOD), \ + G(OPCODE_LOAD_NS), namespace grino { diff --git a/src/types.hpp b/src/types.hpp index ebf44d9..121d407 100644 --- a/src/types.hpp +++ b/src/types.hpp @@ -12,7 +12,8 @@ G(TYPE_PROGRAM), \ G(TYPE_FLOAT), \ G(TYPE_ARRAY), \ - G(TYPE_STRING) + G(TYPE_STRING), \ + G(TYPE_MODULE) namespace grino diff --git a/tests/Lexer.cpp b/tests/Lexer.cpp index 82f5665..b7b73bb 100644 --- a/tests/Lexer.cpp +++ b/tests/Lexer.cpp @@ -129,3 +129,13 @@ TEST_CASE_METHOD(LexerTest, "Lexer_strings") test_next(lexer, "STRING[''bim'']"); test_end(lexer); } + +TEST_CASE_METHOD(LexerTest, "Lexer_import") +{ + grino::Lexer lexer {m_logger, "tests/lexer"}; + + lexer.scan(" @ :: "); + test_next(lexer, "IMPORT"); + test_next(lexer, "NS"); + test_end(lexer); +} diff --git a/tests/Parser.cpp b/tests/Parser.cpp index e12b127..1e79f90 100644 --- a/tests/Parser.cpp +++ b/tests/Parser.cpp @@ -105,3 +105,15 @@ TEST_CASE_METHOD(ParserTest, "Parser_string") test_parse("MODULE(BLOCK(ARRAY(STRING['bim !'],FLOAT[28.5],IDENT[salut])))", "(: ['bim !' 28.5 salut] )"); } + +TEST_CASE_METHOD(ParserTest, "Parser_import") +{ + test_parse("MODULE(IMPORT(STRING['./salut']))", + "(@ './salut')"); + + test_parse("MODULE(NS(IDENT[hello],IDENT[world]))", + " hello::world "); + + test_parse("MODULE(VARDECL(IDENT[bim],IMPORT(STRING['hello'])))", + "($ @bim 'hello')"); +}