BASIS  version 1.2.3 (revision 2104)
PerlFilter.pm
Go to the documentation of this file.
00001 
00002 # =======================================================================
00003 # Doxygen Pre-Processor for Perl
00004 # Copyright (C) 2002  Bart Schuller
00005 # Copyright (C) 2006  Phinex Informatik AG
00006 # All Rights Reserved
00007 # 
00008 # Doxygen Filter is free software; you can redistribute it and/or modify
00009 # it under the same terms as Perl itself.
00010 # 
00011 # Larry Wall's 'Artistic License' for perl can be found in
00012 # http://www.perl.com/pub/a/language/misc/Artistic.html
00013 # 
00014 # =======================================================================
00015 # 
00016 # Author: Aeby Thomas, Phinex Informatik AG,
00017 #     Based on DoxygenFilter from Bart Schuller
00018 # E-Mail: tom.aeby@phinex.ch
00019 # 
00020 # Phinex Informatik AG
00021 # Thomas Aeby
00022 # Kirchweg 52
00023 # 1735 Giffers
00024 # 
00025 # =======================================================================
00026 # 
00027 # @(#) $Id: PerlFilter.pm,v 1.6 2009/01/08 09:32:48 aeby Exp $
00028 # 
00029 # Revision History:
00030 # 
00031 # $Log: PerlFilter.pm,v $
00032 # Revision 1.6  2009/01/08 09:32:48  aeby
00033 # added support for @var command (suggested by Mike Richardson)
00034 #
00035 # Revision 1.5  2006/02/15 15:59:29  aeby
00036 # filter(): "@method" handling: drop auto-recognized $self argument at
00037 # first position
00038 #
00039 # Revision 1.4  2006/01/31 17:46:06  aeby
00040 # filter(): avoid warnings about uninitialized values
00041 # analyze_sub(): added some more argument recognition patterns
00042 #
00043 # Revision 1.3  2006/01/31 16:53:52  aeby
00044 # added copyright info
00045 #
00046 #  
00047 # =======================================================================
00048 
00049 ## @file PerlFilter.pm
00050 # @brief Implementation of DoxyGen::PerlFilter.
00051 
00052 
00053 ## @class
00054 # Filter from perl syntax API docs to Doxygen-compatible syntax.
00055 # This class is meant to be used as a filter for the
00056 # <a href="http://www.doxygen.org/">Doxygen</a> documentation tool.
00057 package SBIA::BASIS::DoxyGen::PerlFilter;
00058 
00059 use warnings;
00060 use strict;
00061 use base qw(SBIA::BASIS::DoxyGen::Filter);
00062 my $id = __PACKAGE__;
00063 
00064 ## @method void filter($infh)
00065 # Do the filtering.
00066 # @param infh input filehandle, normally STDIN
00067 sub filter {
00068     my($self, $infile) = @_;
00069     open(my $infh, $infile);
00070     my $current_class = "";
00071     my $file = [];
00072     while( <$infh> ) {
00073         push( @$file, $_ );
00074     }
00075     $self->file_contents( $file );
00076     my $objcontext = 
00077         grep( /^\s*use\s+base\s/, @$file )
00078         || grep( /\@ISA/, @$file )
00079         || grep( /^\s*bless/, @$file )
00080         || grep( /^\s*sub\s+new\s/, @$file )
00081         || grep( /\$self/, @$file );
00082 
00083     push( @$file, "" );  # in order to have a delimiting empty line at EOF
00084     for( my $line=0; $line <= $#$file; ) {
00085         $_ = $file->[$line++];
00086         if (/^##\s*\@(\S+)\s*(.*)/) {
00087             my($command, $args) = ($1, $2);
00088             my @more;
00089             while ( $_ = $file->[$line++] ) {
00090                 if (/^#\s?(.+)/s) {
00091                     push @more, $1;
00092                 } else {
00093                     last;
00094                 }
00095             }
00096             if ($command eq 'file') {
00097                 $args ||= $infile;
00098                 $self->start("\@$command $args");
00099                 $self->more(@more);
00100                 $self->end;
00101             } elsif ($command eq 'class') {
00102         $objcontext = 1;
00103                 unless ($args) {
00104                     ($args) = /package\s(.*);/;
00105                 }
00106                 if ($current_class) {
00107                     $self->flush;
00108                     $self->print("};\n");
00109                 }
00110                 $current_class = $args;
00111         $self->emit_class( $args, $line, [
00112             "\@$command $args",
00113             @more,
00114             "\@nosubgrouping"
00115         ] );
00116             } elsif ($command  eq 'cmethod') {
00117                 unless ($args) {
00118             ($args) = $self->analyze_sub( $line-1 );
00119                 }
00120                 $args = $self->munge_parameters($args);
00121                 $self->push($self->protection($args).' Class Methods');
00122                 $self->start("\@fn $args")->more(@more)->end;
00123                 $self->print($args, ";\n");
00124                 $self->pop;
00125             } elsif ($command  eq 'fn') {
00126                 unless ($args) {
00127             ($args) = $self->analyze_sub( $line-1 );
00128                 }
00129                 $args = $self->munge_parameters($args);
00130                 $self->push($self->protection($args).' Functions');
00131                 $self->start("\@fn $args")->more(@more)->end;
00132                 $self->print($args, ";\n");
00133                 $self->pop;
00134             } elsif ($command  eq 'method') {
00135                 unless ($args) {
00136             my( $name, @args );
00137             ($args, $name, @args) = $self->analyze_sub( $line-1 );
00138             $args =~ s/\$self,*\s*// if( $args[0] eq '$self' );
00139                 }
00140                 $args = $self->munge_parameters($args);
00141                 $self->push($self->protection($args).' Object Methods');
00142                 $self->start("\@fn $args")->more(@more)->end;
00143                 $self->print($args, ";\n");
00144                 $self->pop;
00145             } elsif ($command  eq 'enum') {
00146                 $self->start("\@$command $args");
00147                 $self->more(@more);
00148                 $self->end;
00149                 $self->print("$command $args;\n");
00150         } elsif ($command  eq 'var') {
00151         $args =~ /([\w:]+)\s*([\w]+)\s*(.*)/ ;
00152         my $type = $1 ;
00153         my $name = $2 ;
00154         my $text = $3 ;
00155         $self->start( $text );
00156         $self->more( @more );
00157         $self->end();
00158         $self->print("$type $name;\n\n");
00159             } else {
00160                 $self->start("\@$command $args");
00161                 $self->more(@more);
00162                 $self->end;
00163             }
00164             # We ate a line when we got the rest of the comment lines
00165             redo if defined $_;
00166         } elsif (/^use\s+([\w:]+)/) {
00167             my $inc = $1;
00168             $inc =~ s/::/\//g;
00169             $self->print("#include \"$inc.pm\"\n");
00170         } elsif (/^package\s+([\w:]+)/) {
00171         if ($current_class) {
00172         $self->flush;
00173         $self->print("};\n");
00174         }
00175         next unless( $objcontext );
00176         $current_class = $1;
00177         $self->emit_class( $current_class, $line );
00178         } elsif (/^\s*sub\s+([\w:]+)/) {
00179         my( $proto, $name, @args ) = $self->analyze_sub( $line-1 );
00180         if( $current_class && @args && ($args[0] eq "\$self") ) {
00181         $self->push($self->protection($proto).' Object Methods');
00182         $proto =~ s/\$self,*\s*//;
00183         } elsif( $current_class 
00184             && ((@args && ($args[0] eq "\$class")) || ($name eq "new")) ) {
00185         $self->push($self->protection($proto).' Class Methods');
00186         } else {
00187         $self->push($self->protection($proto).' Functions');
00188         }
00189         $proto = $self->munge_parameters($proto);
00190         $self->print($proto, ";\n");
00191         $self->pop;
00192     }
00193     }
00194     $self->flush();
00195     if ($current_class) {
00196         $self->print("};\n");
00197     }
00198 }
00199 
00200 
00201 
00202 ## @method @ analyze_sub( int line )
00203 # analyzes a subroutine declaration starting at the given line. Tries
00204 # to determine whicht arguments it takes.
00205 #
00206 # @param line The line number at which the sub starts
00207 # @return A function prototype, the name of the function and a
00208 #   list of arguments it takes
00209 sub analyze_sub {
00210     my( $self, $line ) = @_;
00211 
00212     my $file = $self->file_contents();
00213     $file->[$line] =~ /sub\s+(.*)\{/;
00214     my $name = $1;
00215     my $proto;
00216     my @args;
00217     if( $name =~ /^(.*)\s*\((.*)\)/ ) {
00218         $name = $1;
00219     $proto = $2;
00220     }
00221     else {
00222         my $forward = 5;
00223         for( my $i=0; $forward && ($i+$line <= $#$file) && ! $proto; $i++ ) {
00224         $_ = $file->[$i+$line];
00225         if( /^\s*my\s*\((.*)\)\s*=\s*\@_/ ) {
00226             $proto = $1;
00227         }
00228         elsif( /^\s*(local|my)\s*([^\s]*)\s*=\s*shift\s*;/ ) {
00229             push( @args, $2 );
00230         }
00231         elsif( /^\s*(local|my)\s*([^\s]*)\s*=\s*\$_\[\s*(\d+)\s*]/ ) {
00232             $args[$3] = $2;
00233         }
00234         elsif( /shift\s*->\s*[a-z0-9_]+\(/ ) {
00235             # assuming anonymously used shifted value is $self
00236         push( @args, '$self' );
00237         }
00238         elsif( /^\s*\n/ || /^\s*#/ ) {
00239             ;
00240         }
00241         elsif( /}/ ) {
00242             $forward = 0;
00243         }
00244         else {
00245             $forward--;
00246         }
00247     }
00248     }
00249     if( $proto ) {
00250         $proto =~ s/\s+//g;
00251     $proto =~ s/,/, /g;
00252     @args = split( ", ", $proto );
00253     }
00254         
00255     $name =~ s/\s+$//;
00256     my $protection = "";
00257     if( substr( $name, 0, 1 ) eq "_" ) {
00258         $protection = "protected";
00259     }
00260     return( "$protection retval $name( ".join(", ", @args )." )", $name, @args );
00261 }
00262 
00263 
00264 
00265 ## @method emit_class( string class, int line, arrayref doc )
00266 # Emit one class definition. If the doc parameter is defined,
00267 # emits the array as a comment just before the class definition,
00268 # otherwise, only the class definition is emitted.
00269 #
00270 # @param class the name of the class
00271 # @param line the current line number
00272 # @param doc (optional) an array with comment lines
00273 sub emit_class {
00274     my( $self, $class, $line, $doc ) = @_;
00275 
00276     my(@current_isa, @current_include);
00277     my $file = $self->file_contents();
00278     while ($_ = $file->[$line++] ) {
00279     if (/^\s*(?:use base|\@ISA\s*=|\@${class}::ISA\s*=)\s+(.+);/) {
00280         @current_isa = eval $1;
00281         $file->[$line-1] = "\n";
00282     } elsif (/^use\s+([\w:]+)/) {
00283         my $inc = $1;
00284         $inc =~ s/::/\//g;
00285         push @current_include, $inc;
00286         $file->[$line-1] = "\n";
00287     } elsif (/^package/) {
00288         last;
00289     }
00290     }
00291 
00292     $self->print("#include \"$_.pm\"\n") foreach @current_include;
00293     $self->print("\n");
00294     
00295     if( $doc ) {
00296         $self->start($doc->[0]);
00297     $self->more( @$doc[1 .. $#$doc] );
00298     $self->end();
00299     }
00300     $self->print("class $class");
00301 
00302     if (@current_isa) {
00303     $self->print(":",
00304         join(", ", map {"public $_"} @current_isa) );
00305     }
00306     $self->print(" {\npublic:\n");
00307 }
00308 
00309 
00310 
00311 ## @method arrayref file_contents( arrayref contents )
00312 # set/get an array containing the whole input file, each
00313 # line at one array index.
00314 #
00315 # @param contents (optional) file array ref
00316 # @return The file array ref
00317 sub file_contents {
00318     my( $self, $contents ) = @_;
00319 
00320     $self->{"$id file"} = $contents if( defined $contents );
00321     return( $self->{"$id file"} );
00322 }
00323 
00324 
00325 
00326 ## @method munge_parameters($args)
00327 # Munge the argument list. Because DoxyGen does not seem to handle $, @ and %
00328 # as argument types properly, we replace them with full length strings.
00329 #
00330 # @param args String specifying anything after a directive
00331 # @return Processed string.
00332 sub munge_parameters {
00333     my ($this, $args) = @_;
00334 
00335     $args =~ s/\$\@/scalar_or_list /g;
00336     $args =~ s/\@\$/scalar_or_list /g;
00337     $args =~ s/\$/scalar /g;
00338     $args =~ s/\@/list /g;
00339     $args =~ s/\%/hash /g;
00340 
00341 #    my ($ret, $remainder) = ($args =~ /^\s*(\S+)(.+)/);
00342 #    if ($ret) {
00343 #        if ($ret eq '$') {
00344 #            $ret = 'scalar';
00345 #        } elsif ($ret eq '@') {
00346 #            $ret = 'list';
00347 #        } elsif ($ret eq '$@') {
00348 #            $ret = 'scalar_or_list';
00349 #        } elsif ($ret eq '@$') {
00350 #            $ret = 'list_or_scalar';
00351 #        }
00352 #
00353 #        $args = "$ret$remainder";
00354 #    }
00355 
00356     return $args;
00357 }
00358 
00359 
00360 1;