%{
    use strict;
    use warnings;
    our $VERSION = 0.000_910;
    use Carp;
    
    use rperlrules;  # affirmative, it totally does
%}

%strict                               # require all tokens to be declared, etc.
%tree                                 # automatically create AST
%whites /((?:\s*(?:#[^#!].*)?\s*)*)/  # actual whitespace, or one or more normal comments; neither shebang '#!', nor double-hash critics '##'

%defaultaction {
    my $self = shift;
    my $name = $self->YYName();
    bless { children => [ @_ ] }, $name;
}

%token SHEBANG               = /(^#!\/(?:\w+\/)*perl)/                   # begin line, hash (octothorpe), bang, *NIX path to Perl; ex. '#!/usr/bin/perl'
%token VERSION_NUMBER_ASSIGN = /\$VERSION\ =\ (\d\d?\.\d{3}\_\d{3});/    # match assign match, only capture & return number; ex. '$VERSION = 12.345_678;' returns '12.345_678'
%token LITERAL_NUMBER        = /(-?(((\d{1,2}_)?(\d{3}_)*\d{3})|\d{1,2})(\.((\d{3}(_\d{3})*(_\d{1,2})?)|\d{1,2}))?)/   # number w/ underscores; ex. '12_345_678.910_1'
%token LITERAL_STRING        = /(('[^']+')|("[^"\@\$]*((\\n)|(\\t))+[^"\@\$]*")|(q{[^}]*}))/  # single quotes non-empty; double quotes non-empty w/out sigils & w/ newline or tab; or single q-braces
%token FH_REF_SYMBOL_BRACES  = /(\{\$[A-Z][A-Z0-9_]*\})/                 # left brace, dollar sigil, uppercase letter, uppercase letters & numbers & underscores, right brace; ex. '{$MY_FILEHANDLE_23}'
%token FH_REF_SYMBOL         = /(\$[A-Z][A-Z0-9_]*)/                     # dollar sigil, uppercase letter, uppercase letters & numbers & underscores; ex. '$MY_FILEHANDLE_23'
%token VARIABLE_SYMBOL       = /(\$[a-zA-Z]\w*(::)*\w*)/                 # dollar sigil, scoped word
%token KEYS_OR_VALUES        = /(keys|values)/
%token TYPE_CLASS_OR_SELF    = /(string\s+\$class|object\s+\$self)/      # ex. 'string $class' or 'object $self'
%token STDOUT_STDERR         = /(\{\*STDOUT\}|\{\*STDERR\})/             # '{*STDOUT}' or '{*STDERR}'
%token STDIN                 = /(<STDIN>)/                               # '<STDIN>'

# FEATURE BOUNTY #000, 1_000 CodeCoin: Implement all Perl functions AKA builtins (PERLOPS_PERLTYPES) as C++ functions (CPPOPS_PERLTYPES & CPPOPS_CPPTYPES)
# Affects OP01_NAMED, OP01_NAMED_VOID, and OP10_NAMED_UNARY below, corresponding RPerl::Test::Operator* and C++ code;  http://perldoc.perl.org/perlfunc.html

# PRECEDENCE, LEXICAL ORDERING: earlier declaration gets tried first;  http://perldoc.perl.org/perlop.html#Operator-Precedence-and-Associativity [1]
%token OP24_LOGICAL_OR_XOR      = /(or|xor)/                    # precedence 24 infix: logical or and xor, equivalent to || except for precedence
%token OP23_LOGICAL_AND         = /(and)/                       # precedence 23 infix: logical and, equivalent to && except for precedence
%token OP22_LOGICAL_NEG         = /(not)/                       # precedence 22 prefix: logical negation, equivalent to ! except for precedence
%token OP21_LIST_COMMA          = /(,)/                         # precedence 21 infix: "list operators (rightward)" [1] AKA comma
%token OP20_HASH_FATARROW       = /(=>)/                        # precedence 20 infix: hash entry fat arrow AKA fat comma
%token OP19_LOOP_CONTROL        = /(next|last)/                 # precedence 19 prefix void: loop control next, last
%token OP18_TERNARY             = /(\?)/                        # precedence 18 infix: ternary conditional
%token OP17_LIST_RANGE          = /(\.\.)/                      # precedence 17 infix: range
%token OP16_LOGICAL_OR          = /(\|\|)/                      # precedence 16 infix: logical or
%token OP15_LOGICAL_AND         = /(&&)/                        # precedence 15 infix: logical and
%token OP14_BITWISE_OR_XOR      = /(\||\^)/                     # precedence 14 infix: bitwise or, bitwise xor
%token OP13_BITWISE_AND         = /(&)/                         # precedence 13 infix: bitwise and
%token OP12_COMPARE_EQ_NE       = /(==|!=|eq|ne)/               # precedence 12 infix: comparison numeric equal, numeric not equal, string equal, string not equal
%token OP09_BITWISE_SHIFT       = /(<<|>>)/                     # precedence 09 infix: bitwise shift left, shift right
%token OP10_NAMED_UNARY         = /(chdir|rand|scalar[^t])/     # precedence 10 prefix: "named unary operators" [1]; ex. 'chdir' and 'rand'; 'scalar' not 'scalartype'
%token OP10_STRINGIFY_UNARY     = /((?:main)?::[a-zA-Z]\w*__stringify\()/   # precedence 10 prefix: stringify AKA pretty print; ex. '::integer__array_ref__stringify'
%token OP08_STRING_CAT          = /(\.)/                        # precedence 08 infix: string concatenate
%token OP03_MATH_INC_DEC        = /(\+\+|--)/                   # precedence 03 prefix and postfix: increment, decrement
%token OP04_MATH_POW            = /(\*\*)/                      # precedence 04 infix: arithmetic exponent AKA power
%token OP07_MATH_MULT_DIV_MOD   = /(\*|\/|\%)/                  # precedence 07 infix: arithmetic multiply, divide, modulo
%token OP06_REGEX_PATTERN       = /([ms]\/.*(?:\/.*)?\/[a-z]*)/ # precedence 06 infix: regular expression pattern
%token OP06_REGEX_MATCH         = /(=\~|!\~)/                   # precedence 06 infix: regular expression match, not match
%token OP19_VARIABLE_ASSIGN_BY  = /(\+=|-=|\*=|\/=)/            # precedence 19 infix: add assign, subtract assign, multiply assign, divide assign
%token OP05_LOGICAL_NEG         = /(!)/                         # precedence 05 prefix: logical negation
%token OP02_HASH_THINARROW      = /(->\{)/                      # precedence 02 infix: thin arrow, hash dereference and retrieval
%token OP02_ARRAY_THINARROW     = /(->\[)/                      # precedence 02 infix: thin arrow, array dereference and retrieval
%token OP02_METHOD_THINARROW    = /(->)/                        # precedence 02 infix: thin arrow, method dereference and call
%token OP05_MATH_NEG_LPAREN     = /(-\()/                       # precedence 05 prefix: arithmetic negative
%token OP08_MATH_ADD_SUB        = /(\+|-)/                      # precedence 08 infix: arithmetic add, subtract
%token OP11_COMPARE_LT_GT       = /(<=|>=|<|>|lt|gt|le|ge)/     # precedence 11 infix: comparison less than, greater than, less or equal, greater or equal
%token OP19_VARIABLE_ASSIGN     = /(=)/                         # precedence 19 infix: assign
%token OP01_PRINT               = /(print)/                     # precedence 01 prefix void: print to STDOUT, STDERR, or filehandle
%token OP01_NAMED_VOID          = /(croak|return|exit)/         # precedence 01 prefix void: "terms and list operators (leftward)" [1] AKA builtins, no return value
%token OP01_QW                  = /(qw)/                        # precedence 01 prefix: quoted words
%token OP01_OPEN                = /(open)/                      # precedence 01 prefix: open filehandle
%token OP01_CLOSE               = /(close)/                     # precedence 01 prefix: close filehandle
%token OP01_NAMED               = /(sin|cos|push|pop|ETC)/      # precedence 01 prefix: "terms and list operators (leftward)" [1] AKA builtins

# PRECEDENCE, SYNTAX ASSOCIATIVITY: later declaration gets higher priority
%left       OP24_LOGICAL_OR_XOR
%left       OP23_LOGICAL_AND
%right      OP22_LOGICAL_NEG
%left       OP21_LIST_COMMA
%left       OP20_HASH_FATARROW
%right      OP19_LOOP_CONTROL
%right      OP19_VARIABLE_ASSIGN_BY
%right      OP19_VARIABLE_ASSIGN
%right      OP18_TERNARY
%nonassoc   OP17_LIST_RANGE
%left       OP16_LOGICAL_OR
%left       OP15_LOGICAL_AND
%left       OP14_BITWISE_OR_XOR
%left       OP13_BITWISE_AND
%nonassoc   OP12_COMPARE_EQ_NE
%nonassoc   OP11_COMPARE_LT_GT
%nonassoc   OP10_NAMED_UNARY
%nonassoc   OP10_STRINGIFY_UNARY
%left       OP09_BITWISE_SHIFT
%left       OP08_STRING_CAT
%left       OP08_MATH_ADD_SUB
%left       OP07_MATH_MULT_DIV_MOD
%left       OP06_REGEX_MATCH
%left       OP06_REGEX_PATTERN
%right      OP05_MATH_NEG_LPAREN
%right      OP05_LOGICAL_NEG
%right      OP04_MATH_POW
%nonassoc   OP03_MATH_INC_DEC
%left       OP02_HASH_THINARROW
%left       OP02_ARRAY_THINARROW
%left       OP02_METHOD_THINARROW
%left       OP01_NAMED
%left       OP01_CLOSE
%left       OP01_OPEN
%left       OP01_QW
%left       OP01_NAMED_VOID
%left       OP01_PRINT

%token TYPE_METHOD          = /([a-zA-Z]\w*__method)/               # letter followed by letters, numbers, and underscores, followed by '__method'; ex. 'string__array_ref__method'
%token WORD_SCOPED          = /(([a-zA-Z]\w*(::[a-zA-Z]\w*)+)|(::[a-zA-Z]\w*(::[a-zA-Z]\w*)*))/
%token WORD                 = /([a-zA-Z]\w*)/                       # letter followed by letters, numbers, and underscores; ex. 'foobar_42_Howdy'
%token COLON                = /:/
%token LPAREN               = /\(/
%token LBRACKET             = /\[/
%token LBRACE               = /\{/

%%

CompileUnit:            Program | (ModuleHeader Module)+ ;
Program:                SHEBANG Critic? Header Critic* Include* Constant* Subroutine* Operation+ ;
ModuleHeader:           Critic? 'package' WordScoped ';' Header ;
Module:                 Package | Class ;
Package:                Critic* Include* Constant* Subroutine+ LITERAL_NUMBER ';' ;
Header:                 'use strict;' 'use warnings;' 'use RPerl;' 'our' VERSION_NUMBER_ASSIGN;
Critic:                 '## no critic qw(' WORD+ ')';
Include:                'use' WordScoped ';' | 'use' WordScoped 'qw(' WORD+ ')' ';' ;
Constant:               'use constant' WORD OP20_HASH_FATARROW TypeInner Literal ';' ;
#Constant:              'use constant' WORD OP20_HASH_FATARROW TypeInner ConstantValue ';' ;  # NEED UPGRADE: constant array & hash refs not read-only as of Perl v5.20
#ConstantValue:         Literal | LBRACKET TypeInner? Literal (OP21_LIST_COMMA TypeInner? Literal)* ']' | 
#                       LBRACE WORD OP20_HASH_FATARROW TypeInner? Literal (OP21_LIST_COMMA WORD OP20_HASH_FATARROW TypeInner? Literal)* '}' ;
Subroutine:             'our' Type VARIABLE_SYMBOL '= sub {' SubroutineArguments? Operation+ '}' ';' ;
SubroutineArguments:    '( my' Type VARIABLE_SYMBOL (OP21_LIST_COMMA 'my' Type VARIABLE_SYMBOL)* ')' OP19_VARIABLE_ASSIGN '@_;' ;
Class:                  'use parent qw(' WordScoped ')' ';' Include Critic* Include* Constant* Properties PropertiesClass? MethodOrSubroutine* LITERAL_NUMBER ';' ;
Properties:             'our %properties = (' Critic HashEntryTyped (OP21_LIST_COMMA HashEntryTyped)* ')' ';' | 'our %properties = (' ')' ';' Critic ;
PropertiesClass:        'our %properties_class = (' Critic HashEntryTyped (OP21_LIST_COMMA HashEntryTyped)* ')' ';' ;
Method:                 'our' TYPE_METHOD VARIABLE_SYMBOL '= sub {' MethodArguments? Operation+ '}' ';' ;
MethodArguments:        '( my' TYPE_CLASS_OR_SELF (OP21_LIST_COMMA 'my' Type VARIABLE_SYMBOL)* ')' OP19_VARIABLE_ASSIGN '@_;' ;
MethodOrSubroutine:     Method | Subroutine;

Operation:              Expression ';' | Statement ;
Operator:               LPAREN OP01_PRINT FH_REF_SYMBOL_BRACES ListElements ')' |
                        OP01_NAMED SubExpression | LPAREN OP01_NAMED ListElement OP21_LIST_COMMA ListElements ')' |
                        OP01_OPEN 'my' 'filehandle_ref' FH_REF_SYMBOL OP21_LIST_COMMA LITERAL_STRING OP21_LIST_COMMA SubExpression |
                        OP01_CLOSE FH_REF_SYMBOL | OP03_MATH_INC_DEC Variable | Variable OP03_MATH_INC_DEC | SubExpression OP04_MATH_POW SubExpression |
                        OP05_LOGICAL_NEG SubExpression | OP05_MATH_NEG_LPAREN SubExpression ')' | SubExpression OP06_REGEX_MATCH OP06_REGEX_PATTERN |
                        SubExpression OP07_MATH_MULT_DIV_MOD SubExpression | SubExpression OP08_MATH_ADD_SUB SubExpression |
                        SubExpression OP08_STRING_CAT SubExpression | SubExpression OP09_BITWISE_SHIFT SubExpression | OP10_NAMED_UNARY SubExpression |
                        OP10_NAMED_UNARY | OP10_STRINGIFY_UNARY SubExpression ')' | SubExpression OP11_COMPARE_LT_GT SubExpression |
                        SubExpression OP12_COMPARE_EQ_NE SubExpression | SubExpression OP13_BITWISE_AND SubExpression |
                        SubExpression OP14_BITWISE_OR_XOR SubExpression | SubExpression OP15_LOGICAL_AND SubExpression | SubExpression OP16_LOGICAL_OR SubExpression |
                        SubExpression OP17_LIST_RANGE SubExpression | SubExpression OP18_TERNARY VariableOrLiteral COLON VariableOrLiteral |
                        OP22_LOGICAL_NEG SubExpression | SubExpression OP23_LOGICAL_AND SubExpression | SubExpression OP24_LOGICAL_OR_XOR SubExpression ;
OperatorVoid:           OP01_PRINT (STDOUT_STDERR)? ListElements ';' | OP01_PRINT FH_REF_SYMBOL_BRACES ListElements ';' |
                        OP01_NAMED_VOID ListElements? ';' | OP01_NAMED ListElement OP21_LIST_COMMA ListElements ';' | OP19_LOOP_CONTROL LoopLabel ';' ;
Expression:             Operator | WordScoped LPAREN ListElements? ')' | Variable OP02_METHOD_THINARROW WORD LPAREN ListElements? ')' ;
SubExpression:          Expression | 'undef' | Literal | Variable | ArrayReference | ArrayDereferenced | HashReference | HashDereferenced | LBRACE '}' |
                        LPAREN SubExpression ')' ;
SubExpressionOrStdin:   SubExpression | STDIN;
Statement:              Conditional | (LoopLabel COLON)? Loop | OperatorVoid | VariableDeclaration | VariableModification ;
Conditional:            'if (' SubExpression ')' CodeBlock ('elsif (' SubExpression ')' CodeBlock)* ('else' CodeBlock)? ;
Loop:                   LoopFor | LoopForEach | LoopWhile ;
LoopFor:                'for my integer' VARIABLE_SYMBOL LPAREN SubExpression OP17_LIST_RANGE SubExpression ')' CodeBlock ;
LoopForEach:            'foreach my' Type VARIABLE_SYMBOL LPAREN ListElements ')' CodeBlock ;
LoopWhile:              'while (' SubExpression ')' CodeBlock ;
CodeBlock:              LBRACE Operation+ '}' ;

Variable:               VARIABLE_SYMBOL VariableRetrieval* ;
VariableRetrieval:      OP02_ARRAY_THINARROW SubExpression ']' | OP02_HASH_THINARROW SubExpression '}' | OP02_HASH_THINARROW WORD '}' ;
VariableDeclaration:    'my' Type VARIABLE_SYMBOL ';' | 'my' Type VARIABLE_SYMBOL OP19_VARIABLE_ASSIGN SubExpressionOrStdin ';' ;
VariableModification:   Variable OP19_VARIABLE_ASSIGN SubExpressionOrStdin ';' | Variable OP19_VARIABLE_ASSIGN_BY SubExpression ';' ;
ListElements:           ListElement (OP21_LIST_COMMA ListElement)* | OP01_QW LPAREN WORD+ ')' ;
ListElement:            SubExpression | TypeInner SubExpression | KEYS_OR_VALUES HashDereferenced ;
ArrayReference:         LBRACKET ListElements? ']' ;
ArrayDereferenced:      '@{' Variable '}' | '@{' ArrayReference '}' ;
HashEntry:              WORD OP20_HASH_FATARROW TypeInner? SubExpression | HashDereferenced ;
HashEntryTyped:         WORD OP20_HASH_FATARROW TypeInner SubExpression | HashDereferenced ;
HashReference:          LBRACE HashEntry (OP21_LIST_COMMA HashEntry)* '}' ;
HashDereferenced:       '%{' Variable '}' | '%{' HashReference '}' ;

WordScoped:             WORD | WORD_SCOPED;
LoopLabel:              WORD ;
Type:                   WORD ;
TypeInner:              'my' Type '$TYPED_' WORD OP19_VARIABLE_ASSIGN ;
VariableOrLiteral:      Variable | Literal;
Literal:                LITERAL_STRING | LITERAL_NUMBER ;

%%

{
# Rules from Grammar.output
# NEED FIX: UPDATE!?!
my string__hash_ref $rules = {

CompileUnit_3 => 'RPerl::CompileUnit',
CompileUnit_4 => 'RPerl::CompileUnit',
Program_9 => 'RPerl::CompileUnit::Program',
Module_10 => 'RPerl::CompileUnit::Module',
Module_11 => 'RPerl::CompileUnit::Module',
Package_14 => 'RPerl::CompileUnit::Module::Package',
Subroutine_38 => 'RPerl::CodeBlock::Subroutine',
Class_52 => 'RPerl::CompileUnit::Module::Class',
Properties_55 => 'RPerl::DataStructure::Hash::Properties',
Method_60 => 'RPerl::CodeBlock::Subroutine::Method',
Operation_65 => 'RPerl::Operation',
Operation_66 => 'RPerl::Operation',
Operator_69 => 'RPerl::Operation::Expression::Operator',

}; 
    1;
}