ADD: native function support.
parent
8c46189b20
commit
bcf12d19f7
|
@ -1,3 +1,6 @@
|
||||||
MODULE ::= EXPR*
|
MODULE ::= EXPR*
|
||||||
EXPR ::=
|
EXPR ::=
|
||||||
| int | float | bool | string
|
| int | float | bool | string
|
||||||
|
| ident
|
||||||
|
| CALL
|
||||||
|
CALL opar ident EXPR* cpar
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef fkstd_COMMONS_HPP
|
||||||
|
#define fkstd_COMMONS_HPP
|
||||||
|
|
||||||
|
#include "../src/Module.hpp"
|
||||||
|
#include "../src/Constant.hpp"
|
||||||
|
|
||||||
|
using namespace fk;
|
||||||
|
|
||||||
|
#define STDARGS std::vector<std::shared_ptr<Constant>>
|
||||||
|
#define STDRET std::shared_ptr<Constant>
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include "fun.hpp"
|
||||||
|
|
||||||
|
namespace fkstd
|
||||||
|
{
|
||||||
|
STDRET println(STDARGS args)
|
||||||
|
{
|
||||||
|
std::string sep;
|
||||||
|
|
||||||
|
for (auto arg: args)
|
||||||
|
{
|
||||||
|
std::cout << sep << arg->string();
|
||||||
|
sep = " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
Loc loc {"???"};
|
||||||
|
|
||||||
|
if (args.size() > 0)
|
||||||
|
{
|
||||||
|
loc = args.front()->loc();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
return std::make_shared<Constant>(TYPE_INT, 0, loc);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef fkstd_FUN_HPP
|
||||||
|
#define fkstd_FUN_HPP
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
namespace fkstd
|
||||||
|
{
|
||||||
|
STDRET println(STDARGS args);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "fun.hpp"
|
||||||
|
|
||||||
|
extern "C" void lib(Module& mod)
|
||||||
|
{
|
||||||
|
mod.register_function("println", fkstd::println);
|
||||||
|
}
|
26
meson.build
26
meson.build
|
@ -7,10 +7,13 @@ project('fakir',
|
||||||
'prefix=/usr',
|
'prefix=/usr',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
extra_libs = get_option('prefix') / get_option('libdir') / 'fakir'
|
||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
# =============
|
# =============
|
||||||
conf = configuration_data()
|
conf = configuration_data()
|
||||||
conf.set('version', meson.project_version())
|
conf.set('version', meson.project_version())
|
||||||
|
conf.set('libs', extra_libs)
|
||||||
|
|
||||||
configure_file(input: 'src/conf.in.hpp',
|
configure_file(input: 'src/conf.in.hpp',
|
||||||
output: 'conf.hpp',
|
output: 'conf.hpp',
|
||||||
|
@ -25,6 +28,9 @@ fakir_cpp = [
|
||||||
'src/Lexer.cpp',
|
'src/Lexer.cpp',
|
||||||
'src/Parser.cpp',
|
'src/Parser.cpp',
|
||||||
'src/Constant.cpp',
|
'src/Constant.cpp',
|
||||||
|
'src/NativeFunction.cpp',
|
||||||
|
'src/SymTable.cpp',
|
||||||
|
'src/SymEntry.cpp',
|
||||||
|
|
||||||
'src/Compiler.cpp',
|
'src/Compiler.cpp',
|
||||||
'src/VM.cpp',
|
'src/VM.cpp',
|
||||||
|
@ -38,12 +44,14 @@ fakir_hpp = [
|
||||||
'src/Lexer.hpp',
|
'src/Lexer.hpp',
|
||||||
'src/Parser.hpp',
|
'src/Parser.hpp',
|
||||||
'src/Constant.hpp',
|
'src/Constant.hpp',
|
||||||
|
'src/SymTable.hpp',
|
||||||
|
'src/SymEntry.hpp',
|
||||||
|
|
||||||
'src/Compiler.hpp',
|
'src/Compiler.hpp',
|
||||||
'src/VM.hpp',
|
'src/VM.hpp',
|
||||||
'src/Program.hpp',
|
'src/Program.hpp',
|
||||||
'src/Module.hpp',
|
'src/Module.hpp',
|
||||||
|
'src/NativeFunction.hpp',
|
||||||
'src/opcodes.hpp',
|
'src/opcodes.hpp',
|
||||||
'src/commons.hpp',
|
'src/commons.hpp',
|
||||||
'src/types.hpp',
|
'src/types.hpp',
|
||||||
|
@ -59,6 +67,21 @@ fakir_dep = declare_dependency(
|
||||||
link_with: fakir_lib
|
link_with: fakir_lib
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# STD LIB
|
||||||
|
# =======
|
||||||
|
|
||||||
|
shared_library('fakir-std',
|
||||||
|
sources: [
|
||||||
|
'libstd/lib.cpp',
|
||||||
|
'libstd/fun.cpp',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
fakir_dep
|
||||||
|
],
|
||||||
|
install_dir: extra_libs,
|
||||||
|
install: true)
|
||||||
|
|
||||||
|
|
||||||
# COMPILER
|
# COMPILER
|
||||||
# ========
|
# ========
|
||||||
executable('fakir',
|
executable('fakir',
|
||||||
|
@ -77,6 +100,7 @@ executable('fakir-tests',
|
||||||
'tests/main.cpp',
|
'tests/main.cpp',
|
||||||
'tests/Lexer.cpp',
|
'tests/Lexer.cpp',
|
||||||
'tests/Parser.cpp',
|
'tests/Parser.cpp',
|
||||||
|
'tests/SymTable.cpp',
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
fakir_dep,
|
fakir_dep,
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
/*explicit*/ Compiler::Compiler()
|
/*explicit*/ Compiler::Compiler(std::shared_ptr<SymTable> sym)
|
||||||
|
: m_sym { sym }
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,11 +14,11 @@ namespace fk
|
||||||
std::shared_ptr<Program> Compiler::compile(std::shared_ptr<Node> node)
|
std::shared_ptr<Program> Compiler::compile(std::shared_ptr<Node> node)
|
||||||
{
|
{
|
||||||
auto prog = std::make_shared<Program>();
|
auto prog = std::make_shared<Program>();
|
||||||
compile(node, prog);
|
compile_prog(node, prog);
|
||||||
return prog;
|
return prog;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile(std::shared_ptr<Node> node,
|
void Compiler::compile_prog(std::shared_ptr<Node> node,
|
||||||
std::shared_ptr<Program> prog)
|
std::shared_ptr<Program> prog)
|
||||||
{
|
{
|
||||||
switch (node->type())
|
switch (node->type())
|
||||||
|
@ -25,11 +26,43 @@ namespace fk
|
||||||
case NODE_MODULE: {
|
case NODE_MODULE: {
|
||||||
for (size_t i=0; i<node->size(); i++)
|
for (size_t i=0; i<node->size(); i++)
|
||||||
{
|
{
|
||||||
compile(node->child(i), prog);
|
compile_prog(node->child(i), prog);
|
||||||
prog->add(OP_POP);
|
prog->add(OP_POP);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_CALL: {
|
||||||
|
std::string ident = node->child(0)->repr();
|
||||||
|
for (size_t i=0; i<node->size(); i++)
|
||||||
|
{
|
||||||
|
compile_prog(node->child(i), prog);
|
||||||
|
}
|
||||||
|
|
||||||
|
prog->add(OP_CALL_NATIVE, node->size() - 1);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_IDENT: {
|
||||||
|
auto entry = m_sym->find(node->repr());
|
||||||
|
|
||||||
|
if (!entry)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "'"
|
||||||
|
<< node->repr()
|
||||||
|
<< "' is undefined";
|
||||||
|
|
||||||
|
node->loc().error<compile_error>(LOG_ERROR, ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry->is_global())
|
||||||
|
{
|
||||||
|
throw std::runtime_error { "not implemented yet" };
|
||||||
|
}
|
||||||
|
|
||||||
|
prog->add(OP_LOAD_GLOBAL, entry->addr());
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
case NODE_INT: {
|
case NODE_INT: {
|
||||||
prog->load_const(std::make_shared<Constant>(TYPE_INT,
|
prog->load_const(std::make_shared<Constant>(TYPE_INT,
|
||||||
stoi(node->repr()),
|
stoi(node->repr()),
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "commons.hpp"
|
#include "commons.hpp"
|
||||||
#include "Program.hpp"
|
#include "Program.hpp"
|
||||||
#include "Node.hpp"
|
#include "Node.hpp"
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
|
@ -12,13 +13,15 @@ namespace fk
|
||||||
class Compiler
|
class Compiler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Compiler();
|
explicit Compiler(std::shared_ptr<SymTable> sym);
|
||||||
virtual ~Compiler();
|
virtual ~Compiler();
|
||||||
|
|
||||||
std::shared_ptr<Program> compile(std::shared_ptr<Node> node);
|
std::shared_ptr<Program> compile(std::shared_ptr<Node> node);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void compile(std::shared_ptr<Node> node,
|
std::shared_ptr<SymTable> m_sym;
|
||||||
|
|
||||||
|
void compile_prog(std::shared_ptr<Node> node,
|
||||||
std::shared_ptr<Program> prog);
|
std::shared_ptr<Program> prog);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ namespace fk
|
||||||
case TYPE_FLOAT: return std::to_string(std::get<float>(m_value));
|
case TYPE_FLOAT: return std::to_string(std::get<float>(m_value));
|
||||||
case TYPE_BOOL: return std::get<bool>(m_value) ? "true" : "false";
|
case TYPE_BOOL: return std::get<bool>(m_value) ? "true" : "false";
|
||||||
case TYPE_STRING: return std::get<std::string>(m_value);
|
case TYPE_STRING: return std::get<std::string>(m_value);
|
||||||
|
case TYPE_REF: return std::to_string(std::get<size_t>(m_value));
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace fk
|
||||||
{
|
{
|
||||||
FK_ERROR(constant_error);
|
FK_ERROR(constant_error);
|
||||||
|
|
||||||
using constant_t = std::variant<int, float, bool, std::string>;
|
using constant_t = std::variant<int, float, bool, std::string, size_t>;
|
||||||
|
|
||||||
class Constant
|
class Constant
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,12 +7,19 @@ namespace fk
|
||||||
: m_loc { loc }
|
: m_loc { loc }
|
||||||
{
|
{
|
||||||
std::vector<std::tuple<NodeType, std::string, bool>> text = {
|
std::vector<std::tuple<NodeType, std::string, bool>> text = {
|
||||||
|
{NODE_OPAR, "(", false},
|
||||||
|
{NODE_CPAR, ")", false},
|
||||||
{NODE_BOOL, "true", true},
|
{NODE_BOOL, "true", true},
|
||||||
{NODE_BOOL, "false", true}
|
{NODE_BOOL, "false", true}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto txt: text)
|
for (auto txt: text)
|
||||||
{
|
{
|
||||||
|
if (std::get<1>(txt).size() == 1)
|
||||||
|
{
|
||||||
|
m_separators.push_back(std::get<1>(txt)[0]);
|
||||||
|
}
|
||||||
|
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_text,
|
m_scanners.push_back(std::bind(&Lexer::scan_text,
|
||||||
this,
|
this,
|
||||||
std::get<1>(txt),
|
std::get<1>(txt),
|
||||||
|
@ -23,6 +30,7 @@ namespace fk
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_int, this));
|
m_scanners.push_back(std::bind(&Lexer::scan_int, this));
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_float, this));
|
m_scanners.push_back(std::bind(&Lexer::scan_float, this));
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_string, this));
|
m_scanners.push_back(std::bind(&Lexer::scan_string, this));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*virtual*/ Lexer::~Lexer()
|
/*virtual*/ Lexer::~Lexer()
|
||||||
|
@ -84,12 +92,25 @@ namespace fk
|
||||||
}
|
}
|
||||||
|
|
||||||
m_loc.error<lexical_error>(LOG_ERROR,
|
m_loc.error<lexical_error>(LOG_ERROR,
|
||||||
"unexpected end near '" + text + "'");
|
"unexpected text '" + text + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Lexer::is_sep(size_t index) const
|
||||||
|
{
|
||||||
|
if (index >= m_source.size()
|
||||||
|
|| std::isspace(m_source[index]))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::find(std::begin(m_separators),
|
||||||
|
std::end(m_separators),
|
||||||
|
m_source[index]) != std::end(m_separators);
|
||||||
|
}
|
||||||
|
|
||||||
void Lexer::skip_spaces()
|
void Lexer::skip_spaces()
|
||||||
{
|
{
|
||||||
while (m_cursor < m_source.size()
|
while (m_cursor < m_source.size()
|
||||||
|
@ -227,6 +248,31 @@ namespace fk
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<ScanInfo> Lexer::scan_ident()
|
||||||
|
{
|
||||||
|
size_t cursor = m_cursor;
|
||||||
|
std::string repr;
|
||||||
|
|
||||||
|
while (cursor < m_source.size()
|
||||||
|
&& !is_sep(cursor))
|
||||||
|
{
|
||||||
|
repr += m_source[cursor];
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repr.empty() == false
|
||||||
|
&& !std::isdigit(repr[0]))
|
||||||
|
{
|
||||||
|
return ScanInfo {
|
||||||
|
cursor,
|
||||||
|
NODE_IDENT,
|
||||||
|
repr
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<ScanInfo> Lexer::scan_text(std::string const& text,
|
std::optional<ScanInfo> Lexer::scan_text(std::string const& text,
|
||||||
NodeType type,
|
NodeType type,
|
||||||
bool has_value)
|
bool has_value)
|
||||||
|
|
|
@ -33,11 +33,13 @@ namespace fk
|
||||||
std::vector<scanner_t> m_scanners;
|
std::vector<scanner_t> m_scanners;
|
||||||
std::vector<char> m_separators;
|
std::vector<char> m_separators;
|
||||||
|
|
||||||
|
bool is_sep(size_t index) const;
|
||||||
void skip_spaces();
|
void skip_spaces();
|
||||||
|
|
||||||
std::optional<ScanInfo> scan_int();
|
std::optional<ScanInfo> scan_int();
|
||||||
std::optional<ScanInfo> scan_float();
|
std::optional<ScanInfo> scan_float();
|
||||||
std::optional<ScanInfo> scan_string();
|
std::optional<ScanInfo> scan_string();
|
||||||
|
std::optional<ScanInfo> scan_ident();
|
||||||
std::optional<ScanInfo> scan_text(std::string const& text,
|
std::optional<ScanInfo> scan_text(std::string const& text,
|
||||||
NodeType type,
|
NodeType type,
|
||||||
bool has_value);
|
bool has_value);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include "conf.hpp"
|
||||||
#include "Module.hpp"
|
#include "Module.hpp"
|
||||||
#include "Loc.hpp"
|
#include "Loc.hpp"
|
||||||
|
|
||||||
|
@ -14,6 +16,7 @@ namespace fk
|
||||||
|
|
||||||
void Module::build()
|
void Module::build()
|
||||||
{
|
{
|
||||||
|
import_std();
|
||||||
m_source = load_sources();
|
m_source = load_sources();
|
||||||
m_ast = m_parser->parse(m_source);
|
m_ast = m_parser->parse(m_source);
|
||||||
|
|
||||||
|
@ -22,6 +25,30 @@ namespace fk
|
||||||
m_vm->run();
|
m_vm->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::import_std()
|
||||||
|
{
|
||||||
|
import_library(FK_LIBDIR / "libfakir-std.so");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::import_library(std::filesystem::path lib_path)
|
||||||
|
{
|
||||||
|
void* handle = dlopen(lib_path.c_str(), RTLD_NOW);
|
||||||
|
assert(handle);
|
||||||
|
|
||||||
|
typedef void(*fun)(Module&);
|
||||||
|
fun f = (fun) dlsym(handle, "lib");
|
||||||
|
assert(f);
|
||||||
|
|
||||||
|
f(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::register_function(std::string const& name, native_t native)
|
||||||
|
{
|
||||||
|
auto fun = std::make_shared<NativeFunction>(native);
|
||||||
|
addr_t addr = m_vm->store_global(fun);
|
||||||
|
m_sym->declare_global(name, addr, Loc {"extern"});
|
||||||
|
}
|
||||||
|
|
||||||
std::string Module::load_sources()
|
std::string Module::load_sources()
|
||||||
{
|
{
|
||||||
std::ifstream file { m_source_path };
|
std::ifstream file { m_source_path };
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "Parser.hpp"
|
#include "Parser.hpp"
|
||||||
#include "Compiler.hpp"
|
#include "Compiler.hpp"
|
||||||
#include "VM.hpp"
|
#include "VM.hpp"
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
|
@ -19,13 +20,19 @@ namespace fk
|
||||||
|
|
||||||
void build();
|
void build();
|
||||||
|
|
||||||
|
void import_std();
|
||||||
|
void import_library(std::filesystem::path lib_path);
|
||||||
|
|
||||||
|
void register_function(std::string const& name, native_t native);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::filesystem::path m_source_path;
|
std::filesystem::path m_source_path;
|
||||||
std::string m_source;
|
std::string m_source;
|
||||||
Loc m_loc {m_source_path};
|
Loc m_loc {m_source_path};
|
||||||
std::shared_ptr<Lexer> m_lexer = std::make_shared<Lexer>(m_loc);
|
std::shared_ptr<Lexer> m_lexer = std::make_shared<Lexer>(m_loc);
|
||||||
std::shared_ptr<Parser> m_parser = std::make_shared<Parser>(*m_lexer);
|
std::shared_ptr<Parser> m_parser = std::make_shared<Parser>(*m_lexer);
|
||||||
std::shared_ptr<Compiler> m_compiler = std::make_shared<Compiler>();
|
std::shared_ptr<SymTable> m_sym = std::make_shared<SymTable>();
|
||||||
|
std::shared_ptr<Compiler> m_compiler = std::make_shared<Compiler>(m_sym);
|
||||||
std::shared_ptr<Node> m_ast;
|
std::shared_ptr<Node> m_ast;
|
||||||
std::shared_ptr<VM> m_vm = std::make_shared<VM>();
|
std::shared_ptr<VM> m_vm = std::make_shared<VM>();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
#include "NativeFunction.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
/*explicit*/ NativeFunction::NativeFunction(native_t native)
|
||||||
|
: m_native { native }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ NativeFunction::~NativeFunction()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Constant>
|
||||||
|
NativeFunction::call(std::vector<std::shared_ptr<Constant>> args)
|
||||||
|
{
|
||||||
|
return m_native(args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef fk_NATIVEFUNCTION_HPP
|
||||||
|
#define fk_NATIVEFUNCTION_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Constant.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
using native_t = std::function
|
||||||
|
<std::shared_ptr<Constant>(std::vector<std::shared_ptr<Constant>>)>;
|
||||||
|
|
||||||
|
class NativeFunction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit NativeFunction(native_t native);
|
||||||
|
virtual ~NativeFunction();
|
||||||
|
|
||||||
|
std::shared_ptr<Constant>
|
||||||
|
call(std::vector<std::shared_ptr<Constant>> args);
|
||||||
|
|
||||||
|
private:
|
||||||
|
native_t m_native;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -5,7 +5,8 @@
|
||||||
#include "Loc.hpp"
|
#include "Loc.hpp"
|
||||||
|
|
||||||
#define NODE_TYPES(G) \
|
#define NODE_TYPES(G) \
|
||||||
G(NODE_MODULE), G(NODE_INT), G(NODE_FLOAT), G(NODE_BOOL), G(NODE_STRING),
|
G(NODE_MODULE), G(NODE_INT), G(NODE_FLOAT), G(NODE_BOOL), G(NODE_STRING),\
|
||||||
|
G(NODE_IDENT), G(NODE_OPAR), G(NODE_CPAR), G(NODE_CALL)
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
|
|
|
@ -53,6 +53,11 @@ namespace fk
|
||||||
return consume();
|
return consume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type_is(NODE_OPAR))
|
||||||
|
{
|
||||||
|
return parse_call();
|
||||||
|
}
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "unknown expression '"
|
ss << "unknown expression '"
|
||||||
<< (NodeTypeStr[m_tokens[m_cursor]->type()] + strlen("NODE_"))
|
<< (NodeTypeStr[m_tokens[m_cursor]->type()] + strlen("NODE_"))
|
||||||
|
@ -63,6 +68,22 @@ namespace fk
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_call()
|
||||||
|
{
|
||||||
|
auto node = make_node(NODE_CALL);
|
||||||
|
consume(NODE_OPAR);
|
||||||
|
node->add_child(consume(NODE_IDENT));
|
||||||
|
|
||||||
|
while (type_isnt(NODE_CPAR))
|
||||||
|
{
|
||||||
|
node->add_child(parse_expr());
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(NODE_CPAR);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::make_node(NodeType type)
|
std::shared_ptr<Node> Parser::make_node(NodeType type)
|
||||||
{
|
{
|
||||||
return std::make_shared<Node>(type, "", loc());
|
return std::make_shared<Node>(type, "", loc());
|
||||||
|
|
|
@ -22,6 +22,7 @@ namespace fk
|
||||||
|
|
||||||
std::shared_ptr<Node> parse_module();
|
std::shared_ptr<Node> parse_module();
|
||||||
std::shared_ptr<Node> parse_expr();
|
std::shared_ptr<Node> parse_expr();
|
||||||
|
std::shared_ptr<Node> parse_call();
|
||||||
|
|
||||||
std::shared_ptr<Node> make_node(NodeType type);
|
std::shared_ptr<Node> make_node(NodeType type);
|
||||||
Loc loc() const;
|
Loc loc() const;
|
||||||
|
|
|
@ -22,6 +22,12 @@ namespace fk
|
||||||
return m_instrs[index];
|
return m_instrs[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Constant> Program::get_const(addr_t addr) const
|
||||||
|
{
|
||||||
|
assert(addr < const_size());
|
||||||
|
return m_consts[addr];
|
||||||
|
}
|
||||||
|
|
||||||
size_t Program::add(std::shared_ptr<Constant> constant)
|
size_t Program::add(std::shared_ptr<Constant> constant)
|
||||||
{
|
{
|
||||||
for (size_t i=0; i<m_consts.size(); i++)
|
for (size_t i=0; i<m_consts.size(); i++)
|
||||||
|
|
|
@ -25,6 +25,7 @@ namespace fk
|
||||||
|
|
||||||
size_t add(Opcode opcode, addr_t param=NO_PARAM);
|
size_t add(Opcode opcode, addr_t param=NO_PARAM);
|
||||||
Instr instr(size_t index) const;
|
Instr instr(size_t index) const;
|
||||||
|
std::shared_ptr<Constant> get_const(addr_t addr) const;
|
||||||
|
|
||||||
size_t add(std::shared_ptr<Constant> constant);
|
size_t add(std::shared_ptr<Constant> constant);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include "SymEntry.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
/*explicit*/ SymEntry::SymEntry(std::string const& name,
|
||||||
|
addr_t addr,
|
||||||
|
bool is_global,
|
||||||
|
Loc const& loc)
|
||||||
|
: m_name { name }
|
||||||
|
, m_addr { addr }
|
||||||
|
, m_is_global { is_global }
|
||||||
|
, m_loc { loc }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ SymEntry::~SymEntry()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SymEntry::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << m_name << "\t" << m_addr << "\t" << m_is_global;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef fk_SYMENTRY_HPP
|
||||||
|
#define fk_SYMENTRY_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Loc.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
class SymEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SymEntry(std::string const& name,
|
||||||
|
addr_t addr,
|
||||||
|
bool is_global,
|
||||||
|
Loc const& loc);
|
||||||
|
virtual ~SymEntry();
|
||||||
|
|
||||||
|
std::string name() const { return m_name; }
|
||||||
|
addr_t addr() const { return m_addr; }
|
||||||
|
bool is_global() const { return m_is_global; }
|
||||||
|
Loc loc() const { return m_loc; }
|
||||||
|
|
||||||
|
void set_global(bool global) { m_is_global = global; }
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_name;
|
||||||
|
addr_t m_addr;
|
||||||
|
bool m_is_global = false;
|
||||||
|
Loc m_loc;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
#include "src/Loc.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
/*explicit*/ SymTable::SymTable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ SymTable::~SymTable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymTable::declare_local(std::string const& name,
|
||||||
|
addr_t addr,
|
||||||
|
Loc const& loc)
|
||||||
|
{
|
||||||
|
auto entry = find(name);
|
||||||
|
|
||||||
|
if (entry)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "cannot declare existing variable '"
|
||||||
|
<< name
|
||||||
|
<< "'";
|
||||||
|
|
||||||
|
entry->loc().error<symbol_error>(LOG_ERROR, ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_entries.push_back(SymEntry {name, addr, false, loc});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymTable::declare_global(std::string const& name,
|
||||||
|
addr_t addr,
|
||||||
|
Loc const& loc)
|
||||||
|
{
|
||||||
|
declare_local(name, addr, loc);
|
||||||
|
m_entries.back().set_global(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<SymEntry> SymTable::find(std::string const& name)
|
||||||
|
{
|
||||||
|
for (auto& entry: m_entries)
|
||||||
|
{
|
||||||
|
if (entry.name() == name)
|
||||||
|
{
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SymTable::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
ss << "======== SymTable ========\n";
|
||||||
|
for (auto const& entry: m_entries)
|
||||||
|
{
|
||||||
|
ss << entry.string() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef fk_SYMTABLE_HPP
|
||||||
|
#define fk_SYMTABLE_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "SymEntry.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
FK_ERROR(symbol_error);
|
||||||
|
|
||||||
|
class SymTable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SymTable();
|
||||||
|
virtual ~SymTable();
|
||||||
|
|
||||||
|
void declare_local(std::string const& name,
|
||||||
|
addr_t addr,
|
||||||
|
Loc const& loc);
|
||||||
|
|
||||||
|
void declare_global(std::string const& name,
|
||||||
|
addr_t addr,
|
||||||
|
Loc const& loc);
|
||||||
|
|
||||||
|
std::optional<SymEntry> find(std::string const& name);
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<SymEntry> m_entries;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
43
src/VM.cpp
43
src/VM.cpp
|
@ -25,6 +25,32 @@ namespace fk
|
||||||
|
|
||||||
switch (instr.opcode)
|
switch (instr.opcode)
|
||||||
{
|
{
|
||||||
|
case OP_CALL_NATIVE: {
|
||||||
|
std::vector<std::shared_ptr<Constant>> args;
|
||||||
|
|
||||||
|
for (size_t i=0; i<instr.param; i++)
|
||||||
|
{
|
||||||
|
args.insert(std::begin(args),
|
||||||
|
frame().program->get_const(pop()));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ref = std::get<size_t>(frame().program
|
||||||
|
->get_const(pop())->value());
|
||||||
|
auto fun = std::get<std::shared_ptr<NativeFunction>>
|
||||||
|
(load_global(ref));
|
||||||
|
|
||||||
|
push(frame().program->add(fun->call(args)));
|
||||||
|
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_LOAD_GLOBAL: {
|
||||||
|
auto ref = std::make_shared<Constant>(TYPE_REF,
|
||||||
|
(size_t) instr.param,
|
||||||
|
Loc {"???"});
|
||||||
|
push(frame().program->add(ref));
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
case OP_LOAD_CONST: {
|
case OP_LOAD_CONST: {
|
||||||
push(instr.param);
|
push(instr.param);
|
||||||
|
@ -46,6 +72,23 @@ namespace fk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
global_t VM::load_global(addr_t addr) const
|
||||||
|
{
|
||||||
|
return m_globals.at(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VM::store_global(addr_t addr, global_t value)
|
||||||
|
{
|
||||||
|
m_globals[addr] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t VM::store_global(global_t value)
|
||||||
|
{
|
||||||
|
addr_t addr = m_globals.size();
|
||||||
|
store_global(addr, value);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
std::string VM::string() const
|
std::string VM::string() const
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
|
@ -3,9 +3,12 @@
|
||||||
|
|
||||||
#include "commons.hpp"
|
#include "commons.hpp"
|
||||||
#include "Program.hpp"
|
#include "Program.hpp"
|
||||||
|
#include "NativeFunction.hpp"
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
|
using global_t = std::variant<std::shared_ptr<NativeFunction>>;
|
||||||
|
|
||||||
struct Frame {
|
struct Frame {
|
||||||
std::shared_ptr<Program> program;
|
std::shared_ptr<Program> program;
|
||||||
};
|
};
|
||||||
|
@ -21,11 +24,17 @@ namespace fk
|
||||||
void mount(std::shared_ptr<Program> program);
|
void mount(std::shared_ptr<Program> program);
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
global_t load_global(addr_t addr) const;
|
||||||
|
void store_global(addr_t addr, global_t value);
|
||||||
|
size_t store_global(global_t value);
|
||||||
|
|
||||||
std::string string() const;
|
std::string string() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
addr_t m_pc = 0;
|
addr_t m_pc = 0;
|
||||||
std::vector<Frame> m_frames;
|
std::vector<Frame> m_frames;
|
||||||
std::vector<addr_t> m_stack;
|
std::vector<addr_t> m_stack;
|
||||||
|
std::unordered_map<addr_t, global_t> m_globals;
|
||||||
|
|
||||||
void push(addr_t addr);
|
void push(addr_t addr);
|
||||||
addr_t top();
|
addr_t top();
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef fk_CONF_HPP
|
#ifndef fk_CONF_HPP
|
||||||
#define fk_CONF_HPP
|
#define fk_CONF_HPP
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#define FK_VERSION "@version@"
|
#define FK_VERSION "@version@"
|
||||||
|
#define FK_LIBDIR std::filesystem::path("@libs@")
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#ifndef fk_OPCODES_HPP
|
#ifndef fk_OPCODES_HPP
|
||||||
#define fk_OPCODES_HPP
|
#define fk_OPCODES_HPP
|
||||||
|
|
||||||
#define OPCODES(G) G(OP_LOAD_CONST), G(OP_POP)
|
#define OPCODES(G) G(OP_LOAD_CONST), G(OP_POP), G(OP_CALL_NATIVE), \
|
||||||
|
G(OP_LOAD_GLOBAL)
|
||||||
|
|
||||||
#include "commons.hpp"
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
#include "commons.hpp"
|
#include "commons.hpp"
|
||||||
|
|
||||||
#define TYPES(G) G(TYPE_INT), G(TYPE_FLOAT), G(TYPE_BOOL), G(TYPE_STRING)
|
#define TYPES(G) G(TYPE_INT), G(TYPE_FLOAT), G(TYPE_BOOL), G(TYPE_STRING), \
|
||||||
|
G(TYPE_REF)
|
||||||
|
|
||||||
namespace fk
|
namespace fk
|
||||||
{
|
{
|
||||||
|
|
|
@ -63,3 +63,28 @@ TEST_CASE_METHOD(LexerTest, "Lexer_string")
|
||||||
test_next("STRING['false']");
|
test_next("STRING['false']");
|
||||||
test_end();
|
test_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_ident")
|
||||||
|
{
|
||||||
|
m_lexer.scan(" odd? hello-world! Aze06 _gdb ");
|
||||||
|
|
||||||
|
test_next("IDENT[odd?]");
|
||||||
|
test_next("IDENT[hello-world!]");
|
||||||
|
test_next("IDENT[Aze06]");
|
||||||
|
test_next("IDENT[_gdb]");
|
||||||
|
|
||||||
|
test_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_parenthesis")
|
||||||
|
{
|
||||||
|
m_lexer.scan(" () (aze) ");
|
||||||
|
|
||||||
|
test_next("OPAR");
|
||||||
|
test_next("CPAR");
|
||||||
|
|
||||||
|
test_next("OPAR");
|
||||||
|
test_next("IDENT[aze]");
|
||||||
|
test_next("CPAR");
|
||||||
|
test_end();
|
||||||
|
}
|
||||||
|
|
|
@ -29,3 +29,9 @@ TEST_CASE_METHOD(ParserTest, "Parser_builtins")
|
||||||
test_parse("MODULE(INT[4],FLOAT[3.0],BOOL[false],STRING['salut'])",
|
test_parse("MODULE(INT[4],FLOAT[3.0],BOOL[false],STRING['salut'])",
|
||||||
"4 3. false 'salut'");
|
"4 3. false 'salut'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(ParserTest, "Parser_call")
|
||||||
|
{
|
||||||
|
test_parse("MODULE(CALL(IDENT[bim],INT[2],STRING['3'],BOOL[false]))",
|
||||||
|
" (bim 2 '3' false) ");
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include "../src/SymTable.hpp"
|
||||||
|
|
||||||
|
class SymTableTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SymTableTest() {}
|
||||||
|
virtual ~SymTableTest() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
fk::Loc m_loc {"tests/symtable"};
|
||||||
|
fk::SymTable m_sym;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(SymTableTest, "SymTable_declare_var")
|
||||||
|
{
|
||||||
|
REQUIRE(std::nullopt == m_sym.find("hello"));
|
||||||
|
|
||||||
|
m_sym.declare_local("hello", 404, m_loc);
|
||||||
|
auto entry = m_sym.find("hello");
|
||||||
|
|
||||||
|
REQUIRE(std::nullopt != entry);
|
||||||
|
REQUIRE("hello" == entry->name());
|
||||||
|
REQUIRE(404 == entry->addr());
|
||||||
|
REQUIRE(false == entry->is_global());
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(m_sym.declare_local("hello", 407, m_loc),
|
||||||
|
fk::symbol_error);
|
||||||
|
}
|
Loading…
Reference in New Issue