3554 lines
104 KiB
C++
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
|