Compare commits

..

No commits in common. "3eefed351c3c4cdc4c6731fb6d5909d4d1da859d" and "5219df318ed3a97d634beeea2d07e0035f7aa224" have entirely different histories.

46 changed files with 0 additions and 2208 deletions

4
.gitignore vendored
View File

@ -1,4 +0,0 @@
build
*~*
*\#*
.cache

View File

@ -1,13 +0,0 @@
DIR=build
.PHONY: build tests libs
build:
meson setup $(DIR)
meson compile -C $(DIR)
tests: build
$(DIR)/fakir-tests
install: tests
meson install -C $(DIR)

View File

@ -1,6 +0,0 @@
MODULE ::= EXPR*
EXPR ::=
| int | float | bool | string
| ident
| CALL
CALL opar ident EXPR* cpar

View File

@ -1,38 +0,0 @@
#!/usr/bin/env bash
TOTAL=0
SUCCESS=0
for file in $(find -name "*.fk")
do
MSG=$(fakir "$file" 2>&1 > /dev/null)
RES=$?
NAME=$(basename -s ".fk" $file)
echo -en "\e[34m$NAME ... \e[0m"
if [ $RES -eq 0 ]
then
echo -e "\e[32mok\e[0m"
SUCCESS=$(($SUCCESS + 1))
else
echo -e "\e[31mko\e[0m"
echo "$MSG"
fi
TOTAL=$(($TOTAL + 1))
done
FAIL=$(($TOTAL - $SUCCESS))
if [ $TOTAL -eq $SUCCESS ]
then
echo -e "\e[32m======== All tests passed ========\e[0m"
else
echo -e "\e[31m======== Some tests failed ========\e[0m"
fi
echo -e "\e[34msuccess\e[0m:\t$SUCCESS"
echo -e "\e[34mfailure\e[0m:\t$FAIL"
echo -e "\e[34mTOTAL\e[0m:\t\t$TOTAL"

View File

@ -1,14 +0,0 @@
#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>
FK_ERROR(assert_error);
#endif

View File

@ -1,46 +0,0 @@
#include "fun.hpp"
namespace fkstd
{
STDRET assert_eq(STDARGS args)
{
auto oracle = args[0];
auto expr = args[1];
if (!oracle->equals(*expr))
{
std::stringstream ss;
ss << "assertion failed: expected '"
<< oracle->string()
<< "', got '"
<< expr->string()
<< "'";
oracle->loc().error<assert_error>(LOG_ERROR, ss.str());
}
return std::make_shared<Constant>(TYPE_BOOL, true, oracle->loc());
}
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);
}
}

View File

@ -1,11 +0,0 @@
#ifndef fkstd_FUN_HPP
#define fkstd_FUN_HPP
#include "commons.hpp"
namespace fkstd
{
STDRET assert_eq(STDARGS args);
STDRET println(STDARGS args);
}
#endif

View File

@ -1,11 +0,0 @@
#include "commons.hpp"
#include "fun.hpp"
#include "macro.hpp"
extern "C" void lib(Module& mod)
{
mod.register_function("assert=", fkstd::assert_eq);
mod.register_function("println", fkstd::println);
mod.register_macro("assert-static-fail", fkstd::assert_static_fail);
}

View File

@ -1,25 +0,0 @@
#include "macro.hpp"
namespace fkstd
{
void assert_static_fail(Compiler& compiler,
std::shared_ptr<Node> node,
std::shared_ptr<Program> program)
{
try
{
compiler.compile_prog(node->child(1), program);
}
catch(std::exception const&)
{
program->load_const(std::make_shared<Constant>(TYPE_BOOL,
true,
node->loc()));
return;
}
std::stringstream ss;
ss << "assertion failed";
node->loc().error<assert_error>(LOG_ERROR, ss.str());
}
}

View File

@ -1,12 +0,0 @@
#ifndef fkstd_MACRO_HPP
#define fkstd_MACRO_HPP
#include "commons.hpp"
namespace fkstd
{
void assert_static_fail(Compiler& compiler,
std::shared_ptr<Node> node,
std::shared_ptr<Program> program);
}
#endif

View File

@ -1,111 +0,0 @@
project('fakir',
'cpp',
version: '0.0.0',
default_options: [
'warning_level=3',
'cpp_std=c++17',
'prefix=/usr',
])
extra_libs = get_option('prefix') / get_option('libdir') / 'fakir'
# CONFIGURATION
# =============
conf = configuration_data()
conf.set('version', meson.project_version())
conf.set('libs', extra_libs)
configure_file(input: 'src/conf.in.hpp',
output: 'conf.hpp',
configuration: conf)
# CORE LIB
# ========
fakir_cpp = [
'src/Node.cpp',
'src/Loc.cpp',
'src/Lexer.cpp',
'src/Parser.cpp',
'src/Constant.cpp',
'src/NativeFunction.cpp',
'src/NativeMacro.cpp',
'src/SymTable.cpp',
'src/SymEntry.cpp',
'src/Compiler.cpp',
'src/VM.cpp',
'src/Program.cpp',
'src/Module.cpp',
]
fakir_hpp = [
'src/Node.hpp',
'src/Loc.hpp',
'src/Lexer.hpp',
'src/Parser.hpp',
'src/Constant.hpp',
'src/SymTable.hpp',
'src/SymEntry.hpp',
'src/Compiler.hpp',
'src/VM.hpp',
'src/Program.hpp',
'src/Module.hpp',
'src/NativeFunction.hpp',
'src/NativeMacro.hpp',
'src/opcodes.hpp',
'src/commons.hpp',
'src/types.hpp',
]
fakir_lib = shared_library('fakir',
sources: fakir_cpp,
install: true)
install_headers(fakir_hpp, subdir: 'fakir')
fakir_dep = declare_dependency(
link_with: fakir_lib
)
# STD LIB
# =======
shared_library('fakir-std',
sources: [
'libstd/lib.cpp',
'libstd/fun.cpp',
'libstd/macro.cpp',
],
dependencies: [
fakir_dep
],
install_dir: extra_libs,
install: true)
# COMPILER
# ========
executable('fakir',
sources: [
'src/main.cpp',
],
dependencies: [
fakir_dep
],
install: true)
# TESTS
# =====
executable('fakir-tests',
sources: [
'tests/main.cpp',
'tests/Lexer.cpp',
'tests/Parser.cpp',
'tests/SymTable.cpp',
],
dependencies: [
fakir_dep,
dependency('catch2')
])

View File

@ -1,118 +0,0 @@
#include "Compiler.hpp"
namespace fk
{
/*explicit*/ Compiler::Compiler(std::shared_ptr<SymTable> sym)
: m_sym { sym }
{
}
/*virtual*/ Compiler::~Compiler()
{
}
void Compiler::add_macro(std::string const& name,
std::shared_ptr<NativeMacro> macro)
{
m_macros[name] = macro;
}
std::shared_ptr<Program> Compiler::compile(std::shared_ptr<Node> node)
{
auto prog = std::make_shared<Program>();
compile_prog(node, prog);
return prog;
}
void Compiler::compile_prog(std::shared_ptr<Node> node,
std::shared_ptr<Program> prog)
{
switch (node->type())
{
case NODE_MODULE: {
for (size_t i=0; i<node->size(); i++)
{
compile_prog(node->child(i), prog);
prog->add(OP_POP);
}
} break;
case NODE_CALL: {
std::string ident = node->child(0)->repr();
if (auto itr=m_macros.find(ident);
itr != std::end(m_macros))
{
auto macro = itr->second;
macro->call(*this, node, prog);
}
else
{
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: {
prog->load_const(std::make_shared<Constant>(TYPE_INT,
stoi(node->repr()),
node->loc()));
} break;
case NODE_FLOAT: {
prog->load_const(std::make_shared<Constant>(TYPE_FLOAT,
stof(node->repr()),
node->loc()));
} break;
case NODE_BOOL: {
prog->load_const(std::make_shared<Constant>(TYPE_BOOL,
node->repr() == "true",
node->loc()));
} break;
case NODE_STRING: {
std::string str = node->repr();
prog->load_const(std::make_shared<Constant>(TYPE_STRING,
str.substr(1, str.size() - 2),
node->loc()));
} break;
default: {
std::stringstream ss;
ss << "cannot compile expression '"
<< (NodeTypeStr[node->type()] + strlen("NODE_"))
<< "'";
node->loc().error<compile_error>(LOG_ERROR, ss.str());
} break;
}
}
}

View File

@ -1,36 +0,0 @@
#ifndef fk_COMPILER_HPP
#define fk_COMPILER_HPP
#include "commons.hpp"
#include "Program.hpp"
#include "Node.hpp"
#include "SymTable.hpp"
#include "NativeMacro.hpp"
namespace fk
{
FK_ERROR(compile_error);
class Compiler
{
public:
explicit Compiler(std::shared_ptr<SymTable> sym);
virtual ~Compiler();
void add_macro(std::string const& name,
std::shared_ptr<NativeMacro> macro);
std::shared_ptr<Program> compile(std::shared_ptr<Node> node);
void compile_prog(std::shared_ptr<Node> node,
std::shared_ptr<Program> prog);
private:
std::shared_ptr<SymTable> m_sym;
std::unordered_map<std::string,
std::shared_ptr<NativeMacro>> m_macros;
};
}
#endif

View File

@ -1,44 +0,0 @@
#include "Constant.hpp"
namespace fk
{
/*explicit*/ Constant::Constant(Type type, constant_t value, Loc const& loc)
: m_type { type }
, m_value { value }
, m_loc { loc }
{
}
/*virtual*/ Constant::~Constant()
{
}
std::string Constant::string() const
{
switch (m_type)
{
case TYPE_INT: return std::to_string(std::get<int>(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_STRING: return std::get<std::string>(m_value);
case TYPE_REF: return std::to_string(std::get<size_t>(m_value));
default: {
std::stringstream ss;
ss << "cannot stringify '"
<< (TypeStr[m_type] + strlen("TYPE_"))
<< "'";
m_loc.error<constant_error>(LOG_ERROR, ss.str());
} break;
}
return "";
}
bool Constant::equals(Constant const& rhs) const
{
return m_type == rhs.m_type && m_value == rhs.m_value;
}
}

View File

@ -1,34 +0,0 @@
#ifndef fk_CONSTANT_HPP
#define fk_CONSTANT_HPP
#include "commons.hpp"
#include "types.hpp"
#include "Loc.hpp"
namespace fk
{
FK_ERROR(constant_error);
using constant_t = std::variant<int, float, bool, std::string, size_t>;
class Constant
{
public:
explicit Constant(Type type, constant_t value, Loc const& loc);
virtual ~Constant();
Type type() const { return m_type; }
constant_t value() const { return m_value; }
Loc loc() const { return m_loc; }
std::string string() const;
bool equals(Constant const& rhs) const;
private:
Type m_type;
constant_t m_value;
Loc m_loc;
};
}
#endif

View File

@ -1,300 +0,0 @@
#include "Lexer.hpp"
#include "src/Loc.hpp"
namespace fk
{
/*explicit*/ Lexer::Lexer(Loc const& loc)
: m_loc { loc }
{
std::vector<std::tuple<NodeType, std::string, bool>> text = {
{NODE_OPAR, "(", false},
{NODE_CPAR, ")", false},
{NODE_BOOL, "true", true},
{NODE_BOOL, "false", true}
};
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,
this,
std::get<1>(txt),
std::get<0>(txt),
std::get<2>(txt)));
}
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_string, this));
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
}
/*virtual*/ Lexer::~Lexer()
{
}
void Lexer::scan(std::string const& source)
{
m_source = source;
m_cursor = 0;
}
std::shared_ptr<Node> Lexer::next()
{
std::optional<ScanInfo> best_info;
skip_spaces();
while (m_cursor < m_source.size()
&& m_source[m_cursor] == ';')
{
while (m_cursor < m_source.size()
&& m_source[m_cursor] != '\n')
{
m_cursor++;
}
skip_spaces();
}
for (auto const& scanner: m_scanners)
{
auto info = scanner();
if (info
&& (best_info == std::nullopt
|| info->cursor > best_info->cursor))
{
best_info = info;
}
}
if (best_info)
{
m_cursor = best_info->cursor;
return std::make_shared<Node>(best_info->type,
best_info->repr,
m_loc);
}
if (m_cursor < m_source.size())
{
std::string text;
while (m_cursor < m_source.size()
&& !std::isspace(m_source[m_cursor]))
{
text += m_source[m_cursor];
m_cursor++;
}
m_loc.error<lexical_error>(LOG_ERROR,
"unexpected text '" + text + "'");
}
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()
{
while (m_cursor < m_source.size()
&& std::isspace(m_source[m_cursor]))
{
if (m_source[m_cursor] == '\n')
{
m_loc = Loc {m_loc.path(), m_loc.line() + 1};
}
m_cursor++;
}
}
std::optional<ScanInfo> Lexer::scan_int()
{
size_t cursor = m_cursor;
std::string repr;
if (m_source[cursor] == '-')
{
repr += "-";
cursor++;
}
while (cursor < m_source.size()
&& std::isdigit(m_source[cursor]))
{
repr += m_source[cursor];
cursor++;
}
if (repr.empty() || repr.back() == '-')
{
return std::nullopt;
}
return ScanInfo {
cursor,
NODE_INT,
repr
};
}
std::optional<ScanInfo> Lexer::scan_float()
{
size_t cursor = m_cursor;
std::string repr;
if (m_source[cursor] == '-')
{
repr += "-";
cursor++;
}
while (cursor < m_source.size()
&& std::isdigit(m_source[cursor]))
{
repr += m_source[cursor];
cursor++;
}
if (m_source[cursor] == '.')
{
repr += ".";
cursor++;
}
else
{
return std::nullopt;
}
while (cursor < m_source.size()
&& std::isdigit(m_source[cursor]))
{
repr += m_source[cursor];
cursor++;
}
if (repr.empty() || repr.back() == '-' || repr == ".")
{
return std::nullopt;
}
if (repr.front() == '.')
{
repr = "0" + repr;
}
if (repr.back() == '.')
{
repr += "0";
}
return ScanInfo {
cursor,
NODE_FLOAT,
repr
};
}
std::optional<ScanInfo> Lexer::scan_string()
{
size_t cursor = m_cursor;
std::string repr = "'";
if (cursor > m_source.size()
|| m_source[cursor] != '\'')
{
return std::nullopt;
}
cursor++;
while (cursor < m_source.size()
&& m_source[cursor] != '\'')
{
repr += m_source[cursor];
cursor++;
}
if (cursor < m_source.size()
&& m_source[cursor] == '\'')
{
repr += "'";
cursor++;
return ScanInfo {
cursor,
NODE_STRING,
repr
};
}
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,
NodeType type,
bool has_value)
{
if (m_cursor + text.size() > m_source.size())
{
return std::nullopt;
}
for (size_t i=0; i<text.size(); i++)
{
if (m_source[m_cursor + i] != text[i])
{
return std::nullopt;
}
}
return ScanInfo {
m_cursor + text.size(),
type,
has_value ? text : ""
};
}
}

View File

@ -1,49 +0,0 @@
#ifndef fk_LEXER_HPP
#define fk_LEXER_HPP
#include "commons.hpp"
#include "Loc.hpp"
#include "Node.hpp"
namespace fk
{
FK_ERROR(lexical_error);
struct ScanInfo {
size_t cursor;
NodeType type;
std::string repr;
};
using scanner_t = std::function<std::optional<ScanInfo>()>;
class Lexer
{
public:
explicit Lexer(Loc const& loc);
virtual ~Lexer();
void scan(std::string const& source);
std::shared_ptr<Node> next();
private:
Loc m_loc;
std::string m_source;
size_t m_cursor = 0;
std::vector<scanner_t> m_scanners;
std::vector<char> m_separators;
bool is_sep(size_t index) const;
void skip_spaces();
std::optional<ScanInfo> scan_int();
std::optional<ScanInfo> scan_float();
std::optional<ScanInfo> scan_string();
std::optional<ScanInfo> scan_ident();
std::optional<ScanInfo> scan_text(std::string const& text,
NodeType type,
bool has_value);
};
}
#endif

View File

@ -1,14 +0,0 @@
#include "Loc.hpp"
namespace fk
{
/*explicit*/ Loc::Loc(std::filesystem::path path, int line)
: m_path { path }
, m_line { line }
{
}
/*virtual*/ Loc::~Loc()
{
}
}

View File

@ -1,39 +0,0 @@
#ifndef fk_LOC_HPP
#define fk_LOC_HPP
#include "commons.hpp"
#define LOG_TYPES(G) \
G(LOG_ERROR)
namespace fk
{
FK_ENUM(LogType, LOG_TYPES);
class Loc
{
public:
explicit Loc(std::filesystem::path path, int line=0);
virtual ~Loc();
std::filesystem::path path() const { return m_path; }
int line() const { return m_line; }
template <typename T>
void error(LogType type, std::string const& what) const;
private:
std::filesystem::path m_path;
int m_line;
};
template <typename T>
void Loc::error(LogType type, std::string const& what) const
{
throw T {m_path.string() + ":" + std::to_string(m_line)
+ " " + (LogTypeStr[type] + strlen("LOG_")) + " " + what};
}
}
#endif

View File

@ -1,82 +0,0 @@
#include <dlfcn.h>
#include "conf.hpp"
#include "Module.hpp"
#include "Loc.hpp"
#include "NativeMacro.hpp"
namespace fk
{
/*explicit*/ Module::Module(std::filesystem::path source_path)
: m_source_path { source_path }
{
}
/*virtual*/ Module::~Module()
{
}
void Module::build()
{
import_std();
m_source = load_sources();
m_ast = m_parser->parse(m_source);
auto program = m_compiler->compile(m_ast);
m_vm->mount(program);
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"});
}
void Module::register_macro(std::string const& name, macro_t macro)
{
auto m = std::make_shared<NativeMacro>(macro);
m_compiler->add_macro(name, m);
}
std::string Module::load_sources()
{
std::ifstream file { m_source_path };
Loc loc { m_source_path };
std::string source;
if (!file)
{
std::stringstream ss;
ss << "cannot load module '" << m_source_path.string() << "'";
loc.error<module_error>(LOG_ERROR, ss.str());
}
std::string line;
source = "";
while (std::getline(file, line))
{
source += line + (file.eof() ? "" : "\n");
}
return source;
}
}

View File

@ -1,44 +0,0 @@
#ifndef fk_MODULE_HPP
#define fk_MODULE_HPP
#include "commons.hpp"
#include "Lexer.hpp"
#include "Parser.hpp"
#include "Compiler.hpp"
#include "VM.hpp"
#include "SymTable.hpp"
namespace fk
{
FK_ERROR(module_error);
class Module
{
public:
explicit Module(std::filesystem::path source_path);
virtual ~Module();
void build();
void import_std();
void import_library(std::filesystem::path lib_path);
void register_function(std::string const& name, native_t native);
void register_macro(std::string const& name, macro_t macro);
private:
std::filesystem::path m_source_path;
std::string m_source;
Loc m_loc {m_source_path};
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<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<VM> m_vm = std::make_shared<VM>();
std::string load_sources();
};
}
#endif

View File

@ -1,19 +0,0 @@
#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);
}
}

View File

@ -1,26 +0,0 @@
#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

View File

@ -1,23 +0,0 @@
#include "NativeMacro.hpp"
#include "Compiler.hpp"
#include "Program.hpp"
#include "Node.hpp"
namespace fk
{
/*explicit*/ NativeMacro::NativeMacro(macro_t macro)
: m_macro { macro }
{
}
/*virtual*/ NativeMacro::~NativeMacro()
{
}
void NativeMacro::call(Compiler& compiler,
std::shared_ptr<Node> node,
std::shared_ptr<Program> program)
{
m_macro(compiler, node, program);
}
}

View File

@ -1,30 +0,0 @@
#ifndef fk_NATIVEMACRO_HPP
#define fk_NATIVEMACRO_HPP
#include "commons.hpp"
namespace fk
{
class Compiler;
class Program;
class Node;
using macro_t = std::function<void(Compiler&,
std::shared_ptr<Node>,
std::shared_ptr<Program>)>;
class NativeMacro
{
public:
explicit NativeMacro(macro_t macro);
virtual ~NativeMacro();
void call(Compiler& compiler,
std::shared_ptr<Node> node,
std::shared_ptr<Program> program);
private:
macro_t m_macro;
};
}
#endif

View File

@ -1,55 +0,0 @@
#include "Node.hpp"
namespace fk
{
/*explicit*/ Node::Node(NodeType type,
std::string const& repr,
Loc const& loc)
: m_type { type }
, m_repr { repr }
, m_loc { loc }
{
}
/*virtual*/ Node::~Node()
{
}
void Node::add_child(std::shared_ptr<Node> child)
{
m_children.push_back(child);
}
std::shared_ptr<Node> Node::child(size_t index) const
{
assert(index < size());
return m_children.at(index);
}
std::string Node::string() const
{
std::stringstream ss;
ss << (NodeTypeStr[m_type] + strlen("NODE_"));
if (m_repr.empty() == false)
{
ss << "[" << m_repr << "]";
}
if (size() > 0)
{
ss << "(";
std::string sep;
for (auto child: m_children)
{
ss << sep << child->string();
sep = ",";
}
ss << ")";
}
return ss.str();
}
}

View File

@ -1,41 +0,0 @@
#ifndef fk_NODE_HPP
#define fk_NODE_HPP
#include "commons.hpp"
#include "Loc.hpp"
#define NODE_TYPES(G) \
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
{
FK_ENUM(NodeType, NODE_TYPES);
class Node
{
public:
explicit Node(NodeType type,
std::string const& repr,
Loc const& loc);
virtual ~Node();
NodeType type() const { return m_type; }
std::string repr() const { return m_repr; }
Loc loc() const { return m_loc; }
size_t size() const { return m_children.size(); }
void add_child(std::shared_ptr<Node> child);
std::shared_ptr<Node> child(size_t index) const;
std::string string() const;
private:
NodeType m_type;
std::string m_repr;
Loc m_loc;
std::vector<std::shared_ptr<Node>> m_children;
};
}
#endif

View File

@ -1,172 +0,0 @@
#include "Parser.hpp"
#include "src/Loc.hpp"
namespace fk
{
/*explicit*/ Parser::Parser(Lexer& lexer)
: m_lexer { lexer }
{
}
/*virtual*/ Parser::~Parser()
{
}
std::shared_ptr<Node> Parser::parse(std::string const& source)
{
m_lexer.scan(source);
std::shared_ptr<Node> tok;
m_tokens.clear();
m_cursor = 0;
while ( (tok=m_lexer.next()) )
{
m_tokens.push_back(tok);
}
if (m_tokens.empty())
{
Loc loc {"???"};
auto node = std::make_shared<Node>(NODE_MODULE, "", loc);
return node;
}
return parse_module();
}
std::shared_ptr<Node> Parser::parse_module()
{
auto node = make_node(NODE_MODULE);
while (m_cursor < m_tokens.size())
{
node->add_child(parse_expr());
}
return node;
}
std::shared_ptr<Node> Parser::parse_expr()
{
if (type_any({NODE_INT, NODE_FLOAT, NODE_BOOL, NODE_STRING}))
{
return consume();
}
if (type_is(NODE_OPAR))
{
return parse_call();
}
std::stringstream ss;
ss << "unknown expression '"
<< (NodeTypeStr[m_tokens[m_cursor]->type()] + strlen("NODE_"))
<< "'";
loc().error<syntax_error>(LOG_ERROR, ss.str());
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)
{
return std::make_shared<Node>(type, "", loc());
}
Loc Parser::loc() const
{
if (m_cursor < m_tokens.size())
{
return m_tokens[m_cursor]->loc();
}
else if (m_tokens.empty() == false)
{
return m_tokens.back()->loc();
}
else
{
Loc loc {"???"};
return loc;
}
}
bool Parser::type_is(NodeType type) const
{
return type_all({type});
}
bool Parser::type_isnt(NodeType type) const
{
return !type_is(type);
}
bool Parser::type_all(std::vector<NodeType> types) const
{
for (size_t i=0; i<types.size(); i++)
{
if (m_cursor + i >= m_tokens.size()
|| m_tokens[m_cursor + i]->type() != types[i])
{
return false;
}
}
return true;
}
bool Parser::type_any(std::vector<NodeType> types) const
{
for (size_t i=0; i<types.size(); i++)
{
if (m_cursor < m_tokens.size()
&& m_tokens[m_cursor]->type() == types[i])
{
return true;
}
}
return false;
}
std::shared_ptr<Node> Parser::consume()
{
assert(m_cursor < m_tokens.size());
auto node = m_tokens[m_cursor];
m_cursor++;
return node;
}
std::shared_ptr<Node> Parser::consume(NodeType type)
{
if (type_isnt(type))
{
std::stringstream ss;
ss << "expected '"
<< (NodeTypeStr[type] + strlen("NODE_"))
<< "', got '"
<< (NodeTypeStr[m_tokens[m_cursor]->type()] + strlen("NODE_"))
<< "'";
loc().error<syntax_error>(LOG_ERROR, ss.str());
}
return consume();
}
}

View File

@ -1,39 +0,0 @@
#ifndef fk_PARSER_HPP
#define fk_PARSER_HPP
#include "commons.hpp"
#include "Lexer.hpp"
namespace fk
{
FK_ERROR(syntax_error);
class Parser
{
public:
explicit Parser(Lexer& lexer);
virtual ~Parser();
std::shared_ptr<Node> parse(std::string const& source);
private:
Lexer& m_lexer;
std::vector<std::shared_ptr<Node>> m_tokens;
size_t m_cursor = 0;
std::shared_ptr<Node> parse_module();
std::shared_ptr<Node> parse_expr();
std::shared_ptr<Node> parse_call();
std::shared_ptr<Node> make_node(NodeType type);
Loc loc() const;
bool type_is(NodeType type) const;
bool type_isnt(NodeType type) const;
bool type_all(std::vector<NodeType> types) const;
bool type_any(std::vector<NodeType> types) const;
std::shared_ptr<Node> consume();
std::shared_ptr<Node> consume(NodeType type);
};
}
#endif

View File

@ -1,79 +0,0 @@
#include "Program.hpp"
namespace fk
{
/*explicit*/ Program::Program()
{
}
/*virtual*/ Program::~Program()
{
}
size_t Program::add(Opcode opcode, addr_t param)
{
m_instrs.push_back({opcode, param});
return m_instrs.size() - 1;
}
Instr Program::instr(size_t index) const
{
assert(index < size());
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)
{
for (size_t i=0; i<m_consts.size(); i++)
{
if (m_consts[i]->equals(*constant))
{
return i;
}
}
m_consts.push_back(constant);
return m_consts.size() - 1;
}
size_t Program::load_const(std::shared_ptr<Constant> constant)
{
size_t addr = add(constant);
return add(OP_LOAD_CONST, addr);
}
std::string Program::string() const
{
std::stringstream ss;
ss << std::setw(2) << std::left;
ss << "======== ByteCode ========" << std::endl;
for (size_t i=0; i<m_instrs.size(); i++)
{
ss << i << "\t" << (OpcodeStr[m_instrs[i].opcode] + strlen("OP_"));
if (m_instrs[i].param != NO_PARAM)
{
ss << "\t" << m_instrs[i].param << std::endl;
}
else
{
ss << std::endl;
}
}
ss << "======== Constants ========" << std::endl;
for (size_t i=0; i<m_consts.size(); i++)
{
ss << i << "\t" << m_consts[i]->string() << std::endl;
}
return ss.str();
}
}

View File

@ -1,42 +0,0 @@
#ifndef fk_PROGRAM_HPP
#define fk_PROGRAM_HPP
#include "commons.hpp"
#include "opcodes.hpp"
#include "Constant.hpp"
namespace fk
{
struct Instr {
Opcode opcode;
addr_t param;
};
constexpr addr_t NO_PARAM = -1;
class Program
{
public:
explicit Program();
virtual ~Program();
size_t size() const { return m_instrs.size(); }
size_t const_size() const { return m_consts.size(); }
size_t add(Opcode opcode, addr_t param=NO_PARAM);
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 load_const(std::shared_ptr<Constant> constant);
std::string string() const;
private:
std::vector<Instr> m_instrs;
std::vector<std::shared_ptr<Constant>> m_consts;
};
}
#endif

View File

@ -1,26 +0,0 @@
#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();
}
}

View File

@ -1,35 +0,0 @@
#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

View File

@ -1,66 +0,0 @@
#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();
}
}

View File

@ -1,34 +0,0 @@
#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

View File

@ -1,123 +0,0 @@
#include "VM.hpp"
namespace fk
{
/*explicit*/ VM::VM()
{
}
/*virtual*/ VM::~VM()
{
}
void VM::mount(std::shared_ptr<Program> program)
{
Frame frame;
frame.program = program;
m_frames.push_back(frame);
}
void VM::run()
{
while (m_pc < frame().program->size())
{
Instr instr = frame().program->instr(m_pc);
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: {
push(instr.param);
m_pc++;
} break;
case OP_POP: {
pop();
m_pc++;
} break;
default: {
std::cerr << "cannot run unknown opcode '"
<< (OpcodeStr[instr.opcode] + strlen("OP_"))
<< "'"<< std::endl;
abort();
} break;
}
}
}
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::stringstream ss;
ss << "======== VM Stack ========" << "\n";
ss << std::setw(8) << std::left;
for (size_t i=0; i<m_stack.size(); i++)
{
ss << i << "\t" <<m_stack[i] << std::endl;
}
return ss.str();
}
void VM::push(addr_t addr)
{
m_stack.push_back(addr);
}
addr_t VM::top()
{
assert(m_stack.empty() == false);
return m_stack.back();
}
addr_t VM::pop()
{
addr_t t = top();
m_stack.pop_back();
return t;
}
}

View File

@ -1,45 +0,0 @@
#ifndef fk_VM_HPP
#define fk_VM_HPP
#include "commons.hpp"
#include "Program.hpp"
#include "NativeFunction.hpp"
namespace fk
{
using global_t = std::variant<std::shared_ptr<NativeFunction>>;
struct Frame {
std::shared_ptr<Program> program;
};
class VM
{
public:
explicit VM();
virtual ~VM();
Frame frame() const { return m_frames.back(); }
void mount(std::shared_ptr<Program> program);
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;
private:
addr_t m_pc = 0;
std::vector<Frame> m_frames;
std::vector<addr_t> m_stack;
std::unordered_map<addr_t, global_t> m_globals;
void push(addr_t addr);
addr_t top();
addr_t pop();
};
}
#endif

View File

@ -1,29 +0,0 @@
#ifndef fk_COMMONS_HPP
#define fk_COMMONS_HPP
#include <cassert>
#include <cstring>
#include <sstream>
#include <functional>
#include <optional>
#include <variant>
#include <fstream>
#include <iostream>
#include <vector>
#include <filesystem>
using addr_t = size_t;
#define FK_GEN_ENUM(X) X
#define FK_GEN_STRING(X) #X
#define FK_ENUM(PREFIX, DEFINITION) \
enum PREFIX { DEFINITION(FK_GEN_ENUM) }; \
constexpr char const* PREFIX ## Str [] = { DEFINITION(FK_GEN_STRING) }
#define FK_ERROR(NAME) \
struct NAME : public std::runtime_error { \
NAME (std::string const& what) : std::runtime_error(what ) {} \
}
#endif

View File

@ -1,8 +0,0 @@
#ifndef fk_CONF_HPP
#define fk_CONF_HPP
#include <filesystem>
#define FK_VERSION "@version@"
#define FK_LIBDIR std::filesystem::path("@libs@")
#endif

View File

@ -1,79 +0,0 @@
#include <iostream>
#include <getopt.h>
#include "commons.hpp"
#include "Module.hpp"
int main(int argc, char** argv)
{
bool debug_mode = false;
while (true)
{
static struct option options[] = {
{"help", no_argument, 0, 'h'},
{"debug", no_argument, 0, 'd'},
{0, 0, 0, 0}
};
int opt_index = 0;
int c = getopt_long(argc, argv, "h", options, &opt_index);
if (c == -1)
{
break;
}
switch (c)
{
case 'h': {
std::stringstream ss;
ss << std::setw(8) << std::left;
ss << "Usage: fakir <options> <sources>" << std::endl
<< std::endl;
ss << "[OPTIONS]" << std::endl;
ss << "-h, --help" << "\t" << "show this message." << std::endl;
std::cout << ss.str() << std::endl;
return 0;
} break;
case 'd': {
debug_mode = true;
} break;
default: break;
}
}
std::vector<std::filesystem::path> sources;
while (optind < argc)
{
sources.push_back(argv[optind]);
optind++;
}
if (sources.size())
{
fk::Module mod {sources[0]};
if (debug_mode)
{
mod.build();
}
else
{
try
{
mod.build();
}
catch(std::exception const& err)
{
std::cerr << err.what() << std::endl;
return -1;
}
}
}
return 0;
}

View File

@ -1,14 +0,0 @@
#ifndef fk_OPCODES_HPP
#define fk_OPCODES_HPP
#define OPCODES(G) G(OP_LOAD_CONST), G(OP_POP), G(OP_CALL_NATIVE), \
G(OP_LOAD_GLOBAL)
#include "commons.hpp"
namespace fk
{
FK_ENUM(Opcode, OPCODES);
}
#endif

View File

@ -1,14 +0,0 @@
#ifndef fk_TYPES_HPP
#define fk_TYPES_HPP
#include "commons.hpp"
#define TYPES(G) G(TYPE_INT), G(TYPE_FLOAT), G(TYPE_BOOL), G(TYPE_STRING), \
G(TYPE_REF)
namespace fk
{
FK_ENUM(Type, TYPES);
}
#endif

View File

@ -1,90 +0,0 @@
#include <catch2/catch.hpp>
#include "../src/Lexer.hpp"
class LexerTest
{
public:
explicit LexerTest() {}
virtual ~LexerTest() {}
void test_next(std::string const& oracle)
{
auto node = m_lexer.next();
REQUIRE(node);
REQUIRE(oracle == node->string());
}
void test_end()
{
auto node = m_lexer.next();
REQUIRE(nullptr == node);
}
protected:
fk::Loc m_loc {"tests/lexer"};
fk::Lexer m_lexer { m_loc };
};
TEST_CASE_METHOD(LexerTest, "Lexer_int")
{
m_lexer.scan(" 34 7 -296");
test_next("INT[34]");
test_next("INT[7]");
test_next("INT[-296]");
test_end();
}
TEST_CASE_METHOD(LexerTest, "Lexer_float")
{
m_lexer.scan(" .34 7.3 -296. ");
test_next("FLOAT[0.34]");
test_next("FLOAT[7.3]");
test_next("FLOAT[-296.0]");
test_end();
}
TEST_CASE_METHOD(LexerTest, "Lexer_bool")
{
m_lexer.scan(" true false ");
test_next("BOOL[true]");
test_next("BOOL[false]");
test_end();
}
TEST_CASE_METHOD(LexerTest, "Lexer_string")
{
m_lexer.scan(" 'true ' 'false' ");
test_next("STRING['true ']");
test_next("STRING['false']");
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();
}

View File

@ -1,37 +0,0 @@
#include <catch2/catch.hpp>
#include "../src/Parser.hpp"
class ParserTest
{
public:
explicit ParserTest() {}
virtual ~ParserTest() {}
void test_parse(std::string const& oracle, std::string const& source)
{
auto node = m_parser.parse(source);
REQUIRE(oracle == node->string());
}
protected:
fk::Loc m_loc {"tests/parser"};
fk::Lexer m_lexer { m_loc };
fk::Parser m_parser { m_lexer };
};
TEST_CASE_METHOD(ParserTest, "Parser_empty")
{
test_parse("MODULE", "");
}
TEST_CASE_METHOD(ParserTest, "Parser_builtins")
{
test_parse("MODULE(INT[4],FLOAT[3.0],BOOL[false],STRING['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) ");
}

View File

@ -1,29 +0,0 @@
#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);
}

View File

@ -1,2 +0,0 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>