File Coverage

lib/Config/Grammar/Dynamic.pm
Criterion Covered Total %
statement 256 312 82.0
branch 104 148 70.2
condition 18 33 54.5
subroutine 13 15 86.6
pod 0 2 0.0
total 391 510 76.6


line stmt bran cond sub pod time code
1             package Config::Grammar::Dynamic;
2 2     2   13237 use strict;
  2         10  
  2         53  
3 2     2   673 use Config::Grammar;
  2         5  
  2         80  
4 2     2   16 use base qw(Config::Grammar);
  2         3  
  2         5244  
5              
6             $Config::Grammar::Dynamic::VERSION = $Config::Grammar::VERSION;
7              
8             sub _deepcopy {
9             # this handles circular references on consecutive levels,
10             # but breaks if there are any levels in between
11             # the makepod() and maketmpl() methods have the same limitation
12 411     411   379 my $what = shift;
13 411 100       788 return $what unless ref $what;
14 206         256 for (ref $what) {
15 206 50       320 /^ARRAY$/ and return [ map { $_ eq $what ? $_ : _deepcopy($_) } @$what ];
  106 100       167  
16 153 100       378 /^HASH$/ and return { map { $_ => $what->{$_} eq $what ?
17 293 50       581 $what->{$_} : _deepcopy($what->{$_}) } keys %$what };
18 52 50       195 /^CODE$/ and return $what; # we don't need to copy the subs
19 0 0       0 /^Regexp$/ and return $what; # neither Regexp objects
20             }
21 0         0 die "Cannot _deepcopy reference type @{[ref $what]}";
  0         0  
22             }
23              
24             sub _next_level($$$)
25             {
26 7     7   9 my $self = shift;
27 7         8 my $name = shift;
28              
29             # section name
30 7 100       18 if (defined $self->{section}) {
31 5         9 $self->{section} .= "/$name";
32             }
33             else {
34 2         6 $self->{section} = $name;
35             }
36              
37             # grammar context
38 7         23 my $s = $self->_search_section($name);
39 7 50       15 return 0 unless defined $s;
40 7 50       19 if (not defined $self->{grammar}{$s}) {
41 0         0 $self->_make_error("Config::Grammar internal error (no grammar for $s)");
42 0         0 return 0;
43             }
44 7         10 push @{$self->{grammar_stack}}, $self->{grammar};
  7         12  
45 7 100       19 if ($s =~ m|^/(.*)/$|) {
46             # for sections specified by a regexp, we create
47             # a new branch with a deep copy of the section
48             # grammar so that any _dyn sub further below will edit
49             # just this branch
50              
51 1         4 $self->{grammar}{$name} = _deepcopy($self->{grammar}{$s});
52              
53             # put it at the head of the section list
54 1   50     4 $self->{grammar}{_sections} ||= [];
55 1         1 unshift @{$self->{grammar}{_sections}}, $name;
  1         4  
56             }
57              
58             # support for recursive sections
59             # copy the section syntax to the subsection
60              
61 7 100 100     17 if ($self->{grammar}{_recursive}
62 8         23 and grep { $_ eq $s } @{$self->{grammar}{_recursive}}) {
  5         7  
63 3   50     5 $self->{grammar}{$name}{_sections} ||= [];
64 3   100     13 $self->{grammar}{$name}{_recursive} ||= [];
65 3         4 push @{$self->{grammar}{$name}{_sections}}, $s;
  3         8  
66 3         4 push @{$self->{grammar}{$name}{_recursive}}, $s;
  3         6  
67 3         19 my $grammarcopy = _deepcopy($self->{grammar}{$name});
68 3 50       6 if (exists $self->{grammar}{$name}{$s}) {
69             # there's syntax for a variable by the same name too
70             # make sure we don't lose it
71 0         0 %{$self->{grammar}{$name}{$s}} = ( %$grammarcopy, %{$self->{grammar}{$name}{$s}} );
  0         0  
  0         0  
72             } else {
73 3         5 $self->{grammar}{$name}{$s} = $grammarcopy;
74             }
75             }
76              
77             # this uses the copy created above for regexp sections
78             # and the original for non-regexp sections (where $s == $name)
79 7         11 $self->{grammar} = $self->{grammar}{$name};
80              
81             # support for inherited values
82             # note that we have to do this on the way down
83             # and keep track of which values were inherited
84             # so that we can propagate the values even further
85             # down if needed
86 7         8 my %inherited;
87 7 100       20 if ($self->{grammar}{_inherited}) {
88 5         6 for my $var (@{$self->{grammar}{_inherited}}) {
  5         9  
89 10 100       21 next unless exists $self->{cfg}{$var};
90 6         13 my $value = $self->{cfg}{$var};
91 6 50       10 next unless defined $value;
92 6 50       7 next if ref $value; # it's a section
93 6         13 $inherited{$var} = $value;
94             }
95             }
96              
97             # config context
98 7         8 my $order;
99 7 50       23 if (defined $self->{grammar}{_order}) {
100 0 0       0 if (defined $self->{cfg}{_order_count}) {
101 0         0 $order = ++$self->{cfg}{_order_count};
102             }
103             else {
104 0         0 $order = $self->{cfg}{_order_count} = 0;
105             }
106             }
107              
108 7 50       12 if (defined $self->{cfg}{$name}) {
109 0         0 $self->_make_error('section or variable already exists');
110 0         0 return 0;
111             }
112 7         26 $self->{cfg}{$name} = { %inherited }; # inherit the values
113 7         9 push @{$self->{cfg_stack}}, $self->{cfg};
  7         13  
114 7         8 $self->{cfg} = $self->{cfg}{$name};
115              
116             # keep track of the inherited values here;
117             # we delete it on the way up in _prev_level()
118 7         13 $self->{cfg}{_inherited} = \%inherited;
119              
120             # list of already defined variables on this level
121 7 100       12 if (defined $self->{grammar}{_varlist}) {
122 1         2 $self->{cfg}{_varlist} = [];
123             }
124              
125             # meta data for _mandatory test
126 7         10 $self->{grammar}{_is_section} = 1;
127 7         9 $self->{cfg}{_is_section} = 1;
128              
129             # this uses the copy created above for regexp sections
130             # and the original for non-regexp sections (where $s == $name)
131 7         11 $self->{cfg}{_grammar} = $name;
132              
133 7 50       11 $self->{cfg}{_order} = $order if defined $order;
134              
135             # increase level
136 7         52 $self->{level}++;
137              
138             # if there's a _dyn sub, apply it
139 7 100       14 if (defined $self->{grammar}{_dyn}) {
140 1         2 &{$self->{grammar}{_dyn}}($s, $name, $self->{grammar});
  1         3  
141             }
142              
143 7         33 return 1;
144             }
145              
146             # find variables in old grammar list 'listname'
147             # that aren't in the corresponding list in the new grammar
148             # and list them as a POD document, possibly with a callback
149             # function 'docfunc'
150              
151             sub _findmissing($$$;$) {
152 36     36   31 my $old = shift;
153 36         31 my $new = shift;
154 36         29 my $listname = shift;
155 36         28 my $docfunc = shift;
156              
157 36         31 my @doc;
158 36 100       61 if ($old->{$listname}) {
159 30         24 my %newlist;
160 30 50       38 if ($new->{$listname}) {
161 30         29 @newlist{@{$new->{$listname}}} = undef;
  30         48  
162             }
163 30         27 for my $v (@{$old->{$listname}}) {
  30         36  
164 43 100       62 next if exists $newlist{$v};
165 3 100       6 if ($docfunc) {
166 2         4 push @doc, &$docfunc($old, $v)
167             } else {
168 1         4 push @doc, "=item $v";
169             }
170             }
171             }
172 36         46 return @doc;
173             }
174              
175             # find variables in new grammar list 'listname'
176             # that aren't in the corresponding list in the new grammar
177             #
178             # this is just _findmissing with the arguments swapped
179              
180             sub _findnew($$$;$) {
181 18     18   19 my $old = shift;
182 18         14 my $new = shift;
183 18         15 my $listname = shift;
184 18         15 my $docfunc = shift;
185 18         21 return _findmissing($new, $old, $listname, $docfunc);
186             }
187              
188             # compare two lists for element equality
189              
190             sub _listseq($$);
191             sub _listseq($$) {
192 0     0   0 my ($k, $l) = @_;
193 0         0 my $length = @$k;
194 0 0       0 return 0 unless @$l == $length;
195 0         0 for (my $i=0; $i<$length; $i++) {
196 0 0       0 return 0 unless $k->[$i] eq $l->[$i];
197             }
198 0         0 return 1;
199             }
200              
201             # diff two grammar trees, documenting the differences
202              
203             sub _diffgrammars($$);
204             sub _diffgrammars($$) {
205 9     9   10 my $old = shift;
206 9         9 my $new = shift;
207 9         10 my @doc;
208              
209             my @vdoc;
210 9         14 @vdoc = _findmissing($old, $new, '_vars');
211 9 100       15 push @doc, "The following variables are not valid anymore:", "=over" , @vdoc, "=back"
212             if @vdoc;
213 9         23 @vdoc = _findnew($old, $new, '_vars', \&_describevar);
214 9 100       17 push @doc, "The following new variables are valid:", "=over" , @vdoc, "=back"
215             if @vdoc;
216 9         12 @vdoc = _findmissing($old, $new, '_sections');
217 9 50       11 push @doc, "The following subsections are not valid anymore:", "=over" , @vdoc, "=back"
218             if @vdoc;
219             @vdoc = _findnew($old, $new, '_sections', sub {
220 0     0   0 my ($tree, $sec) = @_;
221 0         0 my @tdoc;
222 0         0 _genpod($tree->{$sec}, 0, \@tdoc);
223 0         0 return @tdoc;
224 9         30 });
225 9 50       29 push @doc, "The following new subsections are defined:", "=over" , @vdoc, "=back"
226             if @vdoc;
227 9         9 for (@{$old->{_sections}}) {
  9         15  
228 4 50       6 next unless exists $new->{$_};
229 4         11 @vdoc = _diffgrammars($old->{$_}, $new->{$_});
230 4 50       8 push @doc, "Syntax changes for subsection B<$_>", "=over", @vdoc, "=back"
231             if @vdoc;
232             }
233 9         11 return @doc;
234             }
235              
236              
237             sub _describevar {
238 18     18   17 my $tree = shift;
239 18         24 my $var = shift;
240             my $mandatory = ( $tree->{_mandatory} and
241 18 50 33     40 grep {$_ eq $var} @{$tree->{_mandatory}} ) ?
242             " I<(mandatory setting)>" : "";
243 18         15 my @doc;
244 18         33 push @doc, "=item B<$var>".$mandatory;
245 18 100       28 push @doc, $tree->{$var}{_doc} if $tree->{$var}{_doc} ;
246             my $inherited = ( $tree->{_inherited} and
247 18   100     31 grep {$_ eq $var} @{$tree->{_inherited}});
248 18 100       26 push @doc, "This variable I its value from the parent section if nothing is specified here."
249             if $inherited;
250             push @doc, "This variable I modifies the grammar based on its value."
251 18 100       28 if $tree->{$var}{_dyn};
252             push @doc, "Default value: $var = $tree->{$var}{_default}"
253 18 100       41 if ($tree->{$var}{_default});
254             push @doc, "Example: $var = $tree->{$var}{_example}"
255 18 50       38 if ($tree->{$var}{_example});
256 18         43 return @doc;
257             }
258              
259             sub _genpod($$$);
260             sub _genpod($$$)
261             {
262 6     6   15 my ($tree, $level, $doc) = @_;
263 6         8 my %dyndoc;
264 6 100       13 if ($tree->{_vars}){
265 5         7 push @{$doc}, "The following variables can be set in this section:";
  5         9  
266 5         6 push @{$doc}, "=over";
  5         7  
267 5         8 foreach my $var (@{$tree->{_vars}}){
  5         11  
268 16         12 push @{$doc}, _describevar($tree, $var);
  16         27  
269             }
270 5         7 push @{$doc}, "=back";
  5         7  
271             }
272              
273 6 50       22 if ($tree->{_text}){
274 0   0     0 push @{$doc}, ($tree->{_text}{_doc} or "Unspecified Text content");
  0         0  
275 0 0       0 if ($tree->{_text}{_example}){
276 0         0 my $ex = $tree->{_text}{_example};
277 0         0 chomp $ex;
278 0         0 $ex = map {" $_"} split /\n/, $ex;
  0         0  
279 0         0 push @{$doc}, "Example:\n\n$ex\n";
  0         0  
280             }
281             }
282              
283 6 50       18 if ($tree->{_table}){
284 0         0 push @{$doc}, ($tree->{_table}{_doc} or
285 0   0     0 "This section can contain a table ".
286             "with the following structure:" );
287 0         0 push @{$doc}, "=over";
  0         0  
288 0         0 for (my $i=0;$i < $tree->{_table}{_columns}; $i++){
289 0         0 push @{$doc}, "=item column $i";
  0         0  
290 0         0 push @{$doc}, ($tree->{_table}{$i}{_doc} or
291 0   0     0 "Unspecific Content");
292 0         0 push @{$doc}, "Example: $tree->{_table}{$i}{_example}"
293             if ($tree->{_table}{$i}{_example})
294 0 0       0 }
295 0         0 push @{$doc}, "=back";
  0         0  
296             }
297 6 100       15 if ($tree->{_sections}){
298 4 100       8 if ($level > 0) {
299 2         4 push @{$doc}, "The following sections are valid on level $level:";
  2         6  
300 2         3 push @{$doc}, "=over";
  2         3  
301             }
302 4         5 foreach my $section (@{$tree->{_sections}}){
  4         8  
303             my $mandatory = ( $tree->{_mandatory} and
304 4 50 33     15 grep {$_ eq $section} @{$tree->{_mandatory}} ) ?
305             " I<(mandatory section)>" : "";
306 4 100       3 push @{$doc}, ($level > 0) ?
  4         19  
307             "=item B<".("+" x $level)."$section>$mandatory" :
308             "=head2 *** $section ***$mandatory";
309 4 50       15 if ($tree eq $tree->{$section}) {
310 0         0 push @{$doc}, "This subsection has the same syntax as its parent.";
  0         0  
311 0         0 next;
312             }
313 0         0 push @{$doc}, ($tree->{$section}{_doc})
314 4 50       8 if $tree->{$section}{_doc};
315 1         2 push @{$doc}, "The grammar of this section is I modified based on its name."
316 4 100       11 if $tree->{$section}{_dyn};
317 4 100 66     11 if ($tree->{_recursive} and
318 1         5 grep {$_ eq $section} @{$tree->{_recursive}}) {
  1         3  
319 1         2 push @{$doc}, "This section is I: it can contain subsection(s) with the same syntax.";
  1         2  
320             }
321 4         68 _genpod ($tree->{$section},$level+1,$doc);
322 4 100 66     22 next unless $tree->{$section}{_dyn} and $tree->{$section}{_dyndoc};
323 1         2 push @{$doc}, "Dynamical grammar changes for example instances of this section:";
  1         3  
324 1         1 push @{$doc}, "=over";
  1         2  
325 1         2 for my $name (sort keys %{$tree->{$section}{_dyndoc}}) {
  1         9  
326 3         9 my $newtree = _deepcopy($tree->{$section});
327 3         4 push @{$doc}, "=item B<$name>: $tree->{$section}{_dyndoc}{$name}";
  3         10  
328 3         5 &{$tree->{$section}{_dyn}}($section, $name, $newtree);
  3         17  
329 3         48 my @tdoc = _diffgrammars($tree->{$section}, $newtree);
330 3 100       5 if (@tdoc) {
331 1         2 push @{$doc}, @tdoc;
  1         2  
332             } else {
333 2         8 push @{$doc}, "No changes that can be automatically described.";
  2         6  
334             }
335 3         4 push @{$doc}, "(End of dynamical grammar changes for example instance C<$name>.)";
  3         14  
336             }
337 1         2 push @{$doc}, "=back";
  1         2  
338 1         2 push @{$doc}, "(End of dynamical grammar changes for example instances of section C<$section>.)";
  1         3  
339             }
340 4 100       16 push @{$doc}, "=back" if $level > 0
  2         5  
341             }
342 6 100       14 if ($tree->{_vars}) {
343 5         4 for my $var (@{$tree->{_vars}}) {
  5         9  
344 16 100 66     36 next unless $tree->{$var}{_dyn} and $tree->{$var}{_dyndoc};
345 1         1 push @{$doc}, "Dynamical grammar changes for example values of variable C<$var>:";
  1         3  
346 1         1 push @{$doc}, "=over";
  1         2  
347 1         2 for my $val (sort keys %{$tree->{$var}{_dyndoc}}) {
  1         5  
348 2         4 my $newtree = _deepcopy($tree);
349 2         3 push @{$doc}, "=item B<$val>: $tree->{$var}{_dyndoc}{$val}";
  2         15  
350 2         5 &{$tree->{$var}{_dyn}}($var, $val, $newtree);
  2         6  
351 2         22 my @tdoc = _diffgrammars($tree, $newtree);
352 2 100       13 if (@tdoc) {
353 1         2 push @{$doc}, @tdoc;
  1         2  
354             } else {
355 1         2 push @{$doc}, "No changes that can be automatically described.";
  1         3  
356             }
357 2         3 push @{$doc}, "(End of dynamical grammar changes for variable C<$var> example value C<$val>.)";
  2         15  
358             }
359 1         3 push @{$doc}, "=back";
  1         2  
360 1         1 push @{$doc}, "(End of dynamical grammar changes for example values of variable C<$var>.)";
  1         3  
361             }
362             }
363             };
364              
365             sub makepod($) {
366 2     2 0 150 my $self = shift;
367 2         12 my $tree = $self->{grammar};
368 2         4 my @doc;
369 2         11 _genpod($tree,0,\@doc);
370 2         38 return join("\n\n", @doc)."\n";
371             }
372              
373              
374             sub _set_variable($$$)
375             {
376 14     14   17 my $self = shift;
377 14         17 my $key = shift;
378 14         17 my $value = shift;
379            
380 14         31 my $gn = $self->_search_variable($key);
381 14 100       30 defined $gn or return 0;
382              
383 13         16 my $varlistref;
384 13 100       25 if (defined $self->{grammar}{_varlist}) {
385 3         4 $varlistref = $self->{cfg}{_varlist};
386             }
387              
388 13 100       30 if (defined $self->{grammar}{$gn}) {
389 12         17 my $g = $self->{grammar}{$gn};
390              
391             # check regular expression
392 12 50       20 if (defined $g->{_re}) {
393 0 0       0 $value =~ /^$g->{_re}$/ or do {
394 0 0       0 if (defined $g->{_re_error}) {
395 0         0 $self->_make_error($g->{_re_error});
396             }
397             else {
398 0         0 $self->_make_error("syntax error in value of '$key'");
399             }
400 0         0 return 0;
401             }
402             }
403 12 100       16 if (defined $g->{_sub}){
404 5         6 my $error = &{$g->{_sub}}($value, $varlistref);
  5         10  
405 5 50       54 if (defined $error){
406 0         0 $self->_make_error($error);
407 0         0 return 0;
408             }
409             }
410             # if there's a _dyn sub, apply it
411 12 100       22 if (defined $g->{_dyn}) {
412 2         3 &{$g->{_dyn}}($key, $value, $self->{grammar});
  2         3  
413             }
414             }
415 13         39 $self->{cfg}{$key} = $value;
416 13 100       20 push @{$varlistref}, $key if ref $varlistref;
  3         5  
417              
418 13         26 return 1;
419             }
420              
421              
422             sub parse($$)
423             {
424 3     3 0 242 my $self = shift;
425 3         6 my $file = shift;
426 3         4 my $args = shift;
427              
428 3 50       13 $self->{encoding} = $args->{encoding} if ref $args eq 'HASH';
429              
430 3         9 $self->{cfg} = {};
431 3         7 $self->{level} = 0;
432 3         5 $self->{cfg_stack} = [];
433 3         5 $self->{grammar_stack} = [];
434 3         6 $self->{file_stack} = [];
435 3         4 $self->{line_stack} = [];
436              
437             # we work with a copy of the grammar so the _dyn subs may change it
438 3         17 local $self->{grammar} = _deepcopy($self->{grammar});
439              
440 3 100       23 $self->_parse_file($file) or return undef;
441              
442 2 50       48 $self->_goto_level(0, undef) or return undef;
443              
444             # fill in the top level values from _default keywords
445 2         6 $self->_fill_defaults;
446              
447             $self->_check_mandatory($self->{grammar}, $self->{cfg}, undef)
448 2 50       15 or return undef;
449              
450 2         26 return $self->{cfg};
451              
452             }
453              
454             =head1 NAME
455              
456             Config::Grammar::Dynamic - A grammar-based, user-friendly config parser
457              
458             =head1 DESCRIPTION
459              
460             Config::Grammar::Dynamic is like Config::Grammar but with some additional
461             features useful for building configuration grammars that are dynamic, i.e.
462             where the syntax changes according to configuration entries in the same file.
463              
464             The following keys can be additionally specified in the grammar when using this
465             module:
466              
467             =head2 Special Section Keys
468              
469             =over 12
470              
471             =item _dyn
472              
473             A subroutine reference (function pointer) that will be called when
474             a new section of this syntax is encountered. The subroutine will get
475             three arguments: the syntax of the section name (string or regexp), the
476             actual name encountered (this will be the same as the first argument for
477             non-regexp sections) and a reference to the grammar tree of the section.
478             This subroutine can then modify the grammar tree dynamically.
479              
480             =item _dyndoc
481              
482             A hash reference that lists interesting names for the section that
483             should be documented. The keys of the hash are the names and the
484             values in the hash are strings that can contain an explanation
485             for the name. The _dyn() subroutine is then called for each of
486             these names and the differences of the resulting grammar and
487             the original one are documented. This module can currently document
488             differences in the _vars list, listing new variables and removed
489             ones, and differences in the _sections list, listing the
490             new and removed sections.
491              
492             =item _recursive
493              
494             Array containing the list of those sub-sections that are I, ie.
495             that can contain a new sub-section with the same syntax as themselves.
496              
497             The same effect can be accomplished with circular references in the
498             grammar tree or a suitable B<_dyn> section subroutine (see below},
499             so this facility is included just for convenience.
500              
501             =back
502              
503             =head2 Special Variable Keys
504              
505             =over 12
506              
507             =item _dyn
508              
509             A subroutine reference (function pointer) that will be called when the
510             variable is assigned some value in the config file. The subroutine will
511             get three arguments: the name of the variable, the value assigned and
512             a reference to the grammar tree of this section. This subroutine can
513             then modify the grammar tree dynamically.
514              
515             Note that no _dyn() call is made for default and inherited values of
516             the variable.
517              
518             =item _dyndoc
519              
520             A hash reference that lists interesting values for the variable that
521             should be documented. The keys of the hash are the values and the
522             values in the hash are strings that can contain an explanation
523             for the value. The _dyn() subroutine is then called for each of
524             these values and the differences of the resulting grammar and
525             the original one are documented. This module can currently document
526             differences in the _vars list, listing new variables and removed
527             ones, and differences in the _sections list, listing the
528             new and removed sections.
529              
530             =back
531              
532             =head1 COPYRIGHT
533              
534             Copyright (c) 2000-2005 by ETH Zurich. All rights reserved.
535             Copyright (c) 2007 by David Schweikert. All rights reserved.
536              
537             =head1 LICENSE
538              
539             This program is free software; you can redistribute it and/or modify it
540             under the same terms as Perl itself.
541              
542             =head1 AUTHORS
543              
544             David Schweikert,
545             Tobias Oetiker,
546             Niko Tyni
547              
548             =cut
549              
550             # Emacs Configuration
551             #
552             # Local Variables:
553             # mode: cperl
554             # eval: (cperl-set-style "PerlStyle")
555             # mode: flyspell
556             # mode: flyspell-prog
557             # End:
558             #
559             # vi: sw=4