gsc-tool/src/h1/xsk/compiler.cpp
2021-12-30 20:36:13 +01:00

2905 lines
87 KiB
C++

// Copyright 2021 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 "h1.hpp"
#include "parser.hpp"
#include "lexer.hpp"
namespace xsk::gsc::h1
{
auto compiler::output() -> std::vector<function::ptr>
{
return std::move(assembly_);
}
void compiler::compile(const std::string& file, std::vector<std::uint8_t>& data)
{
filename_ = file;
sources_.clear();
auto prog = parse_buffer(filename_, data);
compile_program(prog);
}
void compiler::read_callback(std::function<std::vector<std::uint8_t>(const std::string&)> func)
{
read_callback_ = func;
}
auto compiler::parse_buffer(const std::string& file, std::vector<std::uint8_t>& data) -> ast::program::ptr
{
yyscan_t scanner;
context ctx;
ast::program::ptr result(nullptr);
ctx.header_top = 0;
ctx.mode = mode_;
ctx.read_callback = read_callback_;
ctx.sources = &sources_;
ctx.loc.initialize(&file);
// Add the two NULL terminators, required by flex.
data.push_back(0);
data.push_back(0);
if (h1_lex_init(&scanner))
{
throw comp_error(ctx.loc, "An unknown error ocurred while starting lexer context.");
}
ctx.scanner = scanner;
YY_BUFFER_STATE yybuffer = h1__scan_buffer(reinterpret_cast<char*>(data.data()), data.size(), scanner);
parser parser(scanner, &ctx, result);
if (parser.parse() || result == nullptr)
{
throw comp_error(ctx.loc, "An unknown error ocurred while parsing gsc file.");
}
h1__delete_buffer(yybuffer, scanner);
h1_lex_destroy(scanner);
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;
for (const auto& entry : program->declarations)
{
if (entry == ast::kind::decl_thread)
{
local_functions_.push_back(entry.as_thread->name->value);
}
}
for (const auto& include : program->includes)
{
emit_include(include);
}
for (const auto& declaration : program->declarations)
{
emit_declaration(declaration);
}
}
void compiler::emit_include(const ast::include::ptr& include)
{
const auto& path = include->path->value;
for (const auto& inc : includes_)
{
if(inc.name == path)
{
throw comp_error(include->loc(), "error duplicated include file '" + path + "'.");
}
}
if (map_known_includes(path)) return;
try
{
auto program = parse_file(path);
std::vector<std::string> funcs;
for (const auto& decl : program->declarations)
{
if (decl == ast::kind::decl_thread)
{
funcs.push_back(decl.as_thread->name->value);
}
}
if (funcs.size() == 0)
{
throw comp_error(include->loc(), "error empty include file '" + path + "'.");
}
includes_.push_back(include_t(path, funcs));
}
catch (const std::exception& e)
{
throw comp_error(include->loc(), "error parsing include file '" + path + "': " + e.what());
}
}
void compiler::emit_declaration(const ast::decl& decl)
{
switch (decl.kind())
{
case ast::kind::decl_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)
{
animtrees_.push_back({ animtree->name->value, false });
}
void compiler::emit_decl_constant(const ast::decl_constant::ptr& constant)
{
constants_.insert({ constant->name->value, std::move(constant->value) });
}
void compiler::emit_decl_thread(const ast::decl_thread::ptr& thread)
{
function_ = std::make_unique<function>();
function_->index = index_;
function_->name = thread->name->value;
auto blk = std::make_unique<block>();
stack_idx_ = 0;
label_idx_ = 0;
can_break_ = false;
can_continue_ = false;
local_stack_.clear();
break_blks_.clear();
continue_blks_.clear();
process_thread(thread, blk);
emit_expr_parameters(thread->params, blk);
emit_stmt_list(thread->stmt, blk, true);
emit_opcode(opcode::OP_End);
function_->size = index_ - function_->index;
assembly_.push_back(std::move(function_));
}
void compiler::emit_stmt(const ast::stmt& stmt, const block::ptr& blk, bool last)
{
switch (stmt.kind())
{
case ast::kind::stmt_list:
emit_stmt_list(stmt.as_list, blk, last);
break;
case ast::kind::stmt_expr:
emit_stmt_expr(stmt.as_expr, blk);
break;
case ast::kind::stmt_call:
emit_stmt_call(stmt.as_call, blk);
break;
case ast::kind::stmt_assign:
emit_stmt_assign(stmt.as_assign, blk);
break;
case ast::kind::stmt_endon:
emit_stmt_endon(stmt.as_endon, blk);
break;
case ast::kind::stmt_notify:
emit_stmt_notify(stmt.as_notify, blk);
break;
case ast::kind::stmt_wait:
emit_stmt_wait(stmt.as_wait, blk);
break;
case ast::kind::stmt_waittill:
emit_stmt_waittill(stmt.as_waittill, blk);
break;
case ast::kind::stmt_waittillmatch:
emit_stmt_waittillmatch(stmt.as_waittillmatch, blk);
break;
case ast::kind::stmt_waittillframeend:
emit_stmt_waittillframeend(stmt.as_waittillframeend, blk);
break;
case ast::kind::stmt_waitframe:
emit_stmt_waitframe(stmt.as_waitframe, 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_waitframe(const ast::stmt_waitframe::ptr&, const block::ptr&)
{
emit_opcode(opcode::OP_waitFrame);
}
void compiler::emit_stmt_if(const ast::stmt_if::ptr& stmt, const block::ptr& blk, bool last)
{
auto end_loc = create_label();
if (stmt->test == ast::kind::expr_not)
{
emit_expr(stmt->test.as_not->rvalue, blk);
emit_opcode(opcode::OP_JumpOnTrue, end_loc);
}
else
{
emit_expr(stmt->test, blk);
emit_opcode(opcode::OP_JumpOnFalse, end_loc);
}
blk->transfer(stmt->blk);
emit_stmt(stmt->stmt, stmt->blk, last);
last ? emit_opcode(opcode::OP_End) : emit_remove_local_vars(stmt->blk);
insert_label(end_loc);
}
void compiler::emit_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt, const block::ptr& blk, bool last)
{
std::vector<block*> childs;
auto else_loc = create_label();
auto end_loc = create_label();
if (stmt->test == ast::kind::expr_not)
{
emit_expr(stmt->test.as_not->rvalue, blk);
emit_opcode(opcode::OP_JumpOnTrue, else_loc);
}
else
{
emit_expr(stmt->test, blk);
emit_opcode(opcode::OP_JumpOnFalse, else_loc);
}
blk->transfer(stmt->blk_if);
emit_stmt(stmt->stmt_if, stmt->blk_if, last);
emit_remove_local_vars(stmt->blk_if);
if (stmt->blk_if->abort == abort_t::abort_none)
childs.push_back(stmt->blk_if.get());
last ? emit_opcode(opcode::OP_End) : emit_opcode(opcode::OP_jump, end_loc);
insert_label(else_loc);
blk->transfer(stmt->blk_else);
emit_stmt(stmt->stmt_else, stmt->blk_else, last);
last ? emit_opcode(opcode::OP_End) : emit_remove_local_vars(stmt->blk_else);
if (stmt->blk_else->abort == abort_t::abort_none)
childs.push_back(stmt->blk_else.get());
insert_label(end_loc);
blk->init_from_child(childs);
}
void compiler::emit_stmt_while(const ast::stmt_while::ptr& stmt, const block::ptr& blk)
{
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
auto old_break = can_break_;
auto old_continue = can_continue_;
break_blks_.clear();
continue_blks_.clear();
can_break_ = true;
can_continue_ = true;
auto break_loc = create_label();
auto continue_loc = create_label();
blk->transfer(stmt->blk);
stmt->blk->loc_break = break_loc;
stmt->blk->loc_continue = continue_loc;
emit_create_local_vars(stmt->blk);
blk->local_vars_create_count = stmt->blk->local_vars_create_count;
for (auto i = 0u; i < blk->local_vars_create_count; i++)
{
if (!blk->local_vars.at(i).init)
blk->local_vars.at(i).init = true;
}
auto begin_loc = insert_label();
bool const_cond = is_constant_condition(stmt->test);
if (!const_cond)
{
emit_expr(stmt->test, blk);
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
}
emit_stmt(stmt->stmt, stmt->blk, false);
insert_label(continue_loc);
emit_opcode(opcode::OP_jumpback, begin_loc);
insert_label(break_loc);
if (const_cond)
blk->init_from_child(break_blks_);
can_break_ = old_break;
can_continue_ = old_continue;
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::emit_stmt_dowhile(const ast::stmt_dowhile::ptr& stmt, const block::ptr& blk)
{
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
auto old_break = can_break_;
auto old_continue = can_continue_;
break_blks_.clear();
continue_blks_.clear();
can_break_ = true;
can_continue_ = true;
auto break_loc = create_label();
auto continue_loc = create_label();
blk->transfer(stmt->blk);
stmt->blk->loc_break = break_loc;
stmt->blk->loc_continue = continue_loc;
emit_create_local_vars(stmt->blk);
blk->local_vars_create_count = stmt->blk->local_vars_create_count;
for (auto i = 0u; i < blk->local_vars_create_count; i++)
{
if (!blk->local_vars.at(i).init)
blk->local_vars.at(i).init = true;
}
auto begin_loc = insert_label();
emit_stmt(stmt->stmt, stmt->blk, false);
bool const_cond = is_constant_condition(stmt->test);
if (!const_cond)
{
emit_expr(stmt->test, blk);
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
}
insert_label(continue_loc);
emit_opcode(opcode::OP_jumpback, begin_loc);
insert_label(break_loc);
if (const_cond)
blk->init_from_child(break_blks_);
can_break_ = old_break;
can_continue_ = old_continue;
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::emit_stmt_for(const ast::stmt_for::ptr& stmt, const block::ptr& blk)
{
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
auto old_break = can_break_;
auto old_continue = can_continue_;
break_blks_.clear();
continue_blks_.clear();
can_break_ = false;
can_continue_ = false;
auto break_loc = create_label();
auto continue_loc = create_label();
emit_stmt(stmt->init, blk, false);
blk->transfer(stmt->blk);
stmt->blk->loc_break = break_loc;
stmt->blk->loc_continue = continue_loc;
emit_create_local_vars(stmt->blk);
blk->local_vars_create_count = stmt->blk->local_vars_create_count;
for (auto i = 0u; i < blk->local_vars_create_count; i++)
{
if (!blk->local_vars.at(i).init)
blk->local_vars.at(i).init = true;
}
blk->transfer(stmt->blk_iter);
auto begin_loc = insert_label();
bool const_cond = is_constant_condition(stmt->test);
if (!const_cond)
{
emit_expr(stmt->test, blk);
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
}
can_break_ = true;
can_continue_ = true;
emit_stmt(stmt->stmt, stmt->blk, false);
if (stmt->blk->abort == abort_t::abort_none)
continue_blks_.push_back(stmt->blk.get());
can_break_ = false;
can_continue_ = false;
insert_label(continue_loc);
stmt->blk_iter->init_from_child(continue_blks_);
emit_stmt(stmt->iter, stmt->blk_iter, false);
emit_opcode(opcode::OP_jumpback, begin_loc);
insert_label(break_loc);
if (const_cond)
blk->init_from_child(break_blks_);
can_break_ = old_break;
can_continue_ = old_continue;
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::emit_stmt_foreach(const ast::stmt_foreach::ptr& stmt, const block::ptr& blk)
{
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
auto old_break = can_break_;
auto old_continue = can_continue_;
break_blks_.clear();
continue_blks_.clear();
can_break_ = false;
can_continue_ = false;
auto break_loc = create_label();
auto continue_loc = create_label();
emit_expr(stmt->array_expr, blk);
emit_expr_variable_ref(stmt->array, blk, true);
emit_expr_variable(stmt->array, blk);
emit_opcode(opcode::OP_CallBuiltin1, "getfirstarraykey");
emit_expr_variable_ref(stmt->key_expr, blk, true);
blk->transfer(stmt->ctx);
stmt->ctx->loc_break = break_loc;
stmt->ctx->loc_continue = continue_loc;
emit_create_local_vars(stmt->ctx);
blk->local_vars_create_count = stmt->ctx->local_vars_create_count;
for (auto i = 0u; i < blk->local_vars_create_count; i++)
{
if (!blk->local_vars.at(i).init)
blk->local_vars.at(i).init = true;
}
blk->transfer(stmt->ctx_post);
auto begin_loc = insert_label();
emit_expr_variable(stmt->key_expr, blk);
emit_opcode(opcode::OP_CallBuiltin1, "isdefined");
emit_opcode(opcode::OP_JumpOnFalse, break_loc);
can_break_ = true;
can_continue_ = true;
emit_expr_variable(stmt->key_expr, stmt->ctx);
emit_opcode(opcode::OP_EvalLocalArrayCached, variable_access_index(stmt->array.as_identifier, stmt->ctx));
emit_expr_variable_ref(stmt->value_expr, stmt->ctx, true);
emit_stmt(stmt->stmt, stmt->ctx, false);
if (stmt->ctx->abort == abort_t::abort_none)
continue_blks_.push_back(stmt->ctx.get());
can_break_ = false;
can_continue_ = false;
insert_label(continue_loc);
stmt->ctx_post->init_from_child(continue_blks_);
emit_expr_variable(stmt->key_expr, stmt->ctx_post);
emit_expr_variable(stmt->array, stmt->ctx_post);
emit_opcode(opcode::OP_CallBuiltin2, "getnextarraykey");
emit_expr_variable_ref(stmt->key_expr, stmt->ctx_post, true);
emit_opcode(opcode::OP_jumpback, begin_loc);
insert_label(break_loc);
emit_expr_clear_local(stmt->array.as_identifier, blk);
if (!stmt->use_key) emit_expr_clear_local(stmt->key_expr.as_identifier, blk);
can_break_ = old_break;
can_continue_ = old_continue;
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::emit_stmt_switch(const ast::stmt_switch::ptr& stmt, const block::ptr& blk)
{
auto old_breaks = break_blks_;
auto old_break = can_break_;
break_blks_.clear();
can_break_ = false;
auto jmptable_loc = create_label();
auto break_loc = create_label();
emit_expr(stmt->test, blk);
emit_opcode(opcode::OP_switch, jmptable_loc);
can_break_ = true;
std::vector<std::string> data;
data.push_back(utils::string::va("%d", stmt->stmt->list.size()));
bool has_default = false;
block* default_ctx = nullptr;
for (auto i = 0; i < stmt->stmt->list.size(); i++)
{
auto& entry = stmt->stmt->list[i];
if (entry == ast::kind::stmt_case)
{
if (has_default)
{
comp_error(stmt->loc(), "default must be last case");
}
auto& case_ = entry.as_case;
if (case_->label == ast::kind::expr_integer)
{
auto loc = insert_label();
data.push_back("case");
data.push_back(case_->label.as_integer->value);
data.push_back(loc);
}
else if (case_->label == ast::kind::expr_string)
{
auto loc = insert_label();
data.push_back("case");
data.push_back(case_->label.as_string->value);
data.push_back(loc);
}
else
{
throw comp_error(stmt->loc(), "case type must be int or string");
}
blk->transfer(case_->blk);
case_->blk->loc_break = break_loc;
emit_stmt_list(case_->stmt, case_->blk, false);
if (case_->stmt->list.size() > 0)
emit_remove_local_vars(case_->blk);
}
else if (entry == ast::kind::stmt_default)
{
auto loc = insert_label();
data.push_back("default");
data.push_back(loc);
has_default = true;
default_ctx = entry.as_default->blk.get();
blk->transfer(entry.as_default->blk);
entry.as_default->blk->loc_break = break_loc;
emit_stmt_list(entry.as_default->stmt, entry.as_default->blk, false);
if (entry.as_default->stmt->list.size() > 0)
emit_remove_local_vars(entry.as_default->blk);
}
else
{
throw comp_error(entry.loc(), "missing case statement");
}
}
if (has_default)
{
if(default_ctx->abort == abort_t::abort_none)
{
break_blks_.push_back(default_ctx);
}
blk->init_from_child(break_blks_);
}
insert_label(jmptable_loc);
emit_opcode(opcode::OP_endswitch, data);
auto offset = 7 * stmt->stmt->list.size();
function_->instructions.back()->size += offset;
index_ += offset;
insert_label(break_loc);
can_break_ = old_break;
break_blks_ = old_breaks;
}
void compiler::emit_stmt_case(const ast::stmt_case::ptr& stmt, const block::ptr& blk)
{
throw comp_error(stmt->loc(), "illegal case statement");
}
void compiler::emit_stmt_default(const ast::stmt_default::ptr& stmt, const block::ptr& blk)
{
throw comp_error(stmt->loc(), "illegal default statement");
}
void compiler::emit_stmt_break(const ast::stmt_break::ptr& stmt, const block::ptr& blk)
{
if (!can_break_ || blk->abort != abort_t::abort_none || blk->loc_break == "")
throw comp_error(stmt->loc(), "illegal break statement");
break_blks_.push_back(blk.get());
emit_remove_local_vars(blk);
blk->abort = abort_t::abort_break;
emit_opcode(opcode::OP_jump, blk->loc_break);
}
void compiler::emit_stmt_continue(const ast::stmt_continue::ptr& stmt, const block::ptr& blk)
{
if (!can_continue_ || blk->abort != abort_t::abort_none || blk->loc_continue == "")
throw comp_error(stmt->loc(), "illegal continue statement");
continue_blks_.push_back(blk.get());
emit_remove_local_vars(blk);
blk->abort = abort_t::abort_continue;
emit_opcode(opcode::OP_jump, blk->loc_continue);
}
void compiler::emit_stmt_return(const ast::stmt_return::ptr& stmt, const block::ptr& blk)
{
if (blk->abort == abort_t::abort_none)
blk->abort = abort_t::abort_return;
if (stmt->expr != ast::kind::null)
{
emit_expr(stmt->expr, blk);
emit_opcode(opcode::OP_Return);
}
else
emit_opcode(opcode::OP_End);
}
void compiler::emit_stmt_breakpoint(const ast::stmt_breakpoint::ptr& stmt, const block::ptr& blk)
{
// TODO:
}
void compiler::emit_stmt_prof_begin(const ast::stmt_prof_begin::ptr& stmt, const block::ptr& blk)
{
// TODO:
}
void compiler::emit_stmt_prof_end(const ast::stmt_prof_end::ptr& stmt, const block::ptr& blk)
{
// TODO:
}
void compiler::emit_expr(const ast::expr& expr, const block::ptr& blk)
{
switch (expr.kind())
{
case ast::kind::expr_paren:
emit_expr(expr.as_paren->child, blk);
break;
case ast::kind::expr_ternary:
emit_expr_ternary(expr.as_ternary, blk);
break;
case ast::kind::expr_and:
emit_expr_and(expr.as_and, blk);
break;
case ast::kind::expr_or:
emit_expr_or(expr.as_or, blk);
break;
case ast::kind::expr_equality:
case ast::kind::expr_inequality:
case ast::kind::expr_less:
case ast::kind::expr_greater:
case ast::kind::expr_less_equal:
case ast::kind::expr_greater_equal:
case ast::kind::expr_bitwise_or:
case ast::kind::expr_bitwise_and:
case ast::kind::expr_bitwise_exor:
case ast::kind::expr_shift_left:
case ast::kind::expr_shift_right:
case ast::kind::expr_add:
case ast::kind::expr_sub:
case ast::kind::expr_mul:
case ast::kind::expr_div:
case ast::kind::expr_mod:
emit_expr_binary(expr.as_binary, blk);
break;
case ast::kind::expr_complement:
emit_expr_complement(expr.as_complement, blk);
break;
case ast::kind::expr_not:
emit_expr_not(expr.as_not, blk);
break;
case ast::kind::expr_call:
emit_expr_call(expr.as_call, blk);
break;
case ast::kind::expr_method:
emit_expr_method(expr.as_method, blk);
break;
case ast::kind::expr_reference:
emit_expr_reference(expr.as_reference, blk);
break;
case ast::kind::expr_add_array:
emit_expr_add_array(expr.as_add_array, blk);
break;
case ast::kind::expr_array:
emit_expr_array(expr.as_array, blk);
break;
case ast::kind::expr_field:
emit_expr_field(expr.as_field, blk);
break;
case ast::kind::expr_size:
emit_expr_size(expr.as_size, blk);
break;
case ast::kind::expr_thisthread:
emit_opcode(opcode::OP_GetThisthread);
break;
case ast::kind::expr_empty_array:
emit_opcode(opcode::OP_EmptyArray);
break;
case ast::kind::expr_undefined:
emit_opcode(opcode::OP_GetUndefined);
break;
case ast::kind::expr_game:
emit_opcode(opcode::OP_GetGame);
break;
case ast::kind::expr_self:
emit_opcode(opcode::OP_GetSelf);
break;
case ast::kind::expr_anim:
emit_opcode(opcode::OP_GetAnim);
break;
case ast::kind::expr_level:
emit_opcode(opcode::OP_GetLevel);
break;
case ast::kind::expr_animation:
emit_expr_animation(expr.as_animation);
break;
case ast::kind::expr_animtree:
emit_expr_animtree(expr.as_animtree);
break;
case ast::kind::expr_identifier:
emit_expr_local(expr.as_identifier, blk);
break;
case ast::kind::expr_istring:
emit_expr_istring(expr.as_istring);
break;
case ast::kind::expr_string:
emit_expr_string(expr.as_string);
break;
case ast::kind::expr_color:
emit_expr_color(expr.as_color);
break;
case ast::kind::expr_vector:
emit_expr_vector(expr.as_vector, blk);
break;
case ast::kind::expr_float:
emit_expr_float(expr.as_float);
break;
case ast::kind::expr_integer:
emit_expr_integer(expr.as_integer);
break;
case ast::kind::expr_false:
emit_expr_false(expr.as_false);
break;
case ast::kind::expr_true:
emit_expr_true(expr.as_true);
break;
case ast::kind::null:
break;
default:
throw comp_error(expr.loc(), "unknown expression");
}
}
void compiler::emit_expr_assign(const ast::expr_assign::ptr& expr, const block::ptr& blk)
{
if (expr->kind() == ast::kind::expr_assign_equal)
{
if (expr->rvalue == ast::kind::expr_undefined)
{
emit_expr_clear(expr->lvalue, blk);
return;
}
emit_expr(expr->rvalue, blk);
emit_expr_variable_ref(expr->lvalue, blk, true);
return;
}
emit_expr(expr->lvalue, blk);
emit_expr(expr->rvalue, blk);
switch (expr->kind())
{
case ast::kind::expr_assign_add:
emit_opcode(opcode::OP_plus);
break;
case ast::kind::expr_assign_sub:
emit_opcode(opcode::OP_minus);
break;
case ast::kind::expr_assign_mul:
emit_opcode(opcode::OP_multiply);
break;
case ast::kind::expr_assign_div:
emit_opcode(opcode::OP_divide);
break;
case ast::kind::expr_assign_mod:
emit_opcode(opcode::OP_mod);
break;
case ast::kind::expr_assign_shift_left:
emit_opcode(opcode::OP_shift_left);
break;
case ast::kind::expr_assign_shift_right:
emit_opcode(opcode::OP_shift_right);
break;
case ast::kind::expr_assign_bitwise_or:
emit_opcode(opcode::OP_bit_or);
break;
case ast::kind::expr_assign_bitwise_and:
emit_opcode(opcode::OP_bit_and);
break;
case ast::kind::expr_assign_bitwise_exor:
emit_opcode(opcode::OP_bit_ex_or);
break;
default:
throw comp_error(expr->loc(), "unknown assign operation");
}
emit_expr_variable_ref(expr->lvalue, blk, true);
}
void compiler::emit_expr_clear(const ast::expr& expr, const block::ptr& blk)
{
switch (expr.kind())
{
case ast::kind::expr_array:
emit_expr(expr.as_array->key, blk);
expr.as_array->obj == ast::kind::expr_game ? emit_opcode(opcode::OP_GetGameRef) : emit_expr_variable_ref(expr.as_array->obj, blk, false);
emit_opcode(opcode::OP_ClearArray);
break;
case ast::kind::expr_field:
emit_expr_object(expr.as_field->obj, blk);
emit_opcode(opcode::OP_ClearFieldVariable, expr.as_field->field->value);
break;
case ast::kind::expr_identifier:
emit_opcode(opcode::OP_GetUndefined);
emit_expr_local_ref(expr.as_identifier, blk, true);
break;
default:
throw comp_error(expr.loc(), "unknown clear variable lvalue");
}
}
void compiler::emit_expr_clear_local(const ast::expr_identifier::ptr& expr, const block::ptr& blk)
{
auto index = variable_stack_index(expr, blk);
if (index == 0)
emit_opcode(opcode::OP_ClearLocalVariableFieldCached0);
else
emit_opcode(opcode::OP_ClearLocalVariableFieldCached, variable_access_index(expr, blk));
}
void compiler::emit_expr_increment(const ast::expr_increment::ptr& expr, const block::ptr& blk, bool fromstmt)
{
if (fromstmt)
{
emit_expr_variable_ref(expr->lvalue, blk, false);
emit_opcode(opcode::OP_inc);
emit_opcode(opcode::OP_SetVariableField);
}
else
{
// TODO:
}
}
void compiler::emit_expr_decrement(const ast::expr_decrement::ptr& expr, const block::ptr& blk, bool fromstmt)
{
if (fromstmt)
{
emit_expr_variable_ref(expr->lvalue, blk, false);
emit_opcode(opcode::OP_dec);
emit_opcode(opcode::OP_SetVariableField);
}
else
{
// TODO:
}
}
void compiler::emit_expr_ternary(const ast::expr_ternary::ptr& expr, const block::ptr& blk)
{
auto else_loc = create_label();
auto end_loc = create_label();
if (expr->test == ast::kind::expr_not)
{
emit_expr(expr->test.as_not->rvalue, blk);
emit_opcode(opcode::OP_JumpOnTrue, else_loc);
}
else
{
emit_expr(expr->test, blk);
emit_opcode(opcode::OP_JumpOnFalse, else_loc);
}
emit_expr(expr->true_expr, blk);
emit_opcode(opcode::OP_jump, end_loc);
insert_label(else_loc);
emit_expr(expr->false_expr, blk);
insert_label(end_loc);
}
void compiler::emit_expr_binary(const ast::expr_binary::ptr& expr, const block::ptr& blk)
{
emit_expr(expr->lvalue, blk);
emit_expr(expr->rvalue, blk);
switch (expr->kind())
{
case ast::kind::expr_equality:
emit_opcode(opcode::OP_equality);
break;
case ast::kind::expr_inequality:
emit_opcode(opcode::OP_inequality);
break;
case ast::kind::expr_less:
emit_opcode(opcode::OP_less);
break;
case ast::kind::expr_greater:
emit_opcode(opcode::OP_greater);
break;
case ast::kind::expr_less_equal:
emit_opcode(opcode::OP_less_equal);
break;
case ast::kind::expr_greater_equal:
emit_opcode(opcode::OP_greater_equal);
break;
case ast::kind::expr_bitwise_or:
emit_opcode(opcode::OP_bit_or);
break;
case ast::kind::expr_bitwise_and:
emit_opcode(opcode::OP_bit_and);
break;
case ast::kind::expr_bitwise_exor:
emit_opcode(opcode::OP_bit_ex_or);
break;
case ast::kind::expr_shift_left:
emit_opcode(opcode::OP_shift_left);
break;
case ast::kind::expr_shift_right:
emit_opcode(opcode::OP_shift_right);
break;
case ast::kind::expr_add:
emit_opcode(opcode::OP_plus);
break;
case ast::kind::expr_sub:
emit_opcode(opcode::OP_minus);
break;
case ast::kind::expr_mul:
emit_opcode(opcode::OP_multiply);
break;
case ast::kind::expr_div:
emit_opcode(opcode::OP_divide);
break;
case ast::kind::expr_mod:
emit_opcode(opcode::OP_mod);
break;
default:
throw comp_error(expr->loc(), "unknown binary expression");
}
}
void compiler::emit_expr_and(const ast::expr_and::ptr& expr, const block::ptr& blk)
{
auto label = create_label();
emit_expr(expr->lvalue, blk);
emit_opcode(opcode::OP_JumpOnFalseExpr, label);
emit_expr(expr->rvalue, blk);
emit_opcode(opcode::OP_CastBool);
insert_label(label);
}
void compiler::emit_expr_or(const ast::expr_or::ptr& expr, const block::ptr& blk)
{
auto label = create_label();
emit_expr(expr->lvalue, blk);
emit_opcode(opcode::OP_JumpOnTrueExpr, label);
emit_expr(expr->rvalue, blk);
emit_opcode(opcode::OP_CastBool);
insert_label(label);
}
void compiler::emit_expr_complement(const ast::expr_complement::ptr& expr, const block::ptr& blk)
{
emit_expr(expr->rvalue, blk);
emit_opcode(opcode::OP_BoolComplement);
}
void compiler::emit_expr_not(const ast::expr_not::ptr& expr, const block::ptr& blk)
{
emit_expr(expr->rvalue, blk);
emit_opcode(opcode::OP_BoolNot);
}
void compiler::emit_expr_call(const ast::expr_call::ptr& expr, const block::ptr& blk)
{
if (expr->call == ast::kind::expr_pointer)
emit_expr_call_pointer(expr->call.as_pointer, blk);
else if (expr->call == ast::kind::expr_function)
emit_expr_call_function(expr->call.as_function, blk);
else
throw comp_error(expr->loc(), "unknown function call expression");
}
void compiler::emit_expr_call_pointer(const ast::expr_pointer::ptr& expr, const block::ptr& blk)
{
if (expr->mode == ast::call::mode::normal)
emit_opcode(opcode::OP_PreScriptCall);
emit_expr_arguments(expr->args, blk);
emit_expr(expr->func, blk);
auto argcount = utils::string::va("%d", expr->args->list.size());
switch (expr->mode)
{
case ast::call::mode::normal:
emit_opcode(opcode::OP_ScriptFunctionCallPointer);
break;
case ast::call::mode::thread:
emit_opcode(opcode::OP_ScriptThreadCallPointer, argcount);
break;
case ast::call::mode::childthread:
emit_opcode(opcode::OP_ScriptChildThreadCallPointer, argcount);
break;
case ast::call::mode::builtin:
emit_opcode(opcode::OP_CallBuiltinPointer, argcount);
break;
}
}
void compiler::emit_expr_call_function(const ast::expr_function::ptr& expr, const block::ptr& blk)
{
auto type = resolve_function_type(expr);
if (type != ast::call::type::builtin && expr->mode == ast::call::mode::normal && expr->args->list.size() > 0)
emit_opcode(opcode::OP_PreScriptCall);
emit_expr_arguments(expr->args, blk);
auto argcount = utils::string::va("%d", expr->args->list.size());
if (type == ast::call::type::local)
{
switch (expr->mode)
{
case ast::call::mode::normal:
if(expr->args->list.size() > 0)
emit_opcode(opcode::OP_ScriptLocalFunctionCall, expr->name->value);
else
emit_opcode(opcode::OP_ScriptLocalFunctionCall2, expr->name->value);
break;
case ast::call::mode::thread:
emit_opcode(opcode::OP_ScriptLocalThreadCall, { expr->name->value, argcount });
break;
case ast::call::mode::childthread:
emit_opcode(opcode::OP_ScriptLocalChildThreadCall, { expr->name->value, argcount });
break;
case ast::call::mode::builtin:
// no local builtins
break;
}
}
else if (type == ast::call::type::far)
{
switch (expr->mode)
{
case ast::call::mode::normal:
if(expr->args->list.size() > 0)
emit_opcode(opcode::OP_ScriptFarFunctionCall, { expr->path->value, expr->name->value });
else
emit_opcode(opcode::OP_ScriptFarFunctionCall2, { expr->path->value, expr->name->value });
break;
case ast::call::mode::thread:
emit_opcode(opcode::OP_ScriptFarThreadCall, { argcount, expr->path->value, expr->name->value });
break;
case ast::call::mode::childthread:
emit_opcode(opcode::OP_ScriptFarChildThreadCall, { argcount, expr->path->value, expr->name->value });
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, { argcount, expr->name->value });
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, { argcount, expr->path->value, expr->name->value });
break;
case ast::call::mode::childthread:
emit_opcode(opcode::OP_ScriptFarMethodChildThreadCall, { argcount, expr->path->value, expr->name->value });
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, { argcount, expr->name->value });
break;
}
}
}
void compiler::emit_expr_add_array(const ast::expr_add_array::ptr& expr, const block::ptr& blk)
{
emit_opcode(opcode::OP_EmptyArray);
for (const auto& arg : expr->args->list)
{
emit_expr(arg, blk);
emit_opcode(opcode::OP_AddArray);
}
}
void compiler::emit_expr_parameters(const ast::expr_parameters::ptr& expr, const block::ptr& blk)
{
for (const auto& entry : expr->list)
{
initialize_variable(entry, blk);
emit_opcode(opcode::OP_SafeCreateVariableFieldCached, variable_create_index(entry, blk));
}
emit_opcode(opcode::OP_checkclearparams);
}
void compiler::emit_expr_arguments(const ast::expr_arguments::ptr& expr, const block::ptr& blk)
{
std::reverse(expr->list.begin(), expr->list.end());
for (auto& entry : expr->list)
{
emit_expr(entry, blk);
}
}
void compiler::emit_expr_reference(const ast::expr_reference::ptr& expr, const block::ptr& blk)
{
bool method = false;
auto type = resolve_reference_type(expr, method);
switch (type)
{
case ast::call::type::local:
emit_opcode(opcode::OP_GetLocalFunction, expr->name->value);
break;
case ast::call::type::far:
emit_opcode(opcode::OP_GetFarFunction, { expr->path->value, expr->name->value });
break;
case ast::call::type::builtin:
if(method)
emit_opcode(opcode::OP_GetBuiltinMethod, expr->name->value);
else
emit_opcode(opcode::OP_GetBuiltinFunction, expr->name->value);
break;
}
}
void compiler::emit_expr_size(const ast::expr_size::ptr& expr, const block::ptr& blk)
{
emit_expr(expr->obj, blk);
emit_opcode(opcode::OP_size);
}
void compiler::emit_expr_variable_ref(const ast::expr& expr, const block::ptr& blk, bool set)
{
switch (expr.kind())
{
case ast::kind::expr_array:
emit_expr_array_ref(expr.as_array, blk, set);
break;
case ast::kind::expr_field:
emit_expr_field_ref(expr.as_field, blk, set);
break;
case ast::kind::expr_identifier:
emit_expr_local_ref(expr.as_identifier, blk, set);
break;
default:
throw comp_error(expr.loc(), "invalid lvalue");
}
}
void compiler::emit_expr_array_ref(const ast::expr_array::ptr& expr, const block::ptr& blk, bool set)
{
emit_expr(expr->key, blk);
switch (expr->obj.kind())
{
case ast::kind::expr_game:
emit_opcode(opcode::OP_GetGameRef);
emit_opcode(opcode::OP_EvalArrayRef);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case ast::kind::expr_array:
case ast::kind::expr_field:
emit_expr_variable_ref(expr->obj, blk, false);
emit_opcode(opcode::OP_EvalArrayRef);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case ast::kind::expr_identifier:
{
if (!variable_initialized(expr->obj.as_identifier, blk))
{
initialize_variable(expr->obj.as_identifier, blk);
emit_opcode(opcode::OP_EvalNewLocalArrayRefCached0, variable_create_index(expr->obj.as_identifier, blk));
if (!set) throw comp_error(expr->loc(), "INTERNAL: VAR CREATED BUT NOT SET!");
}
else if (variable_stack_index(expr->obj.as_identifier, blk) == 0)
{
emit_opcode(opcode::OP_EvalLocalArrayRefCached0);
}
else
{
emit_opcode(opcode::OP_EvalLocalArrayRefCached, variable_access_index(expr->obj.as_identifier, blk));
}
if (set) emit_opcode(opcode::OP_SetVariableField);
}
break;
case ast::kind::expr_call:
case ast::kind::expr_method:
default:
throw comp_error(expr->loc(), "invalid array lvalue");
}
}
void compiler::emit_expr_field_ref(const ast::expr_field::ptr& expr, const block::ptr& blk, bool set)
{
const auto& field = expr->field->value;
switch (expr->obj.kind())
{
case ast::kind::expr_level:
set ? emit_opcode(opcode::OP_SetLevelFieldVariableField, field) : emit_opcode(opcode::OP_EvalLevelFieldVariableRef, field);
break;
case ast::kind::expr_anim:
set ? emit_opcode(opcode::OP_SetAnimFieldVariableField, field) : emit_opcode(opcode::OP_EvalAnimFieldVariableRef, field);
break;
case ast::kind::expr_self:
set ? emit_opcode(opcode::OP_SetSelfFieldVariableField, field) : emit_opcode(opcode::OP_EvalSelfFieldVariableRef, field);
break;
case ast::kind::expr_array:
emit_expr_array(expr->obj.as_array, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case ast::kind::expr_field:
emit_expr_field(expr->obj.as_field, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case ast::kind::expr_identifier:
emit_opcode(opcode::OP_EvalLocalVariableObjectCached, variable_access_index(expr->obj.as_identifier, blk));
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case ast::kind::expr_call:
emit_expr_call(expr->obj.as_call, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
case ast::kind::expr_method:
emit_expr_method(expr->obj.as_method, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariableRef, field);
if (set) emit_opcode(opcode::OP_SetVariableField);
break;
default:
throw comp_error(expr->obj.loc(), "not an object");
}
}
void compiler::emit_expr_local_ref(const ast::expr_identifier::ptr& expr, const block::ptr& blk, bool set)
{
const auto itr = constants_.find(expr->value);
if (itr != constants_.end())
{
throw comp_error(expr->loc(), "variable name already defined as constant '" + expr->value + "'");
}
if (set)
{
if (!variable_initialized(expr, blk))
{
initialize_variable(expr, blk);
emit_opcode(opcode::OP_SetNewLocalVariableFieldCached0, variable_create_index(expr, blk));
}
else if (variable_stack_index(expr, blk) == 0)
{
emit_opcode(opcode::OP_SetLocalVariableFieldCached0);
}
else
{
emit_opcode(opcode::OP_SetLocalVariableFieldCached, variable_access_index(expr, blk));
}
}
else
{
auto index = variable_stack_index(expr, blk);
if (index == 0)
emit_opcode(opcode::OP_EvalLocalVariableRefCached0);
else
emit_opcode(opcode::OP_EvalLocalVariableRefCached, variable_access_index(expr, blk));
}
}
void compiler::emit_expr_variable(const ast::expr& expr, const block::ptr& blk)
{
switch (expr.kind())
{
case ast::kind::expr_array:
emit_expr_array(expr.as_array, blk);
break;
case ast::kind::expr_field:
emit_expr_field(expr.as_field, blk);
break;
case ast::kind::expr_identifier:
emit_expr_local(expr.as_identifier, blk);
break;
default:
throw comp_error(expr.loc(), "invalid variable type.");
}
}
void compiler::emit_expr_array(const ast::expr_array::ptr& expr, const block::ptr& blk)
{
emit_expr(expr->key, blk);
if (expr->obj == ast::kind::expr_identifier)
{
emit_opcode(opcode::OP_EvalLocalArrayCached, variable_access_index(expr->obj.as_identifier, blk));
}
else
{
emit_expr(expr->obj, blk);
emit_opcode(opcode::OP_EvalArray);
}
}
void compiler::emit_expr_field(const ast::expr_field::ptr& expr, const block::ptr& blk)
{
const auto& field = expr->field->value;
switch (expr->obj.kind())
{
case ast::kind::expr_level:
emit_opcode(opcode::OP_EvalLevelFieldVariable, field);
break;
case ast::kind::expr_anim:
emit_opcode(opcode::OP_EvalAnimFieldVariable, field);
break;
case ast::kind::expr_self:
emit_opcode(opcode::OP_EvalSelfFieldVariable, field);
break;
case ast::kind::expr_array:
emit_expr_array(expr->obj.as_array, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
case ast::kind::expr_field:
emit_expr_field(expr->obj.as_field, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
case ast::kind::expr_call:
emit_expr_call(expr->obj.as_call, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
case ast::kind::expr_method:
emit_expr_method(expr->obj.as_method, blk);
emit_opcode(opcode::OP_CastFieldObject);
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
case ast::kind::expr_identifier:
emit_opcode(opcode::OP_EvalLocalVariableObjectCached, variable_access_index(expr->obj.as_identifier, blk));
emit_opcode(opcode::OP_EvalFieldVariable, field);
break;
default:
throw comp_error(expr->loc(), "unknown field variable object type");
}
}
void compiler::emit_expr_local(const ast::expr_identifier::ptr& expr, const block::ptr& blk)
{
// is constant ( should only allow: string, loc string, number, vector)
const auto itr = constants_.find(expr->value);
if (itr != constants_.end())
{
const auto& value = itr->second;
emit_expr(value, blk);
return;
}
// is local var
auto index = variable_stack_index(expr, blk);
switch (index)
{
case 0:
emit_opcode(opcode::OP_EvalLocalVariableCached0);
break;
case 1:
emit_opcode(opcode::OP_EvalLocalVariableCached1);
break;
case 2:
emit_opcode(opcode::OP_EvalLocalVariableCached2);
break;
case 3:
emit_opcode(opcode::OP_EvalLocalVariableCached3);
break;
case 4:
emit_opcode(opcode::OP_EvalLocalVariableCached4);
break;
case 5:
emit_opcode(opcode::OP_EvalLocalVariableCached5);
break;
default:
emit_opcode(opcode::OP_EvalLocalVariableCached, variable_access_index(expr, blk));
break;
}
}
void compiler::emit_expr_object(const ast::expr& expr, const block::ptr& blk)
{
switch (expr.kind())
{
case ast::kind::expr_level:
emit_opcode(opcode::OP_GetLevelObject);
break;
case ast::kind::expr_anim:
emit_opcode(opcode::OP_GetAnimObject);
break;
case ast::kind::expr_self:
emit_opcode(opcode::OP_GetSelfObject);
break;
case ast::kind::expr_array:
emit_expr_array(expr.as_array, blk);
emit_opcode(opcode::OP_CastFieldObject);
break;
case ast::kind::expr_field:
emit_expr_field(expr.as_field, blk);
emit_opcode(opcode::OP_CastFieldObject);
break;
case ast::kind::expr_call:
emit_expr_call(expr.as_call, blk);
emit_opcode(opcode::OP_CastFieldObject);
break;
case ast::kind::expr_method:
emit_expr_method(expr.as_method, blk);
emit_opcode(opcode::OP_CastFieldObject);
break;
case ast::kind::expr_identifier:
emit_opcode(opcode::OP_EvalLocalVariableObjectCached, variable_access_index(expr.as_identifier, blk));
break;
default:
throw comp_error(expr.loc(), "not an object");
}
}
void compiler::emit_expr_vector(const ast::expr_vector::ptr& expr, const block::ptr& blk)
{
std::vector<std::string> data;
bool isexpr = false;
if (expr->x == ast::kind::expr_integer)
data.push_back(expr->x.as_integer->value);
else if (expr->x == ast::kind::expr_float)
data.push_back(expr->x.as_float->value);
else isexpr = true;
if (expr->y == ast::kind::expr_integer)
data.push_back(expr->y.as_integer->value);
else if (expr->y == ast::kind::expr_float)
data.push_back(expr->y.as_float->value);
else isexpr = true;
if (expr->z == ast::kind::expr_integer)
data.push_back(expr->z.as_integer->value);
else if (expr->z == ast::kind::expr_float)
data.push_back(expr->z.as_float->value);
else isexpr = true;
if (!isexpr)
{
emit_opcode(opcode::OP_GetVector, data);
}
else
{
emit_expr(expr->z, blk);
emit_expr(expr->y, blk);
emit_expr(expr->x, blk);
emit_opcode(opcode::OP_vector);
}
}
void compiler::emit_expr_animation(const ast::expr_animation::ptr& expr)
{
if (animtrees_.size() == 0)
{
throw comp_error(expr->loc(), "trying to use animation without specified using animtree");
}
auto& tree = animtrees_.back();
if (tree.loaded)
{
emit_opcode(opcode::OP_GetAnimation, { "''", expr->value });
}
else
{
emit_opcode(opcode::OP_GetAnimation, { tree.name, expr->value });
tree.loaded = true;
}
}
void compiler::emit_expr_animtree(const ast::expr_animtree::ptr& expr)
{
if (animtrees_.size() == 0)
{
throw comp_error( expr->loc(), "trying to use animtree without specified using animtree");
}
auto& tree = animtrees_.back();
if (tree.loaded)
{
emit_opcode(opcode::OP_GetAnimTree, "''");
}
else
{
emit_opcode(opcode::OP_GetAnimTree, tree.name);
tree.loaded = true;
}
}
void compiler::emit_expr_istring(const ast::expr_istring::ptr& expr)
{
emit_opcode(opcode::OP_GetIString, expr->value);
}
void compiler::emit_expr_string(const ast::expr_string::ptr& expr)
{
emit_opcode(opcode::OP_GetString, expr->value);
}
void compiler::emit_expr_color(const ast::expr_color::ptr& expr)
{
std::vector<std::string> data;
std::string x, y, z;
if (expr->value.size() == 3)
{
x = "0x" + expr->value.substr(0, 1) + expr->value.substr(0, 1);
y = "0x" + expr->value.substr(1, 1) + expr->value.substr(1, 1);
z = "0x" + expr->value.substr(2, 1) + expr->value.substr(2, 1);
}
else
{
x = "0x" + expr->value.substr(0, 2);
y = "0x" + expr->value.substr(2, 2);
z = "0x" + expr->value.substr(4, 2);
}
data.push_back(utils::string::hex_to_dec(x.data()));
data.push_back(utils::string::hex_to_dec(y.data()));
data.push_back(utils::string::hex_to_dec(z.data()));
emit_opcode(opcode::OP_GetVector, data);
}
void compiler::emit_expr_float(const ast::expr_float::ptr& expr)
{
emit_opcode(opcode::OP_GetFloat, expr->value);
}
void compiler::emit_expr_integer(const ast::expr_integer::ptr& expr)
{
auto value = std::atoi(expr->value.data());
if (value == 0)
{
emit_opcode(opcode::OP_GetZero);
}
else if (value > 0 && value < 256)
{
emit_opcode(opcode::OP_GetByte, expr->value);
}
else if (value < 0 && value > -256)
{
emit_opcode(opcode::OP_GetNegByte, expr->value.substr(1));
}
else if (value > 0 && value < 65536)
{
emit_opcode(opcode::OP_GetUnsignedShort, expr->value);
}
else if (value < 0 && value > -65536)
{
emit_opcode(opcode::OP_GetNegUnsignedShort, expr->value.substr(1));
}
else
{
emit_opcode(opcode::OP_GetInteger, expr->value);
}
}
void compiler::emit_expr_false(const ast::expr_false::ptr&)
{
emit_opcode(opcode::OP_GetZero);
}
void compiler::emit_expr_true(const ast::expr_true::ptr&)
{
emit_opcode(opcode::OP_GetByte, "1");
}
void compiler::emit_create_local_vars(const block::ptr& blk)
{
if (blk->local_vars_create_count != blk->local_vars_public_count)
{
for (auto i = blk->local_vars_create_count; i < blk->local_vars_public_count; i++)
{
auto data = utils::string::va("%d", blk->local_vars.at(i).create);
emit_opcode(opcode::OP_CreateLocalVariable, data);
blk->local_vars.at(i).init = true;
}
blk->local_vars_create_count = blk->local_vars_public_count;
}
}
void compiler::emit_remove_local_vars(const block::ptr& blk)
{
if (blk->abort == abort_t::abort_none)
{
auto count = blk->local_vars_create_count - blk->local_vars_public_count;
if (count > 0)
{
auto data = utils::string::va("%d", count);
emit_opcode(opcode::OP_RemoveLocalVariables, data);
}
}
}
void compiler::emit_opcode(opcode op)
{
function_->instructions.push_back(std::make_unique<instruction>());
auto& inst = function_->instructions.back();
inst->opcode = static_cast<std::uint8_t>(op);
inst->size = opcode_size(std::uint8_t(op));
inst->index = index_;
index_ += inst->size;
}
void compiler::emit_opcode(opcode op, const std::string& data)
{
function_->instructions.push_back(std::make_unique<instruction>());
auto& inst = function_->instructions.back();
inst->opcode = static_cast<std::uint8_t>(op);
inst->size = opcode_size(std::uint8_t(op));
inst->index = index_;
inst->data.push_back(data);
index_ += inst->size;
}
void compiler::emit_opcode(opcode op, const std::vector<std::string>& data)
{
function_->instructions.push_back(std::make_unique<instruction>());
auto& inst = function_->instructions.back();
inst->opcode = static_cast<std::uint8_t>(op);
inst->size = opcode_size(std::uint8_t(op));
inst->index = index_;
inst->data = data;
index_ += inst->size;
}
void compiler::process_thread(const ast::decl_thread::ptr& decl, const block::ptr& blk)
{
process_expr_parameters(decl->params, blk);
process_stmt_list(decl->stmt, blk);
}
void compiler::process_stmt(const ast::stmt& stmt, const block::ptr& blk)
{
switch (stmt.kind())
{
case ast::kind::stmt_list:
process_stmt_list(stmt.as_list, blk);
break;
case ast::kind::stmt_expr:
process_stmt_expr(stmt.as_expr, blk);
break;
case ast::kind::stmt_assign:
process_stmt_assign(stmt.as_assign, blk);
break;
case ast::kind::stmt_waittill:
process_stmt_waittill(stmt.as_waittill, blk);
break;
case ast::kind::stmt_if:
process_stmt_if(stmt.as_if, blk);
break;
case ast::kind::stmt_ifelse:
process_stmt_ifelse(stmt.as_ifelse, blk);
break;
case ast::kind::stmt_while:
process_stmt_while(stmt.as_while, blk);
break;
case ast::kind::stmt_dowhile:
process_stmt_dowhile(stmt.as_dowhile, blk);
break;
case ast::kind::stmt_for:
process_stmt_for(stmt.as_for, blk);
break;
case ast::kind::stmt_foreach:
process_stmt_foreach(stmt.as_foreach, blk);
break;
case ast::kind::stmt_switch:
process_stmt_switch(stmt.as_switch, blk);
break;
case ast::kind::stmt_break:
process_stmt_break(stmt.as_break, blk);
break;
case ast::kind::stmt_continue:
process_stmt_continue(stmt.as_continue, blk);
break;
case ast::kind::stmt_return:
process_stmt_return(stmt.as_return, blk);
break;
case ast::kind::stmt_call:
case ast::kind::stmt_endon:
case ast::kind::stmt_notify:
case ast::kind::stmt_wait:
case ast::kind::stmt_waittillmatch:
case ast::kind::stmt_waittillframeend:
case ast::kind::stmt_waitframe:
case ast::kind::stmt_case:
case ast::kind::stmt_default:
case ast::kind::stmt_breakpoint:
case ast::kind::stmt_prof_begin:
case ast::kind::stmt_prof_end:
break;
default:
throw comp_error(stmt.loc(), "unknown statement");
}
}
void compiler::process_stmt_list(const ast::stmt_list::ptr& stmt, const block::ptr& blk)
{
for (const auto& entry : stmt->list)
{
process_stmt(entry, blk);
}
}
void compiler::process_stmt_expr(const ast::stmt_expr::ptr& stmt, const block::ptr& blk)
{
switch (stmt->expr.kind())
{
case ast::kind::expr_increment:
process_expr(stmt->expr.as_increment->lvalue, blk);
break;
case ast::kind::expr_decrement:
process_expr(stmt->expr.as_decrement->lvalue, blk);
break;
case ast::kind::expr_assign_equal:
case ast::kind::expr_assign_add:
case ast::kind::expr_assign_sub:
case ast::kind::expr_assign_mul:
case ast::kind::expr_assign_div:
case ast::kind::expr_assign_mod:
case ast::kind::expr_assign_shift_left:
case ast::kind::expr_assign_shift_right:
case ast::kind::expr_assign_bitwise_or:
case ast::kind::expr_assign_bitwise_and:
case ast::kind::expr_assign_bitwise_exor:
process_expr(stmt->expr.as_assign->lvalue, blk);
break;
case ast::kind::null:
break;
default:
throw comp_error(stmt->loc(), "unknown expr statement expression");
}
}
void compiler::process_stmt_assign(const ast::stmt_assign::ptr& stmt, const block::ptr& blk)
{
switch (stmt->expr.kind())
{
case ast::kind::expr_increment:
process_expr(stmt->expr.as_increment->lvalue, blk);
break;
case ast::kind::expr_decrement:
process_expr(stmt->expr.as_decrement->lvalue, blk);
break;
case ast::kind::expr_assign_equal:
case ast::kind::expr_assign_add:
case ast::kind::expr_assign_sub:
case ast::kind::expr_assign_mul:
case ast::kind::expr_assign_div:
case ast::kind::expr_assign_mod:
case ast::kind::expr_assign_shift_left:
case ast::kind::expr_assign_shift_right:
case ast::kind::expr_assign_bitwise_or:
case ast::kind::expr_assign_bitwise_and:
case ast::kind::expr_assign_bitwise_exor:
process_expr(stmt->expr.as_assign->lvalue, blk);
break;
default:
throw comp_error(stmt->loc(), "unknown assign statement expression");
}
}
void compiler::process_stmt_waittill(const ast::stmt_waittill::ptr& stmt, const block::ptr& blk)
{
for (const auto& entry : stmt->args->list)
{
if (entry != ast::kind::expr_identifier)
{
throw comp_error(entry.loc(), "illegal waittill param, must be a local variable");
}
register_variable(entry.as_identifier->value, blk);
}
}
void compiler::process_stmt_if(const ast::stmt_if::ptr& stmt, const block::ptr& blk)
{
stmt->blk = std::make_unique<block>();
blk->copy(stmt->blk);
process_stmt(stmt->stmt, stmt->blk);
std::vector<block*> childs({ stmt->blk.get() });
blk->merge(childs);
}
void compiler::process_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt, const block::ptr& blk)
{
std::vector<block*> childs;
auto abort = abort_t::abort_return;
stmt->blk_if = std::make_unique<block>();
stmt->blk_else = std::make_unique<block>();
blk->copy(stmt->blk_if);
process_stmt(stmt->stmt_if, stmt->blk_if);
if (stmt->blk_if->abort <= abort_t::abort_return)
{
abort = stmt->blk_if->abort;
if (abort == abort_t::abort_none)
childs.push_back(stmt->blk_if.get());
}
blk->copy(stmt->blk_else);
process_stmt(stmt->stmt_else, stmt->blk_else);
if (stmt->blk_else->abort <= abort)
{
abort = stmt->blk_else->abort;
if (abort == abort_t::abort_none)
childs.push_back(stmt->blk_else.get());
}
if (blk->abort == abort_t::abort_none)
blk->abort = abort;
blk->append(childs);
blk->merge(childs);
}
void compiler::process_stmt_while(const ast::stmt_while::ptr& stmt, const block::ptr& blk)
{
bool const_cond = is_constant_condition(stmt->test);
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
break_blks_.clear();
continue_blks_.clear();
stmt->blk = std::make_unique<block>();
blk->copy(stmt->blk);
process_stmt(stmt->stmt, stmt->blk);
continue_blks_.push_back(stmt->blk.get());
for (auto i = 0; i < continue_blks_.size(); i++)
blk->append({continue_blks_.at(i)});
if (const_cond) blk->append(break_blks_);
blk->merge({stmt->blk.get()});
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::process_stmt_dowhile(const ast::stmt_dowhile::ptr& stmt, const block::ptr& blk)
{
bool const_cond = is_constant_condition(stmt->test);
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
break_blks_.clear();
continue_blks_.clear();
stmt->blk = std::make_unique<block>();
blk->copy(stmt->blk);
process_stmt(stmt->stmt, stmt->blk);
continue_blks_.push_back(stmt->blk.get());
for (auto i = 0; i < continue_blks_.size(); i++)
blk->append({continue_blks_.at(i)});
if (const_cond) blk->append(break_blks_);
blk->merge({stmt->blk.get()});
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::process_stmt_for(const ast::stmt_for::ptr& stmt, const block::ptr& blk)
{
bool const_cond = is_constant_condition(stmt->test);
stmt->blk = std::make_unique<block>();
stmt->blk_iter = std::make_unique<block>();
process_stmt(stmt->init, blk);
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
break_blks_.clear();
continue_blks_.clear();
blk->copy(stmt->blk);
blk->copy(stmt->blk_iter);
process_stmt(stmt->stmt, stmt->blk);
continue_blks_.push_back(stmt->blk.get());
for (auto i = 0; i < continue_blks_.size(); i++)
blk->append({continue_blks_.at(i)});
process_stmt(stmt->iter, stmt->blk_iter);
blk->append({ stmt->blk_iter.get() });
blk->merge({ stmt->blk_iter.get() });
if (const_cond) blk->append(break_blks_);
blk->merge({stmt->blk.get()});
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::process_stmt_foreach(const ast::stmt_foreach::ptr& stmt, const block::ptr& blk)
{
auto array_name = utils::string::va("temp_%d", ++label_idx_);
auto key_name = utils::string::va("temp_%d", ++label_idx_);
stmt->array = ast::expr(std::make_unique<ast::expr_identifier>(stmt->loc(), array_name));
if (!stmt->use_key)
stmt->key_expr = ast::expr(std::make_unique<ast::expr_identifier>(stmt->loc(), key_name));
key_name = stmt->key_expr.as_identifier->value;
// calculate variables
stmt->ctx = std::make_unique<block>();
stmt->ctx_post = std::make_unique<block>();
// calculate pre_expr variables
process_expr(stmt->array, blk);
auto old_breaks = break_blks_;
auto old_continues = continue_blks_;
break_blks_.clear();
continue_blks_.clear();
blk->copy(stmt->ctx);
blk->copy(stmt->ctx_post);
// calculate stmt variables & add missing array access as first stmt
process_expr(stmt->value_expr, stmt->ctx);
process_stmt(stmt->stmt, stmt->ctx);
continue_blks_.push_back(stmt->ctx.get());
for (auto i = 0; i < continue_blks_.size(); i++)
blk->append({continue_blks_.at(i)});
process_expr(stmt->key_expr, stmt->ctx_post);
blk->append({ stmt->ctx_post.get() });
blk->merge({ stmt->ctx_post.get() });
blk->merge({stmt->ctx.get()});
break_blks_ = old_breaks;
continue_blks_ = old_continues;
}
void compiler::process_stmt_switch(const ast::stmt_switch::ptr& stmt, const block::ptr& blk)
{
auto stmt_list = std::make_unique<ast::stmt_list>(stmt->stmt->loc());
auto current_case = ast::stmt(nullptr);
auto num = stmt->stmt->list.size();
for (auto i = 0; i < num; i++)
{
auto& entry = stmt->stmt->list[0];
if (entry == ast::kind::stmt_case || entry == ast::kind::stmt_default)
{
if (current_case.as_node != nullptr)
{
stmt_list->list.push_back(std::move(current_case));
}
current_case = std::move(stmt->stmt->list[0]);
stmt->stmt->list.erase(stmt->stmt->list.begin());
}
else
{
if (current_case.as_node != nullptr)
{
if (current_case == ast::kind::stmt_case)
{
current_case.as_case->stmt->list.push_back(std::move(entry));
stmt->stmt->list.erase(stmt->stmt->list.begin());
}
else
{
current_case.as_default->stmt->list.push_back(std::move(entry));
stmt->stmt->list.erase(stmt->stmt->list.begin());
}
}
else
{
throw comp_error(entry.loc(), "missing case statement");
}
}
}
if (current_case.as_node != nullptr)
{
stmt_list->list.push_back(std::move(current_case));
}
// calculate variables
stmt->ctx = std::make_unique<block>();
std::vector<block*> childs;
auto abort = abort_t::abort_return;
bool has_default = false;
block* default_ctx = nullptr;
auto old_breaks = break_blks_;
break_blks_.clear();
for (auto i = 0; i < stmt_list->list.size(); i++)
{
auto& entry = stmt_list->list[i];
if (entry == ast::kind::stmt_case)
{
entry.as_case->blk = std::make_unique<block>();
blk->copy(entry.as_case->blk);
process_stmt_list(entry.as_case->stmt, entry.as_case->blk);
if (entry.as_case->blk->abort != abort_t::abort_none)
{
if (entry.as_case->blk->abort == abort_t::abort_break )
{
entry.as_case->blk->abort = abort_t::abort_none;
abort = abort_t::abort_none;
childs.push_back(entry.as_case->blk.get());
}
else if (entry.as_case->blk->abort <= abort )
{
abort = entry.as_case->blk->abort;
}
}
}
else if (entry == ast::kind::stmt_default)
{
entry.as_default->blk = std::make_unique<block>();
blk->copy(entry.as_default->blk);
process_stmt_list(entry.as_default->stmt, entry.as_default->blk);
has_default = true;
default_ctx = entry.as_default->blk.get();
if (entry.as_default->blk->abort != abort_t::abort_none)
{
if (entry.as_default->blk->abort == abort_t::abort_break )
{
entry.as_default->blk->abort = abort_t::abort_none;
abort = abort_t::abort_none;
childs.push_back(entry.as_default->blk.get());
}
else if (entry.as_default->blk->abort <= abort )
{
abort = entry.as_default->blk->abort;
}
}
}
}
stmt->stmt =std::move(stmt_list);
if (has_default)
{
if (default_ctx->abort == abort_t::abort_none)
{
break_blks_.push_back(default_ctx);
if (blk->abort == abort_t::abort_none)
blk->abort = abort;
}
blk->append(break_blks_);
blk->merge(childs);
}
break_blks_ = old_breaks;
}
void compiler::process_stmt_break(const ast::stmt_break::ptr& stmt, const block::ptr& blk)
{
if (blk->abort == abort_t::abort_none)
{
break_blks_.push_back(blk.get());
blk->abort = abort_t::abort_break;
}
}
void compiler::process_stmt_continue(const ast::stmt_continue::ptr& stmt, const block::ptr& blk)
{
if (blk->abort == abort_t::abort_none)
{
continue_blks_.push_back(blk.get());
blk->abort = abort_t::abort_continue;
}
}
void compiler::process_stmt_return(const ast::stmt_return::ptr& stmt, const block::ptr& blk)
{
if (blk->abort == abort_t::abort_none)
{
blk->abort = abort_t::abort_return;
}
}
void compiler::process_expr(const ast::expr& expr, const block::ptr& blk)
{
if (expr == ast::kind::expr_identifier)
{
register_variable(expr.as_identifier->value, blk);
}
else if (expr == ast::kind::expr_array)
{
process_expr(expr.as_array->obj, blk);
}
}
void compiler::process_expr_parameters(const ast::expr_parameters::ptr& decl, const block::ptr& blk)
{
for (const auto& entry : decl->list)
{
register_variable(entry->value, blk);
}
}
void compiler::register_variable(const std::string& name, const block::ptr& blk)
{
auto it = std::find_if(blk->local_vars.begin(), blk->local_vars.end(),
[&](const gsc::local_var& v) { return v.name == name; });
if (it == blk->local_vars.end())
{
auto found = false;
for (std::size_t i = 0; i < local_stack_.size(); i++)
{
if (local_stack_[i] == name)
{
blk->local_vars.push_back({ name, static_cast<uint8_t>(i), false });
found = true;
break;
}
}
if (!found)
{
blk->local_vars.push_back({ name, stack_idx_, false });
local_stack_.push_back(name);
stack_idx_++;
}
}
}
void compiler::initialize_variable(const ast::expr_identifier::ptr& name, const block::ptr& blk)
{
for (std::uint32_t i = 0; i < blk->local_vars.size(); i++)
{
if (blk->local_vars[i].name == name->value)
{
if (!blk->local_vars[i].init)
{
for (std::uint32_t j = 0; j < i; j++)
{
if (!blk->local_vars[j].init)
{
emit_opcode(opcode::OP_CreateLocalVariable, utils::string::va("%d", blk->local_vars[j].create));
blk->local_vars[j].init = true;
//ctx->local_vars_create_count++;
}
}
blk->local_vars[i].init = true;
blk->local_vars_create_count = i + 1;
return;
}
}
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not found.");
}
void compiler::create_variable(const ast::expr_identifier::ptr& name, const block::ptr& blk)
{
for (std::size_t i = 0; i < blk->local_vars.size(); i++)
{
auto& var = blk->local_vars.at(i);
if (var.name == name->value)
{
if (!var.init)
{
emit_opcode(opcode::OP_CreateLocalVariable, utils::string::va("%d", var.create));
var.init = true;
blk->local_vars_create_count++;
}
return;
}
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not found.");
}
auto compiler::variable_stack_index(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> std::uint8_t
{
for (std::size_t i = 0; i < blk->local_vars.size(); i++)
{
if (blk->local_vars[i].name == name->value)
{
if (blk->local_vars.at(i).init)
{
return blk->local_vars_create_count - 1 - i;
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not initialized.");
}
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not found.");
}
auto compiler::variable_create_index(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> std::string
{
for (std::size_t i = 0; i < blk->local_vars.size(); i++)
{
if (blk->local_vars[i].name == name->value)
return utils::string::va("%d", blk->local_vars[i].create);
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not found.");
}
auto compiler::variable_access_index(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> std::string
{
for (std::size_t i = 0; i < blk->local_vars.size(); i++)
{
if (blk->local_vars[i].name == name->value)
{
if (blk->local_vars.at(i).init)
{
return utils::string::va("%d", blk->local_vars_create_count - 1 - i);
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not initialized.");
}
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not found.");
}
auto compiler::variable_initialized(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> bool
{
for (std::size_t i = 0; i < blk->local_vars.size(); i++)
{
if (blk->local_vars[i].name == name->value)
{
return blk->local_vars.at(i).init;
}
}
throw comp_error(name->loc(), "local variable '" + name->value + "' not found.");
}
auto compiler::resolve_function_type(const ast::expr_function::ptr& expr) -> ast::call::type
{
if (expr->path->value != "")
return ast::call::type::far;
auto& name = expr->name->value;
if (resolver::find_function(name) || resolver::find_method(name))
return ast::call::type::builtin;
for (const auto& entry : local_functions_)
{
if (entry == name)
return ast::call::type::local;
}
for (const auto& inc : includes_)
{
for (const auto& fun : inc.funcs)
{
if (name == fun)
{
expr->path->value = inc.name;
return ast::call::type::far;
}
}
}
throw comp_error(expr->loc(), "couldn't determine function type");
}
auto compiler::resolve_reference_type(const ast::expr_reference::ptr& expr, bool& method) -> ast::call::type
{
if (expr->path->value != "")
return ast::call::type::far;
auto& name = expr->name->value;
if (resolver::find_function(name))
{
method = false;
return ast::call::type::builtin;
}
if (resolver::find_method(name))
{
method = true;
return ast::call::type::builtin;
}
for (const auto& entry : local_functions_)
{
if (entry == name)
return ast::call::type::local;
}
for (const auto& inc : includes_)
{
for (const auto& fun : inc.funcs)
{
if (name == fun)
{
expr->path->value = inc.name;
return ast::call::type::far;
}
}
}
throw comp_error(expr->loc(), "couldn't determine function reference type");
}
auto compiler::is_constant_condition(const ast::expr& expr) -> bool
{
switch (expr.kind())
{
case ast::kind::null:
case ast::kind::expr_true:
return true;
case ast::kind::expr_false:
throw comp_error(expr.loc(), "condition can't be always false!");
case ast::kind::expr_integer:
{
auto num = std::stoi(expr.as_integer->value);
if (num != 0)
return true;
else
throw comp_error(expr.loc(), "condition can't be always false!");
}
default:
break;
}
return false;
}
auto compiler::create_label() -> std::string
{
label_idx_++;
auto name = utils::string::va("loc_%d", label_idx_);
return name;
}
auto compiler::insert_label() -> std::string
{
const auto itr = function_->labels.find(index_);
if (itr != function_->labels.end())
{
return itr->second;
}
else
{
label_idx_++;
auto name = utils::string::va("loc_%d", label_idx_);
function_->labels.insert({index_, name});
return name;
}
}
void compiler::insert_label(const std::string& name)
{
const auto itr = function_->labels.find(index_);
if (itr != function_->labels.end())
{
for (auto& inst : function_->instructions)
{
switch (opcode(inst->opcode))
{
case opcode::OP_JumpOnFalse:
case opcode::OP_JumpOnTrue:
case opcode::OP_JumpOnFalseExpr:
case opcode::OP_JumpOnTrueExpr:
case opcode::OP_jump:
case opcode::OP_jumpback:
case opcode::OP_switch:
if(inst->data[0] == name)
inst->data[0] = itr->second;
break;
case opcode::OP_endswitch:
default:
break;
}
}
}
else
{
function_->labels.insert({index_, name});
}
}
auto compiler::map_known_includes(const std::string& include) -> bool
{
return false;
}
} // namespace xsk::gsc::h1