File Coverage

blib/lib/Sub/Starter.pm
Criterion Covered Total %
statement 285 352 80.9
branch 101 156 64.7
condition 8 24 33.3
subroutine 26 28 92.8
pod 5 5 100.0
total 425 565 75.2


line stmt bran cond sub pod time code
1             #!/
2             # --------------------------------------
3             #
4             # Title: Sub Starter
5             # Purpose: Creates a skeletal framework for Perl sub's.
6             #
7             # Name: Sub::Starter
8             # File: Starter.pm
9             # Created: July 25, 2009
10             #
11             # Copyright: Copyright 2009 by Shawn H Corey. All rights reserved.
12             #
13             # This program is free software; you can redistribute it and/or modify
14             # it under the terms of the GNU General Public License as published by
15             # the Free Software Foundation, version 3 of the License, or
16             # (at your option) any later version.
17             #
18             # This program is distributed in the hope that it will be useful,
19             # but WITHOUT ANY WARRANTY; without even the implied warranty of
20             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21             # GNU General Public License for more details.
22             #
23             # You should have received a copy of the GNU General Public License
24             # along with this program; if not, write to the Free Software
25             # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26              
27             # --------------------------------------
28             # Object
29             package Sub::Starter;
30              
31             # --------------------------------------
32             # Pragmatics
33              
34             require 5.008;
35              
36 5     5   121021 use strict;
  5         13  
  5         189  
37 5     5   27 use warnings;
  5         8  
  5         134  
38              
39 5     5   5045 use utf8; # Convert all UTF-8 to Perl's internal representation.
  5         57  
  5         26  
40              
41             # --------------------------------------
42             # Version
43 5     5   4273 use version; our $VERSION = qv(v1.0.6);
  5         11602  
  5         46  
44              
45             # --------------------------------------
46             # Modules
47 5     5   453 use Carp;
  5         9  
  5         379  
48 5     5   5074 use Data::Dumper;
  5         52913  
  5         437  
49 5     5   13336 use English qw( -no_match_vars ) ; # Avoids regex performance penalty
  5         25201  
  5         35  
50 5     5   7214 use POSIX;
  5         45462  
  5         79  
51 5     5   42778 use Storable qw( dclone );
  5         24445  
  5         27662  
52              
53             # --------------------------------------
54             # Configuration Parameters
55              
56             my %Expand = (
57             name => sub { [ $_[0]{-name} ] },
58             usage => \&_fill_out_usage,
59             parameters => \&_fill_out_parameters,
60             returns => \&_fill_out_returns,
61             definitions => \&_fill_out_definitions,
62             );
63              
64             my %Selections = (
65             are => \&_fill_out_are,
66             arenot => \&_fill_out_arenot,
67             each => \&_fill_out_each,
68             first => \&_fill_out_first,
69             rest => \&_fill_out_rest,
70             list => \&_fill_out_list,
71             );
72              
73             my %Default_attributes = (
74             -assignment => q{''},
75             -max_usage => 0,
76             -max_variable => 0,
77             -name => '',
78             -object => '',
79             -parameters => [],
80             -returns_alternate => '',
81             -returns => [],
82             );
83              
84             my %String_escapes = (
85             '\\' => '\\', # required, don't delete
86             n => "\n",
87             s => ' ',
88             t => "\t",
89             );
90             my $String_escapes = join( '', sort keys %String_escapes );
91             $String_escapes =~ s{ \\ }{}gmsx;
92             $String_escapes = "[$String_escapes\\\\]";
93              
94             my $RE_id = qr{ [_[:alpha:]] [_[:alnum:]]* }mosx;
95             my $RE_scalar = qr{ \A \$ ( $RE_id ) \z }mosx;
96             my $RE_array = qr{ \A \@ ( $RE_id ) \z }mosx;
97             my $RE_hash = qr{ \A \% ( $RE_id ) \z }mosx;
98             my $RE_scalar_ref = qr{ \A \\ \$ ( $RE_id ) \z }mosx;
99             my $RE_array_ref = qr{ \A \\ \@ ( $RE_id ) \z }mosx;
100             my $RE_hash_ref = qr{ \A \\ \% ( $RE_id ) \z }mosx;
101             my $RE_code_ref = qr{ \A \\ \& ( $RE_id ) \z }mosx;
102             my $RE_typeglob = qr{ \A \\? \* ( $RE_id ) \z }mosx;
103              
104             # Make Data::Dumper pretty
105             $Data::Dumper::Sortkeys = 1;
106             $Data::Dumper::Indent = 1;
107             $Data::Dumper::Maxdepth = 0;
108              
109             # --------------------------------------
110             # Variables
111              
112             # --------------------------------------
113             # Methods
114              
115             # --------------------------------------
116             # Name: new
117             # Usage: $starter_sub = Sub::Starter->new( ; %attributes );
118             # Purpose: To create a new object.
119             # Parameters: %attributes -- keys must be in %Default_attributes
120             # Returns: $starter_sub -- blessed hash
121             #
122             sub new {
123 13     13 1 32287 my $class = shift @_;
124 13         996 my $self = dclone( \%Default_attributes );
125              
126 13   33     77 $class = ( ref( $class ) or $class );
127 13         36 bless $self, $class;
128 13         41 $self->configure( @_ );
129              
130 13         39 return $self;
131             }
132              
133             # --------------------------------------
134             # Name: configure
135             # Usage: $starter_sub->configure( %attributes );
136             # Purpose: To (re)set the initial key-values pairs of the object.
137             # Parameters: %attributes -- keys must be in %Default_attributes
138             # Returns: none
139             #
140             sub configure {
141 25     25 1 42 my $self = shift @_;
142 25         77 my %attributes = @_;
143              
144 25         94 for my $attribute ( keys %attributes ){
145 96 50       186 croak "unknown attribute '$attribute'" unless exists $Default_attributes{$attribute};
146 96         225 $self->{$attribute} = $attributes{$attribute};
147             }
148             }
149              
150             # --------------------------------------
151             # Name: get_attributes
152             # Usage: %attributes = $starter_sub->get_attributes( ; @attribute_names );
153             # Purpose: To retrieve the current value(s) of the attributes.
154             # Parameters: @attribute_names -- each must be a key in %Default_attributes
155             # Returns: %attributes -- current settings
156             #
157             sub get_attributes {
158 0     0 1 0 my $self = shift @_;
159 0         0 my @attributes = @_;
160 0         0 my %attributes = ();
161              
162 0 0       0 if( @attributes ){
163 0         0 for my $attribute ( @attributes ){
164 0 0       0 $attributes{$attribute} = $self->{$attribute} if exists $Default_attributes{$attribute};
165             }
166             }else{
167 0         0 for my $attribute ( keys %Default_attributes ){
168 0         0 $attributes{$attribute} = $self->{$attribute};
169             }
170             }
171              
172 0         0 return %attributes;
173             }
174              
175             # --------------------------------------
176             # Name: _parse_variable
177             # Usage: %attr = _parse_variable( $parsed, $var );
178             # Purpose: Find the attributes of a variable.
179             # Parameters: $parsed -- scratch pad for results
180             # $var -- variable to parse
181             # Returns: %attr -- attributes of the variable
182             #
183             sub _parse_variable {
184 20     20   32 my $parsed = shift @_;
185 20         31 my $var = shift @_;
186 20         39 my $name = '';
187 20         27 my %attr = ();
188              
189 20         46 $attr{-usage} = $var;
190 20 100       74 $parsed->{-max_usage} = length $var if $parsed->{-max_usage} < length $var;
191              
192 20 100       201 if( $var =~ $RE_scalar ){
    100          
    50          
    50          
    50          
    50          
    0          
    0          
193 4         9 $name = $1;
194 4         13 $attr{-type} = 'scalar';
195 4         24 $attr{-variable} = $attr{-usage};
196             }elsif( $var =~ $RE_array ){
197 12         26 $name = $1;
198 12         25 $attr{-type} = 'array';
199 12         53 $attr{-variable} = $attr{-usage};
200             }elsif( $var =~ $RE_hash ){
201 0         0 $name = $1;
202 0         0 $attr{-type} = 'hash';
203 0         0 $attr{-variable} = $attr{-usage};
204             }elsif( $var =~ $RE_scalar_ref ){
205 0         0 $name = $1; # . '_sref';
206 0         0 $attr{-type} = 'scalar_ref';
207 0         0 $attr{-variable} = '$' . $name;
208             }elsif( $var =~ $RE_array_ref ){
209 0         0 $name = $1; # . '_aref';
210 0         0 $attr{-type} = 'array_ref';
211 0         0 $attr{-variable} = '$' . $name;
212             }elsif( $var =~ $RE_hash_ref ){
213 4         27 $name = $1; # . '_href';
214 4         12 $attr{-type} = 'hash_ref';
215 4         13 $attr{-variable} = '$' . $name;
216             }elsif( $var =~ $RE_code_ref ){
217 0         0 $name = $1; # . '_cref';
218 0         0 $attr{-type} = 'code_ref';
219 0         0 $attr{-variable} = '$' . $name;
220             }elsif( $var =~ $RE_typeglob ){
221 0         0 $name = $1; # . '_gref';
222 0         0 $attr{-type} = 'typeglob';
223 0         0 $attr{-variable} = '$' . $name;
224             }else{
225 0         0 croak "unknown variable type: $var";
226             }
227              
228 20         34 my $length = length( $name ) + 1;
229 20 100       66 $parsed->{-max_variable} = $length if $parsed->{-max_variable} < $length;
230 20         125 return %attr;
231             }
232              
233             # --------------------------------------
234             # Name: _parse_returns
235             # Usage: _parse_returns( $parsed, $returns_part );
236             # Purpose: Parse the sub's return variables
237             # Parameters: $parsed -- storage hash
238             # $returns_part -- part of the usage statement before the assignment
239             # Returns: none
240             #
241             sub _parse_returns {
242 12     12   18 my $parsed = shift @_;
243 12         18 my $returns = shift @_;
244 12         17 my $list_var = 0;
245 12         21 my %seen = ();
246              
247 12 100       38 return unless length $returns;
248              
249 8 50       32 if( $returns =~ s{ \+\= \z }{}msx ){
250 0         0 $parsed->{-assignment} = 0;
251             }else{
252 8         34 $returns =~ s{ \= \z }{}msx;
253             }
254              
255 8 50       52 if( $returns =~ m{ \A ( ([^\|]*) \| )? \( (.*?) \) \z }msx ){
    100          
256 0         0 $parsed->{-returns_alternate} = $2;
257 0         0 my $list = $3;
258              
259 0 0       0 if( $parsed->{-returns_alternate} ){
260 0         0 $parsed->{-returns_alternate} = { _parse_variable( $parsed, $parsed->{-returns_alternate} ) };
261 0 0       0 croak "alternative return variable is not a scalar" if $parsed->{-returns_alternate}{-type} ne 'scalar';
262             }
263              
264 0         0 for my $var ( split m{ \, }msx, $list ){
265 0 0       0 if( $seen{$var} ++ ){
266 0         0 croak "Return parameter $var repeated";
267             }
268 0         0 my %attr = _parse_variable( $parsed, $var );
269 0         0 push @{ $parsed->{-returns} }, { %attr };
  0         0  
270 0 0 0     0 if( $attr{-type} eq 'array' or $attr{-type} eq 'hash' ){
271 0 0       0 croak "array or hash may only occur at end of returns list" if $list_var ++;
272             }
273             }
274             }elsif( $returns =~ m{ \A ([^\|]*) \| (.*?) \z }msx ){
275 4         17 $parsed->{-returns_alternate} = $1;
276 4         30 my $var = $2;
277              
278 4         23 $parsed->{-returns_alternate} = { _parse_variable( $parsed, $parsed->{-returns_alternate} ) };
279 4 50       25 croak "alternative return variable is not a scalar" if $parsed->{-returns_alternate}{-type} ne 'scalar';
280 4 50       26 if( $seen{$var} ++ ){
281 0         0 croak "Return parameter $var repeated";
282             }
283 4         15 my %attr = _parse_variable( $parsed, $var );
284 4         10 push @{ $parsed->{-returns} }, { %attr };
  4         28  
285             }else{
286 4 50       21 if( $seen{$returns} ++ ){
287 0         0 croak "Return parameter $returns repeated";
288             }
289 4         13 my %attr = _parse_variable( $parsed, $returns );
290 4         10 push @{ $parsed->{-returns} }, { %attr };
  4         22  
291             }
292 8         26 return;
293             }
294              
295             # --------------------------------------
296             # Name: _parse_parameters
297             # Usage: _parse_parameters( $parsed, $param_part );
298             # Purpose: Break the parameters into variables and store them.
299             # Parameters: $parsed -- storage hash
300             # $param_part -- part of the usage statement including optional parameters
301             # Returns: none
302             #
303             sub _parse_parameters {
304 12     12   18 my $parsed = shift @_;
305 12         18 my $param_part = shift @_;
306 12         19 my $opt_params = '';
307 12         18 my $list_var = 0;
308 12         16 my %seen = ();
309              
310 12 100       44 if( $param_part =~ m{ \A ([^;]*) \; (.*) }msx ){
311 4         8 $param_part = $1;
312 4         10 $opt_params = $2;
313             }
314              
315 12         46 for my $param ( split m{ \, }msx, $param_part ){
316 4 50       19 if( $seen{$param} ++ ){
317 0         0 die "Parameter $param repeated\n";
318             }
319 4         14 my %attr = _parse_variable( $parsed, $param );
320 4         12 push @{ $parsed->{-parameters} }, { %attr };
  4         23  
321 4 50 33     30 if( $attr{-type} eq 'array' or $attr{-type} eq 'hash' ){
322 4 50       25 die "array or hash may only occur at end of parameter list" if $list_var ++;
323             }
324             }
325              
326 12         36 for my $param ( split m{ \, }msx, $opt_params ){
327 4 50       18 if( $seen{$param} ++ ){
328 0         0 die "Parameter $param repeated\n";
329             }
330 4         11 my %attr = _parse_variable( $parsed, $param );
331 4         10 push @{ $parsed->{-parameters} }, { optional=>1, %attr };
  4         26  
332 4 50 33     50 if( $attr{-type} eq 'array' or $attr{-type} eq 'hash' ){
333 4 50       22 die "array or hash may only occur at end of parameter list" if $list_var ++;
334             }
335             }
336              
337 12         31 return;
338             }
339              
340             # --------------------------------------
341             # Name: parse_usage
342             # Usage: $sub_starter->parse_usage( $usage_statement );
343             # Purpose: Parse a usage statement and store its contents.
344             # Parameters: $usage_statement -- See POD for details
345             # Returns: none
346             #
347             sub parse_usage {
348 12     12 1 182 my $self = shift @_;
349 12         21 my $usage_statement = shift @_;
350 12         19 my $usage = $usage_statement;
351              
352             # create a scratch pad
353 12         321 my $parsed = dclone( \%Default_attributes );
354              
355             # clean up for easier processing
356 12         75 $usage =~ s{ \s+ }{}gmsx;
357 12         79 $usage =~ s{ \)? \;? \z }{}msx;
358              
359             # find returns via an assignment symbol
360 12         21 my $returns_part = '';
361 12         17 my $func_part = $usage;
362 12 100       62 if( $usage =~ m{ \A ( [^=]* \= ) (.*) }msx ){
363 8         22 $returns_part = $1;
364 8         21 $func_part = $2;
365             }
366 12 50       42 if( $func_part =~ m{ = }msx ){
367 0         0 croak "Multiple assignments in usage statement";
368             }
369              
370             # get the name and possible object
371 12         24 my $name_part = $func_part;
372 12         20 my $param_part = '';
373 12 100       60 if( $name_part =~ m{ \A ( [^()]* ) \( ( .*? ) \)? \z }msx ){
374 8         18 $name_part = $1;
375 8         16 $param_part = $2;
376             }
377 12 100       50 if( $name_part =~ s{ \A (.*?) \-\> }{}msx ){
378 4         19 $parsed->{-object} = $1;
379 4         11 $parsed->{-max_variable} = 5;
380             }
381 12         21 $name_part =~ s{ \A \& }{}msx;
382 12         29 $parsed->{-name} = $name_part;
383              
384             # parse the rest
385 12         108 _parse_returns( $parsed, $returns_part );
386 12         28 _parse_parameters( $parsed, $param_part );
387              
388             # set the values
389 12         51 $self->configure( %$parsed );
390              
391             # print "\n\nSub::Starter->parse_usage(): ", Dumper $usage_statement, $self;
392 12         67 return;
393             }
394              
395             # --------------------------------------
396             # Name: _fill_out_usage
397             # Usage: \@text = _fill_out_usage( $self );
398             # Purpose: Create a usage statement
399             # Parameters: $self -- parameters of the sub
400             # Returns: \@text -- the usage statement in an anonynous array
401             #
402             sub _fill_out_usage {
403 9     9   16 my $self = shift @_;
404 9         15 my $text = '';
405              
406             # alternative returns
407 9 100       28 if( ref $self->{-returns_alternate} ){
408 3         12 $text = $self->{-returns_alternate}{-usage} . ' | ';
409             }
410              
411             # do returns
412 9 100       12 if( @{ $self->{-returns} } > 0 ){
  9         32  
413 6 50       8 $text .= '( ' if @{ $self->{-returns} } > 1;
  6         21  
414 6         10 my @list = ();
415 6         9 for my $return ( @{ $self->{-returns} } ){
  6         14  
416 6         17 push @list, $return->{-usage};
417             }
418 6         19 $text .= join( ', ', @list ) . ' ';
419 6 50       8 $text .= ') ' if @{ $self->{-returns} } > 1;
  6         22  
420 6 50       20 if( $self->{-assignment} eq '0' ){
421 0         0 $text .= '+= ';
422             }else{
423 6         14 $text .= '= ';
424             }
425             }
426              
427             # do object
428 9 100       29 if( length $self->{-object} ){
429 3         12 $text .= $self->{-object} . '->';
430             }
431              
432             # do name
433 9         21 $text .= $self->{-name} . '(';
434              
435             # do parameters
436 9 100       10 if( @{ $self->{-parameters} } > 0 ){
  9         32  
437 6         10 $text .= ' ';
438 6         7 my @list = ();
439 6         11 my @optional = ();
440 6         11 for my $parameter ( @{ $self->{-parameters} } ){
  6         15  
441 6 100       16 if( $parameter->{optional} ){
442 3         12 push @optional, $parameter->{-usage};
443             }else{
444 3         12 push @list, $parameter->{-usage};
445             }
446             }
447 6         14 $text .= join( ', ', @list );
448 6 100       15 if( @optional ){
449 3         10 $text .= '; ' . join( ', ', @optional );
450             }
451 6         14 $text .= ' ';
452             }
453              
454             # finish
455 9         12 $text .= ');';
456              
457 9         25 return [ $text ];
458             }
459              
460             # --------------------------------------
461             # Name: _fill_out_are
462             # Usage: \@text = _fill_out_are( $max_len, $string, @list );
463             # Purpose: Determine if there is a list
464             # Parameters: $string -- A string to return
465             # @list -- a list to test
466             # Returns: \@text -- array of the string or undef
467             #
468             sub _fill_out_are {
469 12     12   16 my $max_len = shift @_;
470 12         173 my $string = shift @_;
471 12         18 my @list = @_;
472              
473 12 100       29 return unless @list;
474              
475 8 100       17 if( defined $string ){
476 4 50       46 $string =~ s{ \\ ($String_escapes) }{$String_escapes{$1}||$1}egmsx;
  4         19  
477             }else{
478 4         8 $string = '';
479             }
480              
481 8         204 return [ $string ];
482             }
483              
484             # --------------------------------------
485             # Name: _fill_out_arenot
486             # Usage: \@text = _fill_out_arenot( $max_len, $string, @list );
487             # Purpose: Determine if there isn't a list
488             # Parameters: $string -- A string to return
489             # @list -- a list to test
490             # Returns: \@text -- array of the string or undef
491             #
492             sub _fill_out_arenot {
493 12     12   17 my $max_len = shift @_;
494 12         14 my $string = shift @_;
495 12         22 my @list = @_;
496              
497 12 100       42 return if @list;
498              
499 4 50       7 if( defined $string ){
500 0 0       0 $string =~ s{ \\ ($String_escapes) }{$String_escapes{$1}||$1}egmsx;
  0         0  
501             }else{
502 4         8 $string = '';
503             }
504              
505 4         13 return [ $string ];
506             }
507              
508             # --------------------------------------
509             # Name: _fill_out_each
510             # Usage: \@text = _fill_out_each( $max_len, $format, @list );
511             # Purpose: Apply the format to all items in the list.
512             # Parameters: $format -- How to display the items
513             # @list -- The list
514             # Returns: \@text -- Formatted items
515             #
516             sub _fill_out_each {
517 14     14   17 my $max_len = shift @_;
518 14   50     34 my $format = shift @_ || '%s';
519 14         19 my @list = @_;
520 14         18 my $text = undef;
521              
522 14 100       39 return unless @list;
523              
524 9 50       57 $format =~ s{ \\ ($String_escapes) }{$String_escapes{$1}||$1}egmsx;
  12         47  
525              
526             # print 'each: ', Dumper $format, \@list;
527 9 100       28 if( $format =~ m{ \* }msx ){
528 5         6 $text = [ map { sprintf( $format, $max_len, $_ ) } @list ];
  5         19  
529             }else{
530 4         8 $text = [ map { sprintf( $format, $_ ) } @list ];
  5         18  
531             }
532              
533 9         36 return $text;
534             }
535              
536             # --------------------------------------
537             # Name: _fill_out_first
538             # Usage: \@text = _fill_out_first( $max_len, $format, @list );
539             # Purpose: Apply the format to the first item of the list.
540             # Parameters: $format -- How to display the items
541             # @list -- The list
542             # Returns: \@text -- Formatted items
543             #
544             sub _fill_out_first {
545 6     6   7 my $max_len = shift @_;
546 6         7 my $format = shift @_;
547 6         9 my @list = @_;
548              
549 6 100       15 return unless @list;
550              
551 4         12 return _fill_out_each( $max_len, $format, $list[0] );
552             }
553              
554             # --------------------------------------
555             # Name: _fill_out_rest
556             # Usage: \@text = _fill_out_rest( $max_len, $format, @list );
557             # Purpose: Apply the format to all but the first item of the list.
558             # Parameters: $format -- How to display the items
559             # @list -- The list
560             # Returns: \@text -- Formatted items
561             #
562             sub _fill_out_rest {
563 6     6   7 my $max_len = shift @_;
564 6         6 my $format = shift @_;
565 6         9 my @list = @_;
566              
567 6 100       15 return unless @list;
568              
569 4         11 return _fill_out_each( $max_len, $format, @list[ 1 .. $#list ] );
570             }
571              
572             # --------------------------------------
573             # Name: _fill_out_list
574             # Usage: \@text = _fill_out_list( $max_len, $separator, @list );
575             # Purpose: Create a string of the list
576             # Parameters: $separator -- What to join with
577             # @list -- List ot join
578             # Returns: \@text -- Array of the string
579             #
580             sub _fill_out_list {
581 0     0   0 my $max_len = shift @_;
582 0   0     0 my $separator = shift @_ || ' ';
583 0         0 my @list = @_;
584              
585 0         0 return [ join( $separator, @list ) ];
586             }
587              
588             # --------------------------------------
589             # Name: _fill_out_parameters
590             # Usage: \@text = _fill_out_parameters( $self, $selection, $format );
591             # Purpose: Create a list of formatted, selected parameters.
592             # Parameters: $self -- contains parameter list
593             # $selection -- a subset of the parameters
594             # $format -- how to display
595             # Returns: \@text -- formatted, selected parameters
596             #
597             sub _fill_out_parameters {
598 21     21   25 my $self = shift @_;
599 21         26 my $selection = shift @_;
600 21         29 my $format = shift @_;
601              
602 21         23 my @list = map { $_->{-usage} } @{ $self->{-parameters} };
  14         43  
  21         50  
603             #print 'parameters: ',Dumper \@list;
604              
605 21 50       56 if( exists $Selections{$selection} ){
606 21         32 return &{ $Selections{$selection} }( $self->{-max_usage}, $format, @list );
  21         53  
607             }else{
608 0         0 carp "no selection for '$selection', skipped";
609 0         0 return;
610             }
611             }
612              
613             # --------------------------------------
614             # Name: _fill_out_returns_expression
615             # Usage: \@text = _fill_out_returns_expression( $self );
616             # Purpose: Create the return expression for tits statement.
617             # Parameters: $self -- essential data
618             # Returns: \@text -- string in an array
619             #
620             sub _fill_out_returns_expression {
621 3     3   6 my $self = shift @_;
622 3         4 my $text = ' ';
623              
624 3 100       4 return [''] unless @{ $self->{-returns} };
  3         13  
625              
626             # print 'expression: ', Dumper $self;
627 2         3 my $returns = '';
628 2 50       2 if( @{ $self->{-returns} } > 1 ){
  2         7  
629 0         0 $returns = '( ' . join( ', ', map { $_->{-variable} } @{ $self->{-returns} } ) . ' )';
  0         0  
  0         0  
630             }else{
631 2         6 $returns = $self->{-returns}[0]{-variable};
632             }
633              
634 2 100       7 if( $self->{-returns_alternate} ){
635 1         4 $text .= "wantarray ? $returns : $self->{-returns_alternate}{-variable}";
636             }else{
637 1         2 $text .= $returns;
638             }
639              
640 2         9 return [ $text ];
641             }
642              
643             # --------------------------------------
644             # Name: _fill_out_returns
645             # Usage: \@text = _fill_out_returns( $self, $selection, $format );
646             # Purpose: Create a list of formatted, selected returns.
647             # Parameters: $self -- contains returns list
648             # $selection -- a subset of the returns
649             # $format -- how to display
650             # Returns: \@text -- formatted, selected returns
651             #
652             sub _fill_out_returns {
653 24     24   35 my $self = shift @_;
654 24         29 my $selection = shift @_;
655 24         24 my $format = shift @_;
656 24         35 my $text = [];
657              
658 24         385 my @list = map { $_->{-usage} } @{ $self->{-returns} };
  16         245  
  24         221  
659 24 100       66 if( $self->{-returns_alternate} ){
660 8         22 unshift @list, $self->{-returns_alternate}{-usage};
661             }
662             #print 'returns: ',Dumper \@list;
663              
664 24 100       223 if( $selection eq 'expression' ){
    50          
665 3         8 return _fill_out_returns_expression( $self );
666             }elsif( exists $Selections{$selection} ){
667 21         38 return &{ $Selections{$selection} }( $self->{-max_usage}, $format, @list );
  21         52  
668             }else{
669 0         0 carp "no selection for '$selection', skipped";
670 0         0 return;
671             }
672              
673 0         0 return $text;
674             }
675              
676             # --------------------------------------
677             # Name: _fill_out_definitions
678             # Usage: \@text = _fill_out_definitions( $self, $format );
679             # Purpose: Create a list of formatted, selected definitions.
680             # Parameters: $self -- contains parameter and returns list
681             # $format -- how to display
682             # Returns: \@text -- formatted, selected definitions
683             #
684             sub _fill_out_definitions {
685 3     3   5 my $self = shift @_;
686 3   50     7 my $format = shift @_ || '%s = %s';
687 3         5 my @list = ();
688 3         6 my $text = [];
689 3         4 my %seen = ();
690              
691             # print 'self: ', Dumper $self;
692              
693 3 0       33 $format =~ s{ \\ ($String_escapes) }{$String_escapes{$1}||$1}egmsx;
  0         0  
694              
695             # do parameters
696 3 100       10 if( $self->{-object} ){
697 1         6 push @list, {
698             -name => 'self',
699             -variable => '$self',
700             -type => 'scalar',
701             -usage => '$self'
702             };
703             }
704 3         3 push @list, @{ $self->{-parameters} };
  3         7  
705              
706             # print 'parameters @list ', Dumper \@list, $format;
707 3         6 for my $item ( @list ){
708 3 50       12 next if $seen{$item->{-variable}} ++;
709 3         3 my $value = 'shift @_';
710 3 100       9 $value .= " || $self->{-assignment}" if $item->{optional};
711 3 100 66     15 if( $item->{-type} eq 'array' || $item->{-type} eq 'hash' ){
712 2         3 $value = '@_';
713             }
714 3 50       9 if( $format =~ m{ \* }msx ){
715 3         15 push @$text, sprintf( $format, $self->{-max_variable}, $item->{-variable}, $value );
716             }else{
717 0         0 push @$text, sprintf( $format, $item->{-variable}, $value );
718             }
719             }
720              
721             # do returns
722 3         6 @list = ();
723 3 100       9 if( $self->{-returns_alternate} ){
724 1         3 push @list, $self->{-returns_alternate};
725             }
726 3         4 push @list, @{ $self->{-returns} };
  3         6  
727              
728             # print 'returns @list ', Dumper \@list;
729 3         6 for my $item ( @list ){
730 3 100       12 next if $seen{$item->{-variable}} ++;
731 2         5 my $value = $self->{-assignment};
732 2 100 33     18 if( $item->{-type} eq 'scalar' ){
    50          
    50          
    50          
733             # value already set
734             }elsif( $item->{-type} eq 'array' || $item->{-type} eq 'hash' ){
735 0         0 $value = '()';
736             }elsif( $item->{-type} eq 'array_ref' ){
737 0         0 $value = '[]';
738             }elsif( $item->{-type} eq 'hash_ref' ){
739 1         2 $value = '{}';
740             }else{
741 0         0 $value = 'undef';
742             }
743 2 50       6 if( $format =~ m{ \* }msx ){
744 2         13 push @$text, sprintf( $format, $self->{-max_variable}, $item->{-variable}, $value );
745             }else{
746 0         0 push @$text, sprintf( $format, $item->{-variable}, $value );
747             }
748             }
749              
750 3         12 return $text;
751             }
752              
753             # --------------------------------------
754             # Name: fill_out
755             # Usage: $text = $sub_starter->fill_out( \@template );
756             # Purpose: Fill out the template with the current parameters
757             # Parameters: \@template -- List of lines with replacements
758             # Returns: $text -- resulting text
759             #
760             sub fill_out {
761 9     9 1 41 my $self = shift @_;
762 9         12 my $template = shift @_;
763 9         13 my $text = '';
764              
765 9         15 for my $template_line ( @$template ){
766 108         137 my $line = $template_line; # copy to modify
767              
768 108 100       393 if( $line =~ m{ \A (.*?) \e\[1m \( ([^\)]*) \) \e\[0?m (.*) }msx ){
769 66         121 my $front = $1;
770 66         90 my $item = $2;
771 66         78 my $back = $3;
772 66         170 my ( $directive, @arguments ) = split m{ \s+ }msx, $item;
773              
774 66         74 my $expansion; # array reference
775 66 50       120 if( exists $Expand{$directive} ){
776 66         73 $expansion = &{ $Expand{$directive} }( $self, @arguments );
  66         3400  
777             }else{
778 0         0 carp "no expansion for '$directive'";
779 0         0 next;
780             }
781              
782 66         157 for my $expanded ( @$expansion ){
783 48         378 $text .= $front . $expanded . $back;
784             }
785              
786             }else{
787 42         68 $text .= $line;
788             }
789             }
790              
791 9         194 return $text;
792             }
793              
794             1;
795             __DATA__