/* 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.
*/

%require "3.7"
%skeleton "lalr1.cc"
%language "c++"
%output "parser.cpp"
%defines "parser.hpp"
%define api.prefix {ARC}
%define api.namespace {xsk::arc}
%define api.location.type {xsk::arc::location}
%define api.value.type variant
%define api.token.constructor
%define api.token.raw
%define parse.assert
%define parse.trace
%define parse.error detailed
%define parse.lac full
%locations
%lex-param   { xsk::arc::context const* ctx_ }
%lex-param   { xsk::arc::preprocessor& ppr }
%parse-param { xsk::arc::context const* ctx_ }
%parse-param { xsk::arc::preprocessor& ppr }
%parse-param { xsk::arc::program::ptr& ast }
%parse-param { std::uint32_t index }

%code requires
{
#ifdef _MSC_VER
#pragma warning(disable:4065)
#pragma warning(disable:4127)
#endif
#include "context.hpp"
namespace xsk::arc { class preprocessor; }
}

%code top
{
#include "xsk/stdinc.hpp"
#include "xsk/arc/parser.hpp"
#include "xsk/arc/preprocessor.hpp"
using namespace xsk::arc;
namespace xsk::arc
{ 
    auto ARClex(context const* ctx_, preprocessor& ppr) -> parser::symbol_type;
    auto parse_switch(stmt_switch& stm) -> void;
}
}

%token HASH             "#"
%token DEVBEGIN         "/#"
%token DEVEND           "#/"
%token INLINE           "#inline"
%token INCLUDE          "#include"
%token USINGTREE        "#using_animtree"
%token ANIMTREE         "#animtree"
%token AUTOEXEC         "autoexec"
%token CODECALL         "codecall"
%token PRIVATE          "private"
%token ENDON            "endon"
%token NOTIFY           "notify"
%token WAIT             "wait"
%token WAITTILL         "waittill"
%token WAITTILLMATCH    "waittillmatch"
%token WAITTILLFRAMEEND "waittillframeend"
%token IF               "if"
%token ELSE             "else"
%token DO               "do"
%token WHILE            "while"
%token FOR              "for"
%token FOREACH          "foreach"
%token IN               "in"
%token SWITCH           "switch"
%token CASE             "case"
%token DEFAULT          "default"
%token BREAK            "break"
%token CONTINUE         "continue"
%token RETURN           "return"
%token PROFBEGIN        "prof_begin"
%token PROFEND          "prof_end"
%token THREAD           "thread"
%token TRUE             "true"
%token FALSE            "false"
%token UNDEFINED        "undefined"
%token SIZE             "size"
%token GAME             "game"
%token SELF             "self"
%token ANIM             "anim"
%token LEVEL            "level"
%token CONST            "const"
%token GETNEXTARRAYKEY  "getnextarraykey"
%token GETFIRSTARRAYKEY "getfirstarraykey"
%token GETDVARCOLORALPHA "getdvarcoloralpha"
%token GETDVARCOLORBLUE  "getdvarcolorblue"
%token GETDVARCOLORGREEN "getdvarcolorgreen"
%token GETDVARCOLORRED  "getdvarcolorred"
%token GETDVARVECTOR    "getdvarvector"
%token GETDVARFLOAT     "getdvarfloat"
%token GETDVARINT       "getdvarint"
%token GETDVAR          "getdvar"
%token GETTIME          "gettime"
%token ABS              "abs"
%token VECTORTOANGLES   "vectortoangles"
%token ANGLECLAMP180    "angleclamp180"
%token ANGLESTOFORWARD  "anglestoforward"
%token ANGLESTORIGHT    "anglestoright"
%token ANGLESTOUP       "anglestoup"
%token VECTORSCALE      "vectorscale"
%token ISDEFINED        "isdefined"
%token LPAREN           "("
%token RPAREN           ")"
%token LBRACE           "{"
%token RBRACE           "}"
%token LBRACKET         "["
%token RBRACKET         "]"
%token COMMA            ","
%token DOT              "."
%token DOUBLEDOT        ".."
%token ELLIPSIS         "..."
%token DOUBLECOLON      "::"
%token COLON            ":"
%token SEMICOLON        ";"
%token QMARK            "?"
%token INCREMENT        "++"
%token DECREMENT        "--"
%token LSHIFT           "<<"
%token RSHIFT           ">>"
%token OR               "||"
%token AND              "&&"
%token EQUALITY         "=="
%token INEQUALITY       "!="
%token LESS_EQUAL       "<="
%token GREATER_EQUAL    ">="
%token LESS             "<"
%token GREATER          ">"
%token NOT              "!"
%token COMPLEMENT       "~"
%token ASSIGN           "="
%token ASSIGN_ADD       "+="
%token ASSIGN_SUB       "-="
%token ASSIGN_MUL       "*="
%token ASSIGN_DIV       "/="
%token ASSIGN_MOD       "%="
%token ASSIGN_BW_OR     "|="
%token ASSIGN_BW_AND    "&="
%token ASSIGN_BW_EXOR   "^="
%token ASSIGN_RSHIFT    ">>="
%token ASSIGN_LSHIFT    "<<="
%token BITWISE_OR       "|"
%token BITWISE_AND      "&"
%token BITWISE_EXOR     "^"
%token ADD              "+"
%token SUB              "-"
%token MUL              "*"
%token DIV              "/"
%token MOD              "%"
%token <std::string> PATH       "path"
%token <std::string> IDENTIFIER "identifier"
%token <std::string> STRING     "string literal"
%token <std::string> ISTRING    "localized string"
%token <std::string> HASHSTR    "hash string"
%token <std::string> FLOAT      "float"
%token <std::string> INTEGER    "integer"

%type <program::ptr>               program
%type <include::ptr>               include
%type <decl::ptr>                  declaration
%type <decl_usingtree::ptr>        decl_usingtree
%type <decl_function::ptr>         decl_function
%type <stmt::ptr>                  stmt
%type <stmt::ptr>                  stmt_or_dev
%type <stmt_list::ptr>             stmt_list
%type <stmt_list::ptr>             stmt_or_dev_list
%type <stmt_dev::ptr>              stmt_dev
%type <stmt_comp::ptr>             stmt_comp
%type <stmt_expr::ptr>             stmt_expr
%type <stmt_expr::ptr>             stmt_call
%type <stmt_expr::ptr>             stmt_const
%type <stmt_expr::ptr>             stmt_assign
%type <stmt_endon::ptr>            stmt_endon
%type <stmt_notify::ptr>           stmt_notify
%type <stmt_wait::ptr>             stmt_wait
%type <stmt_waittill::ptr>         stmt_waittill
%type <stmt_waittillmatch::ptr>    stmt_waittillmatch
%type <stmt_waittillframeend::ptr> stmt_waittillframeend
%type <stmt_if::ptr>               stmt_if
%type <stmt_ifelse::ptr>           stmt_ifelse
%type <stmt_while::ptr>            stmt_while
%type <stmt_dowhile::ptr>          stmt_dowhile
%type <stmt_for::ptr>              stmt_for
%type <stmt_foreach::ptr>          stmt_foreach
%type <stmt_switch::ptr>           stmt_switch
%type <stmt_case::ptr>             stmt_case
%type <stmt_default::ptr>          stmt_default
%type <stmt_break::ptr>            stmt_break
%type <stmt_continue::ptr>         stmt_continue
%type <stmt_return::ptr>           stmt_return
%type <stmt_prof_begin::ptr>       stmt_prof_begin
%type <stmt_prof_end::ptr>         stmt_prof_end
%type <expr::ptr>                  expr
%type <expr::ptr>                  expr_or_empty
%type <expr::ptr>                  expr_increment
%type <expr::ptr>                  expr_decrement
%type <expr::ptr>                  expr_assign
%type <expr::ptr>                  expr_ternary
%type <expr::ptr>                  expr_binary
%type <expr::ptr>                  expr_primitive
%type <expr_complement::ptr>       expr_complement
%type <expr_negate::ptr>           expr_negate
%type <expr_not::ptr>              expr_not
%type <expr_call::ptr>             expr_call
%type <expr_method::ptr>           expr_method
%type <call::ptr>                  expr_function
%type <call::ptr>                  expr_pointer
%type <expr_parameters::ptr>       expr_parameters
%type <expr::ptr>                  expr_parameters_default
%type <expr_arguments::ptr>        expr_arguments
%type <expr_arguments::ptr>        expr_arguments_no_empty
%type <expr_getnextarraykey::ptr>   expr_getnextarraykey
%type <expr_getfirstarraykey::ptr>  expr_getfirstarraykey
%type <expr_getdvarcoloralpha::ptr> expr_getdvarcoloralpha
%type <expr_getdvarcolorblue::ptr>  expr_getdvarcolorblue
%type <expr_getdvarcolorgreen::ptr> expr_getdvarcolorgreen
%type <expr_getdvarcolorred::ptr>   expr_getdvarcolorred
%type <expr_getdvarvector::ptr>     expr_getdvarvector
%type <expr_getdvarfloat::ptr>      expr_getdvarfloat
%type <expr_getdvarint::ptr>        expr_getdvarint
%type <expr_getdvar::ptr>           expr_getdvar
%type <expr_gettime::ptr>           expr_gettime
%type <expr_abs::ptr>               expr_abs
%type <expr_vectortoangles::ptr>    expr_vectortoangles
%type <expr_angleclamp180::ptr>     expr_angleclamp180
%type <expr_anglestoforward::ptr>   expr_anglestoforward
%type <expr_anglestoright::ptr>     expr_anglestoright
%type <expr_anglestoup::ptr>        expr_anglestoup
%type <expr_vectorscale::ptr>       expr_vectorscale
%type <expr_isdefined::ptr>        expr_isdefined
%type <expr_reference::ptr>        expr_reference
%type <expr_array::ptr>            expr_array
%type <expr_field::ptr>            expr_field
%type <expr_size::ptr>             expr_size
%type <expr_paren::ptr>            expr_paren
%type <expr::ptr>                  expr_object
%type <expr_empty_array::ptr>      expr_empty_array
%type <expr_undefined::ptr>        expr_undefined
%type <expr_game::ptr>             expr_game
%type <expr_self::ptr>             expr_self
%type <expr_anim::ptr>             expr_anim
%type <expr_level::ptr>            expr_level
%type <expr_animation::ptr>        expr_animation
%type <expr_animtree::ptr>         expr_animtree
%type <expr_identifier::ptr>       expr_identifier_nosize
%type <expr_identifier::ptr>       expr_identifier
%type <expr_path::ptr>             expr_path
%type <expr_istring::ptr>          expr_istring
%type <expr_string::ptr>           expr_string
%type <expr_vector::ptr>           expr_vector
%type <expr_hash::ptr>             expr_hash
%type <expr_float::ptr>            expr_float
%type <expr_integer::ptr>          expr_integer
%type <expr_false::ptr>            expr_false
%type <expr_true::ptr>             expr_true

%nonassoc SIZEOF
%nonassoc RBRACKET
%nonassoc THEN
%nonassoc ELSE
%nonassoc INCREMENT DECREMENT

%precedence TERN
%right QMARK
%left OR
%left AND
%left BITWISE_OR
%left BITWISE_EXOR
%left BITWISE_AND
%left EQUALITY INEQUALITY
%left LESS GREATER LESS_EQUAL GREATER_EQUAL
%left LSHIFT RSHIFT
%left ADD SUB
%left MUL DIV MOD
%right NOT COMPLEMENT

%precedence NEG
%precedence ANIMREF
%precedence PREINC PREDEC
%precedence POSTINC POSTDEC

%start root

%%

root
    : program { ast = std::move($1); }
    |         { ast = program::make(@$); }
    ;

program
    : program SEMICOLON
        { $$ = std::move($1); }
    | program inline
        { $$ = std::move($1); }
    | program include
        { $$ = std::move($1); $$->includes.push_back(std::move($2)); }
    | program declaration
        { $$ = std::move($1); $$->declarations.push_back(std::move($2)); }
    | SEMICOLON
        { $$ = program::make(@$); }
    | inline
        { $$ = program::make(@$); }
    | include
        { $$ = program::make(@$); $$->includes.push_back(std::move($1)); }
    | declaration
        { $$ = program::make(@$); $$->declarations.push_back(std::move($1)); }
    ;

inline
    : INLINE expr_path SEMICOLON { ppr.push_header($2->value); }
    ;

include
    : INCLUDE expr_path SEMICOLON
        { $$ = include::make(@$, std::move($2)); }
    ;

declaration
    : DEVBEGIN          { $$ = decl_dev_begin::make(@$); }
    | DEVEND            { $$ = decl_dev_end::make(@$); }
    | decl_usingtree    { $$ = std::move($1); }
    | decl_function     { $$ = std::move($1); }
    ;

decl_usingtree
    : USINGTREE LPAREN expr_string RPAREN SEMICOLON
        { ppr.ban_header(@$); $$ = decl_usingtree::make(@$, std::move($3)); }
    ;

decl_function
    : expr_identifier LPAREN expr_parameters RPAREN stmt_comp
        { ppr.ban_header(@$); $$ = decl_function::make(@$, expr_identifier::make(@$, ""), std::move($1), std::move($3), std::move($5), export_flags::export_none); }
    | AUTOEXEC expr_identifier LPAREN expr_parameters RPAREN stmt_comp
        { ppr.ban_header(@$); $$ = decl_function::make(@$, expr_identifier::make(@$, ""), std::move($2), std::move($4), std::move($6), export_flags::export_autoexec); }
    | CODECALL expr_identifier LPAREN expr_parameters RPAREN stmt_comp
        { ppr.ban_header(@$); $$ = decl_function::make(@$, expr_identifier::make(@$, ""), std::move($2), std::move($4), std::move($6), export_flags::export_codecall); }
    | PRIVATE expr_identifier LPAREN expr_parameters RPAREN stmt_comp
        { ppr.ban_header(@$); $$ = decl_function::make(@$, expr_identifier::make(@$, ""), std::move($2), std::move($4), std::move($6), export_flags::export_private2); }
    ;

stmt
    : stmt_comp              { $$ = std::move($1); }
    | stmt_call              { $$ = std::move($1); }
    | stmt_const             { $$ = std::move($1); }
    | stmt_assign            { $$ = std::move($1); }
    | stmt_endon             { $$ = std::move($1); }
    | stmt_notify            { $$ = std::move($1); }
    | stmt_wait              { $$ = std::move($1); }
    | stmt_waittill          { $$ = std::move($1); }
    | stmt_waittillmatch     { $$ = std::move($1); }
    | stmt_waittillframeend  { $$ = std::move($1); }
    | stmt_if                { $$ = std::move($1); }
    | stmt_ifelse            { $$ = std::move($1); }
    | stmt_while             { $$ = std::move($1); }
    | stmt_dowhile           { $$ = std::move($1); }
    | stmt_for               { $$ = std::move($1); }
    | stmt_foreach           { $$ = std::move($1); }
    | stmt_switch            { $$ = std::move($1); }
    | stmt_case              { $$ = std::move($1); }
    | stmt_default           { $$ = std::move($1); }
    | stmt_break             { $$ = std::move($1); }
    | stmt_continue          { $$ = std::move($1); }
    | stmt_return            { $$ = std::move($1); }
    | stmt_prof_begin        { $$ = std::move($1); }
    | stmt_prof_end          { $$ = std::move($1); }
    ;

stmt_or_dev
    : stmt     { $$ = std::move($1); }
    | stmt_dev { $$ = std::move($1); }
    ;

stmt_list
    : stmt_list stmt
        { $$ = std::move($1); $$->list.push_back(std::move($2)); }
    | stmt
        { $$ = stmt_list::make(@$); $$->list.push_back(std::move($1)); }
    | stmt_list SEMICOLON
        { $$ = std::move($1); }
    | SEMICOLON
        { $$ = stmt_list::make(@$); }
    ;

stmt_or_dev_list
    : stmt_or_dev_list stmt_or_dev
        { $$ = std::move($1); $$->list.push_back(std::move($2)); }
    | stmt_or_dev
        { $$ = stmt_list::make(@$); $$->list.push_back(std::move($1)); }
    | stmt_or_dev_list SEMICOLON
        { $$ = std::move($1); }
    | SEMICOLON
        { $$ = stmt_list::make(@$); }
    ;

stmt_dev
    : DEVBEGIN stmt_list DEVEND { $$ = stmt_dev::make(@$, std::move($2)); }
    | DEVBEGIN DEVEND { $$ = stmt_dev::make(@$, stmt_list::make(@$)); }
    ;

stmt_comp
    : LBRACE stmt_or_dev_list RBRACE { $$ = stmt_comp::make(@$, std::move($2)); }
    | LBRACE RBRACE { $$ = stmt_comp::make(@$, stmt_list::make(@$)); }
    ;

stmt_expr
    : expr_assign
        { $$ = stmt_expr::make(@$, std::move($1)); }
    | expr_increment
        { $$ = stmt_expr::make(@$, std::move($1)); }
    | expr_decrement
        { $$ = stmt_expr::make(@$, std::move($1)); }
    |
        { $$ = stmt_expr::make(@$, expr_empty::make(@$)); }
    ;

stmt_call
    : expr_call SEMICOLON
        { $$ = stmt_expr::make(@$, std::move($1)); }
    | expr_method SEMICOLON
        { $$ = stmt_expr::make(@$, std::move($1)); }
    ;

stmt_const
    : CONST expr_identifier ASSIGN expr SEMICOLON
        { $$ = stmt_expr::make(@$, expr_const::make(@$, std::move($2), std::move($4))); }
    ;

stmt_assign
    : expr_assign SEMICOLON
        { $$ = stmt_expr::make(@$, std::move($1)); }
    | expr_increment SEMICOLON
        { $$ = stmt_expr::make(@$, std::move($1)); }
    | expr_decrement SEMICOLON
        { $$ = stmt_expr::make(@$, std::move($1)); }
    ;

stmt_endon
    : expr_object ENDON LPAREN expr RPAREN SEMICOLON
        { $$ = stmt_endon::make(@$, std::move($1), std::move($4)); }
    ;

stmt_notify
    : expr_object NOTIFY LPAREN expr COMMA expr_arguments_no_empty RPAREN SEMICOLON
        { $$ = stmt_notify::make(@$, std::move($1), std::move($4), std::move($6)); }
    | expr_object NOTIFY LPAREN expr RPAREN SEMICOLON
        { $$ = stmt_notify::make(@$, std::move($1), std::move($4), expr_arguments::make(@$)); }
    ;

stmt_wait
    : WAIT expr SEMICOLON
        { $$ = stmt_wait::make(@$, std::move($2)); }
    ;

stmt_waittill
    : expr_object WAITTILL LPAREN expr COMMA expr_arguments_no_empty RPAREN SEMICOLON
        { $$ = stmt_waittill::make(@$, std::move($1), std::move($4), std::move($6)); }
    | expr_object WAITTILL LPAREN expr RPAREN SEMICOLON
        { $$ = stmt_waittill::make(@$, std::move($1), std::move($4), expr_arguments::make(@$)); }
    ;

stmt_waittillmatch
    : expr_object WAITTILLMATCH LPAREN expr COMMA expr_arguments_no_empty RPAREN SEMICOLON
        { $$ = stmt_waittillmatch::make(@$, std::move($1), std::move($4), std::move($6)); }
    | expr_object WAITTILLMATCH LPAREN expr RPAREN SEMICOLON
        { $$ = stmt_waittillmatch::make(@$, std::move($1), std::move($4), expr_arguments::make(@$)); }
    ;

stmt_waittillframeend
    : WAITTILLFRAMEEND SEMICOLON
        { $$ = stmt_waittillframeend::make(@$); }
    ;

stmt_if
    : IF LPAREN expr RPAREN stmt %prec THEN
        { $$ = stmt_if::make(@$, std::move($3), std::move($5)); }
    ;

stmt_ifelse
    : IF LPAREN expr RPAREN stmt ELSE stmt
        { $$ = stmt_ifelse::make(@$, std::move($3), std::move($5), std::move($7)); }
    ;

stmt_while
    : WHILE LPAREN expr RPAREN stmt
        { $$ = stmt_while::make(@$, std::move($3), std::move($5)); }
    ;

stmt_dowhile
    : DO stmt WHILE LPAREN expr RPAREN SEMICOLON
        { $$ = stmt_dowhile::make(@$, std::move($5), std::move($2)); }
    ;

stmt_for
    : FOR LPAREN stmt_expr SEMICOLON expr_or_empty SEMICOLON stmt_expr RPAREN stmt
        { $$ = stmt_for::make(@$, std::move($3), std::move($5), std::move($7), std::move($9)); }
    ;

stmt_foreach
    : FOREACH LPAREN expr_identifier IN expr RPAREN stmt
        {
            auto array = expr_identifier::make(@$, fmt::format("_a{}", ++index));
            auto key = expr_identifier::make(@$, fmt::format("_k{}", ++index));
            $$ = stmt_foreach::make(@$, std::move($5), std::move($3), std::move(array), std::move(key), std::move($7), false);
        }
    | FOREACH LPAREN expr_identifier COMMA expr_identifier IN expr RPAREN stmt
        {
            auto array = expr_identifier::make(@$, fmt::format("_a{}", ++index));
            $$ = stmt_foreach::make(@$, std::move($7), std::move($5), std::move(array), std::move($3), std::move($9), true);
        }
    ;

stmt_switch
    : SWITCH LPAREN expr RPAREN stmt_comp
        { $$ = stmt_switch::make(@$, std::move($3), std::move($5)); 
          parse_switch(*$$);
        }
    ;

stmt_case
    : CASE expr_integer COLON
        { $$ = stmt_case::make(@$, std::move($2), stmt_list::make(@$)); }
    | CASE expr_string COLON
        { $$ = stmt_case::make(@$, std::move($2), stmt_list::make(@$)); }
    ;

stmt_default
    : DEFAULT COLON
        { $$ = stmt_default::make(@$, stmt_list::make(@$)); }
    ;

stmt_break
    : BREAK SEMICOLON
        { $$ = stmt_break::make(@$); }
    ;

stmt_continue
    : CONTINUE SEMICOLON
        { $$ = stmt_continue::make(@$); }
    ;

stmt_return
    : RETURN expr SEMICOLON
        { $$ = stmt_return::make(@$, std::move($2)); }
    | RETURN SEMICOLON
        { $$ = stmt_return::make(@$, expr_empty::make(@$)); }
    ;

stmt_prof_begin
    : PROFBEGIN LPAREN expr_arguments RPAREN SEMICOLON
        { $$ = stmt_prof_begin::make(@$, std::move($3)); }
    ;

stmt_prof_end
    : PROFEND LPAREN expr_arguments RPAREN SEMICOLON
        { $$ = stmt_prof_end::make(@$, std::move($3)); }
    ;

expr
    : expr_ternary   { $$ = std::move($1); }
    | expr_binary    { $$ = std::move($1); }
    | expr_primitive { $$ = std::move($1); }
    ;

expr_or_empty
    : expr           { $$ = std::move($1); }
    |                { $$ = expr_empty::make(@$); }
    ;

expr_increment
    : INCREMENT expr_object %prec PREINC
        { $$ = expr_increment::make(@$, std::move($2), true); }
    | expr_object INCREMENT %prec POSTINC
        { $$ = expr_increment::make(@$, std::move($1), false); }
    ;

expr_decrement
    : DECREMENT expr_object %prec PREDEC
        { $$ = expr_decrement::make(@$, std::move($2), true); }
    | expr_object DECREMENT %prec POSTDEC
        { $$ = expr_decrement::make(@$, std::move($1), false); }
    ;

expr_assign
    : expr_object ASSIGN expr
        { $$ = expr_assign::make(@$, std::move($1), std::move($3), expr_assign::op::eq); }
    | expr_object ASSIGN_BW_OR expr
        { $$ = expr_assign::make(@$, std::move($1), std::move($3), expr_assign::op::bwor); }
    | expr_object ASSIGN_BW_AND expr
        { $$ = expr_assign::make(@$, std::move($1), std::move($3), expr_assign::op::bwand); }
    | expr_object ASSIGN_BW_EXOR expr
        { $$ = expr_assign::make(@$, std::move($1), std::move($3), expr_assign::op::bwexor); }
    | expr_object ASSIGN_LSHIFT expr
        { $$ = expr_assign::make(@$, std::move($1), std::move($3), expr_assign::op::shl); }
    | expr_object ASSIGN_RSHIFT expr
        { $$ = expr_assign::make(@$, std::move($1), std::move($3), expr_assign::op::shr); }
    | expr_object ASSIGN_ADD expr
        { $$ = expr_assign::make(@$, std::move($1), std::move($3), expr_assign::op::add); }
    | expr_object ASSIGN_SUB expr
        { $$ = expr_assign::make(@$, std::move($1), std::move($3), expr_assign::op::sub); }
    | expr_object ASSIGN_MUL expr
        { $$ = expr_assign::make(@$, std::move($1), std::move($3), expr_assign::op::mul); }
    | expr_object ASSIGN_DIV expr
        { $$ = expr_assign::make(@$, std::move($1), std::move($3), expr_assign::op::div); }
    | expr_object ASSIGN_MOD expr
        { $$ = expr_assign::make(@$, std::move($1), std::move($3), expr_assign::op::mod); }
    ;

expr_ternary
    : expr QMARK expr COLON expr %prec TERN
        { $$ = expr_ternary::make(@$, std::move($1), std::move($3), std::move($5)); }
    ;

expr_binary
    : expr OR expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::bool_or); }
    | expr AND expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::bool_and); }
    | expr EQUALITY expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::eq); }
    | expr INEQUALITY expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::ne); }
    | expr LESS_EQUAL expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::le); }
    | expr GREATER_EQUAL expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::ge); }
    | expr LESS expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::lt); }
    | expr GREATER expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::gt); }
    | expr BITWISE_OR expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::bwor); }
    | expr BITWISE_AND expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::bwand); }
    | expr BITWISE_EXOR expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::bwexor); }
    | expr LSHIFT expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::shl); }
    | expr RSHIFT expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::shr); }
    | expr ADD expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::add); }
    | expr SUB expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::sub); }
    | expr MUL expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::mul); }
    | expr DIV expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::div); }
    | expr MOD expr
        { $$ = expr_binary::make(@$, std::move($1), std::move($3), expr_binary::op::mod); }
    ;

expr_primitive
    : expr_complement        { $$ = std::move($1); }
    | expr_negate            { $$ = std::move($1); }
    | expr_not               { $$ = std::move($1); }
    | expr_call              { $$ = std::move($1); }
    | expr_method            { $$ = std::move($1); }
    | expr_getnextarraykey   { $$ = std::move($1); }
    | expr_getfirstarraykey  { $$ = std::move($1); }
    | expr_getdvarcoloralpha { $$ = std::move($1); }
    | expr_getdvarcolorblue  { $$ = std::move($1); }
    | expr_getdvarcolorgreen { $$ = std::move($1); }
    | expr_getdvarcolorred   { $$ = std::move($1); }
    | expr_getdvarvector     { $$ = std::move($1); }
    | expr_getdvarfloat      { $$ = std::move($1); }
    | expr_getdvarint        { $$ = std::move($1); }
    | expr_getdvar           { $$ = std::move($1); }
    | expr_gettime           { $$ = std::move($1); }
    | expr_abs               { $$ = std::move($1); }
    | expr_vectortoangles    { $$ = std::move($1); }
    | expr_angleclamp180     { $$ = std::move($1); }
    | expr_anglestoforward   { $$ = std::move($1); }
    | expr_anglestoright     { $$ = std::move($1); }
    | expr_anglestoup        { $$ = std::move($1); }
    | expr_vectorscale       { $$ = std::move($1); }
    | expr_isdefined         { $$ = std::move($1); }
    | expr_reference         { $$ = std::move($1); }
    | expr_array             { $$ = std::move($1); }
    | expr_field             { $$ = std::move($1); }
    | expr_size              { $$ = std::move($1); }
    | expr_paren             { $$ = std::move($1); }
    | expr_empty_array       { $$ = std::move($1); }
    | expr_undefined         { $$ = std::move($1); }
    | expr_game              { $$ = std::move($1); }
    | expr_self              { $$ = std::move($1); }
    | expr_anim              { $$ = std::move($1); }
    | expr_level             { $$ = std::move($1); }
    | expr_animation         { $$ = std::move($1); }
    | expr_animtree          { $$ = std::move($1); }
    | expr_identifier        { $$ = std::move($1); }
    | expr_istring           { $$ = std::move($1); }
    | expr_string            { $$ = std::move($1); }
    | expr_vector            { $$ = std::move($1); }
    | expr_hash              { $$ = std::move($1); }
    | expr_float             { $$ = std::move($1); }
    | expr_integer           { $$ = std::move($1); }
    | expr_false             { $$ = std::move($1); }
    | expr_true              { $$ = std::move($1); }
    ;

expr_complement
    : COMPLEMENT expr
        { $$ = expr_complement::make(@$, std::move($2)); }
    ;

expr_negate
    : SUB expr_identifier %prec NEG
        { $$ = expr_negate::make(@$, std::move($2)); }
    | SUB expr_paren %prec NEG
        { $$ = expr_negate::make(@$, std::move($2)); }
    | SUB expr_array %prec NEG
        { $$ = expr_negate::make(@$, std::move($2)); }
    | SUB expr_field %prec NEG
        { $$ = expr_negate::make(@$, std::move($2)); }
    ;

expr_not
    : NOT expr
        { $$ = expr_not::make(@$, std::move($2)); }
    ;

expr_call
    : expr_function                { $$ = expr_call::make(@$, std::move($1)); }
    | expr_pointer                 { $$ = expr_call::make(@$, std::move($1)); }
    ;

expr_method
    : expr_object expr_function
        {
            if ($1->loc().begin.line != $2->loc().begin.line)
                error($2->loc(), "missing ';' ?");

            $$ = expr_method::make(@$, std::move($1), std::move($2));
        }
    | expr_object expr_pointer
        {
            if ($1->loc().begin.line != $2->loc().begin.line)
                error($2->loc(), "missing ';' ?");

            $$ = expr_method::make(@$, std::move($1), std::move($2));
        }
    ;

expr_function
    : expr_identifier LPAREN expr_arguments RPAREN
        { $$ = expr_function::make(@$, expr_path::make(@$), std::move($1), std::move($3), call::mode::normal); }
    | expr_path DOUBLECOLON expr_identifier LPAREN expr_arguments RPAREN
        { $$ = expr_function::make(@$, std::move($1), std::move($3), std::move($5), call::mode::normal); }
    | THREAD expr_identifier LPAREN expr_arguments RPAREN
        { $$ = expr_function::make(@$, expr_path::make(@$), std::move($2), std::move($4), call::mode::thread); }
    | THREAD expr_path DOUBLECOLON expr_identifier LPAREN expr_arguments RPAREN
        { $$ = expr_function::make(@$, std::move($2), std::move($4), std::move($6), call::mode::thread); }
    ;

expr_pointer
    : LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
        { $$ = expr_pointer::make(@$, std::move($3), std::move($7), call::mode::normal); }
    | THREAD LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
        { $$ = expr_pointer::make(@$, std::move($4), std::move($8), call::mode::thread); }
    ;

expr_parameters
    : expr_parameters COMMA expr_parameters_default
        { $$ = std::move($1); $$->list.push_back(std::move($3)); }
    | expr_parameters COMMA expr_identifier
        { $$ = std::move($1); $$->list.push_back(std::move($3)); }
    | expr_parameters_default
        { $$ = expr_parameters::make(@$); $$->list.push_back(std::move($1)); }
    | expr_identifier
        { $$ = expr_parameters::make(@$); $$->list.push_back(std::move($1)); }
    |
        { $$ = expr_parameters::make(@$); }
    ;

expr_parameters_default
    : expr_identifier ASSIGN expr
        { $$ = expr_assign::make(@$, std::move($1), std::move($3), expr_assign::op::eq); }
    ;

expr_arguments
    : expr_arguments_no_empty
        { $$ = std::move($1); }
    |
        { $$ = expr_arguments::make(@$); }
    ;

expr_arguments_no_empty
    : expr_arguments COMMA expr
        { $$ = std::move($1); $$->list.push_back(std::move($3)); }
    | expr
        { $$ = expr_arguments::make(@$); $$->list.push_back(std::move($1)); }
    ;

expr_getnextarraykey
    : GETNEXTARRAYKEY LPAREN expr COMMA expr RPAREN
        { $$ = expr_getnextarraykey::make(@$, std::move($3), std::move($5)); }
    ;

expr_getfirstarraykey
    : GETFIRSTARRAYKEY LPAREN expr RPAREN
        { $$ = expr_getfirstarraykey::make(@$, std::move($3)); }
    ;

expr_getdvarcoloralpha
    : GETDVARCOLORALPHA LPAREN expr RPAREN
        { $$ = expr_getdvarcoloralpha::make(@$, std::move($3)); }
    ;

expr_getdvarcolorblue
    : GETDVARCOLORBLUE LPAREN expr RPAREN
        { $$ = expr_getdvarcolorblue::make(@$, std::move($3)); }
    ;

expr_getdvarcolorgreen
    : GETDVARCOLORGREEN LPAREN expr RPAREN
        { $$ = expr_getdvarcolorgreen::make(@$, std::move($3)); }
    ;

expr_getdvarcolorred
    : GETDVARCOLORRED LPAREN expr RPAREN
        { $$ = expr_getdvarcolorred::make(@$, std::move($3)); }
    ;

expr_getdvarvector
    : GETDVARVECTOR LPAREN expr RPAREN
        { $$ = expr_getdvarvector::make(@$, std::move($3)); }
    ;

expr_getdvarfloat
    : GETDVARFLOAT LPAREN expr RPAREN
        { $$ = expr_getdvarfloat::make(@$, std::move($3)); }
    ;

expr_getdvarint
    : GETDVARINT LPAREN expr RPAREN
        { $$ = expr_getdvarint::make(@$, std::move($3)); }
    ;

expr_getdvar
    : GETDVAR LPAREN expr RPAREN
        { $$ = expr_getdvar::make(@$, std::move($3)); }
    ;

expr_gettime
    : GETTIME LPAREN RPAREN
        { $$ = expr_gettime::make(@$); }
    ;

expr_abs
    : ABS LPAREN expr RPAREN
        { $$ = expr_abs::make(@$, std::move($3)); }
    ;

expr_vectortoangles
    : VECTORTOANGLES LPAREN expr RPAREN
        { $$ = expr_vectortoangles::make(@$, std::move($3)); }
    ;

expr_angleclamp180
    : ANGLECLAMP180 LPAREN expr RPAREN
        { $$ = expr_angleclamp180::make(@$, std::move($3)); }
    ;

expr_anglestoforward
    : ANGLESTOFORWARD LPAREN expr RPAREN
        { $$ = expr_anglestoforward::make(@$, std::move($3)); }
    ;

expr_anglestoright
    : ANGLESTORIGHT LPAREN expr RPAREN
        { $$ = expr_anglestoright::make(@$, std::move($3)); }
    ;

expr_anglestoup
    : ANGLESTOUP LPAREN expr RPAREN
        { $$ = expr_anglestoup::make(@$, std::move($3)); }
    ;

expr_vectorscale
    : VECTORSCALE LPAREN expr COMMA expr RPAREN
        { $$ = expr_vectorscale::make(@$, std::move($3), std::move($5)); }
    ;

expr_isdefined
    : ISDEFINED LPAREN expr RPAREN
        { $$ = expr_isdefined::make(@$, std::move($3)); }
    ;

expr_reference
    : DOUBLECOLON expr_identifier
        { $$ = expr_reference::make(@$, expr_path::make(@$), std::move($2)); }
    | expr_path DOUBLECOLON expr_identifier
        { $$ = expr_reference::make(@$, std::move($1), std::move($3)); }
    ;

expr_array
    : expr_object LBRACKET expr RBRACKET
        { $$ = expr_array::make(@$, std::move($1), std::move($3)); }
    | expr_getdvarvector LBRACKET expr RBRACKET
        { $$ = expr_array::make(@$, std::move($1), std::move($3)); }
    | expr_vectortoangles LBRACKET expr RBRACKET
        { $$ = expr_array::make(@$, std::move($1), std::move($3)); }
    | expr_angleclamp180 LBRACKET expr RBRACKET
        { $$ = expr_array::make(@$, std::move($1), std::move($3)); }
    | expr_anglestoforward LBRACKET expr RBRACKET
        { $$ = expr_array::make(@$, std::move($1), std::move($3)); }
    | expr_anglestoright LBRACKET expr RBRACKET
        { $$ = expr_array::make(@$, std::move($1), std::move($3)); }
    | expr_anglestoup LBRACKET expr RBRACKET
        { $$ = expr_array::make(@$, std::move($1), std::move($3)); }
    | expr_vectorscale LBRACKET expr RBRACKET
        { $$ = expr_array::make(@$, std::move($1), std::move($3)); }
    ;

expr_field
    : expr_object DOT expr_identifier_nosize
        { $$ = expr_field::make(@$, std::move($1), std::move($3)); }
    ;

expr_size
    : expr_object DOT SIZE %prec SIZEOF
        { $$ = expr_size::make(@$, std::move($1)); }
    ;

expr_paren
    : LPAREN expr RPAREN
        { $$ = expr_paren::make(@$, std::move($2)); }
    ;

expr_object
    : expr_call       { $$ = std::move($1); }
    | expr_method     { $$ = std::move($1); }
    | expr_array      { $$ = std::move($1); }
    | expr_field      { $$ = std::move($1); }
    | expr_game       { $$ = std::move($1); }
    | expr_self       { $$ = std::move($1); }
    | expr_anim       { $$ = std::move($1); }
    | expr_level      { $$ = std::move($1); }
    | expr_identifier { $$ = std::move($1); }
    ;

expr_empty_array
    : LBRACKET RBRACKET
        { $$ = expr_empty_array::make(@$); };
    ;

expr_undefined
    : UNDEFINED
        { $$ = expr_undefined::make(@$); };
    ;

expr_game
    : GAME
        { $$ = expr_game::make(@$); };
    ;

expr_self
    : SELF
        { $$ = expr_self::make(@$); };
    ;

expr_anim
    : ANIM
        { $$ = expr_anim::make(@$); };
    ;

expr_level
    : LEVEL
        { $$ = expr_level::make(@$); };
    ;

expr_animation
    : MOD IDENTIFIER %prec ANIMREF
        { $$ = expr_animation::make(@$, $2); };
    ;

expr_animtree
    : ANIMTREE
        { $$ = expr_animtree::make(@$); };
    ;

expr_identifier_nosize
    : IDENTIFIER
        { $$ = expr_identifier::make(@$, $1); };
    ;

expr_identifier
    : IDENTIFIER
        { $$ = expr_identifier::make(@$, $1); };
    | SIZE
        { $$ = expr_identifier::make(@$, "size"); };
    ;

expr_path
    : PATH DIV IDENTIFIER
        { $$ = expr_path::make(@$, $1 + "/" + $3); };
    | IDENTIFIER
        { $$ = expr_path::make(@$, $1); };
    | PATH
        { $$ = expr_path::make(@$, $1); };
    ;

expr_istring
    : ISTRING
        { $$ = expr_istring::make(@$, $1); };
    ;

expr_string
    : STRING
        { $$ = expr_string::make(@$, $1); };
    ;

expr_vector
    : LPAREN expr COMMA expr COMMA expr RPAREN
        { $$ = expr_vector::make(@$, std::move($2), std::move($4), std::move($6)); };
    ;

expr_hash
    : HASHSTR
        { $$ = expr_hash::make(@$, $1); };
    ;

expr_float
    : SUB FLOAT %prec NEG
        { $$ = expr_float::make(@$, "-" + $2); };
    | FLOAT
        { $$ = expr_float::make(@$, $1); };
    ;

expr_integer
    : SUB INTEGER %prec NEG
        { $$ = expr_integer::make(@$, "-" + $2); };
    | INTEGER
        { $$ = expr_integer::make(@$, $1); };
    ;

expr_false
    : FALSE
        { $$ = expr_false::make(@$); };
    ;

expr_true
    : TRUE
        { $$ = expr_true::make(@$); };
    ;

%%

namespace xsk::arc
{

void parser::error(location const& loc, std::string const& msg)
{
    throw comp_error(loc, msg);
}

auto parse_switch(stmt_switch& stm) -> void
{
    auto body = stmt_list::make(stm.body->block->loc());
    auto curr = stmt::ptr{ nullptr };

    auto num = stm.body->block->list.size();

    for (auto i = 0u; i < num; i++)
    {
        auto& entry = stm.body->block->list[0];

        if (entry->is<stmt_case>() || entry->is<stmt_default>())
        {
            if (curr != nullptr)
            {
                body->list.push_back(std::move(curr));
            }

            curr = std::move(stm.body->block->list[0]);
            stm.body->block->list.erase(stm.body->block->list.begin());
        }
        else
        {
            if (curr != nullptr)
            {
                if (curr->is<stmt_case>())
                {
                    curr->as<stmt_case>().body->list.push_back(std::move(entry));
                    stm.body->block->list.erase(stm.body->block->list.begin());
                }
                else
                {
                    curr->as<stmt_default>().body->list.push_back(std::move(entry));
                    stm.body->block->list.erase(stm.body->block->list.begin());
                }
            }
            else
            {
                throw comp_error(entry->loc(), "missing case statement");
            }
        }
    }

    if (curr != nullptr)
    {
        body->list.push_back(std::move(curr));
    }

    stm.body->block = std::move(body);
}

extern std::unordered_map<token::kind, parser::token::token_kind_type> const tok_to_parser;
extern std::unordered_map<std::string_view, parser::token::token_kind_type> const keyword_map;

auto map_token(context const* ctx_, token& tok) -> parser::symbol_type
{
    if (tok.type == token::NAME)
    {
        tok.data = ctx_->make_token(tok.data);

        auto const it = keyword_map.find(tok.data);

        if (it != keyword_map.end())
        {
            return parser::symbol_type(it->second, tok.pos);
        }

        return parser::symbol_type(parser::token::IDENTIFIER, std::move(tok.data), tok.pos);
    }
    else if (tok.type == token::PATH ||tok.type == token::HASHSTR ||tok.type == token::STRING ||tok.type == token::ISTRING || tok.type == token::INT ||tok.type == token::FLT)
    {
        auto it = tok_to_parser.find(tok.type);

        if (it != tok_to_parser.end())
        {
            return parser::symbol_type(it->second, std::move(tok.data), tok.pos);
        }
    }
    else
    {
        auto it = tok_to_parser.find(tok.type);

        if (it != tok_to_parser.end())
        {
            return parser::symbol_type(it->second, tok.pos);
        }
    }

    throw error(fmt::format("unmapped token! {}", (u8)tok.type));
}

auto ARClex(context const* ctx_, preprocessor& ppr) -> parser::symbol_type
{
    auto tok = ppr.process();
    return map_token(ctx_, tok);
}

std::unordered_map<token::kind, parser::token::token_kind_type> const tok_to_parser
{{
    { token::NAME, parser::token::IDENTIFIER },
    { token::PATH, parser::token::PATH },
    { token::STRING, parser::token::STRING },
    { token::ISTRING, parser::token::ISTRING },
    { token::HASHSTR, parser::token::HASHSTR },
    { token::INT, parser::token::INTEGER },
    { token::FLT, parser::token::FLOAT },
    { token::PLUS, parser::token::ADD },
    { token::MINUS, parser::token::SUB },
    { token::STAR, parser::token::MUL },
    { token::DIV, parser::token::DIV },
    { token::MOD, parser::token::MOD },
    { token::BITOR, parser::token::BITWISE_OR },
    { token::BITAND, parser::token::BITWISE_AND },
    { token::BITEXOR, parser::token::BITWISE_EXOR },
    { token::ASSIGN, parser::token::ASSIGN },
    { token::PLUSEQ, parser::token::ASSIGN_ADD },
    { token::MINUSEQ, parser::token::ASSIGN_SUB },
    { token::STAREQ, parser::token::ASSIGN_MUL },
    { token::DIVEQ, parser::token::ASSIGN_DIV },
    { token::MODEQ, parser::token::ASSIGN_MOD },
    { token::BITOREQ, parser::token::ASSIGN_BW_OR },
    { token::BITANDEQ, parser::token::ASSIGN_BW_AND },
    { token::BITEXOREQ, parser::token::ASSIGN_BW_EXOR },
    { token::SHLEQ, parser::token::ASSIGN_LSHIFT },
    { token::SHREQ, parser::token::ASSIGN_RSHIFT },
    { token::TILDE, parser::token::COMPLEMENT },
    { token::BANG, parser::token::NOT },
    { token::GT, parser::token::GREATER },
    { token::LT, parser::token::LESS },
    { token::GE, parser::token::GREATER_EQUAL },
    { token::LE, parser::token::LESS_EQUAL },
    { token::NE, parser::token::INEQUALITY },
    { token::EQ, parser::token::EQUALITY },
    { token::OR, parser::token::OR },
    { token::AND, parser::token::AND },
    { token::SHL, parser::token::LSHIFT },
    { token::SHR, parser::token::RSHIFT },
    { token::INC, parser::token::INCREMENT },
    { token::DEC, parser::token::DECREMENT },
    { token::QMARK, parser::token::QMARK },
    { token::DOT, parser::token::DOT },
    { token::DOUBLEDOT, parser::token::DOUBLEDOT },
    { token::ELLIPSIS, parser::token::ELLIPSIS },
    { token::COMMA, parser::token::COMMA },
    { token::COLON, parser::token::COLON },
    { token::SEMICOLON, parser::token::SEMICOLON },
    { token::DOUBLECOLON, parser::token::DOUBLECOLON },
    { token::LBRACKET, parser::token::LBRACKET },
    { token::RBRACKET, parser::token::RBRACKET },
    { token::LBRACE, parser::token::LBRACE },
    { token::RBRACE, parser::token::RBRACE },
    { token::LPAREN, parser::token::LPAREN },
    { token::RPAREN, parser::token::RPAREN },
    { token::DEVBEGIN, parser::token::DEVBEGIN },
    { token::DEVEND, parser::token::DEVEND },
    { token::INLINE, parser::token::INLINE },
    { token::INCLUDE, parser::token::INCLUDE },
    { token::USINGTREE, parser::token::USINGTREE },
    { token::ANIMTREE, parser::token::ANIMTREE },
    { token::AUTOEXEC, parser::token::AUTOEXEC },
    { token::CODECALL, parser::token::CODECALL },
    { token::PRIVATE, parser::token::PRIVATE },
    { token::ENDON, parser::token::ENDON },
    { token::NOTIFY, parser::token::NOTIFY },
    { token::WAIT, parser::token::WAIT },
    { token::WAITTILL, parser::token::WAITTILL },
    { token::WAITTILLMATCH, parser::token::WAITTILLMATCH },
    { token::WAITTILLFRAMEEND, parser::token::WAITTILLFRAMEEND },
    { token::IF, parser::token::IF },
    { token::ELSE, parser::token::ELSE },
    { token::DO, parser::token::DO },
    { token::WHILE, parser::token::WHILE },
    { token::FOR, parser::token::FOR },
    { token::FOREACH, parser::token::FOREACH },
    { token::IN, parser::token::IN },
    { token::SWITCH, parser::token::SWITCH },
    { token::CASE, parser::token::CASE },
    { token::DEFAULT, parser::token::DEFAULT },
    { token::BREAK, parser::token::BREAK },
    { token::CONTINUE, parser::token::CONTINUE },
    { token::RETURN, parser::token::RETURN },
    { token::PROFBEGIN, parser::token::PROFBEGIN },
    { token::PROFEND, parser::token::PROFEND },
    { token::THREAD, parser::token::THREAD },
    { token::TRUE, parser::token::TRUE },
    { token::FALSE, parser::token::FALSE },
    { token::UNDEFINED, parser::token::UNDEFINED },
    { token::SIZE, parser::token::SIZE },
    { token::GAME, parser::token::GAME },
    { token::SELF, parser::token::SELF },
    { token::ANIM, parser::token::ANIM },
    { token::LEVEL, parser::token::LEVEL },
    { token::CONST, parser::token::CONST },
    { token::ISDEFINED, parser::token::ISDEFINED },
    { token::VECTORSCALE, parser::token::VECTORSCALE },
    { token::ANGLESTOUP, parser::token::ANGLESTOUP },
    { token::ANGLESTORIGHT, parser::token::ANGLESTORIGHT },
    { token::ANGLESTOFORWARD, parser::token::ANGLESTOFORWARD },
    { token::ANGLECLAMP180, parser::token::ANGLECLAMP180 },
    { token::VECTORTOANGLES, parser::token::VECTORTOANGLES },
    { token::ABS, parser::token::ABS },
    { token::GETTIME, parser::token::GETTIME },
    { token::GETDVAR, parser::token::GETDVAR },
    { token::GETDVARINT, parser::token::GETDVARINT },
    { token::GETDVARFLOAT, parser::token::GETDVARFLOAT },
    { token::GETDVARVECTOR, parser::token::GETDVARVECTOR },
    { token::GETDVARCOLORRED, parser::token::GETDVARCOLORRED },
    { token::GETDVARCOLORGREEN, parser::token::GETDVARCOLORGREEN },
    { token::GETDVARCOLORBLUE, parser::token::GETDVARCOLORBLUE },
    { token::GETDVARCOLORALPHA, parser::token::GETDVARCOLORALPHA },
    { token::GETFIRSTARRAYKEY, parser::token::GETFIRSTARRAYKEY },
    { token::GETNEXTARRAYKEY, parser::token::GETNEXTARRAYKEY },
    { token::EOS, parser::token::ARCEOF },
    { token::HASH, parser::token::HASH }
}};

std::unordered_map<std::string_view, parser::token::token_kind_type> const keyword_map
{{
    { "autoexec", parser::token::AUTOEXEC },
    { "codecall", parser::token::CODECALL },
    { "private", parser::token::PRIVATE },
    { "endon", parser::token::ENDON },
    { "notify", parser::token::NOTIFY },
    { "wait", parser::token::WAIT },
    { "waittill", parser::token::WAITTILL },
    { "waittillmatch", parser::token::WAITTILLMATCH },
    { "waittillframeend", parser::token::WAITTILLFRAMEEND },
    { "if", parser::token::IF },
    { "else", parser::token::ELSE },
    { "do", parser::token::DO },
    { "while", parser::token::WHILE },
    { "for", parser::token::FOR },
    { "foreach", parser::token::FOREACH },
    { "in", parser::token::IN },
    { "switch", parser::token::SWITCH },
    { "case", parser::token::CASE },
    { "default", parser::token::DEFAULT },
    { "break", parser::token::BREAK },
    { "continue", parser::token::CONTINUE },
    { "return", parser::token::RETURN },
    { "prof_begin", parser::token::PROFBEGIN },
    { "prof_end", parser::token::PROFEND },
    { "thread", parser::token::THREAD },
    { "true", parser::token::TRUE },
    { "false", parser::token::FALSE },
    { "undefined", parser::token::UNDEFINED },
    { "size", parser::token::SIZE },
    { "game", parser::token::GAME },
    { "self", parser::token::SELF },
    { "anim", parser::token::ANIM },
    { "level", parser::token::LEVEL },
    { "const", parser::token::CONST },
    { "isdefined", parser::token::ISDEFINED },
    { "vectorscale", parser::token::VECTORSCALE },
    { "anglestoup", parser::token::ANGLESTOUP },
    { "anglestoright", parser::token::ANGLESTORIGHT },
    { "anglestoforward", parser::token::ANGLESTOFORWARD },
    { "angleclamp180", parser::token::ANGLECLAMP180 },
    { "vectortoangles", parser::token::VECTORTOANGLES },
    { "abs", parser::token::ABS },
    { "gettime", parser::token::GETTIME },
    { "getdvar", parser::token::GETDVAR },
    { "getdvarint", parser::token::GETDVARINT },
    { "getdvarfloat", parser::token::GETDVARFLOAT },
    { "getdvarvector", parser::token::GETDVARVECTOR },
    { "getdvarcolorred", parser::token::GETDVARCOLORRED },
    { "getdvarcolorgreen", parser::token::GETDVARCOLORGREEN },
    { "getdvarcolorblue", parser::token::GETDVARCOLORBLUE },
    { "getdvarcoloralpha", parser::token::GETDVARCOLORALPHA },
    { "getfirstarraykey", parser::token::GETFIRSTARRAYKEY },
    { "getnextarraykey", parser::token::GETNEXTARRAYKEY },
}};

} // namespace xsk::arc