File Coverage

blib/lib/Pod/FromActionscript.pm
Criterion Covered Total %
statement 170 180 94.4
branch 69 76 90.7
condition 14 18 77.7
subroutine 16 16 100.0
pod 1 1 100.0
total 270 291 92.7


line stmt bran cond sub pod time code
1             package Pod::FromActionscript;
2              
3 1     1   47824 use strict;
  1         3  
  1         36  
4 1     1   4 use warnings;
  1         2  
  1         27  
5 1     1   6 use Exporter;
  1         5  
  1         57  
6 1     1   6 use Carp;
  1         2  
  1         3765  
7              
8             our $VERSION = "0.53";
9             our @ISA = qw(Exporter);
10             our @EXPORT = qw();
11             our @EXPORT_OK = qw(asdoc2pod);
12              
13             # Use Regexp::Common if available, but fall back to an extract from
14             # v2.120 if needed
15 1     1   406 our $comment_re =
  0         0  
  0         0  
16             eval("local \$SIG{__WARN__} = 'DEFAULT'; local \$SIG{__DIE__} = 'DEFAULT';".
17             "use Regexp::Common qw(comment); \$RE{comment}{C}")
18             || qr/(?:(?:\/\*)(?:(?:[^\*]+|\*(?!\/))*)(?:\*\/))/;
19              
20             =head1 NAME
21              
22             Pod::FromActionscript - Convert Actionscript documentation to POD
23              
24             =head1 SYNOPSIS
25              
26             use Pod::FromActionscript (asdoc2pod);
27             asdoc2pod(infile => "com/clotho/Foo.as", outfile => "com.clotho.Foo.pod");
28             asdoc2pod(infile => "-" outfile => "-");
29             asdoc2pod(infile => \*STDIN, outfile => \*STDOUT);
30             asdoc2pod(in => $ascontent, out => \$podcontent);
31              
32             or use the C command-line program included in this
33             distribution.
34              
35             =head1 DESCRIPTION
36              
37             Parse Actionscript code, searching for Javadoc-style
38             comments. If any are found, convert them to POD (Perl's Plain Old
39             Documentation format). The output is just the POD, unless the
40             C flag is used, in which case the original Actionscript is
41             output with the Javadoc converted to POD.
42              
43             Only a limited subset of Javadoc commands are understood. See below
44             for the full list. Any unrecognized directives cause parsing to abort.
45             Future versions of this module should handle such failures more
46             gracefully.
47              
48             =head1 LICENSE
49              
50             Copyright 2005 Clotho Advanced Media, Inc.,
51              
52             This library is free software; you can redistribute it and/or modify it
53             under the same terms as Perl itself.
54              
55             =head1 FUNCTIONS
56              
57             =over
58              
59             =item asdoc2pod OPTIONS...
60              
61             Convert Javadoc-style comments embedded in Actionscript code into POD.
62             The arguments are key-value pairs as follows:
63              
64             =over
65              
66             =item in => SCALAR
67              
68             The input Actionscript code as a string.
69              
70             =item infile => FILENAME
71              
72             =item infile => FILEHANDLE
73              
74             Read the Actionscript code from a file. If the value is a reference,
75             it is assumed to be a filehandle. If it is a scalar, it is assumed to
76             be a filename. If the filename is C<->, then code is read in from
77             C.
78              
79             =item out => SCALARREF
80              
81             The output POD, or an empty string if no Javadoc is detected, is
82             assigned to the specified scalar reference.
83              
84             =item outfile => FILENAME
85              
86             =item outfile => FILEHANDLE
87              
88             Write the POD to a file. If there is no POD found, the no data is
89             written. If the C value is a reference, it is assumed to be a
90             filehandle. If it is a scalar, it is assumed to be a filename. If
91             the filename is C<->, then POD is written to C.
92              
93             =item verbose => BOOLEAN
94              
95             If true, some debugging information is printed. Defaults to false.
96              
97             =item code => BOOLEAN
98              
99             If true, then the Actionscript code is included in the output, with
100             the Javadoc comments replace with appropriate POD comments. If false,
101             then just the POD is output, with the code omitted. Defaults to false.
102              
103             =back
104              
105             =cut
106              
107             sub asdoc2pod
108             {
109 22     22 1 12060 my %opts = @_;
110              
111 22         46 my $in = _get_input(\%opts);
112 20         43 my $out = _convert($in, \%opts);
113 18         36 _write_output($out, \%opts);
114             }
115              
116             sub _get_input
117             {
118 22     22   29 my $opts = shift;
119              
120 22         20 my $in;
121 22 100       54 if (exists $opts->{in})
    100          
122             {
123 18         26 $in = $opts->{in};
124             }
125             elsif (exists $opts->{infile})
126             {
127 3         10 local $/ = undef;
128 3 100       14 if (ref $opts->{infile})
    50          
129             {
130 1         3 my $infh = $opts->{infile};
131 1         34 $in = <$infh>;
132             }
133             elsif ($opts->{infile} eq "-")
134             {
135 0         0 $in = ;
136             }
137             else
138             {
139 2         5 local *IN;
140 2 100       251 open(IN, '<', $opts->{infile})
141             or croak("Failed to read file $opts->{infile}: $!\n");
142 1         29 $in = ;
143 1         16 close(IN);
144             }
145             }
146             else
147             {
148 1         231 croak("No input source specified\n");
149             }
150 20         30 return $in;
151             }
152              
153             sub _write_output
154             {
155 18     18   22 my $out = shift;
156 18         18 my $opts = shift;
157              
158 18 100       45 if (exists $opts->{out})
    100          
    100          
159             {
160 13 100       32 if (ref $opts->{out})
161             {
162 12         13 my $var = $opts->{out};
163 12         35 $$var = $out;
164             }
165             else
166             {
167 1         134 croak("The out parameter is not a reference\n");
168             }
169             }
170             elsif ($out eq "")
171             {
172             # No output
173             }
174             elsif (exists $opts->{outfile})
175             {
176 3 100       12 if (ref $opts->{outfile})
    50          
177             {
178 1         2 my $of = $opts->{outfile};
179 1         19 print $of $out;
180             }
181             elsif ($opts->{outfile} eq "-")
182             {
183 0         0 print STDOUT $out;
184             }
185             else
186             {
187 2         4 local *OUT;
188 2 100       223 open(OUT, '>', $opts->{outfile})
189             or croak("Failed to write file $opts->{outfile}: $!\n");
190 1 50       17 print(OUT $out)
191             or croak("Failed to write file $opts->{outfile}: $!\n");
192 1 50       47 close(OUT)
193             or croak("Failed to write file $opts->{outfile}: $!\n");
194             }
195             }
196             else
197             {
198 1         144 croak("No output destination specified\n");
199             }
200             }
201              
202             sub _convert
203             {
204 20     20   20 my $content = shift;
205 20         22 my $opts = shift;
206              
207 20 100 66     79 if (!$opts->{code} && $content !~ /\/\*\*/)
208             {
209             # No javadoc included...
210 4         11 return "";
211             }
212              
213 16         17 my @out;
214 16         260 my @parts = split /($comment_re)/, $content;
215             #_diag($opts, "Got ".@parts." parts in ".length($content)." characters\n");
216              
217 16         25 my $over = 0;
218 16         17 my $inapi = 0;
219 16         32 foreach my $i (0..$#parts)
220             {
221 79 100 100     348 if ($i < $#parts && $parts[$i] =~ /^\/\*\*/)
222             {
223             # exclude comments like /** foo **/
224 32 100       100 next if ($parts[$i] =~ /^\/\*\*+[^\n\*]*\*+\//);
225            
226 31         45 my $comment = $parts[$i];
227            
228             # Remove comment open and close
229 31         98 $comment =~ s/^\/\*\s*//;
230 31         190 $comment =~ s/\s*\*\/$//;
231            
232             # Unindent the comment lines
233 31         171 $comment =~ s/^\s*\*[ \t]?//gm;
234            
235             # Convert {@code foobar} to C
236 31         44 $comment =~ s/\{\@code\s+([^\}]+)\}/C<$1>/gs;
237            
238            
239 31 100 66     392 if ($parts[$i+1] &&
    100 66        
    100 66        
240             $parts[$i+1] =~ /^\s*(?:class|interface)\s+([^\s;]+)/)
241             {
242 11         23 my $class = $1;
243 11         30 _diag($opts, "Class: $class\n");
244            
245 11         14 my $descrip = "";
246 11         20 my $name = _get_name(\$comment);
247 11         22 my $license = _get_license(\$comment);
248 11         21 my $author = _get_author(\$comment);
249 11         22 my $sees = _get_sees(\$comment);
250 11 100       31 if ($comment =~ /\S/)
251             {
252 4         10 $descrip = "=head1 DESCRIPTION\n\n$comment\n\n";
253             }
254 11         23 $comment = "$name$descrip$sees$license$author";
255            
256 11         16 $inapi = 0;
257             }
258             elsif ($parts[$i+1] &&
259             $parts[$i+1] =~ /^\s*((?:public|private)\s+|)(static\s+|)function\s+(\w+)\s*\(([^\)]*)\)(:\w+|)/)
260             {
261 6         10 my $private = $1;
262 6         9 my $static = $2;
263 6         8 my $fname = $3;
264 6         7 my $args = $4;
265 6         14 my $ftype = $5;
266 6         11 $private = $private =~ /private/;
267 6         8 $static = $static =~ /static/;
268            
269 6 50       13 if (!$inapi)
270             {
271 0         0 $inapi = 1;
272 0         0 $parts[$i-1] .= "/*\n\n=head1 API\n\n=cut\n*/\n";
273 0         0 push @out, "=head1 API\n\n";
274             }
275 6 50       10 if (!$over)
276             {
277 0         0 $over++;
278 0         0 $parts[$i-1] .= "/*\n\n=over\n\n=cut\n*/\n";
279 0         0 push @out, "=over\n\n";
280             }
281            
282 6 100       32 _diag($opts, "Function: ".($private?"private ":"").($static?"static ":"")."function $fname($args)$ftype\n");
    100          
283            
284 6         10 my ($paramlist, $params) = _get_params(\$comment);
285 6         14 my $returns = _get_returns(\$comment);
286 6         10 my $sees = _get_sees(\$comment);
287            
288 6         17 $comment = "=item $fname$paramlist\n\n$params$comment\n\n$returns$sees";
289             }
290             elsif ($parts[$i+1] &&
291             $parts[$i+1] =~ /^\s*((?:public|private)\s+|)(static\s+|)var\s+(\w+)(:\w+|)(\s*=\s*[^;]+|)/)
292             {
293 12         25 my $private = $1;
294 12         17 my $static = $2;
295 12         18 my $vname = $3;
296 12         18 my $vtype = $4;
297 12         19 my $default = $5;
298 12         17 $private = $private =~ /private/;
299 12         17 $static = $static =~ /static/;
300            
301 12         27 $default =~ s/^\s*=\s*//;
302 12 100       27 if ($default ne "")
303             {
304 6         14 $default = "B $default\n\n";
305             }
306            
307 12 100       25 if (!$inapi)
308             {
309 3         4 $inapi = 1;
310 3         10 $parts[$i-1] .= "/*\n\n=head1 API\n\n=cut\n*/\n";
311 3         5 push @out, "=head1 API\n\n";
312             }
313 12 100       20 if (!$over)
314             {
315 3         4 $over++;
316 3         8 $parts[$i-1] .= "/*\n\n=over\n\n=cut\n*/\n";
317 3         4 push @out, "=over\n\n";
318             }
319            
320 12 100       62 _diag($opts, "Var: ".($private?"private ":"").($static?"static ":"")."var $vname$vtype\n");
    100          
321            
322 12         28 my ($paramlist, $params) = _get_params(\$comment);
323 12         27 my $returns = _get_returns(\$comment);
324 12         22 my $sees = _get_sees(\$comment);
325            
326 12         42 $comment = "=item $vname$paramlist\n\n$params$comment\n\n$default$returns$sees";
327             }
328             else
329             {
330 2         290 carp("Unhandled comment type\n");
331             }
332            
333 30 100       114 if ($comment =~ /^=/)
334             {
335 29         109 $comment =~ s/\n\n\n+/\n\n/gs;
336 29         63 $parts[$i] = "/*\n\n$comment=cut\n*/";
337 29         46 push @out, $comment;
338             }
339            
340 30 100       112 if ($parts[$i] =~ /^\s*(\@\w*)/m)
341             {
342             #carp("Unhandled $1 in \n$comment\n");
343 1         147 carp("Unhandled $1\n");
344             }
345             }
346             }
347 14 100       30 if ($over > 0)
348             {
349 3         11 push @parts, "/*\n\n";
350 3         7 for (1..$over)
351             {
352 3         5 push @parts, "=back\n\n";
353 3         31 push @out, "=back\n\n";
354             }
355 3         13 push @parts, "=cut\n*/\n";
356             }
357            
358 14 100 100     55 if (!$opts->{code} && @out == 0)
359             {
360             # No POD to emit
361 2         5 return "";
362             }
363 12 100       59 return join("", $opts->{code} ? @parts : @out);
364             }
365              
366             ###############################################
367              
368             # Extracts @param tags from comments
369             sub _get_params
370             {
371 18     18   21 my $R_comment = shift;
372              
373 18         21 my $paramlist = "";
374 18         20 my $params = "";
375 18         60 while ($$R_comment =~ s/\n?[ \t]*\@param[ \t]+(\w+)(?:[ \t]*:)?[ \t]+([^\n]+)(?:\n|$)/\n/s)
376             {
377 6         13 my $pname = $1;
378 6         7 my $pdesc = $2;
379 6 100       13 $paramlist .= ($paramlist ? "," : "") . " $pname";
380             #$params .= "=item $pname\n\n$pdesc\n\n";
381 6         33 $params .= "B<$pname>: $pdesc\n\n";
382             }
383             #if ($params)
384             #{
385             # $params = "B\n\n=over\n\n" . $params . "=back\n\n";
386             #}
387 18         41 return ($paramlist, $params);
388             }
389              
390             # Extracts @returns tags from comments
391             sub _get_returns
392             {
393 18     18   19 my $R_comment = shift;
394              
395 18         20 my $returns = "";
396 18         54 while ($$R_comment =~ s/\n?[ \t]*\@returns?[ \t]+([^\n]+)(?:\n|$)/\n/s)
397             {
398 3         7 my $rdesc = $1;
399 3         10 $returns .= "B $rdesc\n\n";
400             }
401 18         30 return $returns;
402             }
403              
404             # Extracts @see tags from comments
405             sub _get_sees
406             {
407 29     29   32 my $R_comment = shift;
408              
409 29         42 my $sees = "";
410 29         92 while ($$R_comment =~ s/\n?[ \t]*\@see[ \t]+([^\n]+)(?:\n|$)/\n/s)
411             {
412 3         6 my $sdesc = $1;
413 3         12 $sees .= "B $sdesc\n\n";
414             }
415 29         52 return $sees;
416             }
417              
418             # Extracts @author tags from comments
419             sub _get_author
420             {
421 11     11   11 my $R_comment = shift;
422              
423 11         14 my $author = "";
424 11         67 while ($$R_comment =~ s/\n?[ \t]*\@author[ \t]+([^\n]+)(?:\n|$)/\n/s)
425             {
426 10         17 my $adesc = $1;
427 10         35 $author .= "=head1 AUTHOR\n\n$adesc\n\n";
428             }
429 11         36 return $author;
430             }
431              
432             # Extracts @license tags from comments
433             sub _get_license
434             {
435 11     11   12 my $R_comment = shift;
436              
437 11         12 my $license = "";
438 11         76 while ($$R_comment =~ s/\n?[ \t]*\@license[ \t]*(.*?)(\n[ \t]*\@|$)/$2/s)
439             {
440 3         9 my $adesc = $1;
441 3         16 $license .= "=head1 LICENSE\n\n$adesc\n\n";
442             }
443 11         20 return $license;
444             }
445              
446             # Extracts =head1 NAME from comments
447             sub _get_name
448             {
449 11     11   12 my $R_comment = shift;
450              
451 11         13 my $name = "";
452 11 100       38 if ($$R_comment =~ s/^\n*([\w\.]+)[ \t]+\-[ \t]+([^\n]+)(?:\n+|$)//s)
453             {
454 3         8 my $title = $1;
455 3         5 my $desc = $2;
456 3         10 $name = "=head1 NAME\n\n$title - $desc\n\n";
457             }
458 11         20 return $name;
459             }
460              
461             sub _diag
462             {
463 29     29   33 my $opts = shift;
464 29         30 my $msg = shift;
465              
466 29 50       115 warn $msg if ($opts->{verbose});
467             }
468              
469             1;
470              
471             __END__