#!/usr/bin/perl
# [[[ HEADER ]]]
use strict;
use warnings;
use RPerl;
#use RPerl::AfterSubclass;

# DEV NOTE, CORRELATION #rp16: RPerl's underscore-is-comma (not CPAN's underscore-is-beta) numbering scheme utilized here
our $VERSION = 1.700_000;

#our $VERSION = 20160401;    # NON-RELEASE VERSION NUMBER, OFFICIAL LONGDATE
#our $VERSION = 2016.092;    # NON-RELEASE VERSION NUMBER, OFFICIAL STARDATE

# [[[ CRITICS ]]]
## no critic qw(ProhibitUselessNoCritic ProhibitMagicNumbers RequireCheckedSyscalls)  # USER DEFAULT 1: allow numeric values & print operator
## no critic qw(ProhibitExplicitStdin)  # USER DEFAULT 4: allow <STDIN>
## no critic qw(RequireInterpolationOfMetachars)  # USER DEFAULT 2: allow single-quoted control characters & sigils
## no critic qw(ProhibitBooleanGrep)  # SYSTEM SPECIAL 1: allow grep
## no critic qw(RequireCarping)  # SYSTEM SPECIAL 13: allow die instead of croak

# [[[ INCLUDES ]]]
use RPerl::Parser;    # includes Perl::Critic
use RPerl::Generator;
use RPerl::Compiler;    # includes Inline
use RPerl::DataType::Number;  # number_to_string()
use Getopt::Long qw(:config no_ignore_case);
use Pod::Usage;
use File::Path qw(remove_tree);
use Config qw(config_re);

# [[[ SUBROUTINES ]]]

# this array must be instantiated here, because GetOptions' diamond '<>' operator does not allow passing arguments or receiving return values
my string_arrayref $input_file_names_unlabeled = [];

# [[[ ACCEPT, CHECK, REPORT OPTIONS (COMMAND-LINE ARGUMENTS) ]]]
# [[[ ACCEPT, CHECK, REPORT OPTIONS (COMMAND-LINE ARGUMENTS) ]]]
# [[[ ACCEPT, CHECK, REPORT OPTIONS (COMMAND-LINE ARGUMENTS) ]]]

sub accept_and_verify_input_files {
    ( my string_arrayref $input_file_names, my string_arrayref $input_file_names_unlabeled, my string_hashref $modes ) = @_;

    #    RPerl::diag( 'in rperl::accept_and_verify_input_files(), received $input_file_names = ' . Dumper($input_file_names) . "\n" );
    #    RPerl::diag( 'in rperl::accept_and_verify_input_files(), received $input_file_names_unlabeled = ' . Dumper($input_file_names_unlabeled) . "\n" );

    RPerl::diag("\n\n");    # move output away from initial Inline compiler output
    RPerl::diag( 'in rperl, have $RPerl::DEBUG = ' . $RPerl::DEBUG . "\n" );
    RPerl::diag( 'in rperl, have $RPerl::VERBOSE = ' . $RPerl::VERBOSE . "\n" );

    # accept unlabeled input file name(s) if no labeled values specified
    if ( ( scalar @{$input_file_names_unlabeled} ) > 0 ) {
        if ( ( scalar @{$input_file_names} ) == 0 ) {
            $input_file_names = $input_file_names_unlabeled;
        }
        else {
            die 'ERROR EARG08: Both labeled & unlabeled RPerl source code input file option(s) specified, dying' . "\n";
        }
    }

    if ( ( scalar @{$input_file_names} ) == 0 ) {
        die 'ERROR EARG01: No RPerl source code input file(s) specified, dying' . "\n";
    }

    # verify input file(s)
    my $input_files_count = scalar @{$input_file_names};

    # DEV NOTE: Perl::Critic BUG!  'integer' triggers false positive RequireFinalReturn
    #    for my integer $i ( 0 .. ( $input_files_count - 1 ) ) {
    for my $i ( 0 .. ( $input_files_count - 1 ) ) {
        my string $input_file_name = $input_file_names->[$i];

#    RPerl::diag('in rperl, top of file verifying loop ' . $i . ' of ' . $input_files_count . ", have \$input_file_names->[$i] = '" . $input_file_name . "'\n");

        if ( $input_file_name =~ m/\s/xms ) {

#            RPerl::diag('in rperl, in file verifying loop ' . $i . ' of ' . $input_files_count . ", have \$input_file_name with spaces = '" . $input_file_name . "'\n");
            my string_arrayref $input_file_name_and_args = [ split /[ ]/xms, $input_file_name ];

#            RPerl::diag('in rperl, in file verifying loop ' . $i . ' of ' . $input_files_count . ", have \$input_file_name_and_args = '" . Dumper($input_file_name_and_args) . "'\n");
            $input_file_name = shift @{$input_file_name_and_args};
            $input_file_names->[$i] = $input_file_name;

   #            RPerl::diag('in rperl, in file verifying loop ' . $i . ' of ' . $input_files_count . ", have \$input_file_name = '" . $input_file_name . "'\n");
            $modes->{arguments} = $input_file_name_and_args;

            #            RPerl::diag('in rperl, in file verifying loop ' . $i . ' of ' . $input_files_count . ", have \$modes = '" . Dumper($modes) . "'\n");
        }

        if ( not( -e $input_file_name ) ) {    ## no critic qw(ProhibitCascadingIfElse)  # SYSTEM DEFAULT 2: allow argument-handling logic
            die 'ERROR EARG02: Specified RPerl source code input file ' . q{'} . $input_file_name . q{'} . ' does not exist, dying' . "\n";
        }
        elsif ( not( -r $input_file_name ) ) {
            die 'ERROR EARG03: Specified RPerl source code input file ' . q{'} . $input_file_name . q{'} . ' is not readable, dying' . "\n";
        }
        elsif ( not( -f $input_file_name ) ) {
            die 'ERROR EARG04: Specified RPerl source code input file ' . q{'} . $input_file_name . q{'} . ' is not a plain file, dying' . "\n";
        }
        elsif ( ( $input_file_name !~ /[.]pm$/xms )
            and ( $input_file_name !~ /[.]pl$/xms ) )
        {
            die 'ERROR EARG05: Specified RPerl source code input file ' . q{'}
                . $input_file_name . q{'}
                . ' is not a Perl program ending in ' . q{'} . '.pl' . q{'}
                . ' or module ending in  ' . q{'} . '.pm' . q{'}
                . ', dying' . "\n";
        }

    }
    return $input_file_names;
}

sub verify_and_default_modes {
    (   my string_hashref $modes,
        my string_hashref $modes_default,
        my arrayref_hashref $modes_supported,
        my integer $dependencies_flag,
        my integer $uncompile_flag,
        my integer $compile_flag,
        my integer $subcompile_assemble_flag,
        my integer $subcompile_archive_flag,
        my integer $subcompile_shared_flag,
        my integer $subcompile_static_flag,
        my integer $subcompile_CXX,
        my integer $execute_flag,
        my integer $test_flag,
        my string_arrayref $input_file_names
    ) = @_;

    if ( defined $dependencies_flag ) {
        if ($dependencies_flag) {
            $modes->{dependencies} = 'ON';
        }
        else {
            $modes->{dependencies} = 'OFF';
        }
    }

    if ( defined $uncompile_flag ) {
        if ($uncompile_flag) {
            if ( ( defined $compile_flag ) and $compile_flag ) {
                die 'ERROR EARG09: Incompatible command-line option flags provided, both --uncompile and --compile, dying' . "\n";
            }
            # subcompile mode is used in Compiler::generate_output_file_names() to determine which files to delete, allow flags to set subcompile mode
#            if ( ( defined $subcompile_assemble_flag ) and $subcompile_assemble_flag ) {
#                die 'ERROR EARG09: Incompatible command-line option flags provided, both --uncompile and --assemble, dying' . "\n";
#            }
#            if ( ( defined $subcompile_archive_flag ) and $subcompile_archive_flag ) {
#                die 'ERROR EARG09: Incompatible command-line option flags provided, both --uncompile and --archive, dying' . "\n";
#            }
#            if ( ( defined $subcompile_shared_flag ) and $subcompile_shared_flag ) {
#                die 'ERROR EARG09: Incompatible command-line option flags provided, both --uncompile and --shared, dying' . "\n";
#            }
#            if ( ( defined $subcompile_static_flag ) and $subcompile_static_flag ) {
#                die 'ERROR EARG09: Incompatible command-line option flags provided, both --uncompile and --static, dying' . "\n";
#            }
            if ( ( defined $execute_flag ) and $execute_flag ) {
                die 'ERROR EARG09: Incompatible command-line option flags provided, both --uncompile and --execute, dying' . "\n";
            }
            if ( ( defined $test_flag ) and $test_flag ) {
                die 'ERROR EARG09: Incompatible command-line option flags provided, both --uncompile and --test, dying' . "\n";
            }
            if ($uncompile_flag == 1) { $modes->{uncompile} = 'SOURCE'; }
            if ($uncompile_flag == 2) { $modes->{uncompile} = 'SOURCE_BINARY'; }
            if ($uncompile_flag == 3) { $modes->{uncompile} = 'SOURCE_BINARY_INLINE'; }
            # subcompile mode is used in Compiler::generate_output_file_names() to determine which files to delete,
            # leave compile mode in tact to leave subcompile mode in tact from "can't subcompile if we don't subcompile" at bottom of this subroutine
#            $modes->{compile}   = 'OFF';
            $modes->{execute}   = 'OFF';
        }
        else {
            $modes->{uncompile} = 'OFF';
        }
    }

    if ( defined $compile_flag ) {
        if ($compile_flag) {
            $modes->{compile} = 'SUBCOMPILE';  # already default, does nothing
        }
        else {    # --nocompile command-line argument (option) disables SAVE & SUBCOMPILE steps, PARSE & GENERATE remain enabled
            if (( defined $subcompile_assemble_flag ) and $subcompile_assemble_flag ) {
                die 'ERROR EARG10: Incompatible command-line option flags provided, both --nocompile and --assemble, dying' . "\n";
            }
            if ( ( defined $subcompile_archive_flag ) and $subcompile_archive_flag ) {
                die 'ERROR EARG10: Incompatible command-line option flags provided, both --nocompile and --archive, dying' . "\n";
            }
            if ( ( defined $subcompile_shared_flag ) and $subcompile_shared_flag ) {
                die 'ERROR EARG10: Incompatible command-line option flags provided, both --nocompile and --shared, dying' . "\n";
            }
            if ( ( defined $subcompile_static_flag ) and $subcompile_static_flag ) {
                die 'ERROR EARG10: Incompatible command-line option flags provided, both --nocompile and --static, dying' . "\n";
            }
            $modes->{compile} = 'GENERATE';
        }
    }

    # assemble mode is CPPOPS_CPPTYPES_SUBCOMPILE_ASSEMBLE; flag ignored if false
    if (( defined $subcompile_assemble_flag ) and $subcompile_assemble_flag ) {
        if ( ( defined $subcompile_archive_flag ) and $subcompile_archive_flag ) {
            die 'ERROR EARG11: Incompatible command-line option flags provided, both --assemble and --archive, dying' . "\n";
        }
        if ( ( defined $subcompile_shared_flag ) and $subcompile_shared_flag ) {
            die 'ERROR EARG11: Incompatible command-line option flags provided, both --assemble and --shared, dying' . "\n";
        }
        if ( ( defined $subcompile_static_flag ) and $subcompile_static_flag ) {
            die 'ERROR EARG11: Incompatible command-line option flags provided, both --assemble and --static, dying' . "\n";
        }
        if ( ( defined $execute_flag ) and $execute_flag ) {
            die 'ERROR EARG11: Incompatible command-line option flags provided, both --assemble and --execute, dying' . "\n";
        }
        if ( ( defined $test_flag ) and $test_flag ) {
            die 'ERROR EARG11: Incompatible command-line option flags provided, both --assemble and --test, dying' . "\n";
        }
        $modes->{compile}   = 'SUBCOMPILE';
        $modes->{subcompile} = 'ASSEMBLE';
        $modes->{execute}   = 'OFF';
    }

    # archive mode is CPPOPS_CPPTYPES_SUBCOMPILE_ARCHIVE; flag ignored if false
    if (( defined $subcompile_archive_flag ) and $subcompile_archive_flag ) {
        if ( ( defined $subcompile_shared_flag ) and $subcompile_shared_flag ) {
            die 'ERROR EARG12: Incompatible command-line option flags provided, both --archive and --shared, dying' . "\n";
        }
        if ( ( defined $subcompile_static_flag ) and $subcompile_static_flag ) {
            die 'ERROR EARG12: Incompatible command-line option flags provided, both --archive and --static, dying' . "\n";
        }
        if ( ( defined $execute_flag ) and $execute_flag ) {
            die 'ERROR EARG12: Incompatible command-line option flags provided, both --archive and --execute, dying' . "\n";
        }
        if ( ( defined $test_flag ) and $test_flag ) {
            die 'ERROR EARG12: Incompatible command-line option flags provided, both --archive and --test, dying' . "\n";
        }
        $modes->{compile}   = 'SUBCOMPILE';
        $modes->{subcompile} = 'ARCHIVE';
        $modes->{execute}   = 'OFF';
    }

    # shared mode is CPPOPS_CPPTYPES_SUBCOMPILE_SHARED; flag ignored if false
    if (( defined $subcompile_shared_flag ) and $subcompile_shared_flag ) {
        if ( ( defined $subcompile_static_flag ) and $subcompile_static_flag ) {
            die 'ERROR EARG13: Incompatible command-line option flags provided, both --shared and --static, dying' . "\n";
        }
        if ( ( defined $execute_flag ) and $execute_flag ) {
            die 'ERROR EARG13: Incompatible command-line option flags provided, both --shared and --execute, dying' . "\n";
        }
        if ( ( defined $test_flag ) and $test_flag ) {
            die 'ERROR EARG13: Incompatible command-line option flags provided, both --shared and --test, dying' . "\n";
        }
        $modes->{compile}   = 'SUBCOMPILE';
        $modes->{subcompile} = 'SHARED';
        $modes->{execute}   = 'OFF';
    }

    # static mode is CPPOPS_CPPTYPES_SUBCOMPILE_STATIC; negated is dynamic mode is CPPOPS_CPPTYPES_SUBCOMPILE_DYNAMIC
    if (( defined $subcompile_static_flag ) and $subcompile_static_flag ) {
        if ($subcompile_static_flag) {
            if ( ( defined $test_flag ) and $test_flag ) {
                die 'ERROR EARG14: Incompatible command-line option flags provided, both --static and --test, dying' . "\n";
            }
            foreach my string $input_file_name (@{$input_file_names}) {
                if ( $input_file_name =~ /[.]pm$/xms ) {
                    die 'ERROR EARG15: Incompatible command-line options provided, both --static subcompile mode flag and *.pm Perl module input file(s), dying' . "\n";
                }
            }
            $modes->{compile}   = 'SUBCOMPILE';
            $modes->{subcompile} = 'STATIC';
        }
        else {
            $modes->{compile}   = 'SUBCOMPILE';
            $modes->{subcompile} = 'DYNAMIC';
        }
    }
    
    if (defined $subcompile_CXX) {
        if ($subcompile_CXX =~ m/^\s*$/gxms) {
            die 'ERROR EARG16: Undefined, empty, or all-whitespace CXX command-line option provided, dying' . "\n";
        }
        else {
            $modes->{CXX} = $subcompile_CXX;
        }
    }

    if ( defined $execute_flag ) {
        if ($execute_flag) {
            $modes->{execute} = 'ON';
        }
        else {
            $modes->{execute} = 'OFF';
        }
    }

    # test mode is PERLOPS_PERLTYPES_GENERATE, do not save files to disk; test mode flag ignored if false
    if (( defined $test_flag ) and $test_flag ) {
        $modes->{ops}     = 'PERL';
        $modes->{types}   = 'PERL';
        $modes->{compile} = 'GENERATE';
    }

    # must put Perl variables into environmental variables for the values to be properly utilized,
    # do not simply overwrite values, check for applicability first

    # DEV NOTE, CORRELATION #rp17: default to off; if either variable is set to true, then do emit messages
    if ($RPerl::DEBUG) {
        $ENV{RPERL_DEBUG} = $RPerl::DEBUG;
    }
    if ($RPerl::VERBOSE) {
        $ENV{RPERL_VERBOSE} = $RPerl::VERBOSE;
    }

    # verify modes
    foreach my string $mode_key ( keys %{$modes} ) {
        if ( not( exists $modes_supported->{$mode_key} ) ) {
            die "ERROR EARG06: Unsupported or invalid mode category '$mode_key' specified, supported categories are ("
                . join( ', ', keys %{$modes_supported} )
                . '), dying' . "\n";
        }
        elsif ( ( defined $modes_supported->{$mode_key} ) and not( grep { $_ eq $modes->{$mode_key} } @{ $modes_supported->{$mode_key} } ) ) {
            die 'ERROR EARG07: Unsupported or invalid mode ' . q{'}
                . $modes->{$mode_key} . q{'}
                . ' in mode category ' . q{'}
                . $mode_key . q{'}
                . ' specified, supported modes are ('
                . join( ', ', @{ $modes_supported->{$mode_key} } )
                . '), dying' . "\n";
        }
    }

    # initialize empty symbol table, to be populated during GENERATE phase
    $modes->{_symbol_table} = { _namespace => 'main::', _subroutine => q{} };

    # defaults when mode(s) not provided
    foreach my string $mode_default_key ( keys %{$modes_default} ) { ## no critic qw(ProhibitPostfixControls)  # SYSTEM SPECIAL 6: PERL CRITIC FILED ISSUE #639, not postfix foreach or if
        if ( not( exists $modes->{$mode_default_key} ) ) {
            $modes->{$mode_default_key} = $modes_default->{$mode_default_key};
        }
    }

    # can't subcompile if we don't subcompile
    if ($modes->{compile} ne 'SUBCOMPILE') {
        $modes->{subcompile} = 'OFF';
    }

    1;    # DEV NOTE: Perl::Critic BUG!  must have this '1;' to avoid false positive ProhibitPostfixControls & RequireFinalReturn

    return $modes;
}

sub verbose_versions {
    RPerl::verbose( 'RPERL VERBOSE VERSIONS & OTHER CONFIG INFO' . "\n\n" );

    RPerl::verbose( '$RPerl::VERSION    ' . RPerl::DataType::Number::number_to_string($RPerl::VERSION) . "\n" );
    RPerl::verbose( ' rperl $VERSION    ' . RPerl::DataType::Number::number_to_string($VERSION) . "\n\n" );

    my string $ccflags = [ config_re('ccflags') ]->[0];
    substr $ccflags, 0,  9, q{};                 # remove leading ccflags='
    substr $ccflags, -1, 1, q{};                 # remove trailing '
    RPerl::verbose(q{  Perl config_re('ccflags')       } . $ccflags . "\n" );
    
    my string $cccdlflags = [ config_re('cccdlflags') ]->[0];
    substr $cccdlflags, 0,  12, q{};                                             # remove leading cccdlflags='
    substr $cccdlflags, -1, 1,  q{};                                             # remove trailing '
    RPerl::verbose(q{  Perl config_re('cccdlflags')    } . $cccdlflags . "\n" );

    RPerl::verbose( '$RPerl::Inline::CCFLAGSEX         ' . $RPerl::Inline::CCFLAGSEX . "\n" );
    RPerl::verbose( '$RPerl::Inline::ARGS{optimize}    ' . $RPerl::Inline::ARGS{optimize} . "\n\n" );

    RPerl::verbose( '$RPerl::CHECK           ' . $RPerl::CHECK . "\n" );
    RPerl::verbose( '$RPerl::DEBUG           ' . $RPerl::DEBUG . "\n" );
    RPerl::verbose( '$ENV{RPERL_DEBUG}       ' . ($ENV{RPERL_DEBUG} or '[ NOT SET ]') . "\n" );
    RPerl::verbose( '$RPerl::VERBOSE         ' . $RPerl::VERBOSE . "\n" );
    RPerl::verbose( '$ENV{RPERL_VERBOSE}     ' . ($ENV{RPERL_VERBOSE} or '[ NOT SET ]') . "\n" );
    RPerl::verbose( '$RPerl::WARNINGS        ' . $RPerl::WARNINGS . "\n" );
    RPerl::verbose( '$ENV{RPERL_WARNINGS}    ' . ($ENV{RPERL_WARNINGS} or '[ NOT SET ]') . "\n" );
    RPerl::verbose( '$RPerl::TYPES_CCFLAG    ' . $RPerl::TYPES_CCFLAG . '  [ HARD-CODED DEFAULT ]' . "\n" );
    RPerl::verbose( '$RPerl::BASE_PATH       ' . $RPerl::BASE_PATH . "\n" );
    RPerl::verbose( '$RPerl::INCLUDE_PATH    ' . $RPerl::INCLUDE_PATH . "\n" );
    RPerl::verbose( '$RPerl::SCRIPT_PATH     ' . $RPerl::SCRIPT_PATH . "\n" );
    RPerl::verbose( '$RPerl::CORE_PATH       ' . $RPerl::CORE_PATH . "\n" );

#    RPerl::verbose( 'FOOOOOO' . "\n    " . $BAAAAAAR . "\n" );
    return;
}

sub verbose_multi_file_settings {
    ( my string_arrayref $input_file_names, my hashref_arrayref $output_file_name_groups, my integer $input_files_count, my hashref_hashref $filename_suffixes_supported, my string_hashref $modes ) = @_;
    if ( $input_files_count > 1 ) {
        RPerl::verbose( multi_file_settings( $input_file_names, $output_file_name_groups, $input_files_count, $filename_suffixes_supported, $modes ) );
        RPerl::verbose_pause( "\n" . 'PRESS <ENTER> TO CONTINUE' . "\n" );
    }
    return;
}

sub diag_multi_file_settings {
    ( my string_arrayref $input_file_names, my hashref_arrayref $output_file_name_groups, my integer $input_files_count, my hashref_hashref $filename_suffixes_supported, my string_hashref $modes ) = @_;
    if ( $input_files_count > 1 ) {
        RPerl::diag( multi_file_settings( $input_file_names, $output_file_name_groups, $input_files_count, $filename_suffixes_supported, $modes ) );
        RPerl::diag_pause( "\n" . 'PRESS <ENTER> TO CONTINUE' . "\n" );
    }
    return;
}

sub multi_file_settings {
    ( my string_arrayref $input_file_names, my hashref_arrayref $output_file_name_groups, my integer $input_files_count, my hashref_hashref $filename_suffixes_supported, my string_hashref $modes ) = @_;
    my string $retval = q{};

    $retval .= 'Input File(s):' . "\n";
    foreach my string $input_file_name ( @{$input_file_names} ) {
        $retval .= q{    } . $input_file_name . "\n";
    }
    $retval .= 'Output File(s):' . "\n";
    foreach my string_hashref $output_file_name_group ( @{$output_file_name_groups} ) {
        $retval .= q{    } . stringify_output_file_name_group($output_file_name_group, $filename_suffixes_supported) . "\n";
    }
    $retval .= 'Modes:' . "\n";
    foreach my string $mode_key ( keys %{$modes} ) {
        $retval .= q{    } . $mode_key . ' => ' . $modes->{$mode_key} . "\n";
    }
    return $retval;
}

# allow omission of "-infile" on command line
#our void $store_unlabeled_options = sub {  # NEED FIX: can't define RPerl-style subroutines here?
sub store_unlabeled_options {
    ( my unknown $option ) = @_;
    push @{$input_file_names_unlabeled}, $option;
    return;
}

# print value of option flags
sub verbose_flags {
    (   my integer $dependencies_flag,
        my integer $uncompile_flag,
        my integer $compile_flag,
        my integer $subcompile_assemble_flag,
        my integer $subcompile_archive_flag,
        my integer $subcompile_shared_flag,
        my integer $subcompile_static_flag,
        my integer $execute_flag,
        my integer $test_flag
    ) = @_;

    RPerl::verbose( 'Verbose Flag......... ' . $RPerl::VERBOSE . "\n" );
    RPerl::verbose( 'Debug Flag........... ' . $RPerl::DEBUG . "\n" );
    if ( defined $dependencies_flag ) {
        RPerl::verbose( 'Dependencies Flag.... ' . $dependencies_flag . "\n" );
    }
    if ( defined $uncompile_flag ) {
        RPerl::verbose( 'Uncompile Flag....... ' . $uncompile_flag . "\n" );
    }
    if ( defined $compile_flag ) {
        RPerl::verbose( 'Compile Flag......... ' . $compile_flag . "\n" );
    }
    if ( defined $subcompile_archive_flag ) {
        RPerl::verbose( 'Archive Flag......... ' . $subcompile_archive_flag . "\n" );
    }
    if ( defined $subcompile_assemble_flag ) {
        RPerl::verbose( 'Assemble Flag........ ' . $subcompile_assemble_flag . "\n" );
    }
    if ( defined $subcompile_shared_flag ) {
        RPerl::verbose( 'Shared Flag.......... ' . $subcompile_shared_flag . "\n" );
    }
    if ( defined $subcompile_static_flag ) {
        RPerl::verbose( 'Static Flag.......... ' . $subcompile_static_flag . "\n" );
    }
    if ( defined $execute_flag ) {
        RPerl::verbose( 'Execute Flag......... ' . $execute_flag . "\n" );
    }
    if ( defined $test_flag ) {
        RPerl::verbose( 'Test Flag............ ' . $test_flag . "\n" );
    }
}

sub stringify_output_file_name_group {
    ( my string_hashref $output_file_name_group, my hashref_hashref $filename_suffixes_supported ) = @_;

    my string $output_file_names = q{};
    foreach my string $suffix_key (sort @{[keys %{$filename_suffixes_supported->{OUTPUT_SOURCE}}, keys %{$filename_suffixes_supported->{OUTPUT_BINARY}}]}) {
        if ( exists $output_file_name_group->{$suffix_key} ) {
            $output_file_names .= $output_file_name_group->{$suffix_key};
            if ( exists $output_file_name_group->{ '_' . $suffix_key . '_label' } ) {
                $output_file_names .= $output_file_name_group->{ '_' . $suffix_key . '_label' };
            }
            $output_file_names .= q{  };
        }
    }
    if ( ( length $output_file_names ) > 55 ) {
        my @output_file_names_split = split q{  }, $output_file_names;
        my boolean $is_first = 1;
        foreach my string $output_file_name (@output_file_names_split) {
            if ($is_first) {
                $output_file_names = $output_file_name;
                $is_first          = 0;
            }
            else {
                $output_file_names .= "\n" . ( q{ } x 20 ) . $output_file_name;
            }
        }
    }
    return $output_file_names;
}

# [[[ UNCOMPILER ]]]
# [[[ UNCOMPILER ]]]
# [[[ UNCOMPILER ]]]

sub depends_delete {
    (   my string_arrayref $input_file_names,
        my hashref_arrayref $output_file_name_groups,
        my string_arrayref $output_file_name_prefixes,
        my integer $input_files_count,
        my hashref_hashref $filename_suffixes_supported,
        my string_hashref $modes
    ) = @_;

    # [[[ OUTER UNCOMPILE LOOP, USER-SPECIFIED INPUT FILES ]]]
    # NEED FIX: Perl::Critic BUG!  'integer' triggers false positive RequireFinalReturn
    #    for my integer $i ( 0 .. ( $input_files_count - 1 ) ) {
    for my $i ( 0 .. ( $input_files_count - 1 ) ) {
        my string $input_file_name                = $input_file_names->[$i];
        my string_hashref $output_file_name_group = $output_file_name_groups->[$i];

        if ( $input_files_count > 1 ) {
            RPerl::verbose_clear_screen();
            RPerl::verbose( 'Input File Number:  ' . ( $i + 1 ) . ' of ' . $input_files_count . "\n" );
        }

        RPerl::verbose( 'Input File:         ' . $input_file_name . "\n" );
        RPerl::verbose( 'Output File(s):     ' . stringify_output_file_name_group($output_file_name_group, $filename_suffixes_supported) . "\n" );
        RPerl::verbose( 'Modes:              ops => '
                . $modes->{ops}
                . ', types => '
                . $modes->{types}
                . ', check => '
                . $modes->{check}
                . ', uncompile => '
                . $modes->{uncompile}
                . ', compile => '
                . $modes->{compile}
                . ', subcompile => '
                . $modes->{subcompile}
                . ', execute => '
                . $modes->{execute}
                . ', label => '
                . $modes->{label}
                . "\n\n" );

        my string_arrayref $input_file_name_deps = [$input_file_name];
        my integer $input_file_and_deps_count;
        my integer $input_file_deps_count                = 0;
        my hashref_arrayref $output_file_name_dep_groups = [$output_file_name_group];

        if ( $modes->{dependencies} eq 'ON' ) {
            $input_file_name_deps = find_dependencies( $input_file_name, $modes );
            RPerl::verbose('DEPENDENCIES:       Follow & find all deps... ');
            if ( exists $input_file_name_deps->[0] ) {
                $input_file_name_deps = accept_and_verify_input_files( $input_file_name_deps, [], $modes );    # verify deps input files
            }
            $input_file_name_deps      = [ @{$input_file_name_deps}, $input_file_name ];                       # append input file to deps list
            $input_file_and_deps_count = scalar @{$input_file_name_deps};
            $input_file_deps_count     = $input_file_and_deps_count - 1;
            $output_file_name_dep_groups
                = generate_output_file_names( $input_file_name_deps, $output_file_name_prefixes, $input_file_and_deps_count, $modes ); # generate deps output file names
            RPerl::verbose( sprintf( "%4d", $input_file_deps_count ) . ' found.' . "\n" );
            diag_multi_file_settings( $input_file_name_deps, $output_file_name_dep_groups, $input_file_and_deps_count, $filename_suffixes_supported, $modes );
        }

        # potentially delete OUTPUT_SOURCE and OUTPUT_BINARY files (not _Inline/);
        # skip if INLINE only mode; or *.pm Perl Module input file and DYNAMIC subcompile mode (generates _Inline/ only) and not any SOURCE uncompile modes
        if (not (($modes->{uncompile} eq 'INLINE') or (($input_file_name =~ /[.]pm$/xms) and ($modes->{subcompile} eq 'DYNAMIC') and ($modes->{uncompile} !~ m/SOURCE/gxms)) )) {
            # [[[ INNER UNCOMPILE LOOP, 1 INPUT FILE & ITS DEPENDENCIES ]]]
            for my $j ( 0 .. $input_file_deps_count ) {
                my string $input_file_name_dep                = $input_file_name_deps->[$j];
                my string_hashref $output_file_name_dep_group = $output_file_name_dep_groups->[$j];
    
                if ( $j < $input_file_deps_count ) {
                    RPerl::verbose( "\n" . 'Dep Output File(s): ' . stringify_output_file_name_group($output_file_name_dep_group, $filename_suffixes_supported) . "\n" );
                }
                elsif ($input_file_deps_count > 1) { 
                    RPerl::verbose( "\n" . 'Output File(s):     ' . stringify_output_file_name_group($output_file_name_dep_group), $filename_suffixes_supported . "\n" );
                }
                RPerl::verbose('UNLINK PHASE 0:     Delete files from disk...');
#                RPerl::diag( 'in depends_delete(), have ( keys %{$output_file_name_dep_group} ) = ' . "\n" . Dumper(keys %{$output_file_name_dep_group}) . "\n" );
                foreach my string $output_file_name_key ( keys %{$output_file_name_dep_group} ) {
                    if ((substr $output_file_name_key, 0, 1) eq '_') { next; }
                    if  (($modes->{uncompile} eq 'SOURCE_BINARY_INLINE') or 
                         ($modes->{uncompile} eq 'SOURCE_BINARY') or 
                        (($modes->{uncompile} eq 'SOURCE') and (exists $filename_suffixes_supported->{OUTPUT_SOURCE}->{$output_file_name_key})) or
                        (($modes->{uncompile} eq 'BINARY') and (exists $filename_suffixes_supported->{OUTPUT_BINARY}->{$output_file_name_key}))) {
                        my string $output_file_name = $output_file_name_dep_group->{$output_file_name_key};
                        if ( -f $output_file_name ) {
                            unlink $output_file_name
                                or die "\nERROR EUNFI00, UNCOMPILER, FILE SYSTEM: Cannot delete existing file '$output_file_name',\ndying: $OS_ERROR";
                        }
                    }
                }
                RPerl::verbose( '    done.' . "\n" );
            }
        }

        # delete _Inline/ if (any INLINE mode or (*.pm Perl module input file and DYNAMIC subcompile mode and any BINARY uncompile mode)) and _Inline/ exists
        if (    (    ($modes->{uncompile} =~ m/INLINE/gxms) or 
                     (($input_file_name =~ /[.]pm$/xms) and ($modes->{subcompile} eq 'DYNAMIC') and ($modes->{uncompile} =~ m/BINARY/gxms))
                ) and 
                ( -d '_Inline' )) {
            RPerl::verbose( ( "\n" x ( $input_files_count - 1 ) ) . 'UNLINK PHASE 1:     Delete _Inline/ from disk...' );
            remove_tree( '_Inline', { error => \my $error_hashes } );
            if ( @{$error_hashes} ) {
                foreach my string_hashref $error_hash ( @{$error_hashes} ) {
                    my ( $file_name, $error_message ) = %{$error_hash};
                    if ( $file_name eq '' ) {
                        die "\nERROR EUNFI01, UNCOMPILER, FILE SYSTEM: Cannot delete folder '_Inline', general error, \ndying: $error_message";
                    }
                    else {
                        die "\nERROR EUNFI01, UNCOMPILER, FILE SYSTEM: Cannot delete folder '_Inline', error deleting file '$file_name', \ndying: $error_message";
                    }
                }
            }
            RPerl::verbose( ' done.' . "\n" );
        }

        if ( ( $input_files_count > 1 ) and ( $i < ( $input_files_count - 1 ) ) ) {
            RPerl::verbose_pause("\nPRESS <ENTER> TO CONTINUE\n");
        }
    }

    return;
}

# [[[ CALL COMPILER ]]]
# [[[ CALL COMPILER ]]]
# [[[ CALL COMPILER ]]]

sub depends_parse_generate_save_subcompile_execute {
    (   my string_arrayref $input_file_names,
        my hashref_arrayref $output_file_name_groups,
        my string_arrayref $output_file_name_prefixes,
        my integer $input_files_count,
        my hashref_hashref $filename_suffixes_supported,
        my string_hashref $modes
    ) = @_;

    if ( $modes->{ops} eq 'PERL' ) {

        # PERL ops does not actually SUBCOMPILE, set compile mode to SAVE instead
        if ( $modes->{compile} eq 'SUBCOMPILE' ) {
            $modes->{compile} = 'SAVE';
        }

        # PERL ops does not have CPP types, set types mode to PERL instead
        if ( $modes->{types} eq 'CPP' ) { $modes->{types} = 'PERL'; }
    }

    # only execute single *.pl program files, executing multiple files may cause unexpected behavior
    if ( $input_files_count > 1 ) {
        $modes->{execute} = 'OFF';
    }

    # [[[ OUTER COMPILE LOOP, USER-SPECIFIED INPUT FILES ]]]
    # NEED FIX: Perl::Critic BUG!  'integer' triggers false positive RequireFinalReturn
    #    for my integer $i ( 0 .. ( $input_files_count - 1 ) ) {
    for my $i ( 0 .. ( $input_files_count - 1 ) ) {
        my string $input_file_name                = $input_file_names->[$i];
        my string_hashref $output_file_name_group = $output_file_name_groups->[$i];

        # program files (*.pl) need to know their own input file name to create the proper C++ #ifdef version preprocessor directives;
        # RPerl::Compiler needs to know if it should perform program or module post-processing
        $modes->{_input_file_name} = $input_file_name;

        # clear symtab before each input file, use automatic dependencies mode (enabled by default) to compile multiple files within a single symtab
        $modes->{_symbol_table} = {};

        # only execute *.pl program files, executing *.pm module files causes useless additional subcompile to occur
        if ( ( $modes->{execute} eq 'ON' ) and ( $input_file_name !~ /[.]pl$/xms ) ) {
            $modes->{execute} = 'OFF';
        }

        if ( $input_files_count > 1 ) {
            RPerl::verbose_clear_screen();
            RPerl::verbose( 'Input File Number:  ' . ( $i + 1 ) . ' of ' . $input_files_count . "\n" );
        }

        RPerl::verbose( 'Input File:         ' . $input_file_name . "\n" );
        RPerl::verbose( 'Output File(s):     ' . stringify_output_file_name_group($output_file_name_group, $filename_suffixes_supported) . "\n" );
        RPerl::verbose( 'Modes:              ops => '
                . $modes->{ops}
                . ', types => '
                . $modes->{types}
                . ', check => '
                . $modes->{check}
                . ', uncompile => '
                . $modes->{uncompile}
                . ', compile => '
                . $modes->{compile}
                . ', subcompile => '
                . $modes->{subcompile}
                . ', execute => '
                . $modes->{execute}
                . ', label => '
                . $modes->{label}
                . "\n\n" );

        my string_arrayref $input_file_name_deps = [$input_file_name];
        my integer $input_file_and_deps_count;
        my integer $input_file_deps_count                = 0;
        my hashref_arrayref $output_file_name_dep_groups = [$output_file_name_group];
        my hashref_arrayref $source_dep_groups           = [];

        if ( $modes->{dependencies} eq 'ON' ) {
            $input_file_name_deps = find_dependencies( $input_file_name, $modes );
            RPerl::verbose('DEPENDENCIES:       Follow & find all deps... ');
            if ( exists $input_file_name_deps->[0] ) {
                $input_file_name_deps = accept_and_verify_input_files( $input_file_name_deps, [], $modes );    # verify deps input files
            }
            $input_file_name_deps      = [ @{$input_file_name_deps}, $input_file_name ];                       # append input file to deps list
            $input_file_and_deps_count = scalar @{$input_file_name_deps};
            $input_file_deps_count     = $input_file_and_deps_count - 1;
            $output_file_name_dep_groups
                = generate_output_file_names( $input_file_name_deps, $output_file_name_prefixes, $input_file_and_deps_count, $modes ); # generate deps output file names
            RPerl::verbose( sprintf( "%4d", $input_file_deps_count ) . ' found.' . "\n" );
            diag_multi_file_settings( $input_file_name_deps, $output_file_name_dep_groups, $input_file_and_deps_count, $filename_suffixes_supported, $modes );
        }

        if ( ( $input_file_deps_count > 0 ) and ( $modes->{compile} ne 'PARSE' ) ) {
            $modes->{_compile_saved} = $modes->{compile};
            $modes->{compile}        = 'GENERATE';
        }

        # [[[ FIRST INNER COMPILE LOOP, 1 INPUT FILE & ITS DEPENDENCIES, DEFER SAVE & SUBCOMPILE IF DEPENDENCIES EXIST ]]]
        for my $j ( 0 .. $input_file_deps_count ) {
            my string $input_file_name_dep                = $input_file_name_deps->[$j];
            my string_hashref $output_file_name_dep_group = $output_file_name_dep_groups->[$j];

            if ( $input_file_deps_count > 0 ) {
                if ( $j < $input_file_deps_count ) {
                    RPerl::verbose( "\n" . 'Dep Input File:     ' . $input_file_name_dep . "\n" );
                }
                else {
                    RPerl::verbose( "\n" . 'Input File:         ' . $input_file_name_dep . "\n" );
                }
            }

            if ( $modes->{ops} eq 'PERL' ) {
                $source_dep_groups->[$j] = RPerl::Compiler::rperl_to_rperl__parse_generate( $input_file_name_dep, $output_file_name_dep_group, {}, $modes );
            }
            elsif ( $modes->{ops} eq 'CPP' ) {

                # use eval to trap C++ compiler errors
                my integer $eval_retval = eval {
                    $source_dep_groups->[$j]
                        = RPerl::Compiler::rperl_to_xsbinary__parse_generate_compile( $input_file_name_dep, $output_file_name_dep_group, {}, $modes ); # returns void
                    1;    # return true
                };
                if ( not defined $eval_retval ) {
                    print $EVAL_ERROR;

                    # force-disable execution if an error is trapped in eval() above
                    $modes->{execute} = 'OFF';
                }
            }
        }

        if (    ( $input_file_deps_count > 0 )
            and ( $modes->{compile} ne 'PARSE' )
            and ( ( $modes->{_compile_saved} eq 'SAVE' ) or ( $modes->{_compile_saved} = 'SUBCOMPILE' ) ) )
        {
            RPerl::verbose( "\n" . 'DEPENDENCIES:       Complete deferred actions...' . "\n" );

            # [[[ SECOND INNER COMPILE LOOP, 1 INPUT FILE & ITS DEPENDENCIES, DEFERRED SAVE & SUBCOMPILE ]]]
            for my $j ( 0 .. $input_file_deps_count ) {
                $modes->{compile} = $modes->{_compile_saved} . '_DEFERRED';
                my string $input_file_name_dep                = $input_file_name_deps->[$j];
                my string_hashref $output_file_name_dep_group = $output_file_name_dep_groups->[$j];
                my string_hashref $source_dep_group           = $source_dep_groups->[$j];

                if ( $j < $input_file_deps_count ) {
                    RPerl::verbose( "\n" . 'Dep Output File(s): ' . stringify_output_file_name_group($output_file_name_dep_group, $filename_suffixes_supported) . "\n" );
                }
                else {
                    RPerl::verbose( "\n" . 'Output File(s):     ' . stringify_output_file_name_group($output_file_name_dep_group, $filename_suffixes_supported) . "\n" );
                }

                if ( $modes->{ops} eq 'PERL' ) {
                    RPerl::Compiler::rperl_to_rperl__parse_generate( $input_file_name_dep, $output_file_name_dep_group, $source_dep_group, $modes );
                }
                elsif ( $modes->{ops} eq 'CPP' ) {

#                RPerl::diag( 'in depends_parse_generate_save_subcompile_execute(), have $modes->{_symbol_table} = ' . "\n" . Dumper($modes->{_symbol_table}) . "\n" );

                    # do not subcompile deps
                    if ( ( $j < $input_file_deps_count ) and ( $modes->{compile} eq 'SUBCOMPILE_DEFERRED' ) ) {
                        $modes->{compile} = 'SAVE_DEFERRED';
                    }

                    # use eval to trap C++ compiler errors
                    my integer $eval_retval = eval {
                        RPerl::Compiler::rperl_to_xsbinary__parse_generate_compile( $input_file_name_dep, $output_file_name_dep_group, $source_dep_group,
                            $modes );    # returns void
                        1;               # return true
                    };
                    if ( not defined $eval_retval ) {
                        print $EVAL_ERROR;

                        # force-disable execution if an error is trapped in eval() above
                        $modes->{execute} = 'OFF';
                    }
                }
            }
        }

        if ( $modes->{execute} eq 'ON' ) {
            RPerl::verbose( 'EXECUTE:            Run code...' . "\n" );
            RPerl::verbose("\n");

            #            RPerl::diag('in rperl depends_parse_generate_save_subcompile_execute(), have \$modes = ' . Dumper($modes) . "'\n");
            if ( defined $modes->{arguments} ) {
                my integer $execute_retval = system( $EXECUTABLE_NAME, $input_file_name, @{ $modes->{arguments} } );
            }
            else {
                my integer $execute_retval = system( $EXECUTABLE_NAME, $input_file_name );
            }
        }

 #        RPerl::diag( 'in depends_parse_generate_save_subcompile_execute(), have $modes->{_symbol_table} = ' . "\n" . Dumper($modes->{_symbol_table}) . "\n" );

        if (    ( $input_files_count > 1 )
            and ( $i < ( $input_files_count - 1 ) ) )
        {
            RPerl::verbose_pause("\nPRESS <ENTER> TO CONTINUE\n");
        }
    }
    return;
}

# [[[ OPERATIONS ]]]

# [[[ ACTUALLY RUN CODE ]]]
# [[[ ACTUALLY RUN CODE ]]]
# [[[ ACTUALLY RUN CODE ]]]

my integer $help_flag                           = 0;
my integer $version_flag                        = 0;
my integer $vversions_flag                      = 0;
my integer $dependencies_flag                   = undef;
my integer $uncompile_flag                      = undef;
my integer $uncompile_source_flag               = undef;
my integer $uncompile_source_binary_flag        = undef;
my integer $uncompile_source_binary_inline_flag = undef;
my integer $compile_flag                        = undef;
my integer $subcompile_assemble_flag            = undef;
my integer $subcompile_archive_flag             = undef;
my integer $subcompile_shared_flag              = undef;
my integer $subcompile_static_flag              = undef;
my string  $subcompile_CXX                      = undef;
my integer $execute_flag                        = undef;
my integer $test_flag                           = undef;
my string_arrayref $input_file_names            = [];
my string_arrayref $output_file_name_prefixes   = [];
my hashref_arrayref $output_file_name_groups    = [];
my string_hashref $modes                        = {};      # can't store defaults here, erased by GetOptions()
my string_hashref $modes_default                = {        # default to CPPOPS_CPPTYPES_CHECKTRACE_SUBCOMPILE_EXECUTE_LABEL in C++ output code
    dependencies => 'ON',
    ops          => 'CPP',
    types        => 'CPP',
    check        => 'TRACE',
    uncompile    => 'OFF',
    compile      => 'SUBCOMPILE',
    subcompile   => 'DYNAMIC',
    CXX          => 'g++',  # default to GNU GCC  http://www.gnu.org/software/make/manual/make.html#Implicit-Variables
    execute      => 'ON',
    label        => 'ON',
};
my arrayref_hashref $modes_supported = {
    arguments    => undef,  # accept any value
    dependencies => [ 'OFF', 'ON' ],
    ops          => [ 'PERL', 'CPP' ],
    types        => [ 'PERL', 'CPP', 'DUAL' ],
    check        => [ 'OFF', 'ON', 'TRACE' ],
    uncompile    => [ 'OFF', 'SOURCE', 'BINARY', 'INLINE', 'SOURCE_BINARY', 'SOURCE_BINARY_INLINE' ],
    compile      => [ 'OFF', 'PARSE', 'GENERATE', 'SAVE', 'SUBCOMPILE' ],
    subcompile   => [ 'OFF', 'ASSEMBLE', 'ARCHIVE', 'SHARED', 'STATIC', 'DYNAMIC' ],
    CXX          => undef,  # accept any value
    execute      => [ 'OFF', 'ON' ],
    label        => [ 'OFF', 'ON' ],
};
#my string_arrayref_hashref_hashref $filename_suffixes_supported = {
my hashref_hashref $filename_suffixes_supported = {
    INPUT_SOURCE => { PL => ['.pl'], PM => ['.pm'] },
    OUTPUT_SOURCE => { CPP => ['.cpp'], H => ['.h'], PMC => ['.pmc'] },
    OUTPUT_BINARY => { O => ['.o'], A => ['.a'], SO => ['.so'], EXE => [q{}, '.exe']}
#    OUTPUT_BINARY => { O => ['.o', '.lib'], A => ['.a', '.lib'], SO => ['.so', '.dll'], EXE => [q{}, '.exe']}
};
my integer $input_files_count = 0;

GetOptions(
    'help|?'        => \$help_flag,
    'v'             => \$version_flag,
    'version'       => \$version_flag,
    'vversion'      => \$vversions_flag,
    'dependencies!' => \$dependencies_flag,
    'Verbose!'      => \$RPerl::VERBOSE,
    'Debug!'        => \$RPerl::DEBUG,
    'Warnings!'     => \$RPerl::WARNINGS,
    'u!'            => \$uncompile_source_flag,
    'uncompile!'    => \$uncompile_source_flag,
    'uu!'           => \$uncompile_source_binary_flag,
    'uuncompile!'   => \$uncompile_source_binary_flag,
    'uuuncompile!'  => \$uncompile_source_binary_inline_flag,
    'compile!'      => \$compile_flag,
    'assemble'      => \$subcompile_assemble_flag,
    'archive'       => \$subcompile_archive_flag,
    'shared'        => \$subcompile_shared_flag,
    'static!'       => \$subcompile_static_flag,
    'CXX=s'         => \$subcompile_CXX,
    'execute!'      => \$execute_flag,
    'test'          => \$test_flag,
    'infile=s{1,}'  => \@{$input_file_names},
    'outfile=s{1,}' => \@{$output_file_name_prefixes},
    'mode=s%'       => \$modes,
    '<>'            => \&store_unlabeled_options
) or die "ERROR EARG00: Failure processing command line arguments, dying\n";

if ($help_flag) { pod2usage( -verbose => 1, -width => 80 ); exit; }
if ($vversions_flag) { $RPerl::VERBOSE = 1; verbose_versions(); exit; }
if ($version_flag) {

    # NEED UPDATE: automatically generate version info

    # DEV NOTE, CORRELATION #rp16: numbering schemes mentioned below
    my $version_message = <<EOL;

This is RPerl version 1.700_000, Long Date 20160401, Star Date 2016.092
v1.700_000 using RPerl's underscore-is-comma numbering scheme
v1.700000  using  CPAN's underscore-is-beta  numbering scheme

Copyright © 2013, 2014, 2015, 2016, William N. Braswell, Jr..  All Rights Reserved.
This work is Free & Open Source; you can redistribute it and/or modify it 
under the same terms as Perl 5.22.0.

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.
For licensing details, please see http://dev.perl.org/licenses/

Complete documentation for RPerl, including FAQ lists, should be found on 
the Internet, point your browser at http://www.rperl.org/, the RPerl Home Page.

EOL
    print $version_message;
    exit;
}

# pre-aggregate values of uncompile mode flags
if (defined $uncompile_source_flag) { if ($uncompile_source_flag) { $uncompile_flag = 1; } else { $uncompile_flag = 0; } }
if (defined $uncompile_source_binary_flag) { if ($uncompile_source_binary_flag) { $uncompile_flag = 2; } else { $uncompile_flag = 0; } }
if (defined $uncompile_source_binary_inline_flag) { if ($uncompile_source_binary_inline_flag) { $uncompile_flag = 3; } else { $uncompile_flag = 0; } }

verbose_flags(
    $dependencies_flag, $uncompile_flag, $compile_flag, $subcompile_assemble_flag,
    $subcompile_archive_flag,      $subcompile_shared_flag,           $subcompile_static_flag,        $execute_flag, $test_flag
);
$input_file_names  = accept_and_verify_input_files( $input_file_names, $input_file_names_unlabeled, $modes );
$input_files_count = scalar @{$input_file_names};
$modes             = verify_and_default_modes(
    $modes,              $modes_default, $modes_supported, $dependencies_flag, $uncompile_flag, $compile_flag,  
    $subcompile_assemble_flag,   $subcompile_archive_flag,      $subcompile_shared_flag,
    $subcompile_static_flag, $subcompile_CXX,        $execute_flag,  $test_flag, $input_file_names
);
$output_file_name_groups = generate_output_file_names( $input_file_names, $output_file_name_prefixes, $input_files_count, $modes );
verbose_multi_file_settings( $input_file_names, $output_file_name_groups, $input_files_count, $filename_suffixes_supported, $modes );
if ( $modes->{uncompile} ne 'OFF' ) { depends_delete( $input_file_names, $output_file_name_groups, $output_file_name_prefixes, $input_files_count, $filename_suffixes_supported, $modes ); }
else { depends_parse_generate_save_subcompile_execute( $input_file_names, $output_file_name_groups, $output_file_name_prefixes, $input_files_count, $filename_suffixes_supported, $modes ); }

__END__
=head1 NAME

rperl Front-End Program

Restricted Perl, The Optimizing Perl 5 Compiler

=head1 SYNOPSIS

        rperl [OPTIONS] input_program_0.pl [input_program_1.pl input_program_2.pl ...]
        rperl [OPTIONS] MyClassFoo.pm [MyClassBar.pm MyClassBat.pm ...]
        rperl [OPTIONS] input_program_0.pl MyClassFoo.pm [input_program_1.pl ... MyClassBar.pm ...]

=head1 OPTIONS

=over 8

=item B<--help _OR_ -h _OR_ -?>

 Print a brief help message for command-line usage.

=item B<--version _OR_ -v>

=item B<--vversion _OR_ -vv>

 Print version number and copyright information.
 Repeat as 'vv' for more technical information, similar to `perl -V` configuration summary option.
 Lowercase 'v' not to be confused with uppercase 'V' in 'Verbose' option below.
 
=item B<--dependencies _OR_ -d>

=item B<--nodependencies _OR_ -nod>

 Follow and compile dependencies, or not.
 Enabled by default, equivalent to '--mode dependencies=ON' option.
 Lowercase 'd' not to be confused with uppercase 'D' in 'Debug' option below.
 WARNING: Disabling dependencies will likely cause errors or undefined behavior.

=item B<--infile=MyFile.pm _OR_ -i=MyFile.pm>

 Specify input file, may be repeated for multiple input files.
 Option prefix '--infile' may be entirely omitted.
 Option prefix MUST be omitted to specify wildcard for multiple input files.

=item B<--outfile=MyFile _OR_ -o=MyFile>

 Specify output file prefix, may be repeated for multiple output files.
 RPerl *.pm input file with PERL ops will create MyFile.pmc output file.
 RPerl *.pl input file with PERL ops will create my_file (or my_file.exe) & my_file.pmc output files.
 RPerl *.pm input file with CPP ops will create MyFile.pmc, MyFile.cpp, & MyFile.h output files.
 RPerl *.pl input file with CPP ops will create myfile (or myfile.exe on Windows), MyFile.pmc, MyFile.cpp, & MyFile.h output files.
 Option may be entirely omitted, 'MyFile.*' input file will default to 'MyFile.*' out.

=item B<--mode ops=PERL _OR_ -m ops=PERL>

=item B<--mode ops=CPP _OR_ -m ops=CPP>

 Specify operations mode, CPP by default.
 If set to PERL, generate Perl operations in the source code output file(s).
 If set to CPP, generate C++ operations in the source code output file(s).
 PERL ops mode forces PERL types mode & PARSE or GENERATE compile mode; PERLOPS_PERLTYPES is test mode, does not actually compile.

=item B<--mode types=PERL _OR_ -m types=PERL>

=item B<--mode types=CPP _OR_ -m types=CPP>

=item B<--mode types=DUAL _OR_ -m types=DUAL>

 Specify data types mode, CPP by default.
 If set to PERL, generate Perl data types in the source code output file(s).
 If set to CPP, generate C++ data types in the source code output file(s).
 If set to DUAL, generate both Perl and C++ data types in the source code output file(s).
 DUAL mode allows generate-once-compile-many types, selected by '#define __FOO__TYPES' in lib/rperltypes_mode.h or `gcc -D__FOO__TYPES` manual subcompile option.

=item B<--mode check=OFF _OR_ -m check=OFF>

=item B<--mode check=ON _OR_ -m check=ON>

=item B<--mode check=TRACE _OR_ -m check=TRACE>

 Specify data type checking mode, TRACE by default.
 If set to OFF, do not perform dynamic type checking, only built-in C++ static type checking.
 If set to ON, perform dynamic type checking in addition to built-in C++ static type checking.
 If set to TRACE, perform dynamic type checking in addition to built-in C++ static type checking, with subroutine-and-variable trace information.

=item B<--mode dependencies=OFF _OR_ -m dependencies=OFF>

=item B<--mode dependencies=ON _OR_ -m dependencies=ON>

 Specify dependencies mode, ON by default.
 If set to OFF, do not search for or compile dependencies.
 If set to ON, recursively search for dependencies and subdependencies, include as additional input file(s).

=item B<--mode uncompile=OFF _OR_ -m uncompile=OFF>

=item B<--mode uncompile=SOURCE _OR_ -m uncompile=SOURCE>

=item B<--mode uncompile=BINARY _OR_ -m uncompile=BINARY>

=item B<--mode uncompile=INLINE _OR_ -m uncompile=INLINE>

=item B<--mode uncompile=SOURCE_BINARY _OR_ -m uncompile=SOURCE_BINARY>

=item B<--mode uncompile=SOURCE_BINARY_INLINE _OR_ -m uncompile=SOURCE_BINARY_INLINE>

 Specify uncompile mode, OFF by default.
 If set to SOURCE, delete all generated C++ output source code (not subcompiled) files: *.cpp, *.h, *.pmc
 If set to BINARY, delete all generated C++ output binary (subcompiled) files: *.o, *.a, *.so, *.exe, non-suffixed executables
 If set to INLINE, delete all generated C++ output Inline::CPP files: _Inline/ directory
 If set to SOURCE_BINARY, delete both SOURCE and BINARY files.
 If set to SOURCE_BINARY_INLINE, delete SOURCE, BINARY, and INLINE files.
 For *.pm Perl module input files, BINARY and INLINE are equivalent.

=item B<--mode compile=PARSE _OR_ -m compile=PARSE>

=item B<--mode compile=GENERATE _OR_ -m compile=GENERATE>

=item B<--mode compile=SUBCOMPILE _OR_ -m compile=SUBCOMPILE>

 Specify compile mode, SUBCOMPILE by default.
 If set to PARSE, begin with RPerl input source code file(s), and end with RPerl abstract syntax tree output data structure.
 If set to GENERATE, begin with RPerl input source code file(s), and end with RPerl and/or C++ output source code file(s).
 If set to SUBCOMPILE, begin with RPerl input source code file(s), and end with C++ output binary file(s).

=item B<--mode subcompile=OFF _OR_ -m subcompile=OFF>

=item B<--mode subcompile=ASSEMBLE _OR_ -m subcompile=ASSEMBLE>

=item B<--mode subcompile=ARCHIVE _OR_ -m subcompile=ARCHIVE>

=item B<--mode subcompile=SHARED _OR_ -m subcompile=SHARED>

=item B<--mode subcompile=STATIC _OR_ -m subcompile=STATIC>

=item B<--mode subcompile=DYNAMIC _OR_ -m subcompile=DYNAMIC>

 Specify subcompile mode, DYNAMIC by default.
 If set to ASSEMBLE, generate *.o object binary output file(s).
 If set to ARCHIVE, generate *.a object archive binary output file(s).
 If set to SHARED, generate *.so shared object binary output file(s).
 If set to STATIC, generate statically-linked *.exe or non-suffixed executable binary output file(s).
 If set to DYNAMIC, generate dynamically-linked *.exe or non-suffixed executable binary output file(s).

=item B<--mode CXX=/path/to/compiler _OR_ -m CXX=/path/to/compiler>

 Specify path to C++ compiler for use in subcompile modes, 'g++' by default.

=item B<--mode execute=OFF _OR_ -m execute=OFF>

=item B<--mode execute=ON _OR_ -m execute=ON>

 Specify execute mode, ON by default.
 If set to OFF, do not load or run any user-supplied program(s).
 If set to ON with one *.pl Perl program input file, load and run the program.
 If set to ON with more than one *.pl Perl program input file, do not load or run any programs.

=item B<--mode label=OFF _OR_ -m label=OFF>

=item B<--mode label=ON _OR_ -m label=ON>

 Specify source section label mode, ON by default.
 If set to OFF, generate minimal output source code, may save disk space.
 If set to ON, generate some informative labels in output source code, may be more human-readable.

=item B<--uncompile _OR_ -u>

=item B<--nouncompile _OR_ -nou>

=item B<--uuncompile _OR_ -uu>

=item B<--nouuncompile _OR_ -nouu>

=item B<--uuuncompile _OR_ -uuu>

=item B<--nouuncompile _OR_ -nouuu>

 Uncompile (delete C++ source code and/or binary output files), or not.
 Repeat as 'uu' and 'uuu' for more thorough file removal.
 Do not confuse uncompile with decompile (recreate RPerl source code from C++ source code or binary output files), which does not currently exist.
 '-u' equivalent to '--mode uncompile=SOURCE --mode compile=OFF --mode execute=OFF' options.
 '-uu' equivalent to '--mode uncompile=SOURCE_BINARY --mode compile=OFF --mode execute=OFF' options.
 '-uuu' equivalent to '--mode uncompile=SOURCE_BINARY_INLINE --mode compile=OFF --mode execute=OFF' options.
 Disabled by default.

=item B<--compile _OR_ -c>

=item B<--nocompile _OR_ -noc>

 Generate & subcompile C++ source code, or not.
 Enabled by default, equivalent to '--mode compile=SUBCOMPILE' option.

=item B<--execute _OR_ -e>

=item B<--noexecute _OR_ -noe>

 Run input code after optional compile, or not.
 Enabled by default for *.pl program input files, always disabled for *.pm module input files or multiple input files.
 Equivalent to '--mode execute=ON' option.

=item B<--Verbose _OR_ -V>

=item B<--noVerbose _OR_ -noV>

 Include additional user information in output, or not.
 If enabled, equivalent to `export RPERL_VERBOSE=1` shell command.
 Disabled by default.
 Uppercase 'V' not to be confused with lowercase 'v' in 'version' option above.

=item B<--Debug _OR_ -D>

=item B<--noDebug _OR_ -noD>

 Include system diagnostic information in output, or not.
 If enabled, equivalent to `export RPERL_DEBUG=1` shell command.
 Disabled by default.
 Uppercase 'D' not to be confused with lowercase 'd' in 'dependencies' option above.

=item B<--Warnings _OR_ -W>

=item B<--noWarnings _OR_ -noW>

 Include system warnings in output, or not.
 Enabled by default, equivalent to `export RPERL_WARNINGS=0` shell command.

=item B<--test _OR_ -t>

 Test mode: Perl ops, Perl types, Parse & Generate (no Save or Compile)
 If enabled, equivalent to '--mode ops=PERL --mode types=PERL --mode compile=GENERATE' options.
 Disabled by default.

=item B<--assemble>

 Assemble subcompile mode, output *.o object file(s).
 If enabled, equivalent to '--mode subcompile=ASSEMBLE' option or `gcc -c` manual subcompile option.
 Disabled by default.

=item B<--archive>

 Archive subcompile mode, output *.a object archive file(s).
 If enabled, equivalent to '--mode subcompile=ARCHIVE' option or `gcc -c` followed by `ar` manual subcompile command.
 Disabled by default.

=item B<--shared>

 Archive subcompile mode, output *.so shared object file(s).
 If enabled, equivalent to '--mode subcompile=SHARED' option or `gcc -shared` manual subcompile command.
 Disabled by default.

=item B<--static>

=item B<--nostatic>

 Static subcompile mode, output *.exe or non-suffixed statically-linked executable file(s).
 If disabled, equivalent to '--mode subcompile=DYNAMIC' option or `gcc` manual subcompile command.
 If enabled, equivalent to '--mode subcompile=STATIC' option or `gcc -static` manual subcompile command.
 Disabled by default.

=item B<--CXX=/path/to/compiler>

 Specify path to C++ compiler, equivalent to '--mode CXX=/path/to/compiler' or 'CXX' manual Makefile option.

=back

=head1 DESCRIPTION

B<RPerl> is a compiler.  For more info:

L<https://github.com/wbraswell/rperl/blob/master/README.md>

=head1 SEE ALSO

L<RPerl>

=head1 AUTHOR

B<William N. Braswell, Jr.>

L<mailto:wbraswell@NOSPAM.cpan.org>

=cut
