File Coverage

blib/lib/FLAT/Regex/Op.pm
Criterion Covered Total %
statement 131 143 91.6
branch 36 46 78.2
condition n/a
subroutine 44 49 89.8
pod 0 2 0.0
total 211 240 87.9


line stmt bran cond sub pod time code
1             package FLAT::Regex::Op;
2 6     6   44 use strict;
  6         14  
  6         814  
3              
4             sub new {
5 192847     192847 0 290107 my $pkg = shift;
6             ## flatten alike operations, i.e, "a+(b+c)" into "a+b+c"
7 192847 100       347330 my @flat = map {UNIVERSAL::isa($_, $pkg) ? $_->members : $_} @_;
  234854         1057231  
8              
9 192847         3711555 bless \@flat, $pkg;
10             }
11              
12             sub members {
13 1202     1202 0 1861 my $self = shift;
14 1202 100       6089 wantarray ? @$self[0 .. $#$self] : $self->[0];
15             }
16              
17             #################################
18             #### regex operators / components
19              
20             package FLAT::Regex::Op::atomic;
21 6     6   46 use parent 'FLAT::Regex::Op';
  6         15  
  6         40  
22              
23             sub as_string {
24 58     58   83 my $t = $_[0]->members;
25              
26 58 100       90 return "#" if not defined $t;
27 57 100       194 return $t =~ /^\w$/
28             ? $t
29             : "[$t]";
30             }
31              
32             sub as_perl_regex {
33 112     112   135 my $r = $_[0]->members;
34              
35 112 50       162 return "(?!)" if not defined $r;
36              
37 112         126 $r = quotemeta $r;
38 112 100       290 return $r =~ /^\w$/ ? $r : "(?:$r)";
39             }
40              
41             sub as_nfa {
42 0     0   0 FLAT::NFA->singleton($_[0]->members);
43             }
44              
45             sub as_pfa {
46 546     546   1748 FLAT::PFA->singleton($_[0]->members);
47             }
48              
49             sub from_parse {
50 166937     166937   99269614 my ($pkg, @item) = @_;
51 166937         268253 my $i = $item[1];
52              
53 166937 100       353673 return $pkg->new("") if $i eq "[]";
54 166869 100       278294 return $pkg->new(undef) if $i eq "#";
55              
56 166477         413396 $i =~ s/^\[|\]$//g;
57              
58 166477         389265 return $pkg->new($i);
59             }
60              
61             sub reverse {
62 22     22   30 $_[0];
63             }
64              
65             sub is_empty {
66 18     18   30 not defined $_[0]->members;
67             }
68              
69             sub has_nonempty_string {
70 9     9   11 my $self = shift;
71 9 100       14 defined $self->members and length $self->members;
72             }
73              
74             sub is_finite {
75 7     7   19 1
76             }
77              
78             ##############################
79             package FLAT::Regex::Op::star;
80 6     6   2445 use parent 'FLAT::Regex::Op';
  6         13  
  6         32  
81              
82 12     12   72 sub parse_spec {"%s '*'"}
83 50     50   124 sub precedence {30}
84              
85             sub as_string {
86 5     5   9 my ($self, $prec) = @_;
87 5         27 my $result = $self->members->as_string($self->precedence) . "*";
88 5 50       10 return $prec > $self->precedence ? "($result)" : $result;
89             }
90              
91             sub as_perl_regex {
92 14     14   21 my ($self, $prec) = @_;
93 14         19 my $result = $self->members->as_perl_regex($self->precedence) . "*";
94 14 50       57 return $prec > $self->precedence ? "(?:$result)" : $result;
95             }
96              
97             sub as_nfa {
98 0     0   0 my $self = shift;
99 0         0 $self->members->as_nfa->kleene;
100             }
101              
102             sub as_pfa {
103 68     68   165 my $self = shift;
104 68         231 $self->members->as_pfa->kleene;
105             }
106              
107             sub from_parse {
108 4255     4255   1199783 my ($pkg, @item) = @_;
109 4255         13380 $pkg->new($item[1]);
110             }
111              
112             sub reverse {
113 4     4   5 my $self = shift;
114 4         6 my $op = $self->members->reverse;
115 4         8 __PACKAGE__->new($op);
116             }
117              
118             sub is_empty {
119 3     3   10 0
120             }
121              
122             sub has_nonempty_string {
123 0     0   0 $_[0]->members->has_nonempty_string;
124             }
125              
126             sub is_finite {
127 7     7   17 !$_[0]->members->has_nonempty_string;
128             }
129              
130             ################################
131             package FLAT::Regex::Op::concat;
132 6     6   2354 use parent 'FLAT::Regex::Op';
  6         17  
  6         40  
133              
134 12     12   56 sub parse_spec {"%s(2..)";}
135 174     174   414 sub precedence {20}
136              
137             sub as_string {
138 10     10   16 my ($self, $prec) = @_;
139 10         22 my $result = join "", map {$_->as_string($self->precedence)} $self->members;
  40         63  
140 10 50       24 return $prec > $self->precedence ? "($result)" : $result;
141             }
142              
143             sub as_perl_regex {
144 28     28   38 my ($self, $prec) = @_;
145 28         46 my $result = join "", map {$_->as_perl_regex($self->precedence)} $self->members;
  84         113  
146 28 50       53 return $prec > $self->precedence ? "(?:$result)" : $result;
147             }
148              
149             sub as_nfa {
150 0     0   0 my $self = shift;
151 0         0 my @parts = map {$_->as_nfa} $self->members;
  0         0  
152 0         0 $parts[0]->concat(@parts[1 .. $#parts]);
153             }
154              
155             sub as_pfa {
156 152     152   369 my $self = shift;
157 152         506 my @parts = map {$_->as_pfa} $self->members;
  477         1958  
158 152         1268 $parts[0]->concat(@parts[1 .. $#parts]);
159             }
160              
161             sub from_parse {
162 20068     20068   16341135 my ($pkg, @item) = @_;
163 20068         33422 $pkg->new(@{$item[1]});
  20068         60969  
164             }
165              
166             ## note: "reverse" conflicts with perl builtin
167             sub reverse {
168 6     6   7 my $self = shift;
169 6         9 my @ops = CORE::reverse map {$_->reverse} $self->members;
  18         35  
170 6         10 __PACKAGE__->new(@ops);
171             }
172              
173             sub is_empty {
174 5     5   8 my $self = shift;
175 5         13 my @members = $self->members;
176 5         11 for (@members) {
177 10 100       24 return 1 if $_->is_empty;
178             }
179 4         15 return 0;
180             }
181              
182             sub has_nonempty_string {
183 1     1   3 my $self = shift;
184 1 50       4 return 0 if $self->is_empty;
185              
186 1         3 my @members = $self->members;
187 1         2 for (@members) {
188 2 50       4 return 1 if $_->has_nonempty_string;
189             }
190 1         3 return 0;
191             }
192              
193             sub is_finite {
194 2     2   4 my $self = shift;
195 2 50       4 return 1 if $self->is_empty;
196              
197 2         5 my @members = $self->members;
198 2         4 for (@members) {
199 3 100       6 return 0 if not $_->is_finite;
200             }
201 1         5 return 1;
202             }
203              
204             #############################
205             package FLAT::Regex::Op::alt;
206 6     6   3865 use parent 'FLAT::Regex::Op';
  6         34  
  6         65  
207              
208 12     12   69 sub parse_spec {"%s(2.. /[+|]/)"}
209 143     143   292 sub precedence {10}
210              
211             sub as_string {
212 7     7   15 my ($self, $prec) = @_;
213 7         14 my $result = join "+", map {$_->as_string($self->precedence)} $self->members;
  26         37  
214 7 100       15 return $prec > $self->precedence ? "($result)" : $result;
215             }
216              
217             sub as_perl_regex {
218 28     28   37 my ($self, $prec) = @_;
219 28         38 my $result = join "|", map {$_->as_perl_regex($self->precedence)} $self->members;
  70         116  
220 28 50       49 return $prec > $self->precedence ? "(?:$result)" : $result;
221             }
222              
223             sub as_nfa {
224 0     0   0 my $self = shift;
225 0         0 my @parts = map {$_->as_nfa} $self->members;
  0         0  
226 0         0 $parts[0]->union(@parts[1 .. $#parts]);
227             }
228              
229             sub as_pfa {
230 31     31   78 my $self = shift;
231 31         145 my @parts = map {$_->as_pfa} $self->members;
  70         300  
232 31         234 $parts[0]->union(@parts[1 .. $#parts]);
233             }
234              
235             sub from_parse {
236 225     225   53635 my ($pkg, @item) = @_;
237 225         483 $pkg->new(@{$item[1]});
  225         850  
238             }
239              
240             sub reverse {
241 4     4   6 my $self = shift;
242 4         6 my @ops = map {$_->reverse} $self->members;
  12         17  
243 4         8 __PACKAGE__->new(@ops);
244             }
245              
246             sub is_empty {
247 2     2   5 my $self = shift;
248 2         7 my @members = $self->members;
249 2         3 for (@members) {
250 4 100       8 return 0 if not $_->is_empty;
251             }
252 1         5 return 1;
253             }
254              
255             sub has_nonempty_string {
256 1     1   3 my $self = shift;
257 1         3 my @members = $self->members;
258 1         3 for (@members) {
259 2 50       7 return 1 if $_->has_nonempty_string;
260             }
261 1         6 return 0;
262             }
263              
264             sub is_finite {
265 2     2   4 my $self = shift;
266 2         5 my @members = $self->members;
267 2         22 for (@members) {
268 4 100       8 return 0 if not $_->is_finite;
269             }
270 1         5 return 1;
271             }
272             1;