ADD: int literals.

main
bog 2023-08-30 20:06:26 +02:00
parent 96d923faf8
commit fbbbd4e9f4
42 changed files with 1569 additions and 0 deletions

4
.gitignore vendored Normal file
View File

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

13
Makefile Normal file
View File

@ -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)

4
doc/grammar.bnf Normal file
View File

@ -0,0 +1,4 @@
PROG ::= (INSTR (EOI INSTR)*)?
INSTR ::= EXPR
EXPR ::= BASE
BASE ::= int

52
lib/Compiler.cpp Normal file
View File

@ -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);
}
}
}

27
lib/Compiler.hpp Normal file
View File

@ -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

134
lib/Lexer.cpp Normal file
View File

@ -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 {};
}
}

44
lib/Lexer.hpp Normal file
View File

@ -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

60
lib/Node.cpp Normal file
View File

@ -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];
}
}

41
lib/Node.hpp Normal file
View File

@ -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

118
lib/Parser.cpp Normal file
View File

@ -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;
}
}

38
lib/Parser.hpp Normal file
View File

@ -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

56
lib/Program.cpp Normal file
View File

@ -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();
}
}

39
lib/Program.hpp Normal file
View File

@ -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

19
lib/SrcLoc.cpp Normal file
View File

@ -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;
}
}

25
lib/SrcLoc.hpp Normal file
View File

@ -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

33
lib/StaticPass.cpp Normal file
View File

@ -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() + "'");
}
}
}

23
lib/StaticPass.hpp Normal file
View File

@ -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

23
lib/StatusLog.cpp Normal file
View File

@ -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()};
}
}

27
lib/StatusLog.hpp Normal file
View File

@ -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

23
lib/Type.cpp Normal file
View File

@ -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;
}
}

29
lib/Type.hpp Normal file
View File

@ -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

33
lib/TypeResolver.cpp Normal file
View File

@ -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;
}
}

23
lib/TypeResolver.hpp Normal file
View File

@ -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

54
lib/VM.cpp Normal file
View File

@ -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;
}
}

28
lib/VM.hpp Normal file
View File

@ -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

25
lib/Value.cpp Normal file
View File

@ -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);
}
}

26
lib/Value.hpp Normal file
View File

@ -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

13
lib/commons.hpp Normal file
View File

@ -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

11
lib/mutils.hpp Normal file
View File

@ -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

15
lib/opcodes.hpp Normal file
View File

@ -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

53
meson.build Normal file
View File

@ -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
])

79
src/Args.cpp Normal file
View File

@ -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;
}

29
src/Args.hpp Normal file
View File

@ -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

32
src/Loader.cpp Normal file
View File

@ -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;
}

18
src/Loader.hpp Normal file
View File

@ -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

64
src/main.cpp Normal file
View File

@ -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;
}

61
tests/Compiler.cpp Normal file
View File

@ -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);
}

41
tests/Lexer.cpp Normal file
View File

@ -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));
}

51
tests/Parser.cpp Normal file
View File

@ -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");
}

36
tests/StaticPass.cpp Normal file
View File

@ -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 ");
}

43
tests/TypeResolver.cpp Normal file
View File

@ -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));
}

2
tests/main.cpp Normal file
View File

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