iw7-mod/deps/gsc-tool/src/gsc/compiler.cpp
2024-08-13 05:15:34 -04:00

2938 lines
85 KiB
C++

// Copyright 2024 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#include "xsk/stdinc.hpp"
#include "xsk/gsc/compiler.hpp"
#include "xsk/gsc/context.hpp"
namespace xsk::gsc
{
compiler::compiler(context* ctx) : ctx_{ ctx }
{
}
auto compiler::compile(program const& data) -> assembly::ptr
{
emit_program(data);
return std::move(assembly_);
}
auto compiler::compile(std::string const& file, std::vector<u8>& data) -> assembly::ptr
{
auto prog = ctx_->source().parse_program(file, data);
return compile(*prog);
}
auto compiler::emit_program(program const& prog) -> void
{
assembly_ = assembly::make();
localfuncs_.clear();
constants_.clear();
developer_thread_ = false;
animload_ = false;
animname_ = {};
index_ = 1;
debug_pos_ = { 0, 0 };
ctx_->init_includes();
for (auto const& inc : prog.includes)
{
auto const& path = inc->path->value;
if (!ctx_->load_include(path))
{
throw error(std::format("duplicated include file {}", path));
}
}
for (auto const& dec : prog.declarations)
{
if (dec->is<decl_function>())
{
auto const& name = dec->as<decl_function>().name->value;
if (ctx_->func_exists(name) || ctx_->meth_exists(name))
{
throw comp_error(dec->loc(), std::format("function name '{}' already defined as builtin", name));
}
for (auto const& entry : localfuncs_)
{
if (entry == name)
throw comp_error(dec->loc(), std::format("function name '{}' already defined as local function", name));
}
localfuncs_.push_back(dec->as<decl_function>().name->value);
}
}
for (auto const& dec : prog.declarations)
{
emit_decl(*dec);
}
}
auto compiler::emit_decl(decl const& dec) -> void
{
switch (dec.kind())
{
case node::decl_dev_begin:
developer_thread_ = true;
break;
case node::decl_dev_end:
developer_thread_ = false;
break;
case node::decl_usingtree:
emit_decl_usingtree(dec.as<decl_usingtree>());
break;
case node::decl_constant:
emit_decl_constant(dec.as<decl_constant>());
break;
case node::decl_function:
emit_decl_function(dec.as<decl_function>());
break;
default:
throw comp_error(dec.loc(), "unknown declaration");
}
}
auto compiler::emit_decl_usingtree(decl_usingtree const& animtree) -> void
{
if (developer_thread_)
throw comp_error(animtree.loc(), "cannot put #using_animtree inside developer comment");
animname_ = animtree.name->value;
animload_ = false;
}
auto compiler::emit_decl_constant(decl_constant const& constant) -> void
{
auto const it = constants_.find(constant.name->value);
if (it != constants_.end())
throw comp_error(constant.loc(), std::format("duplicated constant '{}'", constant.name->value));
constants_.insert({ constant.name->value, constant.value.get() });
}
auto compiler::emit_decl_function(decl_function const& func) -> void
{
label_idx_ = 0;
can_break_ = false;
can_continue_ = false;
scopes_.clear();
stackframe_.clear();
break_blks_.clear();
continue_blks_.clear();
function_ = function::make();
function_->index = index_;
function_->name = func.name->value;
function_->id = (ctx_->props() & props::hash) ? 0 : ctx_->token_id(function_->name);
process_function(func);
auto& scp = scopes_.at(func.body.get());
emit_expr_parameters(*func.params, *scp);
emit_stmt_comp(*func.body, *scp, true);
emit_opcode(opcode::OP_End);
function_->size = index_ - function_->index;
assembly_->functions.push_back(std::move(function_));
}
auto compiler::emit_stmt(stmt const& stm, scope& scp, bool last) -> void
{
debug_pos_ = { stm.loc().begin.line, stm.loc().begin.column };
switch (stm.kind())
{
case node::stmt_list:
emit_stmt_list(stm.as<stmt_list>(), scp, last);
break;
case node::stmt_comp:
emit_stmt_comp(stm.as<stmt_comp>(), scp, last);
break;
case node::stmt_dev:
emit_stmt_dev(stm.as<stmt_dev>(), scp, last);
break;
case node::stmt_expr:
emit_stmt_expr(stm.as<stmt_expr>(), scp);
break;
case node::stmt_endon:
emit_stmt_endon(stm.as<stmt_endon>(), scp);
break;
case node::stmt_notify:
emit_stmt_notify(stm.as<stmt_notify>(), scp);
break;
case node::stmt_wait:
emit_stmt_wait(stm.as<stmt_wait>(), scp);
break;
case node::stmt_waittill:
emit_stmt_waittill(stm.as<stmt_waittill>(), scp);
break;
case node::stmt_waittillmatch:
emit_stmt_waittillmatch(stm.as<stmt_waittillmatch>(), scp);
break;
case node::stmt_waittillframeend:
emit_stmt_waittillframeend(stm.as<stmt_waittillframeend>(), scp);
break;
case node::stmt_waitframe:
emit_stmt_waitframe(stm.as<stmt_waitframe>(), scp);
break;
case node::stmt_if:
emit_stmt_if(stm.as<stmt_if>(), scp, last);
break;
case node::stmt_ifelse:
emit_stmt_ifelse(stm.as<stmt_ifelse>(), scp, last);
break;
case node::stmt_while:
emit_stmt_while(stm.as<stmt_while>(), scp);
break;
case node::stmt_dowhile:
emit_stmt_dowhile(stm.as<stmt_dowhile>(), scp);
break;
case node::stmt_for:
emit_stmt_for(stm.as<stmt_for>(), scp);
break;
case node::stmt_foreach:
emit_stmt_foreach(stm.as<stmt_foreach>(), scp);
break;
case node::stmt_switch:
emit_stmt_switch(stm.as<stmt_switch>(), scp);
break;
case node::stmt_case:
emit_stmt_case(stm.as<stmt_case>(), scp);
break;
case node::stmt_default:
emit_stmt_default(stm.as<stmt_default>(), scp);
break;
case node::stmt_break:
emit_stmt_break(stm.as<stmt_break>(), scp);
break;
case node::stmt_continue:
emit_stmt_continue(stm.as<stmt_continue>(), scp);
break;
case node::stmt_return:
emit_stmt_return(stm.as<stmt_return>(), scp);
break;
case node::stmt_breakpoint:
emit_stmt_breakpoint(stm.as<stmt_breakpoint>(), scp);
break;
case node::stmt_prof_begin:
emit_stmt_prof_begin(stm.as<stmt_prof_begin>(), scp);
break;
case node::stmt_prof_end:
emit_stmt_prof_end(stm.as<stmt_prof_end>(), scp);
break;
case node::stmt_assert:
emit_stmt_assert(stm.as<stmt_assert>(), scp);
break;
case node::stmt_assertex:
emit_stmt_assertex(stm.as<stmt_assertex>(), scp);
break;
case node::stmt_assertmsg:
emit_stmt_assertmsg(stm.as<stmt_assertmsg>(), scp);
break;
default:
throw comp_error(stm.loc(), "unknown statement");
}
}
auto compiler::emit_stmt_list(stmt_list const& stm, scope& scp, bool last) -> void
{
for (auto const& entry : stm.list)
{
emit_stmt(*entry, scp, (&entry == &stm.list.back() && last) ? true : false);
}
}
auto compiler::emit_stmt_comp(stmt_comp const& stm, scope& scp, bool last) -> void
{
emit_stmt_list(*stm.block, scp, last);
}
auto compiler::emit_stmt_dev(stmt_dev const& stm, scope& scp, bool last) -> void
{
emit_stmt_list(*stm.block, scp, last);
}
auto compiler::emit_stmt_expr(stmt_expr const& stm, scope& scp) -> void
{
switch (stm.value->kind())
{
case node::expr_increment:
emit_expr_increment(stm.value->as<expr_increment>(), scp, true);
break;
case node::expr_decrement:
emit_expr_decrement(stm.value->as<expr_decrement>(), scp, true);
break;
case node::expr_assign:
emit_expr_assign(stm.value->as<expr_assign>(), scp);
break;
case node::expr_call:
emit_expr_call(stm.value->as<expr_call>(), scp, true);
break;
case node::expr_method:
emit_expr_method(stm.value->as<expr_method>(), scp, true);
break;
case node::expr_empty:
break;
default:
throw comp_error(stm.loc(), "unknown expr statement expression");
}
}
auto compiler::emit_stmt_endon(stmt_endon const& stm, scope& scp) -> void
{
emit_expr(*stm.event, scp);
emit_expr(*stm.obj, scp);
emit_opcode(opcode::OP_endon);
}
auto compiler::emit_stmt_notify(stmt_notify const& stm, scope& scp) -> void
{
emit_opcode(opcode::OP_voidCodepos);
for (auto it = stm.args->list.rbegin(); it != stm.args->list.rend(); it++)
{
emit_expr(**it, scp);
}
emit_expr(*stm.event, scp);
emit_expr(*stm.obj, scp);
emit_opcode(opcode::OP_notify);
}
auto compiler::emit_stmt_wait(stmt_wait const& stm, scope& scp) -> void
{
emit_expr(*stm.time, scp);
emit_opcode(opcode::OP_wait);
}
auto compiler::emit_stmt_waittill(stmt_waittill const& stm, scope& scp) -> void
{
emit_expr(*stm.event, scp);
emit_expr(*stm.obj, scp);
emit_opcode(opcode::OP_waittill);
for (auto const& entry : stm.args->list)
{
emit_opcode(opcode::OP_SafeSetWaittillVariableFieldCached, std::format("{}", variable_create(entry->as<expr_identifier>(), scp)));
}
emit_opcode(opcode::OP_clearparams);
}
auto compiler::emit_stmt_waittillmatch(stmt_waittillmatch const& stm, scope& scp) -> void
{
emit_expr_arguments(*stm.args, scp);
emit_expr(*stm.event, scp);
emit_expr(*stm.obj, scp);
emit_opcode(opcode::OP_waittillmatch, std::format("{}", stm.args->list.size()));
emit_opcode(opcode::OP_waittillmatch2);
emit_opcode(opcode::OP_clearparams);
}
auto compiler::emit_stmt_waittillframeend(stmt_waittillframeend const&, scope&) -> void
{
emit_opcode(opcode::OP_waittillFrameEnd);
}
auto compiler::emit_stmt_waitframe(stmt_waitframe const&, scope&) -> void
{
emit_opcode(opcode::OP_waitframe);
}
auto compiler::emit_stmt_if(stmt_if const& stm, scope& scp, bool last) -> void
{
auto end_loc = create_label();
if (stm.test->is<expr_not>())
{
emit_expr(*stm.test->as<expr_not>().rvalue, scp);
emit_opcode(opcode::OP_JumpOnTrue, end_loc);
}
else
{
emit_expr(*stm.test, scp);
emit_opcode(opcode::OP_JumpOnFalse, end_loc);
}
auto& scp_body = scopes_.at(stm.body.get());
scp.transfer(scp_body);
emit_stmt(*stm.body, *scp_body, last);
last ? emit_opcode(opcode::OP_End) : emit_remove_local_vars(*scp_body);
insert_label(end_loc);
}
auto compiler::emit_stmt_ifelse(stmt_ifelse const& stm, scope& scp, bool last) -> void
{
auto childs = std::vector<scope*>{};
auto else_loc = create_label();
auto end_loc = create_label();
if (stm.test->is<expr_not>())
{
emit_expr(*stm.test->as<expr_not>().rvalue, scp);
emit_opcode(opcode::OP_JumpOnTrue, else_loc);
}
else
{
emit_expr(*stm.test, scp);
emit_opcode(opcode::OP_JumpOnFalse, else_loc);
}
auto& scp_then = scopes_.at(stm.stmt_if.get());
scp.transfer(scp_then);
emit_stmt(*stm.stmt_if, *scp_then, last);
emit_remove_local_vars(*scp_then);
if (scp_then->abort == scope::abort_none)
childs.push_back(scp_then.get());
last ? emit_opcode(opcode::OP_End) : emit_opcode(opcode::OP_jump, end_loc);
insert_label(else_loc);
auto& scp_else = scopes_.at(stm.stmt_else.get());
scp.transfer(scp_else);
emit_stmt(*stm.stmt_else, *scp_else, last);
last ? emit_opcode(opcode::OP_End) : emit_remove_local_vars(*scp_else);
if (scp_else->abort == scope::abort_none)
childs.push_back(scp_else.get());
insert_label(end_loc);
scp.init(childs);
}
auto compiler::emit_stmt_while(stmt_while const& stm, scope& scp) -> void
{
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
auto old_break = can_break_;
auto old_continue = can_continue_;
break_blks_.clear();
continue_blks_.clear();
can_break_ = true;
can_continue_ = true;
auto break_loc = create_label();
auto continue_loc = create_label();
auto& scp_body = scopes_.at(stm.body.get());
scp.transfer(scp_body);
scp_body->loc_break = break_loc;
scp_body->loc_cont = continue_loc;
emit_create_local_vars(*scp_body);
scp.init(scp_body);
auto begin_loc = insert_label();
bool const_cond = is_constant_condition(*stm.test);
if (!const_cond)
{
if (stm.test->is<expr_not>())
{
emit_expr(*stm.test->as<expr_not>().rvalue, scp);
emit_opcode(opcode::OP_JumpOnTrue, break_loc);
}
else
{
emit_expr(*stm.test, scp);
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
}
}
emit_stmt(*stm.body, *scp_body, false);
insert_label(continue_loc);
emit_opcode(opcode::OP_jumpback, begin_loc);
insert_label(break_loc);
if (const_cond)
scp.init(break_blks_);
can_break_ = old_break;
can_continue_ = old_continue;
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
auto compiler::emit_stmt_dowhile(stmt_dowhile const& stm, scope& scp) -> void
{
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
auto old_break = can_break_;
auto old_continue = can_continue_;
break_blks_.clear();
continue_blks_.clear();
can_break_ = true;
can_continue_ = true;
auto break_loc = create_label();
auto continue_loc = create_label();
auto& scp_body = scopes_.at(stm.body.get());
scp.transfer(scp_body);
scp_body->loc_break = break_loc;
scp_body->loc_cont = continue_loc;
emit_create_local_vars(*scp_body);
scp.init(scp_body);
auto begin_loc = insert_label();
emit_stmt(*stm.body, *scp_body, false);
insert_label(continue_loc);
bool const_cond = is_constant_condition(*stm.test);
if (!const_cond)
{
if (stm.test->is<expr_not>())
{
emit_expr(*stm.test->as<expr_not>().rvalue, scp);
emit_opcode(opcode::OP_JumpOnTrue, break_loc);
}
else
{
emit_expr(*stm.test, scp);
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
}
}
emit_opcode(opcode::OP_jumpback, begin_loc);
insert_label(break_loc);
if (const_cond)
scp.init(break_blks_);
can_break_ = old_break;
can_continue_ = old_continue;
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
auto compiler::emit_stmt_for(stmt_for const& stm, scope& scp) -> void
{
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
auto old_break = can_break_;
auto old_continue = can_continue_;
break_blks_.clear();
continue_blks_.clear();
can_break_ = false;
can_continue_ = false;
auto break_loc = create_label();
auto continue_loc = create_label();
emit_stmt(*stm.init, scp, false);
auto& scp_body = scopes_.at(stm.body.get());
scp.transfer(scp_body);
scp_body->loc_break = break_loc;
scp_body->loc_cont = continue_loc;
emit_create_local_vars(*scp_body);
scp.init(scp_body);
auto& scp_iter = scopes_.at(stm.iter.get());
scp.transfer(scp_iter);
auto begin_loc = insert_label();
bool const_cond = is_constant_condition(*stm.test);
if (!const_cond)
{
if (stm.test->is<expr_not>())
{
emit_expr(*stm.test->as<expr_not>().rvalue, scp);
emit_opcode(opcode::OP_JumpOnTrue, break_loc);
}
else
{
emit_expr(*stm.test, scp);
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
}
}
can_break_ = true;
can_continue_ = true;
emit_stmt(*stm.body, *scp_body, false);
if (scp_body->abort == scope::abort_none)
continue_blks_.push_back(scp_body.get());
can_break_ = false;
can_continue_ = false;
insert_label(continue_loc);
scp_iter->init(continue_blks_);
emit_stmt(*stm.iter, *scp_iter, false);
emit_opcode(opcode::OP_jumpback, begin_loc);
insert_label(break_loc);
if (const_cond)
scp.init(break_blks_);
can_break_ = old_break;
can_continue_ = old_continue;
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
auto compiler::emit_stmt_foreach(stmt_foreach const& stm, scope& scp) -> void
{
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
auto old_break = can_break_;
auto old_continue = can_continue_;
break_blks_.clear();
continue_blks_.clear();
can_break_ = false;
can_continue_ = false;
auto break_loc = create_label();
auto continue_loc = create_label();
emit_expr(*stm.container, scp);
emit_expr_variable_ref(*stm.array, scp, true);
emit_expr_variable(*stm.array, scp);
if (ctx_->props() & props::farcall)
emit_opcode(opcode::OP_CallBuiltin, { "getfirstarraykey"s, "1"s });
else
emit_opcode(opcode::OP_CallBuiltin1, "getfirstarraykey");
emit_expr_variable_ref(*stm.key, scp, true);
if (ctx_->props() & props::foreach && stm.use_key)
{
emit_opcode(opcode::OP_GetUndefined);
emit_expr_variable_ref(*stm.index, scp, true);
}
auto& scp_body = scopes_.at(stm.body.get());
auto& scp_iter = scopes_.at(stm.key.get());
scp.transfer(scp_body);
scp_body->loc_break = break_loc;
scp_body->loc_cont = continue_loc;
emit_create_local_vars(*scp_body);
scp.init(scp_body);
scp.transfer(scp_iter);
auto begin_loc = insert_label();
emit_expr_variable(*stm.key, scp);
if (ctx_->props() & props::boolfuncs)
emit_opcode(opcode::OP_IsDefined);
else
emit_opcode(opcode::OP_CallBuiltin1, "isdefined");
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
can_break_ = true;
can_continue_ = true;
emit_expr_variable(*stm.key, *scp_body);
emit_opcode(opcode::OP_EvalLocalArrayCached, std::format("{}", variable_access(stm.array->as<expr_identifier>(), *scp_body)));
emit_expr_variable_ref(*stm.value, *scp_body, true);
if (ctx_->props() & props::foreach && stm.use_key)
{
emit_expr_variable(*stm.key, *scp_body);
emit_expr_variable_ref(*stm.index, *scp_body, true);
}
emit_stmt(*stm.body, *scp_body, false);
if (scp_body->abort == scope::abort_none)
continue_blks_.push_back(scp_body.get());
can_break_ = false;
can_continue_ = false;
insert_label(continue_loc);
scp_iter->init(continue_blks_);
emit_expr_variable(*stm.key, *scp_iter);
emit_expr_variable(*stm.array, *scp_iter);
if (ctx_->props() & props::farcall)
emit_opcode(opcode::OP_CallBuiltin, { "getnextarraykey"s, "2"s });
else
emit_opcode(opcode::OP_CallBuiltin2, "getnextarraykey");
emit_expr_variable_ref(*stm.key, *scp_iter, true);
emit_opcode(opcode::OP_jumpback, begin_loc);
insert_label(break_loc);
emit_expr_clear_local(stm.array->as<expr_identifier>(), scp);
if (ctx_->props() & props::foreach || !stm.use_key) emit_expr_clear_local(stm.key->as<expr_identifier>(), scp);
can_break_ = old_break;
can_continue_ = old_continue;
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
auto compiler::emit_stmt_switch(stmt_switch const& stm, scope& scp) -> void
{
auto old_breaks = break_blks_;
auto old_break = can_break_;
break_blks_.clear();
can_break_ = false;
auto table_loc = create_label();
auto break_loc = create_label();
emit_expr(*stm.test, scp);
emit_opcode(opcode::OP_switch, table_loc);
can_break_ = true;
auto data = std::vector<std::string>{};
data.push_back(std::format("{}", stm.body->block->list.size()));
auto type = switch_type::none;
auto loc_default = std::string{};
auto has_default = false;
scope* default_ctx = nullptr;
for (auto i = 0u; i < stm.body->block->list.size(); i++)
{
auto const& entry = stm.body->block->list[i];
if (entry->is<stmt_case>())
{
data.push_back("case");
if (entry->as<stmt_case>().value->is<expr_integer>())
{
if (ctx_->engine() == engine::iw9)
{
data.push_back(std::format("{}", static_cast<std::underlying_type_t<switch_type>>(switch_type::integer)));
}
else
{
if (type == switch_type::string)
{
throw comp_error(entry->loc(), "switch cases with different types");
}
type = switch_type::integer;
}
data.push_back(entry->as<stmt_case>().value->as<expr_integer>().value);
data.push_back(insert_label());
}
else if (entry->as<stmt_case>().value->is<expr_string>())
{
if (ctx_->engine() == engine::iw9)
{
data.push_back(std::format("{}", static_cast<std::underlying_type_t<switch_type>>(switch_type::string)));
}
else
{
if (type == switch_type::integer)
{
throw comp_error(entry->loc(), "switch cases with different types");
}
type = switch_type::string;
}
data.push_back(entry->as<stmt_case>().value->as<expr_string>().value);
data.push_back(insert_label());
}
else
{
throw comp_error(entry->loc(), "case type must be int or string");
}
auto& scp_body = scopes_.at(entry->as<stmt_case>().body.get());
scp.transfer(scp_body);
scp_body->loc_break = break_loc;
emit_stmt_list(*entry->as<stmt_case>().body, *scp_body, false);
if (entry->as<stmt_case>().body->list.size() > 0)
emit_remove_local_vars(*scp_body);
}
else if (entry->is<stmt_default>())
{
loc_default = insert_label();
has_default = true;
auto& scp_body = scopes_.at(entry->as<stmt_default>().body.get());
default_ctx = scp_body.get();
scp.transfer(scp_body);
scp_body->loc_break = break_loc;
emit_stmt_list(*entry->as<stmt_default>().body, *scp_body, false);
if (entry->as<stmt_default>().body->list.size() > 0)
emit_remove_local_vars(*scp_body);
}
else
{
throw comp_error(entry->loc(), "missing case statement");
}
}
if (has_default)
{
data.push_back("default");
data.push_back(loc_default);
if (default_ctx->abort == scope::abort_none)
break_blks_.push_back(default_ctx);
scp.init(break_blks_);
}
data.push_back(std::format("{}", static_cast<std::underlying_type_t<switch_type>>(type)));
insert_label(table_loc);
emit_opcode(opcode::OP_endswitch, data);
auto offset = static_cast<u32>(((ctx_->engine() == engine::iw9) ? 8 : 7) * stm.body->block->list.size());
function_->instructions.back()->size += offset;
index_ += offset;
insert_label(break_loc);
can_break_ = old_break;
break_blks_ = old_breaks;
}
auto compiler::emit_stmt_case(stmt_case const& stm, scope&) -> void
{
throw comp_error(stm.loc(), "illegal case statement");
}
auto compiler::emit_stmt_default(stmt_default const& stm, scope&) -> void
{
throw comp_error(stm.loc(), "illegal default statement");
}
auto compiler::emit_stmt_break(stmt_break const& stm, scope& scp) -> void
{
if (!can_break_ || scp.abort != scope::abort_none || scp.loc_break == "")
throw comp_error(stm.loc(), "illegal break statement");
break_blks_.push_back(&scp);
emit_remove_local_vars(scp);
scp.abort = scope::abort_break;
emit_opcode(opcode::OP_jump, scp.loc_break);
}
auto compiler::emit_stmt_continue(stmt_continue const& stm, scope& scp) -> void
{
if (!can_continue_ || scp.abort != scope::abort_none || scp.loc_cont == "")
throw comp_error(stm.loc(), "illegal continue statement");
continue_blks_.push_back(&scp);
emit_remove_local_vars(scp);
scp.abort = scope::abort_continue;
emit_opcode(opcode::OP_jump, scp.loc_cont);
}
auto compiler::emit_stmt_return(stmt_return const& stm, scope& scp) -> void
{
if (scp.abort == scope::abort_none)
scp.abort = scope::abort_return;
if (!stm.value->is<expr_empty>())
{
emit_expr(*stm.value, scp);
emit_opcode(opcode::OP_Return);
}
else
emit_opcode(opcode::OP_End);
}
auto compiler::emit_stmt_breakpoint(stmt_breakpoint const&, scope&) -> void
{
// TODO:
}
auto compiler::emit_stmt_prof_begin(stmt_prof_begin const&, scope&) -> void
{
// TODO:
}
auto compiler::emit_stmt_prof_end(stmt_prof_end const&, scope&) -> void
{
// TODO:
}
auto compiler::emit_stmt_assert(stmt_assert const&, scope&) -> void
{
// TODO:
}
auto compiler::emit_stmt_assertex(stmt_assertex const&, scope&) -> void
{
// TODO:
}
auto compiler::emit_stmt_assertmsg(stmt_assertmsg const&, scope&) -> void
{
// TODO:
}
auto compiler::emit_expr(expr const& exp, scope& scp) -> void
{
debug_pos_ = { exp.loc().begin.line, exp.loc().begin.column };
switch (exp.kind())
{
case node::expr_paren:
emit_expr(*exp.as<expr_paren>().value, scp);
break;
case node::expr_ternary:
emit_expr_ternary(exp.as<expr_ternary>(), scp);
break;
case node::expr_binary:
emit_expr_binary(exp.as<expr_binary>(), scp);
break;
case node::expr_complement:
emit_expr_complement(exp.as<expr_complement>(), scp);
break;
case node::expr_negate:
emit_expr_negate(exp.as<expr_negate>(), scp);
break;
case node::expr_not:
emit_expr_not(exp.as<expr_not>(), scp);
break;
case node::expr_call:
emit_expr_call(exp.as<expr_call>(), scp, false);
break;
case node::expr_method:
emit_expr_method(exp.as<expr_method>(), scp, false);
break;
case node::expr_isdefined:
emit_expr_isdefined(exp.as<expr_isdefined>(), scp);
break;
case node::expr_istrue:
emit_expr_istrue(exp.as<expr_istrue>(), scp);
break;
case node::expr_reference:
emit_expr_reference(exp.as<expr_reference>(), scp);
break;
case node::expr_add_array:
emit_expr_add_array(exp.as<expr_add_array>(), scp);
break;
case node::expr_array:
emit_expr_array(exp.as<expr_array>(), scp);
break;
case node::expr_field:
emit_expr_field(exp.as<expr_field>(), scp);
break;
case node::expr_size:
emit_expr_size(exp.as<expr_size>(), scp);
break;
case node::expr_thisthread:
emit_opcode(opcode::OP_GetThisthread);
break;
case node::expr_empty_array:
emit_opcode(opcode::OP_EmptyArray);
break;
case node::expr_undefined:
emit_opcode(opcode::OP_GetUndefined);
break;
case node::expr_game:
emit_opcode(opcode::OP_GetGame);
break;
case node::expr_self:
emit_opcode(opcode::OP_GetSelf);
break;
case node::expr_anim:
emit_opcode(opcode::OP_GetAnim);
break;
case node::expr_level:
emit_opcode(opcode::OP_GetLevel);
break;
case node::expr_animation:
emit_expr_animation(exp.as<expr_animation>());
break;
case node::expr_animtree:
emit_expr_animtree(exp.as<expr_animtree>());
break;
case node::expr_identifier:
emit_expr_local(exp.as<expr_identifier>(), scp);
break;
case node::expr_istring:
emit_expr_istring(exp.as<expr_istring>());
break;
case node::expr_string:
emit_expr_string(exp.as<expr_string>());
break;
case node::expr_vector:
emit_expr_vector(exp.as<expr_vector>(), scp);
break;
case node::expr_float:
emit_expr_float(exp.as<expr_float>());
break;
case node::expr_integer:
emit_expr_integer(exp.as<expr_integer>());
break;
case node::expr_false:
emit_expr_false(exp.as<expr_false>());
break;
case node::expr_true:
emit_expr_true(exp.as<expr_true>());
break;
case node::expr_empty:
break;
default:
throw comp_error(exp.loc(), "unknown expression");
}
}
auto compiler::emit_expr_assign(expr_assign const& exp, scope& scp) -> void
{
if (exp.oper == expr_assign::op::eq)
{
if (exp.rvalue->is<expr_undefined>())
{
emit_expr_clear(*exp.lvalue, scp);
}
else if (exp.lvalue->is<expr_tuple>())
{
emit_expr(*exp.rvalue, scp);
emit_expr_tuple(exp.lvalue->as<expr_tuple>(), scp);
}
else
{
emit_expr(*exp.rvalue, scp);
emit_expr_variable_ref(*exp.lvalue, scp, true);
}
return;
}
emit_expr(*exp.lvalue, scp);
emit_expr(*exp.rvalue, scp);
switch (exp.oper)
{
case expr_assign::op::add:
emit_opcode(opcode::OP_plus);
break;
case expr_assign::op::sub:
emit_opcode(opcode::OP_minus);
break;
case expr_assign::op::mul:
emit_opcode(opcode::OP_multiply);
break;
case expr_assign::op::div:
emit_opcode(opcode::OP_divide);
break;
case expr_assign::op::mod:
emit_opcode(opcode::OP_mod);
break;
case expr_assign::op::shl:
emit_opcode(opcode::OP_shift_left);
break;
case expr_assign::op::shr:
emit_opcode(opcode::OP_shift_right);
break;
case expr_assign::op::bwor:
emit_opcode(opcode::OP_bit_or);
break;
case expr_assign::op::bwand:
emit_opcode(opcode::OP_bit_and);
break;
case expr_assign::op::bwexor:
emit_opcode(opcode::OP_bit_ex_or);
break;
default:
throw comp_error(exp.loc(), "unknown assign operation");
}
emit_expr_variable_ref(*exp.lvalue, scp, true);
}
auto compiler::emit_expr_clear(expr const& exp, scope& scp) -> void
{
switch (exp.kind())
{
case node::expr_array:
emit_expr(*exp.as<expr_array>().key, scp);
exp.as<expr_array>().obj->is<expr_game>() ? emit_opcode(opcode::OP_GetGameRef) : emit_expr_variable_ref(*exp.as<expr_array>().obj, scp, false);
emit_opcode(opcode::OP_ClearArray);
break;
case node::expr_field:
emit_expr_object(*exp.as<expr_field>().obj, scp);
emit_opcode(opcode::OP_ClearFieldVariable, exp.as<expr_field>().field->value);
break;
case node::expr_identifier:
emit_opcode(opcode::OP_GetUndefined);
emit_expr_local_ref(exp.as<expr_identifier>(), scp, true);
break;
default:
throw comp_error(exp.loc(), "unknown clear variable lvalue");
}
}
auto compiler::emit_expr_clear_local(expr_identifier const& exp, scope& scp) -> void
{
auto index = variable_access(exp, scp);
if (index == 0)
emit_opcode(opcode::OP_ClearLocalVariableFieldCached0);
else
emit_opcode(opcode::OP_ClearLocalVariableFieldCached, std::format("{}", index));
}
auto compiler::emit_expr_increment(expr_increment const& exp, scope& scp, bool is_stmt) -> void
{
if (is_stmt)
{
emit_expr_variable_ref(*exp.lvalue, scp, false);
emit_opcode(opcode::OP_inc);
emit_opcode(opcode::OP_SetVariableField);
}
else
{
// TODO:
}
}
auto compiler::emit_expr_decrement(expr_decrement const& exp, scope& scp, bool is_stmt) -> void
{
if (is_stmt)
{
emit_expr_variable_ref(*exp.lvalue, scp, false);
emit_opcode(opcode::OP_dec);
emit_opcode(opcode::OP_SetVariableField);
}
else
{
// TODO:
}
}
auto compiler::emit_expr_ternary(expr_ternary const& exp, scope& scp) -> void
{
auto else_loc = create_label();
auto end_loc = create_label();
if (exp.test->is<expr_not>())
{
emit_expr(*exp.test->as<expr_not>().rvalue, scp);
emit_opcode(opcode::OP_JumpOnTrue, else_loc);
}
else
{
emit_expr(*exp.test, scp);
emit_opcode(opcode::OP_JumpOnFalse, else_loc);
}
emit_expr(*exp.true_expr, scp);
emit_opcode(opcode::OP_jump, end_loc);
insert_label(else_loc);
emit_expr(*exp.false_expr, scp);
insert_label(end_loc);
}
auto compiler::emit_expr_binary(expr_binary const& exp, scope& scp) -> void
{
if (exp.oper == expr_binary::op::bool_and)
{
auto label = create_label();
emit_expr(*exp.lvalue, scp);
emit_opcode(opcode::OP_JumpOnFalseExpr, label);
if (exp.rvalue->is<expr_not>() && (ctx_->props() & props::boolnotand))
{
emit_expr(*exp.rvalue->as<expr_not>().rvalue, scp);
emit_opcode(opcode::OP_BoolNotAfterAnd);
}
else
{
emit_expr(*exp.rvalue, scp);
emit_opcode(opcode::OP_CastBool);
}
insert_label(label);
}
else if (exp.oper == expr_binary::op::bool_or)
{
auto label = create_label();
emit_expr(*exp.lvalue, scp);
emit_opcode(opcode::OP_JumpOnTrueExpr, label);
if (exp.rvalue->is<expr_not>() && (ctx_->props() & props::boolnotand))
{
emit_expr(*exp.rvalue->as<expr_not>().rvalue, scp);
emit_opcode(opcode::OP_BoolNotAfterAnd);
}
else
{
emit_expr(*exp.rvalue, scp);
emit_opcode(opcode::OP_CastBool);
}
insert_label(label);
}
else
{
emit_expr(*exp.lvalue, scp);
emit_expr(*exp.rvalue, scp);
switch (exp.oper)
{
case expr_binary::op::eq:
emit_opcode(opcode::OP_equality);
break;
case expr_binary::op::ne:
emit_opcode(opcode::OP_inequality);
break;
case expr_binary::op::lt:
emit_opcode(opcode::OP_less);
break;
case expr_binary::op::gt:
emit_opcode(opcode::OP_greater);
break;
case expr_binary::op::le:
emit_opcode(opcode::OP_less_equal);
break;
case expr_binary::op::ge:
emit_opcode(opcode::OP_greater_equal);
break;
case expr_binary::op::bwor:
emit_opcode(opcode::OP_bit_or);
break;
case expr_binary::op::bwand:
emit_opcode(opcode::OP_bit_and);
break;
case expr_binary::op::bwexor:
emit_opcode(opcode::OP_bit_ex_or);
break;
case expr_binary::op::shl:
emit_opcode(opcode::OP_shift_left);
break;
case expr_binary::op::shr:
emit_opcode(opcode::OP_shift_right);
break;
case expr_binary::op::add:
emit_opcode(opcode::OP_plus);
break;
case expr_binary::op::sub:
emit_opcode(opcode::OP_minus);
break;
case expr_binary::op::mul:
emit_opcode(opcode::OP_multiply);
break;
case expr_binary::op::div:
emit_opcode(opcode::OP_divide);
break;
case expr_binary::op::mod:
emit_opcode(opcode::OP_mod);
break;
default:
throw comp_error(exp.loc(), "unknown binary expression");
}
}
}
auto compiler::emit_expr_complement(expr_complement const& exp, scope& scp) -> void
{
emit_expr(*exp.rvalue, scp);
emit_opcode(opcode::OP_BoolComplement);
}
auto compiler::emit_expr_negate(expr_negate const& exp, scope& scp) -> void
{
emit_opcode(opcode::OP_GetZero);
emit_expr(*exp.rvalue, scp);
emit_opcode(opcode::OP_minus);
}
auto compiler::emit_expr_not(expr_not const& exp, scope& scp) -> void
{
emit_expr(*exp.rvalue, scp);
emit_opcode(opcode::OP_BoolNot);
}
auto compiler::emit_expr_call(expr_call const& exp, scope& scp, bool is_stmt) -> void
{
if (exp.value->is<expr_pointer>())
emit_expr_call_pointer(exp.value->as<expr_pointer>(), scp, is_stmt);
else if (exp.value->is<expr_function>())
emit_expr_call_function(exp.value->as<expr_function>(), scp, is_stmt);
else
throw comp_error(exp.loc(), "unknown function call expression");
}
auto compiler::emit_expr_call_pointer(expr_pointer const& exp, scope& scp, bool is_stmt) -> void
{
if (exp.mode == call::mode::normal)
emit_opcode(opcode::OP_PreScriptCall);
emit_expr_arguments(*exp.args, scp);
emit_expr(*exp.func, scp);
auto argcount = std::format("{}", exp.args->list.size());
switch (exp.mode)
{
case call::mode::normal:
emit_opcode(opcode::OP_ScriptFunctionCallPointer);
break;
case call::mode::thread:
emit_opcode(opcode::OP_ScriptThreadCallPointer, argcount);
break;
case call::mode::childthread:
emit_opcode(opcode::OP_ScriptChildThreadCallPointer, argcount);
break;
case call::mode::builtin:
emit_opcode(opcode::OP_CallBuiltinPointer, argcount);
break;
}
if (is_stmt)
emit_opcode(opcode::OP_DecTop);
}
auto compiler::emit_expr_call_function(expr_function const& exp, scope& scp, bool is_stmt) -> void
{
auto path = std::string{};
auto type = resolve_function_type(exp, path);
if (ctx_->props() & props::farcall && type == call::type::local)
{
type = call::type::far;
}
if (type != call::type::builtin && exp.mode == call::mode::normal && (ctx_->props() & props::farcall || exp.args->list.size() > 0))
emit_opcode(opcode::OP_PreScriptCall);
emit_expr_arguments(*exp.args, scp);
auto argcount = std::format("{}", exp.args->list.size());
if (type == call::type::local)
{
switch (exp.mode)
{
case call::mode::normal:
if (exp.args->list.size() > 0)
emit_opcode(opcode::OP_ScriptLocalFunctionCall, exp.name->value);
else
emit_opcode(opcode::OP_ScriptLocalFunctionCall2, exp.name->value);
break;
case call::mode::thread:
emit_opcode(opcode::OP_ScriptLocalThreadCall, { exp.name->value, argcount });
break;
case call::mode::childthread:
emit_opcode(opcode::OP_ScriptLocalChildThreadCall, { exp.name->value, argcount });
break;
case call::mode::builtin:
// no local builtins
break;
}
}
else if (type == call::type::far)
{
switch (exp.mode)
{
case call::mode::normal:
if (!(ctx_->props() & props::farcall) && exp.args->list.size() == 0)
emit_opcode(opcode::OP_ScriptFarFunctionCall2, { path, exp.name->value });
else
emit_opcode(opcode::OP_ScriptFarFunctionCall, { path, exp.name->value });
break;
case call::mode::thread:
emit_opcode(opcode::OP_ScriptFarThreadCall, { path, exp.name->value, argcount });
break;
case call::mode::childthread:
emit_opcode(opcode::OP_ScriptFarChildThreadCall, { path, exp.name->value, argcount });
break;
case call::mode::builtin:
// no far builtins
break;
}
}
else if (type == call::type::builtin)
{
if (exp.mode != call::mode::normal)
throw comp_error(exp.loc(), "builtin calls can't be threaded");
if (ctx_->props() & props::farcall)
{
emit_opcode(opcode::OP_CallBuiltin, { exp.name->value, argcount });
}
else
{
switch (exp.args->list.size())
{
case 0:
emit_opcode(opcode::OP_CallBuiltin0, exp.name->value);
break;
case 1:
emit_opcode(opcode::OP_CallBuiltin1, exp.name->value);
break;
case 2:
emit_opcode(opcode::OP_CallBuiltin2, exp.name->value);
break;
case 3:
emit_opcode(opcode::OP_CallBuiltin3, exp.name->value);
break;
case 4:
emit_opcode(opcode::OP_CallBuiltin4, exp.name->value);
break;
case 5:
emit_opcode(opcode::OP_CallBuiltin5, exp.name->value);
break;
default:
emit_opcode(opcode::OP_CallBuiltin, { exp.name->value, argcount });
break;
}
}
}
if (is_stmt)
emit_opcode(opcode::OP_DecTop);
}
auto compiler::emit_expr_method(expr_method const& exp, scope& scp, bool is_stmt) -> void
{
if (exp.value->is<expr_pointer>())
emit_expr_method_pointer(exp.value->as<expr_pointer>(), *exp.obj, scp, is_stmt);
else if (exp.value->is<expr_function>())
emit_expr_method_function(exp.value->as<expr_function>(), *exp.obj, scp, is_stmt);
else
throw comp_error(exp.loc(), "unknown method call expression");
}
auto compiler::emit_expr_method_pointer(expr_pointer const& exp, expr const& obj, scope& scp, bool is_stmt) -> void
{
if (exp.mode == call::mode::normal)
emit_opcode(opcode::OP_PreScriptCall);
emit_expr_arguments(*exp.args, scp);
emit_expr(obj, scp);
emit_expr(*exp.func, scp);
auto argcount = std::format("{}", exp.args->list.size());
switch (exp.mode)
{
case call::mode::normal:
emit_opcode(opcode::OP_ScriptMethodCallPointer);
break;
case call::mode::thread:
emit_opcode(opcode::OP_ScriptMethodThreadCallPointer, argcount);
break;
case call::mode::childthread:
emit_opcode(opcode::OP_ScriptMethodChildThreadCallPointer, argcount);
break;
case call::mode::builtin:
emit_opcode(opcode::OP_CallBuiltinMethodPointer, argcount);
break;
}
if (is_stmt)
emit_opcode(opcode::OP_DecTop);
}
auto compiler::emit_expr_method_function(expr_function const& exp, expr const& obj, scope& scp, bool is_stmt) -> void
{
auto path = std::string{};
auto type = resolve_function_type(exp, path);
if (ctx_->props() & props::farcall && type == call::type::local)
{
type = call::type::far;
}
if (type != call::type::builtin && exp.mode == call::mode::normal)
emit_opcode(opcode::OP_PreScriptCall);
emit_expr_arguments(*exp.args, scp);
emit_expr(obj, scp);
auto argcount = std::format("{}", exp.args->list.size());
if (type == call::type::local)
{
switch (exp.mode)
{
case call::mode::normal:
emit_opcode(opcode::OP_ScriptLocalMethodCall, exp.name->value);
break;
case call::mode::thread:
emit_opcode(opcode::OP_ScriptLocalMethodThreadCall, { exp.name->value, argcount });
break;
case call::mode::childthread:
emit_opcode(opcode::OP_ScriptLocalMethodChildThreadCall, { exp.name->value, argcount });
break;
case call::mode::builtin:
// no local builtins
break;
}
}
else if (type == call::type::far)
{
switch (exp.mode)
{
case call::mode::normal:
emit_opcode(opcode::OP_ScriptFarMethodCall, { path, exp.name->value });
break;
case call::mode::thread:
emit_opcode(opcode::OP_ScriptFarMethodThreadCall, { path, exp.name->value, argcount });
break;
case call::mode::childthread:
emit_opcode(opcode::OP_ScriptFarMethodChildThreadCall, { path, exp.name->value, argcount });
break;
case call::mode::builtin:
// no far builtins
break;
}
}
else if (type == call::type::builtin)
{
if (exp.mode != call::mode::normal)
throw comp_error(exp.loc(), "builtin calls can't be threaded");
if (ctx_->props() & props::farcall)
{
emit_opcode(opcode::OP_CallBuiltinMethod, { exp.name->value, argcount });
}
else
{
switch (exp.args->list.size())
{
case 0:
emit_opcode(opcode::OP_CallBuiltinMethod0, exp.name->value);
break;
case 1:
emit_opcode(opcode::OP_CallBuiltinMethod1, exp.name->value);
break;
case 2:
emit_opcode(opcode::OP_CallBuiltinMethod2, exp.name->value);
break;
case 3:
emit_opcode(opcode::OP_CallBuiltinMethod3, exp.name->value);
break;
case 4:
emit_opcode(opcode::OP_CallBuiltinMethod4, exp.name->value);
break;
case 5:
emit_opcode(opcode::OP_CallBuiltinMethod5, exp.name->value);
break;
default:
emit_opcode(opcode::OP_CallBuiltinMethod, { exp.name->value, argcount });
break;
}
}
}
if (is_stmt)
emit_opcode(opcode::OP_DecTop);
}
auto compiler::emit_expr_add_array(expr_add_array const& exp, scope& scp) -> void
{
emit_opcode(opcode::OP_EmptyArray);
for (auto const& arg : exp.args->list)
{
emit_expr(*arg, scp);
emit_opcode(opcode::OP_AddArray);
}
}
auto compiler::emit_expr_parameters(expr_parameters const& exp, scope& scp) -> void
{
if (ctx_->props() & props::params)
{
auto num = static_cast<u32>(exp.list.size());
if (num)
{
auto data = std::vector<std::string>{};
auto size = (ctx_->props() & props::hash) ? num * 8 : num;
data.push_back(std::format("{}", num));
for (auto const& entry : exp.list)
{
auto index = variable_initialize(*entry, scp);
data.push_back((ctx_->props() & props::hash) ? entry->value : std::format("{}", index));
}
emit_opcode(opcode::OP_FormalParams, data);
function_->instructions.back()->size += size;
index_ += size;
}
else
{
emit_opcode( opcode::OP_checkclearparams);
}
}
else
{
for (auto const& entry : exp.list)
{
emit_opcode(opcode::OP_SafeCreateVariableFieldCached, std::format("{}", variable_initialize(*entry, scp)));
}
emit_opcode(opcode::OP_checkclearparams);
}
}
auto compiler::emit_expr_arguments(expr_arguments const& exp, scope& scp) -> void
{
for (auto it = exp.list.rbegin(); it != exp.list.rend(); ++it)
{
emit_expr(**it, scp);
}
}
auto compiler::emit_expr_isdefined(expr_isdefined const& exp, scope& scp) -> void
{
emit_expr(*exp.value, scp);
emit_opcode(opcode::OP_IsDefined);
}
auto compiler::emit_expr_istrue(expr_istrue const& exp, scope& scp) -> void
{
emit_expr(*exp.value, scp);
emit_opcode(opcode::OP_IsTrue);
}
auto compiler::emit_expr_reference(expr_reference const& exp, scope&) -> void
{
bool method = false;
auto path = std::string{};
auto type = resolve_reference_type(exp, path, method);
if (ctx_->props() & props::farcall && type == call::type::local)
{
type = call::type::far;
}
switch (type)
{
case call::type::local:
emit_opcode(opcode::OP_GetLocalFunction, exp.name->value);
break;
case call::type::far:
emit_opcode(opcode::OP_GetFarFunction, { path, exp.name->value });
break;
case call::type::builtin:
if (method)
emit_opcode(opcode::OP_GetBuiltinMethod, exp.name->value);
else
emit_opcode(opcode::OP_GetBuiltinFunction, exp.name->value);
break;
}
}
auto compiler::emit_expr_size(expr_size const& exp, scope& scp) -> void
{
emit_expr(*exp.obj, scp);
emit_opcode(opcode::OP_size);
}
auto compiler::emit_expr_tuple(expr_tuple const& exp, scope& scp) -> void
{
emit_expr_variable_ref(*exp.temp, scp, true);
auto index = 0u;
for (auto const& entry : exp.list)
{
if (index == 0)
emit_opcode(opcode::OP_GetZero);
else
emit_opcode(opcode::OP_GetByte, std::format("{}", index));
index++;
emit_opcode(opcode::OP_EvalLocalArrayCached, std::format("{}", variable_access(exp.temp->as<expr_identifier>(), scp)));
emit_expr_variable_ref(*entry, scp, true);
}
emit_expr_clear_local(exp.temp->as<expr_identifier>(), scp);
}
auto compiler::emit_expr_variable_ref(expr const& exp, scope& scp, bool set) -> void
{
switch (exp.kind())
{
case node::expr_array:
emit_expr_array_ref(exp.as<expr_array>(), scp, set);
break;
case node::expr_field:
emit_expr_field_ref(exp.as<expr_field>(), scp, set);
break;
case node::expr_identifier:
emit_expr_local_ref(exp.as<expr_identifier>(), scp, set);
break;
default:
throw comp_error(exp.loc(), "invalid lvalue");
}
}
auto compiler::emit_expr_array_ref(expr_array const& exp, scope& scp, bool set) -> void
{
emit_expr(*exp.key, scp);
switch (exp.obj->kind())
{
case node::expr_game:
emit_opcode(opcode::OP_GetGameRef);
emit_opcode(opcode::OP_EvalArrayRef);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case node::expr_array:
case node::expr_field:
emit_expr_variable_ref(*exp.obj, scp, false);
emit_opcode(opcode::OP_EvalArrayRef);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case node::expr_identifier:
{
if (!variable_initialized(exp.obj->as<expr_identifier>(), scp))
{
auto index = variable_initialize(exp.obj->as<expr_identifier>(), scp);
emit_opcode(opcode::OP_EvalNewLocalArrayRefCached0, (ctx_->props() & props::hash) ? exp.obj->as<expr_identifier>().value : std::format("{}", index));
if (!set) throw comp_error(exp.loc(), "INTERNAL: VAR CREATED BUT NOT SET");
}
else
{
auto index = variable_access(exp.obj->as<expr_identifier>(), scp);
if (index == 0)
emit_opcode(opcode::OP_EvalLocalArrayRefCached0);
else
emit_opcode(opcode::OP_EvalLocalArrayRefCached, std::format("{}", index));
}
if (set) emit_opcode(opcode::OP_SetVariableField);
}
break;
case node::expr_call:
case node::expr_method:
default:
throw comp_error(exp.loc(), "invalid array lvalue");
}
}
auto compiler::emit_expr_field_ref(expr_field const& exp, scope& scp, bool set) -> void
{
auto const& field = exp.field->value;
switch (exp.obj->kind())
{
case node::expr_level:
set ? emit_opcode(opcode::OP_SetLevelFieldVariableField, field) : emit_opcode(opcode::OP_EvalLevelFieldVariableRef, field);
break;
case node::expr_anim:
set ? emit_opcode(opcode::OP_SetAnimFieldVariableField, field) : emit_opcode(opcode::OP_EvalAnimFieldVariableRef, field);
break;
case node::expr_self:
set ? emit_opcode(opcode::OP_SetSelfFieldVariableField, field) : emit_opcode(opcode::OP_EvalSelfFieldVariableRef, field);
break;
case node::expr_array:
emit_expr_array(exp.obj->as<expr_array>(), scp);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case node::expr_field:
emit_expr_field(exp.obj->as<expr_field>(), scp);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case node::expr_identifier:
emit_opcode(opcode::OP_EvalLocalVariableObjectCached, std::format("{}", variable_access(exp.obj->as<expr_identifier>(), scp)));
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case node::expr_call:
emit_expr_call(exp.obj->as<expr_call>(), scp, false);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case node::expr_method:
emit_expr_method(exp.obj->as<expr_method>(), scp, false);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
default:
throw comp_error(exp.obj->loc(), "not an object");
}
}
auto compiler::emit_expr_local_ref(expr_identifier const& exp, scope& scp, bool set) -> void
{
auto const it = constants_.find(exp.value);
if (it != constants_.end())
{
throw comp_error(exp.loc(), std::format("variable name already defined as constant '{}'", exp.value));
}
if (set)
{
if (!variable_initialized(exp, scp))
{
auto index = variable_initialize(exp, scp);
emit_opcode(opcode::OP_SetNewLocalVariableFieldCached0, (ctx_->props() & props::hash) ? exp.value : std::format("{}", index));
}
else
{
auto index = variable_access(exp, scp);
if (index == 0)
emit_opcode(opcode::OP_SetLocalVariableFieldCached0);
else
emit_opcode(opcode::OP_SetLocalVariableFieldCached, std::format("{}", index));
}
}
else
{
auto index = variable_access(exp, scp);
if (index == 0)
emit_opcode(opcode::OP_EvalLocalVariableRefCached0);
else
emit_opcode(opcode::OP_EvalLocalVariableRefCached, std::format("{}", index));
}
}
auto compiler::emit_expr_variable(expr const& exp, scope& scp) -> void
{
switch (exp.kind())
{
case node::expr_array:
emit_expr_array(exp.as<expr_array>(), scp);
break;
case node::expr_field:
emit_expr_field(exp.as<expr_field>(), scp);
break;
case node::expr_identifier:
emit_expr_local(exp.as<expr_identifier>(), scp);
break;
default:
throw comp_error(exp.loc(), "invalid variable type.");
}
}
auto compiler::emit_expr_array(expr_array const& exp, scope& scp) -> void
{
emit_expr(*exp.key, scp);
if (exp.obj->is<expr_identifier>())
{
emit_opcode(opcode::OP_EvalLocalArrayCached, std::format("{}", variable_access(exp.obj->as<expr_identifier>(), scp)));
}
else
{
emit_expr(*exp.obj, scp);
emit_opcode(opcode::OP_EvalArray);
}
}
auto compiler::emit_expr_field(expr_field const& exp, scope& scp) -> void
{
auto const& field = exp.field->value;
switch (exp.obj->kind())
{
case node::expr_level:
emit_opcode(opcode::OP_EvalLevelFieldVariable, field);
break;
case node::expr_anim:
emit_opcode(opcode::OP_EvalAnimFieldVariable, field);
break;
case node::expr_self:
emit_opcode(opcode::OP_EvalSelfFieldVariable, field);
break;
case node::expr_array:
emit_expr_array(exp.obj->as<expr_array>(), scp);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
case node::expr_field:
emit_expr_field(exp.obj->as<expr_field>(), scp);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
case node::expr_call:
emit_expr_call(exp.obj->as<expr_call>(), scp, false);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
case node::expr_method:
emit_expr_method(exp.obj->as<expr_method>(), scp, false);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
case node::expr_identifier:
emit_opcode(opcode::OP_EvalLocalVariableObjectCached, std::format("{}", variable_access(exp.obj->as<expr_identifier>(), scp)));
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
default:
throw comp_error(exp.loc(), "unknown field variable object type");
}
}
auto compiler::emit_expr_local(expr_identifier const& exp, scope& scp) -> void
{
auto const it = constants_.find(exp.value);
if (it != constants_.end())
{
// should only allow: string, loc string, number, vector
emit_expr(*it->second, scp);
return;
}
auto index = variable_access(exp, scp);
switch (index)
{
case 0:
emit_opcode(opcode::OP_EvalLocalVariableCached0);
break;
case 1:
emit_opcode(opcode::OP_EvalLocalVariableCached1);
break;
case 2:
emit_opcode(opcode::OP_EvalLocalVariableCached2);
break;
case 3:
emit_opcode(opcode::OP_EvalLocalVariableCached3);
break;
case 4:
emit_opcode(opcode::OP_EvalLocalVariableCached4);
break;
case 5:
emit_opcode(opcode::OP_EvalLocalVariableCached5);
break;
default:
emit_opcode(opcode::OP_EvalLocalVariableCached, std::format("{}", index));
break;
}
}
auto compiler::emit_expr_object(expr const& exp, scope& scp) -> void
{
switch (exp.kind())
{
case node::expr_level:
emit_opcode(opcode::OP_GetLevelObject);
break;
case node::expr_anim:
emit_opcode(opcode::OP_GetAnimObject);
break;
case node::expr_self:
emit_opcode(opcode::OP_GetSelfObject);
break;
case node::expr_array:
emit_expr_array(exp.as<expr_array>(), scp);
emit_opcode(opcode::OP_CastFieldObject);
break;
case node::expr_field:
emit_expr_field(exp.as<expr_field>(), scp);
emit_opcode(opcode::OP_CastFieldObject);
break;
case node::expr_call:
emit_expr_call(exp.as<expr_call>(), scp, false);
emit_opcode(opcode::OP_CastFieldObject);
break;
case node::expr_method:
emit_expr_method(exp.as<expr_method>(), scp, false);
emit_opcode(opcode::OP_CastFieldObject);
break;
case node::expr_identifier:
emit_opcode(opcode::OP_EvalLocalVariableObjectCached, std::format("{}", variable_access(exp.as<expr_identifier>(), scp)));
break;
default:
throw comp_error(exp.loc(), "not an object");
}
}
auto compiler::emit_expr_vector(expr_vector const& exp, scope& scp) -> void
{
auto data = std::vector<std::string>{};
auto isexpr = false;
if (exp.x->is<expr_integer>())
data.push_back(exp.x->as<expr_integer>().value);
else if (exp.x->is<expr_float>())
data.push_back(exp.x->as<expr_float>().value);
else isexpr = true;
if (exp.y->is<expr_integer>())
data.push_back(exp.y->as<expr_integer>().value);
else if (exp.y->is<expr_float>())
data.push_back(exp.y->as<expr_float>().value);
else isexpr = true;
if (exp.z->is<expr_integer>())
data.push_back(exp.z->as<expr_integer>().value);
else if (exp.z->is<expr_float>())
data.push_back(exp.z->as<expr_float>().value);
else isexpr = true;
if (!isexpr)
{
if (ctx_->endian() == endian::little)
emit_opcode(opcode::OP_GetVector, data);
else
{
auto base = index_ + 1;
auto algn = (base + 3) & ~3;
emit_opcode(opcode::OP_GetVector, data);
index_ += (algn - base);
function_->instructions.back()->size += (algn - base);
}
}
else
{
emit_expr(*exp.z, scp);
emit_expr(*exp.y, scp);
emit_expr(*exp.x, scp);
emit_opcode(opcode::OP_vector);
}
}
auto compiler::emit_expr_animation(expr_animation const& exp) -> void
{
if (animname_.empty())
{
throw comp_error(exp.loc(), "trying to use animation without specified using animtree");
}
if (animload_)
{
emit_opcode(opcode::OP_GetAnimation, { "", exp.value });
}
else
{
animload_ = true;
emit_opcode(opcode::OP_GetAnimation, { animname_, exp.value });
}
}
auto compiler::emit_expr_animtree(expr_animtree const& exp) -> void
{
if (animname_.empty())
{
throw comp_error(exp.loc(), "trying to use animtree without specified using animtree");
}
if (animload_)
{
emit_opcode(opcode::OP_GetAnimTree, "");
}
else
{
animload_ = true;
emit_opcode(opcode::OP_GetAnimTree, animname_);
}
}
auto compiler::emit_expr_istring(expr_istring const& exp) -> void
{
emit_opcode(opcode::OP_GetIString, exp.value);
}
auto compiler::emit_expr_string(expr_string const& exp) -> void
{
emit_opcode(opcode::OP_GetString, exp.value);
}
auto compiler::emit_expr_float(expr_float const& exp) -> void
{
emit_opcode(opcode::OP_GetFloat, exp.value);
}
auto compiler::emit_expr_integer(expr_integer const& exp) -> void
{
auto value = std::atoll(exp.value.data());
if (value == 0)
{
emit_opcode(opcode::OP_GetZero);
}
else if (value > 0 && value < 256)
{
emit_opcode(opcode::OP_GetByte, exp.value);
}
else if (value < 0 && value > -256)
{
emit_opcode(opcode::OP_GetNegByte, exp.value.substr(1));
}
else if (value > 0 && value < 65536)
{
emit_opcode(opcode::OP_GetUnsignedShort, exp.value);
}
else if (value < 0 && value > -65536)
{
emit_opcode(opcode::OP_GetNegUnsignedShort, exp.value.substr(1));
}
else
{
if (ctx_->engine() == engine::iw9)
{
if (value > 0 && value < 4294967296)
{
emit_opcode(opcode::OP_GetUnsignedInt, exp.value);
}
else if (value < 0 && value > -4294967296)
{
emit_opcode(opcode::OP_GetNegUnsignedInt, exp.value.substr(1));
}
else
{
emit_opcode(opcode::OP_GetInteger64, exp.value);
}
}
else
{
emit_opcode(opcode::OP_GetInteger, exp.value);
}
}
}
auto compiler::emit_expr_false(expr_false const&) -> void
{
emit_opcode(opcode::OP_GetZero);
}
auto compiler::emit_expr_true(expr_true const&) -> void
{
emit_opcode(opcode::OP_GetByte, "1");
}
auto compiler::emit_create_local_vars(scope& scp) -> void
{
if (scp.create_count != scp.public_count)
{
for (auto i = scp.create_count; i < scp.public_count; i++)
{
emit_opcode(opcode::OP_CreateLocalVariable, (ctx_->props() & props::hash) ? scp.vars[i].name : std::format("{}", scp.vars[i].create));
scp.vars[i].init = true;
}
scp.create_count = scp.public_count;
}
}
auto compiler::emit_remove_local_vars(scope& scp) -> void
{
if (scp.abort == scope::abort_none)
{
auto count = scp.create_count - scp.public_count;
if (count > 0)
{
emit_opcode(opcode::OP_RemoveLocalVariables, std::format("{}", count));
}
}
}
auto compiler::emit_opcode(opcode op) -> void
{
function_->instructions.push_back(instruction::make());
auto& inst = function_->instructions.back();
inst->opcode = op;
inst->size = ctx_->opcode_size(op);
inst->index = index_;
inst->pos = debug_pos_;
index_ += inst->size;
}
auto compiler::emit_opcode(opcode op, std::string const& data) -> void
{
function_->instructions.push_back(instruction::make());
auto& inst = function_->instructions.back();
inst->opcode = op;
inst->size = ctx_->opcode_size(op);
inst->index = index_;
inst->data.push_back(data);
inst->pos = debug_pos_;
index_ += inst->size;
}
auto compiler::emit_opcode(opcode op, std::vector<std::string> const& data) -> void
{
function_->instructions.push_back(instruction::make());
auto& inst = function_->instructions.back();
inst->opcode = op;
inst->size = ctx_->opcode_size(op);
inst->index = index_;
inst->data = data;
inst->pos = debug_pos_;
index_ += inst->size;
}
auto compiler::process_function(decl_function const& func) -> void
{
auto ins = scopes_.insert({ func.body.get(), make_scope() });
auto& scp_body = ins.first->second;
process_expr_parameters(*func.params, *scp_body);
process_stmt_comp(*func.body, *scp_body);
}
auto compiler::process_stmt(stmt const& stm, scope& scp) -> void
{
switch (stm.kind())
{
case node::stmt_list:
process_stmt_list(stm.as<stmt_list>(), scp);
break;
case node::stmt_comp:
process_stmt_comp(stm.as<stmt_comp>(), scp);
break;
case node::stmt_dev:
process_stmt_dev(stm.as<stmt_dev>(), scp);
break;
case node::stmt_expr:
process_stmt_expr(stm.as<stmt_expr>(), scp);
break;
case node::stmt_waittill:
process_stmt_waittill(stm.as<stmt_waittill>(), scp);
break;
case node::stmt_if:
process_stmt_if(stm.as<stmt_if>(), scp);
break;
case node::stmt_ifelse:
process_stmt_ifelse(stm.as<stmt_ifelse>(), scp);
break;
case node::stmt_while:
process_stmt_while(stm.as<stmt_while>(), scp);
break;
case node::stmt_dowhile:
process_stmt_dowhile(stm.as<stmt_dowhile>(), scp);
break;
case node::stmt_for:
process_stmt_for(stm.as<stmt_for>(), scp);
break;
case node::stmt_foreach:
process_stmt_foreach(stm.as<stmt_foreach>(), scp);
break;
case node::stmt_switch:
process_stmt_switch(stm.as<stmt_switch>(), scp);
break;
case node::stmt_break:
process_stmt_break(stm.as<stmt_break>(), scp);
break;
case node::stmt_continue:
process_stmt_continue(stm.as<stmt_continue>(), scp);
break;
case node::stmt_return:
process_stmt_return(stm.as<stmt_return>(), scp);
break;
case node::stmt_endon:
case node::stmt_notify:
case node::stmt_wait:
case node::stmt_waittillmatch:
case node::stmt_waittillframeend:
case node::stmt_waitframe:
case node::stmt_case:
case node::stmt_default:
case node::stmt_breakpoint:
case node::stmt_prof_begin:
case node::stmt_prof_end:
case node::stmt_assert:
case node::stmt_assertex:
case node::stmt_assertmsg:
break;
default:
throw comp_error(stm.loc(), "unknown statement");
}
}
auto compiler::process_stmt_list(stmt_list const& stm, scope& scp) -> void
{
for (auto const& entry : stm.list)
{
process_stmt(*entry, scp);
}
}
auto compiler::process_stmt_comp(stmt_comp const& stm, scope& scp) -> void
{
process_stmt_list(*stm.block, scp);
}
auto compiler::process_stmt_dev(stmt_dev const& stm, scope& scp) -> void
{
process_stmt_list(*stm.block, scp);
}
auto compiler::process_stmt_expr(stmt_expr const& stm, scope& scp) -> void
{
switch (stm.value->kind())
{
case node::expr_increment:
process_expr(*stm.value->as<expr_increment>().lvalue, scp);
break;
case node::expr_decrement:
process_expr(*stm.value->as<expr_decrement>().lvalue, scp);
break;
case node::expr_assign:
process_expr(*stm.value->as<expr_assign>().lvalue, scp);
break;
case node::expr_call:
case node::expr_method:
case node::expr_empty:
break;
default:
throw comp_error(stm.loc(), "unknown expr statement expression");
}
}
auto compiler::process_stmt_waittill(stmt_waittill const& stm, scope& scp) -> void
{
for (auto const& entry : stm.args->list)
{
if (!entry->is<expr_identifier>())
{
throw comp_error(entry->loc(), "illegal waittill param, must be a local variable");
}
variable_register(entry->as<expr_identifier>(), scp);
}
}
auto compiler::process_stmt_if(stmt_if const& stm, scope& scp) -> void
{
auto ins = scopes_.insert({ stm.body.get(), make_scope() });
auto& scp_then = ins.first->second;
scp.copy(scp_then);
process_stmt(*stm.body, *scp_then);
std::vector<scope*> childs({ scp_then.get() });
scp.merge(childs);
}
auto compiler::process_stmt_ifelse(stmt_ifelse const& stm, scope& scp) -> void
{
auto childs = std::vector<scope*>{};
auto abort = scope::abort_return;
auto ins1 = scopes_.insert({ stm.stmt_if.get(), make_scope() });
auto ins2 = scopes_.insert({ stm.stmt_else.get(), make_scope() });
auto& scp_then = ins1.first->second;
auto& scp_else = ins2.first->second;
scp.copy(scp_then);
process_stmt(*stm.stmt_if, *scp_then);
if (scp_then->abort <= scope::abort_return)
{
abort = scp_then->abort;
if (abort == scope::abort_none)
childs.push_back(scp_then.get());
}
scp.copy(scp_else);
process_stmt(*stm.stmt_else, *scp_else);
if (scp_else->abort <= abort)
{
abort = scp_else->abort;
if (abort == scope::abort_none)
childs.push_back(scp_else.get());
}
if (scp.abort == scope::abort_none)
scp.abort = abort;
scp.append(childs);
scp.merge(childs);
}
auto compiler::process_stmt_while(stmt_while const& stm, scope& scp) -> void
{
bool const_cond = is_constant_condition(*stm.test);
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
break_blks_.clear();
continue_blks_.clear();
auto ins = scopes_.insert({ stm.body.get(), make_scope() });
auto& scp_body = ins.first->second;
scp.copy(scp_body);
process_stmt(*stm.body, *scp_body);
continue_blks_.push_back(scp_body.get());
for (auto i = 0u; i < continue_blks_.size(); i++)
scp.append({ continue_blks_.at(i) });
if (const_cond) scp.append(break_blks_);
scp.merge({ scp_body.get() });
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
auto compiler::process_stmt_dowhile(stmt_dowhile const& stm, scope& scp) -> void
{
bool const_cond = is_constant_condition(*stm.test);
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
break_blks_.clear();
continue_blks_.clear();
auto ins = scopes_.insert({ stm.body.get(), make_scope() });
auto& scp_body = ins.first->second;
scp.copy(scp_body);
process_stmt(*stm.body, *scp_body);
continue_blks_.push_back(scp_body.get());
for (auto i = 0u; i < continue_blks_.size(); i++)
scp.append({ continue_blks_.at(i) });
if (const_cond) scp.append(break_blks_);
scp.merge({ scp_body.get() });
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
auto compiler::process_stmt_for(stmt_for const& stm, scope& scp) -> void
{
bool const_cond = is_constant_condition(*stm.test);
auto ins1 = scopes_.insert({ stm.body.get(), make_scope() });
auto ins2 = scopes_.insert({ stm.iter.get(), make_scope() });
auto& scp_body = ins1.first->second;
auto& scp_iter = ins2.first->second;
process_stmt(*stm.init, scp);
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
break_blks_.clear();
continue_blks_.clear();
scp.copy(scp_body);
scp.copy(scp_iter);
process_stmt(*stm.body, *scp_body);
continue_blks_.push_back(scp_body.get());
for (auto i = 0u; i < continue_blks_.size(); i++)
scp.append({ continue_blks_.at(i) });
process_stmt(*stm.iter, *scp_iter);
scp.append({ scp_iter.get() });
scp.merge({ scp_iter.get() });
if (const_cond) scp.append(break_blks_);
scp.merge({ scp_body.get() });
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
auto compiler::process_stmt_foreach(stmt_foreach const& stm, scope& scp) -> void
{
auto ins1 = scopes_.insert({ stm.body.get(), make_scope() });
auto ins2 = scopes_.insert({ stm.key.get(), make_scope() });
auto& scp_body = ins1.first->second;
auto& scp_iter = ins2.first->second;
process_expr(*stm.array, scp);
if (ctx_->props() & props::foreach)
process_expr(*stm.key, scp);
if (ctx_->props() & props::foreach && stm.use_key)
process_expr(*stm.index, scp);
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
break_blks_.clear();
continue_blks_.clear();
scp.copy(scp_body);
scp.copy(scp_iter);
process_expr(*stm.value, *scp_body);
process_stmt(*stm.body, *scp_body);
continue_blks_.push_back(scp_body.get());
for (auto i = 0u; i < continue_blks_.size(); i++)
scp.append({ continue_blks_.at(i) });
if (!(ctx_->props() & props::foreach))
process_expr(*stm.key, *scp_iter);
scp.append({ scp_iter.get() });
scp.merge({ scp_iter.get() });
scp.merge({ scp_body.get() });
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
auto compiler::process_stmt_switch(stmt_switch const& stm, scope& scp) -> void
{
auto childs = std::vector<scope*>{};
auto abort = scope::abort_return;
auto has_default = false;
scope* default_ctx = nullptr;
auto old_breaks = break_blks_;
break_blks_.clear();
for (auto i = 0u; i < stm.body->block->list.size(); i++)
{
auto& entry = stm.body->block->list[i];
if (entry->is<stmt_case>())
{
auto ins = scopes_.insert({ entry->as<stmt_case>().body.get(), make_scope() });
auto& scp_body = ins.first->second;
scp.copy(scp_body);
process_stmt_list(*entry->as<stmt_case>().body, *scp_body);
if (scp_body->abort != scope::abort_none)
{
if (scp_body->abort == scope::abort_break )
{
scp_body->abort = scope::abort_none;
abort = scope::abort_none;
childs.push_back(scp_body.get());
}
else if (scp_body->abort <= abort )
{
abort = scp_body->abort;
}
}
}
else if (entry->is<stmt_default>())
{
auto ins = scopes_.insert({ entry->as<stmt_default>().body.get(), make_scope() });
auto& scp_body = ins.first->second;
scp.copy(scp_body);
process_stmt_list(*entry->as<stmt_default>().body, *scp_body);
has_default = true;
default_ctx = scp_body.get();
if (scp_body->abort != scope::abort_none)
{
if (scp_body->abort == scope::abort_break )
{
scp_body->abort = scope::abort_none;
abort = scope::abort_none;
childs.push_back(scp_body.get());
}
else if (scp_body->abort <= abort )
{
abort = scp_body->abort;
}
}
}
}
if (has_default)
{
if (default_ctx->abort == scope::abort_none)
{
break_blks_.push_back(default_ctx);
if (scp.abort == scope::abort_none)
scp.abort = abort;
}
scp.append(break_blks_);
scp.merge(childs);
}
break_blks_ = old_breaks;
}
auto compiler::process_stmt_break(stmt_break const&, scope& scp) -> void
{
if (scp.abort == scope::abort_none)
{
break_blks_.push_back(&scp);
scp.abort = scope::abort_break;
}
}
auto compiler::process_stmt_continue(stmt_continue const&, scope& scp) -> void
{
if (scp.abort == scope::abort_none)
{
continue_blks_.push_back(&scp);
scp.abort = scope::abort_continue;
}
}
auto compiler::process_stmt_return(stmt_return const&, scope& scp) -> void
{
if (scp.abort == scope::abort_none)
{
scp.abort = scope::abort_return;
}
}
auto compiler::process_expr(expr const& exp, scope& scp) -> void
{
if (exp.is<expr_identifier>())
{
variable_register(exp.as<expr_identifier>(), scp);
}
else if (exp.is<expr_array>())
{
process_expr(*exp.as<expr_array>().obj, scp);
}
else if (exp.is<expr_tuple>())
{
process_expr_tuple(exp.as<expr_tuple>(), scp);
}
}
auto compiler::process_expr_tuple(expr_tuple const& exp, scope& scp) -> void
{
process_expr(*exp.temp, scp);
for (auto const& entry : exp.list)
{
process_expr(*entry, scp);
}
}
auto compiler::process_expr_parameters(expr_parameters const& exp, scope& scp) -> void
{
for (auto const& entry : exp.list)
{
variable_register(*entry, scp);
}
}
auto compiler::variable_register(expr_identifier const& exp, scope& scp) -> void
{
auto it = std::find_if(scp.vars.begin(), scp.vars.end(), [&](scope::var const& v) { return v.name == exp.value; });
if (it == scp.vars.end())
{
auto found = false;
for (auto i = 0u; i < stackframe_.size(); i++)
{
if (stackframe_[i] == exp.value)
{
scp.vars.push_back({ exp.value, static_cast<u8>(i), false });
found = true;
break;
}
}
if (!found)
{
scp.vars.push_back({ exp.value, static_cast<u8>(stackframe_.size()), false });
stackframe_.push_back(exp.value);
}
}
}
auto compiler::variable_initialized(expr_identifier const& exp, scope& scp) -> bool
{
for (auto i = 0u; i < scp.vars.size(); i++)
{
if (scp.vars[i].name == exp.value)
{
return scp.vars[i].init;
}
}
throw comp_error(exp.loc(), std::format("local variable '{}' not found", exp.value));
}
auto compiler::variable_initialize(expr_identifier const& exp, scope& scp) -> u8
{
for (auto i = 0u; i < scp.vars.size(); i++)
{
if (scp.vars[i].name == exp.value)
{
if (!scp.vars[i].init)
{
for (auto j = 0u; j < i; j++)
{
if (!scp.vars[j].init)
{
scp.vars[j].init = true;
emit_opcode(opcode::OP_CreateLocalVariable, (ctx_->props() & props::hash) ? scp.vars[j].name : std::format("{}", scp.vars[j].create));
}
}
scp.vars[i].init = true;
scp.create_count = i + 1;
return scp.vars[i].create;
}
}
}
throw comp_error(exp.loc(), std::format("local variable '{}' not found", exp.value));
}
auto compiler::variable_create(expr_identifier const& exp, scope& scp) -> u8
{
for (auto i = 0u; i < scp.vars.size(); i++)
{
auto& var = scp.vars[i];
if (var.name == exp.value)
{
if (!var.init)
{
emit_opcode(opcode::OP_CreateLocalVariable, (ctx_->props() & props::hash) ? var.name : std::format("{}", var.create));
var.init = true;
scp.create_count++;
}
return static_cast<u8>(scp.create_count - 1 - i);
}
}
throw comp_error(exp.loc(), std::format("local variable '{}' not found", exp.value));
}
auto compiler::variable_access(expr_identifier const& exp, scope& scp) -> u8
{
for (auto i = 0u; i < scp.vars.size(); i++)
{
if (scp.vars[i].name == exp.value)
{
if (scp.vars[i].init)
{
return static_cast<u8>(scp.create_count - 1 - i);
}
throw comp_error(exp.loc(), std::format("local variable '{}' not initialized", exp.value));
}
}
throw comp_error(exp.loc(), std::format("local variable '{}' not found", exp.value));
}
auto compiler::resolve_function_type(expr_function const& exp, std::string& path) -> call::type
{
if (!exp.path->value.empty())
{
path = exp.path->value;
return call::type::far;
}
auto& name = exp.name->value;
if (ctx_->func_exists(name) || ctx_->meth_exists(name))
return call::type::builtin;
for (auto const& entry : localfuncs_)
{
if (entry == name)
return call::type::local;
}
if (ctx_->is_includecall(name, path))
return call::type::far;
throw comp_error(exp.loc(), "couldn't determine function call type");
}
auto compiler::resolve_reference_type(expr_reference const& exp, std::string& path, bool& method) -> call::type
{
if (!exp.path->value.empty())
{
path = exp.path->value;
return call::type::far;
}
auto& name = exp.name->value;
if (ctx_->func_exists(name))
{
method = false;
return call::type::builtin;
}
if (ctx_->meth_exists(name))
{
method = true;
return call::type::builtin;
}
for (auto const& entry : localfuncs_)
{
if (entry == name)
return call::type::local;
}
if (ctx_->is_includecall(name, path))
return call::type::far;
throw comp_error(exp.loc(), "couldn't determine function reference type");
}
auto compiler::is_constant_condition(expr const& exp) -> bool
{
switch (exp.kind())
{
case node::expr_empty:
case node::expr_true:
return true;
case node::expr_false:
throw comp_error(exp.loc(), "condition can't be always false");
case node::expr_integer:
{
auto num = std::stoi(exp.as<expr_integer>().value);
if (num != 0)
return true;
else
throw comp_error(exp.loc(), "condition can't be always false");
}
default:
break;
}
return false;
}
auto compiler::insert_label(std::string const& name) -> void
{
auto const itr = function_->labels.find(index_);
if (itr != function_->labels.end())
{
for (auto& inst : function_->instructions)
{
switch (inst->opcode)
{
case opcode::OP_JumpOnFalse:
case opcode::OP_JumpOnTrue:
case opcode::OP_JumpOnFalseExpr:
case opcode::OP_JumpOnTrueExpr:
case opcode::OP_jump:
case opcode::OP_jumpback:
case opcode::OP_switch:
if (inst->data[0] == name)
inst->data[0] = itr->second;
break;
case opcode::OP_endswitch:
default:
break;
}
}
}
else
{
function_->labels.insert({ index_, name });
}
}
auto compiler::insert_label() -> std::string
{
auto const itr = function_->labels.find(index_);
if (itr != function_->labels.end())
{
return itr->second;
}
else
{
label_idx_++;
auto name = std::format("loc_{}", label_idx_);
function_->labels.insert({ index_, name });
return name;
}
}
auto compiler::create_label() -> std::string
{
label_idx_++;
return std::format("loc_{}", label_idx_);
}
} // namespace xsk::gsc