2905 lines
87 KiB
C++
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
|