# pX/Common/p6rule.t - fglock

use strict;
use warnings;

use Test::More tests => 58;
# use Data::Dumper;
# $Data::Dumper::Indent = 1;
# $Data::Dumper::Pad = '# ';

use_ok( 'Pugs::Runtime::Regex' );
use Pugs::Runtime::Match;

my ( $rule, $match );

{
  $rule = Pugs::Runtime::Regex::constant( 'a' );
  $rule->( 'a123', undef, {capture=>1}, $match );
  #print Dumper( $match );
  ok ( $match->bool, "a =~ /a/ #1" );
  is ( $match->tail, '123', "tail is ok" );
  $rule->( 'c', undef, {capture=>1}, $match );
  ok ( ! $match->bool, "c =~ /a/ #2" );
  #is ( $match->tail, 'c123', "tail is ok" );
  #print Dumper( $match );
  $rule->( 'ca', undef, {}, $match);
  ok( !$match->bool, "anchored match" );
}

{
  $rule = 
    Pugs::Runtime::Regex::non_greedy_plus( 
      Pugs::Runtime::Regex::alternation( [
        Pugs::Runtime::Regex::constant( 'a' ), 
        Pugs::Runtime::Regex::constant( 'c' ), 
      ] ),
    );
  $rule->( 'a123', undef, {capture=>1}, $match );
  ok ( $match->bool, "/[a|c]+?/ #1" );
  is ( $match->tail, '123', "tail is ok" );
  $rule->( 'c123', undef, {capture=>1}, $match );
  ok ( $match->bool, "/[a|c]+?/ #2" );
  is ( $match->tail, '123', "tail is ok" );
  #print Dumper( $match );
  $rule->( 'aa123', undef, {capture=>1}, $match );
  ok ( $match->bool, "/[a|c]+?/ #3" );
  is ( $match->tail, 'a123', "tail is ok" );
}

{
  # -- continuations in alternation()
  $rule = 
      Pugs::Runtime::Regex::alternation( [
        Pugs::Runtime::Regex::constant( 'a' ), 
        Pugs::Runtime::Regex::constant( 'ab' ), 
      ] );
  $rule->( 'ab', undef, {}, $match );
  #print "state: ", Dumper($match->state), "\n";
  is ( $match->str, 'a', "/[a|ab]/ multi-match continuation state #0" );
  $rule->( 'ab', $match->state, {}, $match );
  #print "state: ", Dumper($match->state), "\n";
  is ( $match->str, 'ab', "/[a|ab]/ multi-match continuation state #1" );
  #$rule->( 'ab', $match->state, {}, $match );
  #print "state: ", Dumper($match->state), "\n";
  #is ( $match->str, '', "/[a|ab]/ multi-match state #2" );
  #print Dumper( $match );
}

{
  # -- continuations in concat()
  $rule = 
    Pugs::Runtime::Regex::concat( [
      Pugs::Runtime::Regex::alternation( [
        Pugs::Runtime::Regex::constant( 'a' ), 
        Pugs::Runtime::Regex::constant( 'ab' ), 
      ] ),
      Pugs::Runtime::Regex::alternation( [
        Pugs::Runtime::Regex::constant( 'b' ), 
        Pugs::Runtime::Regex::constant( 'bb' ), 
      ] ),
    ] );
  my $str = 'abbb';
  $rule->( $str, undef, {}, $match );
  #print "state 1: ", Dumper($match->state), "\n";
  is ( $match->str, 'ab', "/[a|ab][b|bb]/ continuation state #0" );

  $rule->( $str, $match->state, {}, $match );
  #print "state 2: ", Dumper($match->state), "\n";
  is ( $match->str, 'abb', "state #1" );

  $rule->( $str, $match->state, {}, $match );
  #print "state 3: ", Dumper($match->state), "\n";
  is ( $match->str, 'abb', "state #2" );

  $rule->( $str, $match->state, {}, $match );
  #print "state 4: ", Dumper($match->state), "\n";
  is ( $match->str, 'abbb', "state #3" );

}

{
  $rule = 
    Pugs::Runtime::Regex::greedy_star( 
      Pugs::Runtime::Regex::constant( 'a' ) 
    );
  is ( ref $rule, "CODE", "rule 'a*' is a coderef" );
  $rule->( 'aa', undef, {}, $match );
  #print Dumper( $match );
  ok ( $match->bool, "/a*/" );
  #print Dumper( $match );
  is ( $match->str, 'aa' );
  $rule->( 'aaaaab', undef, {}, $match );
  ok ($match->bool, "/a*/" );
  is ($match->str, 'aaaaa');
  $rule->( '', undef, {}, $match );
  ok ( $match->bool, "matches 0 occurrences" );
  #print Dumper( $match );
}

{
  $rule = 
    Pugs::Runtime::Regex::greedy_plus( 
      Pugs::Runtime::Regex::constant( 'a' ) 
    );
  $rule->( 'aa', undef, {}, $match );
  ok ( $match->bool, "/a+/" );
  is ( $match->str, 'aa' );
  $rule->( '!!', undef, {}, $match );
  ok ( ! $match->bool, "rejects unmatching text" );
}

{
  $rule = 
    Pugs::Runtime::Regex::greedy_plus( 
      Pugs::Runtime::Regex::constant( 'a' ),
      3, 
    );
  $rule->( 'aaaa', undef, {}, $match );
  is ( "$match", "aaaa", "/a**{3..*}/" );
  $rule->( 'aaa', undef, {}, $match );
  is ( "$match", "aaa", "/a**{3..*}/" );
  $rule->( 'aa', undef, {}, $match );
  ok ( ! $match->bool, "rejects unmatching text" );
}

{
  $rule = 
    Pugs::Runtime::Regex::concat( 
      Pugs::Runtime::Regex::greedy_plus( 
        Pugs::Runtime::Regex::alternation( [
          Pugs::Runtime::Regex::constant( 'a' ), 
          Pugs::Runtime::Regex::constant( 'c' ), 
        ] ),
      ),
      Pugs::Runtime::Regex::constant( 'ab' )
     );
  $rule->( 'aacaab', undef, {}, $match );
  ok ( $match->bool, "/[a|c]+ab/ with backtracking" );
  is ( $match->str, 'aacaab', 'all the chars accepted' );
  # print Dumper( $match );
}

{
  $rule = 
    Pugs::Runtime::Regex::non_greedy_plus( 
      Pugs::Runtime::Regex::alternation( [
        Pugs::Runtime::Regex::constant( 'a' ), 
        Pugs::Runtime::Regex::constant( 'c' ), 
      ] ),
    );
  $rule->( 'aacaab', undef, {capture=>1}, $match );
  ok ( $match, "/[a|c]+?/" );
  is ( $match->tail, 'acaab', "tail is ok" );
  #print Dumper( $match );
  $rule->( 'cacab', undef, {}, $match );
  ok $match->bool;
  is $match->str, 'c';
}

{
  $rule = 
    Pugs::Runtime::Regex::concat(
      Pugs::Runtime::Regex::non_greedy_plus( 
        Pugs::Runtime::Regex::alternation( [
          Pugs::Runtime::Regex::constant( 'a' ), 
          Pugs::Runtime::Regex::constant( 'c' ), 
        ] ),
      ),
      Pugs::Runtime::Regex::constant( 'cb' )
    );
  $rule->( 'aacacb', undef, {capture=>1}, $match );
  ok ( defined $match, "/[a|c]+?cb/ with backtracking" );
  #print Dumper( $match );
  is $match->str, 'aacacb';
  is $match->tail, '';
}

{
  # tests for a problem found in the '|' implementation in p6rule parser
  
  my $rule = 
    Pugs::Runtime::Regex::constant( 'a' );
  my $alt = 
    Pugs::Runtime::Regex::concat(
        $rule,
        Pugs::Runtime::Regex::optional (
            Pugs::Runtime::Regex::concat(
                Pugs::Runtime::Regex::constant( '|' ),
                $rule
            )
        )
    );
  $alt->( 'a', undef, {capture=>1}, $match );
  ok ( defined $match, "/a[\|a]?/ #1" );
  is $match->str, 'a';
  $alt->( 'a|a', undef, {capture=>1}, $match );
  ok ( defined $match, "/a[\|a]?/ #2" );
  is $match->str, 'a|a';
  $alt->( 'a|a|a', undef, {capture=>1}, $match );
  ok ( defined $match, "/a[\|a]?/ #3" );
  is $match->str, 'a|a';

  # adding '*' caused a deep recursion error (fixed)

  $alt = 
    Pugs::Runtime::Regex::concat(
        $rule,
        Pugs::Runtime::Regex::greedy_star(
          Pugs::Runtime::Regex::concat(
              Pugs::Runtime::Regex::constant( '|' ),
              $rule
          )
        )
    );
  $alt->( 'a', undef, {capture=>1}, $match );
  ok ( $match, "/a[\|a]*/ #1" );
  is $match->str, 'a';
  $alt->( 'a|a', undef, {capture=>1}, $match );
  ok ( $match, "/a[\|a]*/ #2" );
  is $match->str, 'a|a';
  $alt->( 'a|a|a', undef, {capture=>1}, $match );
  ok ( $match, "/a[\|a]*/ #3" );
  is $match->str, 'a|a|a';
}

{
    # ranges

  $rule = 
    Pugs::Runtime::Regex::concat(
      Pugs::Runtime::Regex::non_greedy_plus( 
        Pugs::Runtime::Regex::alternation( [
          Pugs::Runtime::Regex::constant( 'a' ), 
          Pugs::Runtime::Regex::constant( 'c' ), 
        ] ),
        2, 4,  # range
      ),
      Pugs::Runtime::Regex::constant( 'cb' )
    );
  $rule->( 'aacacb', undef, {capture=>1}, $match );
  ok ( defined $match, "/[a|c]**{2..4}?cb/ with backtracking" );
  #print Dumper( $match );
  #print "Match: $match \n"; # 
  is ( "$match", "aacacb", "/[a|c]**{2..4}?cb/ with range" );
  $rule->( 'aacb', undef, {}, $match);
  is "$match", "aacb", 'a**{2..2}cb';
  $rule->( 'cccb', undef, {}, $match);
  is "$match", 'cccb', 'c**{2..2}cb';
  $rule->( 'caacb', undef, {}, $match);
  is "$match", 'caacb', '[a|c]**{3..3}cb';

  $rule = 
    Pugs::Runtime::Regex::concat(
      Pugs::Runtime::Regex::non_greedy_plus( 
        Pugs::Runtime::Regex::alternation( [
          Pugs::Runtime::Regex::constant( 'a' ), 
          Pugs::Runtime::Regex::constant( 'c' ), 
        ] ),
        1, 2,  # range
      ),
      Pugs::Runtime::Regex::constant( 'cb' )
    );
  $rule->( 'aacacb', undef, {capture=>1}, $match );
  ok ( $match ? 0 : 1, "/[a|c]**{1..2}?cb/ with bad range fails" );

  $rule = 
    Pugs::Runtime::Regex::concat(
      Pugs::Runtime::Regex::non_greedy_plus( 
        Pugs::Runtime::Regex::alternation( [
          Pugs::Runtime::Regex::constant( 'a' ), 
          Pugs::Runtime::Regex::constant( 'c' ), 
        ] ),
        5, 7,  # range
      ),
      Pugs::Runtime::Regex::constant( 'cb' )
    );
  $rule->( 'aacacb', undef, {capture=>1}, $match );
  ok ( $match ? 0 : 1, "/[a|c]**{5..7}?cb/ with bad range fails" );
}

{
  # -- concat() empty array
  $rule = 
    Pugs::Runtime::Regex::concat( [] );
  my $str = 'abbb';
  $rule->( $str, undef, {}, $match );
  #print "state 1: ", Dumper($match->state), "\n";
  is ( $match->str, '', "empty concat" );
}

