ADD: int literals.
parent
96d923faf8
commit
fbbbd4e9f4
|
@ -0,0 +1,4 @@
|
||||||
|
build
|
||||||
|
*~*
|
||||||
|
*\#*
|
||||||
|
.cache
|
|
@ -0,0 +1,13 @@
|
||||||
|
BUILD_DIR=build
|
||||||
|
|
||||||
|
.PHONY: $(BUILD_DIR) tests
|
||||||
|
|
||||||
|
build:
|
||||||
|
meson setup $(BUILD_DIR)
|
||||||
|
meson compile -C $(BUILD_DIR)
|
||||||
|
|
||||||
|
tests: $(BUILD_DIR)
|
||||||
|
./$(BUILD_DIR)/roza-tests
|
||||||
|
|
||||||
|
install: tests
|
||||||
|
meson install -C $(BUILD_DIR)
|
|
@ -0,0 +1,4 @@
|
||||||
|
PROG ::= (INSTR (EOI INSTR)*)?
|
||||||
|
INSTR ::= EXPR
|
||||||
|
EXPR ::= BASE
|
||||||
|
BASE ::= int
|
|
@ -0,0 +1,52 @@
|
||||||
|
#include "Compiler.hpp"
|
||||||
|
#include "lib/opcodes.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
/*explicit*/ Compiler::Compiler(StatusLog& log)
|
||||||
|
: m_log { log }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Compiler::~Compiler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Program> Compiler::compile(std::shared_ptr<Node> root)
|
||||||
|
{
|
||||||
|
auto program = std::make_shared<Program>();
|
||||||
|
compile_node(root, program);
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::compile_node(std::shared_ptr<Node> root, std::shared_ptr<Program> prog)
|
||||||
|
{
|
||||||
|
switch (root->type())
|
||||||
|
{
|
||||||
|
case NODE_INT: {
|
||||||
|
auto value = std::make_shared<Value>(std::stoi(root->repr()), root->loc());
|
||||||
|
prog->push_instr(OP_PUSH_CONST, prog->push_value(value));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_PROG: {
|
||||||
|
compile_children(root, prog);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_INSTR: {
|
||||||
|
compile_children(root, prog);
|
||||||
|
prog->push_instr(OP_POP);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
m_log.fatal(root->loc(), "cannot compile node '" + root->string() + "'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::compile_children(std::shared_ptr<Node> root, std::shared_ptr<Program> prog)
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<root->size(); i++)
|
||||||
|
{
|
||||||
|
compile_node(root->child(i), prog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef roza_COMPILER_HPP
|
||||||
|
#define roza_COMPILER_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
#include "Node.hpp"
|
||||||
|
#include "opcodes.hpp"
|
||||||
|
#include "StatusLog.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
class Compiler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Compiler(StatusLog& log);
|
||||||
|
virtual ~Compiler();
|
||||||
|
|
||||||
|
std::shared_ptr<Program> compile(std::shared_ptr<Node> root);
|
||||||
|
void compile_node(std::shared_ptr<Node> root, std::shared_ptr<Program> prog);
|
||||||
|
void compile_children(std::shared_ptr<Node> root, std::shared_ptr<Program> prog);
|
||||||
|
|
||||||
|
private:
|
||||||
|
StatusLog& m_log;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,134 @@
|
||||||
|
#include "Lexer.hpp"
|
||||||
|
#include "lib/Node.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
/*explicit*/ Lexer::Lexer(StatusLog& log, SrcLoc loc)
|
||||||
|
: m_log { log }
|
||||||
|
, m_loc { loc }
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
skip_blanks();
|
||||||
|
|
||||||
|
while (m_cursor < source.size())
|
||||||
|
{
|
||||||
|
while (m_cursor < source.size()
|
||||||
|
&& source[m_cursor] == '#')
|
||||||
|
{
|
||||||
|
while (m_cursor < source.size()
|
||||||
|
&& source[m_cursor] != '\n')
|
||||||
|
{
|
||||||
|
m_cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_blanks();
|
||||||
|
|
||||||
|
ScanInfo info;
|
||||||
|
|
||||||
|
std::shared_ptr<Node> node;
|
||||||
|
size_t cursor = m_cursor;
|
||||||
|
|
||||||
|
for (auto& scanner: m_scanners)
|
||||||
|
{
|
||||||
|
ScanInfo info = scanner();
|
||||||
|
|
||||||
|
if (info.node && info.cursor > cursor)
|
||||||
|
{
|
||||||
|
node = info.node;
|
||||||
|
cursor = info.cursor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_nodes.push_back(node);
|
||||||
|
m_cursor = cursor;
|
||||||
|
|
||||||
|
skip_blanks();
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
std::string symb;
|
||||||
|
while (m_cursor < m_source.size()
|
||||||
|
&& !std::isblank(m_source[m_cursor]))
|
||||||
|
{
|
||||||
|
symb += m_source[m_cursor];
|
||||||
|
m_cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_log.fatal(m_loc
|
||||||
|
, std::string()
|
||||||
|
+ "unexpected symbol '"
|
||||||
|
+ symb + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lexer::skip_blanks()
|
||||||
|
{
|
||||||
|
while (m_cursor < m_source.size()
|
||||||
|
&& std::isspace(m_source.at(m_cursor)))
|
||||||
|
{
|
||||||
|
if (m_source.at(m_cursor) == '\n')
|
||||||
|
{
|
||||||
|
m_loc.set_line(m_loc.line() + 1);
|
||||||
|
auto root = std::make_shared<Node>(NODE_EOI, "", m_loc);
|
||||||
|
m_nodes.push_back(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cursor++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Lexer::get_or_nullptr(size_t index) const
|
||||||
|
{
|
||||||
|
if (index >= m_nodes.size())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_nodes.at(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanInfo Lexer::scan_int() const
|
||||||
|
{
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
return ScanInfo {
|
||||||
|
std::make_shared<Node>(NodeType::NODE_INT, repr, m_loc),
|
||||||
|
cursor
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScanInfo {};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef roza_LEXER_HPP
|
||||||
|
#define roza_LEXER_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Node.hpp"
|
||||||
|
#include "SrcLoc.hpp"
|
||||||
|
#include "StatusLog.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
struct ScanInfo {
|
||||||
|
std::shared_ptr<Node> node = nullptr;
|
||||||
|
size_t cursor = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using scanner = std::function<ScanInfo(void)>;
|
||||||
|
|
||||||
|
class Lexer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Lexer(StatusLog& log, SrcLoc loc);
|
||||||
|
virtual ~Lexer();
|
||||||
|
|
||||||
|
SrcLoc loc() const { return m_loc; }
|
||||||
|
size_t size() const { return m_nodes.size(); }
|
||||||
|
|
||||||
|
void scan(std::string const& source);
|
||||||
|
void skip_blanks();
|
||||||
|
std::shared_ptr<Node> get_or_nullptr(size_t index) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
StatusLog& m_log;
|
||||||
|
SrcLoc m_loc;
|
||||||
|
|
||||||
|
std::string m_source;
|
||||||
|
size_t m_cursor = 0;
|
||||||
|
std::vector<std::shared_ptr<Node>> m_nodes;
|
||||||
|
std::vector<scanner> m_scanners;
|
||||||
|
|
||||||
|
ScanInfo scan_int() const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,60 @@
|
||||||
|
#include "Node.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
/*explicit*/ Node::Node(NodeType type, std::string const& repr, SrcLoc loc)
|
||||||
|
: m_type { type }
|
||||||
|
, m_repr { repr }
|
||||||
|
, m_loc { loc }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Node::~Node()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Node::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::string(NodeTypeStr[m_type]).substr(std::string("NODE_").size());
|
||||||
|
|
||||||
|
if (m_repr != "")
|
||||||
|
{
|
||||||
|
ss << "[" << m_repr << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size() > 0)
|
||||||
|
{
|
||||||
|
ss << "(";
|
||||||
|
std::string sep = "";
|
||||||
|
|
||||||
|
for (auto const& child: m_children)
|
||||||
|
{
|
||||||
|
ss << sep << child->string();
|
||||||
|
sep = ",";
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Node::add_child(std::shared_ptr<Node> child)
|
||||||
|
{
|
||||||
|
assert(child);
|
||||||
|
m_children.push_back(std::move(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Node::child(size_t index)
|
||||||
|
{
|
||||||
|
assert(index < size());
|
||||||
|
return m_children[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
Node const& Node::child(size_t index) const
|
||||||
|
{
|
||||||
|
assert(index < size());
|
||||||
|
return *m_children[index];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef roza_NODE_HPP
|
||||||
|
#define roza_NODE_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "SrcLoc.hpp"
|
||||||
|
|
||||||
|
#define NODE_TYPE(G) \
|
||||||
|
G(NODE_PROG), G(NODE_INSTR), G(NODE_EOI), \
|
||||||
|
G(NODE_INT)
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
MAKE_ENUM(NODE_TYPE, NodeType);
|
||||||
|
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Node(NodeType type, std::string const& repr, SrcLoc loc);
|
||||||
|
virtual ~Node();
|
||||||
|
|
||||||
|
NodeType type() const { return m_type; }
|
||||||
|
std::string repr() const { return m_repr; }
|
||||||
|
SrcLoc loc() const { return m_loc; }
|
||||||
|
size_t size() const { return m_children.size(); }
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
void add_child(std::shared_ptr<Node> child);
|
||||||
|
|
||||||
|
std::shared_ptr<Node> child(size_t index);
|
||||||
|
Node const& child(size_t index) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NodeType m_type;
|
||||||
|
std::string m_repr;
|
||||||
|
SrcLoc m_loc;
|
||||||
|
std::vector<std::shared_ptr<Node>> m_children;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,118 @@
|
||||||
|
#include "Parser.hpp"
|
||||||
|
#include "lib/Node.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
/*explicit*/ Parser::Parser(Lexer& lexer, StatusLog& log)
|
||||||
|
: m_lexer { lexer }
|
||||||
|
, m_log { log }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Parser::~Parser()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse()
|
||||||
|
{
|
||||||
|
m_cursor = 0;
|
||||||
|
auto root = parse_prog();
|
||||||
|
|
||||||
|
if (m_cursor < m_lexer.size())
|
||||||
|
{
|
||||||
|
m_log.fatal(m_lexer.loc(), "unexpected end.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::node(size_t offset) const
|
||||||
|
{
|
||||||
|
return m_lexer.get_or_nullptr(m_cursor + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeType Parser::type(size_t offset) const
|
||||||
|
{
|
||||||
|
auto tok = m_lexer.get_or_nullptr(m_cursor + offset);
|
||||||
|
assert(tok);
|
||||||
|
return tok->type();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Parser::type_is(NodeType type, size_t offset) const
|
||||||
|
{
|
||||||
|
auto tok = m_lexer.get_or_nullptr(m_cursor + offset);
|
||||||
|
if (!tok) { return false; }
|
||||||
|
|
||||||
|
return tok->type() == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::consume(NodeType ty)
|
||||||
|
{
|
||||||
|
if (!type_is(ty))
|
||||||
|
{
|
||||||
|
m_log.fatal(node()->loc(),
|
||||||
|
std::string()
|
||||||
|
+ "syntax error, expected '"
|
||||||
|
+ NodeTypeStr[ty]
|
||||||
|
+ "', got '"
|
||||||
|
+ NodeTypeStr[type()]
|
||||||
|
+ "'");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ret = node();
|
||||||
|
next();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::next()
|
||||||
|
{
|
||||||
|
m_cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_prog()
|
||||||
|
{
|
||||||
|
if (m_lexer.size() == 0)
|
||||||
|
{
|
||||||
|
return std::make_shared<Node>(NODE_PROG, "", m_lexer.loc());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto root = std::make_shared<Node>(NODE_PROG, "", node()->loc());
|
||||||
|
root->add_child(parse_instr());
|
||||||
|
|
||||||
|
while (type_is(NODE_EOI))
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
root->add_child(parse_instr());
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_instr()
|
||||||
|
{
|
||||||
|
auto lhs = parse_expr();
|
||||||
|
|
||||||
|
auto root = std::make_shared<Node>(NODE_INSTR, "", lhs->loc());
|
||||||
|
root->add_child(lhs);
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_expr()
|
||||||
|
{
|
||||||
|
return parse_base();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_base()
|
||||||
|
{
|
||||||
|
return parse_int();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_int()
|
||||||
|
{
|
||||||
|
auto root = consume(NODE_INT);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef roza_PARSER_HPP
|
||||||
|
#define roza_PARSER_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Lexer.hpp"
|
||||||
|
#include "lib/StatusLog.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Parser(Lexer& lexer, StatusLog& log);
|
||||||
|
virtual ~Parser();
|
||||||
|
|
||||||
|
std::shared_ptr<Node> parse();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Lexer& m_lexer;
|
||||||
|
StatusLog& m_log;
|
||||||
|
size_t m_cursor = 0;
|
||||||
|
|
||||||
|
std::shared_ptr<Node> node(size_t offset=0) const;
|
||||||
|
NodeType type(size_t offset=0) const;
|
||||||
|
bool type_is(NodeType type, size_t offset=0) const;
|
||||||
|
std::shared_ptr<Node> consume(NodeType type);
|
||||||
|
void next();
|
||||||
|
|
||||||
|
std::shared_ptr<Node> parse_prog();
|
||||||
|
std::shared_ptr<Node> parse_instr();
|
||||||
|
std::shared_ptr<Node> parse_expr();
|
||||||
|
std::shared_ptr<Node> parse_base();
|
||||||
|
std::shared_ptr<Node> parse_int();
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include "Program.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
/*explicit*/ Program::Program()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Program::~Program()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Program::push_instr(Opcode opcode, std::optional<param_t> param)
|
||||||
|
{
|
||||||
|
m_instrs.push_back({opcode, param});
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Program::push_value(std::shared_ptr<Value> value)
|
||||||
|
{
|
||||||
|
size_t sz = m_values.size();
|
||||||
|
m_values.push_back(value);
|
||||||
|
return sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
Opcode Program::opcode(size_t index) const
|
||||||
|
{
|
||||||
|
assert(index < size());
|
||||||
|
return m_instrs[index].opcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<param_t> Program::param(size_t index) const
|
||||||
|
{
|
||||||
|
assert(index < size());
|
||||||
|
return m_instrs[index].param;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Program::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
size_t addr = 0;
|
||||||
|
|
||||||
|
for (auto const& instr: m_instrs)
|
||||||
|
{
|
||||||
|
ss << addr
|
||||||
|
<< "\t"
|
||||||
|
<< (OpcodeStr[instr.opcode])
|
||||||
|
<< "\t"
|
||||||
|
<< (instr.param != std::nullopt ? std::to_string(*instr.param) : "")
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef roza_PROGRAM_HPP
|
||||||
|
#define roza_PROGRAM_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "opcodes.hpp"
|
||||||
|
#include "Value.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
using param_t = int;
|
||||||
|
|
||||||
|
struct Instr {
|
||||||
|
Opcode opcode;
|
||||||
|
std::optional<param_t> param;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Program();
|
||||||
|
virtual ~Program();
|
||||||
|
|
||||||
|
size_t size() const { return m_instrs.size(); }
|
||||||
|
|
||||||
|
void push_instr(Opcode opcode, std::optional<param_t> param = std::nullopt);
|
||||||
|
size_t push_value(std::shared_ptr<Value> value);
|
||||||
|
|
||||||
|
Opcode opcode(size_t index) const;
|
||||||
|
std::optional<param_t> param(size_t index) const;
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Instr> m_instrs;
|
||||||
|
std::vector<std::shared_ptr<Value>> m_values;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,19 @@
|
||||||
|
#include "SrcLoc.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
/*explicit*/ SrcLoc::SrcLoc(std::filesystem::path source_file, int line)
|
||||||
|
: m_source_file { source_file }
|
||||||
|
, m_line { line }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ SrcLoc::~SrcLoc()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrcLoc::set_line(int line)
|
||||||
|
{
|
||||||
|
m_line = line;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef roza_SRCLOC_HPP
|
||||||
|
#define roza_SRCLOC_HPP
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
class SrcLoc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SrcLoc(std::filesystem::path source_file = "???", int line=1);
|
||||||
|
virtual ~SrcLoc();
|
||||||
|
|
||||||
|
std::filesystem::path source_file() const { return m_source_file; }
|
||||||
|
int line() const { return m_line; }
|
||||||
|
|
||||||
|
void set_line(int line);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::filesystem::path m_source_file;
|
||||||
|
int m_line;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,33 @@
|
||||||
|
#include "StaticPass.hpp"
|
||||||
|
#include "lib/Node.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
/*explicit*/ StaticPass::StaticPass(StatusLog& log)
|
||||||
|
: m_log { log }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ StaticPass::~StaticPass()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void StaticPass::check(std::shared_ptr<Node> root)
|
||||||
|
{
|
||||||
|
switch (root->type())
|
||||||
|
{
|
||||||
|
case NODE_INT: break;
|
||||||
|
|
||||||
|
case NODE_PROG:
|
||||||
|
case NODE_INSTR: {
|
||||||
|
for (size_t i=0; i<root->size(); i++)
|
||||||
|
{
|
||||||
|
check(root->child(i));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
m_log.fatal(root->loc(), "cannot check node '" + root->string() + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef roza_STATICPASS_HPP
|
||||||
|
#define roza_STATICPASS_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "StatusLog.hpp"
|
||||||
|
#include "Node.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
class StaticPass
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit StaticPass(StatusLog& log);
|
||||||
|
virtual ~StaticPass();
|
||||||
|
|
||||||
|
void check(std::shared_ptr<Node> root);
|
||||||
|
|
||||||
|
private:
|
||||||
|
StatusLog& m_log;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include "StatusLog.hpp"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
/*explicit*/ StatusLog::StatusLog()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ StatusLog::~StatusLog()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatusLog::fatal(SrcLoc loc, std::string const& what)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
ss << loc.source_file().string()
|
||||||
|
<< ":" << loc.line() << " [FATAL ERROR] " << what;
|
||||||
|
|
||||||
|
throw std::runtime_error {ss.str()};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef roza_STATUSLOG_HPP
|
||||||
|
#define roza_STATUSLOG_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "SrcLoc.hpp"
|
||||||
|
|
||||||
|
#define STATUS_LEVEL(G) \
|
||||||
|
G(LEVEL_WARNING), \
|
||||||
|
G(LEVEL_ERROR), \
|
||||||
|
G(LEVEL_CRITICAL)
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
MAKE_ENUM(STATUS_LEVEL, Status);
|
||||||
|
|
||||||
|
class StatusLog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit StatusLog();
|
||||||
|
virtual ~StatusLog();
|
||||||
|
|
||||||
|
void fatal(SrcLoc loc, std::string const& what);
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include "Type.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
/*explicit*/ Type::Type(BaseType base)
|
||||||
|
: m_base { base }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Type::~Type()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Type::equals(BaseType rhs) const
|
||||||
|
{
|
||||||
|
return m_base == rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Type::equals(Type const& rhs) const
|
||||||
|
{
|
||||||
|
return m_base == rhs.m_base;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef roza_TYPE_HPP
|
||||||
|
#define roza_TYPE_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
#define BASE_TYPE(G) \
|
||||||
|
G(TY_INT)
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
MAKE_ENUM(BASE_TYPE, BaseType)
|
||||||
|
|
||||||
|
class Type
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Type(BaseType base);
|
||||||
|
virtual ~Type();
|
||||||
|
|
||||||
|
BaseType base() const { return m_base; }
|
||||||
|
|
||||||
|
bool equals(BaseType rhs) const;
|
||||||
|
bool equals(Type const& rhs) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BaseType m_base;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,33 @@
|
||||||
|
#include "TypeResolver.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
/*explicit*/ TypeResolver::TypeResolver(StatusLog& log)
|
||||||
|
: m_log { log }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ TypeResolver::~TypeResolver()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Type> TypeResolver::find(std::shared_ptr<Node> root)
|
||||||
|
{
|
||||||
|
switch (root->type())
|
||||||
|
{
|
||||||
|
case NODE_PROG:
|
||||||
|
case NODE_INSTR: {
|
||||||
|
return find(root->child(root->size() - 1));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_INT: {
|
||||||
|
return std::make_shared<Type>(BaseType::TY_INT);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
m_log.fatal(root->loc(), "cannot find type of node '" + root->string() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef roza_TYPERESOLVER_HPP
|
||||||
|
#define roza_TYPERESOLVER_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Type.hpp"
|
||||||
|
#include "StatusLog.hpp"
|
||||||
|
#include "Node.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
class TypeResolver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TypeResolver(StatusLog& log);
|
||||||
|
virtual ~TypeResolver();
|
||||||
|
|
||||||
|
std::shared_ptr<Type> find(std::shared_ptr<Node> root);
|
||||||
|
private:
|
||||||
|
StatusLog& m_log;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,54 @@
|
||||||
|
#include "VM.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
/*explicit*/ VM::VM(StatusLog& log)
|
||||||
|
: m_log { log }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ VM::~VM()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void VM::exec(std::shared_ptr<Program> program)
|
||||||
|
{
|
||||||
|
while (m_pc < program->size())
|
||||||
|
{
|
||||||
|
switch (program->opcode(m_pc))
|
||||||
|
{
|
||||||
|
case OP_PUSH_CONST: {
|
||||||
|
param_t param = *program->param(m_pc);
|
||||||
|
push(param);
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_POP: {
|
||||||
|
pop();
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
m_log.fatal(SrcLoc {},
|
||||||
|
std::string()
|
||||||
|
+ "cannot execute opcode '"
|
||||||
|
+ OpcodeStr[program->opcode(m_pc)]
|
||||||
|
+ "'"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VM::push(param_t param)
|
||||||
|
{
|
||||||
|
m_stack.push_back(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
param_t VM::pop()
|
||||||
|
{
|
||||||
|
assert(m_stack.size() > 0);
|
||||||
|
|
||||||
|
param_t res = m_stack.back();
|
||||||
|
m_stack.pop_back();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef roza_VM_HPP
|
||||||
|
#define roza_VM_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
#include "StatusLog.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
class VM
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit VM(StatusLog& log);
|
||||||
|
virtual ~VM();
|
||||||
|
|
||||||
|
void exec(std::shared_ptr<Program> program);
|
||||||
|
|
||||||
|
private:
|
||||||
|
StatusLog& m_log;
|
||||||
|
std::vector<param_t> m_stack;
|
||||||
|
size_t m_pc = 0;
|
||||||
|
|
||||||
|
void push(param_t param);
|
||||||
|
param_t pop();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include "Value.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
/*explicit*/ Value::Value(int value, SrcLoc loc)
|
||||||
|
: m_type { std::make_shared<Type>(BaseType::TY_INT) }
|
||||||
|
, m_int_val { value }
|
||||||
|
, m_loc { loc }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Value::~Value()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Value::string() const
|
||||||
|
{
|
||||||
|
if (m_type->equals(TY_INT))
|
||||||
|
{
|
||||||
|
return std::to_string(m_int_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert("cannot stringify unknown value " && 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef roza_VALUE_HPP
|
||||||
|
#define roza_VALUE_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "SrcLoc.hpp"
|
||||||
|
#include "Type.hpp"
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
class Value
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Value(int value, SrcLoc loc);
|
||||||
|
virtual ~Value();
|
||||||
|
|
||||||
|
std::shared_ptr<Type> type() const { return m_type; }
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Type> m_type;
|
||||||
|
int m_int_val;
|
||||||
|
SrcLoc m_loc;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef roza_COMMONS_HPP
|
||||||
|
#define roza_COMMONS_HPP
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <cassert>
|
||||||
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
#include <sstream>
|
||||||
|
#include "mutils.hpp"
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef roza_MUTILS_HPP
|
||||||
|
#define roza_MUTILS_HPP
|
||||||
|
|
||||||
|
#define GEN_ENUM(X) X
|
||||||
|
#define GEN_STRING(X) #X
|
||||||
|
|
||||||
|
#define MAKE_ENUM(MY, NAME) \
|
||||||
|
enum NAME { MY(GEN_ENUM) }; \
|
||||||
|
constexpr char const* NAME ## Str [] = { MY(GEN_STRING) }; \
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef roza_OPCODES_HPP
|
||||||
|
#define roza_OPCODES_HPP
|
||||||
|
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
#define OPCODE(G) \
|
||||||
|
G(OP_PUSH_CONST), \
|
||||||
|
G(OP_POP)
|
||||||
|
|
||||||
|
namespace roza
|
||||||
|
{
|
||||||
|
MAKE_ENUM(OPCODE, Opcode)
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,53 @@
|
||||||
|
project('roza',
|
||||||
|
'cpp',
|
||||||
|
version: '0.0.0',
|
||||||
|
default_options: [
|
||||||
|
'warning_level=3',
|
||||||
|
'cpp_std=c++17'
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
roza_lib = static_library(
|
||||||
|
'roza',
|
||||||
|
sources: [
|
||||||
|
'lib/SrcLoc.cpp',
|
||||||
|
'lib/StatusLog.cpp',
|
||||||
|
'lib/Node.cpp',
|
||||||
|
'lib/Lexer.cpp',
|
||||||
|
'lib/Parser.cpp',
|
||||||
|
'lib/Compiler.cpp',
|
||||||
|
'lib/StaticPass.cpp',
|
||||||
|
'lib/Program.cpp',
|
||||||
|
'lib/VM.cpp',
|
||||||
|
'lib/Type.cpp',
|
||||||
|
'lib/Value.cpp',
|
||||||
|
'lib/TypeResolver.cpp',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
roza_dep = declare_dependency(link_with: roza_lib)
|
||||||
|
|
||||||
|
executable('roza',
|
||||||
|
sources: [
|
||||||
|
'src/main.cpp',
|
||||||
|
'src/Args.cpp',
|
||||||
|
'src/Loader.cpp',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
roza_dep
|
||||||
|
],
|
||||||
|
install: true)
|
||||||
|
|
||||||
|
executable('roza-tests',
|
||||||
|
sources: [
|
||||||
|
'tests/main.cpp',
|
||||||
|
'tests/Lexer.cpp',
|
||||||
|
'tests/Parser.cpp',
|
||||||
|
'tests/Compiler.cpp',
|
||||||
|
'tests/TypeResolver.cpp',
|
||||||
|
'tests/StaticPass.cpp',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
dependency('catch2'),
|
||||||
|
roza_dep
|
||||||
|
])
|
|
@ -0,0 +1,79 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include "Args.hpp"
|
||||||
|
|
||||||
|
/*explicit*/ Args::Args(int argc, char** argv)
|
||||||
|
{
|
||||||
|
for (int i=1; i<argc; i++)
|
||||||
|
{
|
||||||
|
std::string arg = argv[i];
|
||||||
|
|
||||||
|
if (i > 1)
|
||||||
|
{
|
||||||
|
if (argv[i - 1][0] == '-')
|
||||||
|
{
|
||||||
|
if (arg.empty() == false && arg[0] == '-')
|
||||||
|
{
|
||||||
|
m_values[argv[i - 1]] = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_values[argv[i - 1]] = arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_opt("-h", arg) || has_opt("--help", arg))
|
||||||
|
{
|
||||||
|
m_help = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_opt("-v", arg) || has_opt("--version", arg))
|
||||||
|
{
|
||||||
|
m_version = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.empty() == false && arg[0] != '-')
|
||||||
|
{
|
||||||
|
m_inputs.push_back(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 1 && argv[argc - 1][0] == '-')
|
||||||
|
{
|
||||||
|
m_values[argv[argc - 1]] = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Args::~Args()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Args::has_opt(std::string const& opt, std::string const& str) const
|
||||||
|
{
|
||||||
|
if (opt.size() == 2 && opt[0] == '-'
|
||||||
|
&& str.size() > 0 && str[0] == '-'
|
||||||
|
&& (str.size() < 2 || str[1] != '-'))
|
||||||
|
{
|
||||||
|
for (char c : str)
|
||||||
|
{
|
||||||
|
if (c == opt[1])
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str == opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Args::get(std::string const& key) const
|
||||||
|
{
|
||||||
|
if (auto itr = m_values.find(key);
|
||||||
|
itr != std::end(m_values))
|
||||||
|
{
|
||||||
|
return itr->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef ARGS_HPP
|
||||||
|
#define ARGS_HPP
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
class Args
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Args(int argc, char** argv);
|
||||||
|
virtual ~Args();
|
||||||
|
|
||||||
|
bool help() const { return m_help; }
|
||||||
|
bool version() const { return m_version; }
|
||||||
|
std::vector<std::string> inputs() const { return m_inputs; }
|
||||||
|
|
||||||
|
std::optional<std::string> get(std::string const& key) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_help = false;
|
||||||
|
bool m_version = false;
|
||||||
|
std::vector<std::string> m_inputs;
|
||||||
|
std::unordered_map<std::string, std::string> m_values;
|
||||||
|
bool has_opt(std::string const& opt, std::string const& str) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,32 @@
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include "Loader.hpp"
|
||||||
|
|
||||||
|
/*explicit*/ Loader::Loader()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Loader::~Loader()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Loader::load(std::filesystem::path file) const
|
||||||
|
{
|
||||||
|
std::ifstream f {file};
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
{
|
||||||
|
std::cerr << "cannot find '" << file.string() << "'" << std::endl;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string content;
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
while (std::getline(f, line))
|
||||||
|
{
|
||||||
|
content += line + (f.eof() ? "" : "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef LOADER_HPP
|
||||||
|
#define LOADER_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
class Loader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Loader();
|
||||||
|
virtual ~Loader();
|
||||||
|
|
||||||
|
std::string load(std::filesystem::path file) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,64 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include "Args.hpp"
|
||||||
|
#include "Loader.hpp"
|
||||||
|
#include "../lib/Lexer.hpp"
|
||||||
|
#include "../lib/Parser.hpp"
|
||||||
|
#include "../lib/StaticPass.hpp"
|
||||||
|
#include "../lib/Compiler.hpp"
|
||||||
|
#include "../lib/VM.hpp"
|
||||||
|
#include "lib/StatusLog.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
Args args { argc, argv };
|
||||||
|
|
||||||
|
if (args.help())
|
||||||
|
{
|
||||||
|
std::cerr << "Usage: roza [OPTIONS] <filename>" << std::endl;
|
||||||
|
std::cerr << "Options:" << std::endl;
|
||||||
|
std::cerr << "\t--ast, show the AST" << std::endl;
|
||||||
|
std::cerr << "\t--code, show the bytecode" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.version())
|
||||||
|
{
|
||||||
|
std::cerr << "Roza v0.0.0" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader loader;
|
||||||
|
|
||||||
|
if (args.inputs().size() > 0)
|
||||||
|
{
|
||||||
|
auto source = loader.load(args.inputs()[0]);
|
||||||
|
roza::SrcLoc loc {args.inputs()[0]};
|
||||||
|
roza::StatusLog log;
|
||||||
|
|
||||||
|
auto lexer = std::make_shared<roza::Lexer>(log, loc);
|
||||||
|
auto parser = std::make_shared<roza::Parser>(*lexer, log);
|
||||||
|
auto static_pass = std::make_shared<roza::StaticPass>(log);
|
||||||
|
auto compiler = std::make_shared<roza::Compiler>(log);
|
||||||
|
auto vm = std::make_shared<roza::VM>(log);
|
||||||
|
|
||||||
|
lexer->scan(source);
|
||||||
|
auto root = parser->parse();
|
||||||
|
|
||||||
|
if (args.get("--ast"))
|
||||||
|
{
|
||||||
|
std::cout << root->string() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static_pass->check(root);
|
||||||
|
auto prog = compiler->compile(root);
|
||||||
|
|
||||||
|
if (args.get("--code"))
|
||||||
|
{
|
||||||
|
std::cout << prog->string() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm->exec(prog);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include "../lib/Lexer.hpp"
|
||||||
|
#include "../lib/Parser.hpp"
|
||||||
|
#include "../lib/Compiler.hpp"
|
||||||
|
#include "lib/Program.hpp"
|
||||||
|
#include "lib/opcodes.hpp"
|
||||||
|
|
||||||
|
class CompilerTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CompilerTest() {}
|
||||||
|
virtual ~CompilerTest() {}
|
||||||
|
|
||||||
|
std::shared_ptr<roza::Program> get_prog(std::string const& source)
|
||||||
|
{
|
||||||
|
roza::SrcLoc loc {"compiler_tests"};
|
||||||
|
roza::StatusLog log;
|
||||||
|
roza::Lexer lexer {log, loc};
|
||||||
|
roza::Parser parser {lexer, log};
|
||||||
|
roza::Compiler compiler {log};
|
||||||
|
|
||||||
|
lexer.scan(source);
|
||||||
|
auto node = parser.parse();
|
||||||
|
return compiler.compile(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compile_err(std::string const& source)
|
||||||
|
{
|
||||||
|
REQUIRE_THROWS(get_prog(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_prog(std::shared_ptr<roza::Program> prog,
|
||||||
|
size_t index,
|
||||||
|
roza::Opcode op,
|
||||||
|
std::optional<roza::param_t> param = std::nullopt)
|
||||||
|
{
|
||||||
|
REQUIRE(index < prog->size());
|
||||||
|
REQUIRE(op == prog->opcode(index));
|
||||||
|
|
||||||
|
if (param)
|
||||||
|
{
|
||||||
|
REQUIRE(param == prog->param(index));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
REQUIRE(!param);
|
||||||
|
REQUIRE(!prog->param(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(CompilerTest, "Compiler_int")
|
||||||
|
{
|
||||||
|
auto prog = get_prog(" 43 ");
|
||||||
|
REQUIRE(2 == prog->size());
|
||||||
|
|
||||||
|
test_prog(prog, 0, roza::OP_PUSH_CONST, 0);
|
||||||
|
test_prog(prog, 1, roza::OP_POP);
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
#include <string>
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include "../lib/Lexer.hpp"
|
||||||
|
#include "lib/SrcLoc.hpp"
|
||||||
|
#include "lib/StatusLog.hpp"
|
||||||
|
|
||||||
|
class LexerTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit LexerTest() {}
|
||||||
|
virtual ~LexerTest() {}
|
||||||
|
|
||||||
|
std::string get_str(size_t index)
|
||||||
|
{
|
||||||
|
auto node = m_lexer.get_or_nullptr(index);
|
||||||
|
|
||||||
|
if (!node) { return ""; }
|
||||||
|
else { return node->string(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
roza::StatusLog m_log;
|
||||||
|
roza::Lexer m_lexer {m_log, roza::SrcLoc("lexer_tests")};
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_integers")
|
||||||
|
{
|
||||||
|
m_lexer.scan("45 12 -3");
|
||||||
|
REQUIRE("INT[45]" == get_str(0));
|
||||||
|
REQUIRE("INT[12]" == get_str(1));
|
||||||
|
REQUIRE("INT[-3]" == get_str(2));
|
||||||
|
REQUIRE("" == get_str(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_comments")
|
||||||
|
{
|
||||||
|
m_lexer.scan("45 # 12 \n -3");
|
||||||
|
REQUIRE("INT[45]" == get_str(0));
|
||||||
|
REQUIRE("INT[-3]" == get_str(1));
|
||||||
|
REQUIRE("" == get_str(2));
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include "../lib/Lexer.hpp"
|
||||||
|
#include "../lib/Parser.hpp"
|
||||||
|
#include "lib/SrcLoc.hpp"
|
||||||
|
#include "lib/StatusLog.hpp"
|
||||||
|
|
||||||
|
class ParserTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ParserTest() {}
|
||||||
|
virtual ~ParserTest() {}
|
||||||
|
|
||||||
|
void test_node(std::string const& oracle, std::string const& source)
|
||||||
|
{
|
||||||
|
roza::SrcLoc loc {"parser_tests"};
|
||||||
|
roza::StatusLog log;
|
||||||
|
roza::Lexer lexer {log, loc};
|
||||||
|
roza::Parser parser {lexer, log};
|
||||||
|
|
||||||
|
lexer.scan(source);
|
||||||
|
auto node = parser.parse();
|
||||||
|
|
||||||
|
REQUIRE(oracle == node->string());
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_node_err(std::string const& oracle)
|
||||||
|
{
|
||||||
|
roza::SrcLoc loc {"parser_tests"};
|
||||||
|
roza::StatusLog log;
|
||||||
|
roza::Lexer lexer {log, loc};
|
||||||
|
roza::Parser parser {lexer, log};
|
||||||
|
|
||||||
|
lexer.scan(oracle);
|
||||||
|
REQUIRE_THROWS(parser.parse());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(ParserTest, "Parser_integers")
|
||||||
|
{
|
||||||
|
test_node("PROG", "");
|
||||||
|
test_node("PROG(INSTR(INT[27]))", "27");
|
||||||
|
test_node("PROG(INSTR(INT[27]))", " 27 ");
|
||||||
|
|
||||||
|
test_node("PROG(INSTR(INT[27]),"
|
||||||
|
"INSTR(INT[9]),"
|
||||||
|
"INSTR(INT[-99]))", "27 \n 9 \n -99");
|
||||||
|
|
||||||
|
test_node_err("32 14 -12");
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include "../lib/Lexer.hpp"
|
||||||
|
#include "../lib/Parser.hpp"
|
||||||
|
#include "../lib/StaticPass.hpp"
|
||||||
|
|
||||||
|
class StaticPassTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit StaticPassTest() {}
|
||||||
|
virtual ~StaticPassTest() {}
|
||||||
|
|
||||||
|
void test_ok(std::string const& source)
|
||||||
|
{
|
||||||
|
roza::SrcLoc loc {"compiler_tests"};
|
||||||
|
roza::StatusLog log;
|
||||||
|
roza::Lexer lexer {log, loc};
|
||||||
|
roza::Parser parser {lexer, log};
|
||||||
|
roza::StaticPass static_pass {log};
|
||||||
|
|
||||||
|
lexer.scan(source);
|
||||||
|
auto node = parser.parse();
|
||||||
|
static_pass.check(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ko(std::string const& source)
|
||||||
|
{
|
||||||
|
REQUIRE_THROWS(test_ok(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(StaticPassTest, "StaticPass_integer")
|
||||||
|
{
|
||||||
|
test_ok(" 43 ");
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include "../lib/TypeResolver.hpp"
|
||||||
|
#include "../lib/Lexer.hpp"
|
||||||
|
#include "../lib/Parser.hpp"
|
||||||
|
#include "lib/Type.hpp"
|
||||||
|
|
||||||
|
class TypeResolverTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TypeResolverTest() {}
|
||||||
|
virtual ~TypeResolverTest() {}
|
||||||
|
|
||||||
|
std::shared_ptr<roza::Type> get_ty(std::string const& source)
|
||||||
|
{
|
||||||
|
roza::SrcLoc loc {"compiler_tests"};
|
||||||
|
roza::StatusLog log;
|
||||||
|
roza::Lexer lexer {log, loc};
|
||||||
|
roza::Parser parser {lexer, log};
|
||||||
|
roza::TypeResolver resolver {log};
|
||||||
|
|
||||||
|
lexer.scan(source);
|
||||||
|
auto node = parser.parse();
|
||||||
|
return resolver.find(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ty(std::string const& source, std::shared_ptr<roza::Type> ty)
|
||||||
|
{
|
||||||
|
REQUIRE(ty);
|
||||||
|
test_ty(source, *ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ty(std::string const& source, roza::Type const& ty)
|
||||||
|
{
|
||||||
|
REQUIRE(get_ty(source)->equals(ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TypeResolverTest, "TypeResolver_")
|
||||||
|
{
|
||||||
|
test_ty(" 42 ", std::make_shared<roza::Type>(roza::BaseType::TY_INT));
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include <catch2/catch.hpp>
|
Loading…
Reference in New Issue