ADD: program and vm for integers.
parent
04f57c631e
commit
8db23dbd3c
|
@ -16,7 +16,11 @@ zarn_lib = shared_library('zarn',
|
|||
'src/Node.cpp',
|
||||
'src/Logger.cpp',
|
||||
'src/Lexer.cpp',
|
||||
'src/Parser.cpp'
|
||||
'src/Parser.cpp',
|
||||
'src/Compiler.cpp',
|
||||
'src/Program.cpp',
|
||||
'src/VM.cpp',
|
||||
'src/Constant.cpp',
|
||||
],
|
||||
install: true)
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
#include "Compiler.hpp"
|
||||
#include "src/Node.hpp"
|
||||
#include "src/Program.hpp"
|
||||
|
||||
namespace zn
|
||||
{
|
||||
/*explicit*/ Compiler::Compiler(Logger& logger)
|
||||
: m_logger { logger }
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ Compiler::~Compiler()
|
||||
{
|
||||
}
|
||||
|
||||
void Compiler::compile(Node const& node, Program& program)
|
||||
{
|
||||
switch (node.type())
|
||||
{
|
||||
case NODE_MODULE: {
|
||||
for (size_t i=0; i<node.size(); i++)
|
||||
{
|
||||
compile(*node.child_at(i), program);
|
||||
program.append(OPCODE_POP);
|
||||
}
|
||||
} break;
|
||||
|
||||
case NODE_INT: {
|
||||
size_t addr = program
|
||||
.add_constant(std::make_shared<Constant>
|
||||
(TYPE_INT, 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,26 @@
|
|||
#ifndef zn_COMPILER_HPP
|
||||
#define zn_COMPILER_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Program.hpp"
|
||||
|
||||
namespace zn
|
||||
{
|
||||
ZN_ERROR(compile_error);
|
||||
|
||||
class Compiler
|
||||
{
|
||||
public:
|
||||
explicit Compiler(Logger& logger);
|
||||
virtual ~Compiler();
|
||||
|
||||
void compile(Node const& node, Program& program);
|
||||
|
||||
private:
|
||||
Logger& m_logger;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,38 @@
|
|||
#include "Constant.hpp"
|
||||
|
||||
namespace zn
|
||||
{
|
||||
/*explicit*/ Constant::Constant(Type type, value_t value)
|
||||
: m_type { type }
|
||||
, 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
|
||||
{
|
||||
if (auto val = std::get_if<int>(&(*m_value));
|
||||
val)
|
||||
{
|
||||
return std::to_string(*val);
|
||||
}
|
||||
|
||||
std::cerr << "cannot stringify "
|
||||
<< (TypeStr[m_type] + strlen("TYPE_"))
|
||||
<< std::endl;
|
||||
abort();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef zn_CONSTANT_HPP
|
||||
#define zn_CONSTANT_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#define TYPES(G) \
|
||||
G(TYPE_NIL), \
|
||||
G(TYPE_INT)
|
||||
|
||||
namespace zn
|
||||
{
|
||||
ZN_MK_ENUM(Type, TYPES);
|
||||
|
||||
using value_t = std::optional<std::variant<int>>;
|
||||
|
||||
class Constant
|
||||
{
|
||||
public:
|
||||
explicit Constant(Type type, value_t value);
|
||||
virtual ~Constant();
|
||||
|
||||
value_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;
|
||||
value_t m_value;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,6 +1,9 @@
|
|||
#include "Module.hpp"
|
||||
#include "Lexer.hpp"
|
||||
#include "Parser.hpp"
|
||||
#include "Program.hpp"
|
||||
#include "Compiler.hpp"
|
||||
#include "VM.hpp"
|
||||
|
||||
namespace zn
|
||||
{
|
||||
|
@ -39,6 +42,18 @@ namespace zn
|
|||
Parser parser { m_logger };
|
||||
|
||||
auto ast = parser.parse(lexer.all());
|
||||
std::cout << ast->string() << std::endl;
|
||||
|
||||
Compiler compiler { m_logger };
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "common.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "VM.hpp"
|
||||
#include "Program.hpp"
|
||||
|
||||
namespace zn
|
||||
{
|
||||
|
@ -16,10 +18,12 @@ namespace zn
|
|||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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,45 @@
|
|||
#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_POP)
|
||||
|
||||
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,78 @@
|
|||
#include "VM.hpp"
|
||||
#include "src/Program.hpp"
|
||||
|
||||
namespace zn
|
||||
{
|
||||
/*explicit*/ VM::VM()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ VM::~VM()
|
||||
{
|
||||
}
|
||||
|
||||
void VM::execute(Program& program)
|
||||
{
|
||||
m_pc = 0;
|
||||
m_frames.push_back(Frame {program});
|
||||
|
||||
while (m_pc < program.size())
|
||||
{
|
||||
auto instr = program.instr(m_pc);
|
||||
execute(instr.opcode, instr.param);
|
||||
}
|
||||
}
|
||||
|
||||
void VM::execute(Opcode opcode, int param)
|
||||
{
|
||||
switch (opcode)
|
||||
{
|
||||
case OPCODE_LOAD_CONST: {
|
||||
push(param);
|
||||
m_pc++;
|
||||
} break;
|
||||
|
||||
case OPCODE_POP: {
|
||||
pop();
|
||||
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();
|
||||
}
|
||||
|
||||
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,34 @@
|
|||
#ifndef zn_VM_HPP
|
||||
#define zn_VM_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
#include "Program.hpp"
|
||||
|
||||
namespace zn
|
||||
{
|
||||
struct Frame {
|
||||
Program& program;
|
||||
};
|
||||
|
||||
class VM
|
||||
{
|
||||
public:
|
||||
explicit VM();
|
||||
virtual ~VM();
|
||||
|
||||
void execute(Program& program);
|
||||
void execute(Opcode opcode, int param);
|
||||
|
||||
std::string string() const;
|
||||
|
||||
private:
|
||||
std::vector<Frame> m_frames;
|
||||
std::vector<int> m_stack;
|
||||
size_t m_pc = 0;
|
||||
|
||||
void push(int value);
|
||||
int pop();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <variant>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
|
60
src/main.cpp
60
src/main.cpp
|
@ -1,15 +1,71 @@
|
|||
#include <iostream>
|
||||
#include <getopt.h>
|
||||
#include "Module.hpp"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
bool debug_mode = false;
|
||||
|
||||
if (argc > 1)
|
||||
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 };
|
||||
|
||||
mod.load_from_file(argv[1]);
|
||||
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;
|
||||
|
|
Reference in New Issue