gsc-tool/src/iw5/xsk/compiler.cpp
2022-01-26 12:08:28 +01:00

3554 lines
104 KiB
C++

// Copyright 2022 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 "stdafx.hpp"
#include "iw5.hpp"
#include "parser.hpp"
#include "lexer.hpp"
namespace xsk::gsc::iw5
{
auto compiler::output() -> std::vector<function::ptr>
{
return std::move(assembly_);
}
void compiler::compile(const std::string& file, std::vector<std::uint8_t>& data)
{
filename_ = file;
auto prog = parse_buffer(filename_, data);
compile_program(prog);
}
void compiler::read_callback(std::function<std::vector<std::uint8_t>(const std::string&)> func)
{
read_callback_ = func;
}
auto compiler::parse_buffer(const std::string& file, std::vector<std::uint8_t>& data) -> ast::program::ptr
{
ast::program::ptr result(nullptr);
resolver::set_reader(read_callback_);
lexer lexer(file, reinterpret_cast<char*>(data.data()), data.size());
parser parser(lexer, result);
if (parser.parse() || result == nullptr)
{
throw comp_error(xsk::gsc::location(&file), "An unknown error ocurred while parsing gsc file.");
}
return result;
}
auto compiler::parse_file(const std::string& file) -> ast::program::ptr
{
auto buffer = read_callback_(file);
auto result = parse_buffer(file, buffer);
return result;
}
void compiler::compile_program(const ast::program::ptr& program)
{
assembly_.clear();
includes_.clear();
animtrees_.clear();
constants_.clear();
local_functions_.clear();
index_ = 1;
developer_thread_ = false;
for (const auto& entry : program->declarations)
{
if (entry == ast::kind::decl_thread)
{
local_functions_.push_back(entry.as_thread->name->value);
}
}
for (const auto& include : program->includes)
{
emit_include(include);
}
for (const auto& declaration : program->declarations)
{
emit_declaration(declaration);
}
}
void compiler::emit_include(const ast::include::ptr& include)
{
const auto& path = include->path->value;
for (const auto& inc : includes_)
{
if (inc.name == path)
{
throw comp_error(include->loc(), "error duplicated include file '" + path + "'.");
}
}
if (map_known_includes(path)) return;
try
{
auto program = parse_file(path);
std::vector<std::string> funcs;
for (const auto& decl : program->declarations)
{
if (decl == ast::kind::decl_thread)
{
funcs.push_back(decl.as_thread->name->value);
}
}
if (funcs.size() == 0)
{
throw comp_error(include->loc(), "error empty include file '" + path + "'.");
}
includes_.push_back(include_t(path, funcs));
}
catch (const std::exception& e)
{
throw comp_error(include->loc(), "error parsing include file '" + path + "': " + e.what());
}
}
void compiler::emit_declaration(const ast::decl& decl)
{
switch (decl.kind())
{
case ast::kind::decl_dev_begin:
developer_thread_ = true;
break;
case ast::kind::decl_dev_end:
developer_thread_ = false;
break;
case ast::kind::decl_usingtree:
emit_decl_usingtree(decl.as_usingtree);
break;
case ast::kind::decl_constant:
emit_decl_constant(decl.as_constant);
break;
case ast::kind::decl_thread:
emit_decl_thread(decl.as_thread);
break;
default:
throw comp_error(decl.loc(), "unknown declaration");
}
}
void compiler::emit_decl_usingtree(const ast::decl_usingtree::ptr& animtree)
{
if(developer_thread_)
throw comp_error(animtree->loc(), "cannot put #using_animtree inside /# ... #/ comment");
animtrees_.push_back({ animtree->name->value, false });
}
void compiler::emit_decl_constant(const ast::decl_constant::ptr& constant)
{
constants_.insert({ constant->name->value, std::move(constant->value) });
}
void compiler::emit_decl_thread(const ast::decl_thread::ptr& thread)
{
function_ = std::make_unique<function>();
function_->index = index_;
function_->name = thread->name->value;
auto blk = std::make_unique<block>();
stack_idx_ = 0;
label_idx_ = 0;
can_break_ = false;
can_continue_ = false;
local_stack_.clear();
break_blks_.clear();
continue_blks_.clear();
process_thread(thread, blk);
emit_expr_parameters(thread->params, blk);
emit_stmt_list(thread->stmt, blk, true);
emit_opcode(opcode::OP_End);
function_->size = index_ - function_->index;
assembly_.push_back(std::move(function_));
}
void compiler::emit_stmt(const ast::stmt& stmt, const block::ptr& blk, bool last)
{
switch (stmt.kind())
{
case ast::kind::stmt_list:
emit_stmt_list(stmt.as_list, blk, last);
break;
case ast::kind::stmt_expr:
emit_stmt_expr(stmt.as_expr, blk);
break;
case ast::kind::stmt_call:
emit_stmt_call(stmt.as_call, blk);
break;
case ast::kind::stmt_assign:
emit_stmt_assign(stmt.as_assign, blk);
break;
case ast::kind::stmt_endon:
emit_stmt_endon(stmt.as_endon, blk);
break;
case ast::kind::stmt_notify:
emit_stmt_notify(stmt.as_notify, blk);
break;
case ast::kind::stmt_wait:
emit_stmt_wait(stmt.as_wait, blk);
break;
case ast::kind::stmt_waittill:
emit_stmt_waittill(stmt.as_waittill, blk);
break;
case ast::kind::stmt_waittillmatch:
emit_stmt_waittillmatch(stmt.as_waittillmatch, blk);
break;
case ast::kind::stmt_waittillframeend:
emit_stmt_waittillframeend(stmt.as_waittillframeend, blk);
break;
case ast::kind::stmt_if:
emit_stmt_if(stmt.as_if, blk, last);
break;
case ast::kind::stmt_ifelse:
emit_stmt_ifelse(stmt.as_ifelse, blk, last);
break;
case ast::kind::stmt_while:
emit_stmt_while(stmt.as_while, blk);
break;
case ast::kind::stmt_dowhile:
emit_stmt_dowhile(stmt.as_dowhile, blk);
break;
case ast::kind::stmt_for:
emit_stmt_for(stmt.as_for, blk);
break;
case ast::kind::stmt_foreach:
emit_stmt_foreach(stmt.as_foreach, blk);
break;
case ast::kind::stmt_switch:
emit_stmt_switch(stmt.as_switch, blk);
break;
case ast::kind::stmt_case:
emit_stmt_case(stmt.as_case, blk);
break;
case ast::kind::stmt_default:
emit_stmt_default(stmt.as_default, blk);
break;
case ast::kind::stmt_break:
emit_stmt_break(stmt.as_break, blk);
break;
case ast::kind::stmt_continue:
emit_stmt_continue(stmt.as_continue, blk);
break;
case ast::kind::stmt_return:
emit_stmt_return(stmt.as_return, blk);
break;
case ast::kind::stmt_breakpoint:
emit_stmt_breakpoint(stmt.as_breakpoint, blk);
break;
case ast::kind::stmt_prof_begin:
emit_stmt_prof_begin(stmt.as_prof_begin, blk);
break;
case ast::kind::stmt_prof_end:
emit_stmt_prof_end(stmt.as_prof_end, blk);
break;
default:
throw comp_error(stmt.loc(), "unknown statement");
}
}
void compiler::emit_stmt_list(const ast::stmt_list::ptr& stmt, const block::ptr& blk, bool last)
{
for (const auto& entry : stmt->list)
{
bool last_ = (&entry == &stmt->list.back() && last) ? true : false;
emit_stmt(entry, blk, last_);
}
}
void compiler::emit_stmt_expr(const ast::stmt_expr::ptr& stmt, const block::ptr& blk)
{
switch (stmt->expr.kind())
{
case ast::kind::expr_increment:
emit_expr_increment(stmt->expr.as_increment, blk, true);
break;
case ast::kind::expr_decrement:
emit_expr_decrement(stmt->expr.as_decrement, blk, true);
break;
case ast::kind::expr_assign_equal:
case ast::kind::expr_assign_add:
case ast::kind::expr_assign_sub:
case ast::kind::expr_assign_mul:
case ast::kind::expr_assign_div:
case ast::kind::expr_assign_mod:
case ast::kind::expr_assign_shift_left:
case ast::kind::expr_assign_shift_right:
case ast::kind::expr_assign_bitwise_or:
case ast::kind::expr_assign_bitwise_and:
case ast::kind::expr_assign_bitwise_exor:
emit_expr_assign(stmt->expr.as_assign, blk);
break;
case ast::kind::null:
break;
default:
throw comp_error(stmt->loc(), "unknown expr statement expression");
}
}
void compiler::emit_stmt_call(const ast::stmt_call::ptr& stmt, const block::ptr& blk)
{
if (stmt->expr == ast::kind::expr_call)
{
if (mode_ != gsc::build::dev && stmt->expr.as_call->call == ast::kind::expr_function)
{
const auto& name = stmt->expr.as_call->call.as_function->name->value;
if (name == "assert" || name == "assertex" || name == "assertmsg") return;
}
emit_expr_call(stmt->expr.as_call, blk);
}
else if (stmt->expr == ast::kind::expr_method)
emit_expr_method(stmt->expr.as_method, blk);
else
throw comp_error(stmt->loc(), "unknown call statement expression");
emit_opcode(opcode::OP_DecTop);
}
void compiler::emit_stmt_assign(const ast::stmt_assign::ptr& stmt, const block::ptr& blk)
{
switch (stmt->expr.kind())
{
case ast::kind::expr_increment:
emit_expr_increment(stmt->expr.as_increment, blk, true);
break;
case ast::kind::expr_decrement:
emit_expr_decrement(stmt->expr.as_decrement, blk, true);
break;
case ast::kind::expr_assign_equal:
case ast::kind::expr_assign_add:
case ast::kind::expr_assign_sub:
case ast::kind::expr_assign_mul:
case ast::kind::expr_assign_div:
case ast::kind::expr_assign_mod:
case ast::kind::expr_assign_shift_left:
case ast::kind::expr_assign_shift_right:
case ast::kind::expr_assign_bitwise_or:
case ast::kind::expr_assign_bitwise_and:
case ast::kind::expr_assign_bitwise_exor:
emit_expr_assign(stmt->expr.as_assign, blk);
break;
default:
throw comp_error(stmt->loc(), "unknown assign statement expression");
}
}
void compiler::emit_stmt_endon(const ast::stmt_endon::ptr& stmt, const block::ptr& blk)
{
emit_expr(stmt->event, blk);
emit_expr(stmt->obj, blk);
emit_opcode(opcode::OP_endon);
}
void compiler::emit_stmt_notify(const ast::stmt_notify::ptr& stmt, const block::ptr& blk)
{
emit_opcode(opcode::OP_voidCodepos);
std::reverse(stmt->args->list.begin(), stmt->args->list.end());
for (const auto& arg : stmt->args->list)
{
emit_expr(arg, blk);
}
emit_expr(stmt->event, blk);
emit_expr(stmt->obj, blk);
emit_opcode(opcode::OP_notify);
}
void compiler::emit_stmt_wait(const ast::stmt_wait::ptr& stmt, const block::ptr& blk)
{
emit_expr(stmt->time, blk);
emit_opcode(opcode::OP_wait);
}
void compiler::emit_stmt_waittill(const ast::stmt_waittill::ptr& stmt, const block::ptr& blk)
{
emit_expr(stmt->event, blk);
emit_expr(stmt->obj, blk);
emit_opcode(opcode::OP_waittill);
for (const auto& entry : stmt->args->list)
{
create_variable(entry.as_identifier, blk);
emit_opcode(opcode::OP_SafeSetWaittillVariableFieldCached, variable_access_index(entry.as_identifier, blk));
}
emit_opcode(opcode::OP_clearparams);
}
void compiler::emit_stmt_waittillmatch(const ast::stmt_waittillmatch::ptr& stmt, const block::ptr& blk)
{
emit_expr_arguments(stmt->args, blk);
emit_expr(stmt->event, blk);
emit_expr(stmt->obj, blk);
emit_opcode(opcode::OP_waittillmatch, utils::string::va("%d", stmt->args->list.size()));
emit_opcode(opcode::OP_waittillmatch2);
emit_opcode(opcode::OP_clearparams);
}
void compiler::emit_stmt_waittillframeend(const ast::stmt_waittillframeend::ptr&, const block::ptr&)
{
emit_opcode(opcode::OP_waittillFrameEnd);
}
void compiler::emit_stmt_if(const ast::stmt_if::ptr& stmt, const block::ptr& blk, bool last)
{
auto end_loc = create_label();
if (stmt->test == ast::kind::expr_not)
{
emit_expr(stmt->test.as_not->rvalue, blk);
emit_opcode(opcode::OP_JumpOnTrue, end_loc);
}
else
{
emit_expr(stmt->test, blk);
emit_opcode(opcode::OP_JumpOnFalse, end_loc);
}
blk->transfer(stmt->blk);
emit_stmt(stmt->stmt, stmt->blk, last);
last ? emit_opcode(opcode::OP_End) : emit_remove_local_vars(stmt->blk);
insert_label(end_loc);
}
void compiler::emit_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt, const block::ptr& blk, bool last)
{
std::vector<block*> childs;
auto else_loc = create_label();
auto end_loc = create_label();
if (stmt->test == ast::kind::expr_not)
{
emit_expr(stmt->test.as_not->rvalue, blk);
emit_opcode(opcode::OP_JumpOnTrue, else_loc);
}
else
{
emit_expr(stmt->test, blk);
emit_opcode(opcode::OP_JumpOnFalse, else_loc);
}
blk->transfer(stmt->blk_if);
emit_stmt(stmt->stmt_if, stmt->blk_if, last);
emit_remove_local_vars(stmt->blk_if);
if (stmt->blk_if->abort == abort_t::abort_none)
childs.push_back(stmt->blk_if.get());
last ? emit_opcode(opcode::OP_End) : emit_opcode(opcode::OP_jump, end_loc);
insert_label(else_loc);
blk->transfer(stmt->blk_else);
emit_stmt(stmt->stmt_else, stmt->blk_else, last);
last ? emit_opcode(opcode::OP_End) : emit_remove_local_vars(stmt->blk_else);
if (stmt->blk_else->abort == abort_t::abort_none)
childs.push_back(stmt->blk_else.get());
insert_label(end_loc);
blk->init_from_child(childs);
}
void compiler::emit_stmt_while(const ast::stmt_while::ptr& stmt, const block::ptr& blk)
{
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();
blk->transfer(stmt->blk);
stmt->blk->loc_break = break_loc;
stmt->blk->loc_continue = continue_loc;
emit_create_local_vars(stmt->blk);
blk->local_vars_create_count = stmt->blk->local_vars_create_count;
for (auto i = 0u; i < blk->local_vars_create_count; i++)
{
if (!blk->local_vars.at(i).init)
blk->local_vars.at(i).init = true;
}
auto begin_loc = insert_label();
bool const_cond = is_constant_condition(stmt->test);
if (!const_cond)
{
emit_expr(stmt->test, blk);
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
}
emit_stmt(stmt->stmt, stmt->blk, false);
insert_label(continue_loc);
emit_opcode(opcode::OP_jumpback, begin_loc);
insert_label(break_loc);
if (const_cond)
blk->init_from_child(break_blks_);
can_break_ = old_break;
can_continue_ = old_continue;
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::emit_stmt_dowhile(const ast::stmt_dowhile::ptr& stmt, const block::ptr& blk)
{
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();
blk->transfer(stmt->blk);
stmt->blk->loc_break = break_loc;
stmt->blk->loc_continue = continue_loc;
emit_create_local_vars(stmt->blk);
blk->local_vars_create_count = stmt->blk->local_vars_create_count;
for (auto i = 0u; i < blk->local_vars_create_count; i++)
{
if (!blk->local_vars.at(i).init)
blk->local_vars.at(i).init = true;
}
auto begin_loc = insert_label();
emit_stmt(stmt->stmt, stmt->blk, false);
bool const_cond = is_constant_condition(stmt->test);
if (!const_cond)
{
emit_expr(stmt->test, blk);
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
}
insert_label(continue_loc);
emit_opcode(opcode::OP_jumpback, begin_loc);
insert_label(break_loc);
if (const_cond)
blk->init_from_child(break_blks_);
can_break_ = old_break;
can_continue_ = old_continue;
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::emit_stmt_for(const ast::stmt_for::ptr& stmt, const block::ptr& blk)
{
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(stmt->init, blk, false);
blk->transfer(stmt->blk);
stmt->blk->loc_break = break_loc;
stmt->blk->loc_continue = continue_loc;
emit_create_local_vars(stmt->blk);
blk->local_vars_create_count = stmt->blk->local_vars_create_count;
for (auto i = 0u; i < blk->local_vars_create_count; i++)
{
if (!blk->local_vars.at(i).init)
blk->local_vars.at(i).init = true;
}
blk->transfer(stmt->blk_iter);
auto begin_loc = insert_label();
bool const_cond = is_constant_condition(stmt->test);
if (!const_cond)
{
emit_expr(stmt->test, blk);
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
}
can_break_ = true;
can_continue_ = true;
emit_stmt(stmt->stmt, stmt->blk, false);
if (stmt->blk->abort == abort_t::abort_none)
continue_blks_.push_back(stmt->blk.get());
can_break_ = false;
can_continue_ = false;
insert_label(continue_loc);
stmt->blk_iter->init_from_child(continue_blks_);
emit_stmt(stmt->iter, stmt->blk_iter, false);
emit_opcode(opcode::OP_jumpback, begin_loc);
insert_label(break_loc);
if (const_cond)
blk->init_from_child(break_blks_);
can_break_ = old_break;
can_continue_ = old_continue;
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::emit_stmt_foreach(const ast::stmt_foreach::ptr& stmt, const block::ptr& blk)
{
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(stmt->array_expr, blk);
emit_expr_variable_ref(stmt->array, blk, true);
emit_expr_variable(stmt->array, blk);
emit_opcode(opcode::OP_CallBuiltin1, "getfirstarraykey");
emit_expr_variable_ref(stmt->key_expr, blk, true);
blk->transfer(stmt->ctx);
stmt->ctx->loc_break = break_loc;
stmt->ctx->loc_continue = continue_loc;
emit_create_local_vars(stmt->ctx);
blk->local_vars_create_count = stmt->ctx->local_vars_create_count;
for (auto i = 0u; i < blk->local_vars_create_count; i++)
{
if (!blk->local_vars.at(i).init)
blk->local_vars.at(i).init = true;
}
blk->transfer(stmt->ctx_post);
auto begin_loc = insert_label();
emit_expr_variable(stmt->key_expr, blk);
emit_opcode(opcode::OP_CallBuiltin1, "isdefined");
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
can_break_ = true;
can_continue_ = true;
emit_expr_variable(stmt->key_expr, stmt->ctx);
emit_opcode(opcode::OP_EvalLocalArrayCached, variable_access_index(stmt->array.as_identifier, stmt->ctx));
emit_expr_variable_ref(stmt->value_expr, stmt->ctx, true);
emit_stmt(stmt->stmt, stmt->ctx, false);
if (stmt->ctx->abort == abort_t::abort_none)
continue_blks_.push_back(stmt->ctx.get());
can_break_ = false;
can_continue_ = false;
insert_label(continue_loc);
stmt->ctx_post->init_from_child(continue_blks_);
emit_expr_variable(stmt->key_expr, stmt->ctx_post);
emit_expr_variable(stmt->array, stmt->ctx_post);
emit_opcode(opcode::OP_CallBuiltin2, "getnextarraykey");
emit_expr_variable_ref(stmt->key_expr, stmt->ctx_post, true);
emit_opcode(opcode::OP_jumpback, begin_loc);
insert_label(break_loc);
emit_expr_clear_local(stmt->array.as_identifier, blk);
if (!stmt->use_key) emit_expr_clear_local(stmt->key_expr.as_identifier, blk);
can_break_ = old_break;
can_continue_ = old_continue;
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::emit_stmt_switch(const ast::stmt_switch::ptr& stmt, const block::ptr& blk)
{
auto old_breaks = break_blks_;
auto old_break = can_break_;
break_blks_.clear();
can_break_ = false;
auto jmptable_loc = create_label();
auto break_loc = create_label();
emit_expr(stmt->test, blk);
emit_opcode(opcode::OP_switch, jmptable_loc);
can_break_ = true;
std::vector<std::string> data;
data.push_back(utils::string::va("%d", stmt->stmt->list.size()));
bool has_default = false;
block* default_ctx = nullptr;
for (auto i = 0; i < stmt->stmt->list.size(); i++)
{
auto& entry = stmt->stmt->list[i];
if (entry == ast::kind::stmt_case)
{
if (has_default)
{
comp_error(stmt->loc(), "default must be last case");
}
auto& case_ = entry.as_case;
if (case_->label == ast::kind::expr_integer)
{
auto loc = insert_label();
data.push_back("case");
data.push_back(case_->label.as_integer->value);
data.push_back(loc);
}
else if (case_->label == ast::kind::expr_string)
{
auto loc = insert_label();
data.push_back("case");
data.push_back(case_->label.as_string->value);
data.push_back(loc);
}
else
{
throw comp_error(stmt->loc(), "case type must be int or string");
}
blk->transfer(case_->blk);
case_->blk->loc_break = break_loc;
emit_stmt_list(case_->stmt, case_->blk, false);
if (case_->stmt->list.size() > 0)
emit_remove_local_vars(case_->blk);
}
else if (entry == ast::kind::stmt_default)
{
auto loc = insert_label();
data.push_back("default");
data.push_back(loc);
has_default = true;
default_ctx = entry.as_default->blk.get();
blk->transfer(entry.as_default->blk);
entry.as_default->blk->loc_break = break_loc;
emit_stmt_list(entry.as_default->stmt, entry.as_default->blk, false);
if (entry.as_default->stmt->list.size() > 0)
emit_remove_local_vars(entry.as_default->blk);
}
else
{
throw comp_error(entry.loc(), "missing case statement");
}
}
if (has_default)
{
if (default_ctx->abort == abort_t::abort_none)
{
break_blks_.push_back(default_ctx);
}
blk->init_from_child(break_blks_);
}
insert_label(jmptable_loc);
emit_opcode(opcode::OP_endswitch, data);
auto offset = 7 * stmt->stmt->list.size();
function_->instructions.back()->size += offset;
index_ += offset;
insert_label(break_loc);
can_break_ = old_break;
break_blks_ = old_breaks;
}
void compiler::emit_stmt_case(const ast::stmt_case::ptr& stmt, const block::ptr& blk)
{
throw comp_error(stmt->loc(), "illegal case statement");
}
void compiler::emit_stmt_default(const ast::stmt_default::ptr& stmt, const block::ptr& blk)
{
throw comp_error(stmt->loc(), "illegal default statement");
}
void compiler::emit_stmt_break(const ast::stmt_break::ptr& stmt, const block::ptr& blk)
{
if (!can_break_ || blk->abort != abort_t::abort_none || blk->loc_break == "")
throw comp_error(stmt->loc(), "illegal break statement");
break_blks_.push_back(blk.get());
emit_remove_local_vars(blk);
blk->abort = abort_t::abort_break;
emit_opcode(opcode::OP_jump, blk->loc_break);
}
void compiler::emit_stmt_continue(const ast::stmt_continue::ptr& stmt, const block::ptr& blk)
{
if (!can_continue_ || blk->abort != abort_t::abort_none || blk->loc_continue == "")
throw comp_error(stmt->loc(), "illegal continue statement");
continue_blks_.push_back(blk.get());
emit_remove_local_vars(blk);
blk->abort = abort_t::abort_continue;
emit_opcode(opcode::OP_jump, blk->loc_continue);
}
void compiler::emit_stmt_return(const ast::stmt_return::ptr& stmt, const block::ptr& blk)
{
if (blk->abort == abort_t::abort_none)
blk->abort = abort_t::abort_return;
if (stmt->expr != ast::kind::null)
{
emit_expr(stmt->expr, blk);
emit_opcode(opcode::OP_Return);
}
else
emit_opcode(opcode::OP_End);
}
void compiler::emit_stmt_breakpoint(const ast::stmt_breakpoint::ptr& stmt, const block::ptr& blk)
{
// TODO:
}
void compiler::emit_stmt_prof_begin(const ast::stmt_prof_begin::ptr& stmt, const block::ptr& blk)
{
// TODO:
}
void compiler::emit_stmt_prof_end(const ast::stmt_prof_end::ptr& stmt, const block::ptr& blk)
{
// TODO:
}
void compiler::emit_expr(const ast::expr& expr, const block::ptr& blk)
{
switch (expr.kind())
{
case ast::kind::expr_paren:
emit_expr(expr.as_paren->child, blk);
break;
case ast::kind::expr_ternary:
emit_expr_ternary(expr.as_ternary, blk);
break;
case ast::kind::expr_and:
emit_expr_and(expr.as_and, blk);
break;
case ast::kind::expr_or:
emit_expr_or(expr.as_or, blk);
break;
case ast::kind::expr_equality:
case ast::kind::expr_inequality:
case ast::kind::expr_less:
case ast::kind::expr_greater:
case ast::kind::expr_less_equal:
case ast::kind::expr_greater_equal:
case ast::kind::expr_bitwise_or:
case ast::kind::expr_bitwise_and:
case ast::kind::expr_bitwise_exor:
case ast::kind::expr_shift_left:
case ast::kind::expr_shift_right:
case ast::kind::expr_add:
case ast::kind::expr_sub:
case ast::kind::expr_mul:
case ast::kind::expr_div:
case ast::kind::expr_mod:
emit_expr_binary(expr.as_binary, blk);
break;
case ast::kind::expr_complement:
emit_expr_complement(expr.as_complement, blk);
break;
case ast::kind::expr_not:
emit_expr_not(expr.as_not, blk);
break;
case ast::kind::expr_call:
emit_expr_call(expr.as_call, blk);
break;
case ast::kind::expr_method:
emit_expr_method(expr.as_method, blk);
break;
case ast::kind::expr_reference:
emit_expr_reference(expr.as_reference, blk);
break;
case ast::kind::expr_add_array:
emit_expr_add_array(expr.as_add_array, blk);
break;
case ast::kind::expr_array:
emit_expr_array(expr.as_array, blk);
break;
case ast::kind::expr_field:
emit_expr_field(expr.as_field, blk);
break;
case ast::kind::expr_size:
emit_expr_size(expr.as_size, blk);
break;
case ast::kind::expr_thisthread:
emit_opcode(opcode::OP_GetThisthread);
break;
case ast::kind::expr_empty_array:
emit_opcode(opcode::OP_EmptyArray);
break;
case ast::kind::expr_undefined:
emit_opcode(opcode::OP_GetUndefined);
break;
case ast::kind::expr_game:
emit_opcode(opcode::OP_GetGame);
break;
case ast::kind::expr_self:
emit_opcode(opcode::OP_GetSelf);
break;
case ast::kind::expr_anim:
emit_opcode(opcode::OP_GetAnim);
break;
case ast::kind::expr_level:
emit_opcode(opcode::OP_GetLevel);
break;
case ast::kind::expr_animation:
emit_expr_animation(expr.as_animation);
break;
case ast::kind::expr_animtree:
emit_expr_animtree(expr.as_animtree);
break;
case ast::kind::expr_identifier:
emit_expr_local(expr.as_identifier, blk);
break;
case ast::kind::expr_istring:
emit_expr_istring(expr.as_istring);
break;
case ast::kind::expr_string:
emit_expr_string(expr.as_string);
break;
case ast::kind::expr_color:
emit_expr_color(expr.as_color);
break;
case ast::kind::expr_vector:
emit_expr_vector(expr.as_vector, blk);
break;
case ast::kind::expr_float:
emit_expr_float(expr.as_float);
break;
case ast::kind::expr_integer:
emit_expr_integer(expr.as_integer);
break;
case ast::kind::expr_false:
emit_expr_false(expr.as_false);
break;
case ast::kind::expr_true:
emit_expr_true(expr.as_true);
break;
case ast::kind::null:
break;
default:
throw comp_error(expr.loc(), "unknown expression");
}
}
void compiler::emit_expr_assign(const ast::expr_assign::ptr& expr, const block::ptr& blk)
{
if (expr->kind() == ast::kind::expr_assign_equal)
{
if (expr->rvalue == ast::kind::expr_undefined)
{
emit_expr_clear(expr->lvalue, blk);
return;
}
emit_expr(expr->rvalue, blk);
emit_expr_variable_ref(expr->lvalue, blk, true);
return;
}
emit_expr(expr->lvalue, blk);
emit_expr(expr->rvalue, blk);
switch (expr->kind())
{
case ast::kind::expr_assign_add:
emit_opcode(opcode::OP_plus);
break;
case ast::kind::expr_assign_sub:
emit_opcode(opcode::OP_minus);
break;
case ast::kind::expr_assign_mul:
emit_opcode(opcode::OP_multiply);
break;
case ast::kind::expr_assign_div:
emit_opcode(opcode::OP_divide);
break;
case ast::kind::expr_assign_mod:
emit_opcode(opcode::OP_mod);
break;
case ast::kind::expr_assign_shift_left:
emit_opcode(opcode::OP_shift_left);
break;
case ast::kind::expr_assign_shift_right:
emit_opcode(opcode::OP_shift_right);
break;
case ast::kind::expr_assign_bitwise_or:
emit_opcode(opcode::OP_bit_or);
break;
case ast::kind::expr_assign_bitwise_and:
emit_opcode(opcode::OP_bit_and);
break;
case ast::kind::expr_assign_bitwise_exor:
emit_opcode(opcode::OP_bit_ex_or);
break;
default:
throw comp_error(expr->loc(), "unknown assign operation");
}
emit_expr_variable_ref(expr->lvalue, blk, true);
}
void compiler::emit_expr_clear(const ast::expr& expr, const block::ptr& blk)
{
switch (expr.kind())
{
case ast::kind::expr_array:
emit_expr(expr.as_array->key, blk);
expr.as_array->obj == ast::kind::expr_game ? emit_opcode(opcode::OP_GetGameRef) : emit_expr_variable_ref(expr.as_array->obj, blk, false);
emit_opcode(opcode::OP_ClearArray);
break;
case ast::kind::expr_field:
emit_expr_object(expr.as_field->obj, blk);
emit_opcode(opcode::OP_ClearFieldVariable, expr.as_field->field->value);
break;
case ast::kind::expr_identifier:
emit_opcode(opcode::OP_GetUndefined);
emit_expr_local_ref(expr.as_identifier, blk, true);
break;
default:
throw comp_error(expr.loc(), "unknown clear variable lvalue");
}
}
void compiler::emit_expr_clear_local(const ast::expr_identifier::ptr& expr, const block::ptr& blk)
{
auto index = variable_stack_index(expr, blk);
if (index == 0)
emit_opcode(opcode::OP_ClearLocalVariableFieldCached0);
else
emit_opcode(opcode::OP_ClearLocalVariableFieldCached, variable_access_index(expr, blk));
}
void compiler::emit_expr_increment(const ast::expr_increment::ptr& expr, const block::ptr& blk, bool fromstmt)
{
if (fromstmt)
{
emit_expr_variable_ref(expr->lvalue, blk, false);
emit_opcode(opcode::OP_inc);
emit_opcode(opcode::OP_SetVariableField);
}
else
{
// TODO:
}
}
void compiler::emit_expr_decrement(const ast::expr_decrement::ptr& expr, const block::ptr& blk, bool fromstmt)
{
if (fromstmt)
{
emit_expr_variable_ref(expr->lvalue, blk, false);
emit_opcode(opcode::OP_dec);
emit_opcode(opcode::OP_SetVariableField);
}
else
{
// TODO:
}
}
void compiler::emit_expr_ternary(const ast::expr_ternary::ptr& expr, const block::ptr& blk)
{
auto else_loc = create_label();
auto end_loc = create_label();
if (expr->test == ast::kind::expr_not)
{
emit_expr(expr->test.as_not->rvalue, blk);
emit_opcode(opcode::OP_JumpOnTrue, else_loc);
}
else
{
emit_expr(expr->test, blk);
emit_opcode(opcode::OP_JumpOnFalse, else_loc);
}
emit_expr(expr->true_expr, blk);
emit_opcode(opcode::OP_jump, end_loc);
insert_label(else_loc);
emit_expr(expr->false_expr, blk);
insert_label(end_loc);
}
void compiler::emit_expr_binary(const ast::expr_binary::ptr& expr, const block::ptr& blk)
{
emit_expr(expr->lvalue, blk);
emit_expr(expr->rvalue, blk);
switch (expr->kind())
{
case ast::kind::expr_equality:
emit_opcode(opcode::OP_equality);
break;
case ast::kind::expr_inequality:
emit_opcode(opcode::OP_inequality);
break;
case ast::kind::expr_less:
emit_opcode(opcode::OP_less);
break;
case ast::kind::expr_greater:
emit_opcode(opcode::OP_greater);
break;
case ast::kind::expr_less_equal:
emit_opcode(opcode::OP_less_equal);
break;
case ast::kind::expr_greater_equal:
emit_opcode(opcode::OP_greater_equal);
break;
case ast::kind::expr_bitwise_or:
emit_opcode(opcode::OP_bit_or);
break;
case ast::kind::expr_bitwise_and:
emit_opcode(opcode::OP_bit_and);
break;
case ast::kind::expr_bitwise_exor:
emit_opcode(opcode::OP_bit_ex_or);
break;
case ast::kind::expr_shift_left:
emit_opcode(opcode::OP_shift_left);
break;
case ast::kind::expr_shift_right:
emit_opcode(opcode::OP_shift_right);
break;
case ast::kind::expr_add:
emit_opcode(opcode::OP_plus);
break;
case ast::kind::expr_sub:
emit_opcode(opcode::OP_minus);
break;
case ast::kind::expr_mul:
emit_opcode(opcode::OP_multiply);
break;
case ast::kind::expr_div:
emit_opcode(opcode::OP_divide);
break;
case ast::kind::expr_mod:
emit_opcode(opcode::OP_mod);
break;
default:
throw comp_error(expr->loc(), "unknown binary expression");
}
}
void compiler::emit_expr_and(const ast::expr_and::ptr& expr, const block::ptr& blk)
{
auto label = create_label();
emit_expr(expr->lvalue, blk);
emit_opcode(opcode::OP_JumpOnFalseExpr, label);
emit_expr(expr->rvalue, blk);
emit_opcode(opcode::OP_CastBool);
insert_label(label);
}
void compiler::emit_expr_or(const ast::expr_or::ptr& expr, const block::ptr& blk)
{
auto label = create_label();
emit_expr(expr->lvalue, blk);
emit_opcode(opcode::OP_JumpOnTrueExpr, label);
emit_expr(expr->rvalue, blk);
emit_opcode(opcode::OP_CastBool);
insert_label(label);
}
void compiler::emit_expr_complement(const ast::expr_complement::ptr& expr, const block::ptr& blk)
{
emit_expr(expr->rvalue, blk);
emit_opcode(opcode::OP_BoolComplement);
}
void compiler::emit_expr_not(const ast::expr_not::ptr& expr, const block::ptr& blk)
{
emit_expr(expr->rvalue, blk);
emit_opcode(opcode::OP_BoolNot);
}
void compiler::emit_expr_call(const ast::expr_call::ptr& expr, const block::ptr& blk)
{
if (expr->call == ast::kind::expr_pointer)
emit_expr_call_pointer(expr->call.as_pointer, blk);
else if (expr->call == ast::kind::expr_function)
emit_expr_call_function(expr->call.as_function, blk);
else
throw comp_error(expr->loc(), "unknown function call expression");
}
void compiler::emit_expr_call_pointer(const ast::expr_pointer::ptr& expr, const block::ptr& blk)
{
if (expr->mode == ast::call::mode::normal)
emit_opcode(opcode::OP_PreScriptCall);
emit_expr_arguments(expr->args, blk);
emit_expr(expr->func, blk);
auto argcount = utils::string::va("%d", expr->args->list.size());
switch (expr->mode)
{
case ast::call::mode::normal:
emit_opcode(opcode::OP_ScriptFunctionCallPointer);
break;
case ast::call::mode::thread:
emit_opcode(opcode::OP_ScriptThreadCallPointer, argcount);
break;
case ast::call::mode::childthread:
emit_opcode(opcode::OP_ScriptChildThreadCallPointer, argcount);
break;
case ast::call::mode::builtin:
emit_opcode(opcode::OP_CallBuiltinPointer, argcount);
break;
}
}
void compiler::emit_expr_call_function(const ast::expr_function::ptr& expr, const block::ptr& blk)
{
auto type = resolve_function_type(expr);
if (type != ast::call::type::builtin && expr->mode == ast::call::mode::normal && expr->args->list.size() > 0)
emit_opcode(opcode::OP_PreScriptCall);
emit_expr_arguments(expr->args, blk);
auto argcount = utils::string::va("%d", expr->args->list.size());
if (type == ast::call::type::local)
{
switch (expr->mode)
{
case ast::call::mode::normal:
if (expr->args->list.size() > 0)
emit_opcode(opcode::OP_ScriptLocalFunctionCall, expr->name->value);
else
emit_opcode(opcode::OP_ScriptLocalFunctionCall2, expr->name->value);
break;
case ast::call::mode::thread:
emit_opcode(opcode::OP_ScriptLocalThreadCall, { expr->name->value, argcount });
break;
case ast::call::mode::childthread:
emit_opcode(opcode::OP_ScriptLocalChildThreadCall, { expr->name->value, argcount });
break;
case ast::call::mode::builtin:
// no local builtins
break;
}
}
else if (type == ast::call::type::far)
{
switch (expr->mode)
{
case ast::call::mode::normal:
if (expr->args->list.size() > 0)
emit_opcode(opcode::OP_ScriptFarFunctionCall, { expr->path->value, expr->name->value });
else
emit_opcode(opcode::OP_ScriptFarFunctionCall2, { expr->path->value, expr->name->value });
break;
case ast::call::mode::thread:
emit_opcode(opcode::OP_ScriptFarThreadCall, { expr->path->value, expr->name->value, argcount });
break;
case ast::call::mode::childthread:
emit_opcode(opcode::OP_ScriptFarChildThreadCall, { expr->path->value, expr->name->value, argcount });
break;
case ast::call::mode::builtin:
// no far builtins
break;
}
}
else if (type == ast::call::type::builtin)
{
if (expr->mode != ast::call::mode::normal)
throw comp_error(expr->loc(), "builtin calls can't be threaded");
switch (expr->args->list.size())
{
case 0:
emit_opcode(opcode::OP_CallBuiltin0, expr->name->value);
break;
case 1:
emit_opcode(opcode::OP_CallBuiltin1, expr->name->value);
break;
case 2:
emit_opcode(opcode::OP_CallBuiltin2, expr->name->value);
break;
case 3:
emit_opcode(opcode::OP_CallBuiltin3, expr->name->value);
break;
case 4:
emit_opcode(opcode::OP_CallBuiltin4, expr->name->value);
break;
case 5:
emit_opcode(opcode::OP_CallBuiltin5, expr->name->value);
break;
default:
emit_opcode(opcode::OP_CallBuiltin, { expr->name->value, argcount });
break;
}
}
}
void compiler::emit_expr_method(const ast::expr_method::ptr& expr, const block::ptr& blk)
{
if (expr->call == ast::kind::expr_pointer)
emit_expr_method_pointer(expr->call.as_pointer, expr->obj, blk);
else if (expr->call == ast::kind::expr_function)
emit_expr_method_function(expr->call.as_function, expr->obj, blk);
else
throw comp_error(expr->loc(), "unknown method call expression");
}
void compiler::emit_expr_method_pointer(const ast::expr_pointer::ptr& expr, const ast::expr& obj, const block::ptr& blk)
{
if (expr->mode == ast::call::mode::normal)
emit_opcode(opcode::OP_PreScriptCall);
emit_expr_arguments(expr->args, blk);
emit_expr(obj, blk);
emit_expr(expr->func, blk);
auto argcount = utils::string::va("%d", expr->args->list.size());
switch (expr->mode)
{
case ast::call::mode::normal:
emit_opcode(opcode::OP_ScriptMethodCallPointer);
break;
case ast::call::mode::thread:
emit_opcode(opcode::OP_ScriptMethodThreadCallPointer, argcount);
break;
case ast::call::mode::childthread:
emit_opcode(opcode::OP_ScriptMethodChildThreadCallPointer, argcount);
break;
case ast::call::mode::builtin:
emit_opcode(opcode::OP_CallBuiltinMethodPointer, argcount);
break;
}
}
void compiler::emit_expr_method_function(const ast::expr_function::ptr& expr, const ast::expr& obj, const block::ptr& blk)
{
auto type = resolve_function_type(expr);
if (type != ast::call::type::builtin && expr->mode == ast::call::mode::normal)
emit_opcode(opcode::OP_PreScriptCall);
emit_expr_arguments(expr->args, blk);
emit_expr(obj, blk);
auto argcount = utils::string::va("%d", expr->args->list.size());
if (type == ast::call::type::local)
{
switch (expr->mode)
{
case ast::call::mode::normal:
emit_opcode(opcode::OP_ScriptLocalMethodCall, expr->name->value);
break;
case ast::call::mode::thread:
emit_opcode(opcode::OP_ScriptLocalMethodThreadCall, { expr->name->value, argcount });
break;
case ast::call::mode::childthread:
emit_opcode(opcode::OP_ScriptLocalMethodChildThreadCall, { expr->name->value, argcount });
break;
case ast::call::mode::builtin:
// no local builtins
break;
}
}
else if (type == ast::call::type::far)
{
switch (expr->mode)
{
case ast::call::mode::normal:
emit_opcode(opcode::OP_ScriptFarMethodCall, { expr->path->value, expr->name->value });
break;
case ast::call::mode::thread:
emit_opcode(opcode::OP_ScriptFarMethodThreadCall, { expr->path->value, expr->name->value, argcount });
break;
case ast::call::mode::childthread:
emit_opcode(opcode::OP_ScriptFarMethodChildThreadCall, { expr->path->value, expr->name->value, argcount });
break;
case ast::call::mode::builtin:
// no far builtins
break;
}
}
else if (type == ast::call::type::builtin)
{
if (expr->mode != ast::call::mode::normal)
throw comp_error(expr->loc(), "builtin calls can't be threaded");
switch (expr->args->list.size())
{
case 0:
emit_opcode(opcode::OP_CallBuiltinMethod0, expr->name->value);
break;
case 1:
emit_opcode(opcode::OP_CallBuiltinMethod1, expr->name->value);
break;
case 2:
emit_opcode(opcode::OP_CallBuiltinMethod2, expr->name->value);
break;
case 3:
emit_opcode(opcode::OP_CallBuiltinMethod3, expr->name->value);
break;
case 4:
emit_opcode(opcode::OP_CallBuiltinMethod4, expr->name->value);
break;
case 5:
emit_opcode(opcode::OP_CallBuiltinMethod5, expr->name->value);
break;
default:
emit_opcode(opcode::OP_CallBuiltinMethod, { expr->name->value, argcount });
break;
}
}
}
void compiler::emit_expr_add_array(const ast::expr_add_array::ptr& expr, const block::ptr& blk)
{
emit_opcode(opcode::OP_EmptyArray);
for (const auto& arg : expr->args->list)
{
emit_expr(arg, blk);
emit_opcode(opcode::OP_AddArray);
}
}
void compiler::emit_expr_parameters(const ast::expr_parameters::ptr& expr, const block::ptr& blk)
{
for (const auto& entry : expr->list)
{
initialize_variable(entry, blk);
emit_opcode(opcode::OP_SafeCreateVariableFieldCached, variable_create_index(entry, blk));
}
emit_opcode(opcode::OP_checkclearparams);
}
void compiler::emit_expr_arguments(const ast::expr_arguments::ptr& expr, const block::ptr& blk)
{
std::reverse(expr->list.begin(), expr->list.end());
for (auto& entry : expr->list)
{
emit_expr(entry, blk);
}
}
void compiler::emit_expr_reference(const ast::expr_reference::ptr& expr, const block::ptr& blk)
{
bool method = false;
auto type = resolve_reference_type(expr, method);
switch (type)
{
case ast::call::type::local:
emit_opcode(opcode::OP_GetLocalFunction, expr->name->value);
break;
case ast::call::type::far:
emit_opcode(opcode::OP_GetFarFunction, { expr->path->value, expr->name->value });
break;
case ast::call::type::builtin:
if (method)
emit_opcode(opcode::OP_GetBuiltinMethod, expr->name->value);
else
emit_opcode(opcode::OP_GetBuiltinFunction, expr->name->value);
break;
}
}
void compiler::emit_expr_size(const ast::expr_size::ptr& expr, const block::ptr& blk)
{
emit_expr(expr->obj, blk);
emit_opcode(opcode::OP_size);
}
void compiler::emit_expr_variable_ref(const ast::expr& expr, const block::ptr& blk, bool set)
{
switch (expr.kind())
{
case ast::kind::expr_array:
emit_expr_array_ref(expr.as_array, blk, set);
break;
case ast::kind::expr_field:
emit_expr_field_ref(expr.as_field, blk, set);
break;
case ast::kind::expr_identifier:
emit_expr_local_ref(expr.as_identifier, blk, set);
break;
default:
throw comp_error(expr.loc(), "invalid lvalue");
}
}
void compiler::emit_expr_array_ref(const ast::expr_array::ptr& expr, const block::ptr& blk, bool set)
{
emit_expr(expr->key, blk);
switch (expr->obj.kind())
{
case ast::kind::expr_game:
emit_opcode(opcode::OP_GetGameRef);
emit_opcode(opcode::OP_EvalArrayRef);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case ast::kind::expr_array:
case ast::kind::expr_field:
emit_expr_variable_ref(expr->obj, blk, false);
emit_opcode(opcode::OP_EvalArrayRef);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case ast::kind::expr_identifier:
{
if (!variable_initialized(expr->obj.as_identifier, blk))
{
initialize_variable(expr->obj.as_identifier, blk);
emit_opcode(opcode::OP_EvalNewLocalArrayRefCached0, variable_create_index(expr->obj.as_identifier, blk));
if (!set) throw comp_error(expr->loc(), "INTERNAL: VAR CREATED BUT NOT SET!");
}
else if (variable_stack_index(expr->obj.as_identifier, blk) == 0)
{
emit_opcode(opcode::OP_EvalLocalArrayRefCached0);
}
else
{
emit_opcode(opcode::OP_EvalLocalArrayRefCached, variable_access_index(expr->obj.as_identifier, blk));
}
if (set) emit_opcode(opcode::OP_SetVariableField);
}
break;
case ast::kind::expr_call:
case ast::kind::expr_method:
default:
throw comp_error(expr->loc(), "invalid array lvalue");
}
}
void compiler::emit_expr_field_ref(const ast::expr_field::ptr& expr, const block::ptr& blk, bool set)
{
const auto& field = expr->field->value;
switch (expr->obj.kind())
{
case ast::kind::expr_level:
set ? emit_opcode(opcode::OP_SetLevelFieldVariableField, field) : emit_opcode(opcode::OP_EvalLevelFieldVariableRef, field);
break;
case ast::kind::expr_anim:
set ? emit_opcode(opcode::OP_SetAnimFieldVariableField, field) : emit_opcode(opcode::OP_EvalAnimFieldVariableRef, field);
break;
case ast::kind::expr_self:
set ? emit_opcode(opcode::OP_SetSelfFieldVariableField, field) : emit_opcode(opcode::OP_EvalSelfFieldVariableRef, field);
break;
case ast::kind::expr_array:
emit_expr_array(expr->obj.as_array, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case ast::kind::expr_field:
emit_expr_field(expr->obj.as_field, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case ast::kind::expr_identifier:
emit_opcode(opcode::OP_EvalLocalVariableObjectCached, variable_access_index(expr->obj.as_identifier, blk));
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case ast::kind::expr_call:
emit_expr_call(expr->obj.as_call, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case ast::kind::expr_method:
emit_expr_method(expr->obj.as_method, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
default:
throw comp_error(expr->obj.loc(), "not an object");
}
}
void compiler::emit_expr_local_ref(const ast::expr_identifier::ptr& expr, const block::ptr& blk, bool set)
{
const auto itr = constants_.find(expr->value);
if (itr != constants_.end())
{
throw comp_error(expr->loc(), "variable name already defined as constant '" + expr->value + "'");
}
if (set)
{
if (!variable_initialized(expr, blk))
{
initialize_variable(expr, blk);
emit_opcode(opcode::OP_SetNewLocalVariableFieldCached0, variable_create_index(expr, blk));
}
else if (variable_stack_index(expr, blk) == 0)
{
emit_opcode(opcode::OP_SetLocalVariableFieldCached0);
}
else
{
emit_opcode(opcode::OP_SetLocalVariableFieldCached, variable_access_index(expr, blk));
}
}
else
{
auto index = variable_stack_index(expr, blk);
if (index == 0)
emit_opcode(opcode::OP_EvalLocalVariableRefCached0);
else
emit_opcode(opcode::OP_EvalLocalVariableRefCached, variable_access_index(expr, blk));
}
}
void compiler::emit_expr_variable(const ast::expr& expr, const block::ptr& blk)
{
switch (expr.kind())
{
case ast::kind::expr_array:
emit_expr_array(expr.as_array, blk);
break;
case ast::kind::expr_field:
emit_expr_field(expr.as_field, blk);
break;
case ast::kind::expr_identifier:
emit_expr_local(expr.as_identifier, blk);
break;
default:
throw comp_error(expr.loc(), "invalid variable type.");
}
}
void compiler::emit_expr_array(const ast::expr_array::ptr& expr, const block::ptr& blk)
{
emit_expr(expr->key, blk);
if (expr->obj == ast::kind::expr_identifier)
{
emit_opcode(opcode::OP_EvalLocalArrayCached, variable_access_index(expr->obj.as_identifier, blk));
}
else
{
emit_expr(expr->obj, blk);
emit_opcode(opcode::OP_EvalArray);
}
}
void compiler::emit_expr_field(const ast::expr_field::ptr& expr, const block::ptr& blk)
{
const auto& field = expr->field->value;
switch (expr->obj.kind())
{
case ast::kind::expr_level:
emit_opcode(opcode::OP_EvalLevelFieldVariable, field);
break;
case ast::kind::expr_anim:
emit_opcode(opcode::OP_EvalAnimFieldVariable, field);
break;
case ast::kind::expr_self:
emit_opcode(opcode::OP_EvalSelfFieldVariable, field);
break;
case ast::kind::expr_array:
emit_expr_array(expr->obj.as_array, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
case ast::kind::expr_field:
emit_expr_field(expr->obj.as_field, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
case ast::kind::expr_call:
emit_expr_call(expr->obj.as_call, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
case ast::kind::expr_method:
emit_expr_method(expr->obj.as_method, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
case ast::kind::expr_identifier:
emit_opcode(opcode::OP_EvalLocalVariableObjectCached, variable_access_index(expr->obj.as_identifier, blk));
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
default:
throw comp_error(expr->loc(), "unknown field variable object type");
}
}
void compiler::emit_expr_local(const ast::expr_identifier::ptr& expr, const block::ptr& blk)
{
// is constant ( should only allow: string, loc string, number, vector)
const auto itr = constants_.find(expr->value);
if (itr != constants_.end())
{
const auto& value = itr->second;
emit_expr(value, blk);
return;
}
// is local var
auto index = variable_stack_index(expr, blk);
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, variable_access_index(expr, blk));
break;
}
}
void compiler::emit_expr_object(const ast::expr& expr, const block::ptr& blk)
{
switch (expr.kind())
{
case ast::kind::expr_level:
emit_opcode(opcode::OP_GetLevelObject);
break;
case ast::kind::expr_anim:
emit_opcode(opcode::OP_GetAnimObject);
break;
case ast::kind::expr_self:
emit_opcode(opcode::OP_GetSelfObject);
break;
case ast::kind::expr_array:
emit_expr_array(expr.as_array, blk);
emit_opcode(opcode::OP_CastFieldObject);
break;
case ast::kind::expr_field:
emit_expr_field(expr.as_field, blk);
emit_opcode(opcode::OP_CastFieldObject);
break;
case ast::kind::expr_call:
emit_expr_call(expr.as_call, blk);
emit_opcode(opcode::OP_CastFieldObject);
break;
case ast::kind::expr_method:
emit_expr_method(expr.as_method, blk);
emit_opcode(opcode::OP_CastFieldObject);
break;
case ast::kind::expr_identifier:
emit_opcode(opcode::OP_EvalLocalVariableObjectCached, variable_access_index(expr.as_identifier, blk));
break;
default:
throw comp_error(expr.loc(), "not an object");
}
}
void compiler::emit_expr_vector(const ast::expr_vector::ptr& expr, const block::ptr& blk)
{
std::vector<std::string> data;
bool isexpr = false;
if (expr->x == ast::kind::expr_integer)
data.push_back(expr->x.as_integer->value);
else if (expr->x == ast::kind::expr_float)
data.push_back(expr->x.as_float->value);
else isexpr = true;
if (expr->y == ast::kind::expr_integer)
data.push_back(expr->y.as_integer->value);
else if (expr->y == ast::kind::expr_float)
data.push_back(expr->y.as_float->value);
else isexpr = true;
if (expr->z == ast::kind::expr_integer)
data.push_back(expr->z.as_integer->value);
else if (expr->z == ast::kind::expr_float)
data.push_back(expr->z.as_float->value);
else isexpr = true;
if (!isexpr)
{
emit_opcode(opcode::OP_GetVector, data);
}
else
{
emit_expr(expr->z, blk);
emit_expr(expr->y, blk);
emit_expr(expr->x, blk);
emit_opcode(opcode::OP_vector);
}
}
void compiler::emit_expr_animation(const ast::expr_animation::ptr& expr)
{
if (animtrees_.size() == 0)
{
throw comp_error(expr->loc(), "trying to use animation without specified using animtree");
}
auto& tree = animtrees_.back();
if (tree.loaded)
{
emit_opcode(opcode::OP_GetAnimation, { "''", expr->value });
}
else
{
emit_opcode(opcode::OP_GetAnimation, { tree.name, expr->value });
tree.loaded = true;
}
}
void compiler::emit_expr_animtree(const ast::expr_animtree::ptr& expr)
{
if (animtrees_.size() == 0)
{
throw comp_error( expr->loc(), "trying to use animtree without specified using animtree");
}
auto& tree = animtrees_.back();
if (tree.loaded)
{
emit_opcode(opcode::OP_GetAnimTree, "''");
}
else
{
emit_opcode(opcode::OP_GetAnimTree, tree.name);
tree.loaded = true;
}
}
void compiler::emit_expr_istring(const ast::expr_istring::ptr& expr)
{
emit_opcode(opcode::OP_GetIString, expr->value);
}
void compiler::emit_expr_string(const ast::expr_string::ptr& expr)
{
emit_opcode(opcode::OP_GetString, expr->value);
}
void compiler::emit_expr_color(const ast::expr_color::ptr& expr)
{
std::vector<std::string> data;
std::string x, y, z;
if (expr->value.size() == 3)
{
x = "0x" + expr->value.substr(0, 1) + expr->value.substr(0, 1);
y = "0x" + expr->value.substr(1, 1) + expr->value.substr(1, 1);
z = "0x" + expr->value.substr(2, 1) + expr->value.substr(2, 1);
}
else
{
x = "0x" + expr->value.substr(0, 2);
y = "0x" + expr->value.substr(2, 2);
z = "0x" + expr->value.substr(4, 2);
}
data.push_back(utils::string::hex_to_dec(x.data()));
data.push_back(utils::string::hex_to_dec(y.data()));
data.push_back(utils::string::hex_to_dec(z.data()));
emit_opcode(opcode::OP_GetVector, data);
}
void compiler::emit_expr_float(const ast::expr_float::ptr& expr)
{
emit_opcode(opcode::OP_GetFloat, expr->value);
}
void compiler::emit_expr_integer(const ast::expr_integer::ptr& expr)
{
auto value = std::atoi(expr->value.data());
if (value == 0)
{
emit_opcode(opcode::OP_GetZero);
}
else if (value > 0 && value < 256)
{
emit_opcode(opcode::OP_GetByte, expr->value);
}
else if (value < 0 && value > -256)
{
emit_opcode(opcode::OP_GetNegByte, expr->value.substr(1));
}
else if (value > 0 && value < 65536)
{
emit_opcode(opcode::OP_GetUnsignedShort, expr->value);
}
else if (value < 0 && value > -65536)
{
emit_opcode(opcode::OP_GetNegUnsignedShort, expr->value.substr(1));
}
else
{
emit_opcode(opcode::OP_GetInteger, expr->value);
}
}
void compiler::emit_expr_false(const ast::expr_false::ptr&)
{
emit_opcode(opcode::OP_GetZero);
}
void compiler::emit_expr_true(const ast::expr_true::ptr&)
{
emit_opcode(opcode::OP_GetByte, "1");
}
void compiler::emit_create_local_vars(const block::ptr& blk)
{
if ( blk->local_vars_create_count != blk->local_vars_public_count )
{
for (auto i = blk->local_vars_create_count; i < blk->local_vars_public_count; i++)
{
auto data = utils::string::va("%d", blk->local_vars.at(i).create);
emit_opcode(opcode::OP_CreateLocalVariable, data);
blk->local_vars.at(i).init = true;
}
blk->local_vars_create_count = blk->local_vars_public_count;
}
}
void compiler::emit_remove_local_vars(const block::ptr& blk)
{
if (blk->abort == abort_t::abort_none)
{
auto count = blk->local_vars_create_count - blk->local_vars_public_count;
if (count > 0)
{
auto data = utils::string::va("%d", count);
emit_opcode(opcode::OP_RemoveLocalVariables, data);
}
}
}
void compiler::emit_opcode(opcode op)
{
function_->instructions.push_back(std::make_unique<instruction>());
auto& inst = function_->instructions.back();
inst->opcode = static_cast<std::uint8_t>(op);
inst->size = opcode_size(std::uint8_t(op));
inst->index = index_;
index_ += inst->size;
}
void compiler::emit_opcode(opcode op, const std::string& data)
{
function_->instructions.push_back(std::make_unique<instruction>());
auto& inst = function_->instructions.back();
inst->opcode = static_cast<std::uint8_t>(op);
inst->size = opcode_size(std::uint8_t(op));
inst->index = index_;
inst->data.push_back(data);
index_ += inst->size;
}
void compiler::emit_opcode(opcode op, const std::vector<std::string>& data)
{
function_->instructions.push_back(std::make_unique<instruction>());
auto& inst = function_->instructions.back();
inst->opcode = static_cast<std::uint8_t>(op);
inst->size = opcode_size(std::uint8_t(op));
inst->index = index_;
inst->data = data;
index_ += inst->size;
}
void compiler::process_thread(const ast::decl_thread::ptr& decl, const block::ptr& blk)
{
process_expr_parameters(decl->params, blk);
process_stmt_list(decl->stmt, blk);
}
void compiler::process_stmt(const ast::stmt& stmt, const block::ptr& blk)
{
switch (stmt.kind())
{
case ast::kind::stmt_list:
process_stmt_list(stmt.as_list, blk);
break;
case ast::kind::stmt_expr:
process_stmt_expr(stmt.as_expr, blk);
break;
case ast::kind::stmt_assign:
process_stmt_assign(stmt.as_assign, blk);
break;
case ast::kind::stmt_waittill:
process_stmt_waittill(stmt.as_waittill, blk);
break;
case ast::kind::stmt_if:
process_stmt_if(stmt.as_if, blk);
break;
case ast::kind::stmt_ifelse:
process_stmt_ifelse(stmt.as_ifelse, blk);
break;
case ast::kind::stmt_while:
process_stmt_while(stmt.as_while, blk);
break;
case ast::kind::stmt_dowhile:
process_stmt_dowhile(stmt.as_dowhile, blk);
break;
case ast::kind::stmt_for:
process_stmt_for(stmt.as_for, blk);
break;
case ast::kind::stmt_foreach:
process_stmt_foreach(stmt.as_foreach, blk);
break;
case ast::kind::stmt_switch:
process_stmt_switch(stmt.as_switch, blk);
break;
case ast::kind::stmt_break:
process_stmt_break(stmt.as_break, blk);
break;
case ast::kind::stmt_continue:
process_stmt_continue(stmt.as_continue, blk);
break;
case ast::kind::stmt_return:
process_stmt_return(stmt.as_return, blk);
break;
case ast::kind::stmt_call:
case ast::kind::stmt_endon:
case ast::kind::stmt_notify:
case ast::kind::stmt_wait:
case ast::kind::stmt_waittillmatch:
case ast::kind::stmt_waittillframeend:
case ast::kind::stmt_case:
case ast::kind::stmt_default:
case ast::kind::stmt_breakpoint:
case ast::kind::stmt_prof_begin:
case ast::kind::stmt_prof_end:
break;
default:
throw comp_error(stmt.loc(), "unknown statement");
}
}
void compiler::process_stmt_list(const ast::stmt_list::ptr& stmt, const block::ptr& blk)
{
for (const auto& entry : stmt->list)
{
process_stmt(entry, blk);
}
}
void compiler::process_stmt_expr(const ast::stmt_expr::ptr& stmt, const block::ptr& blk)
{
switch (stmt->expr.kind())
{
case ast::kind::expr_increment:
process_expr(stmt->expr.as_increment->lvalue, blk);
break;
case ast::kind::expr_decrement:
process_expr(stmt->expr.as_decrement->lvalue, blk);
break;
case ast::kind::expr_assign_equal:
case ast::kind::expr_assign_add:
case ast::kind::expr_assign_sub:
case ast::kind::expr_assign_mul:
case ast::kind::expr_assign_div:
case ast::kind::expr_assign_mod:
case ast::kind::expr_assign_shift_left:
case ast::kind::expr_assign_shift_right:
case ast::kind::expr_assign_bitwise_or:
case ast::kind::expr_assign_bitwise_and:
case ast::kind::expr_assign_bitwise_exor:
process_expr(stmt->expr.as_assign->lvalue, blk);
break;
case ast::kind::null:
break;
default:
throw comp_error(stmt->loc(), "unknown expr statement expression");
}
}
void compiler::process_stmt_assign(const ast::stmt_assign::ptr& stmt, const block::ptr& blk)
{
switch (stmt->expr.kind())
{
case ast::kind::expr_increment:
process_expr(stmt->expr.as_increment->lvalue, blk);
break;
case ast::kind::expr_decrement:
process_expr(stmt->expr.as_decrement->lvalue, blk);
break;
case ast::kind::expr_assign_equal:
case ast::kind::expr_assign_add:
case ast::kind::expr_assign_sub:
case ast::kind::expr_assign_mul:
case ast::kind::expr_assign_div:
case ast::kind::expr_assign_mod:
case ast::kind::expr_assign_shift_left:
case ast::kind::expr_assign_shift_right:
case ast::kind::expr_assign_bitwise_or:
case ast::kind::expr_assign_bitwise_and:
case ast::kind::expr_assign_bitwise_exor:
process_expr(stmt->expr.as_assign->lvalue, blk);
break;
default:
throw comp_error(stmt->loc(), "unknown assign statement expression");
}
}
void compiler::process_stmt_waittill(const ast::stmt_waittill::ptr& stmt, const block::ptr& blk)
{
for (const auto& entry : stmt->args->list)
{
if (entry != ast::kind::expr_identifier)
{
throw comp_error(entry.loc(), "illegal waittill param, must be a local variable");
}
register_variable(entry.as_identifier->value, blk);
}
}
void compiler::process_stmt_if(const ast::stmt_if::ptr& stmt, const block::ptr& blk)
{
stmt->blk = std::make_unique<block>();
blk->copy(stmt->blk);
process_stmt(stmt->stmt, stmt->blk);
std::vector<block*> childs({ stmt->blk.get() });
blk->merge(childs);
}
void compiler::process_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt, const block::ptr& blk)
{
std::vector<block*> childs;
auto abort = abort_t::abort_return;
stmt->blk_if = std::make_unique<block>();
stmt->blk_else = std::make_unique<block>();
blk->copy(stmt->blk_if);
process_stmt(stmt->stmt_if, stmt->blk_if);
if (stmt->blk_if->abort <= abort_t::abort_return)
{
abort = stmt->blk_if->abort;
if (abort == abort_t::abort_none)
childs.push_back(stmt->blk_if.get());
}
blk->copy(stmt->blk_else);
process_stmt(stmt->stmt_else, stmt->blk_else);
if (stmt->blk_else->abort <= abort)
{
abort = stmt->blk_else->abort;
if (abort == abort_t::abort_none)
childs.push_back(stmt->blk_else.get());
}
if (blk->abort == abort_t::abort_none)
blk->abort = abort;
blk->append(childs);
blk->merge(childs);
}
void compiler::process_stmt_while(const ast::stmt_while::ptr& stmt, const block::ptr& blk)
{
bool const_cond = is_constant_condition(stmt->test);
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
break_blks_.clear();
continue_blks_.clear();
stmt->blk = std::make_unique<block>();
blk->copy(stmt->blk);
process_stmt(stmt->stmt, stmt->blk);
continue_blks_.push_back(stmt->blk.get());
for (auto i = 0; i < continue_blks_.size(); i++)
blk->append({continue_blks_.at(i)});
if (const_cond) blk->append(break_blks_);
blk->merge({stmt->blk.get()});
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::process_stmt_dowhile(const ast::stmt_dowhile::ptr& stmt, const block::ptr& blk)
{
bool const_cond = is_constant_condition(stmt->test);
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
break_blks_.clear();
continue_blks_.clear();
stmt->blk = std::make_unique<block>();
blk->copy(stmt->blk);
process_stmt(stmt->stmt, stmt->blk);
continue_blks_.push_back(stmt->blk.get());
for (auto i = 0; i < continue_blks_.size(); i++)
blk->append({continue_blks_.at(i)});
if (const_cond) blk->append(break_blks_);
blk->merge({stmt->blk.get()});
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::process_stmt_for(const ast::stmt_for::ptr& stmt, const block::ptr& blk)
{
bool const_cond = is_constant_condition(stmt->test);
stmt->blk = std::make_unique<block>();
stmt->blk_iter = std::make_unique<block>();
process_stmt(stmt->init, blk);
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
break_blks_.clear();
continue_blks_.clear();
blk->copy(stmt->blk);
blk->copy(stmt->blk_iter);
process_stmt(stmt->stmt, stmt->blk);
continue_blks_.push_back(stmt->blk.get());
for (auto i = 0; i < continue_blks_.size(); i++)
blk->append({continue_blks_.at(i)});
process_stmt(stmt->iter, stmt->blk_iter);
blk->append({ stmt->blk_iter.get() });
blk->merge({ stmt->blk_iter.get() });
if (const_cond) blk->append(break_blks_);
blk->merge({stmt->blk.get()});
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::process_stmt_foreach(const ast::stmt_foreach::ptr& stmt, const block::ptr& blk)
{
auto array_name = utils::string::va("temp_%d", ++label_idx_);
auto key_name = utils::string::va("temp_%d", ++label_idx_);
stmt->array = ast::expr(std::make_unique<ast::expr_identifier>(stmt->loc(), array_name));
if (!stmt->use_key)
stmt->key_expr = ast::expr(std::make_unique<ast::expr_identifier>(stmt->loc(), key_name));
key_name = stmt->key_expr.as_identifier->value;
// calculate variables
stmt->ctx = std::make_unique<block>();
stmt->ctx_post = std::make_unique<block>();
// calculate pre_expr variables
process_expr(stmt->array, blk);
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
break_blks_.clear();
continue_blks_.clear();
blk->copy(stmt->ctx);
blk->copy(stmt->ctx_post);
// calculate stmt variables & add missing array access as first stmt
process_expr(stmt->value_expr, stmt->ctx);
process_stmt(stmt->stmt, stmt->ctx);
continue_blks_.push_back(stmt->ctx.get());
for (auto i = 0; i < continue_blks_.size(); i++)
blk->append({continue_blks_.at(i)});
process_expr(stmt->key_expr, stmt->ctx_post);
blk->append({ stmt->ctx_post.get() });
blk->merge({ stmt->ctx_post.get() });
blk->merge({stmt->ctx.get()});
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::process_stmt_switch(const ast::stmt_switch::ptr& stmt, const block::ptr& blk)
{
auto stmt_list = std::make_unique<ast::stmt_list>(stmt->stmt->loc());
auto current_case = ast::stmt(nullptr);
auto num = stmt->stmt->list.size();
for (auto i = 0; i < num; i++)
{
auto& entry = stmt->stmt->list[0];
if (entry == ast::kind::stmt_case || entry == ast::kind::stmt_default)
{
if (current_case.as_node != nullptr)
{
stmt_list->list.push_back(std::move(current_case));
}
current_case = std::move(stmt->stmt->list[0]);
stmt->stmt->list.erase(stmt->stmt->list.begin());
}
else
{
if (current_case.as_node != nullptr)
{
if (current_case == ast::kind::stmt_case)
{
current_case.as_case->stmt->list.push_back(std::move(entry));
stmt->stmt->list.erase(stmt->stmt->list.begin());
}
else
{
current_case.as_default->stmt->list.push_back(std::move(entry));
stmt->stmt->list.erase(stmt->stmt->list.begin());
}
}
else
{
throw comp_error(entry.loc(), "missing case statement");
}
}
}
if (current_case.as_node != nullptr)
{
stmt_list->list.push_back(std::move(current_case));
}
// calculate variables
stmt->ctx = std::make_unique<block>();
std::vector<block*> childs;
auto abort = abort_t::abort_return;
bool has_default = false;
block* default_ctx = nullptr;
auto old_breaks = break_blks_;
break_blks_.clear();
for (auto i = 0; i < stmt_list->list.size(); i++)
{
auto& entry = stmt_list->list[i];
if (entry == ast::kind::stmt_case)
{
entry.as_case->blk = std::make_unique<block>();
blk->copy(entry.as_case->blk);
process_stmt_list(entry.as_case->stmt, entry.as_case->blk);
if (entry.as_case->blk->abort != abort_t::abort_none)
{
if (entry.as_case->blk->abort == abort_t::abort_break )
{
entry.as_case->blk->abort = abort_t::abort_none;
abort = abort_t::abort_none;
childs.push_back(entry.as_case->blk.get());
}
else if (entry.as_case->blk->abort <= abort )
{
abort = entry.as_case->blk->abort;
}
}
}
else if (entry == ast::kind::stmt_default)
{
entry.as_default->blk = std::make_unique<block>();
blk->copy(entry.as_default->blk);
process_stmt_list(entry.as_default->stmt, entry.as_default->blk);
has_default = true;
default_ctx = entry.as_default->blk.get();
if (entry.as_default->blk->abort != abort_t::abort_none)
{
if (entry.as_default->blk->abort == abort_t::abort_break )
{
entry.as_default->blk->abort = abort_t::abort_none;
abort = abort_t::abort_none;
childs.push_back(entry.as_default->blk.get());
}
else if (entry.as_default->blk->abort <= abort )
{
abort = entry.as_default->blk->abort;
}
}
}
}
stmt->stmt =std::move(stmt_list);
if (has_default)
{
if (default_ctx->abort == abort_t::abort_none)
{
break_blks_.push_back(default_ctx);
if (blk->abort == abort_t::abort_none)
blk->abort = abort;
}
blk->append(break_blks_);
blk->merge(childs);
}
break_blks_ = old_breaks;
}
void compiler::process_stmt_break(const ast::stmt_break::ptr& stmt, const block::ptr& blk)
{
if (blk->abort == abort_t::abort_none)
{
break_blks_.push_back(blk.get());
blk->abort = abort_t::abort_break;
}
}
void compiler::process_stmt_continue(const ast::stmt_continue::ptr& stmt, const block::ptr& blk)
{
if (blk->abort == abort_t::abort_none)
{
continue_blks_.push_back(blk.get());
blk->abort = abort_t::abort_continue;
}
}
void compiler::process_stmt_return(const ast::stmt_return::ptr& stmt, const block::ptr& blk)
{
if (blk->abort == abort_t::abort_none)
{
blk->abort = abort_t::abort_return;
}
}
void compiler::process_expr(const ast::expr& expr, const block::ptr& blk)
{
if (expr == ast::kind::expr_identifier)
{
register_variable(expr.as_identifier->value, blk);
}
else if (expr == ast::kind::expr_array)
{
process_expr(expr.as_array->obj, blk);
}
}
void compiler::process_expr_parameters(const ast::expr_parameters::ptr& decl, const block::ptr& blk)
{
for (const auto& entry : decl->list)
{
register_variable(entry->value, blk);
}
}
void compiler::register_variable(const std::string& name, const block::ptr& blk)
{
auto it = std::find_if (blk->local_vars.begin(), blk->local_vars.end(),
[&](const gsc::local_var& v) { return v.name == name; });
if (it == blk->local_vars.end())
{
auto found = false;
for (std::size_t i = 0; i < local_stack_.size(); i++)
{
if (local_stack_[i] == name)
{
blk->local_vars.push_back({ name, static_cast<uint8_t>(i), false });
found = true;
break;
}
}
if (!found)
{
blk->local_vars.push_back({ name, stack_idx_, false });
local_stack_.push_back(name);
stack_idx_++;
}
}
}
void compiler::initialize_variable(const ast::expr_identifier::ptr& name, const block::ptr& blk)
{
for (std::uint32_t i = 0; i < blk->local_vars.size(); i++)
{
if (blk->local_vars[i].name == name->value)
{
if (!blk->local_vars[i].init)
{
for (std::uint32_t j = 0; j < i; j++)
{
if (!blk->local_vars[j].init)
{
emit_opcode(opcode::OP_CreateLocalVariable, utils::string::va("%d", blk->local_vars[j].create));
blk->local_vars[j].init = true;
//ctx->local_vars_create_count++;
}
}
blk->local_vars[i].init = true;
blk->local_vars_create_count = i + 1;
return;
}
}
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not found.");
}
void compiler::create_variable(const ast::expr_identifier::ptr& name, const block::ptr& blk)
{
for (std::size_t i = 0; i < blk->local_vars.size(); i++)
{
auto& var = blk->local_vars.at(i);
if (var.name == name->value)
{
if (!var.init)
{
emit_opcode(opcode::OP_CreateLocalVariable, utils::string::va("%d", var.create));
var.init = true;
blk->local_vars_create_count++;
}
return;
}
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not found.");
}
auto compiler::variable_stack_index(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> std::uint8_t
{
for (std::size_t i = 0; i < blk->local_vars.size(); i++)
{
if (blk->local_vars[i].name == name->value)
{
if (blk->local_vars.at(i).init)
{
return blk->local_vars_create_count - 1 - i;
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not initialized.");
}
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not found.");
}
auto compiler::variable_create_index(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> std::string
{
for (std::size_t i = 0; i < blk->local_vars.size(); i++)
{
if (blk->local_vars[i].name == name->value)
return utils::string::va("%d", blk->local_vars[i].create);
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not found.");
}
auto compiler::variable_access_index(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> std::string
{
for (std::size_t i = 0; i < blk->local_vars.size(); i++)
{
if (blk->local_vars[i].name == name->value)
{
if (blk->local_vars.at(i).init)
{
return utils::string::va("%d", blk->local_vars_create_count - 1 - i);
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not initialized.");
}
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not found.");
}
auto compiler::variable_initialized(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> bool
{
for (std::size_t i = 0; i < blk->local_vars.size(); i++)
{
if (blk->local_vars[i].name == name->value)
{
return blk->local_vars.at(i).init;
}
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not found.");
}
auto compiler::resolve_function_type(const ast::expr_function::ptr& expr) -> ast::call::type
{
if (expr->path->value != "")
return ast::call::type::far;
auto& name = expr->name->value;
if (resolver::find_function(name) || resolver::find_method(name))
return ast::call::type::builtin;
for (const auto& entry : local_functions_)
{
if (entry == name)
return ast::call::type::local;
}
for (const auto& inc : includes_)
{
for (const auto& fun : inc.funcs)
{
if (name == fun)
{
expr->path->value = inc.name;
return ast::call::type::far;
}
}
}
throw comp_error(expr->loc(), "couldn't determine function type");
}
auto compiler::resolve_reference_type(const ast::expr_reference::ptr& expr, bool& method) -> ast::call::type
{
if (expr->path->value != "")
return ast::call::type::far;
auto& name = expr->name->value;
if (resolver::find_function(name))
{
method = false;
return ast::call::type::builtin;
}
if (resolver::find_method(name))
{
method = true;
return ast::call::type::builtin;
}
for (const auto& entry : local_functions_)
{
if (entry == name)
return ast::call::type::local;
}
for (const auto& inc : includes_)
{
for (const auto& fun : inc.funcs)
{
if (name == fun)
{
expr->path->value = inc.name;
return ast::call::type::far;
}
}
}
throw comp_error(expr->loc(), "couldn't determine function reference type");
}
auto compiler::is_constant_condition(const ast::expr& expr) -> bool
{
switch (expr.kind())
{
case ast::kind::null:
case ast::kind::expr_true:
return true;
case ast::kind::expr_false:
throw comp_error(expr.loc(), "condition can't be always false!");
case ast::kind::expr_integer:
{
auto num = std::stoi(expr.as_integer->value);
if (num != 0)
return true;
else
throw comp_error(expr.loc(), "condition can't be always false!");
}
default:
break;
}
return false;
}
auto compiler::create_label() -> std::string
{
label_idx_++;
auto name = utils::string::va("loc_%d", label_idx_);
return name;
}
auto compiler::insert_label() -> std::string
{
const auto itr = function_->labels.find(index_);
if (itr != function_->labels.end())
{
return itr->second;
}
else
{
label_idx_++;
auto name = utils::string::va("loc_%d", label_idx_);
function_->labels.insert({index_, name});
return name;
}
}
void compiler::insert_label(const std::string& name)
{
const auto itr = function_->labels.find(index_);
if (itr != function_->labels.end())
{
for (auto& inst : function_->instructions)
{
switch (opcode(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});
}
}
gsc::include_t compiler::include_maps_mp_utility_ =
{
"maps/mp/_utility",
{
"exploder_sound",
"_beginlocationselection",
"stoplocationselection",
"endselectiononemp",
"endselectiononaction",
"endselectiononendgame",
"isattachment",
"getattachmenttype",
"delaythread",
"delaythread_proc",
"getplant",
"orienttonormal",
"deleteplacedentity",
"playsoundonplayers",
"sortlowermessages",
"addlowermessage",
"removelowermessage",
"getlowermessage",
"setlowermessage",
"updatelowermessage",
"clearondeath",
"clearafterfade",
"clearlowermessage",
"clearlowermessages",
"printonteam",
"printboldonteam",
"printboldonteamarg",
"printonteamarg",
"printonplayers",
"printandsoundoneveryone",
"printandsoundonteam",
"printandsoundonplayer",
"_playlocalsound",
"dvarintvalue",
"dvarfloatvalue",
"play_sound_on_tag",
"getotherteam",
"wait_endon",
"initpersstat",
"getpersstat",
"incpersstat",
"setpersstat",
"initplayerstat",
"incplayerstat",
"setplayerstat",
"getplayerstat",
"getplayerstattime",
"setplayerstatifgreater",
"setplayerstatiflower",
"updatepersratio",
"updatepersratiobuffered",
"waittillslowprocessallowed",
"waitfortimeornotify",
"isexcluded",
"leaderdialog",
"leaderdialogbothteams",
"leaderdialogonplayers",
"leaderdialogonplayer",
"playleaderdialogonplayer",
"updatemainmenu",
"updateobjectivetext",
"setobjectivetext",
"setobjectivescoretext",
"setobjectivehinttext",
"getobjectivetext",
"getobjectivescoretext",
"getobjectivehinttext",
"gettimepassed",
"getsecondspassed",
"getminutespassed",
"clearkillcamstate",
"isinkillcam",
"isvalidclass",
"getvalueinrange",
"waitfortimeornotifies",
"closemenus",
"logxpgains",
"registerroundswitchdvar",
"registerroundlimitdvar",
"registerwinlimitdvar",
"registerscorelimitdvar",
"registertimelimitdvar",
"registerhalftimedvar",
"registernumlivesdvar",
"setovertimelimitdvar",
"get_damageable_player",
"get_damageable_sentry",
"get_damageable_grenade",
"get_damageable_mine",
"get_damageable_player_pos",
"getstancecenter",
"get_damageable_grenade_pos",
"getdvarvec",
"strip_suffix",
"_takeweaponsexcept",
"savedata",
"restoredata",
"_setactionslot",
"isfloat",
"registerwatchdvarint",
"registerwatchdvarfloat",
"registerwatchdvar",
"setoverridewatchdvar",
"getwatcheddvar",
"updatewatcheddvars",
"isroundbased",
"islastround",
"wasonlyround",
"waslastround",
"hitroundlimit",
"hitscorelimit",
"hitwinlimit",
"getscorelimit",
"getroundswon",
"isobjectivebased",
"gettimelimit",
"gethalftime",
"inovertime",
"gamehasstarted",
"getaverageorigin",
"getlivingplayers",
"setusingremote",
"getremotename",
"freezecontrolswrapper",
"clearusingremote",
"isusingremote",
"queuecreate",
"queueadd",
"queueremovefirst",
"_giveweapon",
"_hasperk",
"giveperk",
"_setperk",
"_setextraperks",
"_unsetperk",
"_unsetextraperks",
"_clearperks",
"quicksort",
"quicksortmid",
"swap",
"_suicide",
"isreallyalive",
"playdeathsound",
"rankingenabled",
"privatematch",
"matchmakinggame",
"setaltsceneobj",
"endsceneondeath",
"getgametypenumlives",
"givecombathigh",
"arrayinsertion",
"getproperty",
"getintproperty",
"getfloatproperty",
"statusmenu",
"ischangingweapon",
"killshouldaddtokillstreak",
"streakshouldchain",
"isjuggernaut",
"iskillstreakweapon",
"isenvironmentweapon",
"getweaponclass",
"isdeathstreakweapon",
"getbaseweaponname",
"fixakimbostring",
"playsoundinspace",
"limitdecimalplaces",
"rounddecimalplaces",
"playerforclientid",
"isrested",
"stringtofloat",
"setselfusable",
"maketeamusable",
"_updateteamusable",
"makeenemyusable",
"_updateenemyusable",
"getnextlifeid",
"initgameflags",
"gameflaginit",
"gameflag",
"gameflagset",
"gameflagclear",
"gameflagwait",
"isprimarydamage",
"isbulletdamage",
"initlevelflags",
"levelflaginit",
"levelflag",
"levelflagset",
"levelflagclear",
"levelflagwait",
"levelflagwaitopen",
"getweaponattachments",
"isemped",
"isairdenied",
"isnuked",
"getplayerforguid",
"teamplayercardsplash",
"iscacprimaryweapon",
"iscacsecondaryweapon",
"getlastlivingplayer",
"getpotentiallivingplayers",
"waittillrecoveredhealth",
"attachmentmap",
"validateattachment",
"_objective_delete",
"touchingbadtrigger",
"setthirdpersondof",
"killtrigger",
"findisfacing",
"combinearrays",
"setrecoilscale",
"cleanarray",
"notusableforjoiningplayers",
"isstrstart",
"validateusestreak",
"currentactivevehiclecount",
"maxvehiclesallowed",
"incrementfauxvehiclecount",
"decrementfauxvehiclecount",
"lightweightscalar",
"allowteamchoice",
"allowclasschoice",
"isbuffunlockedforweapon",
"isbuffequippedonweapon",
"setcommonrulesfrommatchrulesdata",
"reinitializematchrulesonmigration",
"getmatchrulesspecialclass",
"recipeclassapplyjuggernaut",
}
};
gsc::include_t compiler::include_common_scripts_createfx_ =
{
"common_scripts/_createfx",
{
"createeffect",
"getloopeffectdelaydefault",
"getoneshoteffectdelaydefault",
"getexploderdelaydefault",
"getintervalsounddelaymindefault",
"getintervalsounddelaymaxdefault",
"add_effect",
"createloopsound",
"createintervalsound",
"createnewexploder",
"createexploderex",
"set_origin_and_angles",
"set_forward_and_up_vectors",
"createfx_common",
"createfxlogic",
"copy_angles_of_selected_ents",
"reset_axis_of_selected_ents",
"last_selected_entity_has_changed",
"createfx_showorigin",
"drop_selection_to_ground",
"set_off_exploders",
"draw_distance",
"createfx_autosave",
"rotate_over_time",
"delete_pressed",
"remove_selected_option",
"remove_option",
"delete_selection",
"move_selection_to_cursor",
"insert_effect",
"show_help",
"select_last_entity",
"select_all_exploders_of_currently_selected",
"copy_ents",
"post_entity_creation_function",
"paste_ents",
"add_and_select_entity",
"get_center_of_array",
"ent_draw_axis",
"rotation_is_occuring",
"print_fx_options",
"entity_highlight_disable",
"entity_highlight_enable",
"toggle_createfx_drawing",
"manipulate_createfx_ents",
"clear_settable_fx",
"reset_fx_hud_colors",
"button_is_held",
"button_is_clicked",
"toggle_entity_selection",
"select_entity",
"ent_is_highlighted",
"deselect_entity",
"index_is_selected",
"ent_is_selected",
"clear_entity_selection",
"draw_axis",
"clear_fx_hudelements",
"set_fx_hudelement",
"createfx_centerprint",
"createfx_centerprint_thread",
"buttondown",
"buttonpressed_internal",
"get_selected_move_vector",
"process_button_held_and_clicked",
"locked",
"kb_locked",
"add_button",
"add_kb_button",
"set_anglemod_move_vector",
"cfxprintlnstart",
"cfxprintln",
"cfxprintlnend",
"update_selected_entities",
"hack_start",
"get_player",
"createfx_orgranize_array",
"stop_fx_looper",
"stop_loopsound",
"func_get_level_fx",
"restart_fx_looper",
"process_fx_rotater",
"generate_fx_log",
}
};
gsc::include_t compiler::include_common_scripts_utility_ =
{
"common_scripts/utility",
{
"scriptprintln",
"debugprintln",
"draw_debug_line",
"waittillend",
"noself_func",
"self_func",
"randomvector",
"randomvectorrange",
"angle_dif",
"sign",
"track",
"get_enemy_team",
"clear_exception",
"set_exception",
"set_all_exceptions",
"cointoss",
"choose_from_weighted_array",
"get_cumulative_weights",
"waittill_string",
"waittill_multiple",
"waittill_multiple_ents",
"waittill_any_return",
"waittill_any_timeout",
"_timeout",
"waittill_any",
"waittill_any_ents",
"isflashed",
"flag_exist",
"flag",
"init_flags",
"flag_init",
"empty_init_func",
"issuffix",
"flag_set",
"assign_unique_id",
"flag_wait",
"flag_clear",
"flag_waitopen",
"waittill_either",
"array_thread",
"array_call",
"array_thread4",
"array_thread5",
"trigger_on",
"trigger_on_proc",
"trigger_off",
"trigger_off_proc",
"set_trigger_flag_permissions",
"update_trigger_based_on_flags",
"create_flags_and_return_tokens",
"init_trigger_flags",
"getstruct",
"getstructarray",
"struct_class_init",
"fileprint_start",
"fileprint_map_start",
"fileprint_map_header",
"fileprint_map_keypairprint",
"fileprint_map_entity_start",
"fileprint_map_entity_end",
"fileprint_radiant_vec",
"array_remove",
"array_remove_array",
"array_removeundefined",
"array_levelthread",
"array_levelcall",
"add_to_array",
"flag_assert",
"flag_wait_either",
"flag_wait_either_return",
"flag_wait_any",
"flag_wait_any_return",
"flag_wait_all",
"flag_wait_or_timeout",
"flag_waitopen_or_timeout",
"wait_for_flag_or_time_elapses",
"delaycall",
"delaycall_proc",
"noself_delaycall",
"noself_delaycall_proc",
"issp",
"issp_towerdefense",
"string_starts_with",
"plot_points",
"draw_line_for_time",
"array_combine",
"flat_angle",
"flat_origin",
"draw_arrow_time",
"get_linked_ents",
"get_linked_vehicle_nodes",
"get_linked_ent",
"get_linked_vehicle_node",
"get_links",
"run_thread_on_targetname",
"run_thread_on_noteworthy",
"draw_arrow",
"getfx",
"fxexists",
"print_csv_asset",
"fileprint_csv_start",
"_loadfx",
"getlastweapon",
"playerunlimitedammothread",
"isusabilityenabled",
"_disableusability",
"_enableusability",
"resetusability",
"_disableweapon",
"_enableweapon",
"isweaponenabled",
"_disableweaponswitch",
"_enableweaponswitch",
"isweaponswitchenabled",
"_disableoffhandweapons",
"_enableoffhandweapons",
"isoffhandweaponenabled",
"random",
"spawn_tag_origin",
"waittill_notify_or_timeout",
"fileprint_launcher_start_file",
"fileprint_launcher",
"fileprint_launcher_end_file",
"launcher_write_clipboard",
"isdestructible",
"pauseeffect",
"activate_individual_exploder",
"waitframe",
"brush_delete",
"brush_throw",
"get_target_ent",
"brush_show",
"exploder_earthquake",
"do_earthquake",
"exploder_rumble",
"exploder_delay",
"exploder_damage",
"effect_loopsound",
"play_loopsound_in_space",
"sound_effect",
"effect_soundalias",
"play_sound_in_space",
"cannon_effect",
"exploder_playsound",
"fire_effect",
"loop_fx_sound",
"loop_fx_sound_interval",
"loop_sound_delete",
"exploder_before_load",
"exploder_after_load",
"activate_exploder",
"createloopeffect",
"createoneshoteffect",
"createexploder",
"alphabetize",
"is_later_in_alphabet",
"alphabet_compare",
"play_loop_sound_on_entity",
"stop_loop_sound_on_entity",
"delete_on_death",
"error",
"exploder",
"create_dvar",
"void",
"tag_project",
"ter_op",
"create_lock",
"lock",
"is_locked",
"unlock_wait",
"unlock",
"unlock_thread",
"get_template_level",
}
};
gsc::include_t compiler::include_maps_mp_gametypes_hud_util_ =
{
"maps/mp/gametypes/_hud_util",
{
"setparent",
"getparent",
"addchild",
"removechild",
"setpoint",
"setpointbar",
"updatebar",
"updatebarscale",
"createfontstring",
"createserverfontstring",
"createservertimer",
"createtimer",
"createicon",
"createservericon",
"createserverbar",
"createbar",
"getcurrentfraction",
"createprimaryprogressbar",
"createprimaryprogressbartext",
"createteamprogressbar",
"createteamprogressbartext",
"setflashfrac",
"hideelem",
"showelem",
"flashthread",
"destroyelem",
"seticonshader",
"geticonshader",
"seticonsize",
"setwidth",
"setheight",
"setsize",
"updatechildren",
"transitionreset",
"transitionzoomin",
"transitionpulsefxin",
"transitionslidein",
"transitionslideout",
"transitionzoomout",
"transitionfadein",
"transitionfadeout",
"getweeklyref",
"getdailyref",
"ch_getprogress",
"ch_getstate",
"ch_setprogress",
"ch_setstate",
"ch_gettarget",
}
};
auto compiler::map_known_includes(const std::string& include) -> bool
{
if (include == "maps/mp/_utility")
{
includes_.push_back(include_maps_mp_utility_);
return true;
}
else if (include == "common_scripts/utility")
{
includes_.push_back(include_common_scripts_utility_);
return true;
}
else if (include == "common_scripts/_createfx")
{
includes_.push_back(include_common_scripts_createfx_);
return true;
}
else if (include == "maps/mp/gametypes/_hud_util")
{
includes_.push_back(include_maps_mp_gametypes_hud_util_);
return true;
}
return false;
}
void compiler::print_debug_info()
{
printf("----------------------------------\n");
printf("files included: %zu\n", includes_.size());
printf("animtrees used: %zu\n", animtrees_.size());
printf("functions compiled: %zu\n",assembly_.size());
for (auto& func : assembly_)
{
print_function(func);
for (auto& inst : func->instructions)
{
const auto itr = func->labels.find(inst->index);
if (itr != func->labels.end())
{
print_label(itr->second);
}
print_instruction(inst);
}
}
printf("----------------------------------\n");
}
void compiler::print_opcodes(std::uint32_t index, std::uint32_t size)
{
printf(" ");
}
void compiler::print_function(const function::ptr& func)
{
printf("\n");
printf("%s\n", func->name.data());
}
void compiler::print_instruction(const instruction::ptr& inst)
{
switch (opcode(inst->opcode))
{
case opcode::OP_endswitch:
print_opcodes(inst->index, 3);
printf("%s", resolver::opcode_name(inst->opcode).data());
printf(" %s\n", inst->data[0].data());
{
std::uint32_t totalcase = std::stoul(inst->data[0]);
auto index = 0;
for (auto casenum = 0u; casenum < totalcase; casenum++)
{
print_opcodes(inst->index, 7);
if (inst->data[1 + index] == "case")
{
printf("%s %s %s", inst->data[1 + index].data(), inst->data[1 + index + 1].data(), inst->data[1 + index + 2].data());
index += 3;
}
else if (inst->data[1 + index] == "default")
{
printf("%s %s", inst->data[1 + index].data(), inst->data[1 + index + 1].data());
index += 2;
}
if (casenum != totalcase - 1)
{
printf("\n");
}
}
}
break;
default:
print_opcodes(inst->index, inst->size);
printf("%s", resolver::opcode_name(inst->opcode).data());
for (auto& d : inst->data)
{
printf(" %s", d.data());
}
break;
}
printf("\n");
}
void compiler::print_label(const std::string& label)
{
printf(" %s\n", label.data());
}
} // namespace xsk::gsc::iw5