File Coverage

blib/lib/Parse/RandGen/Rule.pm
Criterion Covered Total %
statement 81 111 72.9
branch 26 62 41.9
condition 7 24 29.1
subroutine 14 17 82.3
pod 4 12 33.3
total 132 226 58.4


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::Rule;
21              
22             require 5.006_001;
23 4     4   23 use Carp;
  4         7  
  4         346  
24 4     4   23 use Parse::RandGen qw($Debug);
  4         6  
  4         575  
25 4     4   20 use Data::Dumper; # FIX - debug only
  4         9  
  4         197  
26 4     4   20 use strict;
  4         15  
  4         120  
27 4     4   18 use vars qw($Debug);
  4         5  
  4         6136  
28              
29             ######################################################################
30             #### Creators
31              
32             sub new {
33 9     9 1 76 my $class = shift;
34 9         40 my $self = {
35             _name => undef, # Name of the rule
36             _grammar => undef, # Reference to the parent Grammar object
37             _productions => [ ], # Productions for the rule
38             };
39 9   33     56 bless $self, ref($class)||$class;
40              
41 9         31 $self->{_name} = shift;
42 9 50 66     54 !defined($self->{_name}) or ($self->{_name} =~ /^[a-z_]\w*$/i)
43             or confess("The specified rule name must be exclusively alphanumeric characters (", $self->{_name}, ")!");
44 9         38 return($self);
45             }
46              
47             ######################################################################
48             #### Methods
49              
50             sub set {
51 1 50   1 0 5 my $self = shift or confess("%Error: Cannot call without a valid object!");
52 1         2 my @args = @_;
53 1         2 my $prodType = "Parse::RandGen::Production";
54            
55 1         3 my $numArgs = $#args + 1;
56              
57 1 50       4 ($numArgs) or confess("%Error: Rule::set() called with no arguments!");
58              
59 1 50       4 ($numArgs % 2) and confess("%Error: Rule::set() called with an odd number of arguments ($numArgs); arguments must be in named pairs!");
60 1         4 while ($#args >= 0) {
61 1         3 my ($arg, $val) = (shift(@args), shift(@args));
62 1 50       4 if ($arg eq "prod") {
63 1         4 $self->addProd($val);
64             } else {
65             # Unknown arguments become data members
66 0         0 $self->{$arg} = $val;
67             }
68             }
69             }
70              
71             sub addProd {
72 11 50   11 0 43 my $self = shift or confess("%Error: Cannot call without a valid object!");
73 11         21 my @args = @_;
74 11         21 my $prodType = "Parse::RandGen::Production";
75            
76 11         20 foreach my $arg (@args) {
77 11         15 my $prod; # Production object
78 11         18 my $type = ref($arg);
79 11 100 66     76 if ($type && UNIVERSAL::isa($arg, $prodType)) {
    50          
80 10         17 $prod = $arg;
81             } elsif ($type eq "ARRAY") {
82             # [ 'http://' host path(?) ]
83 1         9 $prod = Parse::RandGen::Production->new(@$arg);
84             } else {
85 0         0 confess("%Error: Passed a $type argument instead of a $prodType or ARRAY reference argument!");
86             }
87 10 50       39 defined($prod->{_rule}) and confess("%Error: Adding a Production that already belongs to another Rule!");
88 10         973 $prod->{_rule} = $self;
89 10         21 push @{$self->{_productions}}, $prod; # Add the production to the end of the _productions list
  10         185  
90 10         15 $prod->{_number} = $#{$self->{_productions}};
  10         215  
91             }
92             }
93              
94             sub check {
95 0 0   0 0 0 my $self = shift or confess("%Error: Cannot call without a valid object!");
96 0 0       0 return "%Error: Production has no Grammar object!" unless $self->grammar();
97 0         0 my $grammarName = $self->grammar()->name();
98              
99 0         0 my $err = "";
100 0         0 foreach my $prod (@{$self->{_productions}}) {
  0         0  
101 0         0 $err .= $prod->check();
102             }
103 0         0 return $err;
104             }
105              
106             sub dump {
107 0 0   0 0 0 my $self = shift or confess("%Error: Cannot call without a valid object!");
108 0         0 my $output = $self->name() . ":\n";
109 0         0 my $firstProd = 1;
110 0         0 foreach my $prod (@{$self->{_productions}}) {
  0         0  
111 0 0       0 if ($firstProd) {
112 0         0 $output .= "\t\t ";
113 0         0 $firstProd = 0;
114             } else {
115 0         0 $output .= "\t\t| ";
116             }
117 0         0 $output .= $prod->dump() . "\n";
118             }
119 0         0 $output .= "\n";
120             }
121              
122             sub dumpHeir {
123 200 50   200 0 674 my $self = shift or confess("%Error: Cannot call without a valid object!");
124 200 50       660 my $name = defined($self->name()) ? ($self->name() . ":") : "";
125 200         436 my $output = "($name ";
126 200         254 my $firstProd = 1;
127 200         211 foreach my $prod (@{$self->{_productions}}) {
  200         3370  
128 400 100       803 if ($firstProd) {
129 200         269 $firstProd = 0;
130             } else {
131 200         278 $output .= " | ";
132             }
133 400         1690 $output .= $prod->dumpHeir();
134             }
135 200         1941 $output .= " )";
136             }
137              
138             sub pick {
139 805 50   805 1 2326 my $self = shift or confess("%Error: Cannot call without a valid object!");
140 805         3652 my %args = ( match=>1, # Default is to pick matching data
141             vals => { }, # Hash of values of various hard-coded sub-rules (by name)
142             @_ );
143              
144             # Return explicitly specified value (if specified by name or reference to $self)
145 805 50 33     2244 return $args{vals}{$self->name()} if (defined($self->name()) && defined($args{vals}{$self->name()}));
146 805 50       3546 return $args{vals}{$self} if defined($args{vals}{$self});
147              
148 805         841 my @prods;
149 805         2070 foreach my $prod ($self->productions()) {
150 1408 50       9411 push(@prods, $prod) if $prod->containsVals(%args);
151             }
152 805 50       3029 @prods = $self->productions() unless(@prods); # If {vals} does not specify any production of this rule, pick from all productions
153              
154 805         2291 my $prodNum = int(rand($#prods+1));
155 805         3677 return( $prods[$prodNum]->pick(%args) );
156             }
157              
158             # Returns true (1) if this rule is or has any explicitly specified values in the "vals" argument
159             sub containsVals {
160 202 50   202 0 602 my $self = shift or confess("%Error: Cannot call without a valid object!");
161 202         1101 my %args = ( vals => { }, # Hash of values of various hard-coded sub-rules (by name)
162             @_ );
163              
164             # Return true if this rule is explicitly specified (if specified by name or reference to $self)
165 202 50 33     1004 return 1 if (defined($self->name()) && defined($args{vals}{$self->name()}));
166 202 50       869 return 1 if defined($args{vals}{$self});
167              
168 202         504 foreach my $prod ($self->productions()) {
169 404 50       5871 return 1 if $prod->containsVals(%args);
170             }
171 202         1641 return 0;
172             }
173              
174             ######################################################################
175             #### Accessors
176              
177             sub name {
178 1209 50   1209 1 3492 my $self = shift or confess("%Error: Cannot call name() without a valid object!");
179 1209         4945 return $self->{_name};
180             }
181              
182             sub grammar {
183 1 50   1 1 4 my $self = shift or confess("%Error: Cannot call grammar() without a valid object!");
184 1         15 return $self->{_grammar};
185             }
186              
187             sub productions {
188 1812 50   1812 0 4998 my $self = shift or confess("%Error: Cannot call productions() without a valid object!");
189 1812         2720 return (@{$self->{_productions}});
  1812         7557  
190             }
191              
192             sub production {
193 0 0   0 0   my $self = shift or confess("%Error: Cannot call productions() without a valid object!");
194 0           my $prodIdent = shift;
195 0 0         defined($prodIdent) or confess("%Error: Must specify either the name or the number of the production to be found!");
196 0           my $isNumber = ($prodIdent !~ m/[^\d]/);
197 0           foreach my $prod (@{$self->{_productions}}) {
  0            
198 0 0 0       if ( ($isNumber && ($prod->{_number} == $prodIdent))
      0        
      0        
199             || (defined($prod->{_name}) && ($prod->{_name} eq $prodIdent)) ) {
200 0           return $prod;
201             }
202             }
203 0           return undef; # Not found
204             }
205              
206             ######################################################################
207             #### Package return
208             1;
209             __END__