/* 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 {T6}
%define api.namespace {xsk::arc::t6}
%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::t6::lexer& lexer }
%parse-param { xsk::arc::t6::lexer& lexer }
%parse-param { xsk::arc::ast::program::ptr& ast }

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

%code top
{
#include "stdinc.hpp"
#include "parser.hpp"
#include "lexer.hpp"
using namespace xsk::arc;
xsk::arc::t6::parser::symbol_type T6lex(xsk::arc::t6::lexer& lexer);
}

%token SH_DEFINE        "#define"
%token SH_UNDEF         "#undef"
%token SH_IFDEF         "#ifdef"
%token SH_IFNDEF        "#ifndef"
%token SH_IF            "#if"
%token SH_ELIF          "#elif"
%token SH_ELSE          "#else"
%token SH_ENDIF         "#endif"
%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 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> HASH       "hash"
%token <std::string> FLOAT      "float"
%token <std::string> INTEGER    "integer"

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

program
    : 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)); }
    | inline
        { $$ = std::make_unique<ast::program>(@$); }
    | include
        { $$ = std::make_unique<ast::program>(@$); $$->includes.push_back(std::move($1)); }
    | declaration
        { $$ = std::make_unique<ast::program>(@$); $$->declarations.push_back(std::move($1)); }
    ;

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

include
    : INCLUDE expr_path SEMICOLON
        { $$ = std::make_unique<ast::include>(@$, std::move($2)); }
    ;

declaration
    : DEVBEGIN          { $$.as_dev_begin = std::make_unique<ast::decl_dev_begin>(@$); }
    | DEVEND            { $$.as_dev_end = std::make_unique<ast::decl_dev_end>(@$); }
    | decl_usingtree    { $$.as_usingtree = std::move($1); }
    | decl_thread       { $$.as_thread = std::move($1); }
    ;

decl_usingtree
    : USINGTREE LPAREN expr_string RPAREN SEMICOLON
        { lexer.ban_header(@$); $$ = std::make_unique<ast::decl_usingtree>(@$, std::move($3)); }
    ;

decl_thread
    : expr_identifier LPAREN expr_parameters RPAREN stmt_block
        { lexer.ban_header(@$); $$ = std::make_unique<ast::decl_thread>(@$, std::move($1), std::move($3), std::move($5), export_flags::export_none); }
    | AUTOEXEC expr_identifier LPAREN expr_parameters RPAREN stmt_block
        { lexer.ban_header(@$); $$ = std::make_unique<ast::decl_thread>(@$, std::move($2), std::move($4), std::move($6), export_flags::export_autoexec); }
    | CODECALL expr_identifier LPAREN expr_parameters RPAREN stmt_block
        { lexer.ban_header(@$); $$ = std::make_unique<ast::decl_thread>(@$, std::move($2), std::move($4), std::move($6), export_flags::export_codecall); }
    | PRIVATE expr_identifier LPAREN expr_parameters RPAREN stmt_block
        { lexer.ban_header(@$); $$ = std::make_unique<ast::decl_thread>(@$, std::move($2), std::move($4), std::move($6), export_flags::export_private2); }
    ;

stmt
    : stmt_block             { $$.as_list = std::move($1); }
    | stmt_call              { $$.as_call = std::move($1); }
    | stmt_const            { $$.as_const = std::move($1); }
    | stmt_assign            { $$.as_assign = std::move($1); }
    | stmt_endon             { $$.as_endon = std::move($1); }
    | stmt_notify            { $$.as_notify = std::move($1); }
    | stmt_wait              { $$.as_wait = std::move($1); }
    | stmt_waittill          { $$.as_waittill = std::move($1); }
    | stmt_waittillmatch     { $$.as_waittillmatch = std::move($1); }
    | stmt_waittillframeend  { $$.as_waittillframeend = std::move($1); }
    | stmt_if                { $$.as_if = std::move($1); }
    | stmt_ifelse            { $$.as_ifelse = std::move($1); }
    | stmt_while             { $$.as_while = std::move($1); }
    | stmt_dowhile           { $$.as_dowhile = std::move($1); }
    | stmt_for               { $$.as_for = std::move($1); }
    | stmt_foreach           { $$.as_foreach = std::move($1); }
    | stmt_switch            { $$.as_switch = std::move($1); }
    | stmt_case              { $$.as_case = std::move($1); }
    | stmt_default           { $$.as_default = std::move($1); }
    | stmt_break             { $$.as_break = std::move($1); }
    | stmt_continue          { $$.as_continue = std::move($1); }
    | stmt_return            { $$.as_return = std::move($1); }
    | stmt_prof_begin        { $$.as_prof_begin = std::move($1); }
    | stmt_prof_end          { $$.as_prof_end = std::move($1); }
    ;

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

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

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

stmt_dev
    : DEVBEGIN stmt_list DEVEND { $$ = std::make_unique<ast::stmt_dev>(@$, std::move($2)); }
    | DEVBEGIN DEVEND { $$ = std::make_unique<ast::stmt_dev>(@$, std::make_unique<ast::stmt_list>(@$)); }
    ;

stmt_block
    : LBRACE stmt_or_dev_list RBRACE { $$ = std::move($2); }
    | LBRACE RBRACE { $$ = std::make_unique<ast::stmt_list>(@$); }
    ;

stmt_expr
    : expr_assign
        { $$ = std::make_unique<ast::stmt_expr>(@$, std::move($1)); }
    | expr_increment
        { $$ = std::make_unique<ast::stmt_expr>(@$, std::move($1)); }
    | expr_decrement
        { $$ = std::make_unique<ast::stmt_expr>(@$, std::move($1)); }
    |
        { $$ = std::make_unique<ast::stmt_expr>(@$, std::make_unique<ast::node>(@$)); }
    ;

stmt_call
    : expr_call SEMICOLON
        { $$ = std::make_unique<ast::stmt_call>(@$, ast::expr(std::move($1))); }
    | expr_method SEMICOLON
        { $$ = std::make_unique<ast::stmt_call>(@$, ast::expr(std::move($1))); }
    ;

stmt_const
    : CONST expr_identifier ASSIGN expr SEMICOLON
        { $$ = std::make_unique<ast::stmt_const>(@$, std::move($2), std::move($4)); }
    ;

stmt_assign
    : expr_assign SEMICOLON
        { $$ = std::make_unique<ast::stmt_assign>(@$, std::move($1)); }
    | expr_increment SEMICOLON
        { $$ = std::make_unique<ast::stmt_assign>(@$, std::move($1)); }
    | expr_decrement SEMICOLON
        { $$ = std::make_unique<ast::stmt_assign>(@$, std::move($1)); }
    ;

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

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

stmt_wait
    : WAIT expr SEMICOLON
        { $$ = std::make_unique<ast::stmt_wait>(@$, std::move($2)); }
    ;

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

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

stmt_waittillframeend
    : WAITTILLFRAMEEND SEMICOLON
        { $$ = std::make_unique<ast::stmt_waittillframeend>(@$); }
    ;

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

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

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

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

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

stmt_foreach
    : FOREACH LPAREN expr_identifier IN expr RPAREN stmt
        { $$ = std::make_unique<ast::stmt_foreach>(@$, ast::expr(std::move($3)), std::move($5), std::move($7)); }
    | FOREACH LPAREN expr_identifier COMMA expr_identifier IN expr RPAREN stmt
        { $$ = std::make_unique<ast::stmt_foreach>(@$, ast::expr(std::move($3)), ast::expr(std::move($5)), std::move($7), std::move($9)); }
    ;

stmt_switch
    : SWITCH LPAREN expr RPAREN stmt_block
        { $$ = std::make_unique<ast::stmt_switch>(@$, std::move($3), std::move($5)); }
    ;

stmt_case
    : CASE expr_integer COLON
        { $$ = std::make_unique<ast::stmt_case>(@$, ast::expr(std::move($2)), std::make_unique<ast::stmt_list>(@$)); }
    | CASE expr_string COLON
        { $$ = std::make_unique<ast::stmt_case>(@$, ast::expr(std::move($2)), std::make_unique<ast::stmt_list>(@$)); }
    ;

stmt_default
    : DEFAULT COLON
        { $$ = std::make_unique<ast::stmt_default>(@$, std::make_unique<ast::stmt_list>(@$)); }
    ;

stmt_break
    : BREAK SEMICOLON
        { $$ = std::make_unique<ast::stmt_break>(@$); }
    ;

stmt_continue
    : CONTINUE SEMICOLON
        { $$ = std::make_unique<ast::stmt_continue>(@$); }
    ;

stmt_return
    : RETURN expr SEMICOLON
        { $$ = std::make_unique<ast::stmt_return>(@$, std::move($2)); }
    | RETURN SEMICOLON
        { $$ = std::make_unique<ast::stmt_return>(@$, std::make_unique<ast::node>(@$)); }
    ;

stmt_prof_begin
    : PROFBEGIN LPAREN expr_arguments RPAREN SEMICOLON
        { $$ = std::make_unique<ast::stmt_prof_begin>(@$, std::move($3)); }
    ;

stmt_prof_end
    : PROFEND LPAREN expr_arguments RPAREN SEMICOLON
        { $$ = std::make_unique<ast::stmt_prof_end>(@$, 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); }
    |                { $$.as_node = std::make_unique<ast::node>(@$); }
    ;

expr_assign
    : expr_object ASSIGN expr
        { $$.as_node = std::make_unique<ast::expr_assign_equal>(@$, std::move($1), std::move($3)); }
    | expr_object ASSIGN_BW_OR expr
        { $$.as_node = std::make_unique<ast::expr_assign_bitwise_or>(@$, std::move($1), std::move($3)); }
    | expr_object ASSIGN_BW_AND expr
        { $$.as_node = std::make_unique<ast::expr_assign_bitwise_and>(@$, std::move($1), std::move($3)); }
    | expr_object ASSIGN_BW_EXOR expr
        { $$.as_node = std::make_unique<ast::expr_assign_bitwise_exor>(@$, std::move($1), std::move($3)); }
    | expr_object ASSIGN_LSHIFT expr
        { $$.as_node = std::make_unique<ast::expr_assign_shift_left>(@$, std::move($1),std::move( $3)); }
    | expr_object ASSIGN_RSHIFT expr
        { $$.as_node = std::make_unique<ast::expr_assign_shift_right>(@$, std::move($1), std::move($3)); }
    | expr_object ASSIGN_ADD expr
        { $$.as_node = std::make_unique<ast::expr_assign_add>(@$, std::move($1), std::move($3)); }
    | expr_object ASSIGN_SUB expr
        { $$.as_node = std::make_unique<ast::expr_assign_sub>(@$, std::move($1), std::move($3)); }
    | expr_object ASSIGN_MUL expr
        { $$.as_node = std::make_unique<ast::expr_assign_mul>(@$, std::move($1), std::move($3)); }
    | expr_object ASSIGN_DIV expr
        { $$.as_node = std::make_unique<ast::expr_assign_div>(@$, std::move($1), std::move($3)); }
    | expr_object ASSIGN_MOD expr
        { $$.as_node = std::make_unique<ast::expr_assign_mod>(@$, std::move($1), std::move($3)); }
    ;

expr_increment
    : INCREMENT expr_object %prec PREINC
        { $$.as_node = std::make_unique<ast::expr_increment>(@$, std::move($2), true); }
    | expr_object INCREMENT %prec POSTINC
        { $$.as_node = std::make_unique<ast::expr_increment>(@$, std::move($1), false); }
    ;

expr_decrement
    : DECREMENT expr_object %prec PREDEC
        { $$.as_node = std::make_unique<ast::expr_decrement>(@$, std::move($2), true); }
    | expr_object DECREMENT %prec POSTDEC
        { $$.as_node = std::make_unique<ast::expr_decrement>(@$, std::move($1), false); }
    ;

expr_ternary
    : expr QMARK expr COLON expr %prec TERN
        { $$.as_node = std::make_unique<ast::expr_ternary>(@$, std::move($1), std::move($3), std::move($5)); }
    ;

expr_binary
    : expr OR expr
        { $$.as_node = std::make_unique<ast::expr_or>(@$, std::move($1), std::move($3)); }
    | expr AND expr
        { $$.as_node = std::make_unique<ast::expr_and>(@$, std::move($1), std::move($3)); }
    | expr EQUALITY expr
        { $$.as_node = std::make_unique<ast::expr_equality>(@$, std::move($1), std::move($3)); }
    | expr INEQUALITY expr
        { $$.as_node = std::make_unique<ast::expr_inequality>(@$, std::move($1), std::move($3)); }
    | expr LESS_EQUAL expr
        { $$.as_node = std::make_unique<ast::expr_less_equal>(@$, std::move($1), std::move($3)); }
    | expr GREATER_EQUAL expr
        { $$.as_node = std::make_unique<ast::expr_greater_equal>(@$, std::move($1), std::move($3)); }
    | expr LESS expr
        { $$.as_node = std::make_unique<ast::expr_less>(@$, std::move($1), std::move($3)); }
    | expr GREATER expr
        { $$.as_node = std::make_unique<ast::expr_greater>(@$, std::move($1), std::move($3)); }
    | expr BITWISE_OR expr
        { $$.as_node = std::make_unique<ast::expr_bitwise_or>(@$, std::move($1), std::move($3)); }
    | expr BITWISE_AND expr
        { $$.as_node = std::make_unique<ast::expr_bitwise_and>(@$, std::move($1), std::move($3)); }
    | expr BITWISE_EXOR expr
        { $$.as_node = std::make_unique<ast::expr_bitwise_exor>(@$, std::move($1), std::move($3)); }
    | expr LSHIFT expr
        { $$.as_node = std::make_unique<ast::expr_shift_left>(@$, std::move($1), std::move($3)); }
    | expr RSHIFT expr
        { $$.as_node = std::make_unique<ast::expr_shift_right>(@$, std::move($1), std::move($3)); }
    | expr ADD expr
        { $$.as_node = std::make_unique<ast::expr_add>(@$, std::move($1), std::move($3)); }
    | expr SUB expr
        { $$.as_node = std::make_unique<ast::expr_sub>(@$, std::move($1), std::move($3)); }
    | expr MUL expr
        { $$.as_node = std::make_unique<ast::expr_mul>(@$, std::move($1), std::move($3)); }
    | expr DIV expr
        { $$.as_node = std::make_unique<ast::expr_div>(@$, std::move($1), std::move($3)); }
    | expr MOD expr
        { $$.as_node = std::make_unique<ast::expr_mod>(@$, std::move($1), std::move($3)); }
    ;

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

expr_complement
    : COMPLEMENT expr
        { $$ = std::make_unique<ast::expr_complement>(@$, std::move($2)); }
    ;

expr_negate
    : SUB expr_identifier %prec NEG
        { $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
    | SUB expr_paren %prec NEG
        { $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
    | SUB expr_array %prec NEG
        { $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
    | SUB expr_field %prec NEG
        { $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
    ;

expr_not
    : NOT expr
        { $$ = std::make_unique<ast::expr_not>(@$, std::move($2)); }
    ;

expr_call
    : expr_function                { $$ = std::make_unique<ast::expr_call>(@$, std::move($1)); }
    | expr_pointer                 { $$ = std::make_unique<ast::expr_call>(@$, std::move($1)); }
    ;
expr_method
    : expr_object expr_function    { $$ = std::make_unique<ast::expr_method>(@$, std::move($1), std::move($2)); }
    | expr_object expr_pointer     { $$ = std::make_unique<ast::expr_method>(@$, std::move($1), std::move($2)); }
    ;

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

expr_pointer
    : LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
        { $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($3), std::move($7), ast::call::mode::normal); }
    | THREAD LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
        { $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($4), std::move($8), ast::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(ast::expr(std::move($3))); }
    | expr_parameters_default
        { $$ = std::make_unique<ast::expr_parameters>(@$); $$->list.push_back(std::move($1)); }
    | expr_identifier
        { $$ = std::make_unique<ast::expr_parameters>(@$); $$->list.push_back(ast::expr(std::move($1))); }
    |
        { $$ = std::make_unique<ast::expr_parameters>(@$); }
    ;

expr_parameters_default
    : expr_identifier ASSIGN expr
        { $$.as_node = std::make_unique<ast::expr_assign_equal>(@$, ast::expr(std::move($1)), std::move($3)); }
    ;

expr_arguments
    : expr_arguments_no_empty
        { $$ = std::move($1); }
    |
        { $$ = std::make_unique<ast::expr_arguments>(@$); }
    ;

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

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

expr_getfirstarraykey
    : GETFIRSTARRAYKEY LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_getfirstarraykey>(@$, std::move($3)); }
    ;

expr_getdvarcoloralpha
    : GETDVARCOLORALPHA LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_getdvarcoloralpha>(@$, std::move($3)); }
    ;

expr_getdvarcolorblue
    : GETDVARCOLORBLUE LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_getdvarcolorblue>(@$, std::move($3)); }
    ;

expr_getdvarcolorgreen
    : GETDVARCOLORGREEN LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_getdvarcolorgreen>(@$, std::move($3)); }
    ;

expr_getdvarcolorred
    : GETDVARCOLORRED LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_getdvarcolorred>(@$, std::move($3)); }
    ;

expr_getdvarvector
    : GETDVARVECTOR LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_getdvarvector>(@$, std::move($3)); }
    ;

expr_getdvarfloat
    : GETDVARFLOAT LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_getdvarfloat>(@$, std::move($3)); }
    ;

expr_getdvarint
    : GETDVARINT LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_getdvarint>(@$, std::move($3)); }
    ;

expr_getdvar
    : GETDVAR LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_getdvar>(@$, std::move($3)); }
    ;

expr_gettime
    : GETTIME LPAREN RPAREN
        { $$ = std::make_unique<ast::expr_gettime>(@$); }
    ;

expr_abs
    : ABS LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_abs>(@$, std::move($3)); }
    ;

expr_vectortoangles
    : VECTORTOANGLES LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_vectortoangles>(@$, std::move($3)); }
    ;

expr_angleclamp180
    : ANGLECLAMP180 LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_angleclamp180>(@$, std::move($3)); }
    ;

expr_anglestoforward
    : ANGLESTOFORWARD LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_anglestoforward>(@$, std::move($3)); }
    ;

expr_anglestoright
    : ANGLESTORIGHT LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_anglestoright>(@$, std::move($3)); }
    ;

expr_anglestoup
    : ANGLESTOUP LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_anglestoup>(@$, std::move($3)); }
    ;

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

expr_isdefined
    : ISDEFINED LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_isdefined>(@$, std::move($3)); }
    ;

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

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

expr_field
    : expr_object DOT expr_identifier_nosize
        { $$ = std::make_unique<ast::expr_field>(@$, std::move($1), std::move($3)); }
    ;

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

expr_paren
    : LPAREN expr RPAREN
        { $$ = std::make_unique<ast::expr_paren>(@$, std::move($2)); }
    ;

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

expr_empty_array
    : LBRACKET RBRACKET
        { $$ = std::make_unique<ast::expr_empty_array>(@$); };
    ;

expr_undefined
    : UNDEFINED
        { $$ = std::make_unique<ast::expr_undefined>(@$); };
    ;

expr_game
    : GAME
        { $$ = std::make_unique<ast::expr_game>(@$); };
    ;

expr_self
    : SELF
        { $$ = std::make_unique<ast::expr_self>(@$); };
    ;

expr_anim
    : ANIM
        { $$ = std::make_unique<ast::expr_anim>(@$); };
    ;

expr_level
    : LEVEL
        { $$ = std::make_unique<ast::expr_level>(@$); };
    ;

expr_animation
    : MOD IDENTIFIER %prec ANIMREF
        { $$ = std::make_unique<ast::expr_animation>(@$, $2); };
    ;

expr_animtree
    : ANIMTREE
        { $$ = std::make_unique<ast::expr_animtree>(@$); };
    ;

expr_identifier_nosize
    : IDENTIFIER
        { $$ = std::make_unique<ast::expr_identifier>(@$, $1); };
    ;

expr_identifier
    : IDENTIFIER
        { $$ = std::make_unique<ast::expr_identifier>(@$, $1); };
    | SIZE
        { $$ = std::make_unique<ast::expr_identifier>(@$, "size"); };
    ;

expr_path
    : PATH DIV IDENTIFIER
        { $$ = std::make_unique<ast::expr_path>(@$, $1 + "/" + $3); };
    | IDENTIFIER
        { $$ = std::make_unique<ast::expr_path>(@$, $1); };
    | PATH
        { $$ = std::make_unique<ast::expr_path>(@$, $1); };
    ;

expr_istring
    : ISTRING
        { $$ = std::make_unique<ast::expr_istring>(@$, $1); };
    ;

expr_string
    : STRING
        { $$ = std::make_unique<ast::expr_string>(@$, $1); };
    ;

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

expr_hash
    : HASH
        { $$ = std::make_unique<ast::expr_hash>(@$, $1); };
    ;

expr_float
    : SUB FLOAT %prec NEG
        { $$ = std::make_unique<ast::expr_float>(@$, "-" + $2); };
    | FLOAT
        { $$ = std::make_unique<ast::expr_float>(@$, $1); };
    ;

expr_integer
    : SUB INTEGER %prec NEG
        { $$ = std::make_unique<ast::expr_integer>(@$, "-" + $2); };
    | INTEGER
        { $$ = std::make_unique<ast::expr_integer>(@$, $1); };
    ;

expr_false
    : FALSE
        { $$ = std::make_unique<ast::expr_false>(@$); };
    ;

expr_true
    : TRUE
        { $$ = std::make_unique<ast::expr_true>(@$); };
    ;

%%

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