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   842 use 5.10.0;
  72         256  
4              
5 72     72   421 use strict;
  72         156  
  72         1443  
6 72     72   396 use warnings;
  72         170  
  72         1893  
7 72     72   341 no warnings 'syntax';
  72         162  
  72         2549  
8              
9 72     72   395 use Regexp::Common qw /pattern clean no_defaults/;
  72         193  
  72         490  
10              
11             our $VERSION = '2017060201';
12              
13             my %closer = ( '{'=>'}', '('=>')', '['=>']', '<'=>'>' );
14             my %cache;
15              
16             sub nested {
17 17     17 0 41 my ($start, $finish) = @_;
18              
19 17 100       70 return $cache {$start} {$finish} if exists $cache {$start} {$finish};
20              
21 12         84 my @starts = map {s/\\(.)/$1/g; $_} grep {length}
  15         39  
  15         47  
  15         43  
22             $start =~ /([^|\\]+|\\.)+/gs;
23 12         56 my @finishes = map {s/\\(.)/$1/g; $_} grep {length}
  14         30  
  14         36  
  14         33  
24             $finish =~ /([^|\\]+|\\.)+/gs;
25              
26 12         33 push @finishes => ($finishes [-1]) x (@starts - @finishes);
27              
28 12         23 my @re;
29 12         26 local $" = "|";
30 12         29 foreach my $begin (@starts) {
31 15         31 my $end = shift @finishes;
32              
33 15         33 my $qb = quotemeta $begin;
34 15         28 my $qe = quotemeta $end;
35 15         35 my $fb = quotemeta substr $begin => 0, 1;
36 15         28 my $fe = quotemeta substr $end => 0, 1;
37              
38 15         39 my $tb = quotemeta substr $begin => 1;
39 15         33 my $te = quotemeta substr $end => 1;
40              
41 15         24 my $add;
42 15 100       42 if ($fb eq $fe) {
43 1         12 push @re =>
44             qq /(?:$qb(?:(?>[^$fb]+)|$fb(?!$tb)(?!$te)|(?-1))*$qe)/;
45             }
46             else {
47 14         45 my @clauses = "(?>[^$fb$fe]+)";
48 14 100       41 push @clauses => "$fb(?!$tb)" if length $tb;
49 14 100       40 push @clauses => "$fe(?!$te)" if length $te;
50 14         37 push @clauses => "(?-1)";
51 14         66 push @re => qq /(?:$qb(?:@clauses)*$qe)/;
52             }
53             }
54              
55 12         442 $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__