ADD: native functions.
parent
e5c388a38e
commit
314fbc0bd6
|
@ -3,4 +3,6 @@ EXPR ::=
|
|||
bool
|
||||
| ident
|
||||
| VARDECL
|
||||
| FUNCALL
|
||||
VARDECL ::= opar decl ident EXPR cpar
|
||||
FUNCALL ::= opar ident EXPR* cpar
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
15
meson.build
15
meson.build
|
@ -6,8 +6,11 @@ project('grino',
|
|||
'cpp_std=c++17'
|
||||
])
|
||||
|
||||
grino_libdir = get_option('prefix') / get_option('libdir') / 'grino'
|
||||
|
||||
conf = configuration_data()
|
||||
conf.set('version', meson.project_version())
|
||||
conf.set('libdir', grino_libdir)
|
||||
|
||||
configure_file(input: 'src/config.in.hpp',
|
||||
output: 'config.hpp',
|
||||
|
@ -24,11 +27,23 @@ grino_src = static_library('grino',
|
|||
'src/Program.cpp',
|
||||
'src/VM.cpp',
|
||||
'src/Value.cpp',
|
||||
'src/Function.cpp',
|
||||
'src/SymTable.cpp',
|
||||
'src/Loader.cpp',
|
||||
])
|
||||
|
||||
grino_dep = declare_dependency(link_with: grino_src)
|
||||
|
||||
shared_library('grino_core',
|
||||
sources: [
|
||||
'lib/core.cpp'
|
||||
],
|
||||
dependencies: [
|
||||
grino_dep
|
||||
],
|
||||
install: true,
|
||||
install_dir: grino_libdir)
|
||||
|
||||
executable('grino',
|
||||
sources: [
|
||||
'src/main.cpp'
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
if (entry->is_object)
|
||||
{
|
||||
program.push_instr(OPCODE_LOAD_OBJ, entry->addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
program.push_instr(OPCODE_LOAD_LOCAL, entry->addr);
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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), \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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++)
|
||||
|
|
|
@ -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;
|
||||
|
|
63
src/VM.cpp
63
src/VM.cpp
|
@ -48,6 +48,34 @@ namespace grino
|
|||
m_pc++;
|
||||
} break;
|
||||
|
||||
case OPCODE_LOAD_OBJ: {
|
||||
size_t addr = *instr.param;
|
||||
auto ref = Value::make_ref(addr);
|
||||
push(program.push_constant(ref));
|
||||
m_pc++;
|
||||
} break;
|
||||
|
||||
case OPCODE_CALL: {
|
||||
size_t const N = *instr.param;
|
||||
|
||||
std::vector<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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
16
src/VM.hpp
16
src/VM.hpp
|
@ -21,12 +21,23 @@ namespace grino
|
|||
explicit VM(Logger& logger);
|
||||
virtual ~VM();
|
||||
|
||||
size_t heap_size() const { return m_heap.size(); }
|
||||
|
||||
void run(Program& program);
|
||||
|
||||
std::string string() const;
|
||||
|
||||
std::shared_ptr<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);
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
|
||||
#define TYPES(G) \
|
||||
G(TYPE_NIL), \
|
||||
G(TYPE_BOOL)
|
||||
G(TYPE_BOOL), \
|
||||
G(TYPE_FUNCTION), \
|
||||
G(TYPE_REF)
|
||||
|
||||
namespace grino
|
||||
{
|
||||
|
|
|
@ -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)");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue