2023-08-30 18:06:26 +00:00
|
|
|
#include "Compiler.hpp"
|
2023-08-31 12:41:43 +00:00
|
|
|
#include "lib/Node.hpp"
|
2023-09-01 20:38:12 +00:00
|
|
|
#include "lib/TypeResolver.hpp"
|
2023-08-30 18:06:26 +00:00
|
|
|
#include "lib/opcodes.hpp"
|
2023-08-31 12:41:43 +00:00
|
|
|
#include "StaticPass.hpp"
|
2023-08-30 18:06:26 +00:00
|
|
|
|
|
|
|
namespace roza
|
|
|
|
{
|
|
|
|
/*explicit*/ Compiler::Compiler(StatusLog& log)
|
|
|
|
: m_log { log }
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*virtual*/ Compiler::~Compiler()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-09-01 20:38:12 +00:00
|
|
|
std::shared_ptr<Program> Compiler::compile(std::shared_ptr<Node> root,
|
|
|
|
std::shared_ptr<SymTable> sym)
|
2023-08-30 18:06:26 +00:00
|
|
|
{
|
|
|
|
auto program = std::make_shared<Program>();
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_node(root, program, sym);
|
2023-08-30 18:06:26 +00:00
|
|
|
return program;
|
|
|
|
}
|
|
|
|
|
2023-09-01 20:38:12 +00:00
|
|
|
void Compiler::compile_node(std::shared_ptr<Node> root,
|
|
|
|
std::shared_ptr<Program> prog,
|
|
|
|
std::shared_ptr<SymTable> sym)
|
2023-08-30 18:06:26 +00:00
|
|
|
{
|
|
|
|
switch (root->type())
|
|
|
|
{
|
2023-09-01 20:38:12 +00:00
|
|
|
case NODE_FUN: {
|
|
|
|
auto params = root->child(0);
|
|
|
|
auto ret = root->child(1);
|
|
|
|
auto body = root->child(2);
|
|
|
|
|
|
|
|
TypeResolver resolver {m_log};
|
|
|
|
auto ty = resolver.find(root, *sym);
|
|
|
|
auto fun = std::make_shared<Fun>(ty);
|
|
|
|
|
|
|
|
for (size_t i=0; i<params->size(); i++)
|
|
|
|
{
|
|
|
|
auto param = params->child(i);
|
|
|
|
std::string name = param->child(0)->repr();
|
|
|
|
auto node = param->child(1);
|
|
|
|
|
|
|
|
fun->sym()->declare_mut(name, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_fun_stack.push_back(fun);
|
|
|
|
compile_node(body, fun->program(), fun->sym());
|
|
|
|
m_fun_stack.pop_back();
|
|
|
|
|
|
|
|
auto value = std::make_shared<Value>(fun, root->loc());
|
|
|
|
prog->push_instr(OP_PUSH_CONST, prog->push_value(value));
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_RETURN: {
|
|
|
|
compile_node(root->child(0), prog, sym);
|
|
|
|
prog->push_instr(OP_RET);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_CALL: {
|
|
|
|
auto fun_addr = sym->find(root->child(0)->repr()).addr;
|
|
|
|
prog->push_instr(OP_PUSH_CONST, fun_addr);
|
|
|
|
|
|
|
|
compile_children(root->child(1), prog, sym);
|
|
|
|
|
|
|
|
prog->push_instr(OP_CALL, root->child(1)->size());
|
|
|
|
|
|
|
|
} break;
|
2023-08-31 23:22:51 +00:00
|
|
|
|
|
|
|
case NODE_IF: {
|
2023-09-01 20:38:12 +00:00
|
|
|
sym->enter_scope();
|
2023-08-31 23:22:51 +00:00
|
|
|
|
|
|
|
std::vector<size_t> to_end;
|
|
|
|
|
|
|
|
std::function<void(std::shared_ptr<Node>)>
|
|
|
|
f = [&](std::shared_ptr<Node> n){
|
|
|
|
auto cond = n->child(0);
|
|
|
|
auto then = n->child(1);
|
|
|
|
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_node(cond, prog, sym);
|
2023-08-31 23:22:51 +00:00
|
|
|
size_t cond_addr = prog->size();
|
|
|
|
prog->push_instr(OP_BRF, 0);
|
|
|
|
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_node(then, prog, sym);
|
2023-08-31 23:22:51 +00:00
|
|
|
|
|
|
|
to_end.push_back(prog->size());
|
|
|
|
prog->push_instr(OP_BR, 0);
|
|
|
|
|
|
|
|
prog->set_param(cond_addr, prog->size());
|
|
|
|
|
|
|
|
if (n->size() > 2)
|
|
|
|
{
|
|
|
|
auto next = n->child(2);
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_node(next, prog, sym);
|
2023-08-31 23:22:51 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
f(root);
|
|
|
|
|
|
|
|
for (auto addr: to_end)
|
|
|
|
{
|
|
|
|
prog->set_param(addr, prog->size());
|
|
|
|
}
|
|
|
|
|
2023-09-01 20:38:12 +00:00
|
|
|
sym->leave_scope();
|
2023-08-31 23:22:51 +00:00
|
|
|
|
|
|
|
} break;
|
|
|
|
|
2023-09-01 20:38:12 +00:00
|
|
|
case NODE_BODY:
|
2023-08-31 23:22:51 +00:00
|
|
|
case NODE_ELSE:
|
|
|
|
case NODE_THEN: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-31 23:22:51 +00:00
|
|
|
} break;
|
|
|
|
|
2023-08-31 12:41:43 +00:00
|
|
|
case NODE_ASSERT: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-31 12:41:43 +00:00
|
|
|
prog->push_instr(OP_ASSERT);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_ASSERT_STATIC_FAIL: {
|
|
|
|
bool failed = false;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2023-09-01 20:38:12 +00:00
|
|
|
StaticPass static_pass {m_log, *sym};
|
2023-08-31 12:41:43 +00:00
|
|
|
static_pass.check_children(root);
|
2023-08-31 19:25:00 +00:00
|
|
|
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-31 12:41:43 +00:00
|
|
|
|
|
|
|
failed = true;
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
if (failed)
|
|
|
|
{
|
|
|
|
m_log.fatal(root->loc(), "static assertion failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
2023-08-31 19:25:00 +00:00
|
|
|
case NODE_IDENT: {
|
2023-09-01 20:38:12 +00:00
|
|
|
SymEntry const& entry = sym->find(root->repr());
|
|
|
|
|
|
|
|
if (m_fun_stack.empty())
|
|
|
|
{
|
|
|
|
prog->push_instr(OP_LOAD_GLOBAL, entry.addr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
prog->push_instr(OP_LOAD_LOCAL, entry.addr);
|
|
|
|
}
|
|
|
|
|
2023-08-31 19:25:00 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_VARDECL: {
|
|
|
|
std::string name = root->child(0)->repr();
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_node(root->child(1), prog, sym);
|
|
|
|
int addr = sym->declare_mut(name, root->child(1));
|
|
|
|
|
|
|
|
if (m_fun_stack.empty())
|
|
|
|
{
|
|
|
|
prog->push_instr(OP_STORE_GLOBAL, addr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
prog->push_instr(OP_STORE_LOCAL, addr);
|
|
|
|
}
|
2023-08-31 19:25:00 +00:00
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_CONSTDECL: {
|
|
|
|
std::string name = root->child(0)->repr();
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_node(root->child(1), prog, sym);
|
|
|
|
int addr = sym->declare(name, root->child(1));
|
|
|
|
|
|
|
|
if (m_fun_stack.empty())
|
|
|
|
{
|
|
|
|
prog->push_instr(OP_STORE_GLOBAL, addr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
prog->push_instr(OP_STORE_LOCAL, addr);
|
|
|
|
}
|
2023-08-31 19:25:00 +00:00
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_ASSIGN: {
|
2023-09-01 20:38:12 +00:00
|
|
|
int addr = sym->find(root->child(0)->repr()).addr;
|
|
|
|
compile_node(root->child(1), prog, sym);
|
|
|
|
|
|
|
|
if (m_fun_stack.empty())
|
|
|
|
{
|
|
|
|
prog->push_instr(OP_STORE_GLOBAL, addr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
prog->push_instr(OP_STORE_LOCAL, addr);
|
|
|
|
}
|
|
|
|
|
2023-08-31 19:25:00 +00:00
|
|
|
} break;
|
|
|
|
|
2023-08-30 18:06:26 +00:00
|
|
|
case NODE_INT: {
|
|
|
|
auto value = std::make_shared<Value>(std::stoi(root->repr()), root->loc());
|
|
|
|
prog->push_instr(OP_PUSH_CONST, prog->push_value(value));
|
|
|
|
} break;
|
|
|
|
|
2023-08-31 09:07:03 +00:00
|
|
|
case NODE_BOOL: {
|
|
|
|
auto value = std::make_shared<Value>(root->repr() == "true", root->loc());
|
|
|
|
prog->push_instr(OP_PUSH_CONST, prog->push_value(value));
|
|
|
|
} break;
|
|
|
|
|
2023-08-31 09:37:13 +00:00
|
|
|
case NODE_EQ: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-31 09:37:13 +00:00
|
|
|
prog->push_instr(OP_EQ);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_NE: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-31 09:37:13 +00:00
|
|
|
prog->push_instr(OP_EQ);
|
|
|
|
prog->push_instr(OP_NOT);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_LT: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-31 09:37:13 +00:00
|
|
|
prog->push_instr(OP_ILT);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_GT: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-31 09:37:13 +00:00
|
|
|
prog->push_instr(OP_IGT);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_LE: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-31 09:37:13 +00:00
|
|
|
prog->push_instr(OP_IGT);
|
|
|
|
prog->push_instr(OP_NOT);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_GE: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-31 09:37:13 +00:00
|
|
|
prog->push_instr(OP_ILT);
|
|
|
|
prog->push_instr(OP_NOT);
|
|
|
|
} break;
|
|
|
|
|
2023-08-31 09:07:03 +00:00
|
|
|
case NODE_IMP: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-31 09:07:03 +00:00
|
|
|
prog->push_instr(OP_IMP);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_AND: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-31 09:07:03 +00:00
|
|
|
prog->push_instr(OP_AND);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_OR: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-31 09:07:03 +00:00
|
|
|
prog->push_instr(OP_OR);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_NOT: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-31 09:07:03 +00:00
|
|
|
prog->push_instr(OP_NOT);
|
|
|
|
} break;
|
|
|
|
|
2023-08-30 22:31:19 +00:00
|
|
|
case NODE_ADD: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-30 22:31:19 +00:00
|
|
|
prog->push_instr(OP_IADD);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_SUB: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-30 22:31:19 +00:00
|
|
|
prog->push_instr(OP_ISUB);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_MUL: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-30 22:31:19 +00:00
|
|
|
prog->push_instr(OP_IMUL);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_DIV: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-30 22:31:19 +00:00
|
|
|
prog->push_instr(OP_IDIV);
|
2023-08-30 18:06:26 +00:00
|
|
|
} break;
|
|
|
|
|
2023-08-30 22:31:19 +00:00
|
|
|
case NODE_MOD: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-30 22:31:19 +00:00
|
|
|
prog->push_instr(OP_IMOD);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_POW: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-30 22:31:19 +00:00
|
|
|
prog->push_instr(OP_IPOW);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_UADD: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-30 22:31:19 +00:00
|
|
|
prog->push_instr(OP_IUADD);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_USUB: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-30 22:31:19 +00:00
|
|
|
prog->push_instr(OP_IUSUB);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NODE_PROG: {
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_children(root, prog, sym);
|
2023-08-30 18:06:26 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
m_log.fatal(root->loc(), "cannot compile node '" + root->string() + "'.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-01 20:38:12 +00:00
|
|
|
void Compiler::compile_children(std::shared_ptr<Node> root,
|
|
|
|
std::shared_ptr<Program> prog,
|
|
|
|
std::shared_ptr<SymTable> sym)
|
2023-08-30 18:06:26 +00:00
|
|
|
{
|
|
|
|
for (size_t i=0; i<root->size(); i++)
|
|
|
|
{
|
2023-09-01 20:38:12 +00:00
|
|
|
compile_node(root->child(i), prog, sym);
|
2023-08-30 18:06:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|