#include "Compiler.hpp" #include "Lambda.hpp" namespace fk { /*explicit*/ Compiler::Compiler(std::shared_ptr sym) : m_sym { sym } { } /*virtual*/ Compiler::~Compiler() { } void Compiler::add_macro(std::string const& name, std::shared_ptr macro) { m_macros[name] = macro; } std::shared_ptr Compiler::compile(std::shared_ptr node) { auto prog = std::make_shared(); compile_prog(node, prog); return prog; } void Compiler::compile_prog(std::shared_ptr node, std::shared_ptr prog) { switch (node->type()) { case NODE_MODULE: { for (size_t i=0; isize(); i++) { compile_prog(node->child(i), prog); prog->add(OP_POP); } } break; case NODE_BODY: { for (size_t i=0; isize(); i++) { compile_prog(node->child(i), prog); if (i != node->size() - 1) { prog->add(OP_POP); } } } break; case NODE_LAMBDA: { auto params = node->child(0); auto body = node->child(1); m_sym->enter_scope(node); for (size_t i=0; isize(); i++) { std::string ident = params->child(i)->repr(); m_sym->declare_local(ident, i, node->loc()); } Compiler compiler {m_sym}; for (auto e: m_macros) { compiler.add_macro(e.first, e.second); } auto program = compiler.compile(body); program->add(OP_RET); m_sym->leave_scope(); auto constant = std::make_shared(TYPE_PROGRAM, program, node->loc()); prog->load_const(constant); prog->add(OP_MAKE_FUNCTION, params->size()); } break; case NODE_CALL: { std::string ident = node->child(0)->repr(); if (auto itr=m_macros.find(ident); itr != std::end(m_macros)) { auto macro = itr->second; macro->call(*this, node, prog); } else { for (size_t i=0; isize(); i++) { compile_prog(node->child(i), prog); } if (node->child(0)->type() == NODE_LAMBDA || node->child(0)->type() == NODE_CALL) { prog->add(OP_CALL_REF, node->size() - 1); } else if (node->child(0)->type() == NODE_IDENT) { if (auto entry = m_sym->find(node->child(0)->repr()); entry && entry->is_global() == false) { size_t arity = entry->node()->child(0)->size(); if (arity != node->size() - 1) { std::stringstream ss; ss << "arity mismatch: " << node->child(0)->repr() << " expect '" << arity << "' arguments, got '" << node->size() - 1 << "'"; node->loc().error(LOG_ERROR, ss.str()); } prog->add(OP_CALL_REF, node->size() - 1); } else { prog->add(OP_CALL_NATIVE, node->size() - 1); } } } } break; case NODE_IDENT: { auto entry = m_sym->find(node->repr()); if (!entry) { std::stringstream ss; ss << "'" << node->repr() << "' is undefined"; node->loc().error(LOG_ERROR, ss.str()); } if (!entry->is_global()) { prog->add(OP_LOAD_LOCAL, entry->addr()); } else { prog->add(OP_LOAD_GLOBAL, entry->addr()); } } break; case NODE_INT: { prog->load_const(std::make_shared(TYPE_INT, stoi(node->repr()), node->loc())); } break; case NODE_FLOAT: { prog->load_const(std::make_shared(TYPE_FLOAT, stof(node->repr()), node->loc())); } break; case NODE_BOOL: { prog->load_const(std::make_shared(TYPE_BOOL, node->repr() == "true", node->loc())); } break; case NODE_STRING: { std::string str = node->repr(); prog->load_const(std::make_shared(TYPE_STRING, str.substr(1, str.size() - 2), node->loc())); } break; default: { std::stringstream ss; ss << "cannot compile expression '" << (NodeTypeStr[node->type()] + strlen("NODE_")) << "'"; node->loc().error(LOG_ERROR, ss.str()); } break; } } }