ADD: built-in literals and comments.
parent
672964c442
commit
6443724c68
|
@ -0,0 +1,4 @@
|
||||||
|
.cache
|
||||||
|
*~*
|
||||||
|
*\#*
|
||||||
|
build
|
|
@ -0,0 +1,11 @@
|
||||||
|
.PHONY: build tests
|
||||||
|
|
||||||
|
build:
|
||||||
|
meson setup build
|
||||||
|
meson compile -C build
|
||||||
|
|
||||||
|
tests: build
|
||||||
|
build/skemla-tests
|
||||||
|
|
||||||
|
install: tests
|
||||||
|
meson install -C build
|
|
@ -0,0 +1,2 @@
|
||||||
|
PROG ::= BUILTIN*
|
||||||
|
BUILTINS ::= int | float | bool | string
|
|
@ -0,0 +1,57 @@
|
||||||
|
#include "Compiler.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
#include "Value.hpp"
|
||||||
|
#include "Node.hpp"
|
||||||
|
#include "lib/opcodes.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
/*explicit*/ Compiler::Compiler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Compiler::~Compiler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::compile(std::shared_ptr<Node> node,
|
||||||
|
std::shared_ptr<Program> program)
|
||||||
|
{
|
||||||
|
switch (node->type())
|
||||||
|
{
|
||||||
|
case NODE_PROG: {
|
||||||
|
for (size_t i=0; i<node->size(); i++)
|
||||||
|
{
|
||||||
|
compile(node->child(i).lock(), program);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_INT: {
|
||||||
|
auto val = Value::make_int(std::stoi(node->repr()));
|
||||||
|
program->push_instr(OPCODE_PUSH_CONST, program->push_value(val));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_FLOAT: {
|
||||||
|
auto val = Value::make_float(std::stof(node->repr()));
|
||||||
|
program->push_instr(OPCODE_PUSH_CONST, program->push_value(val));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_BOOL: {
|
||||||
|
auto val = Value::make_bool(node->repr() == "true");
|
||||||
|
program->push_instr(OPCODE_PUSH_CONST, program->push_value(val));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_STRING: {
|
||||||
|
auto val = Value::make_string(node->repr());
|
||||||
|
program->push_instr(OPCODE_PUSH_CONST, program->push_value(val));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error {std::string()
|
||||||
|
+ "cannot compile unknown node '"
|
||||||
|
+ NodeTypeStr[node->type()]
|
||||||
|
+ "'"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef sk_COMPILER_HPP
|
||||||
|
#define sk_COMPILER_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
class Program;
|
||||||
|
class Node;
|
||||||
|
|
||||||
|
class Compiler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Compiler();
|
||||||
|
virtual ~Compiler();
|
||||||
|
|
||||||
|
void compile(std::shared_ptr<Node> node,
|
||||||
|
std::shared_ptr<Program> program);
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,28 @@
|
||||||
|
#include "Factory.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
/*explicit*/ Factory::Factory(std::filesystem::path path
|
||||||
|
, Logger& logger)
|
||||||
|
: m_path { path }
|
||||||
|
, m_logger { logger }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Factory::~Factory()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Lexer> Factory::make_lexer()
|
||||||
|
{
|
||||||
|
auto lexer = std::make_shared<Lexer>(m_path, m_logger);
|
||||||
|
return lexer;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Parser> Factory::make_parser()
|
||||||
|
{
|
||||||
|
auto lexer = make_lexer();
|
||||||
|
auto parser = std::make_shared<Parser>(lexer, m_logger);
|
||||||
|
return parser;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef sk_FACTORY_HPP
|
||||||
|
#define sk_FACTORY_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Logger.hpp"
|
||||||
|
#include "Lexer.hpp"
|
||||||
|
#include "Parser.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
class Factory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Factory(std::filesystem::path path, Logger& logger);
|
||||||
|
virtual ~Factory();
|
||||||
|
|
||||||
|
std::shared_ptr<Lexer> make_lexer();
|
||||||
|
std::shared_ptr<Parser> make_parser();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::filesystem::path m_path;
|
||||||
|
Logger& m_logger;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,286 @@
|
||||||
|
#include "Lexer.hpp"
|
||||||
|
#include "lib/Node.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
/*explicit*/ Lexer::Lexer(std::filesystem::path path, Logger& logger)
|
||||||
|
: m_loc {path, 1}
|
||||||
|
, m_logger { logger }
|
||||||
|
{
|
||||||
|
std::vector<std::tuple<std::string, NodeType, bool>> texts = {
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::tuple<std::string, NodeType, bool>> keywords = {
|
||||||
|
{"true", NODE_BOOL, true},
|
||||||
|
{"false", NODE_BOOL, true}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto entry: texts)
|
||||||
|
{
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_text,
|
||||||
|
this,
|
||||||
|
std::get<0>(entry),
|
||||||
|
std::get<1>(entry),
|
||||||
|
std::get<2>(entry)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto entry: keywords)
|
||||||
|
{
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_keyword,
|
||||||
|
this,
|
||||||
|
std::get<0>(entry),
|
||||||
|
std::get<1>(entry),
|
||||||
|
std::get<2>(entry)));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_string, this));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_float, this));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_int, this));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Lexer::~Lexer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lexer::scan(std::string const& source)
|
||||||
|
{
|
||||||
|
m_source = source;
|
||||||
|
m_cursor = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Lexer::next()
|
||||||
|
{
|
||||||
|
size_t cursor = m_cursor;
|
||||||
|
std::shared_ptr<Node> node;
|
||||||
|
|
||||||
|
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 scanner: m_scanners)
|
||||||
|
{
|
||||||
|
ScanInfo info = scanner();
|
||||||
|
|
||||||
|
if (info.ok && info.cursor > cursor)
|
||||||
|
{
|
||||||
|
node = std::make_shared<Node>(info.type, info.repr, m_loc);
|
||||||
|
cursor = info.cursor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node
|
||||||
|
&& m_cursor < m_source.size())
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
std::string text;
|
||||||
|
size_t cursor = m_cursor;
|
||||||
|
while (cursor < m_source.size()
|
||||||
|
&& !std::isspace(m_source[cursor]))
|
||||||
|
{
|
||||||
|
text += m_source[cursor];
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << "invalid token '" << text << "'.";
|
||||||
|
|
||||||
|
m_logger.log<lexical_error>(m_loc,
|
||||||
|
LOGGER_ERROR,
|
||||||
|
ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cursor = cursor;
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanInfo Lexer::scan_int()
|
||||||
|
{
|
||||||
|
size_t cursor = m_cursor;
|
||||||
|
std::string repr;
|
||||||
|
|
||||||
|
while (cursor < m_source.size()
|
||||||
|
&& std::isdigit(m_source[cursor]))
|
||||||
|
{
|
||||||
|
repr += m_source[cursor];
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repr.empty() == false)
|
||||||
|
{
|
||||||
|
return ScanInfo {
|
||||||
|
true,
|
||||||
|
cursor,
|
||||||
|
repr,
|
||||||
|
NODE_INT
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScanInfo {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanInfo Lexer::scan_float()
|
||||||
|
{
|
||||||
|
size_t cursor = m_cursor;
|
||||||
|
std::string repr;
|
||||||
|
|
||||||
|
while (cursor < m_source.size()
|
||||||
|
&& std::isdigit(m_source[cursor]))
|
||||||
|
{
|
||||||
|
repr += m_source[cursor];
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor >= m_source.size()
|
||||||
|
|| m_source[cursor] != '.')
|
||||||
|
{
|
||||||
|
return ScanInfo {};
|
||||||
|
}
|
||||||
|
|
||||||
|
repr += ".";
|
||||||
|
cursor++;
|
||||||
|
|
||||||
|
while (cursor < m_source.size()
|
||||||
|
&& std::isdigit(m_source[cursor]))
|
||||||
|
{
|
||||||
|
repr += m_source[cursor];
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repr.empty() == false && repr[0] != '.'
|
||||||
|
&& repr[repr.size() - 1] != '.')
|
||||||
|
{
|
||||||
|
return ScanInfo {
|
||||||
|
true,
|
||||||
|
cursor,
|
||||||
|
repr,
|
||||||
|
NODE_FLOAT
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScanInfo {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanInfo Lexer::scan_text(std::string const& text,
|
||||||
|
NodeType type,
|
||||||
|
bool value)
|
||||||
|
{
|
||||||
|
if (m_cursor + text.size() > m_source.size())
|
||||||
|
{
|
||||||
|
return ScanInfo {};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i<text.size(); i++)
|
||||||
|
{
|
||||||
|
if (text[i] != m_source[m_cursor + i])
|
||||||
|
{
|
||||||
|
return ScanInfo {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScanInfo {
|
||||||
|
true,
|
||||||
|
m_cursor + text.size(),
|
||||||
|
value ? text : "",
|
||||||
|
type
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanInfo Lexer::scan_keyword(std::string const& text,
|
||||||
|
NodeType type,
|
||||||
|
bool value)
|
||||||
|
{
|
||||||
|
if (m_cursor + text.size() > m_source.size())
|
||||||
|
{
|
||||||
|
return ScanInfo {};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i<text.size(); i++)
|
||||||
|
{
|
||||||
|
if (text[i] != m_source[m_cursor + i])
|
||||||
|
{
|
||||||
|
return ScanInfo {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = m_cursor + text.size();
|
||||||
|
|
||||||
|
if (index < m_source.size()
|
||||||
|
&& std::isalnum(m_source[index]))
|
||||||
|
{
|
||||||
|
return ScanInfo {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScanInfo {
|
||||||
|
true,
|
||||||
|
m_cursor + text.size(),
|
||||||
|
value ? text : "",
|
||||||
|
type
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanInfo Lexer::scan_string()
|
||||||
|
{
|
||||||
|
size_t cursor = m_cursor;
|
||||||
|
std::string repr;
|
||||||
|
|
||||||
|
if (cursor >= m_source.size()
|
||||||
|
|| m_source[cursor] != '\'')
|
||||||
|
{
|
||||||
|
return ScanInfo {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor++;
|
||||||
|
repr += "'";
|
||||||
|
|
||||||
|
while (cursor < m_source.size()
|
||||||
|
&& m_source[cursor] != '\'')
|
||||||
|
{
|
||||||
|
repr += m_source[cursor];
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor >= m_source.size()
|
||||||
|
|| m_source[cursor] != '\'')
|
||||||
|
{
|
||||||
|
return ScanInfo {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor++;
|
||||||
|
repr += "'";
|
||||||
|
|
||||||
|
return ScanInfo {
|
||||||
|
true,
|
||||||
|
cursor,
|
||||||
|
repr,
|
||||||
|
NODE_STRING
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef sk_LEXER_HPP
|
||||||
|
#define sk_LEXER_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Node.hpp"
|
||||||
|
#include "Logger.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
SK_ERROR(lexical_error);
|
||||||
|
|
||||||
|
struct ScanInfo {
|
||||||
|
bool ok = false;
|
||||||
|
size_t cursor = 0;
|
||||||
|
std::string repr;
|
||||||
|
NodeType type;
|
||||||
|
};
|
||||||
|
|
||||||
|
using scanner_t = std::function<ScanInfo()>;
|
||||||
|
|
||||||
|
class Lexer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Lexer(std::filesystem::path path, Logger& logger);
|
||||||
|
virtual ~Lexer();
|
||||||
|
|
||||||
|
Loc loc() const { return m_loc; }
|
||||||
|
|
||||||
|
void scan(std::string const& source);
|
||||||
|
std::shared_ptr<Node> next();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Loc m_loc;
|
||||||
|
Logger& m_logger;
|
||||||
|
std::string m_source;
|
||||||
|
size_t m_cursor = 0;
|
||||||
|
std::vector<scanner_t> m_scanners;
|
||||||
|
|
||||||
|
void skip_spaces();
|
||||||
|
ScanInfo scan_int();
|
||||||
|
ScanInfo scan_float();
|
||||||
|
ScanInfo scan_text(std::string const& text,
|
||||||
|
NodeType type,
|
||||||
|
bool value);
|
||||||
|
|
||||||
|
ScanInfo scan_keyword(std::string const& text,
|
||||||
|
NodeType type,
|
||||||
|
bool value);
|
||||||
|
|
||||||
|
ScanInfo scan_string();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include "Loc.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
/*explicit*/ Loc::Loc(std::filesystem::path path, int line)
|
||||||
|
: m_path { path }
|
||||||
|
, m_line { line }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Loc::~Loc()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef sk_LOC_HPP
|
||||||
|
#define sk_LOC_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
class Loc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Loc(std::filesystem::path path, int line);
|
||||||
|
virtual ~Loc();
|
||||||
|
|
||||||
|
std::filesystem::path path() const { return m_path; }
|
||||||
|
int line() const { return m_line; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::filesystem::path m_path;
|
||||||
|
int m_line;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include "Logger.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
/*explicit*/ Logger::Logger()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Logger::~Logger()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef sk_LOGGER_HPP
|
||||||
|
#define sk_LOGGER_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
#include "Loc.hpp"
|
||||||
|
|
||||||
|
#define LOGGER_TYPE(G) \
|
||||||
|
G(LOGGER_ERROR)
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
SK_ENUM(Logger, LOGGER_TYPE);
|
||||||
|
|
||||||
|
class Logger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Logger();
|
||||||
|
virtual ~Logger();
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void log(Loc loc, LoggerType type, std::stringstream const& what);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void log(Loc loc, LoggerType type, std::string const& what);
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void Logger::log(Loc loc, LoggerType type, std::stringstream const& what)
|
||||||
|
{
|
||||||
|
log<T>(loc, type, what.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void Logger::log(Loc loc, LoggerType type, std::string const& what)
|
||||||
|
{
|
||||||
|
std::string type_str = std::string(LoggerTypeStr[type])
|
||||||
|
.substr(std::string("LOGGER_").size());
|
||||||
|
std::stringstream msg;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case LOGGER_ERROR: {
|
||||||
|
msg << loc.path().string() << ":" << loc.line();
|
||||||
|
msg << " " << type_str << " " << what;
|
||||||
|
throw T {msg.str()};
|
||||||
|
} break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,54 @@
|
||||||
|
#include "Node.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
/*explicit*/ Node::Node(NodeType type, std::string const repr, Loc 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::weak_ptr<Node> Node::child(size_t index) const
|
||||||
|
{
|
||||||
|
assert(index < size());
|
||||||
|
return m_children[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Node::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::string(NodeTypeStr[m_type])
|
||||||
|
.substr(std::string("NODE_").size());
|
||||||
|
|
||||||
|
if (!m_repr.empty())
|
||||||
|
{
|
||||||
|
ss << "[" << m_repr << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_children.empty())
|
||||||
|
{
|
||||||
|
ss << "(";
|
||||||
|
std::string sep;
|
||||||
|
|
||||||
|
for (auto child: m_children)
|
||||||
|
{
|
||||||
|
ss << sep << child->string();
|
||||||
|
sep = ",";
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef sk_NODE_HPP
|
||||||
|
#define sk_NODE_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Loc.hpp"
|
||||||
|
|
||||||
|
#define NODE_TYPE(G) \
|
||||||
|
G(NODE_PROG), \
|
||||||
|
G(NODE_INT), \
|
||||||
|
G(NODE_FLOAT), \
|
||||||
|
G(NODE_BOOL), \
|
||||||
|
G(NODE_STRING),
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
SK_ENUM(Node, NODE_TYPE);
|
||||||
|
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Node(NodeType type, std::string const repr, Loc 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::weak_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,99 @@
|
||||||
|
#include "Parser.hpp"
|
||||||
|
#include "lib/Node.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
/*explicit*/ Parser::Parser(std::shared_ptr<Lexer> lexer, Logger& logger)
|
||||||
|
: m_lexer { lexer }
|
||||||
|
, m_logger { logger }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Parser::~Parser()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse(std::string const& source)
|
||||||
|
{
|
||||||
|
m_lexer->scan(source);
|
||||||
|
std::shared_ptr<Node> node;
|
||||||
|
|
||||||
|
while ( (node = m_lexer->next()) )
|
||||||
|
{
|
||||||
|
m_tokens.push_back(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse_prog();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::consume()
|
||||||
|
{
|
||||||
|
assert(m_cursor < m_tokens.size());
|
||||||
|
|
||||||
|
auto root = m_tokens[m_cursor];
|
||||||
|
m_cursor++;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::consume(NodeType type)
|
||||||
|
{
|
||||||
|
if (type != m_tokens[m_cursor]->type())
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "expected '"
|
||||||
|
<< NodeTypeStr[type]
|
||||||
|
<< "' node, got '"
|
||||||
|
<< NodeTypeStr[m_tokens[m_cursor]->type()]
|
||||||
|
<< "'";
|
||||||
|
|
||||||
|
m_logger.log<syntax_error>(m_tokens[m_cursor]->loc(),
|
||||||
|
LOGGER_ERROR,
|
||||||
|
ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
return consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Parser::type_is(NodeType type)
|
||||||
|
{
|
||||||
|
return m_cursor < m_tokens.size()
|
||||||
|
&& m_tokens[m_cursor]->type() == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loc Parser::loc() const
|
||||||
|
{
|
||||||
|
assert(m_cursor < m_tokens.size());
|
||||||
|
return m_tokens[m_cursor]->loc();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_prog()
|
||||||
|
{
|
||||||
|
auto root = std::make_shared<Node>(NODE_PROG, "", m_lexer->loc());
|
||||||
|
|
||||||
|
while (m_cursor < m_tokens.size())
|
||||||
|
{
|
||||||
|
root->add_child(parse_builtins());
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_builtins()
|
||||||
|
{
|
||||||
|
if (type_is(NODE_INT)
|
||||||
|
|| type_is(NODE_FLOAT)
|
||||||
|
|| type_is(NODE_BOOL)
|
||||||
|
|| type_is(NODE_STRING))
|
||||||
|
{
|
||||||
|
return consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "unknown node of type '"
|
||||||
|
<< NodeTypeStr[m_tokens[m_cursor]->type()]
|
||||||
|
<< "'";
|
||||||
|
|
||||||
|
m_logger.log<syntax_error>(loc(), LOGGER_ERROR, ss);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef sk_PARSER_HPP
|
||||||
|
#define sk_PARSER_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Lexer.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
SK_ERROR(syntax_error);
|
||||||
|
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Parser(std::shared_ptr<Lexer> lexer, Logger& logger);
|
||||||
|
virtual ~Parser();
|
||||||
|
|
||||||
|
std::shared_ptr<Node> parse(std::string const& source);
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Lexer> m_lexer;
|
||||||
|
Logger& m_logger;
|
||||||
|
std::vector<std::shared_ptr<Node>> m_tokens;
|
||||||
|
size_t m_cursor = 0;
|
||||||
|
|
||||||
|
std::shared_ptr<Node> consume();
|
||||||
|
std::shared_ptr<Node> consume(NodeType type);
|
||||||
|
bool type_is(NodeType type);
|
||||||
|
Loc loc() const;
|
||||||
|
|
||||||
|
std::shared_ptr<Node> parse_prog();
|
||||||
|
std::shared_ptr<Node> parse_builtins();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,74 @@
|
||||||
|
#include "Program.hpp"
|
||||||
|
#include "Value.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
/*explicit*/ Program::Program()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Program::~Program()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Program::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
for (auto const& instr: m_instrs)
|
||||||
|
{
|
||||||
|
ss << i << "\t";
|
||||||
|
ss << std::string(OpcodeTypeStr[instr.opcode])
|
||||||
|
.substr(std::string("OPCODE_").size());
|
||||||
|
|
||||||
|
if (instr.param)
|
||||||
|
{
|
||||||
|
ss << "\t" << *instr.param;
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << "\n";
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Program::push_instr(OpcodeType opcode,
|
||||||
|
std::optional<param_t> param)
|
||||||
|
{
|
||||||
|
m_instrs.push_back({opcode, param});
|
||||||
|
}
|
||||||
|
|
||||||
|
Instr const& Program::instr(size_t index) const
|
||||||
|
{
|
||||||
|
assert(index < size());
|
||||||
|
return m_instrs[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
Instr& Program::instr(size_t index)
|
||||||
|
{
|
||||||
|
assert(index < size());
|
||||||
|
return m_instrs[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Program::push_value(std::shared_ptr<Value> value)
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<m_values.size(); i++)
|
||||||
|
{
|
||||||
|
if (value->equals(*m_values[i]))
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_values.push_back(value);
|
||||||
|
return m_values.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Value> Program::value(size_t index)
|
||||||
|
{
|
||||||
|
assert(index < m_values.size());
|
||||||
|
return m_values[index];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef sk_PROGRAM_HPP
|
||||||
|
#define sk_PROGRAM_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "opcodes.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
struct Instr {
|
||||||
|
OpcodeType opcode;
|
||||||
|
std::optional<param_t> param;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Value;
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Program();
|
||||||
|
virtual ~Program();
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
size_t size() const { return m_instrs.size(); }
|
||||||
|
size_t value_size() const { return m_values.size(); }
|
||||||
|
|
||||||
|
void push_instr(OpcodeType opcode,
|
||||||
|
std::optional<param_t> param = std::nullopt);
|
||||||
|
|
||||||
|
Instr const& instr(size_t index) const;
|
||||||
|
Instr& instr(size_t index);
|
||||||
|
|
||||||
|
size_t push_value(std::shared_ptr<Value> value);
|
||||||
|
std::shared_ptr<Value> value(size_t index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Instr> m_instrs;
|
||||||
|
std::vector<std::shared_ptr<Value>> m_values;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include "Type.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
/*explicit*/ Type::Type(TypeType type)
|
||||||
|
: m_type { type }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Type::~Type()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef sk_TYPE_HPP
|
||||||
|
#define sk_TYPE_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
#define TYPE_TYPE(G) \
|
||||||
|
G(TYPE_INT), \
|
||||||
|
G(TYPE_FLOAT), \
|
||||||
|
G(TYPE_BOOL), \
|
||||||
|
G(TYPE_STRING),
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
SK_ENUM(Type, TYPE_TYPE);
|
||||||
|
|
||||||
|
class Type
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Type(TypeType type);
|
||||||
|
virtual ~Type();
|
||||||
|
|
||||||
|
TypeType type() const { return m_type; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
TypeType m_type;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,86 @@
|
||||||
|
#include "VM.hpp"
|
||||||
|
#include "Value.hpp"
|
||||||
|
#include "lib/opcodes.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
/*explicit*/ VM::VM()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ VM::~VM()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VM::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
ss << "--- constants ---" << "\n";
|
||||||
|
for (size_t i=0; i<m_frames.back().program->value_size(); i++)
|
||||||
|
{
|
||||||
|
ss << i << "\t" << m_frames.back().program->value(i)->string()
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << "--- stack ---" << "\n";
|
||||||
|
|
||||||
|
for (size_t i=0; i<m_stack.size(); i++)
|
||||||
|
{
|
||||||
|
ss << i << "\t" << m_stack[i] << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VM::exec(std::shared_ptr<Program> program)
|
||||||
|
{
|
||||||
|
Frame frame;
|
||||||
|
frame.program = program;
|
||||||
|
m_frames.push_back(frame);
|
||||||
|
m_pc = 0;
|
||||||
|
|
||||||
|
exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VM::exec()
|
||||||
|
{
|
||||||
|
while (m_pc < program()->size())
|
||||||
|
{
|
||||||
|
Instr instr = program()->instr(m_pc);
|
||||||
|
|
||||||
|
switch (instr.opcode)
|
||||||
|
{
|
||||||
|
|
||||||
|
case OPCODE_PUSH_CONST: {
|
||||||
|
push(*instr.param);
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error {std::string()
|
||||||
|
+ "vm cannot execute unknown opcode '"
|
||||||
|
+ OpcodeTypeStr[instr.opcode]
|
||||||
|
+ "'"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Program> VM::program() const
|
||||||
|
{
|
||||||
|
return m_frames.back().program;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VM::push(param_t param)
|
||||||
|
{
|
||||||
|
m_stack.push_back(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
param_t VM::pop()
|
||||||
|
{
|
||||||
|
assert(m_stack.empty() == false);
|
||||||
|
auto param = m_stack.back();
|
||||||
|
m_stack.pop_back();
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef sk_VM_HPP
|
||||||
|
#define sk_VM_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
struct Frame {
|
||||||
|
std::shared_ptr<Program> program;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VM
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit VM();
|
||||||
|
virtual ~VM();
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
void exec(std::shared_ptr<Program> program);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Frame> m_frames;
|
||||||
|
std::vector<param_t> m_stack;
|
||||||
|
|
||||||
|
size_t m_pc = 0;
|
||||||
|
|
||||||
|
void exec();
|
||||||
|
|
||||||
|
std::shared_ptr<Program> program() const;
|
||||||
|
|
||||||
|
void push(param_t param);
|
||||||
|
param_t pop();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,80 @@
|
||||||
|
#include "Value.hpp"
|
||||||
|
#include "Type.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
/*static*/ std::shared_ptr<Value>
|
||||||
|
Value::make_int(int value)
|
||||||
|
{
|
||||||
|
auto res =
|
||||||
|
std::make_shared<Value>(std::make_shared<Type>(TYPE_INT));
|
||||||
|
res->m_int_val = value;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*static*/ std::shared_ptr<Value>
|
||||||
|
Value::make_float(float value)
|
||||||
|
{
|
||||||
|
auto res =
|
||||||
|
std::make_shared<Value>(std::make_shared<Type>(TYPE_FLOAT));
|
||||||
|
res->m_float_val = value;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*static*/ std::shared_ptr<Value>
|
||||||
|
Value::make_bool(bool value)
|
||||||
|
{
|
||||||
|
auto res =
|
||||||
|
std::make_shared<Value>(std::make_shared<Type>(TYPE_BOOL));
|
||||||
|
res->m_bool_val = value;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*static*/ std::shared_ptr<Value>
|
||||||
|
Value::make_string(std::string const& value)
|
||||||
|
{
|
||||||
|
auto res =
|
||||||
|
std::make_shared<Value>(std::make_shared<Type>(TYPE_STRING));
|
||||||
|
res->m_string_val = value;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*explicit*/ Value::Value(std::shared_ptr<Type> type)
|
||||||
|
: m_type { type }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Value::~Value()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Value::equals(Value const& rhs) const
|
||||||
|
{
|
||||||
|
return m_int_val == rhs.m_int_val
|
||||||
|
&& m_float_val == rhs.m_float_val
|
||||||
|
&& m_bool_val == rhs.m_bool_val
|
||||||
|
&& m_string_val == rhs.m_string_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Value::string() const
|
||||||
|
{
|
||||||
|
switch (m_type->type())
|
||||||
|
{
|
||||||
|
case TYPE_INT: return std::to_string(as_int());
|
||||||
|
case TYPE_FLOAT: return std::to_string(as_float());
|
||||||
|
case TYPE_BOOL: return as_bool() ? "true" : "false";
|
||||||
|
case TYPE_STRING: return as_string().substr(1, as_string().size()-2);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error {std::string()
|
||||||
|
+ "cannot stringify unknown type '"
|
||||||
|
+ TypeTypeStr[m_type->type()]
|
||||||
|
+ "'"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::weak_ptr<Type> Value::type() const
|
||||||
|
{
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef sk_VALUE_HPP
|
||||||
|
#define sk_VALUE_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
namespace sk
|
||||||
|
{
|
||||||
|
class Type;
|
||||||
|
|
||||||
|
class Value
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<Value> make_int(int value);
|
||||||
|
static std::shared_ptr<Value> make_float(float value);
|
||||||
|
static std::shared_ptr<Value> make_bool(bool value);
|
||||||
|
static std::shared_ptr<Value> make_string(std::string const& value);
|
||||||
|
|
||||||
|
explicit Value(std::shared_ptr<Type> type);
|
||||||
|
virtual ~Value();
|
||||||
|
|
||||||
|
bool equals(Value const& rhs) const;
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
|
||||||
|
std::weak_ptr<Type> type() const;
|
||||||
|
|
||||||
|
int as_int() const { return *m_int_val; }
|
||||||
|
float as_float() const { return *m_float_val; }
|
||||||
|
bool as_bool() const { return *m_bool_val; }
|
||||||
|
std::string as_string() const { return *m_string_val; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Type> m_type;
|
||||||
|
std::optional<int> m_int_val;
|
||||||
|
std::optional<float> m_float_val;
|
||||||
|
std::optional<std::string> m_string_val;
|
||||||
|
std::optional<bool> m_bool_val;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef sk_COMMONS_HPP
|
||||||
|
#define sk_COMMONS_HPP
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <optional>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "mutils.hpp"
|
||||||
|
|
||||||
|
using param_t = size_t;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef sk_MUTILS_HPP
|
||||||
|
#define sk_MUTILS_HPP
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#define ENUM_ENUM(X) X
|
||||||
|
#define ENUM_STRING(X) #X
|
||||||
|
|
||||||
|
#define SK_ENUM(PREFIX, ENUM) \
|
||||||
|
enum PREFIX ## Type { ENUM(ENUM_ENUM) }; \
|
||||||
|
constexpr char const* PREFIX ## TypeStr [] = { ENUM(ENUM_STRING) }
|
||||||
|
|
||||||
|
#define SK_ERROR(NAME) \
|
||||||
|
struct NAME : public std::runtime_error { \
|
||||||
|
NAME (std::string const& what) : std::runtime_error(what) {} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef sk_OPCODES_HPP
|
||||||
|
#define sk_OPCODES_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
#define OPCODE(G) \
|
||||||
|
G(OPCODE_PUSH_CONST)
|
||||||
|
|
||||||
|
SK_ENUM(Opcode, OPCODE);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,45 @@
|
||||||
|
project('skemla',
|
||||||
|
'cpp',
|
||||||
|
version: '0.0.0',
|
||||||
|
default_options: [
|
||||||
|
'warning_level=3',
|
||||||
|
'cpp_std=c++17'
|
||||||
|
])
|
||||||
|
|
||||||
|
skemla_lib = static_library('skemla', sources: [
|
||||||
|
'lib/Node.cpp',
|
||||||
|
'lib/Lexer.cpp',
|
||||||
|
'lib/Parser.cpp',
|
||||||
|
'lib/Loc.cpp',
|
||||||
|
'lib/Logger.cpp',
|
||||||
|
'lib/Factory.cpp',
|
||||||
|
|
||||||
|
'lib/Type.cpp',
|
||||||
|
'lib/Value.cpp',
|
||||||
|
|
||||||
|
'lib/Compiler.cpp',
|
||||||
|
'lib/Program.cpp',
|
||||||
|
'lib/VM.cpp',
|
||||||
|
])
|
||||||
|
|
||||||
|
skemla_dep = declare_dependency(link_with: skemla_lib)
|
||||||
|
|
||||||
|
executable('skemla',
|
||||||
|
sources: [
|
||||||
|
'src/main.cpp'
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
skemla_dep
|
||||||
|
],
|
||||||
|
install: true)
|
||||||
|
|
||||||
|
executable('skemla-tests',
|
||||||
|
sources: [
|
||||||
|
'tests/main.cpp',
|
||||||
|
'tests/Lexer.cpp',
|
||||||
|
'tests/Parser.cpp',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
skemla_dep,
|
||||||
|
dependency('catch2')
|
||||||
|
])
|
|
@ -0,0 +1,88 @@
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include "lib/Factory.hpp"
|
||||||
|
#include "lib/Compiler.hpp"
|
||||||
|
#include "lib/VM.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
static struct option long_options[] = {
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{"version", no_argument, 0, 'v'},
|
||||||
|
{0, 0, 0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int option_index = 0;
|
||||||
|
int c = getopt_long(argc, argv
|
||||||
|
, "hv"
|
||||||
|
, long_options, &option_index);
|
||||||
|
|
||||||
|
if (c == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'h': {
|
||||||
|
std::cout << "Usage: skemla [OPTIONS] filename" << std::endl;
|
||||||
|
std::cout << "OPTIONS:" << std::endl;
|
||||||
|
std::cout << "\t" << "-h, --help"
|
||||||
|
<< "\t" << "Show this help message" << std::endl;
|
||||||
|
std::cout << "\t" << "-v, --version"
|
||||||
|
<< "\t" << "Show Skemla version" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case 'v': {
|
||||||
|
std::cout << "skemla v0.0.0" << std::endl;
|
||||||
|
return 0;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string source;
|
||||||
|
|
||||||
|
// Load Sources
|
||||||
|
{
|
||||||
|
std::ifstream file {argv[optind]};
|
||||||
|
|
||||||
|
if(!file)
|
||||||
|
{
|
||||||
|
std::cerr << "cannot open file '"
|
||||||
|
<< argv[optind] << "'" << std::endl;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
while (std::getline(file, line))
|
||||||
|
{
|
||||||
|
source += line + (file.eof() ? "" : "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sk::Logger logger;
|
||||||
|
sk::Factory factory {argv[optind], logger};
|
||||||
|
|
||||||
|
auto parser = factory.make_parser();
|
||||||
|
auto root = parser->parse(source);
|
||||||
|
|
||||||
|
sk::Compiler compiler;
|
||||||
|
auto program = std::make_shared<sk::Program>();
|
||||||
|
compiler.compile(root, program);
|
||||||
|
|
||||||
|
sk::VM vm;
|
||||||
|
vm.exec(program);
|
||||||
|
|
||||||
|
std::cout << vm.string() << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include "../lib/Factory.hpp"
|
||||||
|
|
||||||
|
class LexerTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit LexerTest() {}
|
||||||
|
virtual ~LexerTest() {}
|
||||||
|
|
||||||
|
void test_next(sk::Lexer& lexer, std::string const& oracle)
|
||||||
|
{
|
||||||
|
auto token = lexer.next();
|
||||||
|
REQUIRE(token);
|
||||||
|
REQUIRE(oracle == token->string());
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_end(sk::Lexer& lexer)
|
||||||
|
{
|
||||||
|
auto token = lexer.next();
|
||||||
|
REQUIRE(nullptr == token);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
sk::Logger m_logger;
|
||||||
|
sk::Factory m_factory {"tests/lexer", m_logger};
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_unknown_token")
|
||||||
|
{
|
||||||
|
auto lexer = m_factory.make_lexer();
|
||||||
|
lexer->scan(" § ");
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(lexer->next(), sk::lexical_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_int")
|
||||||
|
{
|
||||||
|
auto lexer = m_factory.make_lexer();
|
||||||
|
|
||||||
|
lexer->scan(" 47 29 3 ");
|
||||||
|
test_next(*lexer, "INT[47]");
|
||||||
|
test_next(*lexer, "INT[29]");
|
||||||
|
test_next(*lexer, "INT[3]");
|
||||||
|
test_end(*lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_float")
|
||||||
|
{
|
||||||
|
auto lexer = m_factory.make_lexer();
|
||||||
|
|
||||||
|
lexer->scan(" 27.0 3.33 ");
|
||||||
|
test_next(*lexer, "FLOAT[27.0]");
|
||||||
|
test_next(*lexer, "FLOAT[3.33]");
|
||||||
|
test_end(*lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_bools")
|
||||||
|
{
|
||||||
|
auto lexer = m_factory.make_lexer();
|
||||||
|
|
||||||
|
lexer->scan(" true false ");
|
||||||
|
test_next(*lexer, "BOOL[true]");
|
||||||
|
test_next(*lexer, "BOOL[false]");
|
||||||
|
test_end(*lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_string")
|
||||||
|
{
|
||||||
|
auto lexer = m_factory.make_lexer();
|
||||||
|
|
||||||
|
lexer->scan(" ' true' 'false ' 'both' ");
|
||||||
|
test_next(*lexer, "STRING[' true']");
|
||||||
|
test_next(*lexer, "STRING['false ']");
|
||||||
|
test_next(*lexer, "STRING['both']");
|
||||||
|
test_end(*lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_comments")
|
||||||
|
{
|
||||||
|
auto lexer = m_factory.make_lexer();
|
||||||
|
|
||||||
|
lexer->scan(" ' true' ;; 'false ' \n 'both' ");
|
||||||
|
test_next(*lexer, "STRING[' true']");
|
||||||
|
test_next(*lexer, "STRING['both']");
|
||||||
|
test_end(*lexer);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include "../lib/Factory.hpp"
|
||||||
|
|
||||||
|
class ParserTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ParserTest() {}
|
||||||
|
virtual ~ParserTest() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
sk::Logger m_logger;
|
||||||
|
sk::Factory m_factory {"tests/parser", m_logger};
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(ParserTest, "Parser_built-in")
|
||||||
|
{
|
||||||
|
auto parser = m_factory.make_parser();
|
||||||
|
auto root = parser->parse(" 34 'hello' false");
|
||||||
|
|
||||||
|
REQUIRE("PROG(INT[34],STRING['hello'],BOOL[false])"
|
||||||
|
== root->string());
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include <catch2/catch.hpp>
|
Reference in New Issue