ADD: builtin type literals (int, float, bool, string).
parent
5219df318e
commit
8c46189b20
|
@ -0,0 +1,4 @@
|
||||||
|
build
|
||||||
|
*~*
|
||||||
|
*\#*
|
||||||
|
.cache
|
|
@ -0,0 +1,13 @@
|
||||||
|
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)
|
|
@ -0,0 +1,3 @@
|
||||||
|
MODULE ::= EXPR*
|
||||||
|
EXPR ::=
|
||||||
|
| int | float | bool | string
|
|
@ -0,0 +1,84 @@
|
||||||
|
project('fakir',
|
||||||
|
'cpp',
|
||||||
|
version: '0.0.0',
|
||||||
|
default_options: [
|
||||||
|
'warning_level=3',
|
||||||
|
'cpp_std=c++17',
|
||||||
|
'prefix=/usr',
|
||||||
|
])
|
||||||
|
|
||||||
|
# CONFIGURATION
|
||||||
|
# =============
|
||||||
|
conf = configuration_data()
|
||||||
|
conf.set('version', meson.project_version())
|
||||||
|
|
||||||
|
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/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/Compiler.hpp',
|
||||||
|
'src/VM.hpp',
|
||||||
|
'src/Program.hpp',
|
||||||
|
'src/Module.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
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
fakir_dep,
|
||||||
|
dependency('catch2')
|
||||||
|
])
|
|
@ -0,0 +1,69 @@
|
||||||
|
#include "Compiler.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
/*explicit*/ Compiler::Compiler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Compiler::~Compiler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Program> Compiler::compile(std::shared_ptr<Node> node)
|
||||||
|
{
|
||||||
|
auto prog = std::make_shared<Program>();
|
||||||
|
compile(node, prog);
|
||||||
|
return prog;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::compile(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(node->child(i), prog);
|
||||||
|
prog->add(OP_POP);
|
||||||
|
}
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef fk_COMPILER_HPP
|
||||||
|
#define fk_COMPILER_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
#include "Node.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
FK_ERROR(compile_error);
|
||||||
|
|
||||||
|
class Compiler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Compiler();
|
||||||
|
virtual ~Compiler();
|
||||||
|
|
||||||
|
std::shared_ptr<Program> compile(std::shared_ptr<Node> node);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void compile(std::shared_ptr<Node> node,
|
||||||
|
std::shared_ptr<Program> prog);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,43 @@
|
||||||
|
#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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#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>;
|
||||||
|
|
||||||
|
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
|
|
@ -0,0 +1,254 @@
|
||||||
|
#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_BOOL, "true", true},
|
||||||
|
{NODE_BOOL, "false", true}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto txt: text)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*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 end near '" + text + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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 : ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
#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;
|
||||||
|
|
||||||
|
void skip_spaces();
|
||||||
|
|
||||||
|
std::optional<ScanInfo> scan_int();
|
||||||
|
std::optional<ScanInfo> scan_float();
|
||||||
|
std::optional<ScanInfo> scan_string();
|
||||||
|
std::optional<ScanInfo> scan_text(std::string const& text,
|
||||||
|
NodeType type,
|
||||||
|
bool has_value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include "Loc.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
/*explicit*/ Loc::Loc(std::filesystem::path path, int line)
|
||||||
|
: m_path { path }
|
||||||
|
, m_line { line }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Loc::~Loc()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
#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
|
|
@ -0,0 +1,48 @@
|
||||||
|
#include "Module.hpp"
|
||||||
|
#include "Loc.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
/*explicit*/ Module::Module(std::filesystem::path source_path)
|
||||||
|
: m_source_path { source_path }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Module::~Module()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::build()
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef fk_MODULE_HPP
|
||||||
|
#define fk_MODULE_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Lexer.hpp"
|
||||||
|
#include "Parser.hpp"
|
||||||
|
#include "Compiler.hpp"
|
||||||
|
#include "VM.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
FK_ERROR(module_error);
|
||||||
|
|
||||||
|
class Module
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Module(std::filesystem::path source_path);
|
||||||
|
virtual ~Module();
|
||||||
|
|
||||||
|
void build();
|
||||||
|
|
||||||
|
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<Compiler> m_compiler = std::make_shared<Compiler>();
|
||||||
|
std::shared_ptr<Node> m_ast;
|
||||||
|
std::shared_ptr<VM> m_vm = std::make_shared<VM>();
|
||||||
|
|
||||||
|
std::string load_sources();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,55 @@
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
#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),
|
||||||
|
|
||||||
|
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
|
|
@ -0,0 +1,151 @@
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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::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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
#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> 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
|
|
@ -0,0 +1,73 @@
|
||||||
|
#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];
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
#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;
|
||||||
|
|
||||||
|
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
|
|
@ -0,0 +1,80 @@
|
||||||
|
#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_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef fk_VM_HPP
|
||||||
|
#define fk_VM_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
private:
|
||||||
|
addr_t m_pc = 0;
|
||||||
|
std::vector<Frame> m_frames;
|
||||||
|
std::vector<addr_t> m_stack;
|
||||||
|
|
||||||
|
void push(addr_t addr);
|
||||||
|
addr_t top();
|
||||||
|
addr_t pop();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,29 @@
|
||||||
|
#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
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef fk_CONF_HPP
|
||||||
|
#define fk_CONF_HPP
|
||||||
|
|
||||||
|
#define FK_VERSION "@version@"
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Module.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
static struct option options[] = {
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{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;
|
||||||
|
|
||||||
|
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]};
|
||||||
|
mod.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef fk_OPCODES_HPP
|
||||||
|
#define fk_OPCODES_HPP
|
||||||
|
|
||||||
|
#define OPCODES(G) G(OP_LOAD_CONST), G(OP_POP)
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
FK_ENUM(Opcode, OPCODES);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,13 @@
|
||||||
|
#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)
|
||||||
|
|
||||||
|
namespace fk
|
||||||
|
{
|
||||||
|
FK_ENUM(Type, TYPES);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,65 @@
|
||||||
|
#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();
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
#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'");
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include <catch2/catch.hpp>
|
Loading…
Reference in New Issue