parent
8db23dbd3c
commit
55c094e4ec
|
@ -1,3 +1,7 @@
|
||||||
MODULE ::= EXPR*
|
MODULE ::= EXPR*
|
||||||
EXPR ::=
|
EXPR ::=
|
||||||
int
|
| 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);
|
||||||
|
}
|
34
meson.build
34
meson.build
|
@ -7,8 +7,20 @@ project('zarn',
|
||||||
'cpp_std=c++17'
|
'cpp_std=c++17'
|
||||||
])
|
])
|
||||||
|
|
||||||
|
# CONFIGURATIONS
|
||||||
|
# ==============
|
||||||
extra_libdir = get_option('prefix') / get_option('libdir') / 'zarn'
|
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',
|
zarn_lib = shared_library('zarn',
|
||||||
sources: [
|
sources: [
|
||||||
'src/Module.cpp',
|
'src/Module.cpp',
|
||||||
|
@ -17,15 +29,35 @@ zarn_lib = shared_library('zarn',
|
||||||
'src/Logger.cpp',
|
'src/Logger.cpp',
|
||||||
'src/Lexer.cpp',
|
'src/Lexer.cpp',
|
||||||
'src/Parser.cpp',
|
'src/Parser.cpp',
|
||||||
|
'src/StaticPass.cpp',
|
||||||
'src/Compiler.cpp',
|
'src/Compiler.cpp',
|
||||||
'src/Program.cpp',
|
'src/Program.cpp',
|
||||||
'src/VM.cpp',
|
'src/VM.cpp',
|
||||||
'src/Constant.cpp',
|
'src/Constant.cpp',
|
||||||
|
'src/SymTable.cpp',
|
||||||
|
'src/NativeFunction.cpp',
|
||||||
|
'src/Prototype.cpp',
|
||||||
|
'src/Zarn.cpp',
|
||||||
],
|
],
|
||||||
install: true)
|
install: true)
|
||||||
|
|
||||||
zarn_dep = declare_dependency(link_with: zarn_lib)
|
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',
|
executable('zarn',
|
||||||
sources: [
|
sources: [
|
||||||
'src/main.cpp'
|
'src/main.cpp'
|
||||||
|
@ -35,6 +67,8 @@ executable('zarn',
|
||||||
],
|
],
|
||||||
install: true)
|
install: true)
|
||||||
|
|
||||||
|
# TESTS
|
||||||
|
# =====
|
||||||
executable('zarn-tests',
|
executable('zarn-tests',
|
||||||
sources: [
|
sources: [
|
||||||
'tests/main.cpp',
|
'tests/main.cpp',
|
||||||
|
|
|
@ -4,8 +4,9 @@
|
||||||
|
|
||||||
namespace zn
|
namespace zn
|
||||||
{
|
{
|
||||||
/*explicit*/ Compiler::Compiler(Logger& logger)
|
/*explicit*/ Compiler::Compiler(Logger& logger, SymTable& sym)
|
||||||
: m_logger { logger }
|
: m_logger { logger }
|
||||||
|
, m_sym { sym }
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +18,8 @@ namespace zn
|
||||||
{
|
{
|
||||||
switch (node.type())
|
switch (node.type())
|
||||||
{
|
{
|
||||||
|
// MODULES
|
||||||
|
// =======
|
||||||
case NODE_MODULE: {
|
case NODE_MODULE: {
|
||||||
for (size_t i=0; i<node.size(); i++)
|
for (size_t i=0; i<node.size(); i++)
|
||||||
{
|
{
|
||||||
|
@ -25,10 +28,44 @@ namespace zn
|
||||||
}
|
}
|
||||||
} break;
|
} 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: {
|
case NODE_INT: {
|
||||||
size_t addr = program
|
size_t addr = program
|
||||||
.add_constant(std::make_shared<Constant>
|
.add_constant(std::make_shared<Constant>
|
||||||
(TYPE_INT, std::stoi(node.repr())));
|
(TYPE_INT, node.loc(),
|
||||||
|
std::stoi(node.repr())));
|
||||||
program.append(OPCODE_LOAD_CONST, addr);
|
program.append(OPCODE_LOAD_CONST, addr);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "Logger.hpp"
|
#include "Logger.hpp"
|
||||||
#include "Node.hpp"
|
#include "Node.hpp"
|
||||||
#include "Program.hpp"
|
#include "Program.hpp"
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
|
||||||
namespace zn
|
namespace zn
|
||||||
{
|
{
|
||||||
|
@ -13,13 +14,14 @@ namespace zn
|
||||||
class Compiler
|
class Compiler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Compiler(Logger& logger);
|
explicit Compiler(Logger& logger, SymTable& sym);
|
||||||
virtual ~Compiler();
|
virtual ~Compiler();
|
||||||
|
|
||||||
void compile(Node const& node, Program& program);
|
void compile(Node const& node, Program& program);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Logger& m_logger;
|
Logger& m_logger;
|
||||||
|
SymTable& m_sym;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,11 @@
|
||||||
|
|
||||||
namespace zn
|
namespace zn
|
||||||
{
|
{
|
||||||
/*explicit*/ Constant::Constant(Type type, value_t value)
|
/*explicit*/ Constant::Constant(Type type,
|
||||||
|
Loc const& loc,
|
||||||
|
constant_t value)
|
||||||
: m_type { type }
|
: m_type { type }
|
||||||
|
, m_loc { loc }
|
||||||
, m_value { value }
|
, m_value { value }
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -24,15 +27,16 @@ namespace zn
|
||||||
|
|
||||||
std::string Constant::string() const
|
std::string Constant::string() const
|
||||||
{
|
{
|
||||||
if (auto val = std::get_if<int>(&(*m_value));
|
switch (m_type)
|
||||||
val)
|
|
||||||
{
|
{
|
||||||
return std::to_string(*val);
|
case TYPE_NIL: return "<nil>";
|
||||||
}
|
case TYPE_INT: return std::to_string(std::get<int>(*m_value));
|
||||||
|
default: {
|
||||||
std::cerr << "cannot stringify "
|
std::cerr << "cannot stringify "
|
||||||
<< (TypeStr[m_type] + strlen("TYPE_"))
|
<< (TypeStr[m_type] + strlen("TYPE_"))
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
abort();
|
abort();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,23 @@
|
||||||
#define zn_CONSTANT_HPP
|
#define zn_CONSTANT_HPP
|
||||||
|
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
#define TYPES(G) \
|
#include "Loc.hpp"
|
||||||
G(TYPE_NIL), \
|
|
||||||
G(TYPE_INT)
|
|
||||||
|
|
||||||
namespace zn
|
namespace zn
|
||||||
{
|
{
|
||||||
ZN_MK_ENUM(Type, TYPES);
|
using constant_t = std::optional<std::variant<int>>;
|
||||||
|
|
||||||
using value_t = std::optional<std::variant<int>>;
|
|
||||||
|
|
||||||
class Constant
|
class Constant
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Constant(Type type, value_t value);
|
explicit Constant(Type type,
|
||||||
|
Loc const& loc,
|
||||||
|
constant_t value=std::nullopt);
|
||||||
virtual ~Constant();
|
virtual ~Constant();
|
||||||
|
|
||||||
value_t value() const { return m_value; }
|
Loc loc() const { return m_loc; }
|
||||||
|
constant_t value() const { return m_value; }
|
||||||
Type type() const { return m_type; }
|
Type type() const { return m_type; }
|
||||||
|
|
||||||
bool equals(Constant const& rhs) const;
|
bool equals(Constant const& rhs) const;
|
||||||
|
@ -27,7 +26,8 @@ namespace zn
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type m_type;
|
Type m_type;
|
||||||
value_t m_value;
|
Loc m_loc;
|
||||||
|
constant_t m_value;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "Lexer.hpp"
|
#include "Lexer.hpp"
|
||||||
|
#include "src/Node.hpp"
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
namespace zn
|
namespace zn
|
||||||
{
|
{
|
||||||
|
@ -6,7 +8,11 @@ namespace zn
|
||||||
: m_logger { logger }
|
: m_logger { logger }
|
||||||
, m_loc { loc }
|
, 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_int, this));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*virtual*/ Lexer::~Lexer()
|
/*virtual*/ Lexer::~Lexer()
|
||||||
|
@ -87,6 +93,32 @@ namespace zn
|
||||||
return result;
|
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()
|
void Lexer::skip_spaces()
|
||||||
{
|
{
|
||||||
while (m_cursor < m_source.size()
|
while (m_cursor < m_source.size()
|
||||||
|
@ -131,4 +163,52 @@ namespace zn
|
||||||
repr
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,9 +35,20 @@ namespace zn
|
||||||
size_t m_cursor;
|
size_t m_cursor;
|
||||||
std::vector<scanner_t> m_scanners;
|
std::vector<scanner_t> m_scanners;
|
||||||
Loc m_loc;
|
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();
|
void skip_spaces();
|
||||||
std::optional<ScanInfo> scan_int();
|
std::optional<ScanInfo> scan_int();
|
||||||
|
std::optional<ScanInfo> scan_text(std::string const& text,
|
||||||
|
NodeType type,
|
||||||
|
bool has_value);
|
||||||
|
std::optional<ScanInfo> scan_ident();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
#include "Parser.hpp"
|
#include "Parser.hpp"
|
||||||
#include "Program.hpp"
|
#include "Program.hpp"
|
||||||
#include "Compiler.hpp"
|
#include "Compiler.hpp"
|
||||||
|
#include "StaticPass.hpp"
|
||||||
#include "VM.hpp"
|
#include "VM.hpp"
|
||||||
|
#include "NativeFunction.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
|
||||||
namespace zn
|
namespace zn
|
||||||
{
|
{
|
||||||
|
@ -43,7 +46,12 @@ namespace zn
|
||||||
|
|
||||||
auto ast = parser.parse(lexer.all());
|
auto ast = parser.parse(lexer.all());
|
||||||
|
|
||||||
Compiler compiler { m_logger };
|
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);
|
compiler.compile(*ast, m_program);
|
||||||
|
|
||||||
m_vm.execute(m_program);
|
m_vm.execute(m_program);
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include "Logger.hpp"
|
#include "Logger.hpp"
|
||||||
#include "VM.hpp"
|
#include "VM.hpp"
|
||||||
#include "Program.hpp"
|
#include "Program.hpp"
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
#include "Zarn.hpp"
|
||||||
|
|
||||||
namespace zn
|
namespace zn
|
||||||
{
|
{
|
||||||
|
@ -23,7 +25,9 @@ namespace zn
|
||||||
Logger& m_logger;
|
Logger& m_logger;
|
||||||
std::string m_source;
|
std::string m_source;
|
||||||
Program m_program;
|
Program m_program;
|
||||||
VM m_vm;
|
VM m_vm { m_program };
|
||||||
|
SymTable m_sym;
|
||||||
|
Zarn m_zarn {m_sym, m_vm};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -6,7 +6,11 @@
|
||||||
|
|
||||||
#define NODE_TYPES(G) \
|
#define NODE_TYPES(G) \
|
||||||
G(NODE_MODULE), \
|
G(NODE_MODULE), \
|
||||||
G(NODE_INT)
|
G(NODE_INT), \
|
||||||
|
G(NODE_OPAR), \
|
||||||
|
G(NODE_CPAR), \
|
||||||
|
G(NODE_IDENT), \
|
||||||
|
G(NODE_CALL),
|
||||||
|
|
||||||
namespace zn
|
namespace zn
|
||||||
{
|
{
|
||||||
|
|
|
@ -77,6 +77,11 @@ namespace zn
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_module()
|
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);
|
auto node = mk_node(NODE_MODULE);
|
||||||
|
|
||||||
while (m_cursor < m_tokens.size())
|
while (m_cursor < m_tokens.size())
|
||||||
|
@ -89,11 +94,17 @@ namespace zn
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_expr()
|
std::shared_ptr<Node> Parser::parse_expr()
|
||||||
{
|
{
|
||||||
if (type_is(NODE_INT))
|
if (type_is(NODE_INT)
|
||||||
|
|| type_is(NODE_IDENT))
|
||||||
{
|
{
|
||||||
return consume();
|
return consume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type_is(NODE_OPAR))
|
||||||
|
{
|
||||||
|
return parse_call();
|
||||||
|
}
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "unknown expression '"
|
ss << "unknown expression '"
|
||||||
<< m_tokens[m_cursor]->string()
|
<< m_tokens[m_cursor]->string()
|
||||||
|
@ -104,4 +115,20 @@ namespace zn
|
||||||
ss.str());
|
ss.str());
|
||||||
abort();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace zn
|
||||||
|
|
||||||
std::shared_ptr<Node> parse_module();
|
std::shared_ptr<Node> parse_module();
|
||||||
std::shared_ptr<Node> parse_expr();
|
std::shared_ptr<Node> parse_expr();
|
||||||
|
std::shared_ptr<Node> parse_call();
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,12 @@
|
||||||
|
|
||||||
#define OPCODES(G) \
|
#define OPCODES(G) \
|
||||||
G(OPCODE_LOAD_CONST), \
|
G(OPCODE_LOAD_CONST), \
|
||||||
G(OPCODE_POP)
|
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
|
namespace zn
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
86
src/VM.cpp
86
src/VM.cpp
|
@ -3,8 +3,11 @@
|
||||||
|
|
||||||
namespace zn
|
namespace zn
|
||||||
{
|
{
|
||||||
/*explicit*/ VM::VM()
|
/*explicit*/ VM::VM(Program& program)
|
||||||
{
|
{
|
||||||
|
m_pc = 0;
|
||||||
|
Frame f = {program, {}};
|
||||||
|
m_frames.push_back(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*virtual*/ VM::~VM()
|
/*virtual*/ VM::~VM()
|
||||||
|
@ -14,16 +17,15 @@ namespace zn
|
||||||
void VM::execute(Program& program)
|
void VM::execute(Program& program)
|
||||||
{
|
{
|
||||||
m_pc = 0;
|
m_pc = 0;
|
||||||
m_frames.push_back(Frame {program});
|
|
||||||
|
|
||||||
while (m_pc < program.size())
|
while (m_pc < program.size())
|
||||||
{
|
{
|
||||||
auto instr = program.instr(m_pc);
|
auto instr = program.instr(m_pc);
|
||||||
execute(instr.opcode, instr.param);
|
execute(program, instr.opcode, instr.param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VM::execute(Opcode opcode, int param)
|
void VM::execute(Program& program, Opcode opcode, int param)
|
||||||
{
|
{
|
||||||
switch (opcode)
|
switch (opcode)
|
||||||
{
|
{
|
||||||
|
@ -32,11 +34,41 @@ namespace zn
|
||||||
m_pc++;
|
m_pc++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case OPCODE_LOAD_LOCAL: {
|
||||||
|
auto constant = frame().locals[to_vm_addr(param)];
|
||||||
|
push(program.add_constant(constant));
|
||||||
|
m_pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
case OPCODE_POP: {
|
case OPCODE_POP: {
|
||||||
pop();
|
pop();
|
||||||
m_pc++;
|
m_pc++;
|
||||||
} break;
|
} 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: {
|
default: {
|
||||||
std::cerr << "cannot execute opcode '"
|
std::cerr << "cannot execute opcode '"
|
||||||
<< OpcodeStr[opcode]
|
<< OpcodeStr[opcode]
|
||||||
|
@ -63,6 +95,52 @@ namespace zn
|
||||||
return ss.str();
|
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)
|
void VM::push(int value)
|
||||||
{
|
{
|
||||||
m_stack.push_back(value);
|
m_stack.push_back(value);
|
||||||
|
|
19
src/VM.hpp
19
src/VM.hpp
|
@ -3,29 +3,44 @@
|
||||||
|
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
#include "Program.hpp"
|
#include "Program.hpp"
|
||||||
|
#include "src/NativeFunction.hpp"
|
||||||
|
|
||||||
namespace zn
|
namespace zn
|
||||||
{
|
{
|
||||||
struct Frame {
|
struct Frame {
|
||||||
Program& program;
|
Program& program;
|
||||||
|
std::vector<std::shared_ptr<Constant>> locals;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using global_var_t = std::variant<std::shared_ptr<NativeFunction>>;
|
||||||
|
|
||||||
class VM
|
class VM
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit VM();
|
explicit VM(Program& program);
|
||||||
virtual ~VM();
|
virtual ~VM();
|
||||||
|
|
||||||
void execute(Program& program);
|
void execute(Program& program);
|
||||||
void execute(Opcode opcode, int param);
|
void execute(Program& program, Opcode opcode, int param);
|
||||||
|
|
||||||
std::string string() const;
|
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:
|
private:
|
||||||
std::vector<Frame> m_frames;
|
std::vector<Frame> m_frames;
|
||||||
std::vector<int> m_stack;
|
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;
|
size_t m_pc = 0;
|
||||||
|
|
||||||
|
Frame& frame();
|
||||||
void push(int value);
|
void push(int value);
|
||||||
int pop();
|
int pop();
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,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,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
|
|
@ -21,12 +21,6 @@ protected:
|
||||||
zn::Lexer m_lexer { m_logger, m_loc };
|
zn::Lexer m_lexer { m_logger, m_loc };
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_CASE_METHOD(LexerTest, "Lexer_unknown_text")
|
|
||||||
{
|
|
||||||
m_lexer.scan(" §§§ ");
|
|
||||||
REQUIRE_THROWS_AS(m_lexer.try_next(), zn::lex_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(LexerTest, "Lexer_int")
|
TEST_CASE_METHOD(LexerTest, "Lexer_int")
|
||||||
{
|
{
|
||||||
m_lexer.scan(" 3 -2 167 ");
|
m_lexer.scan(" 3 -2 167 ");
|
||||||
|
@ -36,3 +30,14 @@ TEST_CASE_METHOD(LexerTest, "Lexer_int")
|
||||||
test_next(m_lexer, "INT[167]");
|
test_next(m_lexer, "INT[167]");
|
||||||
REQUIRE(nullptr == m_lexer.try_next());
|
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());
|
||||||
|
}
|
||||||
|
|
|
@ -29,3 +29,9 @@ TEST_CASE_METHOD(ParserTest, "Parser_int")
|
||||||
{
|
{
|
||||||
test_parse("MODULE(INT[37])", " 37");
|
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)");
|
||||||
|
}
|
||||||
|
|
Reference in New Issue