From e12ff9694332fbc1d6a48042f4f99e85cfb351f0 Mon Sep 17 00:00:00 2001 From: bog Date: Fri, 15 Sep 2023 09:25:40 +0200 Subject: [PATCH] ADD: basic type checker. --- lib/commons.hpp | 6 +- lib/core.cpp | 127 +++++++++++--- meson.build | 5 + src/Compiler.cpp | 7 +- src/Compiler.hpp | 10 +- src/Function.cpp | 4 + src/Function.hpp | 5 + src/Lexer.cpp | 2 +- src/Loader.cpp | 35 +++- src/Loader.hpp | 12 +- src/Logger.hpp | 2 +- src/Module.cpp | 4 + src/Parser.cpp | 4 +- src/Parser.hpp | 2 +- src/Program.cpp | 2 +- src/Prototype.cpp | 65 +++++++ src/Prototype.hpp | 48 ++++++ src/StaticFunction.cpp | 4 +- src/StaticFunction.hpp | 6 +- src/StaticPass.cpp | 183 ++++++++++++++++++++ src/StaticPass.hpp | 32 ++++ src/SymTable.cpp | 11 +- src/SymTable.hpp | 6 + src/VM.cpp | 4 +- src/Value.cpp | 13 +- src/Value.hpp | 16 +- src/main.cpp | 111 +++++++----- src/opcodes.hpp | 2 +- tests/StaticPass.cpp | 379 +++++++++++++++++++++++++++++++++++++++++ 29 files changed, 1012 insertions(+), 95 deletions(-) create mode 100644 src/Prototype.cpp create mode 100644 src/Prototype.hpp create mode 100644 src/StaticPass.cpp create mode 100644 src/StaticPass.hpp create mode 100644 tests/StaticPass.cpp diff --git a/lib/commons.hpp b/lib/commons.hpp index 5d91bc0..8bf2725 100644 --- a/lib/commons.hpp +++ b/lib/commons.hpp @@ -1,5 +1,5 @@ #include "../src/Loader.hpp" -#include "src/Logger.hpp" -#include "src/Value.hpp" -#include "src/opcodes.hpp" +#include "../src/Logger.hpp" +#include "../src/Value.hpp" +#include "../src/opcodes.hpp" #include "../src/Module.hpp" diff --git a/lib/core.cpp b/lib/core.cpp index 6076f2f..dbc62e4 100644 --- a/lib/core.cpp +++ b/lib/core.cpp @@ -1,9 +1,11 @@ #include "../src/Loader.hpp" #include "src/Logger.hpp" +#include "src/Prototype.hpp" #include "src/Value.hpp" #include "src/opcodes.hpp" #include "../src/Module.hpp" #include "cast.hpp" +#include "src/types.hpp" GRINO_ERROR(assertion_error); @@ -42,7 +44,11 @@ extern "C" void lib_collection(grino::Loader& loader) }; return f(args[0], idxs); - }); + }, std::make_shared(std::vector{ + grino::TypeSlot {grino::HINT_CAT_PARAM, grino::TYPE_INT}, + grino::TypeSlot {grino::HINT_CAT_PARAM, grino::TYPE_NIL, grino::HINT_ANY}, + grino::TypeSlot {grino::HINT_CAT_RETURN, grino::TYPE_NIL, grino::HINT_ANY}, + })); loader.add_native("empty?", [&loader](auto args){ auto val = args[0]; @@ -140,11 +146,21 @@ extern "C" void lib_flow_control(grino::Loader& loader) compiler.compile(else_node, prog, sym); prog.set_param(br, prog.size()); - }); + }, std::make_shared(std::vector {{ + grino::TypeSlot {grino::HINT_CAT_RETURN, + grino::TYPE_NIL, + grino::HINT_NONE}, + }})); } extern "C" void lib_int(grino::Loader& loader) { + auto var_proto = + std::make_shared(std::vector{ + grino::TypeSlot {grino::HINT_CAT_VARIADIC_PARAM, grino::TYPE_INT}, + grino::TypeSlot {grino::HINT_CAT_RETURN, grino::TYPE_INT}, + }); + loader.add_native("+", [](auto args){ int result = 0; @@ -161,7 +177,7 @@ extern "C" void lib_int(grino::Loader& loader) } return grino::Value::make_int(loc, result); - }); + }, var_proto); loader.add_native("*", [](auto args){ int result = 1; @@ -179,7 +195,7 @@ extern "C" void lib_int(grino::Loader& loader) } return grino::Value::make_int(loc, result); - }); + }, var_proto); loader.add_native("-", [](auto args){ grino::Loc loc {"???", 0}; @@ -206,7 +222,7 @@ extern "C" void lib_int(grino::Loader& loader) } return grino::Value::make_int(loc, result); - }); + }, var_proto); loader.add_native("/", [](auto args){ grino::Loc loc {"???", 0}; @@ -233,7 +249,7 @@ extern "C" void lib_int(grino::Loader& loader) } return grino::Value::make_int(loc, result); - }); + }, var_proto); loader.add_native("%", [](auto args){ grino::Loc loc {"???", 0}; @@ -260,7 +276,7 @@ extern "C" void lib_int(grino::Loader& loader) } return grino::Value::make_int(loc, result); - }); + }, var_proto); loader.add_native("^", [](auto args){ grino::Loc loc {"???", 0}; @@ -287,11 +303,17 @@ extern "C" void lib_int(grino::Loader& loader) } return grino::Value::make_int(loc, result); - }); + }, var_proto); } extern "C" void lib_float(grino::Loader& loader) { + auto var_proto = + std::make_shared(std::vector{ + grino::TypeSlot {grino::HINT_CAT_VARIADIC_PARAM, grino::TYPE_FLOAT}, + grino::TypeSlot {grino::HINT_CAT_RETURN, grino::TYPE_INT}, + }); + loader.add_native("+.", [](auto args){ float result = 0; @@ -308,7 +330,7 @@ extern "C" void lib_float(grino::Loader& loader) } return grino::Value::make_float(loc, result); - }); + }, var_proto); loader.add_native("*.", [](auto args){ float result = 1; @@ -326,7 +348,7 @@ extern "C" void lib_float(grino::Loader& loader) } return grino::Value::make_float(loc, result); - }); + }, var_proto); loader.add_native("-.", [](auto args){ grino::Loc loc {"???", 0}; @@ -353,7 +375,7 @@ extern "C" void lib_float(grino::Loader& loader) } return grino::Value::make_float(loc, result); - }); + }, var_proto); loader.add_native("/.", [](auto args){ grino::Loc loc {"???", 0}; @@ -380,7 +402,7 @@ extern "C" void lib_float(grino::Loader& loader) } return grino::Value::make_float(loc, result); - }); + }, var_proto); loader.add_native("%.", [](auto args){ grino::Loc loc {"???", 0}; @@ -407,7 +429,7 @@ extern "C" void lib_float(grino::Loader& loader) } return grino::Value::make_float(loc, result); - }); + }, var_proto); loader.add_native("^.", [](auto args){ grino::Loc loc {"???", 0}; @@ -434,11 +456,18 @@ extern "C" void lib_float(grino::Loader& loader) } return grino::Value::make_float(loc, result); - }); + }, var_proto); } extern "C" void lib_cmp(grino::Loader& loader) { + auto var_proto = + std::make_shared(std::vector{ + grino::TypeSlot {grino::HINT_CAT_PARAM, grino::TYPE_INT}, + grino::TypeSlot {grino::HINT_CAT_PARAM, grino::TYPE_INT}, + grino::TypeSlot {grino::HINT_CAT_RETURN, grino::TYPE_BOOL}, + }); + loader.add_native("<", [](auto args){ if (args[0]->type() == grino::TYPE_INT) { @@ -456,7 +485,7 @@ extern "C" void lib_cmp(grino::Loader& loader) } assert(0); - }); + }, var_proto); loader.add_native("<=", [](auto args){ if (args[0]->type() == grino::TYPE_INT) @@ -475,7 +504,7 @@ extern "C" void lib_cmp(grino::Loader& loader) } assert(0); - }); + }, var_proto); loader.add_native(">", [](auto args){ if (args[0]->type() == grino::TYPE_INT) @@ -494,7 +523,7 @@ extern "C" void lib_cmp(grino::Loader& loader) } assert(0); - }); + }, var_proto); loader.add_native(">=", [](auto args){ if (args[0]->type() == grino::TYPE_INT) @@ -513,21 +542,33 @@ extern "C" void lib_cmp(grino::Loader& loader) } assert(0); - }); + }, var_proto); loader.add_native("ne?", [](auto args){ auto lhs = args[0]; auto rhs = args[1]; return grino::Value::make_bool(args[0]->loc(), !lhs->equals(*rhs)); - }); + }, std::make_shared(std::vector {{ + grino::TypeSlot {grino::HINT_CAT_PARAM, + grino::TYPE_NIL, + grino::HINT_ANY}, + grino::TypeSlot {grino::HINT_CAT_PARAM, + grino::TYPE_NIL, + grino::HINT_SAME}, + grino::TypeSlot {grino::HINT_CAT_RETURN, + grino::TYPE_BOOL} + }})); } extern "C" void lib_bool(grino::Loader& loader) { loader.add_native("not", [](auto args){ return grino::Value::make_bool(args[0]->loc(), !args[0]->as_bool()); - }); + }, std::make_shared(std::vector {{ + grino::TypeSlot {grino::HINT_CAT_PARAM, grino::TYPE_BOOL}, + grino::TypeSlot {grino::HINT_CAT_RETURN, grino::TYPE_BOOL} + }})); loader.add_static("and", [](auto& compiler, auto node, auto& program, @@ -557,7 +598,14 @@ extern "C" void lib_bool(grino::Loader& loader) program.push_instr(grino::OPCODE_LOAD_CONST, addr); program.set_param(to_end, program.size()); - }); + }, std::make_shared(std::vector {{ + grino::TypeSlot {grino::HINT_CAT_VARIADIC_PARAM, + grino::TYPE_BOOL, + grino::HINT_NONE}, + grino::TypeSlot {grino::HINT_CAT_RETURN, + grino::TYPE_BOOL, + grino::HINT_NONE}, + }})); loader.add_static("or", [](auto& compiler, auto node, auto& program, auto& sym){ @@ -587,7 +635,14 @@ extern "C" void lib_bool(grino::Loader& loader) program.push_instr(grino::OPCODE_LOAD_CONST, addr); program.set_param(to_end, program.size()); - }); + }, std::make_shared(std::vector {{ + grino::TypeSlot {grino::HINT_CAT_VARIADIC_PARAM, + grino::TYPE_BOOL, + grino::HINT_NONE}, + grino::TypeSlot {grino::HINT_CAT_RETURN, + grino::TYPE_BOOL, + grino::HINT_NONE}, + }})); } extern "C" void lib_assert(grino::Loader& loader) @@ -606,7 +661,10 @@ extern "C" void lib_assert(grino::Loader& loader) } return grino::Value::make_bool(args.front()->loc(), true); - }); + }, std::make_shared(std::vector {{ + grino::TypeSlot {grino::HINT_CAT_PARAM, grino::TYPE_BOOL}, + grino::TypeSlot {grino::HINT_CAT_RETURN, grino::TYPE_NIL} + }})); loader.add_native("assert=", [](auto args){ auto lhs = args.front(); @@ -705,7 +763,17 @@ extern "C" void lib(grino::Loader& loader) } program.push_value(grino::Value::make_nil(node->loc())); - }); + }, std::make_shared(std::vector {{ + grino::TypeSlot {grino::HINT_CAT_PARAM, + grino::TYPE_NIL, + grino::HINT_ANY}, + grino::TypeSlot {grino::HINT_CAT_PARAM, + grino::TYPE_NIL, + grino::HINT_SAME}, + grino::TypeSlot {grino::HINT_CAT_RETURN, + grino::TYPE_NIL, + grino::HINT_NONE} + }})); loader.add_native("eq?", [](auto args){ auto lhs = args.front(); @@ -721,5 +789,14 @@ extern "C" void lib(grino::Loader& loader) } return grino::Value::make_bool(args.front()->loc(), true); - }); + }, std::make_shared(std::vector {{ + grino::TypeSlot {grino::HINT_CAT_PARAM, + grino::TYPE_NIL, + grino::HINT_ANY}, + grino::TypeSlot {grino::HINT_CAT_PARAM, + grino::TYPE_NIL, + grino::HINT_SAME}, + grino::TypeSlot {grino::HINT_CAT_RETURN, + grino::TYPE_BOOL} + }})); } diff --git a/meson.build b/meson.build index 20a4586..faa0578 100644 --- a/meson.build +++ b/meson.build @@ -35,6 +35,8 @@ grino_src = shared_library('grino', 'src/Loader.cpp', 'src/Addr.cpp', 'src/Module.cpp', + 'src/StaticPass.cpp', + 'src/Prototype.cpp', ], install: true) pkg = import('pkgconfig') @@ -62,6 +64,8 @@ install_headers( 'src/Loader.hpp', 'src/Addr.hpp', 'src/Module.hpp', + 'src/StaticPass.hpp', + 'src/Prototype.hpp', ], subdir: 'grino' ) @@ -101,6 +105,7 @@ executable('grino-tests', 'tests/main.cpp', 'tests/Lexer.cpp', 'tests/Parser.cpp', + 'tests/StaticPass.cpp', ], dependencies: [ grino_dep, diff --git a/src/Compiler.cpp b/src/Compiler.cpp index b1a74bf..1cf906d 100644 --- a/src/Compiler.cpp +++ b/src/Compiler.cpp @@ -2,7 +2,7 @@ #include "Program.hpp" #include "Module.hpp" #include "SymTable.hpp" -#include "src/opcodes.hpp" +#include "opcodes.hpp" #include "StaticFunction.hpp" namespace grino @@ -246,6 +246,11 @@ namespace grino } } + bool Compiler::has_static_func(std::string name) const + { + return m_statics.find(name) != std::end(m_statics); + } + void Compiler::add_static_func(std::string const& name, std::shared_ptr fun) { diff --git a/src/Compiler.hpp b/src/Compiler.hpp index 629654e..8688f18 100644 --- a/src/Compiler.hpp +++ b/src/Compiler.hpp @@ -4,7 +4,7 @@ #include "commons.hpp" #include "Logger.hpp" #include "Node.hpp" -#include "src/mutils.hpp" +#include "mutils.hpp" #include "Addr.hpp" namespace grino @@ -27,6 +27,14 @@ namespace grino Program& program, SymTable& sym); + std::shared_ptr + static_func(std::string name) const { + return m_statics.at(name); + } + + + bool has_static_func(std::string name) const; + void add_static_func(std::string const& name, std::shared_ptr fun); diff --git a/src/Function.cpp b/src/Function.cpp index 577fb09..b8f89f8 100644 --- a/src/Function.cpp +++ b/src/Function.cpp @@ -51,4 +51,8 @@ namespace grino m_env[addr] = value; } + void Function::set_prototype(std::shared_ptr prototype) + { + m_prototype = prototype; + } } diff --git a/src/Function.hpp b/src/Function.hpp index 36b6fee..6d4b6da 100644 --- a/src/Function.hpp +++ b/src/Function.hpp @@ -2,6 +2,7 @@ #define grino_FUNCTION_HPP #include "commons.hpp" +#include "Prototype.hpp" namespace grino { @@ -19,6 +20,7 @@ namespace grino explicit Function(std::shared_ptr prog, size_t base_addr); virtual ~Function(); + std::shared_ptr prototype() const { return m_prototype; } size_t base_addr() const { return m_base_addr; } bool is_native() const; @@ -29,11 +31,14 @@ namespace grino std::shared_ptr env(size_t addr) const; void set_env(size_t addr, std::shared_ptr value); + void set_prototype(std::shared_ptr prototype); + private: native_t m_native; std::shared_ptr m_prog; std::unordered_map> m_env; size_t m_base_addr = 0; + std::shared_ptr m_prototype; }; } diff --git a/src/Lexer.cpp b/src/Lexer.cpp index 4be67d8..2ab2dbe 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -1,5 +1,5 @@ #include "Lexer.hpp" -#include "src/Node.hpp" +#include "Node.hpp" namespace grino { diff --git a/src/Loader.cpp b/src/Loader.cpp index ad26e46..3372952 100644 --- a/src/Loader.cpp +++ b/src/Loader.cpp @@ -1,6 +1,5 @@ #include "Loader.hpp" #include "Function.hpp" -#include "src/config.in.hpp" #include #include "Lexer.hpp" #include "Parser.hpp" @@ -44,6 +43,11 @@ namespace grino return deps; } + void Loader::add_lib(std::filesystem::path path) + { + m_libs.push_back(path); + } + void Loader::load_libraries() { for (auto entry: std::filesystem::directory_iterator(GRINO_LIBDIR)) @@ -54,6 +58,15 @@ namespace grino load_library(entry.path()); } } + + for (auto path: m_libs) + { + if (path.has_extension() + && path.extension() == ".so") + { + load_library(path); + } + } } void Loader::load_library(std::filesystem::path path) @@ -61,6 +74,7 @@ namespace grino void* handle = dlopen(path.string().c_str(), RTLD_NOW); typedef void(*libfun)(Loader&); libfun f = (libfun) dlsym(handle, "lib"); + assert(f); f(*this); } @@ -88,8 +102,23 @@ namespace grino m_sym_table.declare_object(loc, name, addr, 0); } - void Loader::add_static(std::string const& name, static_fun_t fun) + void Loader::add_native(std::string const& name, + native_t native, + std::shared_ptr prototype) { - m_compiler.add_static_func(name, std::make_shared(fun)); + size_t addr = m_vm.heap_size(); + grino::Loc loc {"natives/" + name, 0}; + auto val = grino::Value::make_native_function(loc, native); + val->as_function()->set_prototype(prototype); + m_vm.set_heap(addr, val); + m_sym_table.declare_function(loc, name, addr, 0, prototype); + } + + void Loader::add_static(std::string const& name, + static_fun_t fun, + std::shared_ptr prototype) + { + auto fn = std::make_shared(fun, prototype); + m_compiler.add_static_func(name, fn); } } diff --git a/src/Loader.hpp b/src/Loader.hpp index fb275ef..8bea6e8 100644 --- a/src/Loader.hpp +++ b/src/Loader.hpp @@ -20,17 +20,27 @@ namespace grino std::vector dependencies(std::shared_ptr node); + void add_lib(std::filesystem::path path); + void load_libraries(); void load_library(std::filesystem::path path); std::shared_ptr add_module(std::string const& name); void add_native(std::string const& name, native_t native); - void add_static(std::string const& name, static_fun_t fun); + + void add_native(std::string const& name, + native_t native, + std::shared_ptr prototype); + + void add_static(std::string const& name, + static_fun_t fun, + std::shared_ptr prototype); private: VM& m_vm; Compiler& m_compiler; SymTable& m_sym_table; + std::vector m_libs; }; } diff --git a/src/Logger.hpp b/src/Logger.hpp index da8d70c..98e505b 100644 --- a/src/Logger.hpp +++ b/src/Logger.hpp @@ -2,7 +2,7 @@ #define grino_LOGGER_HPP #include "commons.hpp" -#include "src/mutils.hpp" +#include "mutils.hpp" #include "Loc.hpp" #define LOG_TYPE(G) \ diff --git a/src/Module.cpp b/src/Module.cpp index ea4116a..1130799 100644 --- a/src/Module.cpp +++ b/src/Module.cpp @@ -3,6 +3,7 @@ #include "Lexer.hpp" #include "Parser.hpp" #include "Compiler.hpp" +#include "StaticPass.hpp" namespace grino { @@ -44,6 +45,9 @@ namespace grino m_loader = std::make_shared(*m_vm, *m_compiler, *m_sym_table); m_loader->load_libraries(); + StaticPass static_pass {m_logger, *m_compiler, *m_sym_table}; + static_pass.check(ast); + m_compiler->compile(ast, *m_program, *m_sym_table); m_vm->run(); diff --git a/src/Parser.cpp b/src/Parser.cpp index 1d1d897..a7d8302 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -1,6 +1,6 @@ #include "Parser.hpp" -#include "src/Node.hpp" -#include "src/mutils.hpp" +#include "Node.hpp" +#include "mutils.hpp" namespace grino { diff --git a/src/Parser.hpp b/src/Parser.hpp index ff94e87..2ae6f55 100644 --- a/src/Parser.hpp +++ b/src/Parser.hpp @@ -3,7 +3,7 @@ #include "commons.hpp" #include "Lexer.hpp" -#include "src/mutils.hpp" +#include "mutils.hpp" namespace grino { diff --git a/src/Program.cpp b/src/Program.cpp index c3fb236..6b459cb 100644 --- a/src/Program.cpp +++ b/src/Program.cpp @@ -1,5 +1,5 @@ #include "Program.hpp" -#include "src/opcodes.hpp" +#include "opcodes.hpp" namespace grino { diff --git a/src/Prototype.cpp b/src/Prototype.cpp new file mode 100644 index 0000000..cf366a9 --- /dev/null +++ b/src/Prototype.cpp @@ -0,0 +1,65 @@ +#include "Prototype.hpp" + +namespace grino +{ + /*explicit*/ Prototype::Prototype(std::vector const& types) + : m_types { types } + { + } + + /*explicit*/ Prototype::Prototype(std::vector const& params, + TypeType ret) + { + for (auto ty: params) + { + m_types.push_back(TypeSlot { + HINT_CAT_PARAM, + ty, + HINT_NONE + }); + } + + m_types.push_back(TypeSlot { + HINT_CAT_RETURN, + ret, + HINT_NONE + }); + } + + /*virtual*/ Prototype::~Prototype() + { + } + + TypeType Prototype::type(size_t index) const + { + assert(index < size()); + return m_types[index].type; + } + + HintType Prototype::hint(size_t index) const + { + assert(index < size()); + return m_types[index].hint; + } + + HintCatType Prototype::category(size_t index) const + { + assert(index < size()); + return m_types[index].category; + } + + std::vector Prototype::filter(HintCatType category) const + { + std::vector result; + + for (size_t i=0; i const& types); + explicit Prototype(std::vector const& params, TypeType ret); + virtual ~Prototype(); + + size_t size() const { return m_types.size(); } + + TypeType type(size_t index) const; + HintType hint(size_t index) const; + HintCatType category(size_t index) const; + + std::vector filter(HintCatType category) const; + + private: + std::vector m_types; + }; +} + +#endif diff --git a/src/StaticFunction.cpp b/src/StaticFunction.cpp index d7157a8..3f08fff 100644 --- a/src/StaticFunction.cpp +++ b/src/StaticFunction.cpp @@ -3,8 +3,10 @@ namespace grino { - /*explicit*/ StaticFunction::StaticFunction(static_fun_t fun) + /*explicit*/ StaticFunction::StaticFunction(static_fun_t fun, + std::shared_ptr proto) : m_fun { fun } + , m_prototype { proto } { } diff --git a/src/StaticFunction.hpp b/src/StaticFunction.hpp index 9905f5d..8430418 100644 --- a/src/StaticFunction.hpp +++ b/src/StaticFunction.hpp @@ -6,6 +6,7 @@ #include "Node.hpp" #include "Program.hpp" #include "SymTable.hpp" +#include "Prototype.hpp" namespace grino { @@ -18,13 +19,16 @@ namespace grino class StaticFunction { public: - explicit StaticFunction(static_fun_t fun); + explicit StaticFunction(static_fun_t fun, std::shared_ptr proto); virtual ~StaticFunction(); + std::shared_ptr prototype() const { return m_prototype; } + void call(Compiler& compiler, node_t node, prog_t prog, sym_t sym); private: static_fun_t m_fun; + std::shared_ptr m_prototype; }; } diff --git a/src/StaticPass.cpp b/src/StaticPass.cpp new file mode 100644 index 0000000..385922d --- /dev/null +++ b/src/StaticPass.cpp @@ -0,0 +1,183 @@ +#include "StaticPass.hpp" +#include "src/Prototype.hpp" +#include "StaticFunction.hpp" + +namespace grino +{ + /*explicit*/ StaticPass::StaticPass(Logger& logger, + Compiler& compiler, + SymTable& sym) + : m_logger { logger } + , m_compiler { compiler } + , m_sym { sym } + { + } + + /*virtual*/ StaticPass::~StaticPass() + { + } + + TypeType StaticPass::check(std::shared_ptr node) + { + switch (node->type()) + { + case NODE_ARRAY: + case NODE_BLOCK: { + for (size_t i=0; isize(); i++) + { + check(node->child(i).lock()); + } + + return TYPE_NIL; + } break; + + case NODE_MODULE: { + for (size_t i=0; isize(); i++) + { + check(node->child(i).lock()); + } + + return TYPE_MODULE; + } break; + + case NODE_INT: { + return TYPE_INT; + } break; + + case NODE_FLOAT: { + return TYPE_FLOAT; + } break; + + case NODE_BOOL: { + return TYPE_BOOL; + } break; + + case NODE_STRING: { + return TYPE_STRING; + } break; + + case NODE_IDENT: { + std::string ident = node->repr(); + return m_types[ident]; + } break; + + case NODE_LAMBDA: { + return TYPE_FUNCTION; + } break; + + case NODE_IMPORT: { + return TYPE_MODULE; + } break; + + case NODE_VARDECL: { + std::string ident = node->child(0).lock()->repr(); + auto res = check(node->child(1).lock()); + m_types[ident] = res; + return res; + } break; + + case NODE_FUNCALL: { + std::string ident = node->child(0).lock()->repr(); + std::shared_ptr proto; + auto entry = m_sym.find_no_scope(ident); + + if (entry && entry->prototype) + { + proto = entry->prototype; + } + else if (m_compiler.has_static_func(ident)) + { + proto = m_compiler.static_func(ident)->prototype(); + } + + if (proto) + { + auto params = proto->filter(HINT_CAT_PARAM); + auto rets = proto->filter(HINT_CAT_RETURN); + + auto var_params = proto->filter(HINT_CAT_VARIADIC_PARAM); + + if (!var_params.empty()) + { + TypeType ty = var_params.back().type; + + for (size_t i=1; isize(); i++) + { + auto param_ty = check(node->child(i).lock()); + + if (param_ty != ty + && var_params.back().hint != HINT_ANY) + { + error(ty, param_ty, node->loc()); + } + } + + return rets[0].type; + } + + if (params.size() != node->size() - 1) + { + std::stringstream ss; + ss << "arity mismatch, expected '" + << params.size() + << "' got '" + << (node->size() - 1) + << "'"; + std::cout << node->string() << std::endl; + m_logger + .log(LOG_ERROR, node->loc(), ss.str()); + } + + std::vector ty; + + for (size_t i=1; isize(); i++) + { + TypeType param_ty = check(node->child(i).lock()); + ty.push_back(param_ty); + + if (proto->hint(i - 1) == HINT_ANY) + { + continue; + } + + bool same_err = (i > 1 && proto->hint(i - 1) == HINT_SAME) + && ty.back() != ty.at(ty.size() - 2); + + bool normal_err = proto->hint(i - 1) == HINT_NONE + && param_ty != proto->type(i - 1); + + if (same_err || normal_err) + { + error(proto->type(i - 1), param_ty, node->loc()); + } + } + + assert(rets.size() > 0); + return rets[0].type; + } + + return TYPE_NIL; + } break; + + default: + break; + } + + std::cerr << "cannot check node " + << NodeTypeStr[node->type()] << std::endl; + abort(); + } + + void StaticPass::error(TypeType lhs, TypeType rhs, Loc const& loc) + { + std::stringstream ss; + ss << "type mismatch, expected '" + << GRINO_TRIM(TypeTypeStr[lhs], "TYPE_") + << "', got '" + << GRINO_TRIM(TypeTypeStr[rhs], "TYPE_") + << "'"; + + m_logger + .log(LOG_ERROR, loc, ss.str()); + } +} diff --git a/src/StaticPass.hpp b/src/StaticPass.hpp new file mode 100644 index 0000000..7c40be8 --- /dev/null +++ b/src/StaticPass.hpp @@ -0,0 +1,32 @@ +#ifndef grino_STATICPASS_HPP +#define grino_STATICPASS_HPP + +#include "commons.hpp" +#include "Node.hpp" +#include "Logger.hpp" +#include "SymTable.hpp" +#include "Compiler.hpp" + +namespace grino +{ + GRINO_ERROR(static_error); + + class StaticPass + { + public: + explicit StaticPass(Logger& logger, Compiler& compiler, SymTable& sym); + virtual ~StaticPass(); + + TypeType check(std::shared_ptr node); + + private: + Logger& m_logger; + Compiler& m_compiler; + SymTable& m_sym; + std::unordered_map m_types; + + void error(TypeType lhs, TypeType rhs, Loc const& loc); + }; +} + +#endif diff --git a/src/SymTable.cpp b/src/SymTable.cpp index f3583b0..f875983 100644 --- a/src/SymTable.cpp +++ b/src/SymTable.cpp @@ -27,7 +27,6 @@ namespace grino entry.name = name; entry.is_object = false; entry.scope = scope; - m_entries.push_back(entry); } @@ -40,6 +39,16 @@ namespace grino m_entries.back().is_object = true; } + void SymTable::declare_function(Loc const& loc, + std::string const& name, + size_t addr, + size_t scope, + std::shared_ptr prototype) + { + declare_object(loc, name, addr, scope); + m_entries.back().prototype = prototype; + } + std::optional SymTable::find(std::string const& name, size_t scope) { std::optional entry; diff --git a/src/SymTable.hpp b/src/SymTable.hpp index f7b0dea..dadb067 100644 --- a/src/SymTable.hpp +++ b/src/SymTable.hpp @@ -3,6 +3,8 @@ #include "commons.hpp" #include "Logger.hpp" +#include "types.hpp" +#include "Prototype.hpp" namespace grino { @@ -11,6 +13,7 @@ namespace grino bool is_object; /* object are on the heap instead of the stack */ size_t addr; /* address on the heap if object, local address otherwise */ size_t scope; + std::shared_ptr prototype; /* only used by functions */ }; GRINO_ERROR(symbolic_error); @@ -27,6 +30,9 @@ namespace grino void declare_object(Loc const& loc, std::string const& name, size_t addr, size_t scope); + void declare_function(Loc const& loc, std::string const& name, size_t addr, + size_t scope, std::shared_ptr prototype); + std::optional find(std::string const& name, size_t scope); std::optional find_no_scope(std::string const& name); diff --git a/src/VM.cpp b/src/VM.cpp index 7060075..b0a4108 100644 --- a/src/VM.cpp +++ b/src/VM.cpp @@ -1,7 +1,7 @@ #include "VM.hpp" -#include "src/Value.hpp" +#include "Value.hpp" #include "Module.hpp" -#include "src/opcodes.hpp" +#include "opcodes.hpp" #include namespace grino diff --git a/src/Value.cpp b/src/Value.cpp index e7b51dc..736e7c4 100644 --- a/src/Value.cpp +++ b/src/Value.cpp @@ -1,6 +1,6 @@ #include "Value.hpp" #include "Program.hpp" -#include "src/types.hpp" +#include "types.hpp" #include "Module.hpp" namespace grino @@ -37,23 +37,28 @@ namespace grino } /*static*/ - std::shared_ptr Value::make_native_function(Loc const& loc, - native_t val) + std::shared_ptr + Value::make_native_function(Loc const& loc, + native_t val, + std::shared_ptr prototype) { auto value = std::make_shared(loc); value->m_type = TYPE_FUNCTION; value->m_function_val = std::make_shared(val); + value->m_function_val->set_prototype(prototype); return value; } /*static*/ std::shared_ptr Value::make_function(Loc const& loc, std::shared_ptr val, - size_t base_addr) + size_t base_addr, + std::shared_ptr prototype) { auto value = std::make_shared(loc); value->m_type = TYPE_FUNCTION; value->m_function_val = std::make_shared(val, base_addr); + value->m_function_val->set_prototype(prototype); return value; } diff --git a/src/Value.hpp b/src/Value.hpp index 4c84828..d34ae0c 100644 --- a/src/Value.hpp +++ b/src/Value.hpp @@ -20,11 +20,17 @@ namespace grino static std::shared_ptr make_bool(Loc const& loc, bool val); static std::shared_ptr make_int(Loc const& loc, int val); static std::shared_ptr make_float(Loc const& loc, float val); - static std::shared_ptr make_native_function(Loc const& loc, - native_t val); - static std::shared_ptr make_function(Loc const& loc, - std::shared_ptr val, - size_t base_addr); + + static std::shared_ptr + make_native_function(Loc const& loc, + native_t val, + std::shared_ptr prototype=nullptr); + + static std::shared_ptr + make_function(Loc const& loc, + std::shared_ptr val, + size_t base_addr, + std::shared_ptr prototype=nullptr); static std::shared_ptr make_ref(Loc const& loc, size_t val); diff --git a/src/main.cpp b/src/main.cpp index d669201..88d3873 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,12 +8,14 @@ #include "Program.hpp" #include "VM.hpp" #include "Logger.hpp" -#include "src/SymTable.hpp" +#include "SymTable.hpp" #include "Loader.hpp" #include "Addr.hpp" +#include "StaticPass.hpp" - -void run(char* const source_name, bool debug_mode) +void run(char* const source_name, + std::vector const& libs, + bool debug_mode) { std::string source; { @@ -51,12 +53,16 @@ void run(char* const source_name, bool debug_mode) grino::Compiler compiler {logger, addr}; grino::Loader loader {vm, compiler, sym_table}; + + for (auto p: libs) + { + loader.add_lib(p); + } + loader.load_libraries(); - for (auto dep: loader.dependencies(ast)) - { - // TODO: import them - } + grino::StaticPass static_pass {logger, compiler, sym_table}; + static_pass.check(ast); compiler.compile(ast, program, sym_table); @@ -78,55 +84,80 @@ void run(char* const source_name, bool debug_mode) int main(int argc, char** argv) { bool debug_mode = false; + std::vector libs; - static struct option options[] = { - {"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'v'}, - {"debug", no_argument, 0, 'd'}, - {0, 0, 0, 0} - }; - - int option_index = 0; - - int c = getopt_long(argc, argv, "hvd", options, &option_index); - - switch (c) + while (true) { - case 'h': { - std::cout << "Usage: grino [OPTION]... source" << std::endl; - std::cout << "OPTIONS:" << std::endl; - std::cout << "\t" << "-d, --debug, " - << "activate debug mode" << std::endl; + static struct option options[] = { + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'v'}, + {"debug", no_argument, 0, 'd'}, + {"lib", no_argument, 0, 'l'}, + {0, 0, 0, 0} + }; - std::cout << "\t" << "-h, --help, " - << "show this message" << std::endl; - std::cout << "\t" << "-v, --version, " - << "show grino version" << std::endl; - exit(0); - } break; + int option_index = 0; - case 'v': { - std::cout << "grino version: " << GRINO_VERSION << std::endl; - std::cout << "License: " << "GPLv3 or later (see LICENSE)"<< std::endl; - exit(0); - } break; + int c = getopt_long(argc, argv, "hvdl:", options, &option_index); - case 'd': { - debug_mode = true; - } break; + if (c == -1) + { + break; + } + + switch (c) + { + case 'h': { + std::cout << "Usage: grino [OPTION]... source" << std::endl; + std::cout << "OPTIONS:" << std::endl; + std::cout << "\t" << "-d, --debug, " + << "activate debug mode" << std::endl; + + std::cout << "\t" << "-h, --help, " + << "show this message" << std::endl; + std::cout << "\t" << "-v, --version, " + << "show grino version" << std::endl; + exit(0); + } break; + + case 'v': { + std::cout << "grino version: " << GRINO_VERSION << std::endl; + std::cout << "License: " << "GPLv3 or later (see LICENSE)"<< std::endl; + exit(0); + } break; + + case 'd': { + debug_mode = true; + } break; + + case 'l': { + auto path = std::filesystem::path("/usr/lib") + / std::filesystem::path("lib" + std::string(optarg) + ".so"); + + if (std::filesystem::is_regular_file(path)) + { + libs.push_back(path); + } + else + { + std::cerr << "W: cannot find lib " << path << std::endl; + } + } break; + + } } if (optind < argc) { if (debug_mode) { - run(argv[optind], debug_mode); + run(argv[optind], libs, debug_mode); } else { try { - run(argv[optind], debug_mode); + run(argv[optind], libs, debug_mode); } catch(std::exception const& err) { diff --git a/src/opcodes.hpp b/src/opcodes.hpp index 6202016..1d82d8f 100644 --- a/src/opcodes.hpp +++ b/src/opcodes.hpp @@ -2,7 +2,7 @@ #define grino_OPCODES_HPP #include "commons.hpp" -#include "src/mutils.hpp" +#include "mutils.hpp" #define OPCODES(G) \ G(OPCODE_LOAD_CONST), \ diff --git a/tests/StaticPass.cpp b/tests/StaticPass.cpp new file mode 100644 index 0000000..092131c --- /dev/null +++ b/tests/StaticPass.cpp @@ -0,0 +1,379 @@ +#include +#include +#include "../src/StaticPass.hpp" +#include "../src/Logger.hpp" +#include "../src/Lexer.hpp" +#include "../src/Parser.hpp" +#include "src/Prototype.hpp" +#include "src/SymTable.hpp" +#include "src/Loader.hpp" +#include "src/types.hpp" + +class StaticPassTest +{ +public: + explicit StaticPassTest() {} + virtual ~StaticPassTest() {} + + void test_ok(std::string const& source, + std::function init) + { + grino::Logger logger; + grino::Lexer lexer {logger, "tests/parser"}; + grino::Parser parser {logger, lexer}; + grino::SymTable sym {logger}; + grino::Addr addr; + grino::Compiler compiler {logger, addr}; + grino::StaticPass pass {logger, compiler, sym}; + grino::Program prog; + grino::VM vm {logger, prog}; + grino::Loader loader {vm, compiler, sym}; + + init(loader); + + auto root = parser.parse(source); + + INFO("source: " << source); + REQUIRE_NOTHROW(pass.check(root)); + } + + void test_ko(std::string const& source, + std::function init) + { + grino::Logger logger; + grino::Lexer lexer {logger, "tests/parser"}; + grino::Parser parser {logger, lexer}; + grino::SymTable sym {logger}; + grino::Addr addr; + grino::Compiler compiler {logger, addr}; + grino::StaticPass pass {logger, compiler, sym}; + grino::Program prog; + grino::VM vm {logger, prog}; + grino::Loader loader {vm, compiler, sym}; + + init(loader); + + auto root = parser.parse(source); + + INFO("source: " << source); + REQUIRE_THROWS_AS(pass.check(root), grino::static_error); + } + +protected: +}; + +TEST_CASE_METHOD(StaticPassTest, "StaticPass_normal") +{ + grino::TypeSlot ty_int_param { + grino::HintCatType::HINT_CAT_PARAM, + grino::TYPE_INT + }; + + grino::TypeSlot ty_int_ret { + grino::HintCatType::HINT_CAT_RETURN, + grino::TYPE_INT + }; + + auto slots = std::vector {ty_int_param, + ty_int_param, + ty_int_ret}; + + auto proto = std::make_shared(slots); + + test_ok("(+ 45 1)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ko("(+ 45.2 1)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ko("(+ (+ 45.2 1) 3)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); +} + +TEST_CASE_METHOD(StaticPassTest, "StaticPass_hint_any") +{ + grino::TypeSlot ty_int_param { + grino::HintCatType::HINT_CAT_PARAM, + grino::TYPE_INT + }; + + grino::TypeSlot ty_any_param { + grino::HintCatType::HINT_CAT_PARAM, + grino::TYPE_INT, + grino::HINT_ANY + }; + + grino::TypeSlot ty_int_ret { + grino::HintCatType::HINT_CAT_RETURN, + grino::TYPE_INT + }; + + auto slots = std::vector {ty_int_param, + ty_any_param, + ty_int_ret}; + + auto proto = std::make_shared(slots); + + test_ok("(+ 45 1)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ko("(+ 45.2 1)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ok("(+ 45 1.3)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ok("(+ (+ 2 1) 3)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ok("(+ (+ 2 1) 3.2)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ko("(+ 7.14 (+ 2 1))", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); +} + +TEST_CASE_METHOD(StaticPassTest, "StaticPass_hint_same") +{ + grino::TypeSlot ty_any_param { + grino::HintCatType::HINT_CAT_PARAM, + grino::TYPE_NIL, + grino::HINT_ANY + }; + + grino::TypeSlot ty_same_param { + grino::HintCatType::HINT_CAT_PARAM, + grino::TYPE_NIL, + grino::HINT_SAME + }; + + grino::TypeSlot ty_int_ret { + grino::HintCatType::HINT_CAT_RETURN, + grino::TYPE_INT + }; + + auto slots = std::vector {ty_any_param, + ty_same_param, + ty_int_ret}; + + auto proto = std::make_shared(slots); + + test_ok("(+ 45 1)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ok("(+ 45.2 1.3)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + + test_ok("(+ false true)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ko("(+ false 3.2)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ko("(+ 3.7 9)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + +} + +TEST_CASE_METHOD(StaticPassTest, "StaticPass_int_variadic") +{ + grino::TypeSlot ty_same_param { + grino::HintCatType::HINT_CAT_PARAM, + grino::TYPE_NIL, + grino::HINT_SAME + }; + + grino::TypeSlot ty_var_param { + grino::HintCatType::HINT_CAT_VARIADIC_PARAM, + grino::TYPE_INT + }; + + grino::TypeSlot ty_int_ret { + grino::HintCatType::HINT_CAT_RETURN, + grino::TYPE_INT + }; + + auto slots = std::vector { + ty_same_param, + ty_var_param, + ty_int_ret + }; + + auto proto = std::make_shared(slots); + + test_ok("(+ 45 1 3 2 1 7)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ok("(+ 45 1 7)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ko("(+ 45 3.12 1 7)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); +} + +TEST_CASE_METHOD(StaticPassTest, "StaticPass_any_variadic") +{ + grino::TypeSlot ty_same_param { + grino::HintCatType::HINT_CAT_PARAM, + grino::TYPE_NIL, + grino::HINT_SAME + }; + + grino::TypeSlot ty_var_param { + grino::HintCatType::HINT_CAT_VARIADIC_PARAM, + grino::TYPE_INT, + grino::HINT_ANY + }; + + grino::TypeSlot ty_int_ret { + grino::HintCatType::HINT_CAT_RETURN, + grino::TYPE_INT + }; + + auto slots = std::vector { + ty_same_param, + ty_var_param, + ty_int_ret + }; + + auto proto = std::make_shared(slots); + + test_ok("(+ 45 1 3 2 1 7)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ok("(+ 45 1 7)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ok("(+ 45 3.12 1 7)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); + + test_ok("(+ 45 3.12 true 'salut' 7)", [&proto](auto& loader){ + grino::Loc loc {"tests/static_pass"}; + loader.add_native("+", [loader, loc](auto args){ + auto lhs = args[0]; + auto rhs = args[1]; + return grino::Value::make_int(loc, lhs->as_int() + rhs->as_int()); + }, proto); + }); +}