ADD: native functions.

main
bog 2023-09-11 12:14:01 +02:00
parent e5c388a38e
commit 314fbc0bd6
22 changed files with 327 additions and 25 deletions

View File

@ -3,4 +3,6 @@ EXPR ::=
bool
| ident
| VARDECL
| FUNCALL
VARDECL ::= opar decl ident EXPR cpar
FUNCALL ::= opar ident EXPR* cpar

19
lib/core.cpp Normal file
View File

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

View File

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

View File

@ -28,6 +28,16 @@ namespace grino
}
} break;
case NODE_FUNCALL: {
for (size_t i=0; i<node->size(); 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<compile_error>(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:

20
src/Function.cpp Normal file
View File

@ -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);
}
}

27
src/Function.hpp Normal file
View File

@ -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<Value>;
using args_t = std::vector<value_t>;
using native_t = std::function<value_t(args_t)>;
class Function
{
public:
explicit Function(native_t native);
virtual ~Function();
value_t call(args_t args);
private:
native_t m_native;
};
}
#endif

45
src/Loader.cpp Normal file
View File

@ -0,0 +1,45 @@
#include "Loader.hpp"
#include "Function.hpp"
#include "src/config.in.hpp"
#include <dlfcn.h>
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);
}
}

26
src/Loader.hpp Normal file
View File

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

View File

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

View File

@ -117,7 +117,12 @@ namespace grino
std::shared_ptr<Node> 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<Node> 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;
}
}

View File

@ -35,6 +35,7 @@ namespace grino
std::shared_ptr<Node> parse_module();
std::shared_ptr<Node> parse_expr();
std::shared_ptr<Node> parse_vardecl();
std::shared_ptr<Node> parse_funcall();
};
}

View File

@ -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<SymEntry> SymTable::find(std::string const& name)
{
for (size_t i=0; i<m_entries.size(); i++)

View File

@ -8,8 +8,8 @@ namespace grino
{
struct SymEntry {
std::string name;
bool is_global;
size_t addr;
bool is_object; /* object are on the heap instead of the stack */
size_t addr; /* address on the heap if object, local address otherwise */
};
GRINO_ERROR(symbolic_error);
@ -21,6 +21,8 @@ namespace grino
virtual ~SymTable();
void declare(Loc const& loc, std::string const& name, size_t addr);
void declare_object(Loc const& loc, std::string const& name, size_t addr);
std::optional<SymEntry> find(std::string const& name);
std::string string() const;

View File

@ -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<std::shared_ptr<Value>> args;
for (size_t i=0; i<N; i++)
{
args.insert(std::begin(args), program.constant(pop()));
}
size_t ref = program.constant(pop())->as_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<Value> VM::local(size_t addr) const
{
return m_frames.back().locals.at(addr);
}
void VM::set_local(size_t addr,
std::shared_ptr<Value> value)
{
m_frames.back().locals[addr] = value;
}
std::shared_ptr<Value> VM::heap(size_t addr) const
{
return m_heap.at(addr);
}
void VM::set_heap(size_t addr,
std::shared_ptr<Value> 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<Value> VM::local(size_t addr) const
{
return m_frames.back().locals.at(addr);
}
void VM::set_local(size_t addr,
std::shared_ptr<Value> value)
{
m_frames.back().locals[addr] = value;
}
}

View File

@ -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<Value> local(size_t addr) const;
void set_local(size_t addr,
std::shared_ptr<Value> value);
std::shared_ptr<Value> heap(size_t addr) const;
void set_heap(size_t addr,
std::shared_ptr<Value> value);
private:
Logger& m_logger;
std::array<size_t, STACK_SIZE> m_stack;
std::unordered_map<size_t, std::shared_ptr<Value>> m_heap;
std::vector<Frame> 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<Value> local(size_t addr) const;
void set_local(size_t addr,
std::shared_ptr<Value> value);
};
}

View File

@ -17,13 +17,31 @@ namespace grino
return value;
}
/*static*/
std::shared_ptr<Value> Value::make_native_function(native_t val)
{
auto value = std::make_shared<Value>();
value->m_type = TYPE_FUNCTION;
value->m_function_val = std::make_shared<Function>(val);
return value;
}
/*static*/ std::shared_ptr<Value> Value::make_ref(size_t val)
{
auto value = std::make_shared<Value>();
value->m_type = TYPE_REF;
value->m_ref_val = val;
return value;
}
std::string Value::string() const
{
switch (m_type)
{
case TYPE_NIL: return "<nil>";
case TYPE_BOOL: return *m_bool_val ? "true" : "false";
case TYPE_FUNCTION: return "<function>";
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 "

View File

@ -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<Value> make_nil();
static std::shared_ptr<Value> make_bool(bool val);
static std::shared_ptr<Value> make_native_function(native_t val);
static std::shared_ptr<Value> 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<Function> 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<bool> m_bool_val;
std::shared_ptr<Function> m_function_val;
std::optional<size_t> m_ref_val;
};
}

View File

@ -1,6 +1,9 @@
#ifndef grino_CONFIG_IN_HPP
#define grino_CONFIG_IN_HPP
#include <filesystem>
#define GRINO_VERSION "@version@"
#define GRINO_LIBDIR std::filesystem::path {"@libdir@"}
#endif

View File

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

View File

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

View File

@ -5,7 +5,9 @@
#define TYPES(G) \
G(TYPE_NIL), \
G(TYPE_BOOL)
G(TYPE_BOOL), \
G(TYPE_FUNCTION), \
G(TYPE_REF)
namespace grino
{

View File

@ -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)");
}