File Coverage

blib/lib/Parse/RandGen/Condition.pm
Criterion Covered Total %
statement 79 111 71.1
branch 32 76 42.1
condition 16 39 41.0
subroutine 15 24 62.5
pod 10 18 55.5
total 152 268 56.7


line stmt bran cond sub pod time code
1             # $Revision: #3 $$Date: 2005/08/31 $$Author: jd150722 $
2             ######################################################################
3             #
4             # This program is Copyright 2003-2005 by Jeff Dutton.
5             #
6             # This program is free software; you can redistribute it and/or modify
7             # it under the terms of either the GNU General Public License or the
8             # Perl Artistic License.
9             #
10             # This program is distributed in the hope that it will be useful,
11             # but WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13             # GNU General Public License for more details.
14             #
15             # If you do not have a copy of the GNU General Public License write to
16             # the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
17             # MA 02139, USA.
18             ######################################################################
19              
20             package Parse::RandGen::Condition;
21              
22             require 5.006_001;
23 4     4   30 use Carp;
  4         9  
  4         332  
24 4     4   21 use Data::Dumper;
  4         7  
  4         231  
25 4     4   20 use Parse::RandGen qw($Debug);
  4         7  
  4         667  
26 4     4   23 use strict;
  4         8  
  4         196  
27 4     4   20 use vars qw($Debug);
  4         7  
  4         7289  
28              
29             ######################################################################
30             #### Creators
31              
32             sub new {
33 18     18 1 364 my $class = shift;
34 18         32 my $element = shift;
35 18 50       46 defined($element) or confess("%Error: Parse::RandGen::Condition::new() requires the a defined element argument as the first argument!\n");
36              
37 18         87 my $self = {
38             _element => $element, # The element for the condition that must match
39             _min => undef, # The minimum number of times that the element must match for the condition to be true
40             _max => undef, # The maximum (inclusive) number of times that the element must match for the condition to be true
41             _greedy => undef, # By default, conditions are greedy (for pick()ing only, for parsing all conditions are greedy)
42             _production => undef, # The "parent" production that this belongs to...
43             };
44 18   33     81 my $type = ref($class)||$class;
45 18 50       55 ($type eq "Parse::RandGen::Condition") and confess "%Error: Cannot call Parse::RandGen::Condition::new() directly! It is an abstract class!";
46 18         46 bless $self, $type;
47              
48             # Optional named arguments can be passed. Any unknown named arguments are turned into object data members.
49 18         95 my %args = (
50             # Optional
51             min => 1, # Min quantity
52             max => 1, # Max quantity
53             quant => undef, # Quantifier: [ + * ? ]
54             greedy => 1,
55             @_, # Arguments can override defaults or create new attributes in the object
56             );
57 18 100       56 if (defined($args{quant})) {
58 6         14 my $quant = $args{quant};
59 6 100 66     60 ($args{min}, $args{max}) = (1, undef) if (($quant eq '+') || ($quant eq 's'));
60 6 50 33     47 ($args{min}, $args{max}) = (0, undef) if (($quant eq '*') || ($quant eq 's?'));
61 6 50       22 ($args{min}, $args{max}) = (0, 1) if ($quant eq '?');
62 6 100       24 if ($quant =~ m/\{(\d+)(,(\d*))?\}/) { # Support {n} , {n,} , and {n,m} formats
63 1         3 $args{min} = $1;
64 1 50       4 if (defined($2)) {
65 0         0 $args{max} = $3; # {n,} is (n,undef); {n,m} is (n,m)
66             } else {
67 1         3 $args{max} = $args{min}; # {n} is (n,n)
68             }
69             }
70 6 50       30 defined($args{min}) or confess("%Error: quant value of $quant is not understood!\n");
71             }
72 18         97 $self->{_min} = $args{min}; delete $args{min};
  18         40  
73 18         182 $self->{_max} = $args{max}; delete $args{max};
  18         33  
74 18         37 $self->{_greedy} = $args{greedy}; delete $args{greedy};
  18         26  
75 18         29 delete $args{quant};
76              
77 18 100       86 my ($min, $max) = ($self->min(), (defined($self->max()) ? $self->max() : "undef"));
78 18 50 66     91 ($self->isQuantSupported() or $self->once()) or confess "%Error: new $type being created with a specified quantifier (min=$min and max=$max are not supported)!";
79              
80 18         94 $self->_newDerived(\%args); # Derived class can pull out args that are custom/specific...
81              
82             # Delete named arguments, and copy any other values into the object (user-defined fields)
83 12         98 foreach my $userDefField (sort keys %args) {
84 0         0 $self->{$userDefField} = $args{$userDefField};
85             }
86              
87 12         53 return($self);
88             }
89              
90             ######################################################################
91             #### Methods
92              
93             #sub dump { } # Abstract Method
94              
95             sub dumpVal {
96 998 50   998 0 4172 my $self = shift or confess "%Error: Cannot call dumpVal() without a valid object!";
97 998         1351 my $val = shift;
98 998 50       2173 $val = "" unless defined($val);
99 998         6909 my $d = Data::Dumper->new([$val])->Terse(1)->Indent(0)->Useqq(1);
100 998         55782 return($d->Dump());
101             }
102              
103             sub pickRepetitions {
104 202 50   202 0 690 my $self = shift or confess "%Error: Cannot call pickRepetitions without a valid object!";
105 202         579 my %args = @_;
106              
107 202         477 my ($corruptCnt, $corruptData) = (0, 0);
108 202 50 66     1215 if (!$args{match} && !$self->zeroOrMore()) {
109 56 100       154 if (int(rand(2))) {
110 31         65 $corruptData = 1;
111             } else {
112 25         142 $corruptCnt = 1;
113             }
114             }
115              
116 202         251 my ($minCnt, $maxCnt);
117 202 100       340 if ($corruptCnt) {
118 25 100 66     351 if ((int(rand(2)) || !$self->max()) && $self->min()) {
      66        
119             # Choose less than the minimum count (too few)
120 14         32 ($minCnt, $maxCnt) = (0, $self->min()-1);
121             } else {
122             # Choose more than the maximum count (too many)
123 11         27 ($minCnt, $maxCnt) = ($self->max()+1, $self->max()+4);
124             }
125             } else {
126 177   33     366 $minCnt = $self->min() || ($self->containsVals(%args) ? 1 : 0); # containsVals can only be true for SubRule
127 177   33     479 $maxCnt = $self->max() || ($minCnt + (1<
128             }
129              
130 202         1606 my $matchCnt = $minCnt + int(rand($maxCnt-$minCnt+1));
131 202 100       598 my $badOne = $corruptData ? int(rand($matchCnt)) : undef;
132              
133 202         1069 return ( matchCnt => $matchCnt, badOne => $badOne );
134             }
135              
136             #sub pick { } # Abstract Method
137              
138             ######################################################################
139             #### Accessors
140              
141             sub element {
142 3686 50   3686 1 9534 my $self = shift or confess "%Error: Cannot call element() without a valid object!";
143 3686         15449 return $self->{_element};
144             }
145              
146 0     0 1 0 sub subrule { return undef; } # Default
147 0     0 1 0 sub isSubrule { return 0; } # Default
148 0     0 1 0 sub isTerminal { return 1; } # Default
149 144     144 0 610 sub isQuantSupported { return 0; } # Default (Regexp and Literal classes dont support)
150 1812     1812 0 11200 sub containsVals { return 0; } # Default (only Subrule supports)
151              
152             sub min {
153 392 50   392 1 1044 my $self = shift or confess "%Error: Cannot call min() without a valid object!";
154 392         2580 return $self->{_min};
155             }
156              
157             sub max {
158 267 50   267 1 1181 my $self = shift or confess "%Error: Cannot call max() without a valid object!";
159 267         1605 return $self->{_max};
160             }
161              
162             sub once { # Returns true if the Condition must match exactly once
163 12 50   12 0 31 my $self = shift or confess "%Error: Cannot call once() without a valid object!";
164 12   33     30 return (defined($self->max()) && ($self->min() == 1) && ($self->max() == 1));
165             }
166              
167             sub zeroOrMore { # Returns true if the Condition can match 0 or more times
168 157 50   157 0 481 my $self = shift or confess "%Error: Cannot call once() without a valid object!";
169 157   33     321 return (!$self->min() && !defined($self->max()));
170             }
171              
172             sub quant {
173 0 0   0 0   my $self = shift or confess "%Error: Cannot call once() without a valid object!";
174 0 0         my $ngreedy = $self->isGreedy() ? "" : "?";
175 0           my $quant = "";
176 0           my @minmax = ($self->min(), $self->max());
177 0   0 0     my $arrayEq = sub { return (($_[0] == $_[2]) && ( (!defined($_[1]) && !defined($_[3]))
178             || ( (defined($_[1]) && defined($_[3]))
179 0           && ($_[1] == $_[3])) ) ); };
180 0 0         if (&$arrayEq(@minmax, 0, undef)) {
    0          
    0          
    0          
181 0           $quant = "*" . $ngreedy;
182             } elsif (&$arrayEq(@minmax, 1, undef)) {
183 0           $quant = "+" . $ngreedy;
184             } elsif (&$arrayEq(@minmax, 0, 1)) {
185 0           $quant = "?";
186             } elsif (&$arrayEq(@minmax, 1, 1)) {
187 0           $quant = ""; # Print nothing if the quantifier is {1}
188             } else {
189 0           my $min = $self->min();
190 0 0         my $max = defined($self->max()) ? $self->max() : "";
191 0 0 0       if ($max && ($self->min() == $self->max())) {
192 0           $quant = "{$max}";
193             } else {
194 0           $quant = "{$min,$max}";
195             }
196             }
197 0           return $quant;
198             }
199              
200             sub isGreedy { # Almost everything is greedy
201 0 0   0 0   my $self = shift or confess "%Error: Cannot call once() without a valid object!";
202 0           return ($self->{_greedy});
203             }
204              
205             sub production { # Production that this Condition belongs to
206 0 0   0 1   my $self = shift or confess "%Error: Cannot call production() without a valid object!";
207 0           return $self->{_production};
208             }
209              
210             sub rule {
211 0 0   0 1   my $self = shift or confess "%Error: Cannot call rule() without a valid object!";
212 0 0         my $rule = $self->production()->rule() if defined($self->production());
213 0           return $rule;
214             }
215              
216             sub grammar {
217 0 0   0 1   my $self = shift or confess "%Error: Cannot call grammar() without a valid object!";
218 0 0         my $grammar = $self->rule()->grammar() if defined($self->rule());
219 0           return $grammar;
220             }
221              
222             ######################################################################
223             #### Package return
224             1;
225             __END__