// Copyright 2023 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 "stdinc.hpp"
#include "arc.hpp"
#include "utils/string.hpp"

namespace xsk::arc::ast
{

std::uint32_t node::indent_;

void node::reset_indentation()
{
    indent_ = 0;
}

auto node::indented(std::uint32_t indent) -> std::string
{
    static char buff[100];
    snprintf(buff, sizeof(buff), "%*s", indent, "");
    return std::string(buff);
}

auto node::is_special_stmt() -> bool
{
    switch (kind_)
    {
        case kind::stmt_waittill:
        case kind::stmt_waittillmatch:
        case kind::stmt_if:
        case kind::stmt_ifelse:
        case kind::stmt_while:
        case kind::stmt_dowhile:
        case kind::stmt_for:
        case kind::stmt_foreach:
        case kind::stmt_switch:
            return true;
        default:
            return false;
    }
}

auto node::is_special_stmt_dev() -> bool
{
    switch (kind_)
    {
        case kind::stmt_dev:
        case kind::stmt_if:
        case kind::stmt_ifelse:
        case kind::stmt_while:
        case kind::stmt_dowhile:
        case kind::stmt_for:
        case kind::stmt_foreach:
        case kind::stmt_switch:
            return true;
        default:
            return false;
    }
}

auto node::is_special_stmt_noif() -> bool
{
    switch (kind_)
    {
        case kind::stmt_waittill:
        case kind::stmt_waittillmatch:
        case kind::stmt_while:
        case kind::stmt_dowhile:
        case kind::stmt_for:
        case kind::stmt_foreach:
        case kind::stmt_switch:
            return true;
        default:
            return false;
    }
}

auto node::is_special_stmt_dev_noif() -> bool
{
    switch (kind_)
    {
        case kind::stmt_dev:
        case kind::stmt_while:
        case kind::stmt_dowhile:
        case kind::stmt_for:
        case kind::stmt_foreach:
        case kind::stmt_switch:
            return true;
        default:
            return false;
    }
}

auto node::is_binary() -> bool
{
    switch (kind_)
    {
        case kind::expr_or:
        case kind::expr_and:
        case kind::expr_bitwise_or:
        case kind::expr_bitwise_exor:
        case kind::expr_bitwise_and:
        case kind::expr_equality:
        case kind::expr_inequality:
        case kind::expr_less:
        case kind::expr_greater:
        case kind::expr_less_equal:
        case kind::expr_greater_equal:
        case kind::expr_shift_left:
        case kind::expr_shift_right:
        case kind::expr_add:
        case kind::expr_sub:
        case kind::expr_mul:
        case kind::expr_div:
        case kind::expr_mod:
            return true;
        default:
            return false;
    }
}

auto node::precedence() -> std::uint8_t
{
    switch (kind_)
    {
        case kind::expr_or:           return 1;
        case kind::expr_and:          return 2;
        case kind::expr_bitwise_or:   return 3;
        case kind::expr_bitwise_exor: return 4;
        case kind::expr_bitwise_and:  return 5;
        case kind::expr_equality:     return 6;
        case kind::expr_inequality:   return 6;
        case kind::expr_less:         return 7;
        case kind::expr_greater:      return 7;
        case kind::expr_less_equal:   return 7;
        case kind::expr_greater_equal:return 7;
        case kind::expr_shift_left:   return 8;
        case kind::expr_shift_right:  return 8;
        case kind::expr_add:          return 9;
        case kind::expr_sub:          return 9;
        case kind::expr_mul:          return 10;
        case kind::expr_div:          return 10;
        case kind::expr_mod:          return 10;
        default: return 0;
    }
}

expr_true::expr_true() : node(kind::expr_true) {}
expr_true::expr_true(const location& loc) : node(kind::expr_true, loc) {}

expr_false::expr_false() : node(kind::expr_false) {}
expr_false::expr_false(const location& loc) : node(kind::expr_false, loc) {}

expr_integer::expr_integer(const std::string& value) : node(kind::expr_integer), value(std::move(value)) {}
expr_integer::expr_integer(const location& loc, const std::string& value) : node(kind::expr_integer, loc), value(std::move(value)) {}

expr_float::expr_float(const std::string& value) : node(kind::expr_float), value(std::move(value)) {}
expr_float::expr_float(const location& loc, const std::string& value) : node(kind::expr_float, loc), value(std::move(value)) {}

expr_vector::expr_vector(expr x, expr y, expr z) : node(kind::expr_vector), x(std::move(x)), y(std::move(y)), z(std::move(z)) {}
expr_vector::expr_vector(const location& loc, expr x, expr y, expr z) : node(kind::expr_vector, loc), x(std::move(x)), y(std::move(y)), z(std::move(z)) {}

expr_hash::expr_hash(const std::string& value) : node(kind::expr_hash), value(value) {}
expr_hash::expr_hash(const location& loc, const std::string& value) : node(kind::expr_hash, loc), value(value) {}

expr_string::expr_string(const std::string& value) : node(kind::expr_string), value(value) {}
expr_string::expr_string(const location& loc, const std::string& value) : node(kind::expr_string, loc), value(value) {}

expr_istring::expr_istring(const std::string& value) : node(kind::expr_istring), value(std::move(value)) {}
expr_istring::expr_istring(const location& loc, const std::string& value) : node(kind::expr_istring, loc), value(std::move(value)) {}

expr_path::expr_path() : node(kind::expr_path) {}
expr_path::expr_path(const std::string& value) : node(kind::expr_path), value(value) {}
expr_path::expr_path(const location& loc) : node(kind::expr_path, loc) {}
expr_path::expr_path(const location& loc, const std::string& value) : node(kind::expr_path, loc), value(value) {}

expr_identifier::expr_identifier(const std::string& value) : node(kind::expr_identifier), value(value) {}
expr_identifier::expr_identifier(const location& loc, const std::string& value) : node(kind::expr_identifier, loc), value(value) {}

expr_animtree::expr_animtree() : node(kind::expr_animtree) {}
expr_animtree::expr_animtree(const location& loc) : node(kind::expr_animtree, loc) {}

expr_animation::expr_animation(const std::string& value) : node(kind::expr_animation), value(value) {}
expr_animation::expr_animation(const location& loc, const std::string& value) : node(kind::expr_animation, loc), value(value) {}

expr_level::expr_level() : node(kind::expr_level) {}
expr_level::expr_level(const location& loc) : node(kind::expr_level, loc) {}

expr_anim::expr_anim() : node(kind::expr_anim) {}
expr_anim::expr_anim(const location& loc) : node(kind::expr_anim, loc) {}

expr_self::expr_self() : node(kind::expr_self) {}
expr_self::expr_self(const location& loc) : node(kind::expr_self, loc) {}

expr_game::expr_game() : node(kind::expr_game) {}
expr_game::expr_game(const location& loc) : node(kind::expr_game, loc) {}

expr_undefined::expr_undefined() : node(kind::expr_undefined) {}
expr_undefined::expr_undefined(const location& loc) : node(kind::expr_undefined, loc) {}

expr_empty_array::expr_empty_array() : node(kind::expr_empty_array) {}
expr_empty_array::expr_empty_array(const location& loc) : node(kind::expr_empty_array, loc) {}

expr_paren::expr_paren(expr child) : node(kind::expr_paren), child(std::move(child)) {}
expr_paren::expr_paren(const location& loc, expr child) : node(kind::expr_paren, loc), child(std::move(child)) {}

expr_size::expr_size(expr obj) : node(kind::expr_size), obj(std::move(obj)) {}
expr_size::expr_size(const location& loc, expr obj) : node(kind::expr_size, loc), obj(std::move(obj)) {}

expr_field::expr_field(expr obj, expr_identifier::ptr field) : node(kind::expr_field), obj(std::move(obj)), field(std::move(field)) {}
expr_field::expr_field(const location& loc, expr obj, expr_identifier::ptr field) : node(kind::expr_field, loc), obj(std::move(obj)), field(std::move(field)) {}

expr_array::expr_array(expr obj, expr key) : node(kind::expr_array), obj(std::move(obj)), key(std::move(key)) {}
expr_array::expr_array(const location& loc, expr obj, expr key) : node(kind::expr_array, loc), obj(std::move(obj)), key(std::move(key)) {}

expr_reference::expr_reference(expr_path::ptr path, expr_identifier::ptr name) : node(kind::expr_reference), path(std::move(path)), name(std::move(name)) {}
expr_reference::expr_reference(const location& loc, expr_path::ptr path, expr_identifier::ptr name) : node(kind::expr_reference, loc), path(std::move(path)), name(std::move(name)) {}

expr_getnextarraykey::expr_getnextarraykey(expr arg1, expr arg2) : node(kind::expr_getnextarraykey), arg1(std::move(arg1)), arg2(std::move(arg2)) {}
expr_getnextarraykey::expr_getnextarraykey(const location& loc, expr arg1, expr arg2) : node(kind::expr_getnextarraykey, loc), arg1(std::move(arg1)), arg2(std::move(arg2)) {}

expr_getfirstarraykey::expr_getfirstarraykey(expr arg) : node(kind::expr_getfirstarraykey), arg(std::move(arg)) {}
expr_getfirstarraykey::expr_getfirstarraykey(const location& loc, expr arg) : node(kind::expr_getfirstarraykey, loc), arg(std::move(arg)) {}

expr_getdvarcoloralpha::expr_getdvarcoloralpha(expr arg) : node(kind::expr_getdvarcoloralpha), arg(std::move(arg)) {}
expr_getdvarcoloralpha::expr_getdvarcoloralpha(const location& loc, expr arg) : node(kind::expr_getdvarcoloralpha, loc), arg(std::move(arg)) {}

expr_getdvarcolorblue::expr_getdvarcolorblue(expr arg) : node(kind::expr_getdvarcolorblue), arg(std::move(arg)) {}
expr_getdvarcolorblue::expr_getdvarcolorblue(const location& loc, expr arg) : node(kind::expr_getdvarcolorblue, loc), arg(std::move(arg)) {}

expr_getdvarcolorgreen::expr_getdvarcolorgreen(expr arg) : node(kind::expr_getdvarcolorgreen), arg(std::move(arg)) {}
expr_getdvarcolorgreen::expr_getdvarcolorgreen(const location& loc, expr arg) : node(kind::expr_getdvarcolorgreen, loc), arg(std::move(arg)) {}

expr_getdvarcolorred::expr_getdvarcolorred(expr arg) : node(kind::expr_getdvarcolorred), arg(std::move(arg)) {}
expr_getdvarcolorred::expr_getdvarcolorred(const location& loc, expr arg) : node(kind::expr_getdvarcolorred, loc), arg(std::move(arg)) {}

expr_getdvarvector::expr_getdvarvector(expr arg) : node(kind::expr_getdvarvector), arg(std::move(arg)) {}
expr_getdvarvector::expr_getdvarvector(const location& loc, expr arg) : node(kind::expr_getdvarvector, loc), arg(std::move(arg)) {}

expr_getdvarfloat::expr_getdvarfloat(expr arg) : node(kind::expr_getdvarfloat), arg(std::move(arg)) {}
expr_getdvarfloat::expr_getdvarfloat(const location& loc, expr arg) : node(kind::expr_getdvarfloat, loc), arg(std::move(arg)) {}

expr_getdvarint::expr_getdvarint(expr arg) : node(kind::expr_getdvarint), arg(std::move(arg)) {}
expr_getdvarint::expr_getdvarint(const location& loc, expr arg) : node(kind::expr_getdvarint, loc), arg(std::move(arg)) {}

expr_getdvar::expr_getdvar(expr arg) : node(kind::expr_getdvar), arg(std::move(arg)) {}
expr_getdvar::expr_getdvar(const location& loc, expr arg) : node(kind::expr_getdvar, loc), arg(std::move(arg)) {}

expr_gettime::expr_gettime() : node(kind::expr_gettime) {}
expr_gettime::expr_gettime(const location& loc) : node(kind::expr_gettime, loc) {}

expr_abs::expr_abs(expr arg) : node(kind::expr_abs), arg(std::move(arg)) {}
expr_abs::expr_abs(const location& loc, expr arg) : node(kind::expr_abs, loc), arg(std::move(arg)) {}

expr_vectortoangles::expr_vectortoangles(expr arg) : node(kind::expr_vectortoangles), arg(std::move(arg)) {}
expr_vectortoangles::expr_vectortoangles(const location& loc, expr arg) : node(kind::expr_vectortoangles, loc), arg(std::move(arg)) {}

expr_angleclamp180::expr_angleclamp180(expr arg) : node(kind::expr_angleclamp180), arg(std::move(arg)) {}
expr_angleclamp180::expr_angleclamp180(const location& loc, expr arg) : node(kind::expr_angleclamp180, loc), arg(std::move(arg)) {}

expr_anglestoforward::expr_anglestoforward(expr arg) : node(kind::expr_anglestoforward), arg(std::move(arg)) {}
expr_anglestoforward::expr_anglestoforward(const location& loc, expr arg) : node(kind::expr_anglestoforward, loc), arg(std::move(arg)) {}

expr_anglestoright::expr_anglestoright(expr arg) : node(kind::expr_anglestoright), arg(std::move(arg)) {}
expr_anglestoright::expr_anglestoright(const location& loc, expr arg) : node(kind::expr_anglestoright, loc), arg(std::move(arg)) {}

expr_anglestoup::expr_anglestoup(expr arg) : node(kind::expr_anglestoup), arg(std::move(arg)) {}
expr_anglestoup::expr_anglestoup(const location& loc, expr arg) : node(kind::expr_anglestoup, loc), arg(std::move(arg)) {}

expr_vectorscale::expr_vectorscale(expr arg1, expr arg2) : node(kind::expr_vectorscale), arg1(std::move(arg1)), arg2(std::move(arg2)) {}
expr_vectorscale::expr_vectorscale(const location& loc, expr arg1, expr arg2) : node(kind::expr_vectorscale, loc), arg1(std::move(arg1)), arg2(std::move(arg2)) {}

expr_isdefined::expr_isdefined(expr arg) : node(kind::expr_isdefined), arg(std::move(arg)) {}
expr_isdefined::expr_isdefined(const location& loc, expr arg) : node(kind::expr_isdefined, loc), arg(std::move(arg)) {}

expr_arguments::expr_arguments() : node(kind::expr_arguments) {}
expr_arguments::expr_arguments(const location& loc) : node(kind::expr_arguments, loc) {}

expr_parameters::expr_parameters() : node(kind::expr_parameters) {}
expr_parameters::expr_parameters(const location& loc) : node(kind::expr_parameters, loc) {}

expr_pointer::expr_pointer(expr func, expr_arguments::ptr args, call::mode mode) : node(kind::expr_pointer), func(std::move(func)), args(std::move(args)), mode(mode) {}
expr_pointer::expr_pointer(const location& loc, expr func, expr_arguments::ptr args, call::mode mode) : node(kind::expr_pointer, loc), func(std::move(func)), args(std::move(args)), mode(mode) {}

expr_function::expr_function(expr_path::ptr path, expr_identifier::ptr name, expr_arguments::ptr args, call::mode mode) : node(kind::expr_function), path(std::move(path)), name(std::move(name)), args(std::move(args)), mode(mode) {}
expr_function::expr_function(const location& loc, expr_path::ptr path, expr_identifier::ptr name, expr_arguments::ptr args, call::mode mode) : node(kind::expr_function, loc), path(std::move(path)), name(std::move(name)), args(std::move(args)), mode(mode) {}

expr_method::expr_method(expr obj, ast::call call) : node(kind::expr_method), obj(std::move(obj)), call(std::move(call)) {}
expr_method::expr_method(const location& loc, expr obj, ast::call call) : node(kind::expr_method, loc), obj(std::move(obj)), call(std::move(call)) {}

expr_call::expr_call(ast::call call) : node(kind::expr_call), call(std::move(call)) {}
expr_call::expr_call(const location& loc, ast::call call) : node(kind::expr_call, loc), call(std::move(call)) {}

expr_complement::expr_complement(expr rvalue) : node(kind::expr_complement), rvalue(std::move(rvalue)) {}
expr_complement::expr_complement(const location& loc, expr rvalue) : node(kind::expr_complement, loc), rvalue(std::move(rvalue)) {}

expr_negate::expr_negate(expr rvalue) : node(kind::expr_negate), rvalue(std::move(rvalue)) {}
expr_negate::expr_negate(const location& loc, expr rvalue) : node(kind::expr_negate, loc), rvalue(std::move(rvalue)) {}

expr_not::expr_not(expr rvalue) : node(kind::expr_not), rvalue(std::move(rvalue)) {}
expr_not::expr_not(const location& loc, expr rvalue) : node(kind::expr_not, loc), rvalue(std::move(rvalue)) {}

expr_binary::expr_binary(ast::kind kind, expr lvalue, expr rvalue) : node(kind), lvalue(std::move(lvalue)), rvalue(std::move(rvalue)) {}
expr_binary::expr_binary(ast::kind kind, const location& loc, expr lvalue, expr rvalue) : node(kind, loc), lvalue(std::move(lvalue)), rvalue(std::move(rvalue)) {}

expr_add::expr_add(expr lvalue, expr rvalue) : expr_binary(kind::expr_add, std::move(lvalue), std::move(rvalue)) {}
expr_add::expr_add(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_add, loc, std::move(lvalue), std::move(rvalue)) {}

expr_sub::expr_sub(expr lvalue, expr rvalue) : expr_binary(kind::expr_sub, std::move(lvalue), std::move(rvalue)) {}
expr_sub::expr_sub(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_sub, loc, std::move(lvalue), std::move(rvalue)) {}

expr_mul::expr_mul( expr lvalue, expr rvalue) : expr_binary(kind::expr_mul, std::move(lvalue), std::move(rvalue)) {}
expr_mul::expr_mul(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_mul, loc, std::move(lvalue), std::move(rvalue)) {}

expr_div::expr_div(expr lvalue, expr rvalue) : expr_binary(kind::expr_div, std::move(lvalue), std::move(rvalue)) {}
expr_div::expr_div(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_div, loc, std::move(lvalue), std::move(rvalue)) {}

expr_mod::expr_mod(expr lvalue, expr rvalue) : expr_binary(kind::expr_mod, std::move(lvalue), std::move(rvalue)) {}
expr_mod::expr_mod(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_mod, loc, std::move(lvalue), std::move(rvalue)) {}

expr_shift_left::expr_shift_left(expr lvalue, expr rvalue) : expr_binary(kind::expr_shift_left, std::move(lvalue), std::move(rvalue)) {}
expr_shift_left::expr_shift_left(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_shift_left, loc, std::move(lvalue), std::move(rvalue)) {}

expr_shift_right::expr_shift_right(expr lvalue, expr rvalue) : expr_binary(kind::expr_shift_right, std::move(lvalue), std::move(rvalue)) {}
expr_shift_right::expr_shift_right(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_shift_right, loc, std::move(lvalue), std::move(rvalue)) {}

expr_bitwise_or::expr_bitwise_or(expr lvalue, expr rvalue) : expr_binary(kind::expr_bitwise_or, std::move(lvalue), std::move(rvalue)) {}
expr_bitwise_or::expr_bitwise_or(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_bitwise_or, loc, std::move(lvalue), std::move(rvalue)) {}

expr_bitwise_and::expr_bitwise_and(expr lvalue, expr rvalue) : expr_binary(kind::expr_bitwise_and, std::move(lvalue), std::move(rvalue)) {}
expr_bitwise_and::expr_bitwise_and(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_bitwise_and, loc, std::move(lvalue), std::move(rvalue)) {}

expr_bitwise_exor::expr_bitwise_exor(expr lvalue, expr rvalue) : expr_binary(kind::expr_bitwise_exor, std::move(lvalue), std::move(rvalue)) {}
expr_bitwise_exor::expr_bitwise_exor(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_bitwise_exor, loc, std::move(lvalue), std::move(rvalue)) {}

expr_equality::expr_equality(expr lvalue, expr rvalue) : expr_binary(kind::expr_equality, std::move(lvalue), std::move(rvalue)) {}
expr_equality::expr_equality(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_equality, loc, std::move(lvalue), std::move(rvalue)) {}

expr_inequality::expr_inequality(expr lvalue, expr rvalue) : expr_binary(kind::expr_inequality, std::move(lvalue), std::move(rvalue)) {}
expr_inequality::expr_inequality(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_inequality, loc, std::move(lvalue), std::move(rvalue)) {}

expr_less_equal::expr_less_equal(expr lvalue, expr rvalue) : expr_binary(kind::expr_less_equal, std::move(lvalue), std::move(rvalue)) {}
expr_less_equal::expr_less_equal(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_less_equal, loc, std::move(lvalue), std::move(rvalue)) {}

expr_greater_equal::expr_greater_equal(expr lvalue, expr rvalue) : expr_binary(kind::expr_greater_equal, std::move(lvalue), std::move(rvalue)) {}
expr_greater_equal::expr_greater_equal(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_greater_equal, loc, std::move(lvalue), std::move(rvalue)) {}

expr_less::expr_less(expr lvalue, expr rvalue) : expr_binary(kind::expr_less, std::move(lvalue), std::move(rvalue)) {}
expr_less::expr_less(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_less, loc, std::move(lvalue), std::move(rvalue)) {}

expr_greater::expr_greater(expr lvalue, expr rvalue) : expr_binary(kind::expr_greater, std::move(lvalue), std::move(rvalue)) {}
expr_greater::expr_greater(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_greater, loc, std::move(lvalue), std::move(rvalue)) {}

expr_or::expr_or(expr lvalue, expr rvalue) : expr_binary(kind::expr_or, std::move(lvalue), std::move(rvalue)) {}
expr_or::expr_or(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_or, loc, std::move(lvalue), std::move(rvalue)) {}

expr_and::expr_and(expr lvalue, expr rvalue) : expr_binary(kind::expr_and, std::move(lvalue), std::move(rvalue)) {}
expr_and::expr_and(const location& loc, expr lvalue, expr rvalue) : expr_binary(kind::expr_and, loc, std::move(lvalue), std::move(rvalue)) {}

expr_ternary::expr_ternary(expr test, expr true_expr, expr false_expr) : node(kind::expr_ternary), test(std::move(test)), true_expr(std::move(true_expr)), false_expr(std::move(false_expr)) {}
expr_ternary::expr_ternary(const location& loc, expr test, expr true_expr, expr false_expr) : node(kind::expr_ternary, loc), test(std::move(test)), true_expr(std::move(true_expr)), false_expr(std::move(false_expr)) {}

expr_increment::expr_increment(expr lvalue, bool prefix) : node(kind::expr_increment), lvalue(std::move(lvalue)), prefix(prefix) {}
expr_increment::expr_increment(const location& loc, expr lvalue, bool prefix) : node(kind::expr_increment, loc), lvalue(std::move(lvalue)), prefix(prefix)  {}

expr_decrement::expr_decrement(expr lvalue, bool prefix) : node(kind::expr_decrement), lvalue(std::move(lvalue)), prefix(prefix) {}
expr_decrement::expr_decrement(const location& loc, expr lvalue, bool prefix) : node(kind::expr_decrement, loc), lvalue(std::move(lvalue)), prefix(prefix)  {}

expr_assign::expr_assign(ast::kind kind, expr lvalue, expr rvalue) : node(kind), lvalue(std::move(lvalue)), rvalue(std::move(rvalue)) {}
expr_assign::expr_assign(ast::kind kind, const location& loc, expr lvalue, expr rvalue) : node(kind, loc), lvalue(std::move(lvalue)), rvalue(std::move(rvalue)) {}

expr_assign_equal::expr_assign_equal(expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_equal, std::move(lvalue), std::move(rvalue)) {}
expr_assign_equal::expr_assign_equal(const location& loc, expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_equal, loc, std::move(lvalue), std::move(rvalue)) {}

expr_assign_add::expr_assign_add(expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_add, std::move(lvalue), std::move(rvalue)) {}
expr_assign_add::expr_assign_add(const location& loc, expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_add, loc, std::move(lvalue), std::move(rvalue)) {}

expr_assign_sub::expr_assign_sub(expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_sub, std::move(lvalue), std::move(rvalue)) {}
expr_assign_sub::expr_assign_sub(const location& loc, expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_sub, loc, std::move(lvalue), std::move(rvalue)) {}

expr_assign_mul::expr_assign_mul(expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_mul, std::move(lvalue), std::move(rvalue)) {}
expr_assign_mul::expr_assign_mul(const location& loc, expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_mul, loc, std::move(lvalue), std::move(rvalue)) {}

expr_assign_div::expr_assign_div(expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_div, std::move(lvalue), std::move(rvalue)) {}
expr_assign_div::expr_assign_div(const location& loc, expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_div, loc, std::move(lvalue), std::move(rvalue)) {}

expr_assign_mod::expr_assign_mod(expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_mod, std::move(lvalue), std::move(rvalue)) {}
expr_assign_mod::expr_assign_mod(const location& loc, expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_mod, loc, std::move(lvalue), std::move(rvalue)) {}

expr_assign_shift_left::expr_assign_shift_left(expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_shift_left, std::move(lvalue), std::move(rvalue)) {}
expr_assign_shift_left::expr_assign_shift_left(const location& loc, expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_shift_left, loc, std::move(lvalue), std::move(rvalue)) {}

expr_assign_shift_right::expr_assign_shift_right(expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_shift_right, std::move(lvalue), std::move(rvalue)) {}
expr_assign_shift_right::expr_assign_shift_right(const location& loc, expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_shift_right, loc, std::move(lvalue), std::move(rvalue)) {}

expr_assign_bitwise_or::expr_assign_bitwise_or(expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_bitwise_or, std::move(lvalue), std::move(rvalue)) {}
expr_assign_bitwise_or::expr_assign_bitwise_or(const location& loc, expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_bitwise_or, loc, std::move(lvalue), std::move(rvalue)) {}

expr_assign_bitwise_and::expr_assign_bitwise_and(expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_bitwise_and, std::move(lvalue), std::move(rvalue)) {}
expr_assign_bitwise_and::expr_assign_bitwise_and(const location& loc, expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_bitwise_and, loc, std::move(lvalue), std::move(rvalue)) {}

expr_assign_bitwise_exor::expr_assign_bitwise_exor(expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_bitwise_exor, std::move(lvalue), std::move(rvalue)) {}
expr_assign_bitwise_exor::expr_assign_bitwise_exor(const location& loc, expr lvalue, expr rvalue) : expr_assign(kind::expr_assign_bitwise_exor, loc, std::move(lvalue), std::move(rvalue)) {}

stmt_list::stmt_list() : node(kind::stmt_list) {}
stmt_list::stmt_list(const location& loc) : node(kind::stmt_list, loc) {}

stmt_dev::stmt_dev(ast::stmt_list::ptr list) : node(kind::stmt_dev), list(std::move(list)) {}
stmt_dev::stmt_dev(const location& loc, ast::stmt_list::ptr list) : node(kind::stmt_dev, loc), list(std::move(list)) {}

stmt_expr::stmt_expr(ast::expr expr) : node(kind::stmt_expr), expr(std::move(expr)) {}
stmt_expr::stmt_expr(const location& loc, ast::expr expr) : node(kind::stmt_expr, loc), expr(std::move(expr)) {}

stmt_call::stmt_call(ast::expr expr) : node(kind::stmt_call), expr(std::move(expr)) {}
stmt_call::stmt_call(const location& loc, ast::expr expr) : node(kind::stmt_call, loc), expr(std::move(expr)) {}

stmt_const::stmt_const(expr_identifier::ptr lvalue, expr rvalue) : node(kind::stmt_const), lvalue(std::move(lvalue)), rvalue(std::move(rvalue)) {}
stmt_const::stmt_const(const location& loc, expr_identifier::ptr lvalue, expr rvalue) : node(kind::stmt_const, loc), lvalue(std::move(lvalue)), rvalue(std::move(rvalue)) {}

stmt_assign::stmt_assign(ast::expr expr) : node(kind::stmt_assign), expr(std::move(expr)) {}
stmt_assign::stmt_assign(const location& loc, ast::expr expr) : node(kind::stmt_assign, loc), expr(std::move(expr)) {}

stmt_endon::stmt_endon(expr obj, expr event) : node(kind::stmt_endon), obj(std::move(obj)), event(std::move(event)) {}
stmt_endon::stmt_endon(const location& loc, expr obj, expr event) : node(kind::stmt_endon, loc), obj(std::move(obj)), event(std::move(event)) {}

stmt_notify::stmt_notify(expr obj, expr event, expr_arguments::ptr args) : node(kind::stmt_notify), obj(std::move(obj)), event(std::move(event)), args(std::move(args)) {}
stmt_notify::stmt_notify(const location& loc, expr obj, expr event, expr_arguments::ptr args) : node(kind::stmt_notify, loc), obj(std::move(obj)), event(std::move(event)), args(std::move(args)) {}

stmt_realwait::stmt_realwait(expr time) : node(kind::stmt_realwait), time(std::move(time)) {}
stmt_realwait::stmt_realwait(const location& loc, expr time) : node(kind::stmt_realwait, loc), time(std::move(time)) {}

stmt_wait::stmt_wait(expr time) : node(kind::stmt_wait), time(std::move(time)) {}
stmt_wait::stmt_wait(const location& loc, expr time) : node(kind::stmt_wait, loc), time(std::move(time)) {}

stmt_waittill::stmt_waittill(expr obj, expr event, expr_arguments::ptr args) : node(kind::stmt_waittill), obj(std::move(obj)), event(std::move(event)), args(std::move(args)) {}
stmt_waittill::stmt_waittill(const location& loc, expr obj, expr event, expr_arguments::ptr args) : node(kind::stmt_waittill, loc), obj(std::move(obj)), event(std::move(event)), args(std::move(args)) {}

stmt_waittillmatch::stmt_waittillmatch(expr obj, expr event, expr_arguments::ptr args) : node(kind::stmt_waittillmatch), obj(std::move(obj)), event(std::move(event)), args(std::move(args)) {}
stmt_waittillmatch::stmt_waittillmatch(const location& loc, expr obj, expr event, expr_arguments::ptr args) : node(kind::stmt_waittillmatch, loc), obj(std::move(obj)), event(std::move(event)), args(std::move(args)) {}

stmt_waittillframeend::stmt_waittillframeend() : node(kind::stmt_waittillframeend) {}
stmt_waittillframeend::stmt_waittillframeend(const location& loc) : node(kind::stmt_waittillframeend, loc) {}

stmt_if::stmt_if(expr test, ast::stmt stmt) : node(kind::stmt_if), test(std::move(test)), stmt(std::move(stmt)), blk(nullptr) {}
stmt_if::stmt_if(const location& loc, expr test, ast::stmt stmt) : node(kind::stmt_if, loc), test(std::move(test)), stmt(std::move(stmt)), blk(nullptr) {}

stmt_ifelse::stmt_ifelse(expr test, stmt stmt_if, stmt stmt_else) : node(kind::stmt_ifelse), test(std::move(test)), stmt_if(std::move(stmt_if)), stmt_else(std::move(stmt_else)), blk_if(nullptr), blk_else(nullptr) {}
stmt_ifelse::stmt_ifelse(const location& loc, expr test, stmt stmt_if, stmt stmt_else) : node(kind::stmt_ifelse, loc), test(std::move(test)), stmt_if(std::move(stmt_if)), stmt_else(std::move(stmt_else)), blk_if(nullptr), blk_else(nullptr) {}

stmt_while::stmt_while(expr test, ast::stmt stmt) : node(kind::stmt_while), test(std::move(test)), stmt(std::move(stmt)), blk(nullptr) {}
stmt_while::stmt_while(const location& loc, expr test, ast::stmt stmt) : node(kind::stmt_while, loc), test(std::move(test)), stmt(std::move(stmt)), blk(nullptr) {}

stmt_dowhile::stmt_dowhile(expr test, ast::stmt stmt) : node(kind::stmt_dowhile), test(std::move(test)), stmt(std::move(stmt)), blk(nullptr) {}
stmt_dowhile::stmt_dowhile(const location& loc, expr test, ast::stmt stmt) : node(kind::stmt_dowhile, loc), test(std::move(test)), stmt(std::move(stmt)), blk(nullptr) {}

stmt_for::stmt_for(ast::stmt init, expr test, ast::stmt iter, ast::stmt stmt) : node(kind::stmt_for), init(std::move(init)), test(std::move(test)), iter(std::move(iter)), stmt(std::move(stmt)), blk(nullptr), blk_iter(nullptr) {}
stmt_for::stmt_for(const location& loc, ast::stmt init, expr test, ast::stmt iter, ast::stmt stmt) : node(kind::stmt_for, loc), init(std::move(init)), test(std::move(test)), iter(std::move(iter)), stmt(std::move(stmt)), blk(nullptr), blk_iter(nullptr) {}

stmt_foreach::stmt_foreach(const location& loc, ast::stmt stmt, bool use_key) : node(kind::stmt_foreach, loc), array_expr(nullptr), value_expr(nullptr), key_expr(nullptr), array(nullptr), pre_expr(nullptr), cond_expr(nullptr), post_expr(nullptr), stmt0(nullptr), stmt(std::move(stmt)), ctx(nullptr), ctx_post(nullptr), use_key(use_key) {}
stmt_foreach::stmt_foreach(ast::expr element, ast::expr container, ast::stmt stmt) : node(kind::stmt_foreach), array_expr(std::move(container)), value_expr(std::move(element)), key_expr(nullptr), array(nullptr), pre_expr(nullptr), cond_expr(nullptr), post_expr(nullptr), stmt0(nullptr), stmt(std::move(stmt)), ctx(nullptr), ctx_post(nullptr), use_key(false) {}
stmt_foreach::stmt_foreach(ast::expr key, ast::expr element, ast::expr container, ast::stmt stmt) : node(kind::stmt_foreach), array_expr(std::move(container)), value_expr(std::move(element)), key_expr(std::move(key)), array(nullptr), pre_expr(nullptr), cond_expr(nullptr), post_expr(nullptr), stmt0(nullptr), stmt(std::move(stmt)), ctx(nullptr), ctx_post(nullptr), use_key(true) {}
stmt_foreach::stmt_foreach(const location& loc, ast::expr element, ast::expr container, ast::stmt stmt) : node(kind::stmt_foreach, loc), array_expr(std::move(container)), value_expr(std::move(element)), key_expr(nullptr), array(nullptr), pre_expr(nullptr), cond_expr(nullptr), post_expr(nullptr), stmt0(nullptr), stmt(std::move(stmt)), ctx(nullptr), ctx_post(nullptr), use_key(false) {}
stmt_foreach::stmt_foreach(const location& loc, ast::expr key, ast::expr element, ast::expr container, ast::stmt stmt) : node(kind::stmt_foreach, loc), array_expr(std::move(container)), value_expr(std::move(element)), key_expr(std::move(key)), array(nullptr), pre_expr(nullptr), cond_expr(nullptr), post_expr(nullptr), stmt0(nullptr), stmt(std::move(stmt)), ctx(nullptr), ctx_post(nullptr), use_key(true) {}

stmt_switch::stmt_switch(expr test, stmt_list::ptr stmt) : node(kind::stmt_switch), test(std::move(test)), stmt(std::move(stmt)), ctx(nullptr) {}
stmt_switch::stmt_switch(const location& loc, expr test, stmt_list::ptr stmt) : node(kind::stmt_switch, loc), test(std::move(test)), stmt(std::move(stmt)), ctx(nullptr) {}

stmt_case::stmt_case(expr label) : node(kind::stmt_case), label(std::move(label)), stmt(nullptr), blk(nullptr) {}
stmt_case::stmt_case(const location& loc, expr label) : node(kind::stmt_case, loc), label(std::move(label)), stmt(nullptr), blk(nullptr) {}
stmt_case::stmt_case(const location& loc, expr label, stmt_list::ptr stmt) : node(kind::stmt_case, loc), label(std::move(label)), stmt(std::move(stmt)), blk(nullptr) {}

stmt_default::stmt_default() : node(kind::stmt_default), stmt(nullptr), blk(nullptr) {}
stmt_default::stmt_default(const location& loc) : node(kind::stmt_default, loc), stmt(nullptr), blk(nullptr) {}
stmt_default::stmt_default(const location& loc, stmt_list::ptr stmt) : node(kind::stmt_default, loc), stmt(std::move(stmt)), blk(nullptr) {}

stmt_break::stmt_break() : node(kind::stmt_break) {}
stmt_break::stmt_break(const location& loc) : node(kind::stmt_break, loc) {}

stmt_continue::stmt_continue() : node(kind::stmt_continue) {}
stmt_continue::stmt_continue(const location& loc) : node(kind::stmt_continue, loc) {}

stmt_return::stmt_return(ast::expr expr) : node(kind::stmt_return), expr(std::move(expr)) {}
stmt_return::stmt_return(const location& loc, ast::expr expr) : node(kind::stmt_return, loc), expr(std::move(expr)) {}

stmt_breakpoint::stmt_breakpoint() : node(kind::stmt_breakpoint) {}
stmt_breakpoint::stmt_breakpoint(const location& loc) : node(kind::stmt_breakpoint, loc) {}

stmt_prof_begin::stmt_prof_begin(expr_arguments::ptr args) : node(kind::stmt_prof_begin), args(std::move(args)) {}
stmt_prof_begin::stmt_prof_begin(const location& loc, expr_arguments::ptr args) : node(kind::stmt_prof_begin, loc), args(std::move(args)) {}

stmt_prof_end::stmt_prof_end(expr_arguments::ptr args) : node(kind::stmt_prof_end), args(std::move(args)) {}
stmt_prof_end::stmt_prof_end(const location& loc, expr_arguments::ptr args) : node(kind::stmt_prof_end, loc), args(std::move(args)) {}

decl_thread::decl_thread(expr_identifier::ptr name, expr_parameters::ptr params, stmt_list::ptr stmt, export_flags flags) : node(kind::decl_thread), name(std::move(name)), params(std::move(params)), stmt(std::move(stmt)), flags(flags) {}
decl_thread::decl_thread(const location& loc, expr_identifier::ptr name, expr_parameters::ptr params, stmt_list::ptr stmt,  export_flags flags) : node(kind::decl_thread, loc), name(std::move(name)), params(std::move(params)), stmt(std::move(stmt)), flags(flags) {}

decl_usingtree::decl_usingtree(expr_string::ptr name) : node(kind::decl_usingtree), name(std::move(name)) {}
decl_usingtree::decl_usingtree(const location& loc, expr_string::ptr name) : node(kind::decl_usingtree, loc), name(std::move(name)) {}

decl_dev_begin::decl_dev_begin() : node(kind::decl_dev_begin) {}
decl_dev_begin::decl_dev_begin(const location& loc) : node(kind::decl_dev_begin, loc) {}

decl_dev_end::decl_dev_end() : node(kind::decl_dev_end) {}
decl_dev_end::decl_dev_end(const location& loc) : node(kind::decl_dev_end, loc) {}

include::include(expr_path::ptr path) : node(kind::include), path(std::move(path)) {}
include::include(const location& loc, expr_path::ptr path) : node(kind::include, loc), path(std::move(path)) {}

program::program() : node(kind::program) {}
program::program(const location& loc) : node(kind::program, loc) {}

asm_loc::asm_loc(const std::string& value) : node(kind::asm_loc), value(value) {}
asm_loc::asm_loc(const location& loc, const std::string& value) : node(kind::asm_loc, loc), value(value) {}

asm_jump::asm_jump(const std::string& value) : node(kind::asm_jump), value(std::move(value)) {}
asm_jump::asm_jump(const location& loc, const std::string& value) : node(kind::asm_jump, loc), value(value) {}

asm_jump_back::asm_jump_back(const std::string& value) : node(kind::asm_jump_back), value(value) {}
asm_jump_back::asm_jump_back(const location& loc, const std::string& value) : node(kind::asm_jump_back, loc), value(value) {}

asm_jump_cond::asm_jump_cond(ast::expr expr, const std::string& value) : node(kind::asm_jump_cond), expr(std::move(expr)), value(value) {}
asm_jump_cond::asm_jump_cond(const location& loc, ast::expr expr, const std::string& value) : node(kind::asm_jump_cond, loc), expr(std::move(expr)), value(value) {}

asm_jump_true_expr::asm_jump_true_expr(ast::expr expr, const std::string& value) : node(kind::asm_jump_true_expr), expr(std::move(expr)), value(value) {}
asm_jump_true_expr::asm_jump_true_expr(const location& loc, ast::expr expr, const std::string& value) : node(kind::asm_jump_true_expr, loc), expr(std::move(expr)), value(value) {}

asm_jump_false_expr::asm_jump_false_expr(ast::expr expr, const std::string& value) : node(kind::asm_jump_false_expr), expr(std::move(expr)), value(value) {}
asm_jump_false_expr::asm_jump_false_expr(const location& loc, ast::expr expr, const std::string& value) : node(kind::asm_jump_false_expr, loc), expr(std::move(expr)), value(value) {}

asm_switch::asm_switch(ast::expr expr, const std::string& value) : node(kind::asm_switch), expr(std::move(expr)), value(value) {}
asm_switch::asm_switch(const location& loc, ast::expr expr, const std::string& value) : node(kind::asm_switch, loc), expr(std::move(expr)), value(value) {}

asm_endswitch::asm_endswitch(std::vector<std::string> data, const std::string& count) : node(kind::asm_endswitch), data(std::move(data)), count(count) {}
asm_endswitch::asm_endswitch(const location& loc, std::vector<std::string> data, const std::string& count) : node(kind::asm_endswitch, loc), data(std::move(data)), count(count) {}

asm_prescriptcall::asm_prescriptcall() : node(kind::asm_prescriptcall) {}
asm_prescriptcall::asm_prescriptcall(const location& loc) : node(kind::asm_prescriptcall, loc) {}

asm_voidcodepos::asm_voidcodepos() : node(kind::asm_voidcodepos) {}
asm_voidcodepos::asm_voidcodepos(const location& loc) : node(kind::asm_voidcodepos, loc) {}

asm_dev::asm_dev(const std::string& value) : node(kind::asm_dev), value(std::move(value)) {}
asm_dev::asm_dev(const location& loc, const std::string& value) : node(kind::asm_dev, loc), value(value) {}

auto expr_true::print() const -> std::string
{
    return "true";
}

auto expr_false::print() const -> std::string
{
    return "false";
}

auto expr_integer::print() const -> std::string
{
    return value;
}

auto expr_float::print() const -> std::string
{
    return value;
}

auto expr_vector::print() const -> std::string
{
    return "( "s + x.print() + ", " + y.print() + ", " + z.print() + " )";
}

auto expr_hash::print() const -> std::string
{
    return value.starts_with("_hash_") ? value : ("\"" + value + "\"");
}

auto expr_string::print() const -> std::string
{
    return utils::string::to_literal(value);
}

auto expr_istring::print() const -> std::string
{
    return "&"s += utils::string::to_literal(value);
}

auto expr_path::print() const -> std::string
{
    return utils::string::backslash(value);
}

auto expr_identifier::print() const -> std::string
{
    return value;
}

auto expr_animtree::print() const -> std::string
{
    return "#animtree";
}

auto expr_animation::print() const -> std::string
{
    return "%"s += value;
}

auto expr_level::print() const -> std::string
{
    return "level";
}

auto expr_anim::print() const -> std::string
{
    return "anim";
}

auto expr_self::print() const -> std::string
{
    return "self";
}

auto expr_game::print() const -> std::string
{
    return "game";
}

auto expr_undefined::print() const -> std::string
{
    return "undefined";
}

auto expr_empty_array::print() const -> std::string
{
    return "[]";
}

auto expr_paren::print() const -> std::string
{
    return "( " + child.print() + " )";
}

auto expr_size::print() const -> std::string
{
    return obj.print() + ".size";
}

auto expr_field::print() const -> std::string
{
    return obj.print() + "." + field->print();
}

auto expr_array::print() const -> std::string
{
    return obj.print() + "[" + key.print() + "]";
}

auto expr_reference::print() const -> std::string
{
    return path->print() + "::" + name->print();
}

auto expr_getnextarraykey::print() const -> std::string
{
    return "getnextarraykey( " + arg1.print() + ", " + arg2.print() + " )";
}

auto expr_getfirstarraykey::print() const -> std::string
{
    return "getfirstarraykey( " + arg.print() + " )";
}

auto expr_getdvarcoloralpha::print() const -> std::string
{
    return "getdvarcoloralpha( " + arg.print() + " )";
}

auto expr_getdvarcolorblue::print() const -> std::string
{
    return "getdvarcolorblue( " + arg.print() + " )";
}

auto expr_getdvarcolorgreen::print() const -> std::string
{
    return "getdvarcolorgreen( " + arg.print() + " )";
}

auto expr_getdvarcolorred::print() const -> std::string
{
    return "getdvarcolorred( " + arg.print() + " )";
}

auto expr_getdvarvector::print() const -> std::string
{
    return "getdvarvector( " + arg.print() + " )";
}

auto expr_getdvarfloat::print() const -> std::string
{
    return "getdvarfloat( " + arg.print() + " )";
}

auto expr_getdvarint::print() const -> std::string
{
    return "getdvarint( " + arg.print() + " )";
}

auto expr_getdvar::print() const -> std::string
{
    return "getdvar( " + arg.print() + " )";
}

auto expr_gettime::print() const -> std::string
{
    return "gettime()";
}

auto expr_abs::print() const -> std::string
{
    return "abs( " + arg.print() + " )";
}

auto expr_vectortoangles::print() const -> std::string
{
    return "vectortoangles( " + arg.print() + " )";
}

auto expr_angleclamp180::print() const -> std::string
{
    return "angleclamp180( " + arg.print() + " )";
}

auto expr_anglestoforward::print() const -> std::string
{
    return "anglestoforward( " + arg.print() + " )";
}

auto expr_anglestoright::print() const -> std::string
{
    return "anglestoright( " + arg.print() + " )";
}

auto expr_anglestoup::print() const -> std::string
{
    return "anglestoup( " + arg.print() + " )";
}

auto expr_vectorscale::print() const -> std::string
{
    return "vectorscale( " + arg1.print() + ", " + arg2.print() + " )";
}

auto expr_isdefined::print() const -> std::string
{
    return "isdefined( " + arg.print() + " )";
}

auto expr_arguments::print() const -> std::string
{
    std::string data;

    for (const auto& entry : list)
    {
        data += " " + entry.print();
        data += (&entry != &list.back()) ? "," : " ";
    }

    return data;
}

auto expr_parameters::print() const -> std::string
{
    std::string data;

    for (const auto& entry : list)
    {
        data += " " + entry.print();
        data += (&entry != &list.back()) ? "," : " ";
    }

    return data;
}

auto expr_pointer::print() const -> std::string
{
    std::string data;

    if (mode == call::mode::thread)
        data += "thread ";

    return data += "[[ "s + func.print() + " ]](" + args->print() + ")";
}

auto expr_function::print() const -> std::string
{
    std::string data;

    if (mode == call::mode::thread)
        data += "thread ";

    if (path->value != "")
        data += path->print() + "::";

    return data += name->print() + "(" + args->print() + ")";
}

auto expr_method::print() const -> std::string
{
    return obj.print() + " " + call.print();
}

auto expr_call::print() const -> std::string
{
    return call.print();
}

auto expr_complement::print() const -> std::string
{
    return "~" + rvalue.print();
}

auto expr_negate::print() const -> std::string
{
    return "-" + rvalue.print();
}

auto expr_not::print() const -> std::string
{
    return "!" + rvalue.print();
}

auto expr_add::print() const -> std::string
{
    return lvalue.print() + " + " + rvalue.print();
}

auto expr_sub::print() const -> std::string
{
    return lvalue.print() + " - " + rvalue.print();
}

auto expr_mul::print() const -> std::string
{
    return lvalue.print() + " * " + rvalue.print();
}

auto expr_div::print() const -> std::string
{
    return lvalue.print() + " / " + rvalue.print();
}

auto expr_mod::print() const -> std::string
{
    return lvalue.print() + " % " + rvalue.print();
}

auto expr_shift_left::print() const -> std::string
{
    return lvalue.print() + " << " + rvalue.print();
}

auto expr_shift_right::print() const -> std::string
{
    return lvalue.print() + " >> " + rvalue.print();
}

auto expr_bitwise_or::print() const -> std::string
{
    return lvalue.print() + " | " + rvalue.print();
}

auto expr_bitwise_and::print() const -> std::string
{
    return lvalue.print() + " & " + rvalue.print();
}

auto expr_bitwise_exor::print() const -> std::string
{
    return lvalue.print() + " ^ " + rvalue.print();
}

auto expr_equality::print() const -> std::string
{
    return lvalue.print() + " == " + rvalue.print();
}

auto expr_inequality::print() const -> std::string
{
    return lvalue.print() + " != " + rvalue.print();
}

auto expr_less_equal::print() const -> std::string
{
    return lvalue.print() + " <= " + rvalue.print();
}

auto expr_greater_equal::print() const -> std::string
{
    return lvalue.print() + " >= " + rvalue.print();
}

auto expr_less::print() const -> std::string
{
    return lvalue.print() + " < " + rvalue.print();
}

auto expr_greater::print() const -> std::string
{
    return lvalue.print() + " > " + rvalue.print();
}

auto expr_or::print() const -> std::string
{
    return lvalue.print() + " || " + rvalue.print();
}

auto expr_and::print() const -> std::string
{
    return lvalue.print() + " && " + rvalue.print();
}

auto expr_ternary::print() const -> std::string
{
    return test.print() + " ? " + true_expr.print() + " : " + false_expr.print();
}

auto expr_increment::print() const -> std::string
{
    if (prefix) return "++" + lvalue.print();
    return lvalue.print() + "++";
}

auto expr_decrement::print() const -> std::string
{
    if (prefix) return "--" + lvalue.print();
    return lvalue.print() + "--";
}

auto expr_assign_equal::print() const -> std::string
{
    return lvalue.print() + " = " + rvalue.print();
};

auto expr_assign_add::print() const -> std::string
{
    return lvalue.print() + " += " + rvalue.print() ;
};

auto expr_assign_sub::print() const -> std::string
{
    return lvalue.print() + " -= " + rvalue.print() ;
};

auto expr_assign_mul::print() const -> std::string
{
    return lvalue.print() + " *= " + rvalue.print() ;
};

auto expr_assign_div::print() const -> std::string
{
    return lvalue.print() + " /= " + rvalue.print() ;
};

auto expr_assign_mod::print() const -> std::string
{
    return lvalue.print() + " %= " + rvalue.print() ;
};

auto expr_assign_shift_left::print() const -> std::string
{
    return lvalue.print() + " <<= " + rvalue.print() ;
};

auto expr_assign_shift_right::print() const -> std::string
{
    return lvalue.print() + " >>= " + rvalue.print() ;
};

auto expr_assign_bitwise_or::print() const -> std::string
{
    return lvalue.print() + " |= " + rvalue.print();
}

auto expr_assign_bitwise_and::print() const -> std::string
{
    return lvalue.print() + " &= " + rvalue.print();
}

auto expr_assign_bitwise_exor::print() const -> std::string
{
    return lvalue.print() + " ^= " + rvalue.print();
}

auto stmt_list::print() const -> std::string
{
    if (is_expr)
    {
        if (list.size() > 0)
        {
            auto s = list[0].print();
            s.pop_back();
            return s;
        }
        else return "";
    }

    std::string data;
    bool last_special = false;
    bool last_devblock = false;
    auto block_pad = indented(indent_);
    indent_ += 4;

    auto stmts_pad = indented(indent_);

    if (!is_case)
        data += block_pad + "{\n";

    for (const auto& stmt : list)
    {
        if (((&stmt != &list.front() && stmt.as_node->is_special_stmt()) || last_special) && stmt != kind::stmt_dev && !last_devblock)
            data += "\n";

        if (stmt == kind::stmt_dev)
        {
            data += stmt.print();
            last_devblock = true;
        }
        else
        {
            data += stmts_pad + stmt.print();
            last_devblock = false;
        }

        if (&stmt != &list.back())
            data += "\n";

        if (stmt.as_node->is_special_stmt())
            last_special = true;
        else
            last_special = false;
    }

    indent_ -= 4;

    if (!is_case)
        data += "\n" + block_pad + "}";

    return data;
}

auto stmt_dev::print() const -> std::string
{
    std::string data;
    bool last_special = false;

    auto stmts_pad = indented(indent_);

    data += "/#\n";

    for (const auto& stmt : list->list)
    {
        if ((&stmt != &list->list.front() && stmt.as_node->is_special_stmt()) || last_special)
            data += "\n";

        data += stmts_pad + stmt.print();

        if (&stmt != &list->list.back())
            data += "\n";

        if (stmt.as_node->is_special_stmt())
            last_special = true;
        else
            last_special = false;
    }

    data += "\n#/";

    return data;
}

auto stmt_expr::print() const -> std::string
{
    return expr.print();
}

auto stmt_call::print() const -> std::string
{
    return expr.print() + ";";
};

auto stmt_const::print() const -> std::string
{
    return "const "s + lvalue->print() + " = " + rvalue.print() + ";";
};

auto stmt_assign::print() const -> std::string
{
    return expr.print() + ";";
};

auto stmt_endon::print() const -> std::string
{
    return obj.print() + " endon( " + event.print() + " );";
};

auto stmt_notify::print() const -> std::string
{
    if (args->list.size() == 0)
        return obj.print() + " notify( " + event.print() + " );";
    else
        return obj.print() + " notify( " + event.print() + "," + args->print() + ");";
};

auto stmt_realwait::print() const -> std::string
{
    if (time == kind::expr_float || time == kind::expr_integer)
        return "realwait " + time.print() + ";";
    else
        return "realwait( " + time.print() + " );";
};

auto stmt_wait::print() const -> std::string
{
    if (time == kind::expr_float || time == kind::expr_integer)
        return "wait " + time.print() + ";";
    else
        return "wait( " + time.print() + " );";
};

auto stmt_waittill::print() const -> std::string
{
    if (args->list.size() == 0)
        return obj.print() + " waittill( " + event.print() + " );";
    else
        return obj.print() + " waittill( " + event.print() + "," + args->print() + ");";
};

auto stmt_waittillmatch::print() const -> std::string
{
    if (args->list.size() == 0)
        return obj.print() + " waittillmatch( " + event.print() + " );";
    else
        return obj.print() + " waittillmatch( " + event.print() + "," + args->print() + ");";
};

auto stmt_waittillframeend::print() const -> std::string
{
    return "waittillframeend;";
};

auto stmt_if::print() const -> std::string
{
    std::string data = "if ( " + test.print() + " )\n";

    if (stmt == kind::stmt_list)
    {
        data += stmt.as_list->print();
    }
    else
    {
        indent_ += 4;
        data += indented(indent_) + stmt.print();
        indent_ -= 4;
    }

    return data;
};

auto stmt_ifelse::print() const -> std::string
{
    std::string pad = indented(indent_);
    std::string data = "if ( " + test.print() + " )\n";

    if (stmt_if == kind::stmt_list)
    {
        data += stmt_if.print();
    }
    else
    {
        indent_ += 4;
        data += indented(indent_) + stmt_if.print();
        indent_ -= 4;
    }

    data += "\n" + pad + "else";

    if (stmt_else == kind::stmt_list)
    {
        data += "\n" + stmt_else.as_list->print();
    }
    else
    {
        if (stmt_else == kind::stmt_if || stmt_else == kind::stmt_ifelse)
        {
            data += " " + stmt_else.print();
        }
        else
        {
            indent_ += 4;
            data += "\n" + indented(indent_) + stmt_else.print();
            indent_ -= 4;
        }
    }

    return data;
};

auto stmt_while::print() const -> std::string
{
    std::string data;

    if (test == kind::null)
    {
        data += "while ( true )\n";
    }
    else
    {
        data += "while ( " + test.print() + " )\n";
    }

    std::string pad = indented(indent_);

    if (stmt == kind::stmt_list)
    {
        data += stmt.print();
    }
    else
    {
        indent_ += 4;
        data += indented(indent_) + stmt.print();
        indent_ -= 4;
    }

    return data;
};

auto stmt_dowhile::print() const -> std::string
{
    std::string data;

    data += "do\n";

    std::string pad = indented(indent_);

    if (stmt == kind::stmt_list)
    {
        data += stmt.print();
    }
    else
    {
        indent_ += 4;
        data += indented(indent_) + stmt.print();
        indent_ -= 4;
    }

    if (test == kind::null)
    {
        data += "\n" + pad + "while ( true );";
    }
    else
    {
        data += "\n" + pad + "while ( " + test.print() + " );";
    }

    return data;
};

auto stmt_for::print() const -> std::string
{
    std::string data;

    if (test == kind::null)
    {
        data += "for (;;)\n";
    }
    else
    {
        data += "for ( " + init.print() + "; " + test.print() + "; " + iter.print() + " )";
        data += "\n";
    }

    if (stmt == kind::stmt_list)
    {
        data += stmt.print();
    }
    else
    {
        indent_ += 4;
        data += indented(indent_) + stmt.print();
        indent_ -= 4;
    }

    return data;
};

auto stmt_foreach::print() const -> std::string
{
    std::string data;

    data += "foreach ( ";

    if (use_key) data += key_expr.print() + ", ";

    data += value_expr.print() + " in " + array_expr.print() + " )\n";

    if (stmt == kind::stmt_list)
    {
        data += stmt.print();
    }
    else
    {
        indent_ += 4;
        data += indented(indent_) + stmt.print();
        indent_ -= 4;
    }

    return data;
};

auto stmt_switch::print() const -> std::string
{
    std::string data;

    data += "switch ( " + test.print() + " )\n";
    data += stmt->print();

    return data;
};

auto stmt_case::print() const -> std::string
{
    if (stmt != nullptr && stmt->list.size() == 0)
        return "case " + label.print() + ":";
    else
        return "case " + label.print() + ":\n" + stmt->print();
};

auto stmt_default::print() const -> std::string
{
    if (stmt != nullptr && stmt->list.size() == 0)
        return "default:";
    else
        return "default:\n" + stmt->print();
};

auto stmt_break::print() const -> std::string
{
    return "break;";
};

auto stmt_continue::print() const -> std::string
{
    return "continue;";
};

auto stmt_return::print() const -> std::string
{
    if (expr == kind::null) return "return;";

    return "return " + expr.print() + ";";
};

auto stmt_breakpoint::print() const -> std::string
{
    return "breakpoint;";
};

auto stmt_prof_begin::print() const -> std::string
{
    return "prof_begin(" + args->print() + ");";
};

auto stmt_prof_end::print() const -> std::string
{
    return "prof_end(" + args->print() + ");";
};

auto decl_thread::print() const -> std::string
{
    std::string data;

    if (static_cast<std::uint8_t>(flags) & static_cast<std::uint8_t>(export_flags::export_autoexec))
        data += "autoexec ";

    if (static_cast<std::uint8_t>(flags) & static_cast<std::uint8_t>(export_flags::export_codecall))
        data += "codecall ";

    if (static_cast<std::uint8_t>(flags) & static_cast<std::uint8_t>(export_flags::export_private2))
        data += "private ";

    data += name->print() + "(" + params->print() + ")" + "\n" + stmt->print() + "\n";

    return data;
}

auto decl_usingtree::print() const -> std::string
{
    return "#using_animtree"s + "(" + name->print() + ");\n";
}

auto decl_dev_begin::print() const -> std::string
{
    return "/#\n";
}

auto decl_dev_end::print() const -> std::string
{
    return "#/\n";
}

auto include::print() const -> std::string
{
    return "#include"s + " " + path->print() + ";\n";
}

auto program::print() const -> std::string
{
    std::string data;

    for (const auto& include : includes)
    {
        data += include->print();
    }

    data += "\n";

    for (const auto& entry : declarations)
    {
        data += entry.print();

        if (&entry != &declarations.back())
            data += "\n";
    }

    return data;
}

auto asm_loc::print() const -> std::string
{
    return value + ":";
}

auto asm_jump::print() const -> std::string
{
    return "asm_jump( " + value + " );";
}

auto asm_jump_back::print() const -> std::string
{
    return "asm_jump_back( " + value + " );";
}

auto asm_jump_cond::print() const -> std::string
{
    return "asm_cond( " + expr.print() + ", " + value + " );";
}

auto asm_jump_true_expr::print() const -> std::string
{
    return "asm_expr_true( " + value + " );";
}

auto asm_jump_false_expr::print() const -> std::string
{
    return "asm_expr_false( " + value + " );";
}

auto asm_switch::print() const -> std::string
{
    return "asm_switch( " + expr.print() + ", " + value + " );";
}

auto asm_endswitch::print() const -> std::string
{
    std::string result;

    for (auto& entry : data)
    {
        result += " " + entry;
    }
    return "asm_endswitch( " + count + result + " );";
}

auto asm_prescriptcall::print() const -> std::string
{
    return "asm_prescriptcall();";
}

auto asm_voidcodepos::print() const -> std::string
{
    return "asm_voidcodepos();";
}

auto asm_dev::print() const -> std::string
{
    return "devblock( " + value + " );";
}

// operators

bool operator==(const node& n, kind k)
{
    return n.kind_ == k;
}

bool operator==(const node& lhs, const node& rhs)
{
    return lhs.kind_ == rhs.kind_;
}

bool operator==(const expr_true&, const expr_true&)
{
    return true;
}

bool operator==(const expr_false&, const expr_false&)
{
    return true;
}

bool operator==(const expr_integer& lhs, const expr_integer& rhs)
{
    return lhs.value == rhs.value;
}

bool operator==(const expr_float& lhs, const expr_float& rhs)
{
    return lhs.value == rhs.value;
}

bool operator==(const expr_vector& lhs, const expr_vector& rhs)
{
    return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z;
}

bool operator==(const expr_hash& lhs, const expr_hash& rhs)
{
    return lhs.value == rhs.value;
}

bool operator==(const expr_string& lhs, const expr_string& rhs)
{
    return lhs.value == rhs.value;
}

bool operator==(const expr_istring& lhs, const expr_istring& rhs)
{
    return lhs.value == rhs.value;
}

bool operator==(const expr_path& lhs, const expr_path& rhs)
{
    return lhs.value == rhs.value;
}

bool operator==(const expr_identifier& lhs, const expr_identifier& rhs)
{
    return lhs.value == rhs.value;
}

bool operator==(const expr_animtree&, const expr_animtree&)
{
    return true;
}

bool operator==(const expr_animation& lhs, const expr_animation& rhs)
{
    return lhs.value == rhs.value;
}

bool operator==(const expr_level&, const expr_level&)
{
    return true;
}

bool operator==(const expr_anim&, const expr_anim&)
{
    return true;
}

bool operator==(const expr_self&, const expr_self&)
{
    return true;
}

bool operator==(const expr_game&, const expr_game&)
{
    return true;
}

bool operator==(const expr_undefined&, const expr_undefined&)
{
    return true;
}

bool operator==(const expr_empty_array&, const expr_empty_array&)
{
    return true;
}

bool operator==(const expr_paren& lhs, const expr_paren& rhs)
{
    return lhs.child == rhs.child;
}

bool operator==(const expr_size& lhs, const expr_size& rhs)
{
    return lhs.obj == rhs.obj;
}

bool operator==(const expr_field& lhs, const expr_field& rhs)
{
    return lhs.obj == rhs.obj && *lhs.field == *rhs.field;
}

bool operator==(const expr_array& lhs, const expr_array& rhs)
{
    return lhs.obj == rhs.obj && lhs.key == rhs.key;
}

call::call() : as_node(nullptr) {}

call::call(std::unique_ptr<node> value) : as_node(std::move(value)) {}

call::call(call&& value)
{
    new(&as_node) std::unique_ptr<node>(std::move(value.as_node));
}

call::~call()
{
    if (as_node == nullptr) return;

    switch (as_node->kind())
    {
        case kind::null: as_node.~unique_ptr(); return;
        case kind::expr_pointer: as_pointer.~unique_ptr(); return;
        case kind::expr_function: as_function.~unique_ptr(); return;
        default: return;
    }
}

bool operator==(const call& lhs, kind rhs)
{
    return *lhs.as_node == rhs;
}

auto call::loc() const -> location
{
    return as_node->loc();
}

auto call::kind() const -> ast::kind
{
    return as_node->kind();
}

auto call::print() const -> std::string
{
    return as_node->print();
}

expr::expr() : as_node(nullptr) {}

expr::expr(std::unique_ptr<node> value) : as_node(std::move(value)) {}

expr::expr(expr&& value)
{
    new(&as_node) std::unique_ptr<node>(std::move(value.as_node));
}

expr& expr::operator=(expr&& value)
{
    new(&as_node) std::unique_ptr<node>(std::move(value.as_node));
    return *(expr*)&as_node;
}

expr::~expr()
{
    if (as_node == nullptr) return;

    switch (as_node->kind())
    {
        case kind::null: as_node.~unique_ptr(); return;
        case kind::expr_true: as_true.~unique_ptr(); return;
        case kind::expr_false: as_false.~unique_ptr(); return;
        case kind::expr_integer: as_integer.~unique_ptr(); return;
        case kind::expr_float: as_float.~unique_ptr(); return;
        case kind::expr_vector: as_vector.~unique_ptr(); return;
        case kind::expr_hash: as_hash.~unique_ptr(); return;
        case kind::expr_string: as_string.~unique_ptr(); return;
        case kind::expr_istring: as_istring.~unique_ptr(); return;
        case kind::expr_path: as_path.~unique_ptr(); return;
        case kind::expr_identifier: as_identifier.~unique_ptr(); return;
        case kind::expr_animtree: as_animtree.~unique_ptr(); return;
        case kind::expr_animation: as_animation.~unique_ptr(); return;
        case kind::expr_level: as_level.~unique_ptr(); return;
        case kind::expr_anim: as_anim.~unique_ptr(); return;
        case kind::expr_self: as_self.~unique_ptr(); return;
        case kind::expr_game: as_game.~unique_ptr(); return;
        case kind::expr_undefined: as_undefined.~unique_ptr(); return;
        case kind::expr_empty_array: as_empty_array.~unique_ptr(); return;
        case kind::expr_paren: as_paren.~unique_ptr(); return;
        case kind::expr_size: as_size.~unique_ptr(); return;
        case kind::expr_field: as_field.~unique_ptr(); return;
        case kind::expr_array: as_array.~unique_ptr(); return;
        case kind::expr_reference: as_reference.~unique_ptr(); return;
        case kind::expr_getnextarraykey: as_getnextarraykey.~unique_ptr(); return;
        case kind::expr_getfirstarraykey: as_getfirstarraykey.~unique_ptr(); return;
        case kind::expr_getdvarcoloralpha: as_getdvarcoloralpha.~unique_ptr(); return;
        case kind::expr_getdvarcolorblue: as_getdvarcolorblue.~unique_ptr(); return;
        case kind::expr_getdvarcolorgreen: as_getdvarcolorgreen.~unique_ptr(); return;
        case kind::expr_getdvarcolorred: as_getdvarcolorred.~unique_ptr(); return;
        case kind::expr_getdvarvector: as_getdvarvector.~unique_ptr(); return;
        case kind::expr_getdvarfloat: as_getdvarfloat.~unique_ptr(); return;
        case kind::expr_getdvarint: as_getdvarint.~unique_ptr(); return;
        case kind::expr_getdvar: as_getdvar.~unique_ptr(); return;
        case kind::expr_gettime: as_gettime.~unique_ptr(); return;
        case kind::expr_abs: as_abs.~unique_ptr(); return;
        case kind::expr_vectortoangles: as_vectortoangles.~unique_ptr(); return;
        case kind::expr_angleclamp180: as_angleclamp180.~unique_ptr(); return;
        case kind::expr_anglestoforward: as_anglestoforward.~unique_ptr(); return;
        case kind::expr_anglestoright: as_anglestoright.~unique_ptr(); return;
        case kind::expr_anglestoup: as_anglestoup.~unique_ptr(); return;
        case kind::expr_vectorscale: as_vectorscale.~unique_ptr(); return;
        case kind::expr_isdefined: as_isdefined.~unique_ptr(); return;
        case kind::expr_arguments: as_arguments.~unique_ptr(); return;
        case kind::expr_parameters: as_parameters.~unique_ptr(); return;
        case kind::expr_pointer: as_pointer.~unique_ptr(); return;
        case kind::expr_function: as_function.~unique_ptr(); return;
        case kind::expr_method: as_method.~unique_ptr(); return;
        case kind::expr_call: as_call.~unique_ptr(); return;
        case kind::expr_complement: as_complement.~unique_ptr(); return;
        case kind::expr_negate: as_negate.~unique_ptr(); return;
        case kind::expr_not: as_not.~unique_ptr(); return;
        case kind::expr_add: as_add.~unique_ptr(); return;
        case kind::expr_sub: as_sub.~unique_ptr(); return;
        case kind::expr_mul: as_mul.~unique_ptr(); return;
        case kind::expr_div: as_div.~unique_ptr(); return;
        case kind::expr_mod: as_mod.~unique_ptr(); return;
        case kind::expr_shift_left: as_shift_left.~unique_ptr(); return;
        case kind::expr_shift_right: as_shift_right.~unique_ptr(); return;
        case kind::expr_bitwise_or: as_bitwise_or.~unique_ptr(); return;
        case kind::expr_bitwise_and: as_bitwise_and.~unique_ptr(); return;
        case kind::expr_bitwise_exor: as_bitwise_exor.~unique_ptr(); return;
        case kind::expr_equality: as_equality.~unique_ptr(); return;
        case kind::expr_inequality: as_inequality.~unique_ptr(); return;
        case kind::expr_less_equal: as_less_equal.~unique_ptr(); return;
        case kind::expr_greater_equal: as_greater_equal.~unique_ptr(); return;
        case kind::expr_less: as_less.~unique_ptr(); return;
        case kind::expr_greater: as_greater.~unique_ptr(); return;
        case kind::expr_or: as_or.~unique_ptr(); return;
        case kind::expr_and: as_and.~unique_ptr(); return;
        case kind::expr_ternary: as_ternary.~unique_ptr(); return;
        case kind::expr_increment: as_increment.~unique_ptr(); return;
        case kind::expr_decrement: as_decrement.~unique_ptr(); return;
        case kind::expr_assign_equal: as_assign_equal.~unique_ptr(); return;
        case kind::expr_assign_add: as_assign_add.~unique_ptr(); return;
        case kind::expr_assign_sub: as_assign_sub.~unique_ptr(); return;
        case kind::expr_assign_mul: as_assign_mul.~unique_ptr(); return;
        case kind::expr_assign_div: as_assign_div.~unique_ptr(); return;
        case kind::expr_assign_mod: as_assign_mod.~unique_ptr(); return;
        case kind::expr_assign_shift_left: as_assign_shift_left.~unique_ptr(); return;
        case kind::expr_assign_shift_right: as_assign_shift_right.~unique_ptr(); return;
        case kind::expr_assign_bitwise_or: as_assign_bw_or.~unique_ptr(); return;
        case kind::expr_assign_bitwise_and: as_assign_bw_and.~unique_ptr(); return;
        case kind::expr_assign_bitwise_exor: as_assign_bw_exor.~unique_ptr(); return;
        default: return;
    }
}

bool operator!=(const expr& lhs, kind rhs)
{
    return lhs.as_node->kind() != rhs;
}

bool operator==(const expr& lhs, kind rhs)
{
    return *lhs.as_node == rhs;
}

bool operator==(const expr& lhs, const expr& rhs)
{
    if (!(*lhs.as_node == *rhs.as_node)) return false;

    switch(lhs.as_node->kind())
    {
        case kind::expr_true: return *lhs.as_true == *rhs.as_true;
        case kind::expr_false: return *lhs.as_false == *rhs.as_false;
        case kind::expr_integer: return *lhs.as_integer == *rhs.as_integer;
        case kind::expr_float: return *lhs.as_float == *rhs.as_float;
        case kind::expr_vector: return *lhs.as_vector == *rhs.as_vector;
        case kind::expr_hash: return *lhs.as_hash == *rhs.as_hash;
        case kind::expr_string: return *lhs.as_string == *rhs.as_string;
        case kind::expr_istring: return *lhs.as_istring == *rhs.as_istring;
        case kind::expr_path: return *lhs.as_path == *rhs.as_path;
        case kind::expr_identifier: return *lhs.as_identifier == *rhs.as_identifier;
        case kind::expr_animtree: return *lhs.as_animtree == *rhs.as_animtree;
        case kind::expr_animation: return *lhs.as_animation == *rhs.as_animation;
        case kind::expr_level: return *lhs.as_level == *rhs.as_level;
        case kind::expr_anim: return *lhs.as_anim == *rhs.as_anim;
        case kind::expr_self: return *lhs.as_self == *rhs.as_self;
        case kind::expr_game: return *lhs.as_game == *rhs.as_game;
        case kind::expr_undefined: return *lhs.as_undefined == *rhs.as_undefined;
        case kind::expr_empty_array: return *lhs.as_empty_array == *rhs.as_empty_array;
        case kind::expr_paren: return *lhs.as_paren == *rhs.as_paren;
        case kind::expr_size: return *lhs.as_size == *rhs.as_size;
        case kind::expr_field: return *lhs.as_field == *rhs.as_field;
        case kind::expr_array: return *lhs.as_array == *rhs.as_array;
        default: return false;
    }
}

auto expr::loc() const -> location
{
    return as_node->loc();
}

auto expr::kind() const -> ast::kind
{
    return as_node->kind();
}

auto expr::print() const -> std::string
{
    return as_node->print();
}

stmt::stmt() : as_node(nullptr) {}

stmt::stmt(std::unique_ptr<node> value) : as_node(std::move(value)) {}

stmt::stmt(stmt&& value)
{
    new(&as_node) std::unique_ptr<node>(std::move(value.as_node));
}

stmt& stmt::operator=(stmt&& value)
{
    new(&as_node) std::unique_ptr<node>(std::move(value.as_node));
    return *(stmt*)&as_node;
}

stmt::~stmt()
{
    if (as_node == nullptr) return;

    switch (as_node->kind())
    {
        case kind::null: as_node.~unique_ptr(); return;
        case kind::stmt_list: as_list.~unique_ptr(); return;
        case kind::stmt_dev: as_dev.~unique_ptr(); return;
        case kind::stmt_expr: as_expr.~unique_ptr(); return;
        case kind::stmt_call: as_call.~unique_ptr(); return;
        case kind::stmt_const: as_const.~unique_ptr(); return;
        case kind::stmt_assign: as_assign.~unique_ptr(); return;
        case kind::stmt_endon: as_endon.~unique_ptr(); return;
        case kind::stmt_notify: as_notify.~unique_ptr(); return;
        case kind::stmt_realwait: as_realwait.~unique_ptr(); return;
        case kind::stmt_wait: as_wait.~unique_ptr(); return;
        case kind::stmt_waittill: as_waittill.~unique_ptr(); return;
        case kind::stmt_waittillmatch: as_waittillmatch.~unique_ptr(); return;
        case kind::stmt_waittillframeend: as_waittillframeend.~unique_ptr(); return;
        case kind::stmt_if: as_if.~unique_ptr(); return;
        case kind::stmt_ifelse: as_ifelse.~unique_ptr(); return;
        case kind::stmt_while: as_while.~unique_ptr(); return;
        case kind::stmt_dowhile: as_dowhile.~unique_ptr(); return;
        case kind::stmt_for: as_for.~unique_ptr(); return;
        case kind::stmt_foreach: as_foreach.~unique_ptr(); return;
        case kind::stmt_switch: as_switch.~unique_ptr(); return;
        case kind::stmt_case: as_case.~unique_ptr(); return;
        case kind::stmt_default: as_default.~unique_ptr(); return;
        case kind::stmt_break: as_break.~unique_ptr(); return;
        case kind::stmt_continue: as_continue.~unique_ptr(); return;
        case kind::stmt_return: as_return.~unique_ptr(); return;
        case kind::stmt_breakpoint: as_breakpoint.~unique_ptr(); return;
        case kind::stmt_prof_begin: as_prof_begin.~unique_ptr(); return;
        case kind::stmt_prof_end: as_prof_end.~unique_ptr(); return;
        case kind::asm_loc: as_loc.~unique_ptr(); return;
        case kind::asm_jump: as_jump.~unique_ptr(); return;
        case kind::asm_jump_back: as_jump_back.~unique_ptr(); return;
        case kind::asm_jump_cond: as_cond.~unique_ptr(); return;
        case kind::asm_jump_true_expr: as_jump_true_expr.~unique_ptr(); return;
        case kind::asm_jump_false_expr: as_jump_false_expr.~unique_ptr(); return;
        case kind::asm_switch: as_asm_switch.~unique_ptr(); return;
        case kind::asm_endswitch: as_asm_endswitch.~unique_ptr(); return;
        case kind::asm_prescriptcall: as_asm_prescriptcall.~unique_ptr(); return;
        case kind::asm_voidcodepos: as_asm_voidcodepos.~unique_ptr(); return;
        case kind::asm_dev: as_asm_dev.~unique_ptr(); return;
        default: return;
    }
}

bool operator==(const stmt& lhs, kind rhs)
{
    return *lhs.as_node == rhs;
}

auto stmt::loc() const -> location
{
    return as_node->loc();
}

auto stmt::kind() const -> ast::kind
{
    return as_node->kind();
}

auto stmt::print() const -> std::string
{
    return as_node->print();
}

decl::decl() : as_node(nullptr) {}

decl::decl(std::unique_ptr<node> value) : as_node(std::move(value)) {}

decl::decl(decl&& value)
{
    new(&as_node) std::unique_ptr<node>(std::move(value.as_node));
}

decl& decl::operator=(decl&& value)
{
    new(&as_node) std::unique_ptr<node>(std::move(value.as_node));
    return *(decl*)&as_node;
}

decl::~decl()
{
    if (as_node == nullptr) return;

    switch (as_node->kind())
    {
        case kind::null: as_node.~unique_ptr(); return;
        case kind::decl_dev_begin: as_dev_begin.~unique_ptr(); return;
        case kind::decl_dev_end: as_dev_end.~unique_ptr(); return;
        case kind::decl_thread: as_thread.~unique_ptr(); return;
        case kind::decl_usingtree: as_usingtree.~unique_ptr(); return;
        default: return;
    }
}

bool operator==(const decl& lhs, kind rhs)
{
    return *lhs.as_node == rhs;
}

auto decl::loc() const -> location
{
    return as_node->loc();
}

auto decl::kind() const -> ast::kind
{
    return as_node->kind();
}

auto decl::print() const -> std::string
{
    return as_node->print();
}

} // namespace xsk::arc::ast
