=head1 LICENSE Copyright [1999-2013] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =cut =head1 LICENSE Doxygen Pre-Processor for Perl Copyright (C) 2002 Bart Schuller Copyright (C) 2006 Phinex Informatik AG All Rights Reserved Doxygen Filter is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Larry Wall's 'Artistic License' for perl can be found in http://www.perl.com/pub/a/language/misc/Artistic.html ------------------------------------------------ Author: Aeby Thomas, Phinex Informatik AG, Based on DoxygenFilter from Bart Schuller E-Mail: tom.aeby@phinex.ch Phinex Informatik AG Thomas Aeby Kirchweg 52 1735 Giffers ------------------------------------------------ This completely rewritten version of Doxygen::PerlFilter =head1 CONTACT Please email comments or questions to the public Ensembl developers list at . Questions may also be sent to the Ensembl help desk at . =cut =head1 NAME EnsEMBL::PerlFilter =head1 DESCRIPTION Implementation of EnsEMBL::PerlFilter. Derived from http://www.bigsister.ch/doxygenfilter by Bart Schuller and Thomas Aeby Original distributed under Perl artistic license, see: http://www.bigsister.ch/doxygenfilter/license.html Adaptations by Kieron Taylor (ktaylor@ebi.ac.uk), 2011 State Machine rewrite of existing filter. Was going to use DFA::Command to handle the logic, but actually it won't work well for parsing Perl, hence own simplified state machine. Intentionally not using PPI package to parse Perl, too complex. This is a 80/20 EnsEMBL specific POD->Doxygen converter, however it should work somewhat with other code. =cut package EnsEMBL::PerlFilter; use warnings; use strict; use base qw(EnsEMBL::Filter); # Possible states use constant { NORMAL => 0, INHERIT => 1, PODTOP => 2, PODSECTION => 3, PODMETHOD => 4, SEEALSO => 5, TERMINAL => 6, #would have been END, but that's a reserved word CODE => 7, }; # State-determining functions my @parse = ( \&normal_parser, \&inheritance_parser, \&pod_top_parser, \&pod_section_parser, \&pod_method_parser, \&see_also_parser, \&finish, \&code_parser ); # State-reactive functions my @act = ( \&normal_action, \&inheritance_action, \&pod_top_action, \&pod_section_action, \&pod_method_action, \&see_also_action, \&finish, \&code_action ); my @buffer; my $state; # state of state machine, see? my @big_buffer; # to absorb everything we want to print right up until we know whom we inherit from. my $class_declaration; my @inheritance; my @leading_text; my $method_description; my $previous_doc_header; my $brackets =0; my $id = __PACKAGE__; sub filter { my($self, $infile) = @_; open(my $infh, $infile); my $current_class = ""; $state = NORMAL; my $line; # Read file, using lookup table to run correct parser on each line. # Parsing is done in two stages: # 1) Parsing based on the previous mode. The methods are called "_parse" # 2) Parsing after any mode switch in stage 1. The methods are called "_action" # Strictly speaking actions are also performed in "parse" code, but that's what was needed in the end. while( defined($line = <$infh>) && $state != TERMINAL) { my $sub_ref = $parse[$state]; $parse[$state]->([$self,$line]); $act[$state]->([$self,$line]); } # Create the filtered file: # beware, #include declarations are coming from elsewhere ( inheritance_action() ). my @namespaces; my $class_name; if (defined($class_declaration)) { @namespaces = split(/::/,$class_declaration); $class_name = pop @namespaces; foreach (@namespaces) { $self->print("namespace ".$_." {\n"); } $self->more(@leading_text); $self->print("class ".$class_name); } else { $self->print("# No class definition in this file."); warn "No package line found in $infile\n"; } if (scalar @inheritance > 0) { my $string = shift @inheritance; $self->print(" : public ".$string); foreach my $parent (@inheritance) { $self->print(", public ".$parent); } } $self->print(" {\n"); $self->print("public: \n"); $self->more(@big_buffer); $self->print("};\n"); foreach (@namespaces) { print("}\n"); } } sub normal_parser { my $args = $_[0]; my $self = $args->[0]; my $line = $args->[1]; chomp($line); if ($line =~ /^=head1/) { $state = PODTOP; } elsif ($line =~/^=head2/) { # head2 usually signifies a doc-block just before a method $state = PODMETHOD; } elsif ($line =~/^1;/) { $state = TERMINAL; warn "Reached end of code: 1;\n"; } elsif ($line =~ /^\s*package\s+(.*);/) { $class_declaration = $1; } elsif ($line =~/^\s*use\s/ || $line =~/^(our|my)?\s*\@ISA/ || $line =~(/^\s*.*::ISA/) ){ $state = INHERIT; } } sub normal_action { my $args = $_[0]; my $self = $args->[0]; my $line = $args->[1]; # for catching undocumented subroutines and adding code blocks to documentation if ($line =~ /^\s*sub\s+([\w:]+)/) { $state = CODE; my $method_name = $1; if ($line =~ /{/) {$brackets = 1;} else {$brackets = 0;} #warn "Previous: $previous_doc_header. Present: $method_name\n"; if (defined($previous_doc_header) && $previous_doc_header =~ /$method_name/){ # We've found the corresponding sub to go with the documentation. $previous_doc_header = ""; } else { # Create an undocumented entry my $scope = "public"; if ($method_name =~ /^_/) {$scope = "protected";} push @big_buffer,"/** \@fn $scope $method_name( ) \n Undocumented method\n\n"; $method_description = $scope." ".$method_name; warn "Found undocumented method $method_name\n"; } my $html_lump = "
\@htmlonly Code:
click to view