File Coverage

blib/lib/XS/Install/ParseXS.pm
Criterion Covered Total %
statement 180 304 59.2
branch 53 142 37.3
condition 17 79 21.5
subroutine 28 34 82.3
pod 0 11 0.0
total 278 570 48.7


line stmt bran cond sub pod time code
1             package # hide from CPAN indexer
2             XS::Install::ParseXS;
3 1     1   136844 use strict;
  1         2  
  1         30  
4 1     1   3 use warnings;
  1         1  
  1         47  
5 1     1   4 use feature 'state';
  1         2  
  1         104  
6 1     1   3 no warnings 'redefine';
  1         1  
  1         31  
7 1     1   3 use ExtUtils::ParseXS;
  1         1  
  1         28  
8 1     1   728 use XS::Install::FrozenShit::ParseXS;
  1         3  
  1         57  
9 1     1   7 use XS::Install::FrozenShit::ParseXS::Eval;
  1         17  
  1         23  
10 1     1   4 use XS::Install::FrozenShit::ParseXS::Utilities;
  1         1  
  1         27  
11 1     1   526 use XS::Install::FrozenShit::Typemaps;
  1         2  
  1         29  
12 1     1   3 use XS::Install::FrozenShit::Typemaps::InputMap;
  1         2  
  1         16  
13 1     1   3 use XS::Install::FrozenShit::Typemaps::OutputMap;
  1         1  
  1         4202  
14              
15             *ExtUtils::ParseXS::new = *XS::Install::FrozenShit::ParseXS::new;
16              
17             my (@pre_callbacks, @no_typemap_callbacks);
18             our ($top_typemaps, $cur_typemaps);
19             our $cplus = grep { /-C\+\+/ } @ARGV;
20             my $re_quot1 = qr/"(?:[^"\\]+|\\.)*"/;
21             my $re_quot2 = qr/'(?:[^'\\]+|\\.)*'/;
22             my $re_quot = qr/(?:$re_quot1|$re_quot2)/;
23             my $re_comment_single = qr#//[^\n]*\n#;
24             my $re_comment_multi = qr#/\*.*?\*/#ms;
25             my $re_ignored = qr/(?:$re_quot|$re_comment_single|$re_comment_multi)/ms;
26             my $re_braces = qr#(?\{(?>[^/"'{}]+|$re_ignored|(?&braces)|/)*\})#ms;
27             our $re_xsub = qr/(XS_EUPXS\(XS_[a-zA-Z0-9_]+\))[^{]+($re_braces)/ms;
28             our $re_boot = qr/(XS_EXTERNAL\(boot_[a-zA-Z0-9_]+\))[^{]+($re_braces)/ms;
29             my $new_parsexs = $XS::Install::FrozenShit::ParseXS::VERSION >= 3.53;
30              
31 0     0 0 0 sub add_pre_callback { push @pre_callbacks, shift; }
32 0     0 0 0 sub add_post_callback { push @CatchEnd::post_callbacks, shift; }
33 0     0 0 0 sub add_no_typemap_callback { push @no_typemap_callbacks, shift; }
34              
35             sub call {
36 5     5 0 19 my ($cbs, @args) = @_;
37 5         70 $_->(@args) for @$cbs;
38             }
39              
40             sub code_start_idx {
41 8     8 0 15 my $lines = shift;
42 8         13 my $idx;
43 8         38 for (my $i = 2; $i < @$lines; ++$i) {
44 8 50       118 return $i+1 if $lines->[$i] =~ /^(PP)?CODE\s*:/;
45             }
46 0         0 die "code start not found";
47             }
48              
49             sub code_end_idx {
50 4     4 0 50 my $lines = shift;
51 4         15 my $idx = code_start_idx($lines);
52 4         13 for (; $idx < @$lines; ++$idx) {
53 30 100       149 return $idx if $lines->[$idx] =~ /^[a-zA-Z0-9]+\s*:/;
54             }
55 2         10 return $idx;
56             }
57              
58             sub is_empty {
59 4     4 0 8 my $lines = shift;
60 4         10 return code_start_idx($lines) == code_end_idx($lines);
61             }
62              
63             sub insert_code_top {
64 0     0 0 0 my ($parser, $code) = @_;
65 0         0 my $lines = $parser->{line};
66 0         0 my $linno = $parser->{line_no};
67 0         0 my $idx = code_start_idx($lines);
68 0         0 splice(@$lines, $idx, 0, $code);
69 0   0     0 splice(@$linno, $idx, 0, $linno->[$idx] // $linno->[-1]);
70             }
71              
72             sub insert_code_bottom {
73 0     0 0 0 my ($parser, $code) = @_;
74 0         0 my $lines = $parser->{line};
75 0         0 my $linno = $parser->{line_no};
76 0         0 my $idx = code_end_idx($lines);
77 0         0 splice(@$lines, $idx, 0, $code);
78 0   0     0 splice(@$linno, $idx, 0, $linno->[$idx] // $linno->[-1]);
79             }
80              
81             my $orig_new = \&XS::Install::FrozenShit::ParseXS::new;
82             *XS::Install::FrozenShit::ParseXS::new = sub {
83 1     1   198795 my $class = shift;
84 1         5 $class = 'XS::Install::FrozenShit::ParseXS';
85 1         7 my $self = $orig_new->($class, @_);
86 1 50       8 if ($new_parsexs) {
87 1         5 Hash::Util::unlock_keys(%$self);
88             };
89 1         15 return $self;
90             };
91              
92             my $orig_pmxl = \&XS::Install::FrozenShit::ParseXS::_process_module_xs_line;
93             *XS::Install::FrozenShit::ParseXS::_process_module_xs_line = sub {
94 2     2   20 my ($self, $module, $pkg, $prefix) = @_;
95 2         13 $orig_pmxl->(@_);
96 2         19 $self->{xsi}{package} = $pkg;
97 2         9 $self->{xsi}{module} = $module;
98 2         11 $self->{xsi}{inline_mode} = 0;
99             };
100              
101             sub get_mode {
102 7 50   7 0 46 return '' unless $_[0] =~ /^MODE\s*:\s*(\w+)\s*$/;
103 0         0 return uc($1);
104             }
105              
106             # pre process XS function
107             my $orig_fetch_para = \&XS::Install::FrozenShit::ParseXS::fetch_para;
108             *XS::Install::FrozenShit::ParseXS::fetch_para = sub {
109 10     10   21 my $self = shift;
110 10         85 my $ret = $orig_fetch_para->($self, @_);
111 10         24 my $lines = $self->{line};
112 10         23 my $linno = $self->{line_no};
113 10 100       76 return $ret unless @$lines;
114            
115 7 50       39 if (get_mode($lines->[0]) eq 'INLINE') {
116 0         0 $self->{xsi}{inline_mode} = 1;
117 0         0 shift @$lines;
118 0         0 shift @$linno;
119             }
120            
121 7 50       24 if ($self->{xsi}{inline_mode}) {
122 0         0 while (@$lines) {
123 0         0 my $line = shift @$lines;
124 0         0 shift @$linno;
125 0 0       0 if (get_mode($line) eq 'XS') {
126 0         0 $self->{xsi}{inline_mode} = 0;
127 0         0 last;
128             }
129 0         0 print "$line\n";
130             }
131 0 0       0 return $ret unless @$lines;
132             }
133            
134             # concat 2 lines codes (functions with default behaviour) to make it preprocessed like C-like synopsis
135 7 50       26 if (@$lines == 2) {
136 0         0 $lines->[0] .= ' '.$lines->[1];
137 0         0 splice(@$lines, 1, 1);
138 0         0 splice(@$linno, 1, 1);
139             }
140            
141 7 100 66     55 if ($lines->[0] and $lines->[0] =~ /^([A-Z]+)\s*\{/) {
142 1         7 $lines->[0] = "$1:";
143 1 50       6 if ($lines->[-1] =~ /^\}/) { pop @$lines; pop @$linno; }
  1         3  
  1         3  
144             }
145            
146 7         18 my %attrs;
147            
148 7 100 66     182 if ($lines->[0] and $lines->[0] =~ /^(.+?)\s+([^\s()]+\s*(\((?:[^()]|(?3))*\)))\s*(.*)/) {
149 4         73 my ($type, $sig, $rest) = ($1, $2, $4);
150 4         13 shift @$lines;
151 4         8 my $deflinno = shift @$linno;
152            
153 4         9 my $remove_closing;
154 4 50 0     18 if ((my $idx = index($rest, '{')) >= 0) { # move following text on next line
    0          
155 4         7 $remove_closing = 1;
156 4         15 my $content = substr($rest, $idx+1);
157 4         10 substr($rest, $idx) = '';
158 4 50       13 if ($content =~ /\S/) {
159 0         0 unshift @$lines, $content;
160 0         0 unshift @$linno, $deflinno;
161             }
162             } elsif ($lines->[0] and $lines->[0] =~ s/^\s*\{//) { # '{' on next line
163 0         0 $remove_closing = 1;
164 0 0       0 if ($lines->[0] !~ /\S/) { # nothing remains, delete entire line
165 0         0 shift @$lines;
166 0         0 shift @$linno;
167             }
168             }
169              
170 4 50       12 if ($remove_closing) {
171 4         82 $lines->[-1] =~ s/}\s*;?\s*$//;
172 4 50       16 if ($lines->[-1] !~ /\S/) { pop @$lines; pop @$linno; }
  4         9  
  4         8  
173            
174 4 50 33     41 if (!$lines->[0] or $lines->[0] !~ /\S/) { # no code remains, but body was present ({}), add empty code to prevent default behaviour
175 0         0 $lines->[0] = ' ';
176 0   0     0 $linno->[0] ||= $deflinno;
177             }
178             }
179            
180 4 50       15 if (length $lines->[0]) {
181 4 100       67 unshift @$lines, $type =~ /^void(\s|$)/ ? 'PPCODE:' : 'CODE:';
182 4         13 unshift @$linno, $deflinno;
183             }
184            
185 4 50       13 if ($rest =~ /:(.+)/) {
186 0         0 my $attrs_str = $1;
187 0         0 %attrs = ($attrs_str =~ /\s*([A-Za-z]+)\s*(?:\(([^()]*)\)|)\s*/g);
188             }
189            
190 4         19 while (my ($attr, $val) = each %attrs) {
191 0         0 $attr = uc($attr);
192 0 0 0     0 if ($attr eq 'ALIAS' && (my @alias = split /\s*,\s*/, $val)) {
    0          
    0          
193 0         0 foreach my $alias_entry (reverse @alias) {
194 0         0 unshift @$lines, " $alias_entry";
195 0         0 unshift @$linno, $deflinno;
196             }
197 0         0 unshift @$lines, 'ALIAS:';
198 0         0 unshift @$linno, $deflinno;
199             }
200 0         0 elsif ($attr eq 'CONST') { next }
201             elsif (defined $val) {
202 0         0 unshift @$lines, "$attr: $val";
203 0         0 unshift @$linno, $deflinno;
204             }
205             }
206              
207 4         12 unshift @$lines, $sig;
208 4         8 unshift @$lines, $type;
209 4         77 unshift @$linno, $deflinno for 1..2;
210             }
211            
212             # make BOOT's code in personal scope
213 7 100       26 if ($lines->[0] =~ /^BOOT\s*:/) {
214 1         5 splice(@$lines, 1, 0, " {");
215 1         4 splice(@$linno, 1, 0, $linno->[0]);
216 1         3 push @$lines, " }";
217 1         5 push @$linno, $linno->[-1];
218             }
219            
220             map {
221 7         89 s/\b__PACKAGE__\b/"$self->{xsi}{package}"/g;
  49         194  
222 49         128 s/\b__MODULE__\b/"$self->{xsi}{module}"/g;
223             } @$lines;
224            
225 7 50       28 my $out_type = $lines->[0] or return $ret;
226             # filter out junk, because first line might be "BOOT:", "PROTOTYPES: ...", "INCLUDE: ...", "#ifdef", etc
227 7 100 33     148 return $ret if !$out_type or $out_type =~ /^#/ or $out_type =~ /^[_A-Z]+\s*:([^:]|$)/;
      66        
228              
229             # parse signature -> $func and @args
230 4         20 my $sig = $lines->[1];
231 4 50       29 $sig =~ /^([^(]+)\((.*)\)\s*$/ or die "bad signature: '$sig', at $self->{filepathname}, function $self->{pname}";
232 4         15 my $func = $1;
233 4         23 my $args_str = $2;
234 4         65 $func =~ s/^\s+//; $func =~ s/\s+$//;
  4         29  
235 4         27 my @args;
236             my $variadic;
237 4         79 for my $str (split /\s*,\s*/, $args_str) {
238 8         16 my %info;
239 8 50       24 $info{default} = $1 if $str =~ s/\s*=\s*(.+)$//;
240 8 100       57 $info{name} = $1 if $str =~ s/([a-zA-Z0-9_\$]+)\s*$//;
241 8         19 $str =~ s/^\s+//g; $str =~ s/\s+$//g;
  8         27  
242 8         54 $info{type} = $str;
243 8 100 33     97 if ($str eq '...') {
    50 66        
    100          
244 2         6 $variadic = 1;
245 2         7 next;
246             }
247             elsif (!$info{type} && !$info{name}) {
248            
249             }
250             elsif (!$info{type} || !$info{name}) { # arg with no name (unused arg)
251 2   33     9 $info{type} ||= $info{name}; # (for signatures like "string, " name will consume type)
252 2 50       7 die "bad argument: '$sig', at $self->{filepathname}, function $self->{pname}" unless $info{type};
253 2         85 state $unused_no = 1;
254 2 50       23 $info{name} = $new_parsexs ? '_____unused'.($unused_no++) : '';
255             }
256            
257 6         22 map { s/^\s+//; s/\s+$// } values %info;
  12         33  
  12         95  
258 6         20 push @args, \%info;
259             }
260            
261 4 50       16 if ($func =~ s/^(.+):://) { # replace 'Class::meth' with 'meth(Class* THIS)'
262 0 0       0 unshift @args, $func eq 'new' ? {name => 'CLASS', type => 'SV*', orig_type => $1} :
263             {name => 'THIS', type => "$1*"};
264             }
265            
266 4         13 my $first_arg = $args[0];
267 4 50 33     76 $first_arg->{type} = 'const '.$first_arg->{type} if exists($attrs{const}) or exists($attrs{CONST});
268 4   33     36 my $is_method = $first_arg && $first_arg->{name} eq 'THIS';
269            
270 4         34 my $para = join("\n", @$lines);
271            
272 4 50       96 if ($para !~ /^(PP)?CODE\s*:/m) { # empty function, replace with $func(@args) or $first_arg->$func(@rest_args)
273 0         0 my $void = $out_type =~ /^void(?:\s|$)/;
274 0 0       0 push @$lines, $void ? 'PPCODE:' : 'CODE:';
275 0         0 push @$linno, $linno->[-1];
276 0 0 0     0 if ($func ne 'new' and ($func ne 'DESTROY' or !$is_method)) {
      0        
277 0         0 my $code = '';
278 0         0 my @real_args = @args;
279 0 0       0 if ($is_method) {
280 0         0 shift @real_args;
281 0         0 $code = $first_arg->{name}.'->';
282             }
283 0         0 $code .= $func.'('.join(', ', map { $_->{name} } @real_args).')';
  0         0  
284 0 0       0 $code = "RETVAL = $code" unless $void;
285 0         0 push @$lines, " $code;";
286 0         0 push @$linno, $linno->[-1];
287             }
288 0         0 $para = join("\n", @$lines);
289             }
290            
291 4 100 66     54 if ($para =~ /^CODE\s*:/m and $para !~ /^OUTPUT\s*:/m) { # add OUTPUT:RETVAL unless any
292 2         12 push @$lines, 'OUTPUT:', ' RETVAL';
293 2         13 push @$linno, $linno->[-1] for 1..2;
294 2         10 $para = join("\n", @$lines);
295             }
296            
297 4         48 my $cb_args = {
298             ret => $out_type,
299             func => $func,
300             args => \@args,
301             variadic => $variadic,
302             };
303 4         28 call(\@pre_callbacks, $self, $cb_args);
304            
305             # form final signature for ParseXS
306 4 50       13 my @args_lines = map { "$_->{type} $_->{name}".(defined($_->{default}) ? " = $_->{default}" : '') } @args;
  6         41  
307 4 100       16 push @args_lines, '...' if $variadic;
308 4         14 $sig = $func.' ('.join(', ', @args_lines).')';
309            
310 4         28 $lines->[0] = $out_type;
311 4         8 $lines->[1] = $sig;
312            
313 4 50       14 if (is_empty($lines)) {
314 0 0 0     0 if ($func eq 'DESTROY' and $is_method) {
    0          
315 0         0 insert_code_top($self, " delete THIS;");
316             }
317             elsif ($func eq 'new') {
318 0         0 insert_code_top($self, " RETVAL = ".default_constructor($out_type, \@args).';');
319             }
320             }
321            
322 4         50 return $ret;
323             };
324              
325             if ($new_parsexs) {
326             my $orig_node_param_as_code = \&XS::Install::FrozenShit::ParseXS::Node::Param::as_code;
327             *XS::Install::FrozenShit::ParseXS::Node::Param::as_code = sub {
328 8     8   17 my XS::Install::FrozenShit::ParseXS::Node::Param $self = shift;
329 8 100       44 return '' if $self->{var} =~ /^_____unused/;
330 6         21 return $orig_node_param_as_code->($self, @_);
331             };
332             }
333              
334             sub default_constructor {
335 0     0 0 0 my ($ret_type, $args) = @_;
336 0         0 my @pass_args = @$args;
337 0         0 my $fa = shift @pass_args;
338 0         0 my $args_str = join(', ', map { $_->{name} } @pass_args);
  0         0  
339 0         0 my $new_type = $fa->{orig_type};
340 0 0       0 unless ($new_type) {
341 0         0 $new_type = $ret_type;
342 0         0 $new_type =~ s/\s*\*$//;
343             }
344 0         0 my $ret = "new $new_type($args_str)";
345            
346 0 0       0 $ret = "$ret_type($ret)" unless $ret_type =~ /\*$/;
347            
348 0         0 return $ret;
349             }
350              
351             {
352             my $orig_merge = \&XS::Install::FrozenShit::Typemaps::merge;
353             my $orig_parse = \&XS::Install::FrozenShit::Typemaps::_parse;
354             my $orig_get = \&XS::Install::FrozenShit::Typemaps::get_typemap;
355            
356             *XS::Install::FrozenShit::Typemaps::get_typemap = sub {
357 14     14   44 my $ret = $orig_get->(@_);
358 14 50       55 return $ret if $ret;
359 0         0 call(\@no_typemap_callbacks, @_);
360 0         0 return $orig_get->(@_);
361             };
362            
363             *XS::Install::FrozenShit::Typemaps::merge = sub {
364 2     2   13 $top_typemaps = $_[0];
365 2         35 return $orig_merge->(@_);
366             };
367            
368             *XS::Install::FrozenShit::Typemaps::_parse = sub {
369 2     2   10 local $cur_typemaps = $_[0];
370 2         15 return $orig_parse->(@_);
371             };
372             }
373              
374             {
375             # remove ugly default behaviour, it always overrides typemaps in xsubpp's command line
376             *XS::Install::FrozenShit::ParseXS::Utilities::standard_typemap_locations = sub {
377 1     1   3 my $inc = shift;
378 1         5 my @ret;
379 1 50       44 push @ret, 'typemap' if -e 'typemap';
380 1         7 return @ret;
381             };
382             }
383              
384             {
385             package # hide from CPAN
386             CatchEnd;
387 1     1   9 use strict;
  1         2  
  1         24  
388 1     1   5 use feature 'say';
  1         2  
  1         1317  
389            
390             our @post_callbacks;
391              
392             my ($out, $orig_stdout);
393             open $orig_stdout, '>&', STDOUT;
394             close STDOUT;
395             open STDOUT, '>', \$out or die $!; # This shouldn't fail
396            
397             #post-process XS out
398             sub END {
399 1   50 1   5543 $out //= '';
400 1         31 select $orig_stdout;
401              
402 1         20 $out =~ s/^MODE\s*:.+//mg;
403            
404             # remove XS function C-prototype (it causes warnings on many compilers)
405 1         71 $out =~ s/XS_EUPXS\(XS_[a-zA-Z0-9_]+\);.*\n/\n/mg;
406            
407             # remove XS BOOT function C-prototype
408 1         45 $out =~ s/XS_EXTERNAL\(boot_[a-zA-Z0-9_]+\);.*\n/\n/mg;
409              
410 1         7 XS::Install::ParseXS::call(\@post_callbacks, \$out);
411 1         3922 print $out;
412             }
413             }
414              
415             {
416             package #hide from CPAN
417             XS::Install::FrozenShit::ParseXS;
418             my $END = "!End!\n\n";
419             my $BLOCK_regexp = '\s*(' . $XS::Install::FrozenShit::ParseXS::Constants::XSKeywordsAlternation . "|$END)\\s*:";
420             # copy-paste from XS::Install::FrozenShit::ParseXS to fix Typemaps with references (&). ParseXS was simply removing it from type
421             # only one line changed with regexp
422             # my ($var_type, $var_addr, $var_name) = /^(.*?[^&\s])\s*(\&?)\s*\b(\w+)$/s
423             *INPUT_handler = sub {
424 4     4   7 my $self = shift;
425 4         10 $_ = shift;
426 4         267 for (; !/^$BLOCK_regexp/o; $_ = shift(@{ $self->{line} })) {
  0            
427 0 0         last if /^\s*NOT_IMPLEMENTED_YET/;
428 0 0         next unless /\S/; # skip blank lines
429            
430 0           trim_whitespace($_);
431 0           my $ln = $_;
432            
433             # remove trailing semicolon if no initialisation
434 0 0         s/\s*;$//g unless /[=;+].*\S/;
435            
436             # Process the length(foo) declarations
437 0 0         if (s/^([^=]*)\blength\(\s*(\w+)\s*\)\s*$/$1 XSauto_length_of_$2=NO_INIT/x) {
438 0           print "\tSTRLEN\tSTRLEN_length_of_$2;\n";
439 0           $self->{lengthof}->{$2} = undef;
440 0           $self->{deferred} .= "\n\tXSauto_length_of_$2 = STRLEN_length_of_$2;\n";
441             }
442            
443             # check for optional initialisation code
444 0           my $var_init = '';
445 0 0         $var_init = $1 if s/\s*([=;+].*)$//s;
446 0           $var_init =~ s/"/\\"/g;
447             # *sigh* It's valid to supply explicit input typemaps in the argument list...
448 0           my $is_overridden_typemap = $var_init =~ /ST\s*\(|\$arg\b/;
449            
450 0           s/\s+/ /g;
451 0           my $var_addr = '';
452 0 0         my ($var_type, $var_name) = /^(.*?[^\s])\s*\b(\w+)$/s
453             or $self->blurt("Error: invalid argument declaration '$ln'"), next;
454            
455             # Check for duplicate definitions
456             $self->blurt("Error: duplicate definition of argument '$var_name' ignored"), next
457             if $self->{arg_list}->{$var_name}++
458 0 0 0       or defined $self->{argtype_seen}->{$var_name} and not $self->{processing_arg_with_types};
      0        
459            
460 0           $self->{thisdone} |= $var_name eq "THIS";
461 0           $self->{retvaldone} |= $var_name eq "RETVAL";
462 0           $self->{var_types}->{$var_name} = $var_type;
463             # XXXX This check is a safeguard against the unfinished conversion of
464             # generate_init(). When generate_init() is fixed,
465             # one can use 2-args map_type() unconditionally.
466 0           my $printed_name;
467 0 0         if ($var_type =~ / \( \s* \* \s* \) /x) {
468             # Function pointers are not yet supported with output_init()!
469 0           print "\t" . map_type($self, $var_type, $var_name);
470 0           $printed_name = 1;
471             }
472             else {
473 0           print "\t" . map_type($self, $var_type, undef);
474 0           $printed_name = 0;
475             }
476 0           $self->{var_num} = $self->{args_match}->{$var_name};
477            
478 0 0         if ($self->{var_num}) {
479 0           my $typemap = $self->{typemap}->get_typemap(ctype => $var_type);
480 0 0 0       $self->report_typemap_failure($self->{typemap}, $var_type, "death")
481             if not $typemap and not $is_overridden_typemap;
482 0   0       $self->{proto_arg}->[$self->{var_num}] = ($typemap && $typemap->proto) || "\$";
483             }
484 0 0         $self->{func_args} =~ s/\b($var_name)\b/&$1/ if $var_addr;
485 0 0 0       if ($var_init =~ /^[=;]\s*NO_INIT\s*;?\s*$/
    0 0        
    0 0        
486             or $self->{in_out}->{$var_name} and $self->{in_out}->{$var_name} =~ /^OUT/
487             and $var_init !~ /\S/) {
488 0 0         if ($printed_name) {
489 0           print ";\n";
490             }
491             else {
492 0           print "\t$var_name;\n";
493             }
494             }
495             elsif ($var_init =~ /\S/) {
496             $self->output_init( {
497             type => $var_type,
498             num => $self->{var_num},
499 0           var => $var_name,
500             init => $var_init,
501             printed_name => $printed_name,
502             } );
503             }
504             elsif ($self->{var_num}) {
505             $self->generate_init( {
506             type => $var_type,
507             num => $self->{var_num},
508 0           var => $var_name,
509             printed_name => $printed_name,
510             } );
511             }
512             else {
513 0           print ";\n";
514             }
515             }
516             };
517             }
518              
519             1;