ADD: basic type checker.

main
bog 2023-09-15 09:25:40 +02:00
parent 29facf1647
commit e12ff96943
29 changed files with 1012 additions and 95 deletions

View File

@ -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"

View File

@ -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<grino::Prototype>(std::vector<grino::TypeSlot>{
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<grino::Prototype>(std::vector<grino::TypeSlot> {{
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<grino::Prototype>(std::vector<grino::TypeSlot>{
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<grino::Prototype>(std::vector<grino::TypeSlot>{
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<grino::Prototype>(std::vector<grino::TypeSlot>{
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<grino::Prototype>(std::vector<grino::TypeSlot> {{
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<grino::Prototype>(std::vector<grino::TypeSlot> {{
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<grino::Prototype>(std::vector<grino::TypeSlot> {{
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<grino::Prototype>(std::vector<grino::TypeSlot> {{
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<grino::Prototype>(std::vector<grino::TypeSlot> {{
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<grino::Prototype>(std::vector<grino::TypeSlot> {{
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<grino::Prototype>(std::vector<grino::TypeSlot> {{
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}
}}));
}

View File

@ -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,

View File

@ -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<StaticFunction> fun)
{

View File

@ -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<StaticFunction>
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<StaticFunction> fun);

View File

@ -51,4 +51,8 @@ namespace grino
m_env[addr] = value;
}
void Function::set_prototype(std::shared_ptr<Prototype> prototype)
{
m_prototype = prototype;
}
}

View File

@ -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<Program> prog, size_t base_addr);
virtual ~Function();
std::shared_ptr<Prototype> 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<Value> env(size_t addr) const;
void set_env(size_t addr, std::shared_ptr<Value> value);
void set_prototype(std::shared_ptr<Prototype> prototype);
private:
native_t m_native;
std::shared_ptr<Program> m_prog;
std::unordered_map<size_t, std::shared_ptr<Value>> m_env;
size_t m_base_addr = 0;
std::shared_ptr<Prototype> m_prototype;
};
}

View File

@ -1,5 +1,5 @@
#include "Lexer.hpp"
#include "src/Node.hpp"
#include "Node.hpp"
namespace grino
{

View File

@ -1,6 +1,5 @@
#include "Loader.hpp"
#include "Function.hpp"
#include "src/config.in.hpp"
#include <dlfcn.h>
#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> prototype)
{
m_compiler.add_static_func(name, std::make_shared<StaticFunction>(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> prototype)
{
auto fn = std::make_shared<StaticFunction>(fun, prototype);
m_compiler.add_static_func(name, fn);
}
}

View File

@ -20,17 +20,27 @@ namespace grino
std::vector<std::filesystem::path>
dependencies(std::shared_ptr<Node> node);
void add_lib(std::filesystem::path path);
void load_libraries();
void load_library(std::filesystem::path path);
std::shared_ptr<Module> 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> prototype);
void add_static(std::string const& name,
static_fun_t fun,
std::shared_ptr<Prototype> prototype);
private:
VM& m_vm;
Compiler& m_compiler;
SymTable& m_sym_table;
std::vector<std::filesystem::path> m_libs;
};
}

View File

@ -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) \

View File

@ -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<Loader>(*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();

View File

@ -1,6 +1,6 @@
#include "Parser.hpp"
#include "src/Node.hpp"
#include "src/mutils.hpp"
#include "Node.hpp"
#include "mutils.hpp"
namespace grino
{

View File

@ -3,7 +3,7 @@
#include "commons.hpp"
#include "Lexer.hpp"
#include "src/mutils.hpp"
#include "mutils.hpp"
namespace grino
{

View File

@ -1,5 +1,5 @@
#include "Program.hpp"
#include "src/opcodes.hpp"
#include "opcodes.hpp"
namespace grino
{

65
src/Prototype.cpp Normal file
View File

@ -0,0 +1,65 @@
#include "Prototype.hpp"
namespace grino
{
/*explicit*/ Prototype::Prototype(std::vector<TypeSlot> const& types)
: m_types { types }
{
}
/*explicit*/ Prototype::Prototype(std::vector<TypeType> 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<TypeSlot> Prototype::filter(HintCatType category) const
{
std::vector<TypeSlot> result;
for (size_t i=0; i<size(); i++)
{
if (m_types[i].category == category)
{
result.push_back(m_types[i]);
}
}
return result;
}
}

48
src/Prototype.hpp Normal file
View File

@ -0,0 +1,48 @@
#ifndef grino_PROTOTYPE_HPP
#define grino_PROTOTYPE_HPP
#include "commons.hpp"
#include "types.hpp"
#define HINTS(G) \
G(HINT_NONE), \
G(HINT_ANY), \
G(HINT_SAME),
#define HINTS_CATEGORY(G) \
G(HINT_CAT_PARAM), \
G(HINT_CAT_VARIADIC_PARAM), \
G(HINT_CAT_RETURN)
namespace grino
{
GRINO_ENUM(Hint, HINTS);
GRINO_ENUM(HintCat, HINTS_CATEGORY);
struct TypeSlot {
HintCatType category;
TypeType type;
HintType hint = HINT_NONE;
};
class Prototype
{
public:
explicit Prototype(std::vector<TypeSlot> const& types);
explicit Prototype(std::vector<TypeType> 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<TypeSlot> filter(HintCatType category) const;
private:
std::vector<TypeSlot> m_types;
};
}
#endif

View File

@ -3,8 +3,10 @@
namespace grino
{
/*explicit*/ StaticFunction::StaticFunction(static_fun_t fun)
/*explicit*/ StaticFunction::StaticFunction(static_fun_t fun,
std::shared_ptr<Prototype> proto)
: m_fun { fun }
, m_prototype { proto }
{
}

View File

@ -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<Prototype> proto);
virtual ~StaticFunction();
std::shared_ptr<Prototype> 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<Prototype> m_prototype;
};
}

183
src/StaticPass.cpp Normal file
View File

@ -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> node)
{
switch (node->type())
{
case NODE_ARRAY:
case NODE_BLOCK: {
for (size_t i=0; i<node->size(); i++)
{
check(node->child(i).lock());
}
return TYPE_NIL;
} break;
case NODE_MODULE: {
for (size_t i=0; i<node->size(); 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<Prototype> 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; i<node->size(); 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<static_error>(LOG_ERROR, node->loc(), ss.str());
}
std::vector<TypeType> ty;
for (size_t i=1; i<node->size(); 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<static_error>(LOG_ERROR, loc, ss.str());
}
}

32
src/StaticPass.hpp Normal file
View File

@ -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> node);
private:
Logger& m_logger;
Compiler& m_compiler;
SymTable& m_sym;
std::unordered_map<std::string, TypeType> m_types;
void error(TypeType lhs, TypeType rhs, Loc const& loc);
};
}
#endif

View File

@ -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> prototype)
{
declare_object(loc, name, addr, scope);
m_entries.back().prototype = prototype;
}
std::optional<SymEntry> SymTable::find(std::string const& name, size_t scope)
{
std::optional<SymEntry> entry;

View File

@ -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> 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> prototype);
std::optional<SymEntry> find(std::string const& name, size_t scope);
std::optional<SymEntry> find_no_scope(std::string const& name);

View File

@ -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 <optional>
namespace grino

View File

@ -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> Value::make_native_function(Loc const& loc,
native_t val)
std::shared_ptr<Value>
Value::make_native_function(Loc const& loc,
native_t val,
std::shared_ptr<Prototype> prototype)
{
auto value = std::make_shared<Value>(loc);
value->m_type = TYPE_FUNCTION;
value->m_function_val = std::make_shared<Function>(val);
value->m_function_val->set_prototype(prototype);
return value;
}
/*static*/ std::shared_ptr<Value>
Value::make_function(Loc const& loc,
std::shared_ptr<Program> val,
size_t base_addr)
size_t base_addr,
std::shared_ptr<Prototype> prototype)
{
auto value = std::make_shared<Value>(loc);
value->m_type = TYPE_FUNCTION;
value->m_function_val = std::make_shared<Function>(val, base_addr);
value->m_function_val->set_prototype(prototype);
return value;
}

View File

@ -20,11 +20,17 @@ namespace grino
static std::shared_ptr<Value> make_bool(Loc const& loc, bool val);
static std::shared_ptr<Value> make_int(Loc const& loc, int val);
static std::shared_ptr<Value> make_float(Loc const& loc, float val);
static std::shared_ptr<Value> make_native_function(Loc const& loc,
native_t val);
static std::shared_ptr<Value> make_function(Loc const& loc,
static std::shared_ptr<Value>
make_native_function(Loc const& loc,
native_t val,
std::shared_ptr<Prototype> prototype=nullptr);
static std::shared_ptr<Value>
make_function(Loc const& loc,
std::shared_ptr<Program> val,
size_t base_addr);
size_t base_addr,
std::shared_ptr<Prototype> prototype=nullptr);
static std::shared_ptr<Value> make_ref(Loc const& loc,
size_t val);

View File

@ -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<std::filesystem::path> 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,17 +84,26 @@ void run(char* const source_name, bool debug_mode)
int main(int argc, char** argv)
{
bool debug_mode = false;
std::vector<std::filesystem::path> libs;
while (true)
{
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}
};
int option_index = 0;
int c = getopt_long(argc, argv, "hvd", options, &option_index);
int c = getopt_long(argc, argv, "hvdl:", options, &option_index);
if (c == -1)
{
break;
}
switch (c)
{
@ -114,19 +129,35 @@ int main(int argc, char** argv)
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)
{

View File

@ -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), \

379
tests/StaticPass.cpp Normal file
View File

@ -0,0 +1,379 @@
#include <catch2/catch.hpp>
#include <memory>
#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<void(grino::Loader&)> 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<void(grino::Loader&)> 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<grino::TypeSlot> {ty_int_param,
ty_int_param,
ty_int_ret};
auto proto = std::make_shared<grino::Prototype>(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<grino::TypeSlot> {ty_int_param,
ty_any_param,
ty_int_ret};
auto proto = std::make_shared<grino::Prototype>(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<grino::TypeSlot> {ty_any_param,
ty_same_param,
ty_int_ret};
auto proto = std::make_shared<grino::Prototype>(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<grino::TypeSlot> {
ty_same_param,
ty_var_param,
ty_int_ret
};
auto proto = std::make_shared<grino::Prototype>(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<grino::TypeSlot> {
ty_same_param,
ty_var_param,
ty_int_ret
};
auto proto = std::make_shared<grino::Prototype>(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);
});
}