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 72     72   647 use 5.10.0;
  72         161  
4              
5 72     72   304 use strict;
  72         95  
  72         1271  
6 72     72   220 use warnings;
  72         91  
  72         1744  
7 72     72   330 no warnings 'syntax';
  72         171  
  72         2368  
8              
9 72     72   254 use Regexp::Common qw /pattern clean no_defaults/;
  72         113  
  72         476  
10              
11             our $VERSION = '2017040401';
12              
13             my %closer = ( '{'=>'}', '('=>')', '['=>']', '<'=>'>' );
14             my %cache;
15              
16             sub nested {
17 17     17 0 25 my ($start, $finish) = @_;
18              
19 17 100       50 return $cache {$start} {$finish} if exists $cache {$start} {$finish};
20              
21 12         68 my @starts = map {s/\\(.)/$1/g; $_} grep {length}
  15         23  
  15         28  
  15         23  
22             $start =~ /([^|\\]+|\\.)+/gs;
23 12         39 my @finishes = map {s/\\(.)/$1/g; $_} grep {length}
  14         15  
  14         22  
  14         16  
24             $finish =~ /([^|\\]+|\\.)+/gs;
25              
26 12         25 push @finishes => ($finishes [-1]) x (@starts - @finishes);
27              
28 12         13 my @re;
29 12         14 local $" = "|";
30 12         21 foreach my $begin (@starts) {
31 15         11 my $end = shift @finishes;
32              
33 15         20 my $qb = quotemeta $begin;
34 15         15 my $qe = quotemeta $end;
35 15         19 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         11 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         28 my @clauses = "(?>[^$fb$fe]+)";
48 14 100       28 push @clauses => "$fb(?!$tb)" if length $tb;
49 14 100       25 push @clauses => "$fe(?!$te)" if length $te;
50 14         13 push @clauses => "(?-1)";
51 14         50 push @re => qq /(?:$qb(?:@clauses)*$qe)/;
52             }
53             }
54              
55 12         375 $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__