2243 lines
62 KiB
C++
2243 lines
62 KiB
C++
// Copyright 2024 xensik. All rights reserved.
|
|
//
|
|
// Use of this source code is governed by a GNU GPLv3 license
|
|
// that can be found in the LICENSE file.
|
|
|
|
#include "xsk/stdinc.hpp"
|
|
#include "xsk/arc/compiler.hpp"
|
|
#include "xsk/arc/context.hpp"
|
|
|
|
namespace xsk::arc
|
|
{
|
|
|
|
compiler::compiler(context* ctx) : ctx_{ ctx }
|
|
{
|
|
}
|
|
|
|
auto compiler::compile(program const& data) -> assembly::ptr
|
|
{
|
|
emit_program(data);
|
|
return std::move(assembly_);
|
|
}
|
|
|
|
auto compiler::compile(std::string const& file, std::vector<u8>& data) -> assembly::ptr
|
|
{
|
|
auto prog = ctx_->source().parse_program(file, data);
|
|
return compile(*prog);
|
|
}
|
|
|
|
auto compiler::emit_program(program const& prog) -> void
|
|
{
|
|
assembly_ = assembly::make();
|
|
localfuncs_.clear();
|
|
developer_thread_ = false;
|
|
animtree_ = {};
|
|
index_ = 0;
|
|
debug_pos_ = { 0, 0 };
|
|
|
|
for (auto const& include : prog.includes)
|
|
{
|
|
emit_include(*include);
|
|
}
|
|
|
|
for (auto const& dec : prog.declarations)
|
|
{
|
|
if (dec->is<decl_function>())
|
|
{
|
|
auto const& name = dec->as<decl_function>().name->value;
|
|
|
|
for (auto const& entry : localfuncs_)
|
|
{
|
|
if (entry == name)
|
|
throw comp_error(dec->loc(), fmt::format("function name '{}' already defined as local function", name));
|
|
}
|
|
|
|
localfuncs_.push_back(dec->as<decl_function>().name->value);
|
|
}
|
|
}
|
|
|
|
for (auto const& dec : prog.declarations)
|
|
{
|
|
emit_decl(*dec);
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_include(include const& inc) -> void
|
|
{
|
|
auto const& path = inc.path->value;
|
|
|
|
for (auto const& entry : assembly_->includes)
|
|
{
|
|
if (entry == path)
|
|
{
|
|
throw comp_error(inc.loc(), fmt::format("duplicated include file {}", path));
|
|
}
|
|
}
|
|
|
|
assembly_->includes.push_back(path);
|
|
}
|
|
|
|
auto compiler::emit_decl(decl const& dec) -> void
|
|
{
|
|
switch (dec.kind())
|
|
{
|
|
case node::decl_dev_begin:
|
|
developer_thread_ = true;
|
|
break;
|
|
case node::decl_dev_end:
|
|
developer_thread_ = false;
|
|
break;
|
|
case node::decl_usingtree:
|
|
emit_decl_usingtree(dec.as<decl_usingtree>());
|
|
break;
|
|
case node::decl_function:
|
|
emit_decl_function(dec.as<decl_function>());
|
|
break;
|
|
default:
|
|
throw comp_error(dec.loc(), "unknown declaration");
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_decl_usingtree(decl_usingtree const& animtree) -> void
|
|
{
|
|
if (developer_thread_)
|
|
throw comp_error(animtree.loc(), "cannot put #using_animtree inside developer comment");
|
|
|
|
animtree_ = animtree.name->value;
|
|
}
|
|
|
|
auto compiler::emit_decl_function(decl_function const& func) -> void
|
|
{
|
|
label_idx_ = 0;
|
|
can_break_ = false;
|
|
can_continue_ = false;
|
|
scopes_.clear();
|
|
constants_.clear();
|
|
stackframe_.clear();
|
|
|
|
function_ = function::make();
|
|
function_->index = index_;
|
|
function_->name = func.name->value;
|
|
function_->params = static_cast<u8>(func.params->list.size());
|
|
function_->flags = static_cast<u8>(func.flags);
|
|
|
|
process_function(func);
|
|
|
|
scopes_.push_back(scope());
|
|
|
|
emit_expr_parameters(*func.params);
|
|
emit_stmt_comp(*func.body);
|
|
|
|
if (scopes_.back().abort == scope::abort_none)
|
|
emit_opcode(opcode::OP_End);
|
|
|
|
scopes_.pop_back();
|
|
|
|
function_->size = index_ - function_->index;
|
|
assembly_->functions.push_back(std::move(function_));
|
|
}
|
|
|
|
auto compiler::emit_stmt(stmt const& stm) -> void
|
|
{
|
|
debug_pos_ = { stm.loc().begin.line, stm.loc().begin.column };
|
|
|
|
switch (stm.kind())
|
|
{
|
|
case node::stmt_list:
|
|
emit_stmt_list(stm.as<stmt_list>());
|
|
break;
|
|
case node::stmt_comp:
|
|
emit_stmt_comp(stm.as<stmt_comp>());
|
|
break;
|
|
case node::stmt_dev:
|
|
emit_stmt_dev(stm.as<stmt_dev>());
|
|
break;
|
|
case node::stmt_expr:
|
|
emit_stmt_expr(stm.as<stmt_expr>());
|
|
break;
|
|
case node::stmt_endon:
|
|
emit_stmt_endon(stm.as<stmt_endon>());
|
|
break;
|
|
case node::stmt_notify:
|
|
emit_stmt_notify(stm.as<stmt_notify>());
|
|
break;
|
|
case node::stmt_wait:
|
|
emit_stmt_wait(stm.as<stmt_wait>());
|
|
break;
|
|
case node::stmt_waitrealtime:
|
|
emit_stmt_waitrealtime(stm.as<stmt_waitrealtime>());
|
|
break;
|
|
case node::stmt_waittill:
|
|
emit_stmt_waittill(stm.as<stmt_waittill>());
|
|
break;
|
|
case node::stmt_waittillmatch:
|
|
emit_stmt_waittillmatch(stm.as<stmt_waittillmatch>());
|
|
break;
|
|
case node::stmt_waittillframeend:
|
|
emit_stmt_waittillframeend(stm.as<stmt_waittillframeend>());
|
|
break;
|
|
case node::stmt_if:
|
|
emit_stmt_if(stm.as<stmt_if>());
|
|
break;
|
|
case node::stmt_ifelse:
|
|
emit_stmt_ifelse(stm.as<stmt_ifelse>());
|
|
break;
|
|
case node::stmt_while:
|
|
emit_stmt_while(stm.as<stmt_while>());
|
|
break;
|
|
case node::stmt_dowhile:
|
|
emit_stmt_dowhile(stm.as<stmt_dowhile>());
|
|
break;
|
|
case node::stmt_for:
|
|
emit_stmt_for(stm.as<stmt_for>());
|
|
break;
|
|
case node::stmt_foreach:
|
|
emit_stmt_foreach(stm.as<stmt_foreach>());
|
|
break;
|
|
case node::stmt_switch:
|
|
emit_stmt_switch(stm.as<stmt_switch>());
|
|
break;
|
|
case node::stmt_case:
|
|
emit_stmt_case(stm.as<stmt_case>());
|
|
break;
|
|
case node::stmt_default:
|
|
emit_stmt_default(stm.as<stmt_default>());
|
|
break;
|
|
case node::stmt_break:
|
|
emit_stmt_break(stm.as<stmt_break>());
|
|
break;
|
|
case node::stmt_continue:
|
|
emit_stmt_continue(stm.as<stmt_continue>());
|
|
break;
|
|
case node::stmt_return:
|
|
emit_stmt_return(stm.as<stmt_return>());
|
|
break;
|
|
case node::stmt_breakpoint:
|
|
emit_stmt_breakpoint(stm.as<stmt_breakpoint>());
|
|
break;
|
|
case node::stmt_prof_begin:
|
|
emit_stmt_prof_begin(stm.as<stmt_prof_begin>());
|
|
break;
|
|
case node::stmt_prof_end:
|
|
emit_stmt_prof_end(stm.as<stmt_prof_end>());
|
|
break;
|
|
default:
|
|
throw comp_error(stm.loc(), "unknown statement");
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_stmt_list(stmt_list const& stm) -> void
|
|
{
|
|
for (auto const& entry : stm.list)
|
|
{
|
|
emit_stmt(*entry);
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_stmt_comp(stmt_comp const& stm) -> void
|
|
{
|
|
emit_stmt_list(*stm.block);
|
|
}
|
|
|
|
auto compiler::emit_stmt_dev(stmt_dev const& stm) -> void
|
|
{
|
|
auto end = create_label();
|
|
developer_thread_ = true;
|
|
emit_opcode(opcode::OP_DevblockBegin, end);
|
|
emit_stmt_list(*stm.block);
|
|
insert_label(end);
|
|
developer_thread_ = false;
|
|
}
|
|
|
|
auto compiler::emit_stmt_expr(stmt_expr const& stm) -> void
|
|
{
|
|
switch (stm.value->kind())
|
|
{
|
|
case node::expr_increment:
|
|
emit_expr_increment(stm.value->as<expr_increment>(), true);
|
|
break;
|
|
case node::expr_decrement:
|
|
emit_expr_decrement(stm.value->as<expr_decrement>(), true);
|
|
break;
|
|
case node::expr_assign:
|
|
emit_expr_assign(stm.value->as<expr_assign>());
|
|
break;
|
|
case node::expr_call:
|
|
emit_expr_call(stm.value->as<expr_call>(), true);
|
|
break;
|
|
case node::expr_method:
|
|
emit_expr_method(stm.value->as<expr_method>(), true);
|
|
break;
|
|
case node::expr_const:
|
|
emit_expr_const(stm.value->as<expr_const>());
|
|
break;
|
|
case node::expr_empty:
|
|
break;
|
|
default:
|
|
throw comp_error(stm.loc(), "unknown expr statement expression");
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_stmt_endon(stmt_endon const& stm) -> void
|
|
{
|
|
emit_expr(*stm.event);
|
|
emit_expr(*stm.obj);
|
|
emit_opcode(opcode::OP_EndOn);
|
|
}
|
|
|
|
auto compiler::emit_stmt_notify(stmt_notify const& stm) -> void
|
|
{
|
|
emit_opcode(opcode::OP_VoidCodePos);
|
|
|
|
for (auto it = stm.args->list.rbegin(); it != stm.args->list.rend(); it++)
|
|
{
|
|
emit_expr(**it);
|
|
}
|
|
|
|
emit_expr(*stm.event);
|
|
emit_expr(*stm.obj);
|
|
emit_opcode(opcode::OP_Notify);
|
|
}
|
|
|
|
auto compiler::emit_stmt_wait(stmt_wait const& stm) -> void
|
|
{
|
|
emit_expr(*stm.time);
|
|
emit_opcode(opcode::OP_Wait);
|
|
}
|
|
|
|
auto compiler::emit_stmt_waitrealtime(stmt_waitrealtime const& stm) -> void
|
|
{
|
|
emit_expr(*stm.time);
|
|
emit_opcode(opcode::OP_RealWait);
|
|
}
|
|
|
|
auto compiler::emit_stmt_waittill(stmt_waittill const& stm) -> void
|
|
{
|
|
emit_expr(*stm.event);
|
|
emit_expr(*stm.obj);
|
|
emit_opcode(opcode::OP_WaitTill);
|
|
|
|
for (auto const& entry : stm.args->list)
|
|
{
|
|
if (entry->is<expr_undefined>())
|
|
emit_opcode(opcode::OP_SafeDecTop);
|
|
else
|
|
emit_opcode(opcode::OP_SafeSetWaittillVariableFieldCached, fmt::format("{}", variable_access(entry->as<expr_identifier>())));
|
|
}
|
|
|
|
emit_opcode(opcode::OP_ClearParams);
|
|
}
|
|
|
|
auto compiler::emit_stmt_waittillmatch(stmt_waittillmatch const& stm) -> void
|
|
{
|
|
emit_expr_arguments(*stm.args);
|
|
emit_expr(*stm.event);
|
|
emit_expr(*stm.obj);
|
|
emit_opcode(opcode::OP_WaitTillMatch, fmt::format("{}", stm.args->list.size()));
|
|
emit_opcode(opcode::OP_ClearParams);
|
|
}
|
|
|
|
auto compiler::emit_stmt_waittillframeend(stmt_waittillframeend const&) -> void
|
|
{
|
|
emit_opcode(opcode::OP_WaitTillFrameEnd);
|
|
}
|
|
|
|
auto compiler::emit_stmt_if(stmt_if const& stm) -> void
|
|
{
|
|
auto end_loc = create_label();
|
|
|
|
if (stm.test->is<expr_not>())
|
|
{
|
|
emit_expr(*stm.test->as<expr_not>().rvalue);
|
|
emit_opcode(opcode::OP_JumpOnTrue, end_loc);
|
|
}
|
|
else
|
|
{
|
|
emit_expr(*stm.test);
|
|
emit_opcode(opcode::OP_JumpOnFalse, end_loc);
|
|
}
|
|
|
|
auto& paren = scopes_.back();
|
|
scopes_.push_back(scope(paren.brk, paren.cnt));
|
|
|
|
emit_stmt(*stm.body);
|
|
|
|
scopes_.pop_back();
|
|
|
|
insert_label(end_loc);
|
|
}
|
|
|
|
auto compiler::emit_stmt_ifelse(stmt_ifelse const& stm) -> void
|
|
{
|
|
auto else_loc = create_label();
|
|
auto end_loc = create_label();
|
|
|
|
if (stm.test->is<expr_not>())
|
|
{
|
|
emit_expr(*stm.test->as<expr_not>().rvalue);
|
|
emit_opcode(opcode::OP_JumpOnTrue, else_loc);
|
|
}
|
|
else
|
|
{
|
|
emit_expr(*stm.test);
|
|
emit_opcode(opcode::OP_JumpOnFalse, else_loc);
|
|
}
|
|
|
|
auto& paren = scopes_.back();
|
|
scopes_.push_back(scope(paren.brk, paren.cnt));
|
|
emit_stmt(*stm.stmt_if);
|
|
scopes_.pop_back();
|
|
|
|
emit_opcode(opcode::OP_Jump, end_loc);
|
|
|
|
insert_label(else_loc);
|
|
|
|
auto& paren2 = scopes_.back();
|
|
scopes_.push_back(scope(paren2.brk, paren2.cnt));
|
|
emit_stmt(*stm.stmt_else);
|
|
scopes_.pop_back();
|
|
|
|
insert_label(end_loc);
|
|
}
|
|
|
|
auto compiler::emit_stmt_while(stmt_while const& stm) -> void
|
|
{
|
|
auto break_loc = create_label();
|
|
auto continue_loc = insert_label();
|
|
auto old_break = can_break_;
|
|
auto old_continue = can_continue_;
|
|
can_break_ = true;
|
|
can_continue_ = true;
|
|
|
|
if (stm.test->is<expr_not>())
|
|
{
|
|
emit_expr(*stm.test->as<expr_not>().rvalue);
|
|
emit_opcode(opcode::OP_JumpOnTrue, break_loc);
|
|
}
|
|
else
|
|
{
|
|
emit_expr(*stm.test);
|
|
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
|
|
}
|
|
|
|
scopes_.push_back(scope(break_loc, continue_loc));
|
|
emit_stmt(*stm.body);
|
|
scopes_.pop_back();
|
|
|
|
emit_opcode(opcode::OP_Jump, continue_loc);
|
|
insert_label(break_loc);
|
|
|
|
can_break_ = old_break;
|
|
can_continue_ = old_continue;
|
|
}
|
|
|
|
auto compiler::emit_stmt_dowhile(stmt_dowhile const& stm) -> void
|
|
{
|
|
auto old_break = can_break_;
|
|
auto old_continue = can_continue_;
|
|
can_break_ = true;
|
|
can_continue_ = true;
|
|
|
|
auto break_loc = create_label();
|
|
auto continue_loc = create_label();
|
|
auto begin_loc = insert_label();
|
|
|
|
scopes_.push_back(scope(break_loc, continue_loc));
|
|
emit_stmt(*stm.body);
|
|
scopes_.pop_back();
|
|
|
|
insert_label(continue_loc);
|
|
|
|
if (stm.test->is<expr_not>())
|
|
{
|
|
emit_expr(*stm.test->as<expr_not>().rvalue);
|
|
emit_opcode(opcode::OP_JumpOnFalse, begin_loc);
|
|
}
|
|
else
|
|
{
|
|
emit_expr(*stm.test);
|
|
emit_opcode(opcode::OP_JumpOnTrue, begin_loc);
|
|
}
|
|
|
|
insert_label(break_loc);
|
|
|
|
can_break_ = old_break;
|
|
can_continue_ = old_continue;
|
|
}
|
|
|
|
auto compiler::emit_stmt_for(stmt_for const& stm) -> void
|
|
{
|
|
auto old_break = can_break_;
|
|
auto old_continue = can_continue_;
|
|
|
|
emit_stmt(*stm.init);
|
|
|
|
auto break_loc = create_label();
|
|
auto continue_loc = create_label();
|
|
|
|
auto begin_loc = insert_label();
|
|
|
|
bool const_cond = is_constant_condition(*stm.test);
|
|
|
|
if (!const_cond)
|
|
{
|
|
emit_expr(*stm.test);
|
|
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
|
|
}
|
|
|
|
can_break_ = true;
|
|
can_continue_ = true;
|
|
|
|
scopes_.push_back(scope(break_loc, continue_loc));
|
|
emit_stmt(*stm.body);
|
|
scopes_.pop_back();
|
|
|
|
can_break_ = false;
|
|
can_continue_ = false;
|
|
|
|
insert_label(continue_loc);
|
|
emit_stmt(*stm.iter);
|
|
emit_opcode(opcode::OP_Jump, begin_loc);
|
|
insert_label(break_loc);
|
|
|
|
can_break_ = old_break;
|
|
can_continue_ = old_continue;
|
|
}
|
|
|
|
auto compiler::emit_stmt_foreach(stmt_foreach const& stm) -> void
|
|
{
|
|
auto old_break = can_break_;
|
|
auto old_continue = can_continue_;
|
|
|
|
emit_expr(*stm.container);
|
|
emit_expr_variable_ref(*stm.array, true);
|
|
emit_expr_variable(*stm.array);
|
|
emit_opcode(opcode::OP_FirstArrayKey);
|
|
emit_expr_variable_ref(*stm.key, true);
|
|
|
|
auto break_loc = create_label();
|
|
auto continue_loc = create_label();
|
|
auto begin_loc = insert_label();
|
|
|
|
emit_expr_variable(*stm.key);
|
|
emit_opcode(opcode::OP_IsDefined);
|
|
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
|
|
|
|
can_break_ = true;
|
|
can_continue_ = true;
|
|
|
|
emit_expr_variable(*stm.key);
|
|
emit_opcode(opcode::OP_EvalLocalVariableCached, fmt::format("{}", variable_access(stm.array->as<expr_identifier>())));
|
|
emit_opcode(opcode::OP_EvalArray);
|
|
emit_expr_variable_ref(*stm.value, true);
|
|
|
|
scopes_.push_back(scope(break_loc, continue_loc));
|
|
emit_stmt(*stm.body);
|
|
scopes_.pop_back();
|
|
|
|
can_break_ = false;
|
|
can_continue_ = false;
|
|
|
|
insert_label(continue_loc);
|
|
|
|
emit_expr_variable(*stm.key);
|
|
emit_expr_variable(*stm.array);
|
|
emit_opcode(opcode::OP_NextArrayKey);
|
|
emit_expr_variable_ref(*stm.key, true);
|
|
emit_opcode(opcode::OP_Jump, begin_loc);
|
|
insert_label(break_loc);
|
|
|
|
can_break_ = old_break;
|
|
can_continue_ = old_continue;
|
|
}
|
|
|
|
auto compiler::emit_stmt_switch(stmt_switch const& stm) -> void
|
|
{
|
|
auto old_break = can_break_;
|
|
can_break_ = false;
|
|
|
|
auto table_loc = create_label();
|
|
auto break_loc = create_label();
|
|
|
|
emit_expr(*stm.test);
|
|
emit_opcode(opcode::OP_Switch, table_loc);
|
|
|
|
can_break_ = true;
|
|
|
|
auto data = std::vector<std::string>{};
|
|
data.push_back(fmt::format("{}", stm.body->block->list.size()));
|
|
|
|
auto type = switch_type::none;
|
|
auto loc_default = std::string{};
|
|
auto has_default = false;
|
|
|
|
for (auto i = 0u; i < stm.body->block->list.size(); i++)
|
|
{
|
|
auto const& entry = stm.body->block->list[i];
|
|
|
|
if (entry->is<stmt_case>())
|
|
{
|
|
data.push_back("case");
|
|
|
|
if (entry->as<stmt_case>().value->is<expr_integer>())
|
|
{
|
|
if (type == switch_type::string)
|
|
{
|
|
throw comp_error(entry->loc(), "switch cases with different types");
|
|
}
|
|
|
|
type = switch_type::integer;
|
|
|
|
data.push_back(entry->as<stmt_case>().value->as<expr_integer>().value);
|
|
data.push_back(insert_label());
|
|
}
|
|
else if (entry->as<stmt_case>().value->is<expr_string>())
|
|
{
|
|
if (type == switch_type::integer)
|
|
{
|
|
throw comp_error(entry->loc(), "switch cases with different types");
|
|
}
|
|
|
|
type = switch_type::string;
|
|
|
|
data.push_back(entry->as<stmt_case>().value->as<expr_string>().value);
|
|
data.push_back(insert_label());
|
|
}
|
|
else
|
|
{
|
|
throw comp_error(entry->loc(), "case type must be int or string");
|
|
}
|
|
|
|
auto& paren = scopes_.back();
|
|
scopes_.push_back(scope(break_loc, paren.cnt));
|
|
emit_stmt_list(*entry->as<stmt_case>().body);
|
|
scopes_.pop_back();
|
|
}
|
|
else if (entry->is<stmt_default>())
|
|
{
|
|
loc_default = insert_label();
|
|
has_default = true;
|
|
|
|
auto& paren = scopes_.back();
|
|
scopes_.push_back(scope(break_loc, paren.cnt));
|
|
emit_stmt_list(*entry->as<stmt_default>().body);
|
|
scopes_.pop_back();
|
|
}
|
|
else
|
|
{
|
|
throw comp_error(entry->loc(), "missing case statement");
|
|
}
|
|
}
|
|
|
|
if (has_default)
|
|
{
|
|
data.push_back("default");
|
|
data.push_back(loc_default);
|
|
}
|
|
|
|
data.push_back(fmt::format("{}", static_cast<std::underlying_type_t<switch_type>>(type)));
|
|
|
|
insert_label(table_loc);
|
|
emit_opcode(opcode::OP_EndSwitch, data);
|
|
insert_label(break_loc);
|
|
|
|
can_break_ = old_break;
|
|
}
|
|
|
|
auto compiler::emit_stmt_case(stmt_case const& stm) -> void
|
|
{
|
|
throw comp_error(stm.loc(), "illegal case statement");
|
|
}
|
|
|
|
auto compiler::emit_stmt_default(stmt_default const& stm) -> void
|
|
{
|
|
throw comp_error(stm.loc(), "illegal default statement");
|
|
}
|
|
|
|
auto compiler::emit_stmt_break(stmt_break const& stm) -> void
|
|
{
|
|
if (!can_break_ || scopes_.back().abort != scope::abort_none || scopes_.back().brk == "")
|
|
throw comp_error(stm.loc(), "illegal break statement");
|
|
|
|
scopes_.back().abort = scope::abort_break;
|
|
emit_opcode(opcode::OP_Jump, scopes_.back().brk);
|
|
}
|
|
|
|
auto compiler::emit_stmt_continue(stmt_continue const& stm) -> void
|
|
{
|
|
if (!can_continue_ || scopes_.back().abort != scope::abort_none || scopes_.back().cnt == "")
|
|
throw comp_error(stm.loc(), "illegal continue statement");
|
|
|
|
scopes_.back().abort = scope::abort_continue;
|
|
emit_opcode(opcode::OP_Jump, scopes_.back().cnt);
|
|
}
|
|
|
|
auto compiler::emit_stmt_return(stmt_return const& stm) -> void
|
|
{
|
|
if (scopes_.back().abort == scope::abort_none)
|
|
scopes_.back().abort = scope::abort_return;
|
|
|
|
if (!stm.value->is<expr_empty>())
|
|
{
|
|
emit_expr(*stm.value);
|
|
emit_opcode(opcode::OP_Return);
|
|
}
|
|
else
|
|
emit_opcode(opcode::OP_End);
|
|
}
|
|
|
|
auto compiler::emit_stmt_breakpoint(stmt_breakpoint const&) -> void
|
|
{
|
|
// TODO:
|
|
}
|
|
|
|
auto compiler::emit_stmt_prof_begin(stmt_prof_begin const&) -> void
|
|
{
|
|
// TODO:
|
|
}
|
|
|
|
auto compiler::emit_stmt_prof_end(stmt_prof_end const&) -> void
|
|
{
|
|
// TODO:
|
|
}
|
|
|
|
auto compiler::emit_expr(expr const& exp) -> void
|
|
{
|
|
debug_pos_ = { exp.loc().begin.line, exp.loc().begin.column };
|
|
|
|
switch (exp.kind())
|
|
{
|
|
case node::expr_paren:
|
|
emit_expr(*exp.as<expr_paren>().value);
|
|
break;
|
|
case node::expr_ternary:
|
|
emit_expr_ternary(exp.as<expr_ternary>());
|
|
break;
|
|
case node::expr_binary:
|
|
emit_expr_binary(exp.as<expr_binary>());
|
|
break;
|
|
case node::expr_complement:
|
|
emit_expr_complement(exp.as<expr_complement>());
|
|
break;
|
|
case node::expr_negate:
|
|
emit_expr_negate(exp.as<expr_negate>());
|
|
break;
|
|
case node::expr_not:
|
|
emit_expr_not(exp.as<expr_not>());
|
|
break;
|
|
case node::expr_call:
|
|
emit_expr_call(exp.as<expr_call>(), false);
|
|
break;
|
|
case node::expr_method:
|
|
emit_expr_method(exp.as<expr_method>(), false);
|
|
break;
|
|
case node::expr_isdefined:
|
|
emit_expr_isdefined(exp.as<expr_isdefined>());
|
|
break;
|
|
case node::expr_vectorscale:
|
|
emit_expr_vectorscale(exp.as<expr_vectorscale>());
|
|
break;
|
|
case node::expr_anglestoup:
|
|
emit_expr_anglestoup(exp.as<expr_anglestoup>());
|
|
break;
|
|
case node::expr_anglestoright:
|
|
emit_expr_anglestoright(exp.as<expr_anglestoright>());
|
|
break;
|
|
case node::expr_anglestoforward:
|
|
emit_expr_anglestoforward(exp.as<expr_anglestoforward>());
|
|
break;
|
|
case node::expr_angleclamp180:
|
|
emit_expr_angleclamp180(exp.as<expr_angleclamp180>());
|
|
break;
|
|
case node::expr_vectortoangles:
|
|
emit_expr_vectortoangles(exp.as<expr_vectortoangles>());
|
|
break;
|
|
case node::expr_abs:
|
|
emit_expr_abs(exp.as<expr_abs>());
|
|
break;
|
|
case node::expr_gettime:
|
|
emit_expr_gettime(exp.as<expr_gettime>());
|
|
break;
|
|
case node::expr_getdvar:
|
|
emit_expr_getdvar(exp.as<expr_getdvar>());
|
|
break;
|
|
case node::expr_getdvarint:
|
|
emit_expr_getdvarint(exp.as<expr_getdvarint>());
|
|
break;
|
|
case node::expr_getdvarfloat:
|
|
emit_expr_getdvarfloat(exp.as<expr_getdvarfloat>());
|
|
break;
|
|
case node::expr_getdvarvector:
|
|
emit_expr_getdvarvector(exp.as<expr_getdvarvector>());
|
|
break;
|
|
case node::expr_getdvarcolorred:
|
|
emit_expr_getdvarcolorred(exp.as<expr_getdvarcolorred>());
|
|
break;
|
|
case node::expr_getdvarcolorgreen:
|
|
emit_expr_getdvarcolorgreen(exp.as<expr_getdvarcolorgreen>());
|
|
break;
|
|
case node::expr_getdvarcolorblue:
|
|
emit_expr_getdvarcolorblue(exp.as<expr_getdvarcolorblue>());
|
|
break;
|
|
case node::expr_getdvarcoloralpha:
|
|
emit_expr_getdvarcoloralpha(exp.as<expr_getdvarcoloralpha>());
|
|
break;
|
|
case node::expr_getfirstarraykey:
|
|
emit_expr_getfirstarraykey(exp.as<expr_getfirstarraykey>());
|
|
break;
|
|
case node::expr_getnextarraykey:
|
|
emit_expr_getnextarraykey(exp.as<expr_getnextarraykey>());
|
|
break;
|
|
case node::expr_reference:
|
|
emit_expr_reference(exp.as<expr_reference>());
|
|
break;
|
|
case node::expr_array:
|
|
emit_expr_array(exp.as<expr_array>());
|
|
break;
|
|
case node::expr_field:
|
|
emit_expr_field(exp.as<expr_field>());
|
|
break;
|
|
case node::expr_size:
|
|
emit_expr_size(exp.as<expr_size>());
|
|
break;
|
|
case node::expr_empty_array:
|
|
emit_opcode(opcode::OP_EmptyArray);
|
|
break;
|
|
case node::expr_undefined:
|
|
emit_opcode(opcode::OP_GetUndefined);
|
|
break;
|
|
case node::expr_game:
|
|
emit_opcode(opcode::OP_GetGame);
|
|
break;
|
|
case node::expr_self:
|
|
emit_opcode(opcode::OP_GetSelf);
|
|
break;
|
|
case node::expr_anim:
|
|
emit_opcode(opcode::OP_GetAnim);
|
|
break;
|
|
case node::expr_level:
|
|
emit_opcode(opcode::OP_GetLevel);
|
|
break;
|
|
case node::expr_animation:
|
|
emit_expr_animation(exp.as<expr_animation>());
|
|
break;
|
|
case node::expr_animtree:
|
|
emit_expr_animtree(exp.as<expr_animtree>());
|
|
break;
|
|
case node::expr_identifier:
|
|
emit_expr_local(exp.as<expr_identifier>());
|
|
break;
|
|
case node::expr_istring:
|
|
emit_expr_istring(exp.as<expr_istring>());
|
|
break;
|
|
case node::expr_string:
|
|
emit_expr_string(exp.as<expr_string>());
|
|
break;
|
|
case node::expr_vector:
|
|
emit_expr_vector(exp.as<expr_vector>());
|
|
break;
|
|
case node::expr_hash:
|
|
emit_expr_hash(exp.as<expr_hash>());
|
|
break;
|
|
case node::expr_float:
|
|
emit_expr_float(exp.as<expr_float>());
|
|
break;
|
|
case node::expr_integer:
|
|
emit_expr_integer(exp.as<expr_integer>());
|
|
break;
|
|
case node::expr_false:
|
|
emit_expr_false(exp.as<expr_false>());
|
|
break;
|
|
case node::expr_true:
|
|
emit_expr_true(exp.as<expr_true>());
|
|
break;
|
|
case node::expr_empty:
|
|
break;
|
|
default:
|
|
throw comp_error(exp.loc(), "unknown expression");
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_const(expr_const const& exp) -> void
|
|
{
|
|
auto const itr = constants_.find(exp.lvalue->value);
|
|
|
|
if (itr != constants_.end())
|
|
throw comp_error(exp.loc(), fmt::format("duplicated constant '{}'", exp.lvalue->value));
|
|
|
|
if (std::find(stackframe_.begin(), stackframe_.end(), exp.lvalue->value) != stackframe_.end())
|
|
throw comp_error(exp.loc(), fmt::format("constant already defined as local variable '{}'", exp.lvalue->value));
|
|
|
|
constants_.insert({ exp.lvalue->value, exp.rvalue.get() });
|
|
}
|
|
|
|
auto compiler::emit_expr_assign(expr_assign const& exp) -> void
|
|
{
|
|
if (exp.oper == expr_assign::op::eq)
|
|
{
|
|
if (exp.rvalue->is<expr_undefined>())
|
|
{
|
|
emit_expr_clear(*exp.lvalue);
|
|
}
|
|
else
|
|
{
|
|
emit_expr(*exp.rvalue);
|
|
emit_expr_variable_ref(*exp.lvalue, true);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
emit_expr(*exp.lvalue);
|
|
emit_expr(*exp.rvalue);
|
|
|
|
switch (exp.oper)
|
|
{
|
|
case expr_assign::op::add:
|
|
emit_opcode(opcode::OP_Plus);
|
|
break;
|
|
case expr_assign::op::sub:
|
|
emit_opcode(opcode::OP_Minus);
|
|
break;
|
|
case expr_assign::op::mul:
|
|
emit_opcode(opcode::OP_Multiply);
|
|
break;
|
|
case expr_assign::op::div:
|
|
emit_opcode(opcode::OP_Divide);
|
|
break;
|
|
case expr_assign::op::mod:
|
|
emit_opcode(opcode::OP_Modulus);
|
|
break;
|
|
case expr_assign::op::shl:
|
|
emit_opcode(opcode::OP_ShiftLeft);
|
|
break;
|
|
case expr_assign::op::shr:
|
|
emit_opcode(opcode::OP_ShiftRight);
|
|
break;
|
|
case expr_assign::op::bwor:
|
|
emit_opcode(opcode::OP_Bit_Or);
|
|
break;
|
|
case expr_assign::op::bwand:
|
|
emit_opcode(opcode::OP_Bit_And);
|
|
break;
|
|
case expr_assign::op::bwexor:
|
|
emit_opcode(opcode::OP_Bit_Xor);
|
|
break;
|
|
default:
|
|
throw comp_error(exp.loc(), "unknown assign operation");
|
|
}
|
|
|
|
emit_expr_variable_ref(*exp.lvalue, true);
|
|
}
|
|
|
|
auto compiler::emit_expr_clear(expr const& exp) -> void
|
|
{
|
|
switch (exp.kind())
|
|
{
|
|
case node::expr_array:
|
|
emit_expr(*exp.as<expr_array>().key);
|
|
exp.as<expr_array>().obj->is<expr_game>() ? emit_opcode(opcode::OP_GetGameRef) : emit_expr_variable_ref(*exp.as<expr_array>().obj, false);
|
|
emit_opcode(opcode::OP_ClearArray);
|
|
break;
|
|
case node::expr_field:
|
|
emit_expr_object(*exp.as<expr_field>().obj);
|
|
emit_opcode(opcode::OP_ClearFieldVariable, exp.as<expr_field>().field->value);
|
|
break;
|
|
case node::expr_identifier:
|
|
emit_opcode(opcode::OP_GetUndefined);
|
|
emit_expr_local_ref(exp.as<expr_identifier>(), true);
|
|
break;
|
|
default:
|
|
throw comp_error(exp.loc(), "unknown clear variable lvalue");
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_increment(expr_increment const& exp, bool is_stmt) -> void
|
|
{
|
|
if (is_stmt)
|
|
{
|
|
emit_expr_variable_ref(*exp.lvalue, false);
|
|
emit_opcode(opcode::OP_Inc);
|
|
}
|
|
else
|
|
{
|
|
// TODO:
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_decrement(expr_decrement const& exp, bool is_stmt) -> void
|
|
{
|
|
if (is_stmt)
|
|
{
|
|
emit_expr_variable_ref(*exp.lvalue, false);
|
|
emit_opcode(opcode::OP_Dec);
|
|
}
|
|
else
|
|
{
|
|
// TODO:
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_ternary(expr_ternary const& exp) -> void
|
|
{
|
|
auto else_loc = create_label();
|
|
auto end_loc = create_label();
|
|
|
|
if (exp.test->is<expr_not>())
|
|
{
|
|
emit_expr(*exp.test->as<expr_not>().rvalue);
|
|
emit_opcode(opcode::OP_JumpOnTrue, else_loc);
|
|
}
|
|
else
|
|
{
|
|
emit_expr(*exp.test);
|
|
emit_opcode(opcode::OP_JumpOnFalse, else_loc);
|
|
}
|
|
|
|
emit_expr(*exp.true_expr);
|
|
emit_opcode(opcode::OP_Jump, end_loc);
|
|
|
|
insert_label(else_loc);
|
|
emit_expr(*exp.false_expr);
|
|
insert_label(end_loc);
|
|
}
|
|
|
|
auto compiler::emit_expr_binary(expr_binary const& exp) -> void
|
|
{
|
|
if (exp.oper == expr_binary::op::bool_and)
|
|
{
|
|
auto label = create_label();
|
|
|
|
emit_expr(*exp.lvalue);
|
|
emit_opcode(opcode::OP_JumpOnFalseExpr, label);
|
|
emit_expr(*exp.rvalue);
|
|
|
|
insert_label(label);
|
|
}
|
|
else if (exp.oper == expr_binary::op::bool_or)
|
|
{
|
|
auto label = create_label();
|
|
|
|
emit_expr(*exp.lvalue);
|
|
emit_opcode(opcode::OP_JumpOnTrueExpr, label);
|
|
emit_expr(*exp.rvalue);
|
|
|
|
insert_label(label);
|
|
}
|
|
else
|
|
{
|
|
emit_expr(*exp.lvalue);
|
|
emit_expr(*exp.rvalue);
|
|
|
|
switch (exp.oper)
|
|
{
|
|
case expr_binary::op::eq:
|
|
emit_opcode(opcode::OP_Equal);
|
|
break;
|
|
case expr_binary::op::ne:
|
|
emit_opcode(opcode::OP_NotEqual);
|
|
break;
|
|
case expr_binary::op::lt:
|
|
emit_opcode(opcode::OP_LessThan);
|
|
break;
|
|
case expr_binary::op::gt:
|
|
emit_opcode(opcode::OP_GreaterThan);
|
|
break;
|
|
case expr_binary::op::le:
|
|
emit_opcode(opcode::OP_LessThanOrEqualTo);
|
|
break;
|
|
case expr_binary::op::ge:
|
|
emit_opcode(opcode::OP_GreaterThanOrEqualTo);
|
|
break;
|
|
case expr_binary::op::bwor:
|
|
emit_opcode(opcode::OP_Bit_Or);
|
|
break;
|
|
case expr_binary::op::bwand:
|
|
emit_opcode(opcode::OP_Bit_And);
|
|
break;
|
|
case expr_binary::op::bwexor:
|
|
emit_opcode(opcode::OP_Bit_Xor);
|
|
break;
|
|
case expr_binary::op::shl:
|
|
emit_opcode(opcode::OP_ShiftLeft);
|
|
break;
|
|
case expr_binary::op::shr:
|
|
emit_opcode(opcode::OP_ShiftRight);
|
|
break;
|
|
case expr_binary::op::add:
|
|
emit_opcode(opcode::OP_Plus);
|
|
break;
|
|
case expr_binary::op::sub:
|
|
emit_opcode(opcode::OP_Minus);
|
|
break;
|
|
case expr_binary::op::mul:
|
|
emit_opcode(opcode::OP_Multiply);
|
|
break;
|
|
case expr_binary::op::div:
|
|
emit_opcode(opcode::OP_Divide);
|
|
break;
|
|
case expr_binary::op::mod:
|
|
emit_opcode(opcode::OP_Modulus);
|
|
break;
|
|
default:
|
|
throw comp_error(exp.loc(), "unknown binary expression");
|
|
}
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_complement(expr_complement const& exp) -> void
|
|
{
|
|
emit_expr(*exp.rvalue);
|
|
emit_opcode(opcode::OP_BoolComplement);
|
|
}
|
|
|
|
auto compiler::emit_expr_negate(expr_negate const& exp) -> void
|
|
{
|
|
emit_opcode(opcode::OP_GetZero);
|
|
emit_expr(*exp.rvalue);
|
|
emit_opcode(opcode::OP_Minus);
|
|
}
|
|
|
|
auto compiler::emit_expr_not(expr_not const& exp) -> void
|
|
{
|
|
emit_expr(*exp.rvalue);
|
|
emit_opcode(opcode::OP_BoolNot);
|
|
}
|
|
|
|
auto compiler::emit_expr_call(expr_call const& exp, bool is_stmt) -> void
|
|
{
|
|
if (exp.value->is<expr_pointer>())
|
|
emit_expr_call_pointer(exp.value->as<expr_pointer>(), is_stmt);
|
|
else if (exp.value->is<expr_function>())
|
|
emit_expr_call_function(exp.value->as<expr_function>(), is_stmt);
|
|
else
|
|
throw comp_error(exp.loc(), "unknown function call expression");
|
|
}
|
|
|
|
auto compiler::emit_expr_call_pointer(expr_pointer const& exp, bool is_stmt) -> void
|
|
{
|
|
emit_opcode(opcode::OP_PreScriptCall);
|
|
emit_expr_arguments(*exp.args);
|
|
emit_expr(*exp.func);
|
|
|
|
auto argcount = fmt::format("{}", exp.args->list.size());
|
|
|
|
switch (exp.mode)
|
|
{
|
|
case call::mode::normal:
|
|
emit_opcode(opcode::OP_ScriptFunctionCallPointer, argcount);
|
|
break;
|
|
case call::mode::thread:
|
|
emit_opcode(opcode::OP_ScriptThreadCallPointer, argcount);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (is_stmt)
|
|
emit_opcode(opcode::OP_DecTop);
|
|
}
|
|
|
|
auto compiler::emit_expr_call_function(expr_function const& exp, bool is_stmt) -> void
|
|
{
|
|
if (exp.path->value != "")
|
|
{
|
|
bool found = false;
|
|
|
|
for (const auto& entry : assembly_->includes)
|
|
{
|
|
if (entry == exp.path->value)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
assembly_->includes.push_back(exp.path->value);
|
|
}
|
|
}
|
|
|
|
bool as_dev = false;
|
|
std::string end;
|
|
|
|
if (!developer_thread_ && is_stmt && exp.name->value == "assert")
|
|
{
|
|
as_dev = true;
|
|
developer_thread_ = true;
|
|
end = create_label();
|
|
emit_opcode(opcode::OP_DevblockBegin, end);
|
|
}
|
|
|
|
emit_opcode(opcode::OP_PreScriptCall);
|
|
emit_expr_arguments(*exp.args);
|
|
|
|
auto argcount = fmt::format("{}", exp.args->list.size());
|
|
auto flags = developer_thread_ ? static_cast<u8>(import_flags::developer) : 0;
|
|
|
|
switch (exp.mode)
|
|
{
|
|
case call::mode::normal:
|
|
flags |= static_cast<u8>(import_flags::func_call);
|
|
emit_opcode(opcode::OP_ScriptFunctionCall, { exp.path->value, exp.name->value, argcount, fmt::format("{}", flags) });
|
|
break;
|
|
case call::mode::thread:
|
|
flags |= static_cast<u8>(import_flags::func_call_thread);
|
|
emit_opcode(opcode::OP_ScriptThreadCall, { exp.path->value, exp.name->value, argcount, fmt::format("{}", flags) });
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (is_stmt)
|
|
emit_opcode(opcode::OP_DecTop);
|
|
|
|
if (as_dev)
|
|
{
|
|
insert_label(end);
|
|
developer_thread_ = false;
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_method(expr_method const& exp, bool is_stmt) -> void
|
|
{
|
|
if (exp.value->is<expr_pointer>())
|
|
emit_expr_method_pointer(exp.value->as<expr_pointer>(), *exp.obj, is_stmt);
|
|
else if (exp.value->is<expr_function>())
|
|
emit_expr_method_function(exp.value->as<expr_function>(), *exp.obj, is_stmt);
|
|
else
|
|
throw comp_error(exp.loc(), "unknown method call expression");
|
|
}
|
|
|
|
auto compiler::emit_expr_method_pointer(expr_pointer const& exp, expr const& obj, bool is_stmt) -> void
|
|
{
|
|
emit_opcode(opcode::OP_PreScriptCall);
|
|
emit_expr_arguments(*exp.args);
|
|
emit_expr(obj);
|
|
emit_expr(*exp.func);
|
|
|
|
auto argcount = fmt::format("{}", exp.args->list.size());
|
|
|
|
switch (exp.mode)
|
|
{
|
|
case call::mode::normal:
|
|
emit_opcode(opcode::OP_ScriptMethodCallPointer, argcount);
|
|
break;
|
|
case call::mode::thread:
|
|
emit_opcode(opcode::OP_ScriptMethodThreadCallPointer, argcount);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (is_stmt)
|
|
emit_opcode(opcode::OP_DecTop);
|
|
}
|
|
|
|
auto compiler::emit_expr_method_function(expr_function const& exp, expr const& obj, bool is_stmt) -> void
|
|
{
|
|
if (exp.path->value != "")
|
|
{
|
|
bool found = false;
|
|
|
|
for (const auto& entry : assembly_->includes)
|
|
{
|
|
if (entry == exp.path->value)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
assembly_->includes.push_back(exp.path->value);
|
|
}
|
|
}
|
|
|
|
emit_opcode(opcode::OP_PreScriptCall);
|
|
emit_expr_arguments(*exp.args);
|
|
emit_expr(obj);
|
|
|
|
auto argcount = fmt::format("{}", exp.args->list.size());
|
|
auto flags = developer_thread_ ? static_cast<u8>(import_flags::developer) : 0;
|
|
|
|
switch (exp.mode)
|
|
{
|
|
case call::mode::normal:
|
|
flags |= static_cast<u8>(import_flags::meth_call);
|
|
emit_opcode(opcode::OP_ScriptMethodCall, { exp.path->value, exp.name->value, argcount, fmt::format("{}", flags) });
|
|
break;
|
|
case call::mode::thread:
|
|
flags |= static_cast<u8>(import_flags::meth_call_thread);
|
|
emit_opcode(opcode::OP_ScriptMethodThreadCall, { exp.path->value, exp.name->value, argcount, fmt::format("{}", flags) });
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (is_stmt)
|
|
emit_opcode(opcode::OP_DecTop);
|
|
}
|
|
|
|
auto compiler::emit_expr_parameters(expr_parameters const& exp) -> void
|
|
{
|
|
if (stackframe_.size() == 0)
|
|
{
|
|
emit_opcode(opcode::OP_CheckClearParams);
|
|
}
|
|
else
|
|
{
|
|
emit_opcode(opcode::OP_SafeCreateLocalVariables, stackframe_);
|
|
}
|
|
|
|
for (auto const& entry : exp.list)
|
|
{
|
|
if (entry->is<expr_assign>())
|
|
{
|
|
auto end_loc = create_label();
|
|
emit_expr_variable(*(entry->as<expr_assign>().lvalue));
|
|
emit_opcode(opcode::OP_IsDefined);
|
|
emit_opcode(opcode::OP_JumpOnTrue, end_loc);
|
|
emit_expr(*(entry->as<expr_assign>().rvalue));
|
|
emit_expr_variable_ref(*(entry->as<expr_assign>().lvalue), true);
|
|
insert_label(end_loc);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_arguments(expr_arguments const& exp) -> void
|
|
{
|
|
for (auto it = exp.list.rbegin(); it != exp.list.rend(); ++it)
|
|
{
|
|
emit_expr(**it);
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_isdefined(expr_isdefined const& exp) -> void
|
|
{
|
|
emit_expr(*exp.value);
|
|
emit_opcode(opcode::OP_IsDefined);
|
|
}
|
|
|
|
auto compiler::emit_expr_vectorscale(expr_vectorscale const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg2);
|
|
emit_expr(*exp.arg1);
|
|
emit_opcode(opcode::OP_VectorScale);
|
|
}
|
|
|
|
auto compiler::emit_expr_anglestoup(expr_anglestoup const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_AnglesToUp);
|
|
}
|
|
|
|
auto compiler::emit_expr_anglestoright(expr_anglestoright const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_AnglesToRight);
|
|
}
|
|
|
|
auto compiler::emit_expr_anglestoforward(expr_anglestoforward const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_AnglesToForward);
|
|
}
|
|
|
|
auto compiler::emit_expr_angleclamp180(expr_angleclamp180 const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_AngleClamp180);
|
|
}
|
|
|
|
auto compiler::emit_expr_vectortoangles(expr_vectortoangles const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_VectorToAngles);
|
|
}
|
|
|
|
auto compiler::emit_expr_abs(expr_abs const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_Abs);
|
|
}
|
|
|
|
auto compiler::emit_expr_gettime(expr_gettime const&) -> void
|
|
{
|
|
emit_opcode(opcode::OP_GetTime);
|
|
}
|
|
|
|
auto compiler::emit_expr_getdvar(expr_getdvar const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_GetDvar);
|
|
}
|
|
|
|
auto compiler::emit_expr_getdvarint(expr_getdvarint const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_GetDvarInt);
|
|
}
|
|
|
|
auto compiler::emit_expr_getdvarfloat(expr_getdvarfloat const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_GetDvarFloat);
|
|
}
|
|
|
|
auto compiler::emit_expr_getdvarvector(expr_getdvarvector const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_GetDvarVector);
|
|
}
|
|
|
|
auto compiler::emit_expr_getdvarcolorred(expr_getdvarcolorred const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_GetDvarColorRed);
|
|
}
|
|
|
|
auto compiler::emit_expr_getdvarcolorgreen(expr_getdvarcolorgreen const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_GetDvarColorGreen);
|
|
}
|
|
|
|
auto compiler::emit_expr_getdvarcolorblue(expr_getdvarcolorblue const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_GetDvarColorBlue);
|
|
}
|
|
|
|
auto compiler::emit_expr_getdvarcoloralpha(expr_getdvarcoloralpha const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_GetDvarColorAlpha);
|
|
}
|
|
|
|
auto compiler::emit_expr_getfirstarraykey(expr_getfirstarraykey const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg);
|
|
emit_opcode(opcode::OP_FirstArrayKey);
|
|
}
|
|
|
|
auto compiler::emit_expr_getnextarraykey(expr_getnextarraykey const& exp) -> void
|
|
{
|
|
emit_expr(*exp.arg2);
|
|
emit_expr(*exp.arg1);
|
|
emit_opcode(opcode::OP_NextArrayKey);
|
|
}
|
|
|
|
auto compiler::emit_expr_reference(expr_reference const& exp) -> void
|
|
{
|
|
if (exp.path->value != "")
|
|
{
|
|
bool found = false;
|
|
|
|
for (const auto& entry : assembly_->includes)
|
|
{
|
|
if (entry == exp.path->value)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
assembly_->includes.push_back(exp.path->value);
|
|
}
|
|
}
|
|
|
|
// TODO: resolve import calls path
|
|
|
|
auto flags = developer_thread_ ? static_cast<u8>(import_flags::developer) : 0;
|
|
flags |= static_cast<u8>(import_flags::func_reference);
|
|
|
|
emit_opcode(opcode::OP_GetFunction, { exp.path->value, exp.name->value, "0", fmt::format("{}", flags) });
|
|
}
|
|
|
|
auto compiler::emit_expr_size(expr_size const& exp) -> void
|
|
{
|
|
emit_expr(*exp.obj);
|
|
emit_opcode(opcode::OP_SizeOf);
|
|
}
|
|
|
|
auto compiler::emit_expr_variable_ref(expr const& exp, bool set) -> void
|
|
{
|
|
switch (exp.kind())
|
|
{
|
|
case node::expr_array:
|
|
emit_expr_array_ref(exp.as<expr_array>(), set);
|
|
break;
|
|
case node::expr_field:
|
|
emit_expr_field_ref(exp.as<expr_field>(), set);
|
|
break;
|
|
case node::expr_identifier:
|
|
emit_expr_local_ref(exp.as<expr_identifier>(), set);
|
|
break;
|
|
default:
|
|
throw comp_error(exp.loc(), "invalid lvalue");
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_array_ref(expr_array const& exp, bool set) -> void
|
|
{
|
|
emit_expr(*exp.key);
|
|
|
|
switch (exp.obj->kind())
|
|
{
|
|
case node::expr_game:
|
|
emit_opcode(opcode::OP_GetGameRef);
|
|
emit_opcode(opcode::OP_EvalArrayRef);
|
|
if (set) emit_opcode(opcode::OP_SetVariableField);
|
|
break;
|
|
case node::expr_array:
|
|
case node::expr_field:
|
|
case node::expr_identifier:
|
|
emit_expr_variable_ref(*exp.obj, false);
|
|
emit_opcode(opcode::OP_EvalArrayRef);
|
|
if (set) emit_opcode(opcode::OP_SetVariableField);
|
|
break;
|
|
case node::expr_call:
|
|
case node::expr_method:
|
|
default:
|
|
throw comp_error(exp.loc(), "invalid array lvalue");
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_field_ref(expr_field const& exp, bool set) -> void
|
|
{
|
|
auto const& field = exp.field->value;
|
|
|
|
switch (exp.obj->kind())
|
|
{
|
|
case node::expr_level:
|
|
emit_opcode(opcode::OP_GetLevelObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
|
|
if (set) emit_opcode(opcode::OP_SetVariableField);
|
|
break;
|
|
case node::expr_anim:
|
|
emit_opcode(opcode::OP_GetAnimObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
|
|
if (set) emit_opcode(opcode::OP_SetVariableField);
|
|
break;
|
|
case node::expr_self:
|
|
emit_opcode(opcode::OP_GetSelfObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
|
|
if (set) emit_opcode(opcode::OP_SetVariableField);
|
|
break;
|
|
case node::expr_array:
|
|
emit_expr_array(exp.obj->as<expr_array>());
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
|
|
if (set) emit_opcode(opcode::OP_SetVariableField);
|
|
break;
|
|
case node::expr_field:
|
|
emit_expr_field(exp.obj->as<expr_field>());
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
|
|
if (set) emit_opcode(opcode::OP_SetVariableField);
|
|
break;
|
|
case node::expr_identifier:
|
|
emit_opcode(opcode::OP_EvalLocalVariableCached, fmt::format("{}", variable_access(exp.obj->as<expr_identifier>())));
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
|
|
if (set) emit_opcode(opcode::OP_SetVariableField);
|
|
break;
|
|
case node::expr_call:
|
|
emit_expr_call(exp.obj->as<expr_call>(), false);
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
|
|
if (set) emit_opcode(opcode::OP_SetVariableField);
|
|
break;
|
|
case node::expr_method:
|
|
emit_expr_method(exp.obj->as<expr_method>(), false);
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
|
|
if (set) emit_opcode(opcode::OP_SetVariableField);
|
|
break;
|
|
default:
|
|
throw comp_error(exp.obj->loc(), "not an object");
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_local_ref(expr_identifier const& exp, bool set) -> void
|
|
{
|
|
auto const it = constants_.find(exp.value);
|
|
|
|
if (it != constants_.end())
|
|
{
|
|
throw comp_error(exp.loc(), fmt::format("variable name already defined as constant '{}'", exp.value));
|
|
}
|
|
|
|
emit_opcode(opcode::OP_EvalLocalVariableRefCached, fmt::format("{}", variable_access(exp)));
|
|
|
|
if (set)
|
|
{
|
|
emit_opcode(opcode::OP_SetVariableField);
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_variable(expr const& exp) -> void
|
|
{
|
|
switch (exp.kind())
|
|
{
|
|
case node::expr_array:
|
|
emit_expr_array(exp.as<expr_array>());
|
|
break;
|
|
case node::expr_field:
|
|
emit_expr_field(exp.as<expr_field>());
|
|
break;
|
|
case node::expr_identifier:
|
|
emit_expr_local(exp.as<expr_identifier>());
|
|
break;
|
|
default:
|
|
throw comp_error(exp.loc(), "invalid variable type.");
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_array(expr_array const& exp) -> void
|
|
{
|
|
emit_expr(*exp.key);
|
|
emit_expr(*exp.obj);
|
|
emit_opcode(opcode::OP_EvalArray);
|
|
}
|
|
|
|
auto compiler::emit_expr_field(expr_field const& exp) -> void
|
|
{
|
|
auto const& field = exp.field->value;
|
|
|
|
switch (exp.obj->kind())
|
|
{
|
|
case node::expr_level:
|
|
emit_opcode(opcode::OP_GetLevelObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariable, field);
|
|
break;
|
|
case node::expr_anim:
|
|
emit_opcode(opcode::OP_GetAnimObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariable, field);
|
|
break;
|
|
case node::expr_self:
|
|
emit_opcode(opcode::OP_GetSelfObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariable, field);
|
|
break;
|
|
case node::expr_array:
|
|
emit_expr_array(exp.obj->as<expr_array>());
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariable, field);
|
|
break;
|
|
case node::expr_field:
|
|
emit_expr_field(exp.obj->as<expr_field>());
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariable, field);
|
|
break;
|
|
case node::expr_call:
|
|
emit_expr_call(exp.obj->as<expr_call>(), false);
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariable, field);
|
|
break;
|
|
case node::expr_method:
|
|
emit_expr_method(exp.obj->as<expr_method>(), false);
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariable, field);
|
|
break;
|
|
case node::expr_identifier:
|
|
emit_opcode(opcode::OP_EvalLocalVariableCached, fmt::format("{}", variable_access(exp.obj->as<expr_identifier>())));
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
emit_opcode(opcode::OP_EvalFieldVariable, field);
|
|
break;
|
|
default:
|
|
throw comp_error(exp.loc(), "unknown field variable object type");
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_local(expr_identifier const& exp) -> void
|
|
{
|
|
auto const it = constants_.find(exp.value);
|
|
|
|
if (it != constants_.end())
|
|
emit_expr(*it->second);
|
|
else
|
|
emit_opcode(opcode::OP_EvalLocalVariableCached, fmt::format("{}", variable_access(exp)));
|
|
}
|
|
|
|
auto compiler::emit_expr_object(expr const& exp) -> void
|
|
{
|
|
switch (exp.kind())
|
|
{
|
|
case node::expr_level:
|
|
emit_opcode(opcode::OP_GetLevelObject);
|
|
break;
|
|
case node::expr_anim:
|
|
emit_opcode(opcode::OP_GetAnimObject);
|
|
break;
|
|
case node::expr_self:
|
|
emit_opcode(opcode::OP_GetSelfObject);
|
|
break;
|
|
case node::expr_array:
|
|
emit_expr_array(exp.as<expr_array>());
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
break;
|
|
case node::expr_field:
|
|
emit_expr_field(exp.as<expr_field>());
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
break;
|
|
case node::expr_call:
|
|
emit_expr_call(exp.as<expr_call>(), false);
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
break;
|
|
case node::expr_method:
|
|
emit_expr_method(exp.as<expr_method>(), false);
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
break;
|
|
case node::expr_identifier:
|
|
emit_opcode(opcode::OP_EvalLocalVariableCached, fmt::format("{}", variable_access(exp.as<expr_identifier>())));
|
|
emit_opcode(opcode::OP_CastFieldObject);
|
|
break;
|
|
default:
|
|
throw comp_error(exp.loc(), "not an object");
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_vector(expr_vector const& exp) -> void
|
|
{
|
|
auto data = std::vector<std::string>{};
|
|
auto isconst = true;
|
|
auto flags = 0;
|
|
|
|
if (exp.x->is<expr_integer>())
|
|
{
|
|
auto value = std::atoi(exp.x->as<expr_integer>().value.data());
|
|
data.push_back(exp.x->as<expr_integer>().value);
|
|
|
|
if (value != 1 && value != -1 && value != 0)
|
|
isconst = false;
|
|
else
|
|
flags |= (value == 1) ? 0x20 : (value == -1) ? 0x10 : 0;
|
|
}
|
|
else if (exp.x->is<expr_float>())
|
|
{
|
|
auto value = std::stof(exp.x->as<expr_float>().value.data());
|
|
data.push_back(exp.x->as<expr_float>().value);
|
|
|
|
if (value != 1.0 && value != -1.0 && value != 0.0)
|
|
isconst = false;
|
|
else
|
|
flags |= (value == 1.0) ? 0x20 : (value == -1.0) ? 0x10 : 0;
|
|
}
|
|
else
|
|
{
|
|
isconst = false;
|
|
}
|
|
|
|
if (exp.y->is<expr_integer>())
|
|
{
|
|
auto value = std::atoi(exp.y->as<expr_integer>().value.data());
|
|
data.push_back(exp.y->as<expr_integer>().value);
|
|
|
|
if (value != 1 && value != -1 && value != 0)
|
|
isconst = false;
|
|
else
|
|
flags |= (value == 1) ? 0x08 : (value == -1) ? 0x04 : 0;
|
|
}
|
|
else if (exp.y->is<expr_float>())
|
|
{
|
|
auto value = std::stof(exp.y->as<expr_float>().value.data());
|
|
data.push_back(exp.y->as<expr_float>().value);
|
|
|
|
if (value != 1.0 && value != -1.0 && value != 0.0)
|
|
isconst = false;
|
|
else
|
|
flags |= (value == 1.0) ? 0x08 : (value == -1.0) ? 0x04 : 0;
|
|
}
|
|
else
|
|
{
|
|
isconst = false;
|
|
}
|
|
|
|
if (exp.z->is<expr_integer>())
|
|
{
|
|
auto value = std::atoi(exp.z->as<expr_integer>().value.data());
|
|
data.push_back(exp.z->as<expr_integer>().value);
|
|
|
|
if (value != 1 && value != -1 && value != 0)
|
|
isconst = false;
|
|
else
|
|
flags |= (value == 1) ? 0x02 : (value == -1) ? 0x01 : 0;
|
|
}
|
|
else if (exp.z->is<expr_float>())
|
|
{
|
|
auto value = std::stof(exp.z->as<expr_float>().value.data());
|
|
data.push_back(exp.z->as<expr_float>().value);
|
|
|
|
if (value != 1.0 && value != -1.0 && value != 0.0)
|
|
isconst = false;
|
|
else
|
|
flags |= (value == 1.0) ? 0x02 : (value == -1.0) ? 0x01 : 0;
|
|
}
|
|
else
|
|
{
|
|
isconst = false;
|
|
}
|
|
|
|
if (isconst)
|
|
{
|
|
emit_opcode(opcode::OP_VectorConstant, fmt::format("{}", flags));
|
|
}
|
|
else
|
|
{
|
|
// OP_GetVector seems to be broken, always use OP_Vector
|
|
emit_expr(*exp.z);
|
|
emit_expr(*exp.y);
|
|
emit_expr(*exp.x);
|
|
emit_opcode(opcode::OP_Vector);
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_animation(expr_animation const& exp) -> void
|
|
{
|
|
if (animtree_.empty())
|
|
{
|
|
throw comp_error(exp.loc(), "trying to use animation without specified using animtree");
|
|
}
|
|
|
|
emit_opcode(opcode::OP_GetAnimation, { animtree_, exp.value });
|
|
}
|
|
|
|
auto compiler::emit_expr_animtree(expr_animtree const& exp) -> void
|
|
{
|
|
if (animtree_.empty())
|
|
{
|
|
throw comp_error(exp.loc(), "trying to use animtree without specified using animtree");
|
|
}
|
|
|
|
emit_opcode(opcode::OP_GetInteger, { animtree_, "-1" });
|
|
}
|
|
|
|
auto compiler::emit_expr_istring(expr_istring const& exp) -> void
|
|
{
|
|
emit_opcode(opcode::OP_GetIString, exp.value);
|
|
}
|
|
|
|
auto compiler::emit_expr_string(expr_string const& exp) -> void
|
|
{
|
|
emit_opcode(opcode::OP_GetString, exp.value);
|
|
}
|
|
|
|
auto compiler::emit_expr_hash(expr_hash const& exp) -> void
|
|
{
|
|
emit_opcode(opcode::OP_GetHash, exp.value);
|
|
}
|
|
|
|
auto compiler::emit_expr_float(expr_float const& exp) -> void
|
|
{
|
|
emit_opcode(opcode::OP_GetFloat, exp.value);
|
|
}
|
|
|
|
auto compiler::emit_expr_integer(expr_integer const& exp) -> void
|
|
{
|
|
auto value = std::atoi(exp.value.data());
|
|
|
|
if (value == 0)
|
|
{
|
|
emit_opcode(opcode::OP_GetZero);
|
|
}
|
|
else if (value > 0 && value < 256)
|
|
{
|
|
emit_opcode(opcode::OP_GetByte, exp.value);
|
|
}
|
|
else if (value < 0 && value > -256)
|
|
{
|
|
emit_opcode(opcode::OP_GetNegByte, exp.value.substr(1));
|
|
}
|
|
else if (value > 0 && value < 65536)
|
|
{
|
|
emit_opcode(opcode::OP_GetUnsignedShort, exp.value);
|
|
}
|
|
else if (value < 0 && value > -65536)
|
|
{
|
|
emit_opcode(opcode::OP_GetNegUnsignedShort, exp.value.substr(1));
|
|
}
|
|
else
|
|
{
|
|
emit_opcode(opcode::OP_GetInteger, exp.value);
|
|
}
|
|
}
|
|
|
|
auto compiler::emit_expr_false(expr_false const&) -> void
|
|
{
|
|
emit_opcode(opcode::OP_GetZero);
|
|
}
|
|
|
|
auto compiler::emit_expr_true(expr_true const&) -> void
|
|
{
|
|
emit_opcode(opcode::OP_GetByte, "1");
|
|
}
|
|
|
|
auto compiler::emit_opcode(opcode op) -> void
|
|
{
|
|
function_->instructions.push_back(instruction::make());
|
|
|
|
auto& inst = function_->instructions.back();
|
|
inst->opcode = op;
|
|
inst->size = ctx_->opcode_size(op);
|
|
inst->index = index_;
|
|
inst->pos = debug_pos_;
|
|
|
|
index_ += inst->size;
|
|
}
|
|
|
|
auto compiler::emit_opcode(opcode op, std::string const& data) -> void
|
|
{
|
|
function_->instructions.push_back(instruction::make());
|
|
|
|
auto& inst = function_->instructions.back();
|
|
inst->opcode = op;
|
|
inst->size = ctx_->opcode_size(op);
|
|
inst->index = index_;
|
|
inst->data.push_back(data);
|
|
inst->pos = debug_pos_;
|
|
|
|
index_ += inst->size;
|
|
}
|
|
|
|
auto compiler::emit_opcode(opcode op, std::vector<std::string> const& data) -> void
|
|
{
|
|
function_->instructions.push_back(instruction::make());
|
|
|
|
auto& inst = function_->instructions.back();
|
|
inst->opcode = op;
|
|
inst->size = ctx_->opcode_size(op);
|
|
inst->index = index_;
|
|
inst->data = data;
|
|
inst->pos = debug_pos_;
|
|
|
|
index_ += inst->size;
|
|
}
|
|
|
|
auto compiler::process_function(decl_function const& func) -> void
|
|
{
|
|
process_expr_parameters(*func.params);
|
|
process_stmt_comp(*func.body);
|
|
}
|
|
|
|
auto compiler::process_stmt(stmt const& stm) -> void
|
|
{
|
|
switch (stm.kind())
|
|
{
|
|
case node::stmt_list:
|
|
process_stmt_list(stm.as<stmt_list>());
|
|
break;
|
|
case node::stmt_comp:
|
|
process_stmt_comp(stm.as<stmt_comp>());
|
|
break;
|
|
case node::stmt_dev:
|
|
process_stmt_dev(stm.as<stmt_dev>());
|
|
break;
|
|
case node::stmt_expr:
|
|
process_stmt_expr(stm.as<stmt_expr>());
|
|
break;
|
|
case node::stmt_waittill:
|
|
process_stmt_waittill(stm.as<stmt_waittill>());
|
|
break;
|
|
case node::stmt_if:
|
|
process_stmt_if(stm.as<stmt_if>());
|
|
break;
|
|
case node::stmt_ifelse:
|
|
process_stmt_ifelse(stm.as<stmt_ifelse>());
|
|
break;
|
|
case node::stmt_while:
|
|
process_stmt_while(stm.as<stmt_while>());
|
|
break;
|
|
case node::stmt_dowhile:
|
|
process_stmt_dowhile(stm.as<stmt_dowhile>());
|
|
break;
|
|
case node::stmt_for:
|
|
process_stmt_for(stm.as<stmt_for>());
|
|
break;
|
|
case node::stmt_foreach:
|
|
process_stmt_foreach(stm.as<stmt_foreach>());
|
|
break;
|
|
case node::stmt_switch:
|
|
process_stmt_switch(stm.as<stmt_switch>());
|
|
break;
|
|
case node::stmt_endon:
|
|
case node::stmt_notify:
|
|
case node::stmt_wait:
|
|
case node::stmt_waitrealtime:
|
|
case node::stmt_waittillmatch:
|
|
case node::stmt_waittillframeend:
|
|
case node::stmt_case:
|
|
case node::stmt_default:
|
|
case node::stmt_break:
|
|
case node::stmt_continue:
|
|
case node::stmt_return:
|
|
case node::stmt_breakpoint:
|
|
case node::stmt_prof_begin:
|
|
case node::stmt_prof_end:
|
|
break;
|
|
default:
|
|
throw comp_error(stm.loc(), "unknown statement");
|
|
}
|
|
}
|
|
|
|
auto compiler::process_stmt_list(stmt_list const& stm) -> void
|
|
{
|
|
for (auto const& entry : stm.list)
|
|
{
|
|
process_stmt(*entry);
|
|
}
|
|
}
|
|
|
|
auto compiler::process_stmt_comp(stmt_comp const& stm) -> void
|
|
{
|
|
process_stmt_list(*stm.block);
|
|
}
|
|
|
|
auto compiler::process_stmt_dev(stmt_dev const& stm) -> void
|
|
{
|
|
process_stmt_list(*stm.block);
|
|
}
|
|
|
|
auto compiler::process_stmt_expr(stmt_expr const& stm) -> void
|
|
{
|
|
switch (stm.value->kind())
|
|
{
|
|
case node::expr_increment:
|
|
process_expr(*stm.value->as<expr_increment>().lvalue);
|
|
break;
|
|
case node::expr_decrement:
|
|
process_expr(*stm.value->as<expr_decrement>().lvalue);
|
|
break;
|
|
case node::expr_assign:
|
|
process_expr(*stm.value->as<expr_assign>().lvalue);
|
|
break;
|
|
case node::expr_call:
|
|
case node::expr_method:
|
|
case node::expr_const:
|
|
case node::expr_empty:
|
|
break;
|
|
default:
|
|
throw comp_error(stm.loc(), "unknown expr statement expression");
|
|
}
|
|
}
|
|
|
|
auto compiler::process_stmt_waittill(stmt_waittill const& stm) -> void
|
|
{
|
|
for (auto const& entry : stm.args->list)
|
|
{
|
|
if (!entry->is<expr_identifier>() && !entry->is<expr_undefined>())
|
|
{
|
|
throw comp_error(entry->loc(), "illegal waittill param, must be a local variable or undefined");
|
|
}
|
|
|
|
if (entry->is<expr_identifier>())
|
|
{
|
|
variable_register(entry->as<expr_identifier>());
|
|
}
|
|
}
|
|
}
|
|
|
|
auto compiler::process_stmt_if(stmt_if const& stm) -> void
|
|
{
|
|
process_stmt(*stm.body);
|
|
}
|
|
|
|
auto compiler::process_stmt_ifelse(stmt_ifelse const& stm) -> void
|
|
{
|
|
process_stmt(*stm.stmt_if);
|
|
process_stmt(*stm.stmt_else);
|
|
}
|
|
|
|
auto compiler::process_stmt_while(stmt_while const& stm) -> void
|
|
{
|
|
process_stmt(*stm.body);
|
|
}
|
|
|
|
auto compiler::process_stmt_dowhile(stmt_dowhile const& stm) -> void
|
|
{
|
|
process_stmt(*stm.body);
|
|
}
|
|
|
|
auto compiler::process_stmt_for(stmt_for const& stm) -> void
|
|
{
|
|
process_stmt(*stm.init);
|
|
process_stmt(*stm.body);
|
|
process_stmt(*stm.iter);
|
|
}
|
|
|
|
auto compiler::process_stmt_foreach(stmt_foreach const& stm) -> void
|
|
{
|
|
if (stm.use_key)
|
|
{
|
|
process_expr(*stm.key);
|
|
process_expr(*stm.value);
|
|
}
|
|
else
|
|
{
|
|
process_expr(*stm.value);
|
|
process_expr(*stm.key);
|
|
}
|
|
|
|
process_expr(*stm.array);
|
|
process_stmt(*stm.body);
|
|
}
|
|
|
|
auto compiler::process_stmt_switch(stmt_switch const& stm) -> void
|
|
{
|
|
for (auto i = 0u; i < stm.body->block->list.size(); i++)
|
|
{
|
|
auto& entry = stm.body->block->list[i];
|
|
|
|
if (entry->is<stmt_case>())
|
|
{
|
|
process_stmt_list(*entry->as<stmt_case>().body);
|
|
}
|
|
else if (entry->is<stmt_default>())
|
|
{
|
|
process_stmt_list(*entry->as<stmt_default>().body);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto compiler::process_expr(expr const& exp) -> void
|
|
{
|
|
if (exp.is<expr_identifier>())
|
|
{
|
|
variable_register(exp.as<expr_identifier>());
|
|
}
|
|
else if (exp.is<expr_array>())
|
|
{
|
|
process_expr(*exp.as<expr_array>().obj);
|
|
}
|
|
}
|
|
|
|
auto compiler::process_expr_parameters(expr_parameters const& exp) -> void
|
|
{
|
|
for (auto const& entry : exp.list)
|
|
{
|
|
if (entry->is<expr_identifier>())
|
|
{
|
|
variable_register(entry->as<expr_identifier>());
|
|
}
|
|
else if (entry->is<expr_assign>())
|
|
{
|
|
process_expr(*entry->as<expr_assign>().lvalue);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto compiler::variable_register(expr_identifier const& exp) -> void
|
|
{
|
|
auto found = false;
|
|
|
|
for (auto i = 0u; i < stackframe_.size(); i++)
|
|
{
|
|
if (stackframe_[i] == exp.value)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) stackframe_.push_back(exp.value);
|
|
}
|
|
|
|
auto compiler::variable_access(expr_identifier const& exp) -> u8
|
|
{
|
|
for (auto i = 0u; i < stackframe_.size(); i++)
|
|
{
|
|
if (stackframe_[i] == exp.value)
|
|
{
|
|
return static_cast<u8>(stackframe_.size() - 1 - i);
|
|
}
|
|
}
|
|
|
|
throw comp_error(exp.loc(), fmt::format("local variable '{}' not found", exp.value));
|
|
}
|
|
|
|
auto compiler::is_constant_condition(expr const& exp) -> bool
|
|
{
|
|
switch (exp.kind())
|
|
{
|
|
case node::expr_empty:
|
|
case node::expr_true:
|
|
return true;
|
|
case node::expr_false:
|
|
throw comp_error(exp.loc(), "condition can't be always false");
|
|
case node::expr_integer:
|
|
{
|
|
auto num = std::stoi(exp.as<expr_integer>().value);
|
|
if (num != 0)
|
|
return true;
|
|
else
|
|
throw comp_error(exp.loc(), "condition can't be always false");
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
auto compiler::insert_label(std::string const& name) -> void
|
|
{
|
|
auto const itr = function_->labels.find(index_);
|
|
|
|
if (itr != function_->labels.end())
|
|
{
|
|
for (auto& inst : function_->instructions)
|
|
{
|
|
switch (inst->opcode)
|
|
{
|
|
case opcode::OP_JumpOnFalse:
|
|
case opcode::OP_JumpOnTrue:
|
|
case opcode::OP_JumpOnFalseExpr:
|
|
case opcode::OP_JumpOnTrueExpr:
|
|
case opcode::OP_Jump:
|
|
case opcode::OP_JumpBack:
|
|
case opcode::OP_Switch:
|
|
case opcode::OP_DevblockBegin:
|
|
if (inst->data[0] == name)
|
|
inst->data[0] = itr->second;
|
|
break;
|
|
case opcode::OP_EndSwitch:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
function_->labels.insert({ index_, name });
|
|
}
|
|
}
|
|
|
|
auto compiler::insert_label() -> std::string
|
|
{
|
|
auto const itr = function_->labels.find(index_);
|
|
|
|
if (itr != function_->labels.end())
|
|
{
|
|
return itr->second;
|
|
}
|
|
else
|
|
{
|
|
label_idx_++;
|
|
auto name = fmt::format("loc_{}", label_idx_);
|
|
function_->labels.insert({ index_, name });
|
|
return name;
|
|
}
|
|
}
|
|
|
|
auto compiler::create_label() -> std::string
|
|
{
|
|
label_idx_++;
|
|
return fmt::format("loc_{}", label_idx_);
|
|
}
|
|
|
|
} // namespace xsk::arc
|