Compare commits
No commits in common. "55c094e4ec8ed2088d5ebfeaa63bc2170a042799" and "62fd1956d8c2eb939a3c60356677ff8dc0f9ea59" have entirely different histories.
55c094e4ec
...
62fd1956d8
|
@ -1,4 +0,0 @@
|
|||
*\#*
|
||||
*~*
|
||||
build
|
||||
.cache
|
11
Makefile
11
Makefile
|
@ -1,11 +0,0 @@
|
|||
.PHONY: build tests
|
||||
|
||||
build:
|
||||
meson setup build
|
||||
meson compile -C build
|
||||
|
||||
tests: build
|
||||
build/zarn-tests
|
||||
|
||||
install: tests
|
||||
meson install -C build
|
|
@ -1,7 +0,0 @@
|
|||
MODULE ::= EXPR*
|
||||
EXPR ::=
|
||||
| int
|
||||
| ident
|
||||
| CALL
|
||||
|
||||
CALL ::= opar ident EXPR* cpar
|
|
@ -1,9 +0,0 @@
|
|||
#ifndef COMMON_HPP
|
||||
#define COMMON_HPP
|
||||
|
||||
#include "../src/Zarn.hpp"
|
||||
using namespace zn;
|
||||
|
||||
#define STDARGS std::vector<std::shared_ptr<Constant>>
|
||||
|
||||
#endif
|
|
@ -1,36 +0,0 @@
|
|||
#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);
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
#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
|
|
@ -1,19 +0,0 @@
|
|||
#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);
|
||||
}
|
81
meson.build
81
meson.build
|
@ -1,81 +0,0 @@
|
|||
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')
|
||||
])
|
|
@ -1,79 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
#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
|
|
@ -1,42 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
#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
|
214
src/Lexer.cpp
214
src/Lexer.cpp
|
@ -1,214 +0,0 @@
|
|||
#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
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
#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
|
14
src/Loc.cpp
14
src/Loc.cpp
|
@ -1,14 +0,0 @@
|
|||
#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()
|
||||
{
|
||||
}
|
||||
}
|
24
src/Loc.hpp
24
src/Loc.hpp
|
@ -1,24 +0,0 @@
|
|||
#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
|
|
@ -1,12 +0,0 @@
|
|||
#include "Logger.hpp"
|
||||
|
||||
namespace zn
|
||||
{
|
||||
/*explicit*/ Logger::Logger()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Logger::~Logger()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
#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
|
|
@ -1,67 +0,0 @@
|
|||
#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();
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
#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
|
|
@ -1,24 +0,0 @@
|
|||
#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);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#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
|
56
src/Node.cpp
56
src/Node.cpp
|
@ -1,56 +0,0 @@
|
|||
#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();
|
||||
}
|
||||
}
|
45
src/Node.hpp
45
src/Node.hpp
|
@ -1,45 +0,0 @@
|
|||
#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
|
134
src/Parser.cpp
134
src/Parser.cpp
|
@ -1,134 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
#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
|
|
@ -1,68 +0,0 @@
|
|||
#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();
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
#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
|
|
@ -1,105 +0,0 @@
|
|||
#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();
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
#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
|
|
@ -1,114 +0,0 @@
|
|||
#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());
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#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
|
|
@ -1,37 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
#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
|
156
src/VM.cpp
156
src/VM.cpp
|
@ -1,156 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
}
|
49
src/VM.hpp
49
src/VM.hpp
|
@ -1,49 +0,0 @@
|
|||
#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
|
54
src/Zarn.cpp
54
src/Zarn.cpp
|
@ -1,54 +0,0 @@
|
|||
#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);
|
||||
}
|
||||
}
|
29
src/Zarn.hpp
29
src/Zarn.hpp
|
@ -1,29 +0,0 @@
|
|||
#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
|
|
@ -1,31 +0,0 @@
|
|||
#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
|
|
@ -1,8 +0,0 @@
|
|||
#ifndef zn_CONFIG_HPP
|
||||
#define zn_CONFIG_HPP
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#define ZN_VERSION "@version@"
|
||||
#define ZN_LIBDIR std::filesystem::path("@libdir@")
|
||||
#endif
|
72
src/main.cpp
72
src/main.cpp
|
@ -1,72 +0,0 @@
|
|||
#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;
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
#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
|
|
@ -1,43 +0,0 @@
|
|||
#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());
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
#include <catch2/catch.hpp>
|
||||
#include "../src/Parser.hpp"
|
||||
#include "../src/Lexer.hpp"
|
||||
|
||||
class ParserTest
|
||||
{
|
||||
public:
|
||||
explicit ParserTest() {}
|
||||
virtual ~ParserTest() {}
|
||||
|
||||
void test_parse(std::string const& oracle,
|
||||
std::string const& source)
|
||||
{
|
||||
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)");
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch2/catch.hpp>
|
Reference in New Issue