Compare commits
3 Commits
62fd1956d8
...
55c094e4ec
Author | SHA1 | Date |
---|---|---|
bog | 55c094e4ec | |
bog | 8db23dbd3c | |
bog | 04f57c631e |
|
@ -0,0 +1,4 @@
|
||||||
|
*\#*
|
||||||
|
*~*
|
||||||
|
build
|
||||||
|
.cache
|
|
@ -0,0 +1,11 @@
|
||||||
|
.PHONY: build tests
|
||||||
|
|
||||||
|
build:
|
||||||
|
meson setup build
|
||||||
|
meson compile -C build
|
||||||
|
|
||||||
|
tests: build
|
||||||
|
build/zarn-tests
|
||||||
|
|
||||||
|
install: tests
|
||||||
|
meson install -C build
|
|
@ -0,0 +1,7 @@
|
||||||
|
MODULE ::= EXPR*
|
||||||
|
EXPR ::=
|
||||||
|
| int
|
||||||
|
| ident
|
||||||
|
| CALL
|
||||||
|
|
||||||
|
CALL ::= opar ident EXPR* cpar
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef COMMON_HPP
|
||||||
|
#define COMMON_HPP
|
||||||
|
|
||||||
|
#include "../src/Zarn.hpp"
|
||||||
|
using namespace zn;
|
||||||
|
|
||||||
|
#define STDARGS std::vector<std::shared_ptr<Constant>>
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,36 @@
|
||||||
|
#include "fun.hpp"
|
||||||
|
|
||||||
|
std::shared_ptr<Constant> println(STDARGS args)
|
||||||
|
{
|
||||||
|
std::string sep;
|
||||||
|
|
||||||
|
for (auto const& arg: args)
|
||||||
|
{
|
||||||
|
std::cout << sep << arg->string();
|
||||||
|
sep = " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
return std::make_shared<Constant>(TYPE_NIL, args[0]->loc());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Constant> assert_eq(STDARGS args)
|
||||||
|
{
|
||||||
|
bool equals = args[0]->equals(*args[1]);
|
||||||
|
auto loc = args[0]->loc();
|
||||||
|
|
||||||
|
if (!equals)
|
||||||
|
{
|
||||||
|
std::cerr << loc.file_path().string() << ":" << loc.line();
|
||||||
|
std::cerr << " ASSERTION FAILED: ";
|
||||||
|
|
||||||
|
std::cerr << "expected '" << args[0]->string() << "', "
|
||||||
|
<< "got '" << args[1]->string() << "'."
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_shared<Constant>(TYPE_NIL, loc, 0);
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef FUN_HPP
|
||||||
|
#define FUN_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
std::shared_ptr<Constant> println(STDARGS args);
|
||||||
|
std::shared_ptr<Constant> assert_eq(STDARGS args);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,19 @@
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "fun.hpp"
|
||||||
|
|
||||||
|
extern "C" void lib(Zarn& zarn)
|
||||||
|
{
|
||||||
|
zarn.register_function("println",
|
||||||
|
std::make_shared<Prototype>()
|
||||||
|
->param_any()
|
||||||
|
->param_variadic()
|
||||||
|
->ret(TYPE_NIL),
|
||||||
|
println);
|
||||||
|
|
||||||
|
zarn.register_function("assert=",
|
||||||
|
std::make_shared<Prototype>()
|
||||||
|
->param_any()
|
||||||
|
->param_any()
|
||||||
|
->ret(TYPE_NIL),
|
||||||
|
assert_eq);
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
project('zarn',
|
||||||
|
'cpp',
|
||||||
|
version: '0.0.0',
|
||||||
|
default_options: [
|
||||||
|
'prefix=/usr',
|
||||||
|
'warning_level=3',
|
||||||
|
'cpp_std=c++17'
|
||||||
|
])
|
||||||
|
|
||||||
|
# CONFIGURATIONS
|
||||||
|
# ==============
|
||||||
|
extra_libdir = get_option('prefix') / get_option('libdir') / 'zarn'
|
||||||
|
|
||||||
|
conf = configuration_data()
|
||||||
|
conf.set('version', meson.project_version())
|
||||||
|
conf.set('libdir', extra_libdir)
|
||||||
|
|
||||||
|
configure_file(input: 'src/config.in.hpp',
|
||||||
|
output: 'config.hpp',
|
||||||
|
configuration: conf)
|
||||||
|
|
||||||
|
# ZARN_LIB
|
||||||
|
# ========
|
||||||
|
zarn_lib = shared_library('zarn',
|
||||||
|
sources: [
|
||||||
|
'src/Module.cpp',
|
||||||
|
'src/Loc.cpp',
|
||||||
|
'src/Node.cpp',
|
||||||
|
'src/Logger.cpp',
|
||||||
|
'src/Lexer.cpp',
|
||||||
|
'src/Parser.cpp',
|
||||||
|
'src/StaticPass.cpp',
|
||||||
|
'src/Compiler.cpp',
|
||||||
|
'src/Program.cpp',
|
||||||
|
'src/VM.cpp',
|
||||||
|
'src/Constant.cpp',
|
||||||
|
'src/SymTable.cpp',
|
||||||
|
'src/NativeFunction.cpp',
|
||||||
|
'src/Prototype.cpp',
|
||||||
|
'src/Zarn.cpp',
|
||||||
|
],
|
||||||
|
install: true)
|
||||||
|
|
||||||
|
zarn_dep = declare_dependency(link_with: zarn_lib)
|
||||||
|
|
||||||
|
# STANDARD LIBRARY
|
||||||
|
# ================
|
||||||
|
shared_library('zarn-std',
|
||||||
|
sources: [
|
||||||
|
'libstd/std.cpp',
|
||||||
|
'libstd/fun.cpp',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
zarn_dep
|
||||||
|
],
|
||||||
|
install_dir: extra_libdir,
|
||||||
|
install: true)
|
||||||
|
|
||||||
|
# COMPILER
|
||||||
|
# ========
|
||||||
|
executable('zarn',
|
||||||
|
sources: [
|
||||||
|
'src/main.cpp'
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
zarn_dep
|
||||||
|
],
|
||||||
|
install: true)
|
||||||
|
|
||||||
|
# TESTS
|
||||||
|
# =====
|
||||||
|
executable('zarn-tests',
|
||||||
|
sources: [
|
||||||
|
'tests/main.cpp',
|
||||||
|
'tests/Lexer.cpp',
|
||||||
|
'tests/Parser.cpp',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
zarn_dep,
|
||||||
|
dependency('catch2')
|
||||||
|
])
|
|
@ -0,0 +1,79 @@
|
||||||
|
#include "Compiler.hpp"
|
||||||
|
#include "src/Node.hpp"
|
||||||
|
#include "src/Program.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ Compiler::Compiler(Logger& logger, SymTable& sym)
|
||||||
|
: m_logger { logger }
|
||||||
|
, m_sym { sym }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Compiler::~Compiler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::compile(Node const& node, Program& program)
|
||||||
|
{
|
||||||
|
switch (node.type())
|
||||||
|
{
|
||||||
|
// MODULES
|
||||||
|
// =======
|
||||||
|
case NODE_MODULE: {
|
||||||
|
for (size_t i=0; i<node.size(); i++)
|
||||||
|
{
|
||||||
|
compile(*node.child_at(i), program);
|
||||||
|
program.append(OPCODE_POP);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// FUNCTION STUFF
|
||||||
|
// ==============
|
||||||
|
case NODE_CALL: {
|
||||||
|
for (size_t i=0; i<node.size(); i++)
|
||||||
|
{
|
||||||
|
compile(*node.child_at(i), program);
|
||||||
|
}
|
||||||
|
|
||||||
|
program.append(OPCODE_CALL_NATIVE, node.size() - 1);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// VALUES
|
||||||
|
// ======
|
||||||
|
case NODE_IDENT: {
|
||||||
|
std::string ident = node.repr();
|
||||||
|
auto sym = m_sym.find(ident);
|
||||||
|
assert(sym);
|
||||||
|
|
||||||
|
if (sym->prototype)
|
||||||
|
{
|
||||||
|
auto ref = std::make_shared<Constant>(TYPE_REF,
|
||||||
|
node.loc(),
|
||||||
|
(int) sym->addr);
|
||||||
|
size_t val = program.add_constant(ref);
|
||||||
|
program.append(OPCODE_LOAD_CONST, val);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
program.append(OPCODE_LOAD_LOCAL, sym->addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_INT: {
|
||||||
|
size_t addr = program
|
||||||
|
.add_constant(std::make_shared<Constant>
|
||||||
|
(TYPE_INT, node.loc(),
|
||||||
|
std::stoi(node.repr())));
|
||||||
|
program.append(OPCODE_LOAD_CONST, addr);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
std::cerr << "cannot compile node '"
|
||||||
|
<< node.string() << "'" << std::endl;
|
||||||
|
abort();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef zn_COMPILER_HPP
|
||||||
|
#define zn_COMPILER_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "Logger.hpp"
|
||||||
|
#include "Node.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
ZN_ERROR(compile_error);
|
||||||
|
|
||||||
|
class Compiler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Compiler(Logger& logger, SymTable& sym);
|
||||||
|
virtual ~Compiler();
|
||||||
|
|
||||||
|
void compile(Node const& node, Program& program);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Logger& m_logger;
|
||||||
|
SymTable& m_sym;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,42 @@
|
||||||
|
#include "Constant.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ Constant::Constant(Type type,
|
||||||
|
Loc const& loc,
|
||||||
|
constant_t value)
|
||||||
|
: m_type { type }
|
||||||
|
, m_loc { loc }
|
||||||
|
, m_value { value }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Constant::~Constant()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Constant::equals(Constant const& rhs) const
|
||||||
|
{
|
||||||
|
if (m_type != rhs.m_type)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_value == rhs.m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Constant::string() const
|
||||||
|
{
|
||||||
|
switch (m_type)
|
||||||
|
{
|
||||||
|
case TYPE_NIL: return "<nil>";
|
||||||
|
case TYPE_INT: return std::to_string(std::get<int>(*m_value));
|
||||||
|
default: {
|
||||||
|
std::cerr << "cannot stringify "
|
||||||
|
<< (TypeStr[m_type] + strlen("TYPE_"))
|
||||||
|
<< std::endl;
|
||||||
|
abort();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef zn_CONSTANT_HPP
|
||||||
|
#define zn_CONSTANT_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
#include "Loc.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
using constant_t = std::optional<std::variant<int>>;
|
||||||
|
|
||||||
|
class Constant
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Constant(Type type,
|
||||||
|
Loc const& loc,
|
||||||
|
constant_t value=std::nullopt);
|
||||||
|
virtual ~Constant();
|
||||||
|
|
||||||
|
Loc loc() const { return m_loc; }
|
||||||
|
constant_t value() const { return m_value; }
|
||||||
|
Type type() const { return m_type; }
|
||||||
|
|
||||||
|
bool equals(Constant const& rhs) const;
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type m_type;
|
||||||
|
Loc m_loc;
|
||||||
|
constant_t m_value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,214 @@
|
||||||
|
#include "Lexer.hpp"
|
||||||
|
#include "src/Node.hpp"
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ Lexer::Lexer(Logger& logger, Loc const& loc)
|
||||||
|
: m_logger { logger }
|
||||||
|
, m_loc { loc }
|
||||||
|
{
|
||||||
|
add_text("(", NODE_OPAR);
|
||||||
|
add_text(")", NODE_CPAR);
|
||||||
|
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_int, this));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Lexer::~Lexer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lexer::scan(std::string const& source)
|
||||||
|
{
|
||||||
|
m_source = source;
|
||||||
|
m_cursor = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Lexer::try_next()
|
||||||
|
{
|
||||||
|
std::optional<ScanInfo> info;
|
||||||
|
|
||||||
|
skip_spaces();
|
||||||
|
|
||||||
|
while (m_cursor < m_source.size()
|
||||||
|
&& m_source[m_cursor] == ';')
|
||||||
|
{
|
||||||
|
while (m_cursor < m_source.size()
|
||||||
|
&& m_source[m_cursor] != '\n')
|
||||||
|
{
|
||||||
|
m_cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_spaces();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& scanner: m_scanners)
|
||||||
|
{
|
||||||
|
auto myinfo = scanner();
|
||||||
|
|
||||||
|
if (myinfo &&
|
||||||
|
(info == std::nullopt
|
||||||
|
|| info->cursor < myinfo->cursor))
|
||||||
|
{
|
||||||
|
info = myinfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == std::nullopt
|
||||||
|
&& m_cursor < m_source.size())
|
||||||
|
{
|
||||||
|
std::string tok;
|
||||||
|
|
||||||
|
while (m_cursor < m_source.size()
|
||||||
|
&& !std::isspace(m_source[m_cursor]))
|
||||||
|
{
|
||||||
|
tok += m_source[m_cursor];
|
||||||
|
m_cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_logger.log<lex_error>(LOG_ERROR, m_loc,
|
||||||
|
"unexpected token '" + tok + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info)
|
||||||
|
{
|
||||||
|
m_cursor = info->cursor;
|
||||||
|
return std::make_shared<Node>(info->type, info->repr, m_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Node>> Lexer::all()
|
||||||
|
{
|
||||||
|
std::vector<std::shared_ptr<Node>> result;
|
||||||
|
std::shared_ptr<Node> n;
|
||||||
|
|
||||||
|
while ( (n = try_next()) )
|
||||||
|
{
|
||||||
|
result.push_back(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lexer::is_sep(size_t index) const
|
||||||
|
{
|
||||||
|
if (index >= m_source.size()
|
||||||
|
|| std::isspace(m_source[index]))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_separators.find(m_source[index]) != std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lexer::add_text(std::string const& text,
|
||||||
|
NodeType type,
|
||||||
|
bool has_value)
|
||||||
|
{
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_text,
|
||||||
|
this,
|
||||||
|
text,
|
||||||
|
type,
|
||||||
|
has_value));
|
||||||
|
if (text.size() == 1)
|
||||||
|
{
|
||||||
|
m_separators += text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lexer::skip_spaces()
|
||||||
|
{
|
||||||
|
while (m_cursor < m_source.size()
|
||||||
|
&& std::isspace(m_source[m_cursor]))
|
||||||
|
{
|
||||||
|
if (m_source[m_cursor] == '\n')
|
||||||
|
{
|
||||||
|
m_loc = Loc {m_loc.file_path(), m_loc.line() + 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cursor++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ScanInfo> Lexer::scan_int()
|
||||||
|
{
|
||||||
|
size_t cursor = m_cursor;
|
||||||
|
std::string repr;
|
||||||
|
|
||||||
|
if (cursor < m_source.size()
|
||||||
|
&& m_source[cursor] == '-')
|
||||||
|
{
|
||||||
|
repr += '-';
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (cursor < m_source.size()
|
||||||
|
&& std::isdigit(m_source[cursor]))
|
||||||
|
{
|
||||||
|
repr += m_source[cursor];
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repr.empty() || repr.back() == '-')
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScanInfo {
|
||||||
|
cursor,
|
||||||
|
NODE_INT,
|
||||||
|
repr
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ScanInfo> Lexer::scan_text(std::string const& text,
|
||||||
|
NodeType type,
|
||||||
|
bool has_value)
|
||||||
|
{
|
||||||
|
if (m_cursor + text.size() > m_source.size())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i<text.size(); i++)
|
||||||
|
{
|
||||||
|
if (text.at(i) != m_source.at(m_cursor + i))
|
||||||
|
{
|
||||||
|
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 (cursor < m_source.size()
|
||||||
|
&& !is_sep(cursor))
|
||||||
|
{
|
||||||
|
repr += m_source[cursor];
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repr.empty())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScanInfo {
|
||||||
|
cursor,
|
||||||
|
NODE_IDENT,
|
||||||
|
repr
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
#ifndef zn_LEXER_HPP
|
||||||
|
#define zn_LEXER_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
#include "Logger.hpp"
|
||||||
|
#include "Node.hpp"
|
||||||
|
#include "Loc.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
ZN_ERROR(lex_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();
|
||||||
|
|
||||||
|
void scan(std::string const& source);
|
||||||
|
std::shared_ptr<Node> try_next();
|
||||||
|
std::vector<std::shared_ptr<Node>> all();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Logger& m_logger;
|
||||||
|
std::string m_source;
|
||||||
|
size_t m_cursor;
|
||||||
|
std::vector<scanner_t> m_scanners;
|
||||||
|
Loc m_loc;
|
||||||
|
std::string m_separators;
|
||||||
|
|
||||||
|
bool is_sep(size_t index) const;
|
||||||
|
|
||||||
|
void add_text(std::string const& text,
|
||||||
|
NodeType type,
|
||||||
|
bool has_value=false);
|
||||||
|
|
||||||
|
void skip_spaces();
|
||||||
|
std::optional<ScanInfo> scan_int();
|
||||||
|
std::optional<ScanInfo> scan_text(std::string const& text,
|
||||||
|
NodeType type,
|
||||||
|
bool has_value);
|
||||||
|
std::optional<ScanInfo> scan_ident();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include "Loc.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ Loc::Loc(std::filesystem::path file_path, int line)
|
||||||
|
: m_file_path { file_path }
|
||||||
|
, m_line { line }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Loc::~Loc()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef zn_LOC_HPP
|
||||||
|
#define zn_LOC_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
class Loc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Loc(std::filesystem::path file_path, int line=1);
|
||||||
|
virtual ~Loc();
|
||||||
|
|
||||||
|
std::filesystem::path file_path() const { return m_file_path; }
|
||||||
|
int line() const { return m_line; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::filesystem::path m_file_path;
|
||||||
|
int m_line;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include "Logger.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ Logger::Logger()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Logger::~Logger()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef zn_LOGGER_HPP
|
||||||
|
#define zn_LOGGER_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
#include "Loc.hpp"
|
||||||
|
|
||||||
|
#define LOG_CATEGORIES(G) \
|
||||||
|
G(LOG_ERROR), \
|
||||||
|
G(LOG_WARNING)
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
ZN_MK_ENUM(LogCat, LOG_CATEGORIES);
|
||||||
|
|
||||||
|
class Logger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Logger();
|
||||||
|
virtual ~Logger();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void log(LogCat category,
|
||||||
|
Loc const& loc,
|
||||||
|
std::string const& what);
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Logger::log(LogCat category,
|
||||||
|
Loc const& loc,
|
||||||
|
std::string const& what)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << loc.file_path().string() << ":" << loc.line();
|
||||||
|
ss << " " << (LogCatStr[category] + strlen("LOG_"));
|
||||||
|
ss << " " << what;
|
||||||
|
|
||||||
|
throw T {ss.str()};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,67 @@
|
||||||
|
#include "Module.hpp"
|
||||||
|
#include "Lexer.hpp"
|
||||||
|
#include "Parser.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
#include "Compiler.hpp"
|
||||||
|
#include "StaticPass.hpp"
|
||||||
|
#include "VM.hpp"
|
||||||
|
#include "NativeFunction.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ Module::Module(Logger& logger)
|
||||||
|
: m_logger { logger }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Module::~Module()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::load_from_file(std::filesystem::path file_path)
|
||||||
|
{
|
||||||
|
std::string line;
|
||||||
|
std::ifstream file { file_path };
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
m_logger.log<module_error>(LOG_ERROR,
|
||||||
|
Loc {file_path},
|
||||||
|
"cannot load module '"
|
||||||
|
+ file_path.string() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_source = "";
|
||||||
|
|
||||||
|
while (std::getline(file, line))
|
||||||
|
{
|
||||||
|
m_source += line + (file.eof() ? "":"\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer lexer { m_logger, Loc {file_path} };
|
||||||
|
lexer.scan(m_source);
|
||||||
|
|
||||||
|
Parser parser { m_logger };
|
||||||
|
|
||||||
|
auto ast = parser.parse(lexer.all());
|
||||||
|
|
||||||
|
m_zarn.load_std_library();
|
||||||
|
|
||||||
|
StaticPass static_pass { m_logger, m_sym };
|
||||||
|
static_pass.execute(*ast);
|
||||||
|
|
||||||
|
Compiler compiler { m_logger, m_sym };
|
||||||
|
compiler.compile(*ast, m_program);
|
||||||
|
|
||||||
|
m_vm.execute(m_program);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Module::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << m_program.string() << "\n";
|
||||||
|
ss << m_vm.string() << "\n";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef zn_MODULE_HPP
|
||||||
|
#define zn_MODULE_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "Logger.hpp"
|
||||||
|
#include "VM.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
#include "Zarn.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
ZN_ERROR(module_error);
|
||||||
|
|
||||||
|
class Module
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Module(Logger& logger);
|
||||||
|
virtual ~Module();
|
||||||
|
|
||||||
|
void load_from_file(std::filesystem::path file_path);
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
private:
|
||||||
|
Logger& m_logger;
|
||||||
|
std::string m_source;
|
||||||
|
Program m_program;
|
||||||
|
VM m_vm { m_program };
|
||||||
|
SymTable m_sym;
|
||||||
|
Zarn m_zarn {m_sym, m_vm};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include "NativeFunction.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ NativeFunction::NativeFunction(std::shared_ptr<Prototype>
|
||||||
|
prototype,
|
||||||
|
native_fn_t body)
|
||||||
|
: m_prototype { prototype }
|
||||||
|
, m_body { body }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ NativeFunction::~NativeFunction()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Constant>
|
||||||
|
NativeFunction::call(std::vector<std::shared_ptr<Constant>> const& args)
|
||||||
|
{
|
||||||
|
assert(m_body);
|
||||||
|
return m_body(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef zn_NATIVEFUNCTION_HPP
|
||||||
|
#define zn_NATIVEFUNCTION_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "Prototype.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
class Constant;
|
||||||
|
using const_t = std::shared_ptr<Constant>;
|
||||||
|
using native_fn_t = std::function<const_t(std::vector<const_t>)>;
|
||||||
|
class NativeFunction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit NativeFunction(std::shared_ptr<Prototype> prototype,
|
||||||
|
native_fn_t body);
|
||||||
|
virtual ~NativeFunction();
|
||||||
|
|
||||||
|
std::shared_ptr<Prototype> prototype() const { return m_prototype; }
|
||||||
|
|
||||||
|
std::shared_ptr<Constant>
|
||||||
|
call(std::vector<std::shared_ptr<Constant>> const& args);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Prototype> m_prototype;
|
||||||
|
native_fn_t m_body;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include "Node.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*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)
|
||||||
|
{
|
||||||
|
assert(child);
|
||||||
|
m_children.push_back(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Node::child_at(size_t index) const
|
||||||
|
{
|
||||||
|
assert(index < size());
|
||||||
|
return m_children.at(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Node::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
ss << (NodeTypeStr[m_type] + strlen("NODE_"));
|
||||||
|
|
||||||
|
if (!m_repr.empty())
|
||||||
|
{
|
||||||
|
ss << "[" << m_repr << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size() > 0)
|
||||||
|
{
|
||||||
|
ss << "(";
|
||||||
|
std::string sep;
|
||||||
|
|
||||||
|
for (auto const& child: m_children)
|
||||||
|
{
|
||||||
|
ss << sep << child->string();
|
||||||
|
sep = ",";
|
||||||
|
}
|
||||||
|
ss << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef zn_NODE_HPP
|
||||||
|
#define zn_NODE_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "Loc.hpp"
|
||||||
|
|
||||||
|
#define NODE_TYPES(G) \
|
||||||
|
G(NODE_MODULE), \
|
||||||
|
G(NODE_INT), \
|
||||||
|
G(NODE_OPAR), \
|
||||||
|
G(NODE_CPAR), \
|
||||||
|
G(NODE_IDENT), \
|
||||||
|
G(NODE_CALL),
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
ZN_MK_ENUM(NodeType, NODE_TYPES);
|
||||||
|
|
||||||
|
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> child);
|
||||||
|
std::shared_ptr<Node> child_at(size_t index) const;
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NodeType m_type;
|
||||||
|
std::string m_repr;
|
||||||
|
Loc m_loc;
|
||||||
|
std::vector<std::shared_ptr<Node>> m_children;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,134 @@
|
||||||
|
#include "Parser.hpp"
|
||||||
|
#include "src/Logger.hpp"
|
||||||
|
#include "src/Node.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ Parser::Parser(Logger& logger)
|
||||||
|
: m_logger { logger }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Parser::~Parser()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node>
|
||||||
|
Parser::parse(std::vector<std::shared_ptr<Node>> tokens)
|
||||||
|
{
|
||||||
|
m_tokens = tokens;
|
||||||
|
m_cursor = 0;
|
||||||
|
|
||||||
|
return parse_module();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::mk_node(NodeType type)
|
||||||
|
{
|
||||||
|
return std::make_shared<Node>(type, "", m_tokens[m_cursor]->loc());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Parser::type_is(std::vector<NodeType> types) const
|
||||||
|
{
|
||||||
|
if (types.size() + m_cursor > m_tokens.size())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i<types.size(); i++)
|
||||||
|
{
|
||||||
|
if (types[i] != m_tokens[m_cursor + i]->type())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Parser::type_is(NodeType type) const
|
||||||
|
{
|
||||||
|
return type_is(std::vector<NodeType>{type});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::consume(NodeType type)
|
||||||
|
{
|
||||||
|
if (!type_is(type))
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "expected '"
|
||||||
|
<< (NodeTypeStr[type] + strlen("NODE_"))
|
||||||
|
<< "', got '"
|
||||||
|
<< (NodeTypeStr[m_tokens[m_cursor]->type()] + strlen("NODE_"))
|
||||||
|
<< "'";
|
||||||
|
|
||||||
|
m_logger.log<syntax_error>(LOG_ERROR,
|
||||||
|
m_tokens[m_cursor]->loc(),
|
||||||
|
ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::consume()
|
||||||
|
{
|
||||||
|
m_cursor++;
|
||||||
|
return m_tokens[m_cursor - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_module()
|
||||||
|
{
|
||||||
|
if (m_tokens.empty())
|
||||||
|
{
|
||||||
|
return std::make_shared<Node>(NODE_MODULE, "", Loc {""});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto node = mk_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_INT)
|
||||||
|
|| type_is(NODE_IDENT))
|
||||||
|
{
|
||||||
|
return consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_is(NODE_OPAR))
|
||||||
|
{
|
||||||
|
return parse_call();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "unknown expression '"
|
||||||
|
<< m_tokens[m_cursor]->string()
|
||||||
|
<< "'";
|
||||||
|
|
||||||
|
m_logger.log<syntax_error>(LOG_ERROR,
|
||||||
|
m_tokens[m_cursor]->loc(),
|
||||||
|
ss.str());
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_call()
|
||||||
|
{
|
||||||
|
consume(NODE_OPAR);
|
||||||
|
auto node = mk_node(NODE_CALL);
|
||||||
|
node->add_child(consume(NODE_IDENT));
|
||||||
|
|
||||||
|
while (!type_is(NODE_CPAR))
|
||||||
|
{
|
||||||
|
node->add_child(parse_expr());
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(NODE_CPAR);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef zn_PARSER_HPP
|
||||||
|
#define zn_PARSER_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "Logger.hpp"
|
||||||
|
#include "Node.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
ZN_ERROR(syntax_error);
|
||||||
|
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Parser(Logger& logger);
|
||||||
|
virtual ~Parser();
|
||||||
|
|
||||||
|
std::shared_ptr<Node>
|
||||||
|
parse(std::vector<std::shared_ptr<Node>> tokens);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Logger& m_logger;
|
||||||
|
std::vector<std::shared_ptr<Node>> m_tokens;
|
||||||
|
size_t m_cursor;
|
||||||
|
|
||||||
|
std::shared_ptr<Node> mk_node(NodeType type);
|
||||||
|
bool type_is(std::vector<NodeType> types) const;
|
||||||
|
bool type_is(NodeType type) const;
|
||||||
|
std::shared_ptr<Node> consume(NodeType type);
|
||||||
|
std::shared_ptr<Node> consume();
|
||||||
|
|
||||||
|
std::shared_ptr<Node> parse_module();
|
||||||
|
std::shared_ptr<Node> parse_expr();
|
||||||
|
std::shared_ptr<Node> parse_call();
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include "Program.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ Program::Program()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Program::~Program()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Program::append(Opcode opcode, int param)
|
||||||
|
{
|
||||||
|
m_instrs.push_back(Instr {opcode, param});
|
||||||
|
}
|
||||||
|
|
||||||
|
Instr Program::instr(size_t index) const
|
||||||
|
{
|
||||||
|
assert(index < size());
|
||||||
|
return m_instrs[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Program::add_constant(std::shared_ptr<Constant> constant)
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<const_size(); i++)
|
||||||
|
{
|
||||||
|
if (m_constant_pool[i]->equals(*constant))
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_constant_pool.push_back(constant);
|
||||||
|
return m_constant_pool.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Constant> Program::constant(size_t index) const
|
||||||
|
{
|
||||||
|
assert(index < const_size());
|
||||||
|
return m_constant_pool[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Program::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
ss << "======== Instructions ========\n";
|
||||||
|
for (size_t i=0; i<size(); i++)
|
||||||
|
{
|
||||||
|
ss << i
|
||||||
|
<< "\t"
|
||||||
|
<< (OpcodeStr[m_instrs[i].opcode] + strlen("OPCODE_"))
|
||||||
|
<< "\t" << (m_instrs[i].param == NO_PARAM ? ""
|
||||||
|
: std::to_string(m_instrs[i].param))
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << "======== Constant Pool ========\n";
|
||||||
|
|
||||||
|
for (size_t i=0; i<const_size(); i++)
|
||||||
|
{
|
||||||
|
ss << i << "\t" << m_constant_pool[i]->string() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef zn_PROGRAM_HPP
|
||||||
|
#define zn_PROGRAM_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "Constant.hpp"
|
||||||
|
|
||||||
|
#define NO_PARAM (-1)
|
||||||
|
|
||||||
|
#define OPCODES(G) \
|
||||||
|
G(OPCODE_LOAD_CONST), \
|
||||||
|
G(OPCODE_LOAD_GLOBAL), \
|
||||||
|
G(OPCODE_STORE_GLOBAL), \
|
||||||
|
G(OPCODE_LOAD_LOCAL), \
|
||||||
|
G(OPCODE_STORE_LOCAL), \
|
||||||
|
G(OPCODE_POP), \
|
||||||
|
G(OPCODE_CALL_NATIVE), \
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
ZN_MK_ENUM(Opcode, OPCODES);
|
||||||
|
|
||||||
|
struct Instr {
|
||||||
|
Opcode opcode;
|
||||||
|
int param;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Program();
|
||||||
|
virtual ~Program();
|
||||||
|
|
||||||
|
size_t size() const { return m_instrs.size(); }
|
||||||
|
size_t const_size() const { return m_constant_pool.size(); }
|
||||||
|
|
||||||
|
void append(Opcode opcode, int param=NO_PARAM);
|
||||||
|
Instr instr(size_t index) const;
|
||||||
|
|
||||||
|
size_t add_constant(std::shared_ptr<Constant> constant);
|
||||||
|
std::shared_ptr<Constant> constant(size_t index) const;
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Instr> m_instrs;
|
||||||
|
std::vector<std::shared_ptr<Constant>> m_constant_pool;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,105 @@
|
||||||
|
#include "Prototype.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ Prototype::Prototype()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Prototype::~Prototype()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeSlot Prototype::get_param(size_t index) const
|
||||||
|
{
|
||||||
|
assert(index < m_params.size());
|
||||||
|
return m_params[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Prototype> Prototype::param(TypeSlot param)
|
||||||
|
{
|
||||||
|
m_params.push_back(param);
|
||||||
|
return shared_from_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Prototype> Prototype::param(Type param)
|
||||||
|
{
|
||||||
|
TypeSlot slot;
|
||||||
|
slot.type = param;
|
||||||
|
m_params.push_back(slot);
|
||||||
|
return shared_from_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Prototype> Prototype::param_any()
|
||||||
|
{
|
||||||
|
TypeSlot slot;
|
||||||
|
slot.type = std::nullopt;
|
||||||
|
slot.tag = TAG_ANY;
|
||||||
|
m_params.push_back(slot);
|
||||||
|
return shared_from_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Prototype> Prototype::param_variadic()
|
||||||
|
{
|
||||||
|
TypeSlot slot;
|
||||||
|
slot.type = std::nullopt;
|
||||||
|
slot.tag = TAG_VARIADIC;
|
||||||
|
m_params.push_back(slot);
|
||||||
|
return shared_from_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Prototype> Prototype::ret(TypeSlot ret)
|
||||||
|
{
|
||||||
|
m_return = ret;
|
||||||
|
return shared_from_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Prototype> Prototype::ret(Type ret)
|
||||||
|
{
|
||||||
|
TypeSlot slot;
|
||||||
|
slot.type = ret;
|
||||||
|
m_return = slot;
|
||||||
|
return shared_from_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Prototype> Prototype::ret_any()
|
||||||
|
{
|
||||||
|
TypeSlot slot;
|
||||||
|
slot.type = std::nullopt;
|
||||||
|
slot.tag = TAG_ANY;
|
||||||
|
m_return = slot;
|
||||||
|
return shared_from_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Prototype::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
std::string sep;
|
||||||
|
|
||||||
|
for (auto param: m_params)
|
||||||
|
{
|
||||||
|
if (param.tag == TAG_ANY)
|
||||||
|
{
|
||||||
|
ss << sep << "ANY";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ss << sep << (TypeStr[*param.type] + strlen("TYPE_"));
|
||||||
|
}
|
||||||
|
|
||||||
|
sep = ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_return.tag == TAG_ANY)
|
||||||
|
{
|
||||||
|
ss << " -> " << "ANY";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ss << " -> " << (TypeStr[*m_return.type] + strlen("TYPE_"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef zn_PROTOTYPE_HPP
|
||||||
|
#define zn_PROTOTYPE_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#define TYPE_TAGS(G) \
|
||||||
|
G(TAG_NONE), \
|
||||||
|
G(TAG_ANY), \
|
||||||
|
G(TAG_VARIADIC)
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
ZN_MK_ENUM(Tag, TYPE_TAGS);
|
||||||
|
|
||||||
|
struct TypeSlot {
|
||||||
|
std::optional<Type> type = TYPE_NIL;
|
||||||
|
Tag tag = TAG_NONE;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Prototype: public std::enable_shared_from_this<Prototype>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Prototype();
|
||||||
|
virtual ~Prototype();
|
||||||
|
|
||||||
|
TypeSlot get_ret() const { return m_return; }
|
||||||
|
size_t get_param_count() const { return m_params.size(); }
|
||||||
|
|
||||||
|
TypeSlot get_param(size_t index) const;
|
||||||
|
|
||||||
|
std::shared_ptr<Prototype> param(TypeSlot param);
|
||||||
|
std::shared_ptr<Prototype> param(Type param);
|
||||||
|
std::shared_ptr<Prototype> param_any();
|
||||||
|
std::shared_ptr<Prototype> param_variadic();
|
||||||
|
|
||||||
|
std::shared_ptr<Prototype> ret(TypeSlot ret);
|
||||||
|
std::shared_ptr<Prototype> ret(Type ret);
|
||||||
|
std::shared_ptr<Prototype> ret_any();
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<TypeSlot> m_params;
|
||||||
|
TypeSlot m_return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,114 @@
|
||||||
|
#include "StaticPass.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ StaticPass::StaticPass(Logger& logger, SymTable& sym)
|
||||||
|
: m_logger { logger }
|
||||||
|
, m_sym { sym }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ StaticPass::~StaticPass()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeSlot StaticPass::execute(Node const& node)
|
||||||
|
{
|
||||||
|
switch (node.type())
|
||||||
|
{
|
||||||
|
case NODE_MODULE: {
|
||||||
|
for (size_t i=0; i<node.size(); i++)
|
||||||
|
{
|
||||||
|
execute(*node.child_at(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TypeSlot {TYPE_NIL};
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_CALL: {
|
||||||
|
std::string ident = node.child_at(0)->repr();
|
||||||
|
auto sym = m_sym.find(ident);
|
||||||
|
|
||||||
|
if (sym == std::nullopt)
|
||||||
|
{
|
||||||
|
m_logger.log<static_error>(LOG_ERROR,
|
||||||
|
node.loc(),
|
||||||
|
"cannot call unknown function '"
|
||||||
|
+ ident
|
||||||
|
+ "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto proto = sym->prototype;
|
||||||
|
assert(proto);
|
||||||
|
|
||||||
|
if (proto->get_param_count() != node.size() - 1
|
||||||
|
&& proto->get_param(proto->get_param_count() - 1).tag
|
||||||
|
!= TAG_VARIADIC)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "type mismatch, expected '"
|
||||||
|
<< proto->get_param_count()
|
||||||
|
<< "' parameters, got '"
|
||||||
|
<< (node.size() - 1)
|
||||||
|
<< "'";
|
||||||
|
|
||||||
|
m_logger.log<static_error>(LOG_ERROR, node.loc(), ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t j = 0;
|
||||||
|
|
||||||
|
for (size_t i=1; i<node.size(); i++)
|
||||||
|
{
|
||||||
|
auto ty = execute(*node.child_at(i));
|
||||||
|
|
||||||
|
if ((ty.tag != proto->get_param(j).tag
|
||||||
|
|| ty.type != proto->get_param(j).type)
|
||||||
|
&& ty.tag != TAG_ANY
|
||||||
|
&& proto->get_param(j).tag != TAG_ANY)
|
||||||
|
{
|
||||||
|
type_mismatch(node.loc(), proto->get_param(j), ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j + 1 < proto->get_param_count()
|
||||||
|
&& proto->get_param(j + 1).tag != TAG_VARIADIC)
|
||||||
|
{
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return proto->get_ret();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_INT: {
|
||||||
|
return TypeSlot {TYPE_INT};
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
std::cerr << "cannot static check node '"
|
||||||
|
<< NodeTypeStr[node.type()]
|
||||||
|
<< "'"
|
||||||
|
<< std::endl;
|
||||||
|
abort();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StaticPass::type_mismatch(Loc const& loc,
|
||||||
|
TypeSlot lhs,
|
||||||
|
TypeSlot rhs)
|
||||||
|
{
|
||||||
|
auto to_s = [](TypeSlot s){
|
||||||
|
if (s.tag == TAG_ANY) { return "ANY"; }
|
||||||
|
return TypeStr[*s.type] + strlen("TYPE_");
|
||||||
|
};
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "type mismatch, expected '"
|
||||||
|
<< to_s(lhs)
|
||||||
|
<< "', got '"
|
||||||
|
<< to_s(rhs)
|
||||||
|
<< "'";
|
||||||
|
|
||||||
|
m_logger.log<static_error>(LOG_ERROR, loc, ss.str());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef zn_STATICPASS_HPP
|
||||||
|
#define zn_STATICPASS_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "Node.hpp"
|
||||||
|
#include "Logger.hpp"
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
ZN_ERROR(static_error);
|
||||||
|
|
||||||
|
class StaticPass
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit StaticPass(Logger& logger, SymTable& sym);
|
||||||
|
virtual ~StaticPass();
|
||||||
|
|
||||||
|
TypeSlot execute(Node const& node);
|
||||||
|
private:
|
||||||
|
Logger& m_logger;
|
||||||
|
SymTable& m_sym;
|
||||||
|
|
||||||
|
void type_mismatch(Loc const& loc,
|
||||||
|
TypeSlot lhs,
|
||||||
|
TypeSlot rhs);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ SymTable::SymTable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ SymTable::~SymTable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymTable::declare(std::string const& name, size_t addr)
|
||||||
|
{
|
||||||
|
m_syms.push_back(Sym {name, addr});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymTable::declare_function(std::string const& name,
|
||||||
|
size_t addr,
|
||||||
|
std::shared_ptr<Prototype> prototype)
|
||||||
|
{
|
||||||
|
m_syms.push_back(Sym {name, addr, prototype});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Sym> SymTable::find(std::string const& name)
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<m_syms.size(); i++)
|
||||||
|
{
|
||||||
|
if (name == m_syms[i].name)
|
||||||
|
{
|
||||||
|
return m_syms[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef zn_SYMTABLE_HPP
|
||||||
|
#define zn_SYMTABLE_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "Prototype.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
struct Sym {
|
||||||
|
std::string name;
|
||||||
|
size_t addr;
|
||||||
|
std::shared_ptr<Prototype> prototype = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SymTable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SymTable();
|
||||||
|
virtual ~SymTable();
|
||||||
|
|
||||||
|
void declare(std::string const& name, size_t addr);
|
||||||
|
|
||||||
|
void declare_function(std::string const& name,
|
||||||
|
size_t addr,
|
||||||
|
std::shared_ptr<Prototype> prototype);
|
||||||
|
|
||||||
|
std::optional<Sym> find(std::string const& name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Sym> m_syms;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,156 @@
|
||||||
|
#include "VM.hpp"
|
||||||
|
#include "src/Program.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ VM::VM(Program& program)
|
||||||
|
{
|
||||||
|
m_pc = 0;
|
||||||
|
Frame f = {program, {}};
|
||||||
|
m_frames.push_back(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ VM::~VM()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void VM::execute(Program& program)
|
||||||
|
{
|
||||||
|
m_pc = 0;
|
||||||
|
|
||||||
|
while (m_pc < program.size())
|
||||||
|
{
|
||||||
|
auto instr = program.instr(m_pc);
|
||||||
|
execute(program, instr.opcode, instr.param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VM::execute(Program& program, Opcode opcode, int param)
|
||||||
|
{
|
||||||
|
switch (opcode)
|
||||||
|
{
|
||||||
|
case OPCODE_LOAD_CONST: {
|
||||||
|
push(param);
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OPCODE_LOAD_LOCAL: {
|
||||||
|
auto constant = frame().locals[to_vm_addr(param)];
|
||||||
|
push(program.add_constant(constant));
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OPCODE_POP: {
|
||||||
|
pop();
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OPCODE_CALL_NATIVE: {
|
||||||
|
std::vector<std::shared_ptr<Constant>> args;
|
||||||
|
|
||||||
|
for (int i=0; i<param; i++)
|
||||||
|
{
|
||||||
|
size_t addr = pop();
|
||||||
|
args.insert(std::begin(args),
|
||||||
|
frame().program.constant(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fun_ref = m_frames.back().program.constant(pop());
|
||||||
|
int ref = std::get<int>(*fun_ref->value());
|
||||||
|
|
||||||
|
auto fun = std::get<std::shared_ptr<NativeFunction>>
|
||||||
|
(m_globals[ref]);
|
||||||
|
assert(fun);
|
||||||
|
|
||||||
|
auto result = fun->call(args);
|
||||||
|
int addr = m_frames.back().program.add_constant(result);
|
||||||
|
push(addr);
|
||||||
|
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
std::cerr << "cannot execute opcode '"
|
||||||
|
<< OpcodeStr[opcode]
|
||||||
|
<< "'" << std::endl;
|
||||||
|
abort();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VM::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
ss << "======== VM Stack ========\n";
|
||||||
|
|
||||||
|
size_t addr = 0;
|
||||||
|
|
||||||
|
for (int val: m_stack)
|
||||||
|
{
|
||||||
|
ss << addr << " " << val << "\n";
|
||||||
|
addr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t VM::store_local(int index, std::shared_ptr<Constant> constant)
|
||||||
|
{
|
||||||
|
frame().locals.push_back(constant);
|
||||||
|
size_t addr = frame().locals.size() - 1;
|
||||||
|
m_addr_mapping[index] = addr;
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Constant> VM::load_local(int index) const
|
||||||
|
{
|
||||||
|
size_t addr = to_vm_addr(index);
|
||||||
|
assert(addr < m_frames.back().locals.size());
|
||||||
|
return m_frames.back().locals[addr];
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t VM::store_global(global_var_t var)
|
||||||
|
{
|
||||||
|
m_globals.push_back(var);
|
||||||
|
return m_globals.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t VM::to_vm_addr(int user_addr) const
|
||||||
|
{
|
||||||
|
return m_addr_mapping.at(user_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int VM::to_user_addr(size_t vm_addr) const
|
||||||
|
{
|
||||||
|
for (auto const& entry: m_addr_mapping)
|
||||||
|
{
|
||||||
|
if (entry.second == vm_addr)
|
||||||
|
{
|
||||||
|
return entry.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "cannot find user addr of '"
|
||||||
|
<< vm_addr << "'" << std::endl;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame& VM::frame()
|
||||||
|
{
|
||||||
|
return m_frames.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VM::push(int value)
|
||||||
|
{
|
||||||
|
m_stack.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int VM::pop()
|
||||||
|
{
|
||||||
|
assert(m_stack.size() > 0);
|
||||||
|
int value = m_stack.back();
|
||||||
|
m_stack.pop_back();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef zn_VM_HPP
|
||||||
|
#define zn_VM_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "Program.hpp"
|
||||||
|
#include "src/NativeFunction.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
struct Frame {
|
||||||
|
Program& program;
|
||||||
|
std::vector<std::shared_ptr<Constant>> locals;
|
||||||
|
};
|
||||||
|
|
||||||
|
using global_var_t = std::variant<std::shared_ptr<NativeFunction>>;
|
||||||
|
|
||||||
|
class VM
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit VM(Program& program);
|
||||||
|
virtual ~VM();
|
||||||
|
|
||||||
|
void execute(Program& program);
|
||||||
|
void execute(Program& program, Opcode opcode, int param);
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
|
size_t store_local(int index, std::shared_ptr<Constant> constant);
|
||||||
|
std::shared_ptr<Constant> load_local(int index) const;
|
||||||
|
|
||||||
|
size_t store_global(global_var_t var);
|
||||||
|
|
||||||
|
size_t to_vm_addr(int user_addr) const;
|
||||||
|
int to_user_addr(size_t vm_addr) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Frame> m_frames;
|
||||||
|
std::vector<int> m_stack;
|
||||||
|
std::vector<global_var_t> m_globals;
|
||||||
|
std::unordered_map<int, size_t> m_addr_mapping;
|
||||||
|
size_t m_pc = 0;
|
||||||
|
|
||||||
|
Frame& frame();
|
||||||
|
void push(int value);
|
||||||
|
int pop();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,54 @@
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "Zarn.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
/*explicit*/ Zarn::Zarn(SymTable& sym, VM& vm)
|
||||||
|
: m_sym { sym }
|
||||||
|
, m_vm { vm }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ Zarn::~Zarn()
|
||||||
|
{
|
||||||
|
for (void* handler: m_handlers)
|
||||||
|
{
|
||||||
|
// dlclose(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_handlers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Zarn::register_function(std::string const& name,
|
||||||
|
std::shared_ptr<Prototype> prototype,
|
||||||
|
native_fn_t body)
|
||||||
|
{
|
||||||
|
auto func = std::make_shared<NativeFunction>(prototype, body);
|
||||||
|
int addr = m_vm.store_global(func);
|
||||||
|
m_sym.declare_function(name, addr, prototype);
|
||||||
|
|
||||||
|
// auto ref = std::make_shared<Constant>(TYPE_REF, addr);
|
||||||
|
// m_vm.store_local(0, ref);
|
||||||
|
// m_sym.declare_function(name, 0, prototype);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Zarn::load_std_library()
|
||||||
|
{
|
||||||
|
load_library(ZN_LIBDIR / "libzarn-std.so");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Zarn::load_library(std::filesystem::path lib_path)
|
||||||
|
{
|
||||||
|
void* handler = dlopen(lib_path.string().c_str(), RTLD_LAZY);
|
||||||
|
assert(handler);
|
||||||
|
m_handlers.push_back(handler);
|
||||||
|
|
||||||
|
|
||||||
|
typedef void(*fun)(Zarn&);
|
||||||
|
fun f = (fun) dlsym(handler, "lib");
|
||||||
|
assert(f);
|
||||||
|
|
||||||
|
f(*this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef zn_ZARN_HPP
|
||||||
|
#define zn_ZARN_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
#include "VM.hpp"
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
class Zarn
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Zarn(SymTable& sym, VM& vm);
|
||||||
|
virtual ~Zarn();
|
||||||
|
|
||||||
|
void register_function(std::string const& name,
|
||||||
|
std::shared_ptr<Prototype> prototype,
|
||||||
|
native_fn_t body);
|
||||||
|
|
||||||
|
void load_std_library();
|
||||||
|
void load_library(std::filesystem::path lib_path);
|
||||||
|
private:
|
||||||
|
SymTable& m_sym;
|
||||||
|
VM& m_vm;
|
||||||
|
std::vector<void*> m_handlers;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef zn_COMMON_HPP
|
||||||
|
#define zn_COMMON_HPP
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#define ZN_GEN_ENUM(X) X
|
||||||
|
#define ZN_GEN_STRING(X) #X
|
||||||
|
|
||||||
|
#define ZN_MK_ENUM(PREFIX, ENUM) \
|
||||||
|
enum PREFIX { ENUM(ZN_GEN_ENUM) }; \
|
||||||
|
constexpr char const* PREFIX ## Str [] = { ENUM(ZN_GEN_STRING) }
|
||||||
|
|
||||||
|
#define ZN_ERROR(NAME) \
|
||||||
|
struct NAME : public std::runtime_error { \
|
||||||
|
NAME (std::string const& what) : std::runtime_error(what) {} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <fstream>
|
||||||
|
#include <variant>
|
||||||
|
#include <iostream>
|
||||||
|
#include <functional>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef zn_CONFIG_HPP
|
||||||
|
#define zn_CONFIG_HPP
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#define ZN_VERSION "@version@"
|
||||||
|
#define ZN_LIBDIR std::filesystem::path("@libdir@")
|
||||||
|
#endif
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include "Module.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
bool debug_mode = false;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
static struct option options[] = {
|
||||||
|
{"debug", no_argument, 0, 'd'},
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
int opt_index;
|
||||||
|
int c = getopt_long(argc, argv, "hd", options, &opt_index);
|
||||||
|
|
||||||
|
if (c == -1) { break; }
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'h': {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Usage: zarn [OPTIONS]... <source_file>" << "\n";
|
||||||
|
|
||||||
|
ss << "\n\t" << "[OPTIONS]" << "\n";
|
||||||
|
|
||||||
|
ss << "\t" << "-d, --debug"
|
||||||
|
<< "\t" << "show debug informations." << "\n";
|
||||||
|
|
||||||
|
ss << "\t" << "-h, --help"
|
||||||
|
<< "\t" << "show this message." << "\n";
|
||||||
|
std::cout << ss.str() << std::endl;
|
||||||
|
return 0;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case 'd': {
|
||||||
|
debug_mode = true;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind < argc)
|
||||||
|
{
|
||||||
|
zn::Logger logger;
|
||||||
|
zn::Module mod { logger };
|
||||||
|
|
||||||
|
if (debug_mode)
|
||||||
|
{
|
||||||
|
mod.load_from_file(argv[optind]);
|
||||||
|
std::cout << mod.string() << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mod.load_from_file(argv[optind]);
|
||||||
|
}
|
||||||
|
catch(std::exception const& err)
|
||||||
|
{
|
||||||
|
std::cerr << err.what() << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef zn_TYPES_HPP
|
||||||
|
#define zn_TYPES_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
#define TYPES(G) \
|
||||||
|
G(TYPE_NIL), \
|
||||||
|
G(TYPE_INT), \
|
||||||
|
G(TYPE_REF)
|
||||||
|
|
||||||
|
namespace zn
|
||||||
|
{
|
||||||
|
ZN_MK_ENUM(Type, TYPES);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,43 @@
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include "../src/Lexer.hpp"
|
||||||
|
|
||||||
|
class LexerTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit LexerTest() {}
|
||||||
|
virtual ~LexerTest() {}
|
||||||
|
|
||||||
|
void test_next(zn::Lexer& lexer, std::string const& oracle)
|
||||||
|
{
|
||||||
|
auto node = lexer.try_next();
|
||||||
|
INFO("expected " << oracle << " got nullptr");
|
||||||
|
REQUIRE(nullptr != node);
|
||||||
|
REQUIRE(oracle == node->string());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
zn::Logger m_logger;
|
||||||
|
zn::Loc m_loc {"tests/lexer"};
|
||||||
|
zn::Lexer m_lexer { m_logger, m_loc };
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_int")
|
||||||
|
{
|
||||||
|
m_lexer.scan(" 3 -2 167 ");
|
||||||
|
|
||||||
|
test_next(m_lexer, "INT[3]");
|
||||||
|
test_next(m_lexer, "INT[-2]");
|
||||||
|
test_next(m_lexer, "INT[167]");
|
||||||
|
REQUIRE(nullptr == m_lexer.try_next());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_call")
|
||||||
|
{
|
||||||
|
m_lexer.scan(" (hello world) ");
|
||||||
|
|
||||||
|
test_next(m_lexer, "OPAR");
|
||||||
|
test_next(m_lexer, "IDENT[hello]");
|
||||||
|
test_next(m_lexer, "IDENT[world]");
|
||||||
|
test_next(m_lexer, "CPAR");
|
||||||
|
REQUIRE(nullptr == m_lexer.try_next());
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
zn::Logger logger;
|
||||||
|
zn::Loc loc {"tests/parser"};
|
||||||
|
zn::Lexer lexer { logger, loc };
|
||||||
|
lexer.scan(source);
|
||||||
|
std::vector<std::shared_ptr<zn::Node>> tokens = lexer.all();
|
||||||
|
|
||||||
|
zn::Parser parser {logger};
|
||||||
|
auto node = parser.parse(tokens);
|
||||||
|
|
||||||
|
REQUIRE(oracle == node->string());
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(ParserTest, "Parser_int")
|
||||||
|
{
|
||||||
|
test_parse("MODULE(INT[37])", " 37");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(ParserTest, "Parser_call")
|
||||||
|
{
|
||||||
|
test_parse("MODULE(CALL(IDENT[hello],INT[1],INT[2],IDENT[world]))",
|
||||||
|
" (hello 1 2 world)");
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include <catch2/catch.hpp>
|
Reference in New Issue