File Coverage

blib/lib/Regexp/Common/balanced.pm
Criterion Covered Total %
statement 44 44 100.0
branch 8 8 100.0
condition n/a
subroutine 6 6 100.0
pod 0 1 0.0
total 58 59 98.3


line stmt bran cond sub pod time code
1             package Regexp::Common::balanced; {
2              
3 71     71   699 use 5.10.0;
  71         174  
4              
5 71     71   233 use strict;
  71         78  
  71         1192  
6 71     71   215 use warnings;
  71         75  
  71         1616  
7 71     71   209 no warnings 'syntax';
  71         93  
  71         2150  
8              
9 71     71   230 use Regexp::Common qw /pattern clean no_defaults/;
  71         80  
  71         360  
10              
11             our $VERSION = '2016060801';
12              
13             my %closer = ( '{'=>'}', '('=>')', '['=>']', '<'=>'>' );
14             my %cache;
15              
16             sub nested {
17 17     17 0 18 my ($start, $finish) = @_;
18              
19 17 100       46 return $cache {$start} {$finish} if exists $cache {$start} {$finish};
20              
21 12         56 my @starts = map {s/\\(.)/$1/g; $_} grep {length}
  15         22  
  15         26  
  15         18  
22             $start =~ /([^|\\]+|\\.)+/gs;
23 12         33 my @finishes = map {s/\\(.)/$1/g; $_} grep {length}
  14         18  
  14         21  
  14         20  
24             $finish =~ /([^|\\]+|\\.)+/gs;
25              
26 12         24 push @finishes => ($finishes [-1]) x (@starts - @finishes);
27              
28 12         11 my @re;
29 12         15 local $" = "|";
30 12         17 foreach my $begin (@starts) {
31 15         16 my $end = shift @finishes;
32              
33 15         16 my $qb = quotemeta $begin;
34 15         15 my $qe = quotemeta $end;
35 15         18 my $fb = quotemeta substr $begin => 0, 1;
36 15         15 my $fe = quotemeta substr $end => 0, 1;
37              
38 15         18 my $tb = quotemeta substr $begin => 1;
39 15         16 my $te = quotemeta substr $end => 1;
40              
41 15         8 my $add;
42 15 100       27 if ($fb eq $fe) {
43 1         6 push @re =>
44             qq /(?:$qb(?:(?>[^$fb]+)|$fb(?!$tb)(?!$te)|(?-1))*$qe)/;
45             }
46             else {
47 14         66 my @clauses = "(?>[^$fb$fe]+)";
48 14 100       31 push @clauses => "$fb(?!$tb)" if length $tb;
49 14 100       22 push @clauses => "$fe(?!$te)" if length $te;
50 14         16 push @clauses => "(?-1)";
51 14         46 push @re => qq /(?:$qb(?:@clauses)*$qe)/;
52             }
53             }
54              
55 12         336 $cache {$start} {$finish} = qr /(@re)/;
56             }
57              
58              
59             pattern name => [qw /balanced -parens=() -begin= -end=/],
60             create => sub {
61             my $flag = $_[1];
62             unless (defined $flag -> {-begin} && length $flag -> {-begin} &&
63             defined $flag -> {-end} && length $flag -> {-end}) {
64             my @open = grep {index ($flag->{-parens}, $_) >= 0}
65             ('[','(','{','<');
66             my @close = map {$closer {$_}} @open;
67             $flag -> {-begin} = join "|" => @open;
68             $flag -> {-end} = join "|" => @close;
69             }
70             return nested @$flag {qw /-begin -end/};
71             },
72             ;
73              
74             }
75              
76             1;
77              
78             __END__