1 # ============================================================================
2 # Copyright (c) 2011-2012 University of Pennsylvania
3 # Copyright (c) 2013-2016 Andreas Schuh
6 # See COPYING file for license information or visit
7 # https://cmake-basis.github.io/download.html#license
8 # ============================================================================
10 ##############################################################################
12 # @brief Base class for Doxygen filter implementations.
14 # @note Not to confuse with the Doxygen::Filter::Perl package available on CPAN.
15 ##############################################################################
21 package BASIS::DoxyFilter;
23 # ============================================================================
25 # ============================================================================
27 use Exporter qw(import);
29 our $VERSION = '1.0.0';
30 our @EXPORT_OK = qw(FROM CONDITION ACTION TO CODE LABELS);
31 our %EXPORT_TAGS = (indices => [qw(FROM CONDITION ACTION TO CODE LABELS)]);
33 # ============================================================================
35 # ============================================================================
37 ## @brief Array indices for transition 4-tuple.
39 FROM => 0, # current state of filter
40 CONDITION => 1, # condition (regex line must match) for transition
41 ACTION => 2, # action to perform upon transition
42 TO => 3 # state to transition to
45 ## @brief Array indices for output lines.
47 CODE => 0, # line of output code
48 LABELS => 1 # array of labels associated with this line
51 # ============================================================================
53 # ============================================================================
55 # ----------------------------------------------------------------------------
56 ## @brief Constructs a Doxygen filter object.
60 my $transitions = shift;
61 my $doxydoc_begin = shift;
62 my $doxydoc_line = shift;
63 my $doxydoc_end = shift;
65 $doxydoc_begin = qr/##+/ unless defined $doxydoc_begin;
66 $doxydoc_line = qr/##*/ unless defined $doxydoc_line;
67 $doxydoc_end = qr/[^#]/ unless defined $doxydoc_end;
68 $transitions = [] unless defined $transitions;
69 # add default transitions for handling of Doxygen comment blocks
70 push @$transitions, ['start', qr/^$doxydoc_begin(.*)$/, \&_doxydoc_begin, 'doxydoc'];
71 push @$transitions, ['doxydoc', qr/^$doxydoc_line(\s*[\@])param\s*(\[\s*in\s*\]|\[\s*out\s*\]|\[\s*in,\s*out\s*\]|\[\s*out,\s*in\s*\])?\s+(\w+)\s+(.*)$/, \&_doxydoc_param, 'doxydoc'];
72 push @$transitions, ['doxydoc', qr/^$doxydoc_line((\s*[\@])returns?\s+.*)$/, \&_doxydoc_returns, 'doxydoc'];
73 push @$transitions, ['doxydoc', qr/^$doxydoc_line(.*)$/, \&_doxydoc_comment, 'doxydoc'];
74 push @$transitions, ['doxydoc', qr/^$doxydoc_end|^$/, \&_doxydoc_end, 'start'];
75 # last transition is handling all none-blank lines
76 push @$transitions, ['start', qr/[^\s]+/, \&_noneblank, 'start'];
77 # initialize object and return it
79 'transitions' => $transitions, # reference to array defining the transitions
80 'output' => [] # generated output lines
84 # ----------------------------------------------------------------------------
85 ## @brief Process input file.
90 my ($line, $next, @match);
92 $self->{'state'} = 'start'; # initial start state of filter
93 $self->{'history'} = ['start']; # linear history of visited states
94 $self->{'reprocess'} = 0; # can be set by actions to request a
95 # reprocessing of the current line after
96 # the state has been changed
97 $self->{'line'} = ''; # current input line
98 $self->{'lineno'} = 0; # current line number of input
99 $self->{'params'} = []; # parameters extracted from comment
101 open FILE, $filename or die "Failed to open file $filename!";
102 while ($self->{'reprocess'} == 1 or $self->{'line'} = <FILE>) {
103 if ($self->{'reprocess'}) {
104 $self->{'reprocess'} = 0;
106 chomp $self->{'line'};
107 $self->{'lineno'} += 1;
109 foreach my $transition (@{$self->{'transitions'}}) {
110 if ($transition->[+FROM] eq $self->{'state'}) {
111 if (@match = ($self->{'line'} =~ /$transition->[+CONDITION]/)) {
112 # Fill-in blank lines until next output line matches
113 # current input line. Otherwise warnings and errors
114 # of Doxygen cannot be easily related to the input source.
115 $self->_append('', 'blank') until @{$self->{'output'}} >= $self->{'lineno'} - 1;
116 # perform action of transition
117 $self->{'transition'} = $transition;
118 $transition->[+ACTION]->($self, @match) if defined $transition->[+ACTION];
119 # keep track of visited states
120 push @{$self->{'history'}}, $self->{'state'}
121 unless $self->{'history'}->[-1] eq $self->{'state'};
122 # transition to next state
123 $self->{'state'} = $transition->[+TO];
132 # ----------------------------------------------------------------------------
133 ## @brief Get filter output.
138 foreach my $line (@{$self->{'output'}}) {
139 $output .= $line->[+CODE] . "\n";
144 # ============================================================================
146 # ============================================================================
148 # ----------------------------------------------------------------------------
149 ## @brief Append line to output.
154 push @{$self->{'output'}}, [$line, [@_]];
157 # ----------------------------------------------------------------------------
158 ## @brief Handle none-blank line.
160 # This action inserts a dummy class definition which is ignored by Doxygen
161 # if the previous block was a Doxygen comment that is not associated with
162 # any following declaration. Otherwise, another transition would have handled
163 # this declaration before.
167 if ($self->{'history'}->[-1] eq 'doxydoc') {
168 $self->_append("class DO_NOT_MERGE_WITH_FOLLOWING_COMMENT;", 'prevent-merge');
173 # ----------------------------------------------------------------------------
174 ## @brief Start of Doxygen comment.
177 my ($self, $comment) = @_;
178 $self->{'params'} = [];
179 $self->{'returndoc'} = 0;
180 $self->{'returndoc'} = 1 if $comment =~ /[\@]returns?\s+/;
181 $self->_doxydoc_comment($comment);
184 # ----------------------------------------------------------------------------
185 ## @brief Doxygen comment line.
188 my ($self, $comment) = @_;
189 $self->_append("///$comment", 'doxydoc');
192 # ----------------------------------------------------------------------------
193 ## @brief Doxygen parameter documentation.
195 # The documentation lines which document function/method/macro parameters
196 # are extracted and the information stored in the filter object. These parameter
197 # documentations can then be used by the particular Doxygen filter to generate
198 # a proper parameter list in case of languages which do by themselves not
199 # explicitly specify the type and name of the function parameters such as in
200 # Perl and Bash, in particular. Moreover, CMake provides the special ARGN
201 # parameter which stores all additional unnamed arguments.
204 my ($self, $prefix, $dir, $name, $comment) = @_;
205 $dir = '' if not defined $dir;
206 $self->_append("///" . $prefix . "param$dir $name $comment", 'doxydoc', 'param');
207 if ($dir =~ /out/ and $dir =~ /in/) { $dir = 'inout'; }
208 elsif ($dir =~ /out/) { $dir = 'out'; }
209 else { $dir = 'in'; }
210 push @{$self->{'params'}}, {'dir' => $dir, 'name' => $name};
213 # ----------------------------------------------------------------------------
214 ## @brief Doxygen return value documentation.
216 # This function simply records in the 'returndoc' member of the filter that
217 # a "@returns" or "\returns" Doxygen is present in the current Doxygen comment.
218 # Some filters such as the one for CMake or Bash, use a pseudo return type
219 # which indicates the type of the function rather than the actual type of
220 # a return value. Often these functions do not return any particular value.
221 # In this case, if the Doxygen comment does not include a documentation for
222 # the pseudo return value, Doxygen will warn. To avoid this warning, a standard
223 # documentation for the pseudo return value may be added by the filter.
226 my ($self, $comment) = @_;
227 $self->{'returndoc'} = 1;
228 $self->_doxydoc_comment($comment);
231 # ----------------------------------------------------------------------------
232 ## @brief End of Doxygen comment.
236 # mark current line that it needs to be reprocessed as this transition
237 # only leaves the current state but another transition may actually apply
238 $self->{'reprocess'} = 1;