// 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 { return std::move(assembly_); } void compiler::compile(const std::string& file, std::vector& data) { filename_ = file; auto prog = parse_buffer(filename_, data); compile_program(prog); } void compiler::read_callback(std::function(const std::string&)> func) { read_callback_ = func; } auto compiler::parse_buffer(const std::string& file, std::vector& data) -> ast::program::ptr { ast::program::ptr result(nullptr); resolver::set_reader(read_callback_); lexer lexer(file, reinterpret_cast(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 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_->index = index_; function_->name = thread->name->value; auto blk = std::make_unique(); 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 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 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 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 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()); auto& inst = function_->instructions.back(); inst->opcode = static_cast(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()); auto& inst = function_->instructions.back(); inst->opcode = static_cast(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& data) { function_->instructions.push_back(std::make_unique()); auto& inst = function_->instructions.back(); inst->opcode = static_cast(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(); blk->copy(stmt->blk); process_stmt(stmt->stmt, stmt->blk); std::vector childs({ stmt->blk.get() }); blk->merge(childs); } void compiler::process_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt, const block::ptr& blk) { std::vector childs; auto abort = abort_t::abort_return; stmt->blk_if = std::make_unique(); stmt->blk_else = std::make_unique(); 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(); 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(); 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(); stmt->blk_iter = std::make_unique(); 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(stmt->loc(), array_name)); if (!stmt->use_key) stmt->key_expr = ast::expr(std::make_unique(stmt->loc(), key_name)); key_name = stmt->key_expr.as_identifier->value; // calculate variables stmt->ctx = std::make_unique(); stmt->ctx_post = std::make_unique(); // 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(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(); std::vector 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(); 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(); 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(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