# 09_debug.t
#
# Test suite for Regexp::Assemble
# Exercise the debug parts
#
# copyright (C) 2006-2011 David Landgren

use strict;

eval qq{use Test::More tests => 68};
if( $@ ) {
    warn "# Test::More not available, no tests performed\n";
    print "1..1\nok 1\n";
    exit 0;
}

use Regexp::Assemble;

my $PERL_VERSION_TOO_HIGH = ($] >= 5.013);

my $fixed = 'The scalar remains the same';
$_ = $fixed;

{
    my $r = Regexp::Assemble->new( debug => 15 );
    is( $r->{debug}, 15, 'debug new(n)' );
    $r->debug( 0 );
    is( $r->{debug}, 0, 'debug(0)' );
    $r->debug( 4 );
    is( $r->{debug}, 4, 'debug(4)' );
    $r->debug();
    is( $r->{debug}, 0, 'debug()' );
}

{
    my $u = Regexp::Assemble->new(unroll_plus => 1)->debug(4);
    my $str;

    $u->add( "[a]", );
    $str = $u->as_string;
    is( $str, 'a', '[a] -> a' );

    $u->add( "a+b", 'ac' );
    $str = $u->as_string;
    is( $str, 'a(?:a*b|c)', 'unroll plus a+b ac' );

    $u->add( "\\LA+B", "ac" );
    $str = $u->as_string;
    is( $str, 'a(?:a*b|c)', 'unroll plus \\LA+B ac' );

    $u->add( '\\Ua+?b', "AC" );
    $str = $u->as_string;
    is( $str, 'A(?:A*?B|C)', 'unroll plus \\Ua+?b AC' );

    $u->add( "\\d+d", "\\de" );
    $str = $u->as_string;
    is( $str, '\\d(?:\d*d|e)', 'unroll plus \\d+d \\de' );

    $u->add( "\\xab+f", "\\xabg" );
    $str = $u->as_string;
    is( $str, "\xab(?:\xab*f|g)", 'unroll plus \\xab+f \\xabg' );

    $u->add( "[a-e]+h", "[a-e]i" );
    $str = $u->as_string;
    is( $str, "[a-e](?:[a-e]*h|i)", 'unroll plus [a-e]+h [a-e]i' );

    $u->add( "a+b" );
    $str = $u->as_string;
    is( $str, "a+b", 'reroll a+b' );

    $u->add( "a+b", "a+" );
    $str = $u->as_string;
    is( $str, "a+b?", 'reroll a+b?' );

    $u->add( "a+?b", "a+?" );
    $str = $u->as_string;
    is( $str, "a+?b?", 'reroll a+?b?' );

    $u->add( qw(defused fused used) );
    $str = $u->as_string;
    is( $str, "(?:(?:de)?f)?used", 'big debug block in _insert_path()' );
}

{
    my $str = '\t+b*c?\\x41';
    is_deeply( Regexp::Assemble->new->debug(4)->_lex( $str ),
        [ '\t+', 'b*', 'c?', 'A' ],
        "_lex $str",
    );

    $str = '\Q[';
    is_deeply( Regexp::Assemble->new->debug(4)->_lex( $str ),
        [ '\\[' ],
        "_lex $str",
    );

    $str = '\Q]';
    is_deeply( Regexp::Assemble->new->debug(4)->_lex( $str ),
        [ '\\]' ],
        "_lex $str",
    );

    $str = '\Q(';
    is_deeply( Regexp::Assemble->new->debug(4)->_lex( $str ),
        [ '\\(' ],
        "_lex $str",
    );

    $str = '\Q)';
    is_deeply( Regexp::Assemble->new->debug(4)->_lex( $str ),
        [ '\\)' ],
        "_lex $str",
    );

    $str = '\Qa+b*c?';
    is_deeply( Regexp::Assemble->new->debug(4)->_lex( $str ),
        [ 'a', '\+', 'b', '\*', 'c', '\?' ],
        "_lex $str",
    );

    $str = 'a\\LBC\\Ude\\Ef\\Qg+';
    is_deeply( Regexp::Assemble->new->debug(4)->_lex( $str  ),
        [ 'a', 'b', 'c', 'D', 'E', 'f', 'g', '\\+' ],
        "_lex $str",
    );

    $str = 'a\\uC';
    is_deeply( Regexp::Assemble->new(debug => 4) ->_lex( $str  ),
        [ 'a', 'C' ],
        "_lex $str",
    );

    $str = '\Q\/?';
    is_deeply( Regexp::Assemble->new->debug(4)->_lex( $str  ), [ '\/', '\?' ], "_lex $str" );

    $str = 'p\\L\\QA+\\EZ';
    is_deeply( Regexp::Assemble->new->debug(4)->add( $str )->_path,
        [ 'p', 'a', '\\+', 'Z' ], "add $str" );

    $str = '^\Qa[b[';
    is_deeply( Regexp::Assemble->new->debug(15)->add( $str )->_path,
        [ '^', 'a', '\\[', 'b', '\\[' ], "add $str" );
}

{
    my $r = Regexp::Assemble->new->debug(4)->add('\x45');
    is_deeply( $r->_path, [ 'E' ], '_lex(\\x45) with debug' );
}

{
    my $ra = Regexp::Assemble->new(debug => 1);
    $ra->insert( undef );
    is_deeply( $ra->_path, [{'' => undef}], 'insert(undef)' );
}

{
    my $r = Regexp::Assemble->new(lex => '\\d');
    is_deeply( $r->debug(4)->add( '67abc123def+' )->_path,
        [ '6', '7', 'abc', '1', '2', '3', 'def+' ],
        '67abc123def+ with \\d lexer',
    );
    is_deeply( $r->reset->debug(0)->add( '67ab12de+' )->_path,
        [ '6', '7', 'ab', '1', '2', 'de+' ],
        '67ab12de+ with \\d lexer',
    );
}

{
    my $r = Regexp::Assemble->new(lex => '\\d');
    is_deeply( $r->debug(4)->add( '67\\Q1a*\\E12jk' )->_path,
        [ '6', '7', '1', 'a', '\\*', '1', '2', 'jk' ],
        '67\\Q1a*\\E12jk with \\d lexer',
    );
}

{
    my $r = Regexp::Assemble->new(lex => '\\d');
    is_deeply( $r->debug(4)->add( '67\\Q1a*45k+' )->_path,
        [ '6', '7', '1', 'a', '\\*', '4', '5', 'k', '\\+' ],
        '67\\Q1a*45k+ with \\d lexer',
    );
}

{
    my $r = Regexp::Assemble->new(lex => '\\d');
    is_deeply( $r->debug(4)->add( '7\U6a' )->_path,
        [ '7', '6', 'A' ],
        '7\\U6a with \\d lexer',
    );
}

{
    my $r = Regexp::Assemble->new(lex => '\\d');
    is_deeply( $r->debug(4)->add( '8\L9C' )->_path,
        [ '8', '9', 'c' ],
        '8\\L9C with \\d lexer',
    );
}

{
    my $r = Regexp::Assemble->new(lex => '\\d');
    is_deeply( $r->debug(4)->add( '57\\Q2a+23d+' )->_path,
        [ '5', '7', '2', 'a', '\\+', '2', '3', 'd', '\\+' ],
        '57\\Q2a+23d+ with \\d lexer',
    );
}

{
    my $save = $Regexp::Assemble::Default_Lexer;
    Regexp::Assemble::Default_Lexer('\\d');
    my $r = Regexp::Assemble->new;
    is_deeply( $r->debug(4)->add( '67\\Uabc\\E123def' )->_path,
        [ '6', '7', '\\Uabc\\E', '1', '2', '3', 'def' ],
        '67\Uabc\\E123def with \\d lexer',
    );

    is_deeply( $r->reset->add( '67\\Q(?:a)?\\E123def' )->_path,
        [ '6', '7', '\\Q(?:a)?\\E', '1', '2', '3', 'def' ],
        '67\Uabc\\E123def with \\d lexer',
    );

    $Regexp::Assemble::Default_Lexer = $save;
}

is( Regexp::Assemble->new->debug(1)->add( qw/
        0\.0 0\.2 0\.7 0\.01 0\.003
    / )->as_string(indent => 4),
'0\.
(?:
    0
    (?:
        03
        |1
    )
    ?
    |[27]
)'
, 'pretty 0.0 0.2 0.7 0.01 0.003' );

{
    my $ra = Regexp::Assemble->new->debug(3);

    is( $ra->add( qw/ dog darkness doggerel dark / )->as_string,
        'd(?:ark(?:ness)?|og(?:gerel)?)' );

    is( $ra->add( qw/ limit lit / )->as_string,
        'l(?:im)?it' );

    is( $ra->add( qw/ seafood seahorse sea / )->as_string,
        'sea(?:horse|food)?' );

    is( $ra->add( qw/ bird cat dog elephant fox / )->as_string,
        '(?:(?:elephan|ca)t|bird|dog|fox)' );

    is( $ra->add( qw/ bit bat sit sat fit fat / )->as_string,
        '[bfs][ai]t' );

    is( $ra->add( qw/ split splat slit slat flat flit / )->as_string,
        '(?:sp?|f)l[ai]t' );

    is( $ra->add( qw/bcktx bckx bdix bdktx bdkx/ )
        ->as_string, 'b(?:d(?:kt?|i)|ckt?)x',
        'bcktx bckx bdix bdktx bdkx' );

    is( $ra->add( qw/gait grit wait writ /)->as_string,
        '[gw][ar]it' );

    is( $ra->add( qw/gait grit lit limit /)->as_string,
        '(?:l(?:im)?|g[ar])it' );

    is( $ra->add( qw/bait brit frit gait grit tait wait writ /)->as_string,
        '(?:[bgw][ar]|fr|ta)it' );

    is( $ra->add( qw/schoolkids acids acidoids/ )->as_string,
        '(?:ac(?:ido)?|schoolk)ids' );

    is( $ra->add( qw/schoolkids acidoids/ )->as_string,
        '(?:schoolk|acido)ids' );

    is( $ra->add( qw/nonschoolkids nonacidoids/ )->as_string,
        'non(?:schoolk|acido)ids' );

    is( $ra->add( qw/schoolkids skids acids acidoids/ )->as_string,
        '(?:s(?:chool)?k|ac(?:ido)?)ids' );

    is( $ra->add( qw/kids schoolkids skids acids acidoids/ )->as_string,
        '(?:(?:s(?:chool)?)?k|ac(?:ido)?)ids' );

    is( $ra->add( qw(abcd abd acd ad bcd bd d) )->as_string,
        '(?:(?:ab?|b)c?)?d', 'abcd abd acd ad bcd bd d',
        'indentical nodes in sub_path/insert_node(bifurc)');

    is( $ra->add( qw(^a$ ^ab$ ^abc$ ^abd$ ^bdef$ ^bdf$ ^bef$ ^bf$) )->as_string,
        '^(?:a(?:b[cd]?)?|bd?e?f)$', 'fused node');

    is( $ra->add(qw[bait brit frit gait grit tait wait writ])->as_string,
        '(?:[bgw][ar]|fr|ta)it', 'after _insert_path()');

    is( $ra->add(qw(0 1 10 100))->as_string,
        '(?:1(?:0?0)?|0)', '_scan_node slid' );

    is( $ra->add( qw(abcd abd bcd bd d) )->as_string,
        '(?:a?bc?)?d', 'abcd abd bcd bd d' );
}

SKIP: {
    skip("perl version too recent ($]), 5.012+ max", 2) if $PERL_VERSION_TOO_HIGH;
    {
        my $r = Regexp::Assemble->new->debug(8)->add(qw(this that));
        my $re = $r->re;
        is( $re, '(?-xism:th(?:at|is))', 'time debug' );
    }

    {
        my $r = Regexp::Assemble->new->add(qw(this that))->debug(8)->add('those');
        my $re = $r->re;
        is( $re, '(?-xism:th(?:ose|at|is))', 'deferred time debug' );
    }
}

{
    my $r = Regexp::Assemble->new->debug(8)->add(qw(this that those));
    # sabotage
    delete $r->{_begin_time};
    is( $r->as_string, 'th(?:ose|at|is)', 'time debug mangle' );

    # use internal time() instead of Time::HiRes
    delete $r->{_time_func};
    $r->{_use_time_hires} = 'more sabotage';
    $r->reset->add(qw(abc ac));
    is( $r->as_string, 'ab?c', 'internal time debug' );
}

is_deeply( Regexp::Assemble->new->debug(4)->_fastlex('ab+c{2,4}'),
    ['a', 'b+', 'c{2,4}'],
    '_fastlex reg plus min-max'
);

my $x;
is_deeply( $x = Regexp::Assemble->new->debug(4)->_fastlex('\\d+\\s{3,4}?\\Qa+\\E\\lL\\uu\\Ufo\\E\\Lba\\x40'),
    ['\\d+', '\\s{3,4}?', 'a', '\\+', qw(l U F O b a @)],
    '_fastlex backslash'
) or diag("@$x");

is_deeply( Regexp::Assemble->new->debug(4)->_fastlex('\\Q\\L\\Ua+\\E\\Ub?\\Ec'),
    [qw(a \\+ B? c)], '_fastlex in and out of quotemeta'
);

is_deeply( $x = Regexp::Assemble->new->debug(4)->_fastlex('\\bw[0-5]*\\\\(?:x|y){,5}?\\'),
    [qw(\\b w [0-5]* \\\\), '(?:x|y){,5}?'], '_fastlex more metachars'
) or diag("@$x");

is_deeply( $x = Regexp::Assemble->new(debug => 4)->_fastlex('\\cG\\007'),
    [qw(\\cG \\cG)], '_fastlex backslash misc'
) or diag("@$x");

is( $_, $fixed, '$_ has not been altered' );
