The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use strict;
use warnings;
no  warnings 'syntax';

use Regexp::Common;
use Test::More;

my $r = eval "require Test::Regexp; 1";

unless ($r) {
    print "1..0 # SKIP Test::Regexp not found\n";
    exit;
}


sub make_test {
    my ($name, $base, @options) = @_;
    my $pat = $base;
    while (@options) {
        my $opt = shift @options;
        if (@options && $options [0] !~ /^-/) {
            my $val = shift @options;
            $pat = $$pat {$opt => $val};
            $name .= ", $opt => $val";
        }
        else {
            $pat = $$pat {$opt};
            $name .= ", $opt";
        }
    }
    my $keep = $$pat {-keep};
    Test::Regexp:: -> new -> init (
        pattern      => $pat,
        keep_pattern => $keep,
        name         => $name,
    );
}


#
# Some basic patterns: plain integers, signed integers, unsigned integers.
#
my $integer          = make_test "Integer pattern" => $RE {num} {int};
my $signed_integer   = make_test "Integer pattern" => $RE {num} {int},
                                                      -sign => '[-+]';
my $unsigned_integer = make_test "Integer pattern" => $RE {num} {int},
                                                      -sign => '';


foreach my $digit (0 .. 9) {
    $integer -> match (  $digit,  [  $digit,  "",  $digit],
                          test => "Single digit");
    $integer -> match ("-$digit", ["-$digit", "-", $digit],
                          test => "Single digit, with minus sign");
    $integer -> match ("+$digit", ["+$digit", "+", $digit],
                          test => "Single digit, with plus sign");

    $signed_integer -> no_match ($digit, reason => "No sign");
    $signed_integer -> match ("-$digit", ["-$digit", "-", $digit],
                                 test => "Single digit, with minus sign");
    $signed_integer -> match ("+$digit", ["+$digit", "+", $digit],
                                 test => "Single digit, with plus sign");

    $unsigned_integer -> match (  $digit,  [  $digit,  "",  $digit],
                                   test => "Single digit");
    $unsigned_integer -> no_match ("-$digit", reason => "Number has - sign");
    $unsigned_integer -> no_match ("+$digit", reason => "Number has + sign");
}

foreach my $letter ('A' .. 'Z') {
    my $reason = "Cannot match letters";
    $integer          -> no_match (  $letter,  reason => $reason);
    $integer          -> no_match ("-$letter", reason => $reason);
    $integer          -> no_match ("+$letter", reason => $reason);
    $signed_integer   -> no_match (  $letter,  reason => $reason);
    $signed_integer   -> no_match ("-$letter", reason => $reason);
    $signed_integer   -> no_match ("+$letter", reason => $reason);
    $unsigned_integer -> no_match (  $letter,  reason => $reason);
    $unsigned_integer -> no_match ("-$letter", reason => $reason);
    $unsigned_integer -> no_match ("+$letter", reason => $reason);
}

my @numbers = qw [
    123456789 987654321 00 00000 
    918710985710984523480938457287510917634178356017501984571273461782346
    2109381270129857102931405984051817410923193913810985
    123981098509850493582357010910371947524594785923602871749187249504395
    000000000000000000000000000000000000000000000000000000000000000000000001
    12890991823457 09857109247120 0000000090000000000000009000000000000000
];
my @big_numbers = (
    '123456789' x 100,
    '0' x 10_000,
);
my @failures = (
    [" 12345"       =>  "Leading space"],
    ["123 "         =>  "Trailing space"],
    ["-+1234"       =>  "Double sign"],
    ["--54311"      =>  "Double sign"],
    ["- 897"        =>  "Space after sign"],
    [""             =>  "Empty string"],
    ["-"            =>  "Sign only"],
    ["1234 678"     =>  "Space in number"],
    ["1234+678"     =>  "Sign in number"],
    ["678A90"       =>  "Letter in number"],
    ["0x1234"       =>  "Hex number"],
    ["0b1234"       =>  "Octal number"],
    ["Bla bla"      =>  "Garbage"],
);

foreach my $number (@numbers) {
    $integer -> match (  $number , [  $number , "",  $number],
                          test => "Unsigned number");
    $integer -> match ("-$number", ["-$number", "-", $number],
                          test => "Number with minus sign");
    $integer -> match ("+$number", ["+$number", "+", $number],
                          test => "Number with plus sign");
    $signed_integer -> no_match ($number, reason => "Number is unsigned");
    $signed_integer -> match ("-$number", ["-$number", "-", $number],
                                 test => "Number with minus sign");
    $signed_integer -> match ("+$number", ["+$number", "+", $number],
                                 test => "Number with plus sign");
    $unsigned_integer -> match (  $number , [  $number , "",  $number],
                                   test => "Unsigned number");
    $unsigned_integer -> no_match ("-$number", reason => "Number has - sign");
    $unsigned_integer -> no_match ("+$number", reason => "Number has + sign");
}


foreach my $number (@big_numbers) {
    $integer -> match (  $number , [  $number , "",  $number],
                          test => "Unsigned big number");
    $integer -> match ("-$number", ["-$number", "-", $number],
                          test => "Big number with minus sign");
    $integer -> match ("+$number", ["+$number", "+", $number],
                          test => "Big number with plus sign");
    $signed_integer -> no_match ($number, reason => "Number is unsigned");
    $signed_integer -> match ("-$number", ["-$number", "-", $number],
                                 test => "Big number with minus sign");
    $signed_integer -> match ("+$number", ["+$number", "+", $number],
                                 test => "Big number with plus sign");
    $unsigned_integer -> match (  $number , [  $number , "",  $number],
                                   test => "Unsigned big number");
    $unsigned_integer -> no_match ("-$number", reason => "Number has - sign");
    $unsigned_integer -> no_match ("+$number", reason => "Number has + sign");
}

foreach my $failure (@failures) {
    my ($subject, $reason) = @$failure;
    $integer        -> no_match ($subject, reason => $reason);
    $signed_integer -> no_match ($subject, reason => $reason);
}

done_testing ();



__END__