Compare commits
No commits in common. "e8e4906cfca9c96f91c29005b7eed17262495ac7" and "59eab10960738d9b0ecd028d4edd969c82c4005a" have entirely different histories.
e8e4906cfc
...
59eab10960
|
@ -1,4 +0,0 @@
|
||||||
*~*
|
|
||||||
*\#*
|
|
||||||
build
|
|
||||||
.cache
|
|
12
Makefile
12
Makefile
|
@ -1,12 +0,0 @@
|
||||||
.PHONY: build tests
|
|
||||||
|
|
||||||
build:
|
|
||||||
meson setup build
|
|
||||||
meson compile -C build
|
|
||||||
|
|
||||||
tests: build
|
|
||||||
build/joko-tests
|
|
||||||
|
|
||||||
|
|
||||||
install: tests
|
|
||||||
meson install -C build
|
|
|
@ -1,6 +0,0 @@
|
||||||
#include "lib/Function.hpp"
|
|
||||||
#include "lib/Loader.hpp"
|
|
||||||
|
|
||||||
extern "C" void lib(jk::Loader& loader)
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
PROG ::= FUNCALL*
|
|
||||||
EXPR ::= LITERAL | FUNCALL
|
|
||||||
FUNCALL ::= opar ident EXPR* cpar
|
|
||||||
LITERAL ::= int | ident
|
|
13
lib/Code.cpp
13
lib/Code.cpp
|
@ -1,13 +0,0 @@
|
||||||
#include "Code.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ Code::Code(foreign_t foreign)
|
|
||||||
: m_foreign { foreign }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Code::~Code()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
22
lib/Code.hpp
22
lib/Code.hpp
|
@ -1,22 +0,0 @@
|
||||||
#ifndef jk_CODE_HPP
|
|
||||||
#define jk_CODE_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Function.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
class Code
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Code(foreign_t foreign);
|
|
||||||
virtual ~Code();
|
|
||||||
|
|
||||||
foreign_t foreign() const { return m_foreign; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
foreign_t m_foreign;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,80 +0,0 @@
|
||||||
#include "Compiler.hpp"
|
|
||||||
#include "lib/Opcodes.hpp"
|
|
||||||
#include "Code.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ Compiler::Compiler(std::shared_ptr<SymTable> sym,
|
|
||||||
Logger& logger)
|
|
||||||
: m_sym { sym }
|
|
||||||
, m_logger(logger)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Compiler::~Compiler()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::compile(std::shared_ptr<Node> node,
|
|
||||||
std::shared_ptr<Program> program)
|
|
||||||
{
|
|
||||||
switch (node->type())
|
|
||||||
{
|
|
||||||
case NODE_PROG: {
|
|
||||||
for (size_t i=0; i<node->size(); i++)
|
|
||||||
{
|
|
||||||
compile(node->child(i).lock(), program);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NODE_FUNCALL: {
|
|
||||||
for (size_t i=0; i<node->size(); i++)
|
|
||||||
{
|
|
||||||
compile(node->child(i).lock(), program);
|
|
||||||
}
|
|
||||||
|
|
||||||
program->push_instr(OPCODE_CALL, node->size() - 1);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NODE_IDENT: {
|
|
||||||
std::string ident = node->repr();
|
|
||||||
auto sym = m_sym->find(ident);
|
|
||||||
|
|
||||||
if (!sym)
|
|
||||||
{
|
|
||||||
m_logger.log<compile_error>(LOG_ERROR, node->loc(),
|
|
||||||
std::string()
|
|
||||||
+ "'"
|
|
||||||
+ ident
|
|
||||||
+ "' is undefined");
|
|
||||||
}
|
|
||||||
|
|
||||||
OpcodeType op_load = OPCODE_LOAD;
|
|
||||||
|
|
||||||
if (sym->is_global)
|
|
||||||
{
|
|
||||||
op_load = OPCODE_LOAD_GLOBAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sym->type->type() == TYPE_FUNCTION)
|
|
||||||
{
|
|
||||||
program->push_instr(op_load, sym->addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NODE_INT: {
|
|
||||||
auto value = Value::make_int(std::stoi(node->repr()));
|
|
||||||
size_t addr = program->push_constant(value);
|
|
||||||
program->push_instr(OPCODE_PUSH_CONST, addr);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
std::cerr << "cannot compile unknown node '"
|
|
||||||
<< NodeTypeStr[node->type()] << "'" << std::endl;
|
|
||||||
abort();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
#ifndef jk_COMPILER_HPP
|
|
||||||
#define jk_COMPILER_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "SymTable.hpp"
|
|
||||||
#include "Logger.hpp"
|
|
||||||
#include "Node.hpp"
|
|
||||||
#include "Program.hpp"
|
|
||||||
#include "VM.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
JK_ERROR(compile_error);
|
|
||||||
|
|
||||||
class Compiler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Compiler(std::shared_ptr<SymTable> sym,
|
|
||||||
Logger& logger);
|
|
||||||
virtual ~Compiler();
|
|
||||||
|
|
||||||
void compile(std::shared_ptr<Node> node,
|
|
||||||
std::shared_ptr<Program> program);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<SymTable> m_sym;
|
|
||||||
Logger& m_logger;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,26 +0,0 @@
|
||||||
#include "Factory.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ Factory::Factory(Logger& logger, std::filesystem::path path)
|
|
||||||
: m_logger { logger }
|
|
||||||
, m_path { path }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Factory::~Factory()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Lexer> Factory::make_lexer()
|
|
||||||
{
|
|
||||||
Loc loc {m_path, 1, 0};
|
|
||||||
return std::make_shared<Lexer>(m_logger, loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Parser> Factory::make_parser()
|
|
||||||
{
|
|
||||||
auto lexer = make_lexer();
|
|
||||||
return std::make_shared<Parser>(m_logger, lexer);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
#ifndef jk_FACTORY_HPP
|
|
||||||
#define jk_FACTORY_HPP
|
|
||||||
|
|
||||||
#include "Lexer.hpp"
|
|
||||||
#include "Parser.hpp"
|
|
||||||
#include "Logger.hpp"
|
|
||||||
#include "commons.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
class Factory
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Factory(Logger& logger, std::filesystem::path path);
|
|
||||||
virtual ~Factory();
|
|
||||||
|
|
||||||
std::shared_ptr<Lexer> make_lexer();
|
|
||||||
std::shared_ptr<Parser> make_parser();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Logger& m_logger;
|
|
||||||
std::filesystem::path m_path;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,19 +0,0 @@
|
||||||
#include "Function.hpp"
|
|
||||||
#include "Value.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ Function::Function(foreign_t foreign)
|
|
||||||
: m_foreign { foreign }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Function::~Function()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
value_t Function::call(std::vector<value_t> const& args)
|
|
||||||
{
|
|
||||||
return m_foreign(args);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
#ifndef jk_FUNCTION_HPP
|
|
||||||
#define jk_FUNCTION_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
class Value;
|
|
||||||
|
|
||||||
using value_t = std::shared_ptr<Value>;
|
|
||||||
using foreign_t = std::function<value_t(std::vector<value_t>)>;
|
|
||||||
|
|
||||||
class Function
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Function(foreign_t foreign);
|
|
||||||
virtual ~Function();
|
|
||||||
|
|
||||||
value_t call(std::vector<value_t> const& args);
|
|
||||||
|
|
||||||
private:
|
|
||||||
foreign_t m_foreign;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
212
lib/Lexer.cpp
212
lib/Lexer.cpp
|
@ -1,212 +0,0 @@
|
||||||
#include "Lexer.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ Lexer::Lexer(Logger& logger, Loc const& loc)
|
|
||||||
: m_logger { logger }
|
|
||||||
, m_loc { loc }
|
|
||||||
{
|
|
||||||
std::vector<std::tuple<NodeType, std::string, bool>> texts = {
|
|
||||||
{NODE_OPAR, "(", false},
|
|
||||||
{NODE_CPAR, ")", false}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto text: texts)
|
|
||||||
{
|
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_text,
|
|
||||||
this,
|
|
||||||
std::get<0>(text),
|
|
||||||
std::get<1>(text),
|
|
||||||
std::get<2>(text)));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
|
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_int, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Lexer::~Lexer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lexer::scan(std::string const& source)
|
|
||||||
{
|
|
||||||
m_source = source;
|
|
||||||
m_cursor = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Lexer::next()
|
|
||||||
{
|
|
||||||
skip_spaces();
|
|
||||||
|
|
||||||
while (more(m_cursor)
|
|
||||||
&& at(m_cursor) == '#')
|
|
||||||
{
|
|
||||||
while (more(m_cursor)
|
|
||||||
&& at(m_cursor) != '\n')
|
|
||||||
{
|
|
||||||
m_cursor++;
|
|
||||||
}
|
|
||||||
|
|
||||||
skip_spaces();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<ScanInfo> info;
|
|
||||||
|
|
||||||
for (auto scanner: m_scanners)
|
|
||||||
{
|
|
||||||
auto my_info = scanner();
|
|
||||||
|
|
||||||
if ((!info && my_info)
|
|
||||||
|| (info && my_info
|
|
||||||
&& my_info->cursor > info->cursor))
|
|
||||||
{
|
|
||||||
info = my_info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info)
|
|
||||||
{
|
|
||||||
m_cursor = info->cursor;
|
|
||||||
return std::make_shared<Node>(info->type, info->repr, m_loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (more(m_cursor))
|
|
||||||
{
|
|
||||||
std::string text;
|
|
||||||
|
|
||||||
while (more(m_cursor)
|
|
||||||
&& !std::isspace(at(m_cursor)))
|
|
||||||
{
|
|
||||||
text += at(m_cursor);
|
|
||||||
m_cursor++;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "unknown text '" << text << "'";
|
|
||||||
m_logger.log<lexical_error>(LOG_ERROR, m_loc, ss.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Lexer::more(size_t index) const
|
|
||||||
{
|
|
||||||
return index < m_source.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
char Lexer::at(size_t index) const
|
|
||||||
{
|
|
||||||
assert(more(index));
|
|
||||||
|
|
||||||
return m_source[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lexer::skip_spaces()
|
|
||||||
{
|
|
||||||
while (more(m_cursor)
|
|
||||||
&& std::isspace(at(m_cursor)))
|
|
||||||
{
|
|
||||||
if (at(m_cursor) == '\n')
|
|
||||||
{
|
|
||||||
m_loc = Loc {
|
|
||||||
m_loc.path(),
|
|
||||||
m_loc.line() + 1,
|
|
||||||
m_loc.column()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
m_cursor++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<ScanInfo> Lexer::scan_int() const
|
|
||||||
{
|
|
||||||
size_t cursor = m_cursor;
|
|
||||||
std::string repr;
|
|
||||||
|
|
||||||
while (more(cursor)
|
|
||||||
&& std::isdigit(at(cursor)))
|
|
||||||
{
|
|
||||||
repr += at(cursor);
|
|
||||||
cursor++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (repr.empty() == false)
|
|
||||||
{
|
|
||||||
return ScanInfo {
|
|
||||||
cursor,
|
|
||||||
NODE_INT,
|
|
||||||
repr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<ScanInfo> Lexer::scan_text(NodeType type,
|
|
||||||
std::string const& text,
|
|
||||||
bool has_value) const
|
|
||||||
{
|
|
||||||
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_ident() const
|
|
||||||
{
|
|
||||||
auto car = [](char c){
|
|
||||||
return std::isalpha(c)
|
|
||||||
|| c == '_'
|
|
||||||
|| c == '-'
|
|
||||||
|| c == '?'
|
|
||||||
|| c == '!'
|
|
||||||
|| c == '/';
|
|
||||||
};
|
|
||||||
|
|
||||||
auto cdr = [car](char c){
|
|
||||||
return car(c)
|
|
||||||
|| std::isdigit(c)
|
|
||||||
;
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t cursor = m_cursor;
|
|
||||||
std::string repr;
|
|
||||||
|
|
||||||
if (!more(cursor)
|
|
||||||
|| !car(at(cursor)))
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
repr += at(cursor);
|
|
||||||
cursor++;
|
|
||||||
|
|
||||||
while (more(cursor)
|
|
||||||
&& cdr(at(cursor)))
|
|
||||||
{
|
|
||||||
repr += at(cursor);
|
|
||||||
cursor++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ScanInfo {
|
|
||||||
cursor,
|
|
||||||
NODE_IDENT,
|
|
||||||
repr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
#ifndef jk_LEXER_HPP
|
|
||||||
#define jk_LEXER_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Logger.hpp"
|
|
||||||
#include "Node.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
JK_ERROR(lexical_error);
|
|
||||||
|
|
||||||
struct ScanInfo {
|
|
||||||
size_t cursor;
|
|
||||||
NodeType type;
|
|
||||||
std::string repr;
|
|
||||||
};
|
|
||||||
|
|
||||||
using scanner_t = std::function<std::optional<ScanInfo>()>;
|
|
||||||
|
|
||||||
class Lexer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Lexer(Logger& logger, Loc const& loc);
|
|
||||||
virtual ~Lexer();
|
|
||||||
|
|
||||||
Loc loc() const { return m_loc; }
|
|
||||||
|
|
||||||
void scan(std::string const& source);
|
|
||||||
std::shared_ptr<Node> next();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Logger& m_logger;
|
|
||||||
Loc m_loc;
|
|
||||||
size_t m_cursor;
|
|
||||||
std::string m_source;
|
|
||||||
std::vector<scanner_t> m_scanners;
|
|
||||||
|
|
||||||
bool more(size_t index) const;
|
|
||||||
char at(size_t index) const;
|
|
||||||
|
|
||||||
void skip_spaces();
|
|
||||||
|
|
||||||
std::optional<ScanInfo> scan_int() const;
|
|
||||||
std::optional<ScanInfo> scan_text(NodeType type,
|
|
||||||
std::string const& text,
|
|
||||||
bool has_value) const;
|
|
||||||
std::optional<ScanInfo> scan_ident() const;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,50 +0,0 @@
|
||||||
#include "Loader.hpp"
|
|
||||||
#include "lib/Function.hpp"
|
|
||||||
#include "lib/config.in.hpp"
|
|
||||||
#include <dlfcn.h>
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ Loader::Loader(std::shared_ptr<VM> vm,
|
|
||||||
std::shared_ptr<SymTable> sym)
|
|
||||||
: m_vm { vm }
|
|
||||||
, m_sym { sym }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Loader::~Loader()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::load()
|
|
||||||
{
|
|
||||||
for (auto entry: std::filesystem::directory_iterator(JOKO_LIBDIR))
|
|
||||||
{
|
|
||||||
if (entry.path().extension() == ".so")
|
|
||||||
{
|
|
||||||
void* handle = dlopen(entry.path().string().c_str(), RTLD_LAZY);
|
|
||||||
assert(handle);
|
|
||||||
typedef void(*func)(Loader&);
|
|
||||||
func f = (func) dlsym(handle, "lib");
|
|
||||||
assert(f);
|
|
||||||
|
|
||||||
f(*this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::declare(std::string const& name, foreign_t body)
|
|
||||||
{
|
|
||||||
auto fun = std::make_shared<Function>(body);
|
|
||||||
|
|
||||||
auto fun_val = Value::make_function(fun);
|
|
||||||
size_t addr = m_vm->push_heap(fun_val);
|
|
||||||
|
|
||||||
auto ref = Value::make_ref(addr);
|
|
||||||
size_t global_addr = m_sym->declare_global(name,
|
|
||||||
fun_val->type().lock(),
|
|
||||||
Loc {"global", 1, 1});
|
|
||||||
m_vm->set_global(global_addr, ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
#ifndef jk_LOADER_HPP
|
|
||||||
#define jk_LOADER_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "VM.hpp"
|
|
||||||
#include "SymTable.hpp"
|
|
||||||
#include "Function.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
class Loader
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Loader(std::shared_ptr<VM> vm,
|
|
||||||
std::shared_ptr<SymTable> sym);
|
|
||||||
virtual ~Loader();
|
|
||||||
|
|
||||||
void load();
|
|
||||||
void declare(std::string const& name, foreign_t body);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<VM> m_vm;
|
|
||||||
std::shared_ptr<SymTable> m_sym;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
15
lib/Loc.cpp
15
lib/Loc.cpp
|
@ -1,15 +0,0 @@
|
||||||
#include "Loc.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ Loc::Loc(std::filesystem::path path, int line, int column)
|
|
||||||
: m_path { path }
|
|
||||||
, m_line { line }
|
|
||||||
, m_column { column }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Loc::~Loc()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
25
lib/Loc.hpp
25
lib/Loc.hpp
|
@ -1,25 +0,0 @@
|
||||||
#ifndef jk_LOC_HPP
|
|
||||||
#define jk_LOC_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
class Loc
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Loc(std::filesystem::path path, int line, int column);
|
|
||||||
virtual ~Loc();
|
|
||||||
|
|
||||||
std::filesystem::path path() const { return m_path; }
|
|
||||||
int line() const { return m_line; }
|
|
||||||
int column() const { return m_column; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::filesystem::path m_path;
|
|
||||||
int m_line;
|
|
||||||
int m_column;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,12 +0,0 @@
|
||||||
#include "Logger.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ Logger::Logger()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Logger::~Logger()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
#ifndef jk_LOGGER_HPP
|
|
||||||
#define jk_LOGGER_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Loc.hpp"
|
|
||||||
|
|
||||||
#define LOG_TYPE(G) \
|
|
||||||
G(LOG_ERROR)
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
JK_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 << " " << (std::string(LogTypeStr[type])
|
|
||||||
.substr(std::string("LOG_").size()));
|
|
||||||
ss << " " << what;
|
|
||||||
|
|
||||||
throw T { ss.str() };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
57
lib/Node.cpp
57
lib/Node.cpp
|
@ -1,57 +0,0 @@
|
||||||
#include "Node.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ Node::Node(NodeType type,
|
|
||||||
std::string const& repr,
|
|
||||||
Loc const& loc)
|
|
||||||
: m_type { type }
|
|
||||||
, m_repr { repr }
|
|
||||||
, m_loc { loc }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Node::~Node()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Node::add_child(std::shared_ptr<Node> child)
|
|
||||||
{
|
|
||||||
m_children.push_back(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::weak_ptr<Node> Node::child(size_t index) const
|
|
||||||
{
|
|
||||||
assert(index < size());
|
|
||||||
return m_children[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Node::string() const
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << std::string(NodeTypeStr[m_type])
|
|
||||||
.substr(std::string("NODE_").size());
|
|
||||||
|
|
||||||
if (m_repr.empty() == false)
|
|
||||||
{
|
|
||||||
ss << "[" << m_repr << "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size() > 0)
|
|
||||||
{
|
|
||||||
ss << "(";
|
|
||||||
|
|
||||||
std::string sep;
|
|
||||||
|
|
||||||
for (auto child: m_children)
|
|
||||||
{
|
|
||||||
ss << sep << child->string();
|
|
||||||
sep = ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
ss << ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
}
|
|
44
lib/Node.hpp
44
lib/Node.hpp
|
@ -1,44 +0,0 @@
|
||||||
#ifndef jk_NODE_HPP
|
|
||||||
#define jk_NODE_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Loc.hpp"
|
|
||||||
|
|
||||||
#define NODE_TYPE(G) \
|
|
||||||
G(NODE_PROG), \
|
|
||||||
G(NODE_INT), \
|
|
||||||
G(NODE_OPAR), \
|
|
||||||
G(NODE_CPAR), \
|
|
||||||
G(NODE_IDENT), \
|
|
||||||
G(NODE_FUNCALL),
|
|
||||||
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
JK_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 const& loc() const { return m_loc; }
|
|
||||||
size_t size() const { return m_children.size(); }
|
|
||||||
|
|
||||||
void add_child(std::shared_ptr<Node> child);
|
|
||||||
std::weak_ptr<Node> child(size_t index) const;
|
|
||||||
|
|
||||||
std::string string() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeType m_type;
|
|
||||||
std::string m_repr;
|
|
||||||
Loc m_loc;
|
|
||||||
std::vector<std::shared_ptr<Node>> m_children;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,19 +0,0 @@
|
||||||
#ifndef jk_OPCODES_HPP
|
|
||||||
#define jk_OPCODES_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
|
|
||||||
#define OPCODES(G) \
|
|
||||||
G(OPCODE_PUSH_CONST), \
|
|
||||||
G(OPCODE_CALL), \
|
|
||||||
G(OPCODE_LOAD), \
|
|
||||||
G(OPCODE_STORE), \
|
|
||||||
G(OPCODE_LOAD_GLOBAL), \
|
|
||||||
G(OPCODE_MK_FUNCTION)
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
JK_ENUM(Opcode, OPCODES);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
142
lib/Parser.cpp
142
lib/Parser.cpp
|
@ -1,142 +0,0 @@
|
||||||
#include "Parser.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ Parser::Parser(Logger& logger, std::shared_ptr<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> tok;
|
|
||||||
m_cursor = 0;
|
|
||||||
m_lexer->scan(source);
|
|
||||||
|
|
||||||
while ( (tok=m_lexer->next()) )
|
|
||||||
{
|
|
||||||
m_tokens.push_back(tok);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_tokens.empty())
|
|
||||||
{
|
|
||||||
return std::make_shared<Node>(NODE_PROG, "",
|
|
||||||
Loc {m_lexer->loc().path(), 1, 1});
|
|
||||||
}
|
|
||||||
|
|
||||||
return parse_prog();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::current() const
|
|
||||||
{
|
|
||||||
assert(m_cursor < m_tokens.size());
|
|
||||||
return m_tokens[m_cursor];
|
|
||||||
}
|
|
||||||
|
|
||||||
Loc Parser::loc() const
|
|
||||||
{
|
|
||||||
return current()->loc();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::consume()
|
|
||||||
{
|
|
||||||
assert(m_cursor < m_tokens.size());
|
|
||||||
|
|
||||||
auto tok = m_tokens[m_cursor];
|
|
||||||
m_cursor++;
|
|
||||||
return tok;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::consume(NodeType type)
|
|
||||||
{
|
|
||||||
assert(m_cursor < m_tokens.size());
|
|
||||||
|
|
||||||
if (m_tokens[m_cursor]->type() != type)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "expected '"
|
|
||||||
<< std::string(NodeTypeStr[type])
|
|
||||||
.substr(std::string("NODE_").size())
|
|
||||||
<< "', got '"
|
|
||||||
<< std::string(NodeTypeStr[m_tokens[m_cursor]->type()])
|
|
||||||
.substr(std::string("NODE_").size())
|
|
||||||
<< "'";
|
|
||||||
|
|
||||||
m_logger.log<syntax_error>(LOG_ERROR,
|
|
||||||
m_tokens[m_cursor]->loc(),
|
|
||||||
ss.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return consume();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Parser::type_is(NodeType type, int lookahead) const
|
|
||||||
{
|
|
||||||
if (m_cursor + lookahead >= m_tokens.size())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_tokens[m_cursor + lookahead]->type() == type;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_prog()
|
|
||||||
{
|
|
||||||
auto root = std::make_shared<Node>(NODE_PROG, "", loc());
|
|
||||||
|
|
||||||
while (m_cursor < m_tokens.size())
|
|
||||||
{
|
|
||||||
root->add_child(parse_funcall());
|
|
||||||
}
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_expr()
|
|
||||||
{
|
|
||||||
if (type_is(NODE_OPAR))
|
|
||||||
{
|
|
||||||
return parse_funcall();
|
|
||||||
}
|
|
||||||
|
|
||||||
return parse_literal();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_funcall()
|
|
||||||
{
|
|
||||||
auto root = std::make_shared<Node>(NODE_FUNCALL, "", loc());
|
|
||||||
consume(NODE_OPAR);
|
|
||||||
|
|
||||||
while (!type_is(NODE_CPAR))
|
|
||||||
{
|
|
||||||
root->add_child(parse_expr());
|
|
||||||
}
|
|
||||||
|
|
||||||
consume(NODE_CPAR);
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_literal()
|
|
||||||
{
|
|
||||||
if (type_is(NODE_INT)
|
|
||||||
|| type_is(NODE_IDENT))
|
|
||||||
{
|
|
||||||
return consume();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_logger.log<syntax_error>(LOG_ERROR,
|
|
||||||
loc(),
|
|
||||||
std::string()
|
|
||||||
+ "unexpected token '"
|
|
||||||
+ current()->repr()
|
|
||||||
+ "'");
|
|
||||||
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
#ifndef jk_PARSER_HPP
|
|
||||||
#define jk_PARSER_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Logger.hpp"
|
|
||||||
#include "Lexer.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
JK_ERROR(syntax_error);
|
|
||||||
|
|
||||||
class Parser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Parser(Logger& logger, std::shared_ptr<Lexer> lexer);
|
|
||||||
virtual ~Parser();
|
|
||||||
|
|
||||||
std::shared_ptr<Node> parse(std::string const& source);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Logger& m_logger;
|
|
||||||
std::shared_ptr<Lexer> m_lexer;
|
|
||||||
std::vector<std::shared_ptr<Node>> m_tokens;
|
|
||||||
size_t m_cursor;
|
|
||||||
|
|
||||||
std::shared_ptr<Node> current() const;
|
|
||||||
Loc loc() const;
|
|
||||||
std::shared_ptr<Node> consume();
|
|
||||||
std::shared_ptr<Node> consume(NodeType type);
|
|
||||||
bool type_is(NodeType type, int lookahead=0) const;
|
|
||||||
|
|
||||||
std::shared_ptr<Node> parse_prog();
|
|
||||||
std::shared_ptr<Node> parse_expr();
|
|
||||||
std::shared_ptr<Node> parse_funcall();
|
|
||||||
std::shared_ptr<Node> parse_literal();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,61 +0,0 @@
|
||||||
#include "Program.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ Program::Program()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Program::~Program()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Instr Program::get(size_t index) const
|
|
||||||
{
|
|
||||||
assert(index < size());
|
|
||||||
return m_instrs[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void Program::push_instr(OpcodeType opcode,
|
|
||||||
std::optional<param_t> param /*= std::nullopt*/)
|
|
||||||
{
|
|
||||||
m_instrs.push_back(Instr { opcode, param });
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Program::string() const
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
size_t addr = 0;
|
|
||||||
|
|
||||||
for (auto const& instr: m_instrs)
|
|
||||||
{
|
|
||||||
ss << addr << "\t"
|
|
||||||
<< std::string(OpcodeTypeStr[instr.opcode])
|
|
||||||
.substr(std::string("OPCODE_").size());
|
|
||||||
|
|
||||||
if (instr.param)
|
|
||||||
{
|
|
||||||
ss << "\t" << *instr.param;
|
|
||||||
}
|
|
||||||
|
|
||||||
ss << std::endl;
|
|
||||||
|
|
||||||
addr++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Program::push_constant(std::shared_ptr<Value> value)
|
|
||||||
{
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
#ifndef jk_PROGRAM_HPP
|
|
||||||
#define jk_PROGRAM_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Opcodes.hpp"
|
|
||||||
#include "Value.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
using param_t = size_t;
|
|
||||||
|
|
||||||
struct Instr {
|
|
||||||
OpcodeType opcode;
|
|
||||||
std::optional<param_t> param;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Program
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Program();
|
|
||||||
virtual ~Program();
|
|
||||||
|
|
||||||
size_t size() const { return m_instrs.size(); }
|
|
||||||
|
|
||||||
Instr get(size_t index) const;
|
|
||||||
|
|
||||||
void push_instr(OpcodeType opcode,
|
|
||||||
std::optional<param_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,37 +0,0 @@
|
||||||
#include "StaticPass.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ StaticPass::StaticPass(std::shared_ptr<SymTable> sym,
|
|
||||||
Logger& logger)
|
|
||||||
: m_sym { sym }
|
|
||||||
, m_logger { logger }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ StaticPass::~StaticPass()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void StaticPass::pass(std::shared_ptr<Node> node)
|
|
||||||
{
|
|
||||||
switch (node->type())
|
|
||||||
{
|
|
||||||
case NODE_PROG: {
|
|
||||||
for (size_t i=0; i<node->size(); i++)
|
|
||||||
{
|
|
||||||
pass(node->child(i).lock());
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NODE_FUNCALL: {
|
|
||||||
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
std::cerr << "cannot static_pass unknown node '"
|
|
||||||
<< NodeTypeStr[node->type()] << "'" << std::endl;
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
#ifndef jk_STATICPASS_HPP
|
|
||||||
#define jk_STATICPASS_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "SymTable.hpp"
|
|
||||||
#include "Node.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
class StaticPass
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit StaticPass(std::shared_ptr<SymTable> sym, Logger& logger);
|
|
||||||
virtual ~StaticPass();
|
|
||||||
|
|
||||||
void pass(std::shared_ptr<Node> node);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<SymTable> m_sym;
|
|
||||||
Logger& m_logger;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,64 +0,0 @@
|
||||||
#include "SymTable.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ SymTable::SymTable(Logger& logger)
|
|
||||||
: m_logger { logger }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ SymTable::~SymTable()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
SymTable::declare(std::string const& name,
|
|
||||||
std::shared_ptr<Type> type,
|
|
||||||
Loc const& loc)
|
|
||||||
{
|
|
||||||
if (find(name))
|
|
||||||
{
|
|
||||||
m_logger.log<symbolic_error>(LOG_ERROR,
|
|
||||||
loc,
|
|
||||||
std::string()
|
|
||||||
+ "'"
|
|
||||||
+ name
|
|
||||||
+ "' is already defined");
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t id = m_syms.size();
|
|
||||||
|
|
||||||
m_syms.push_back(Sym {
|
|
||||||
name,
|
|
||||||
type,
|
|
||||||
loc,
|
|
||||||
false,
|
|
||||||
id
|
|
||||||
});
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
SymTable::declare_global(std::string const& name,
|
|
||||||
std::shared_ptr<Type> type,
|
|
||||||
Loc const& loc)
|
|
||||||
{
|
|
||||||
size_t addr = declare(name, type, loc);
|
|
||||||
m_syms[addr].is_global = true;
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<Sym> SymTable::find(std::string const& name) const
|
|
||||||
{
|
|
||||||
for (auto const& sym: m_syms)
|
|
||||||
{
|
|
||||||
if (sym.name == name)
|
|
||||||
{
|
|
||||||
return sym;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
#ifndef jk_SYMTABLE_HPP
|
|
||||||
#define jk_SYMTABLE_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Type.hpp"
|
|
||||||
#include "Logger.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
struct Sym {
|
|
||||||
std::string name;
|
|
||||||
std::shared_ptr<Type> type;
|
|
||||||
Loc loc;
|
|
||||||
bool is_global = false;
|
|
||||||
size_t addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
JK_ERROR(symbolic_error);
|
|
||||||
|
|
||||||
class SymTable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit SymTable(Logger& logger);
|
|
||||||
virtual ~SymTable();
|
|
||||||
|
|
||||||
size_t declare(std::string const& name,
|
|
||||||
std::shared_ptr<Type> type,
|
|
||||||
Loc const& loc);
|
|
||||||
|
|
||||||
size_t declare_global(std::string const& name,
|
|
||||||
std::shared_ptr<Type> type,
|
|
||||||
Loc const& loc);
|
|
||||||
|
|
||||||
std::optional<Sym> find(std::string const& name) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Logger& m_logger;
|
|
||||||
std::vector<Sym> m_syms;
|
|
||||||
size_t m_addr = 0;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
13
lib/Type.cpp
13
lib/Type.cpp
|
@ -1,13 +0,0 @@
|
||||||
#include "Type.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ Type::Type(TypeType type)
|
|
||||||
: m_type { type }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ Type::~Type()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
30
lib/Type.hpp
30
lib/Type.hpp
|
@ -1,30 +0,0 @@
|
||||||
#ifndef jk_TYPE_HPP
|
|
||||||
#define jk_TYPE_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
|
|
||||||
#define TYPE_TYPE(G) \
|
|
||||||
G(TYPE_NIL), \
|
|
||||||
G(TYPE_INT), \
|
|
||||||
G(TYPE_FUNCTION), \
|
|
||||||
G(TYPE_CODE), \
|
|
||||||
G(TYPE_REF)
|
|
||||||
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
JK_ENUM(Type, TYPE_TYPE);
|
|
||||||
|
|
||||||
class Type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Type(TypeType type);
|
|
||||||
virtual ~Type();
|
|
||||||
|
|
||||||
TypeType type() const { return m_type; }
|
|
||||||
private:
|
|
||||||
TypeType m_type;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
141
lib/VM.cpp
141
lib/VM.cpp
|
@ -1,141 +0,0 @@
|
||||||
#include "VM.hpp"
|
|
||||||
#include "Function.hpp"
|
|
||||||
#include "Code.hpp"
|
|
||||||
#include "lib/Opcodes.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*explicit*/ VM::VM()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*virtual*/ VM::~VM()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void VM::execute(std::shared_ptr<Program> program)
|
|
||||||
{
|
|
||||||
Frame frame;
|
|
||||||
frame.program = program;
|
|
||||||
m_frames.push_back(frame);
|
|
||||||
|
|
||||||
while (m_pc < m_frames.back().program->size())
|
|
||||||
{
|
|
||||||
Instr instr = m_frames.back().program->get(m_pc);
|
|
||||||
|
|
||||||
switch (instr.opcode)
|
|
||||||
{
|
|
||||||
case OPCODE_MK_FUNCTION: {
|
|
||||||
auto code = program->constant(pop());
|
|
||||||
|
|
||||||
auto function = std::make_shared<Function>
|
|
||||||
(code->as_code()->foreign());
|
|
||||||
|
|
||||||
auto value = Value::make_function(function);
|
|
||||||
|
|
||||||
size_t addr = push_heap(value);
|
|
||||||
auto ref = Value::make_ref(addr);
|
|
||||||
|
|
||||||
push(program->push_constant(ref));
|
|
||||||
|
|
||||||
m_pc++;
|
|
||||||
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case OPCODE_PUSH_CONST: {
|
|
||||||
push(*instr.param);
|
|
||||||
m_pc++;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case OPCODE_LOAD: {
|
|
||||||
auto value = m_frames.back().locals[*instr.param];
|
|
||||||
push(program->push_constant(value));
|
|
||||||
m_pc++;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case OPCODE_STORE: {
|
|
||||||
auto idx = pop();
|
|
||||||
|
|
||||||
m_frames.back().locals[*instr.param] =
|
|
||||||
program->constant(idx);
|
|
||||||
|
|
||||||
m_pc++;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case OPCODE_LOAD_GLOBAL: {
|
|
||||||
auto value = m_globals[*instr.param];
|
|
||||||
push(program->push_constant(value));
|
|
||||||
m_pc++;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case OPCODE_CALL: {
|
|
||||||
std::vector<std::shared_ptr<Value>> args;
|
|
||||||
|
|
||||||
for (size_t i=0; i<*instr.param; i++)
|
|
||||||
{
|
|
||||||
args.insert(std::begin(args), program->constant(pop()));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ref_val = program->constant(pop());
|
|
||||||
auto ref = ref_val->as_ref();
|
|
||||||
auto fun_val = heap(ref);
|
|
||||||
auto fun = fun_val->as_function();
|
|
||||||
|
|
||||||
auto result = fun->call(args);
|
|
||||||
|
|
||||||
push(program->push_constant(result));
|
|
||||||
|
|
||||||
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_stack.size(); i++)
|
|
||||||
{
|
|
||||||
ss << i << "\t" << m_stack[i] << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t VM::push_heap(std::shared_ptr<Value> value)
|
|
||||||
{
|
|
||||||
m_heap.push_back(value);
|
|
||||||
return m_heap.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Value> VM::heap(size_t addr)
|
|
||||||
{
|
|
||||||
assert(addr < m_heap.size());
|
|
||||||
return m_heap[addr];
|
|
||||||
}
|
|
||||||
|
|
||||||
void VM::set_global(size_t addr, std::shared_ptr<Value> value)
|
|
||||||
{
|
|
||||||
m_globals[addr] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VM::push(param_t param)
|
|
||||||
{
|
|
||||||
m_stack.push_back(param);
|
|
||||||
}
|
|
||||||
|
|
||||||
param_t VM::pop()
|
|
||||||
{
|
|
||||||
auto param = m_stack.back();
|
|
||||||
m_stack.pop_back();
|
|
||||||
return param;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
41
lib/VM.hpp
41
lib/VM.hpp
|
@ -1,41 +0,0 @@
|
||||||
#ifndef jk_VM_HPP
|
|
||||||
#define jk_VM_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
#include "Program.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
struct Frame {
|
|
||||||
std::shared_ptr<Program> program;
|
|
||||||
std::unordered_map<int, std::shared_ptr<Value>> locals;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VM
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit VM();
|
|
||||||
virtual ~VM();
|
|
||||||
|
|
||||||
void execute(std::shared_ptr<Program> program);
|
|
||||||
|
|
||||||
std::string string() const;
|
|
||||||
|
|
||||||
size_t push_heap(std::shared_ptr<Value> value);
|
|
||||||
std::shared_ptr<Value> heap(size_t addr);
|
|
||||||
|
|
||||||
void set_global(size_t addr, std::shared_ptr<Value> value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
size_t m_pc = 0;
|
|
||||||
std::vector<Frame> m_frames;
|
|
||||||
std::vector<param_t> m_stack;
|
|
||||||
std::vector<std::shared_ptr<Value>> m_heap;
|
|
||||||
std::unordered_map<size_t, std::shared_ptr<Value>> m_globals;
|
|
||||||
|
|
||||||
void push(param_t param);
|
|
||||||
param_t pop();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,80 +0,0 @@
|
||||||
#include "Value.hpp"
|
|
||||||
#include "Type.hpp"
|
|
||||||
#include "Function.hpp"
|
|
||||||
#include "Code.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
/*static*/ std::shared_ptr<Value> Value::make_nil()
|
|
||||||
{
|
|
||||||
auto value = std::make_shared<Value>();
|
|
||||||
value->m_type = std::make_shared<Type>(TYPE_NIL);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*static*/ std::shared_ptr<Value> Value::make_int(int val)
|
|
||||||
{
|
|
||||||
auto value = std::make_shared<Value>();
|
|
||||||
value->m_type = std::make_shared<Type>(TYPE_INT);
|
|
||||||
value->m_int_val = val;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*static*/ std::shared_ptr<Value>
|
|
||||||
Value::make_function(std::shared_ptr<Function> val)
|
|
||||||
{
|
|
||||||
auto value = std::make_shared<Value>();
|
|
||||||
value->m_type = std::make_shared<Type>(TYPE_FUNCTION);
|
|
||||||
value->m_function_val = val;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*static*/ std::shared_ptr<Value>
|
|
||||||
Value::make_code(std::shared_ptr<Code> val)
|
|
||||||
{
|
|
||||||
auto value = std::make_shared<Value>();
|
|
||||||
value->m_type = std::make_shared<Type>(TYPE_CODE);
|
|
||||||
value->m_code_val = val;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*static*/ std::shared_ptr<Value>
|
|
||||||
Value::make_ref(size_t val)
|
|
||||||
{
|
|
||||||
auto value = std::make_shared<Value>();
|
|
||||||
value->m_type = std::make_shared<Type>(TYPE_REF);
|
|
||||||
value->m_ref_val = val;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Function> Value::as_function() const
|
|
||||||
{
|
|
||||||
return m_function_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Code> Value::as_code() const
|
|
||||||
{
|
|
||||||
return m_code_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::weak_ptr<Type> Value::type() const
|
|
||||||
{
|
|
||||||
return m_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Value::string() const
|
|
||||||
{
|
|
||||||
switch (m_type->type())
|
|
||||||
{
|
|
||||||
case TYPE_NIL: return "<nil>";
|
|
||||||
case TYPE_INT: return std::to_string(*m_int_val);
|
|
||||||
|
|
||||||
default:
|
|
||||||
std::cerr << "cannot stringify value '"
|
|
||||||
<< TypeTypeStr[m_type->type()]
|
|
||||||
<< "'"
|
|
||||||
<< std::endl;
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
#ifndef jk_VALUE_HPP
|
|
||||||
#define jk_VALUE_HPP
|
|
||||||
|
|
||||||
#include "commons.hpp"
|
|
||||||
|
|
||||||
namespace jk
|
|
||||||
{
|
|
||||||
class Type;
|
|
||||||
class Function;
|
|
||||||
class Code;
|
|
||||||
|
|
||||||
class Value
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static std::shared_ptr<Value> make_nil();
|
|
||||||
static std::shared_ptr<Value> make_int(int val);
|
|
||||||
static std::shared_ptr<Value> make_function(std::shared_ptr<Function>
|
|
||||||
val);
|
|
||||||
static std::shared_ptr<Value> make_code(std::shared_ptr<Code> val);
|
|
||||||
static std::shared_ptr<Value> make_ref(size_t val);
|
|
||||||
|
|
||||||
explicit Value() = default;
|
|
||||||
virtual ~Value() = default;
|
|
||||||
|
|
||||||
int as_int() const { return *m_int_val; }
|
|
||||||
size_t as_ref() const { return *m_ref_val; }
|
|
||||||
|
|
||||||
std::shared_ptr<Function> as_function() const;
|
|
||||||
std::shared_ptr<Code> as_code() const;
|
|
||||||
|
|
||||||
std::weak_ptr<Type> type() const;
|
|
||||||
|
|
||||||
std::string string() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<Type> m_type;
|
|
||||||
std::optional<int> m_int_val;
|
|
||||||
std::shared_ptr<Function> m_function_val;
|
|
||||||
std::shared_ptr<Code> m_code_val;
|
|
||||||
std::optional<size_t> m_ref_val;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,19 +0,0 @@
|
||||||
#ifndef jk_COMMONS_HPP
|
|
||||||
#define jk_COMMONS_HPP
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <sstream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <filesystem>
|
|
||||||
|
|
||||||
#include "mutils.hpp"
|
|
||||||
#include "config.hpp"
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,9 +0,0 @@
|
||||||
#ifndef jk_CONFIG_IN_HPP
|
|
||||||
#define jk_CONFIG_IN_HPP
|
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
|
|
||||||
#define JOKO_VERSION "@version@"
|
|
||||||
#define JOKO_LIBDIR std::filesystem::path("@libdir@")
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,18 +0,0 @@
|
||||||
#ifndef jk_MUTILS_HPP
|
|
||||||
#define jk_MUTILS_HPP
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#define ENUM_ENUM(X) X
|
|
||||||
#define ENUM_STRING(X) #X
|
|
||||||
|
|
||||||
#define JK_ENUM(PREFIX, DECL) \
|
|
||||||
enum PREFIX ## Type { DECL(ENUM_ENUM) }; \
|
|
||||||
constexpr char const* PREFIX ## TypeStr [] { DECL(ENUM_STRING) }
|
|
||||||
|
|
||||||
#define JK_ERROR(NAME) \
|
|
||||||
struct NAME : public std::runtime_error { \
|
|
||||||
NAME (std::string const& what): std::runtime_error {what} {} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
77
meson.build
77
meson.build
|
@ -1,77 +0,0 @@
|
||||||
project('joko',
|
|
||||||
'cpp',
|
|
||||||
version: '0.0.0',
|
|
||||||
default_options: [
|
|
||||||
'warning_level=3',
|
|
||||||
'cpp_std=c++17'
|
|
||||||
])
|
|
||||||
|
|
||||||
joko_libdir = get_option('prefix') / get_option('libdir') / 'joko'
|
|
||||||
|
|
||||||
conf = configuration_data()
|
|
||||||
conf.set('version', meson.project_version())
|
|
||||||
conf.set('libdir', joko_libdir)
|
|
||||||
|
|
||||||
configure_file(
|
|
||||||
input: 'lib/config.in.hpp',
|
|
||||||
output: 'config.hpp',
|
|
||||||
configuration: conf
|
|
||||||
)
|
|
||||||
|
|
||||||
joko_lib = static_library(
|
|
||||||
'joko',
|
|
||||||
sources: [
|
|
||||||
'lib/Node.cpp',
|
|
||||||
'lib/Loc.cpp',
|
|
||||||
|
|
||||||
'lib/Factory.cpp',
|
|
||||||
|
|
||||||
'lib/Logger.cpp',
|
|
||||||
'lib/Lexer.cpp',
|
|
||||||
'lib/Parser.cpp',
|
|
||||||
|
|
||||||
'lib/Type.cpp',
|
|
||||||
'lib/Value.cpp',
|
|
||||||
'lib/Function.cpp',
|
|
||||||
'lib/Code.cpp',
|
|
||||||
'lib/SymTable.cpp',
|
|
||||||
'lib/StaticPass.cpp',
|
|
||||||
'lib/Compiler.cpp',
|
|
||||||
'lib/Program.cpp',
|
|
||||||
'lib/VM.cpp',
|
|
||||||
'lib/Loader.cpp',
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
])
|
|
||||||
|
|
||||||
joko_dep = declare_dependency(link_with: joko_lib)
|
|
||||||
|
|
||||||
|
|
||||||
joko_core_lib = shared_library('joko-core',
|
|
||||||
sources: [
|
|
||||||
'core/core.cpp'
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
joko_dep
|
|
||||||
],
|
|
||||||
install: true,
|
|
||||||
install_dir: joko_libdir)
|
|
||||||
executable('joko',
|
|
||||||
sources: [
|
|
||||||
'src/main.cpp',
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
joko_dep
|
|
||||||
],
|
|
||||||
install: true)
|
|
||||||
|
|
||||||
executable('joko-tests',
|
|
||||||
sources: [
|
|
||||||
'tests/main.cpp',
|
|
||||||
'tests/Lexer.cpp',
|
|
||||||
'tests/Parser.cpp',
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
joko_dep,
|
|
||||||
dependency('catch2')
|
|
||||||
])
|
|
116
src/main.cpp
116
src/main.cpp
|
@ -1,116 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include "config.hpp"
|
|
||||||
#include "../lib/Lexer.hpp"
|
|
||||||
#include "../lib/Parser.hpp"
|
|
||||||
#include "../lib/SymTable.hpp"
|
|
||||||
#include "../lib/StaticPass.hpp"
|
|
||||||
#include "../lib/Compiler.hpp"
|
|
||||||
#include "../lib/VM.hpp"
|
|
||||||
#include "lib/Value.hpp"
|
|
||||||
#include "../lib/Loader.hpp"
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
int index;
|
|
||||||
bool debug_mode = false;
|
|
||||||
|
|
||||||
struct option options[] = {
|
|
||||||
{"debug", no_argument, 0, 'd'},
|
|
||||||
{"help", no_argument, 0, 'h'},
|
|
||||||
{"version", no_argument, 0, 'v'},
|
|
||||||
{0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
int c = getopt_long(argc, argv, "dhv", options, &index);
|
|
||||||
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 'd': debug_mode = true; break;
|
|
||||||
|
|
||||||
case 'h': {
|
|
||||||
std::cout << "Usage: joko [OPTIONS] source_file" << std::endl;
|
|
||||||
std::cout << "OPTIONS" << std::endl;
|
|
||||||
std::cout << "\t" << "-h, --help"
|
|
||||||
<< "\t" << "show this message." << std::endl;
|
|
||||||
std::cout << "\t" << "-v, --version"
|
|
||||||
<< "\t" << "show joko version." << std::endl;
|
|
||||||
return 0;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 'v': {
|
|
||||||
std::cout << "--- Joko Programming Language ---" << std::endl;
|
|
||||||
std::cout << "[License]: " << "GPL v3 (see LICENSE)" << std::endl;
|
|
||||||
std::cout << "[Version]: " << JOKO_VERSION << std::endl;
|
|
||||||
std::cout << "[Lib directory]: " << JOKO_LIBDIR << std::endl;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optind < argc)
|
|
||||||
{
|
|
||||||
jk::Logger logger;
|
|
||||||
jk::Loc loc {argv[optind], 1, 1};
|
|
||||||
|
|
||||||
std::ifstream file { argv[optind] };
|
|
||||||
|
|
||||||
if (!file)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "cannot find file '" << argv[optind] << "'";
|
|
||||||
logger.log<std::runtime_error>(jk::LOG_ERROR, loc, ss.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string source;
|
|
||||||
std::string line;
|
|
||||||
|
|
||||||
while (std::getline(file, line))
|
|
||||||
{
|
|
||||||
source += line + (file.eof() ? "" : "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lexer = std::make_shared<jk::Lexer>(logger, loc);
|
|
||||||
auto parser = std::make_shared<jk::Parser>(logger, lexer);
|
|
||||||
auto ast = parser->parse(source);
|
|
||||||
|
|
||||||
if (debug_mode)
|
|
||||||
{
|
|
||||||
std::cout << "--- ast ---" << std::endl;
|
|
||||||
std::cout << ast->string() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto sym = std::make_shared<jk::SymTable>(logger);
|
|
||||||
|
|
||||||
auto static_pass = std::make_shared<jk::StaticPass>(sym, logger);
|
|
||||||
static_pass->pass(ast);
|
|
||||||
|
|
||||||
auto compiler = std::make_shared<jk::Compiler>(sym, logger);
|
|
||||||
auto program = std::make_shared<jk::Program>();
|
|
||||||
auto vm = std::make_shared<jk::VM>();
|
|
||||||
auto loader = std::make_shared<jk::Loader>(vm, sym);
|
|
||||||
loader->load();
|
|
||||||
|
|
||||||
compiler->compile(ast, program);
|
|
||||||
|
|
||||||
if (debug_mode)
|
|
||||||
{
|
|
||||||
std::cout << "--- program ---" << std::endl;
|
|
||||||
std::cout << program->string() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
vm->execute(program);
|
|
||||||
|
|
||||||
if (debug_mode)
|
|
||||||
{
|
|
||||||
std::cout << "--- stack ---" << std::endl;
|
|
||||||
std::cout << vm->string() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
#include <catch2/catch.hpp>
|
|
||||||
#include "../lib/Lexer.hpp"
|
|
||||||
#include "../lib/Factory.hpp"
|
|
||||||
|
|
||||||
class LexerTest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit LexerTest() {}
|
|
||||||
virtual ~LexerTest() {}
|
|
||||||
|
|
||||||
void test_next(jk::Lexer& lexer, std::string const& oracle)
|
|
||||||
{
|
|
||||||
auto token = lexer.next();
|
|
||||||
REQUIRE(token);
|
|
||||||
REQUIRE(oracle == token->string());
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_end(jk::Lexer& lexer)
|
|
||||||
{
|
|
||||||
auto token = lexer.next();
|
|
||||||
REQUIRE(!token);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
jk::Logger m_logger;
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(LexerTest, "Lexer_int")
|
|
||||||
{
|
|
||||||
auto lexer = jk::Factory(m_logger, "tests/lexer").make_lexer();
|
|
||||||
lexer->scan("4 128 333");
|
|
||||||
test_next(*lexer, "INT[4]");
|
|
||||||
test_next(*lexer, "INT[128]");
|
|
||||||
test_next(*lexer, "INT[333]");
|
|
||||||
test_end(*lexer);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(LexerTest, "Lexer_comments")
|
|
||||||
{
|
|
||||||
auto lexer = jk::Factory(m_logger, "tests/lexer").make_lexer();
|
|
||||||
lexer->scan("4 # 128 \n 333");
|
|
||||||
test_next(*lexer, "INT[4]");
|
|
||||||
test_next(*lexer, "INT[333]");
|
|
||||||
test_end(*lexer);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(LexerTest, "Lexer_error")
|
|
||||||
{
|
|
||||||
auto lexer = jk::Factory(m_logger, "tests/lexer").make_lexer();
|
|
||||||
lexer->scan(" § ");
|
|
||||||
REQUIRE_THROWS_AS(lexer->next(), jk::lexical_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(LexerTest, "Lexer_funcall")
|
|
||||||
{
|
|
||||||
auto lexer = jk::Factory(m_logger, "tests/lexer").make_lexer();
|
|
||||||
lexer->scan(" )( salut salut! salut? _salut -salut salut/monde");
|
|
||||||
test_next(*lexer, "CPAR");
|
|
||||||
test_next(*lexer, "OPAR");
|
|
||||||
test_next(*lexer, "IDENT[salut]");
|
|
||||||
test_next(*lexer, "IDENT[salut!]");
|
|
||||||
test_next(*lexer, "IDENT[salut?]");
|
|
||||||
test_next(*lexer, "IDENT[_salut]");
|
|
||||||
test_next(*lexer, "IDENT[-salut]");
|
|
||||||
test_next(*lexer, "IDENT[salut/monde]");
|
|
||||||
test_end(*lexer);
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
#include <catch2/catch.hpp>
|
|
||||||
#include "../lib/Parser.hpp"
|
|
||||||
#include "../lib/Factory.hpp"
|
|
||||||
|
|
||||||
class ParserTest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit ParserTest() {}
|
|
||||||
virtual ~ParserTest() {}
|
|
||||||
|
|
||||||
void test_parser(std::string const& oracle, std::string const& source)
|
|
||||||
{
|
|
||||||
auto parser = jk::Factory(m_logger, "tests/parser").make_parser();
|
|
||||||
auto root = parser->parse(source);
|
|
||||||
|
|
||||||
REQUIRE(oracle == root->string());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
jk::Logger m_logger;
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(ParserTest, "Parser_funcall")
|
|
||||||
{
|
|
||||||
test_parser("PROG",
|
|
||||||
" ");
|
|
||||||
|
|
||||||
test_parser("PROG(FUNCALL(IDENT[hello]))",
|
|
||||||
" (hello) ");
|
|
||||||
|
|
||||||
test_parser("PROG(FUNCALL(IDENT[hello],INT[1],INT[2]))",
|
|
||||||
" (hello 1 2) ");
|
|
||||||
|
|
||||||
test_parser("PROG(FUNCALL(IDENT[hello],INT[1],INT[2],IDENT[bim!]))",
|
|
||||||
" (hello 1 2 bim!) ");
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
#define CATCH_CONFIG_MAIN
|
|
||||||
#include <catch2/catch.hpp>
|
|
Reference in New Issue