Compare commits
No commits in common. "e5c388a38e4e1f552c93517753a82598873976dd" and "b188eba63a2878bb8c35d912a23bca3eda2bf315" have entirely different histories.
e5c388a38e
...
b188eba63a
|
@ -1,4 +0,0 @@
|
||||||
.cache
|
|
||||||
build
|
|
||||||
*~*
|
|
||||||
*\#*
|
|
11
Makefile
11
Makefile
|
@ -1,11 +0,0 @@
|
||||||
.PHONY: build tests
|
|
||||||
|
|
||||||
build:
|
|
||||||
meson setup build
|
|
||||||
meson compile -C build
|
|
||||||
|
|
||||||
tests: build
|
|
||||||
build/grino-tests
|
|
||||||
|
|
||||||
install: tests
|
|
||||||
meson install -C build
|
|
|
@ -1,6 +0,0 @@
|
||||||
MODULE ::= EXPR*
|
|
||||||
EXPR ::=
|
|
||||||
bool
|
|
||||||
| ident
|
|
||||||
| VARDECL
|
|
||||||
VARDECL ::= opar decl ident EXPR cpar
|
|
50
meson.build
50
meson.build
|
@ -1,50 +0,0 @@
|
||||||
project('grino',
|
|
||||||
'cpp',
|
|
||||||
version: '0.0.0',
|
|
||||||
default_options: [
|
|
||||||
'warning_level=3',
|
|
||||||
'cpp_std=c++17'
|
|
||||||
])
|
|
||||||
|
|
||||||
conf = configuration_data()
|
|
||||||
conf.set('version', meson.project_version())
|
|
||||||
|
|
||||||
configure_file(input: 'src/config.in.hpp',
|
|
||||||
output: 'config.hpp',
|
|
||||||
configuration: conf)
|
|
||||||
|
|
||||||
grino_src = static_library('grino',
|
|
||||||
sources: [
|
|
||||||
'src/Compiler.cpp',
|
|
||||||
'src/Lexer.cpp',
|
|
||||||
'src/Loc.cpp',
|
|
||||||
'src/Logger.cpp',
|
|
||||||
'src/Node.cpp',
|
|
||||||
'src/Parser.cpp',
|
|
||||||
'src/Program.cpp',
|
|
||||||
'src/VM.cpp',
|
|
||||||
'src/Value.cpp',
|
|
||||||
'src/SymTable.cpp',
|
|
||||||
])
|
|
||||||
|
|
||||||
grino_dep = declare_dependency(link_with: grino_src)
|
|
||||||
|
|
||||||
executable('grino',
|
|
||||||
sources: [
|
|
||||||
'src/main.cpp'
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
grino_dep
|
|
||||||
],
|
|
||||||
install: true)
|
|
||||||
|
|
||||||
executable('grino-tests',
|
|
||||||
sources: [
|
|
||||||
'tests/main.cpp',
|
|
||||||
'tests/Lexer.cpp',
|
|
||||||
'tests/Parser.cpp',
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
grino_dep,
|
|
||||||
dependency('catch2')
|
|
||||||
])
|
|
|
@ -1,78 +0,0 @@
|
||||||
#include "Compiler.hpp"
|
|
||||||
#include "Program.hpp"
|
|
||||||
#include "SymTable.hpp"
|
|
||||||
#include "src/opcodes.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
/*explicit*/ Compiler::Compiler(Logger& logger, SymTable& sym)
|
|
||||||
: m_logger { logger }
|
|
||||||
, m_sym { sym }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Compiler::~Compiler()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::compile(std::shared_ptr<Node> node,
|
|
||||||
Program& program)
|
|
||||||
{
|
|
||||||
switch (node->type())
|
|
||||||
{
|
|
||||||
case NODE_MODULE: {
|
|
||||||
for (size_t i=0; i<node->size(); i++)
|
|
||||||
{
|
|
||||||
compile(node->child(i).lock(), program);
|
|
||||||
program.push_instr(OPCODE_POP);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NODE_BOOL: {
|
|
||||||
std::string repr = node->repr();
|
|
||||||
auto value = Value::make_bool(repr == "true");
|
|
||||||
|
|
||||||
program.push_instr(OPCODE_LOAD_CONST,
|
|
||||||
program.push_constant(value));
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NODE_VARDECL: {
|
|
||||||
std::string ident = node->child(0).lock()->repr();
|
|
||||||
auto expr = node->child(1).lock();
|
|
||||||
size_t address = get_local_address();
|
|
||||||
|
|
||||||
compile(expr, program);
|
|
||||||
|
|
||||||
m_sym.declare(node->loc(), ident, address);
|
|
||||||
program.push_instr(OPCODE_STORE_LOCAL, address);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NODE_IDENT: {
|
|
||||||
std::string ident = node->repr();
|
|
||||||
auto entry = m_sym.find(ident);
|
|
||||||
|
|
||||||
if (entry == std::nullopt)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "undefined variable '" << ident << "'";
|
|
||||||
m_logger.log<compile_error>(LOG_ERROR, node->loc(), ss.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
program.push_instr(OPCODE_LOAD_LOCAL, entry->addr);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
std::cerr << "cannot compile node '"
|
|
||||||
<< GRINO_TRIM(NodeTypeStr[node->type()], "NODE_")
|
|
||||||
<< "'" << std::endl;
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Compiler::get_local_address()
|
|
||||||
{
|
|
||||||
static size_t addr = 0;
|
|
||||||
addr++;
|
|
||||||
return addr - 1;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
#ifndef grino_COMPILER_HPP
|
|
||||||
#define grino_COMPILER_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Logger.hpp"
|
|
||||||
#include "Node.hpp"
|
|
||||||
#include "src/mutils.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
class Program;
|
|
||||||
class SymTable;
|
|
||||||
|
|
||||||
GRINO_ERROR(compile_error);
|
|
||||||
|
|
||||||
class Compiler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Compiler(Logger& logger, SymTable& sym);
|
|
||||||
virtual ~Compiler();
|
|
||||||
|
|
||||||
void compile(std::shared_ptr<Node> node,
|
|
||||||
Program& program);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Logger& m_logger;
|
|
||||||
SymTable& m_sym;
|
|
||||||
|
|
||||||
size_t get_local_address();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
225
src/Lexer.cpp
225
src/Lexer.cpp
|
@ -1,225 +0,0 @@
|
||||||
#include "Lexer.hpp"
|
|
||||||
#include "src/Node.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
/*explicit*/ Lexer::Lexer(Logger& logger, std::filesystem::path source_path)
|
|
||||||
: m_logger { logger }
|
|
||||||
, m_loc {source_path, 1}
|
|
||||||
{
|
|
||||||
add_text(NODE_OPAR, "(", false);
|
|
||||||
add_text(NODE_CPAR, ")", false);
|
|
||||||
add_text(NODE_DECL, "$", false);
|
|
||||||
|
|
||||||
add_keyword(NODE_BOOL, "true", true);
|
|
||||||
add_keyword(NODE_BOOL, "false", true);
|
|
||||||
|
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Lexer::~Lexer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lexer::scan(std::string const& source)
|
|
||||||
{
|
|
||||||
m_cursor = 0;
|
|
||||||
m_source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Lexer::next()
|
|
||||||
{
|
|
||||||
std::optional<ScanInfo> info;
|
|
||||||
|
|
||||||
skip_spaces();
|
|
||||||
|
|
||||||
while (has_more(m_cursor)
|
|
||||||
&& at(m_cursor) == ';')
|
|
||||||
{
|
|
||||||
while (has_more(m_cursor)
|
|
||||||
&& at(m_cursor) != '\n')
|
|
||||||
{
|
|
||||||
m_cursor++;
|
|
||||||
}
|
|
||||||
|
|
||||||
skip_spaces();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const& scanner: m_scanners)
|
|
||||||
{
|
|
||||||
auto my_info = scanner();
|
|
||||||
if ((my_info && !info)
|
|
||||||
|| (my_info && info && my_info->cursor > info->cursor))
|
|
||||||
{
|
|
||||||
info = my_info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info)
|
|
||||||
{
|
|
||||||
m_cursor = info->cursor;
|
|
||||||
return std::make_shared<Node>(info->node, info->repr, m_loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_more(m_cursor))
|
|
||||||
{
|
|
||||||
std::string text;
|
|
||||||
while (has_more(m_cursor) && !is_sep(m_cursor))
|
|
||||||
{
|
|
||||||
text += at(m_cursor);
|
|
||||||
m_cursor++;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "unknown symbol '" << text << "'";
|
|
||||||
m_logger.log<lexical_error>(LOG_ERROR, m_loc, ss.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lexer::skip_spaces()
|
|
||||||
{
|
|
||||||
while (has_more(m_cursor)
|
|
||||||
&& std::isspace(at(m_cursor)))
|
|
||||||
{
|
|
||||||
|
|
||||||
if (at(m_cursor) == '\n')
|
|
||||||
{
|
|
||||||
m_loc = Loc {m_loc.path(), m_loc.line() + 1};
|
|
||||||
}
|
|
||||||
|
|
||||||
m_cursor++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Lexer::is_sep(size_t index) const
|
|
||||||
{
|
|
||||||
if (index >= m_source.size())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
char c = m_source[index];
|
|
||||||
if (std::isspace(c)) { return true; }
|
|
||||||
if (c == ';') { return true; }
|
|
||||||
|
|
||||||
auto itr = std::find(std::begin(m_separators),
|
|
||||||
std::end(m_separators),
|
|
||||||
c);
|
|
||||||
|
|
||||||
return itr != std::end(m_separators);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Lexer::has_more(size_t index) const
|
|
||||||
{
|
|
||||||
return index < m_source.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
char Lexer::at(size_t index) const
|
|
||||||
{
|
|
||||||
assert(has_more(index));
|
|
||||||
return m_source[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lexer::add_text(NodeType type,
|
|
||||||
std::string const& text,
|
|
||||||
bool has_value /* = false */)
|
|
||||||
{
|
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_text,
|
|
||||||
this, type,
|
|
||||||
text, has_value));
|
|
||||||
if (text.size() == 1)
|
|
||||||
{
|
|
||||||
m_separators.push_back(text[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lexer::add_keyword(NodeType type,
|
|
||||||
std::string const& text,
|
|
||||||
bool has_value /* = false */)
|
|
||||||
{
|
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_keyword,
|
|
||||||
this, type,
|
|
||||||
text, has_value));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<ScanInfo> Lexer::scan_text(NodeType type,
|
|
||||||
std::string const& text,
|
|
||||||
bool has_value)
|
|
||||||
{
|
|
||||||
if (m_cursor + text.size() > m_source.size())
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i=0; i<text.size(); i++)
|
|
||||||
{
|
|
||||||
if (at(m_cursor + i) != text[i])
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ScanInfo {
|
|
||||||
m_cursor + text.size(),
|
|
||||||
type,
|
|
||||||
has_value ? text : ""
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<ScanInfo> Lexer::scan_keyword(NodeType type,
|
|
||||||
std::string const& text,
|
|
||||||
bool has_value)
|
|
||||||
{
|
|
||||||
if (m_cursor + text.size() > m_source.size())
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i=0; i<text.size(); i++)
|
|
||||||
{
|
|
||||||
if (at(m_cursor + i) != text[i])
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_cursor + text.size() < m_source.size()
|
|
||||||
&& !is_sep(m_cursor + text.size()))
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ScanInfo {
|
|
||||||
m_cursor + text.size(),
|
|
||||||
type,
|
|
||||||
has_value ? text : ""
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<ScanInfo> Lexer::scan_ident()
|
|
||||||
{
|
|
||||||
size_t cursor = m_cursor;
|
|
||||||
|
|
||||||
std::string repr;
|
|
||||||
|
|
||||||
while (has_more(cursor)
|
|
||||||
&& !is_sep(cursor))
|
|
||||||
{
|
|
||||||
repr += at(cursor);
|
|
||||||
cursor++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (repr.empty() == false)
|
|
||||||
{
|
|
||||||
return ScanInfo {
|
|
||||||
cursor,
|
|
||||||
NODE_IDENT,
|
|
||||||
repr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
#ifndef grino_LEXER_HPP
|
|
||||||
#define grino_LEXER_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Node.hpp"
|
|
||||||
#include "Logger.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
GRINO_ERROR(lexical_error);
|
|
||||||
|
|
||||||
struct ScanInfo {
|
|
||||||
size_t cursor;
|
|
||||||
NodeType node;
|
|
||||||
std::string repr;
|
|
||||||
};
|
|
||||||
|
|
||||||
using scanner_t = std::function<std::optional<ScanInfo>()>;
|
|
||||||
|
|
||||||
class Lexer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Lexer(Logger& logger, std::filesystem::path source_path);
|
|
||||||
virtual ~Lexer();
|
|
||||||
|
|
||||||
void scan(std::string const& source);
|
|
||||||
std::shared_ptr<Node> next();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Logger& m_logger;
|
|
||||||
Loc m_loc;
|
|
||||||
std::vector<scanner_t> m_scanners;
|
|
||||||
std::vector<char> m_separators;
|
|
||||||
|
|
||||||
std::string m_source;
|
|
||||||
size_t m_cursor;
|
|
||||||
|
|
||||||
void skip_spaces();
|
|
||||||
|
|
||||||
bool is_sep(size_t index) const;
|
|
||||||
bool has_more(size_t index) const;
|
|
||||||
char at(size_t index) const;
|
|
||||||
|
|
||||||
void add_text(NodeType type,
|
|
||||||
std::string const& text,
|
|
||||||
bool has_value = false);
|
|
||||||
|
|
||||||
void add_keyword(NodeType type,
|
|
||||||
std::string const& text,
|
|
||||||
bool has_value = false);
|
|
||||||
|
|
||||||
std::optional<ScanInfo> scan_text(NodeType type,
|
|
||||||
std::string const& text,
|
|
||||||
bool has_value);
|
|
||||||
|
|
||||||
std::optional<ScanInfo> scan_keyword(NodeType type,
|
|
||||||
std::string const& text,
|
|
||||||
bool has_value);
|
|
||||||
|
|
||||||
std::optional<ScanInfo> scan_ident();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
14
src/Loc.cpp
14
src/Loc.cpp
|
@ -1,14 +0,0 @@
|
||||||
#include "Loc.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
/*explicit*/ Loc::Loc(std::filesystem::path path, int line)
|
|
||||||
: m_path { path }
|
|
||||||
, m_line { line }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Loc::~Loc()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
23
src/Loc.hpp
23
src/Loc.hpp
|
@ -1,23 +0,0 @@
|
||||||
#ifndef grino_LOC_HPP
|
|
||||||
#define grino_LOC_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
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
|
|
|
@ -1,12 +0,0 @@
|
||||||
#include "Logger.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
/*explicit*/ Logger::Logger()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Logger::~Logger()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
#ifndef grino_LOGGER_HPP
|
|
||||||
#define grino_LOGGER_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "src/mutils.hpp"
|
|
||||||
#include "Loc.hpp"
|
|
||||||
|
|
||||||
#define LOG_TYPE(G) \
|
|
||||||
G(LOG_ERROR)
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
GRINO_ENUM(Log, LOG_TYPE);
|
|
||||||
|
|
||||||
class Logger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Logger();
|
|
||||||
virtual ~Logger();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void log(LogType type, Loc const& loc, std::string const& what);
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void Logger::log(LogType type, Loc const& loc, std::string const& what)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << loc.path().string() << ":" << loc.line() << " ";
|
|
||||||
ss << "[" << GRINO_TRIM(LogTypeStr[type], "LOG_") << "] ";
|
|
||||||
ss << what;
|
|
||||||
|
|
||||||
throw T { ss.str() };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
55
src/Node.cpp
55
src/Node.cpp
|
@ -1,55 +0,0 @@
|
||||||
#include "Node.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
/*explicit*/ Node::Node(NodeType type,
|
|
||||||
std::string const& repr,
|
|
||||||
Loc const& loc)
|
|
||||||
: m_type { type }
|
|
||||||
, m_repr { repr }
|
|
||||||
, m_loc { loc }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Node::~Node()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Node::add_child(std::shared_ptr<Node> node)
|
|
||||||
{
|
|
||||||
m_children.push_back(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::weak_ptr<Node> Node::child(size_t index)
|
|
||||||
{
|
|
||||||
assert(index < size());
|
|
||||||
return m_children[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Node::string() const
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << GRINO_TRIM(NodeTypeStr[m_type], "NODE_");
|
|
||||||
|
|
||||||
if (!m_repr.empty())
|
|
||||||
{
|
|
||||||
ss << "[" << m_repr << "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size() > 0)
|
|
||||||
{
|
|
||||||
std::string sep;
|
|
||||||
ss << "(";
|
|
||||||
|
|
||||||
for (auto c: m_children)
|
|
||||||
{
|
|
||||||
ss << sep << c->string();
|
|
||||||
sep = ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
ss << ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
}
|
|
47
src/Node.hpp
47
src/Node.hpp
|
@ -1,47 +0,0 @@
|
||||||
#ifndef grino_NODE_HPP
|
|
||||||
#define grino_NODE_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Loc.hpp"
|
|
||||||
|
|
||||||
#define NODE_TYPE(G) \
|
|
||||||
G(NODE_MODULE), \
|
|
||||||
G(NODE_BOOL), \
|
|
||||||
G(NODE_VARDECL), \
|
|
||||||
G(NODE_IDENT), \
|
|
||||||
G(NODE_OPAR), \
|
|
||||||
G(NODE_CPAR), \
|
|
||||||
G(NODE_DECL),
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
GRINO_ENUM(Node, NODE_TYPE);
|
|
||||||
|
|
||||||
class Node
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Node(NodeType type,
|
|
||||||
std::string const& repr,
|
|
||||||
Loc const& loc);
|
|
||||||
|
|
||||||
virtual ~Node();
|
|
||||||
|
|
||||||
NodeType type() const { return m_type; }
|
|
||||||
std::string repr() const { return m_repr; }
|
|
||||||
Loc loc() const { return m_loc; }
|
|
||||||
size_t size() const { return m_children.size(); }
|
|
||||||
|
|
||||||
void add_child(std::shared_ptr<Node> node);
|
|
||||||
std::weak_ptr<Node> child(size_t index);
|
|
||||||
|
|
||||||
std::string string() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeType m_type;
|
|
||||||
std::string m_repr;
|
|
||||||
Loc m_loc;
|
|
||||||
std::vector<std::shared_ptr<Node>> m_children;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
154
src/Parser.cpp
154
src/Parser.cpp
|
@ -1,154 +0,0 @@
|
||||||
#include "Parser.hpp"
|
|
||||||
#include "src/mutils.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
/*explicit*/ Parser::Parser(Logger& logger, Lexer& lexer)
|
|
||||||
: m_logger { logger }
|
|
||||||
, m_lexer { lexer }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Parser::~Parser()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse(std::string const& source)
|
|
||||||
{
|
|
||||||
std::shared_ptr<Node> token;
|
|
||||||
|
|
||||||
m_lexer.scan(source);
|
|
||||||
|
|
||||||
while ( (token = m_lexer.next()) )
|
|
||||||
{
|
|
||||||
m_tokens.push_back(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_cursor = 0;
|
|
||||||
|
|
||||||
return parse_module();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::consume(NodeType type)
|
|
||||||
{
|
|
||||||
if (!type_is(type))
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "'" << GRINO_TRIM(NodeTypeStr[type], "NODE_")
|
|
||||||
<< "' expected, got '"
|
|
||||||
<< GRINO_TRIM(NodeTypeStr[m_tokens[m_cursor]->type()], "NODE_")
|
|
||||||
<< "'";
|
|
||||||
|
|
||||||
m_logger.log<syntax_error>(LOG_ERROR, loc(), ss.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return consume();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::consume()
|
|
||||||
{
|
|
||||||
assert(m_cursor < m_tokens.size());
|
|
||||||
auto node = m_tokens[m_cursor];
|
|
||||||
m_cursor++;
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Parser::type_is(NodeType type) const
|
|
||||||
{
|
|
||||||
if (m_cursor >= m_tokens.size())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_tokens[m_cursor]->type() == type;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Parser::type_is(std::vector<NodeType> const& types) const
|
|
||||||
{
|
|
||||||
if (m_cursor + types.size() >= m_tokens.size())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i=0; i<types.size(); i++)
|
|
||||||
{
|
|
||||||
if (m_tokens[m_cursor + i]->type() != types[i])
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Loc Parser::loc() const
|
|
||||||
{
|
|
||||||
if (m_cursor < m_tokens.size())
|
|
||||||
{
|
|
||||||
return m_tokens[m_cursor]->loc();
|
|
||||||
}
|
|
||||||
else if (m_tokens.empty() == false)
|
|
||||||
{
|
|
||||||
return m_tokens.back()->loc();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Loc {"???", 1};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::make_node(NodeType type,
|
|
||||||
std::string const& repr)
|
|
||||||
{
|
|
||||||
return std::make_shared<Node>(type, repr, loc());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_module()
|
|
||||||
{
|
|
||||||
auto node = make_node(NODE_MODULE);
|
|
||||||
|
|
||||||
while (m_cursor < m_tokens.size())
|
|
||||||
{
|
|
||||||
node->add_child(parse_expr());
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_expr()
|
|
||||||
{
|
|
||||||
if (type_is(NODE_OPAR))
|
|
||||||
{
|
|
||||||
return parse_vardecl();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type_is(NODE_IDENT)
|
|
||||||
|| type_is(NODE_BOOL))
|
|
||||||
{
|
|
||||||
return consume();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "unexpected '"
|
|
||||||
<< GRINO_TRIM(NodeTypeStr[m_tokens[m_cursor]->type()], "NODE_")
|
|
||||||
<< "'";
|
|
||||||
|
|
||||||
m_logger.log<syntax_error>(LOG_ERROR, loc(), ss.str());
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_vardecl()
|
|
||||||
{
|
|
||||||
auto node = make_node(NODE_VARDECL);
|
|
||||||
consume(NODE_OPAR);
|
|
||||||
consume(NODE_DECL);
|
|
||||||
|
|
||||||
node->add_child(consume(NODE_IDENT));
|
|
||||||
node->add_child(parse_expr());
|
|
||||||
|
|
||||||
consume(NODE_CPAR);
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
#ifndef grino_PARSER_HPP
|
|
||||||
#define grino_PARSER_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Lexer.hpp"
|
|
||||||
#include "src/mutils.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
GRINO_ERROR(syntax_error);
|
|
||||||
|
|
||||||
class Parser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Parser(Logger& logger, Lexer& lexer);
|
|
||||||
virtual ~Parser();
|
|
||||||
|
|
||||||
std::shared_ptr<Node> parse(std::string const& source);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Logger& m_logger;
|
|
||||||
Lexer& m_lexer;
|
|
||||||
std::vector<std::shared_ptr<Node>> m_tokens;
|
|
||||||
size_t m_cursor;
|
|
||||||
|
|
||||||
std::shared_ptr<Node> consume(NodeType type);
|
|
||||||
std::shared_ptr<Node> consume();
|
|
||||||
bool type_is(NodeType type) const;
|
|
||||||
bool type_is(std::vector<NodeType> const& types) const;
|
|
||||||
Loc loc() const;
|
|
||||||
|
|
||||||
std::shared_ptr<Node> make_node(NodeType type,
|
|
||||||
std::string const& repr="");
|
|
||||||
|
|
||||||
std::shared_ptr<Node> parse_module();
|
|
||||||
std::shared_ptr<Node> parse_expr();
|
|
||||||
std::shared_ptr<Node> parse_vardecl();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,79 +0,0 @@
|
||||||
#include "Program.hpp"
|
|
||||||
#include "src/opcodes.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
/*explicit*/ Program::Program()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Program::~Program()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Instr Program::get(size_t index) const
|
|
||||||
{
|
|
||||||
assert(index < size());
|
|
||||||
return m_instrs[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Program::push_instr(OpcodeType opcode,
|
|
||||||
std::optional<size_t> param /*=std::nullopt*/)
|
|
||||||
{
|
|
||||||
m_instrs.push_back(Instr {opcode, param});
|
|
||||||
return m_instrs.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Program::push_constant(std::shared_ptr<Value> value)
|
|
||||||
{
|
|
||||||
size_t addr = 0;
|
|
||||||
|
|
||||||
for (auto const& constant: m_constants)
|
|
||||||
{
|
|
||||||
if (value->equals(*constant))
|
|
||||||
{
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
addr++;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_constants.push_back(value);
|
|
||||||
return m_constants.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Value> Program::constant(size_t index) const
|
|
||||||
{
|
|
||||||
assert(index < m_constants.size());
|
|
||||||
return m_constants[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Program::string() const
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
size_t addr = 0;
|
|
||||||
|
|
||||||
for (auto const& instr: m_instrs)
|
|
||||||
{
|
|
||||||
ss << addr << "\t" << GRINO_TRIM(OpcodeTypeStr[instr.opcode], "OPCODE_");
|
|
||||||
|
|
||||||
if (instr.param)
|
|
||||||
{
|
|
||||||
ss << "\t" << *instr.param;
|
|
||||||
}
|
|
||||||
|
|
||||||
ss << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
addr = 0;
|
|
||||||
ss << "\n";
|
|
||||||
|
|
||||||
for (auto const& value: m_constants)
|
|
||||||
{
|
|
||||||
ss << addr << "\t" << value->string() << "\n";
|
|
||||||
addr++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
#ifndef grino_PROGRAM_HPP
|
|
||||||
#define grino_PROGRAM_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Value.hpp"
|
|
||||||
#include "opcodes.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
struct Instr {
|
|
||||||
OpcodeType opcode;
|
|
||||||
std::optional<size_t> param;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Program
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Program();
|
|
||||||
virtual ~Program();
|
|
||||||
|
|
||||||
|
|
||||||
size_t size() const { return m_instrs.size(); }
|
|
||||||
|
|
||||||
Instr get(size_t index) const;
|
|
||||||
|
|
||||||
size_t push_instr(OpcodeType opcode,
|
|
||||||
std::optional<size_t> param=std::nullopt);
|
|
||||||
|
|
||||||
size_t push_constant(std::shared_ptr<Value> value);
|
|
||||||
std::shared_ptr<Value> constant(size_t index) const;
|
|
||||||
|
|
||||||
std::string string() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Instr> m_instrs;
|
|
||||||
std::vector<std::shared_ptr<Value>> m_constants;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,55 +0,0 @@
|
||||||
#include "SymTable.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
/*explicit*/ SymTable::SymTable(Logger& logger)
|
|
||||||
: m_logger { logger }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ SymTable::~SymTable()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SymTable::declare(Loc const& loc, std::string const& name, size_t addr)
|
|
||||||
{
|
|
||||||
if (find(name))
|
|
||||||
{
|
|
||||||
m_logger.log<symbolic_error>(LOG_ERROR, loc, "'"
|
|
||||||
+ name
|
|
||||||
+ "' already defined");
|
|
||||||
}
|
|
||||||
|
|
||||||
SymEntry entry;
|
|
||||||
entry.addr = addr;
|
|
||||||
entry.name = name;
|
|
||||||
entry.is_global = false;
|
|
||||||
|
|
||||||
m_entries.push_back(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<SymEntry> SymTable::find(std::string const& name)
|
|
||||||
{
|
|
||||||
for (size_t i=0; i<m_entries.size(); i++)
|
|
||||||
{
|
|
||||||
if (m_entries[i].name == name)
|
|
||||||
{
|
|
||||||
return m_entries[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string SymTable::string() const
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
|
|
||||||
for (auto const& entry: m_entries)
|
|
||||||
{
|
|
||||||
ss << entry.addr << "\t" << entry.name << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
#ifndef grino_SYMTABLE_HPP
|
|
||||||
#define grino_SYMTABLE_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Logger.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
struct SymEntry {
|
|
||||||
std::string name;
|
|
||||||
bool is_global;
|
|
||||||
size_t addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
GRINO_ERROR(symbolic_error);
|
|
||||||
|
|
||||||
class SymTable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit SymTable(Logger& logger);
|
|
||||||
virtual ~SymTable();
|
|
||||||
|
|
||||||
void declare(Loc const& loc, std::string const& name, size_t addr);
|
|
||||||
std::optional<SymEntry> find(std::string const& name);
|
|
||||||
|
|
||||||
std::string string() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Logger& m_logger;
|
|
||||||
std::vector<SymEntry> m_entries;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
112
src/VM.cpp
112
src/VM.cpp
|
@ -1,112 +0,0 @@
|
||||||
#include "VM.hpp"
|
|
||||||
#include "src/opcodes.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
/*explicit*/ VM::VM(Logger& logger)
|
|
||||||
: m_logger { logger }
|
|
||||||
{
|
|
||||||
m_frames.push_back(Frame {});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ VM::~VM()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void VM::run(Program& program)
|
|
||||||
{
|
|
||||||
m_bp = 0;
|
|
||||||
m_sp = 0;
|
|
||||||
m_pc = 0;
|
|
||||||
|
|
||||||
while (m_pc < program.size())
|
|
||||||
{
|
|
||||||
Instr instr = program.get(m_pc);
|
|
||||||
|
|
||||||
switch (instr.opcode)
|
|
||||||
{
|
|
||||||
case OPCODE_LOAD_CONST: {
|
|
||||||
push(*instr.param);
|
|
||||||
m_pc++;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case OPCODE_POP: {
|
|
||||||
pop();
|
|
||||||
m_pc++;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case OPCODE_STORE_LOCAL: {
|
|
||||||
size_t addr = *instr.param;
|
|
||||||
auto value = program.constant(top());
|
|
||||||
set_local(addr, value);
|
|
||||||
m_pc++;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case OPCODE_LOAD_LOCAL: {
|
|
||||||
size_t addr = *instr.param;
|
|
||||||
push(program.push_constant(local(addr)));
|
|
||||||
m_pc++;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
std::cerr << "cannot execute unknown opcode "
|
|
||||||
<< OpcodeTypeStr[instr.opcode]
|
|
||||||
<< std::endl;
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string VM::string() const
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
|
|
||||||
for (size_t i=0; i<m_sp; i++)
|
|
||||||
{
|
|
||||||
ss << i << "\t" << m_stack[i] << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VM::push(size_t addr)
|
|
||||||
{
|
|
||||||
if (m_sp >= STACK_SIZE)
|
|
||||||
{
|
|
||||||
m_logger.log<execution_error>(LOG_ERROR,
|
|
||||||
Loc {"-", 1},
|
|
||||||
"stack overflow");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_stack[m_sp] = addr;
|
|
||||||
m_sp++;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t VM::pop()
|
|
||||||
{
|
|
||||||
assert(m_sp > 0);
|
|
||||||
|
|
||||||
size_t addr = m_stack[m_sp - 1];
|
|
||||||
m_sp--;
|
|
||||||
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t VM::top()
|
|
||||||
{
|
|
||||||
size_t addr = m_stack[m_sp - 1];
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Value> VM::local(size_t addr) const
|
|
||||||
{
|
|
||||||
return m_frames.back().locals.at(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VM::set_local(size_t addr,
|
|
||||||
std::shared_ptr<Value> value)
|
|
||||||
{
|
|
||||||
m_frames.back().locals[addr] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
45
src/VM.hpp
45
src/VM.hpp
|
@ -1,45 +0,0 @@
|
||||||
#ifndef grino_VM_HPP
|
|
||||||
#define grino_VM_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Logger.hpp"
|
|
||||||
#include "Program.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
constexpr size_t STACK_SIZE = 4096;
|
|
||||||
|
|
||||||
GRINO_ERROR(execution_error);
|
|
||||||
|
|
||||||
struct Frame {
|
|
||||||
std::unordered_map<size_t, std::shared_ptr<Value>> locals;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VM
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit VM(Logger& logger);
|
|
||||||
virtual ~VM();
|
|
||||||
|
|
||||||
void run(Program& program);
|
|
||||||
|
|
||||||
std::string string() const;
|
|
||||||
private:
|
|
||||||
Logger& m_logger;
|
|
||||||
std::array<size_t, STACK_SIZE> m_stack;
|
|
||||||
std::vector<Frame> m_frames;
|
|
||||||
|
|
||||||
size_t m_sp; /* stack pointer */
|
|
||||||
size_t m_bp; /* base pointer */
|
|
||||||
size_t m_pc; /* program counter */
|
|
||||||
|
|
||||||
void push(size_t addr);
|
|
||||||
size_t pop();
|
|
||||||
size_t top();
|
|
||||||
std::shared_ptr<Value> local(size_t addr) const;
|
|
||||||
void set_local(size_t addr,
|
|
||||||
std::shared_ptr<Value> value);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,49 +0,0 @@
|
||||||
#include "Value.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
/*static*/ std::shared_ptr<Value> Value::make_nil()
|
|
||||||
{
|
|
||||||
auto value = std::make_shared<Value>();
|
|
||||||
value->m_type = TYPE_NIL;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*static*/ std::shared_ptr<Value> Value::make_bool(bool val)
|
|
||||||
{
|
|
||||||
auto value = std::make_shared<Value>();
|
|
||||||
value->m_type = TYPE_BOOL;
|
|
||||||
value->m_bool_val = val;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Value::string() const
|
|
||||||
{
|
|
||||||
switch (m_type)
|
|
||||||
{
|
|
||||||
case TYPE_NIL: return "<nil>";
|
|
||||||
case TYPE_BOOL: return *m_bool_val ? "true" : "false";
|
|
||||||
|
|
||||||
default:
|
|
||||||
std::cerr << "cannot stringify value "
|
|
||||||
<< TypeTypeStr[m_type] << std::endl;
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Value::equals(Value const& other) const
|
|
||||||
{
|
|
||||||
if (m_type != other.m_type) { return false; }
|
|
||||||
|
|
||||||
switch (m_type)
|
|
||||||
{
|
|
||||||
case TYPE_NIL: return true;
|
|
||||||
case TYPE_BOOL: return *m_bool_val == *other.m_bool_val;
|
|
||||||
|
|
||||||
default:
|
|
||||||
std::cerr << "cannot compare equality with value "
|
|
||||||
<< TypeTypeStr[m_type] << std::endl;
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
#ifndef grino_VALUE_HPP
|
|
||||||
#define grino_VALUE_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "types.hpp"
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
class Value
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static std::shared_ptr<Value> make_nil();
|
|
||||||
static std::shared_ptr<Value> make_bool(bool val);
|
|
||||||
|
|
||||||
explicit Value() = default;
|
|
||||||
virtual ~Value() = default;
|
|
||||||
|
|
||||||
TypeType type() const { return m_type; }
|
|
||||||
bool as_bool() const { return *m_bool_val; }
|
|
||||||
|
|
||||||
std::string string() const;
|
|
||||||
bool equals(Value const& other) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
TypeType m_type = TYPE_NIL;
|
|
||||||
std::optional<bool> m_bool_val;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,18 +0,0 @@
|
||||||
#ifndef grino_COMMONS_HPP
|
|
||||||
#define grino_COMMONS_HPP
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <vector>
|
|
||||||
#include <array>
|
|
||||||
#include <memory>
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "config.hpp"
|
|
||||||
#include "mutils.hpp"
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,6 +0,0 @@
|
||||||
#ifndef grino_CONFIG_IN_HPP
|
|
||||||
#define grino_CONFIG_IN_HPP
|
|
||||||
|
|
||||||
#define GRINO_VERSION "@version@"
|
|
||||||
|
|
||||||
#endif
|
|
101
src/main.cpp
101
src/main.cpp
|
@ -1,101 +0,0 @@
|
||||||
#include <getopt.h>
|
|
||||||
#include "commons.hpp"
|
|
||||||
|
|
||||||
#include "Node.hpp"
|
|
||||||
#include "Lexer.hpp"
|
|
||||||
#include "Parser.hpp"
|
|
||||||
#include "Compiler.hpp"
|
|
||||||
#include "Program.hpp"
|
|
||||||
#include "VM.hpp"
|
|
||||||
#include "Logger.hpp"
|
|
||||||
#include "src/SymTable.hpp"
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
bool debug_mode = false;
|
|
||||||
|
|
||||||
static struct option options[] = {
|
|
||||||
{"help", no_argument, 0, 'h'},
|
|
||||||
{"version", no_argument, 0, 'v'},
|
|
||||||
{"debug", no_argument, 0, 'd'},
|
|
||||||
{0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
int option_index = 0;
|
|
||||||
|
|
||||||
int c = getopt_long(argc, argv, "hvd", options, &option_index);
|
|
||||||
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 'h': {
|
|
||||||
std::cout << "Usage: grino [OPTION]... source" << std::endl;
|
|
||||||
std::cout << "OPTIONS:" << std::endl;
|
|
||||||
std::cout << "\t" << "-d, --debug, "
|
|
||||||
<< "activate debug mode" << std::endl;
|
|
||||||
|
|
||||||
std::cout << "\t" << "-h, --help, "
|
|
||||||
<< "show this message" << std::endl;
|
|
||||||
std::cout << "\t" << "-v, --version, "
|
|
||||||
<< "show grino version" << std::endl;
|
|
||||||
exit(0);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 'v': {
|
|
||||||
std::cout << "grino version: " << GRINO_VERSION << std::endl;
|
|
||||||
std::cout << "License: " << "GPLv3 or later (see LICENSE)"<< std::endl;
|
|
||||||
exit(0);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 'd': {
|
|
||||||
debug_mode = true;
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optind < argc)
|
|
||||||
{
|
|
||||||
std::string source;
|
|
||||||
{
|
|
||||||
std::ifstream file { argv[optind] };
|
|
||||||
assert(file);
|
|
||||||
std::string line;
|
|
||||||
|
|
||||||
while (std::getline(file, line))
|
|
||||||
{ source += line + (file.eof() ? "" : "\n"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
grino::Logger logger;
|
|
||||||
grino::Lexer lexer {logger, argv[optind]};
|
|
||||||
grino::Parser parser {logger, lexer};
|
|
||||||
|
|
||||||
auto ast = parser.parse(source);
|
|
||||||
|
|
||||||
if (debug_mode)
|
|
||||||
{
|
|
||||||
std::cout << "--- ast ---" << std::endl;
|
|
||||||
std::cout << ast->string() << std::endl;
|
|
||||||
}
|
|
||||||
grino::SymTable sym_table {logger};
|
|
||||||
|
|
||||||
grino::Compiler compiler {logger, sym_table};
|
|
||||||
grino::Program program;
|
|
||||||
compiler.compile(ast, program);
|
|
||||||
|
|
||||||
if (debug_mode)
|
|
||||||
{
|
|
||||||
std::cout << "--- program ---" << std::endl;
|
|
||||||
std::cout << program.string() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
grino::VM vm {logger};
|
|
||||||
|
|
||||||
vm.run(program);
|
|
||||||
|
|
||||||
if (debug_mode)
|
|
||||||
{
|
|
||||||
std::cout << "--- stack ---" << std::endl;
|
|
||||||
std::cout << vm.string() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
#ifndef grino_MUTILS_HPP
|
|
||||||
#define grino_MUTILS_HPP
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#define GEN_ENUM(X) X
|
|
||||||
#define GEN_STR(X) #X
|
|
||||||
|
|
||||||
#define GRINO_ENUM(PREFIX, TYPE) \
|
|
||||||
enum PREFIX ## Type { TYPE(GEN_ENUM) }; \
|
|
||||||
constexpr char const* PREFIX ## TypeStr [] = { TYPE(GEN_STR) }
|
|
||||||
|
|
||||||
#define GRINO_TRIM(SRC, TOREMOVE) \
|
|
||||||
std::string(SRC).substr(std::string(TOREMOVE).size())
|
|
||||||
|
|
||||||
#define GRINO_ERROR(NAME) \
|
|
||||||
struct NAME : public std::runtime_error { \
|
|
||||||
NAME (std::string const& what) : std::runtime_error { what } {} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,18 +0,0 @@
|
||||||
#ifndef grino_OPCODES_HPP
|
|
||||||
#define grino_OPCODES_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "src/mutils.hpp"
|
|
||||||
|
|
||||||
#define OPCODES(G) \
|
|
||||||
G(OPCODE_LOAD_CONST), \
|
|
||||||
G(OPCODE_POP), \
|
|
||||||
G(OPCODE_LOAD_LOCAL), \
|
|
||||||
G(OPCODE_STORE_LOCAL)
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
GRINO_ENUM(Opcode, OPCODES);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,15 +0,0 @@
|
||||||
#ifndef grino_TYPES_HPP
|
|
||||||
#define grino_TYPES_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
|
|
||||||
#define TYPES(G) \
|
|
||||||
G(TYPE_NIL), \
|
|
||||||
G(TYPE_BOOL)
|
|
||||||
|
|
||||||
namespace grino
|
|
||||||
{
|
|
||||||
GRINO_ENUM(Type, TYPES);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,79 +0,0 @@
|
||||||
#include <catch2/catch.hpp>
|
|
||||||
#include "../src/Lexer.hpp"
|
|
||||||
|
|
||||||
class LexerTest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit LexerTest() {}
|
|
||||||
virtual ~LexerTest() {}
|
|
||||||
|
|
||||||
void test_next(grino::Lexer& lexer, std::string const& oracle)
|
|
||||||
{
|
|
||||||
auto node = lexer.next();
|
|
||||||
REQUIRE(node != nullptr);
|
|
||||||
REQUIRE(oracle == node->string());
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_end(grino::Lexer& lexer)
|
|
||||||
{
|
|
||||||
auto node = lexer.next();
|
|
||||||
REQUIRE(node == nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
grino::Logger m_logger;
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(LexerTest, "Lexer_booleans")
|
|
||||||
{
|
|
||||||
grino::Lexer lexer {m_logger, "tests/lexer"};
|
|
||||||
|
|
||||||
SECTION("space around")
|
|
||||||
{
|
|
||||||
lexer.scan(" true false ");
|
|
||||||
test_next(lexer, "BOOL[true]");
|
|
||||||
test_next(lexer, "BOOL[false]");
|
|
||||||
test_end(lexer);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("no space around")
|
|
||||||
{
|
|
||||||
lexer.scan("true false");
|
|
||||||
test_next(lexer, "BOOL[true]");
|
|
||||||
test_next(lexer, "BOOL[false]");
|
|
||||||
test_end(lexer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(LexerTest, "Lexer_comments")
|
|
||||||
{
|
|
||||||
grino::Lexer lexer {m_logger, "tests/lexer"};
|
|
||||||
|
|
||||||
lexer.scan(" true ; false \n\n true ");
|
|
||||||
test_next(lexer, "BOOL[true]");
|
|
||||||
test_next(lexer, "BOOL[true]");
|
|
||||||
test_end(lexer);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(LexerTest, "Lexer_vardecl")
|
|
||||||
{
|
|
||||||
grino::Lexer lexer {m_logger, "tests/lexer"};
|
|
||||||
|
|
||||||
lexer.scan(" $ () coucou) ");
|
|
||||||
test_next(lexer, "DECL");
|
|
||||||
test_next(lexer, "OPAR");
|
|
||||||
test_next(lexer, "CPAR");
|
|
||||||
test_next(lexer, "IDENT[coucou]");
|
|
||||||
test_next(lexer, "CPAR");
|
|
||||||
test_end(lexer);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(LexerTest, "Lexer_no_end_space")
|
|
||||||
{
|
|
||||||
grino::Lexer lexer {m_logger, "tests/lexer"};
|
|
||||||
|
|
||||||
lexer.scan(")");
|
|
||||||
test_next(lexer, "CPAR");
|
|
||||||
test_end(lexer);
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
#include <catch2/catch.hpp>
|
|
||||||
#include "../src/Parser.hpp"
|
|
||||||
#include "../src/Lexer.hpp"
|
|
||||||
|
|
||||||
class ParserTest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit ParserTest() {}
|
|
||||||
virtual ~ParserTest() {}
|
|
||||||
|
|
||||||
void test_parse(std::string const& oracle, std::string const& source)
|
|
||||||
{
|
|
||||||
grino::Logger logger;
|
|
||||||
grino::Lexer lexer {logger, "tests/parser"};
|
|
||||||
grino::Parser parser {logger, lexer};
|
|
||||||
|
|
||||||
auto root = parser.parse(source);
|
|
||||||
REQUIRE(oracle == root->string());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(ParserTest, "Parser_empty")
|
|
||||||
{
|
|
||||||
test_parse("MODULE", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(ParserTest, "Parser_booleans")
|
|
||||||
{
|
|
||||||
test_parse("MODULE(BOOL[true],BOOL[false])", "true false");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(ParserTest, "Parser_vardecl")
|
|
||||||
{
|
|
||||||
test_parse("MODULE(VARDECL(IDENT[hello],BOOL[false]))",
|
|
||||||
"($ hello false)");
|
|
||||||
|
|
||||||
test_parse("MODULE(VARDECL(IDENT[hello],IDENT[world]))",
|
|
||||||
"($ hello world)");
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
#define CATCH_CONFIG_MAIN
|
|
||||||
#include <catch2/catch.hpp>
|
|
Loading…
Reference in New Issue