2014-06-16 15:11:57 +00:00
#!/usr/bin/env perl
2014-12-15 13:11:50 +00:00
= head1 NAME
fixcomments . pl
= head1 SYNOPSIS
2019-09-25 17:57:29 +02:00
fixcomments . pl [ options ] [ infile ]
2014-12-15 13:11:50 +00:00
= head1 OPTIONS
= over 4
= item B <infile>
2019-09-25 17:57:29 +02:00
Input file to process ( uses stdin if not specified )
2014-12-15 13:11:50 +00:00
2019-09-25 17:57:29 +02:00
= item B <--help|-h>
2014-12-15 13:11:50 +00:00
2019-09-25 17:57:29 +02:00
A little help
2014-12-15 13:11:50 +00:00
2019-09-25 17:57:29 +02:00
= item B <--verbose|-v>
2014-12-15 13:11:50 +00:00
2019-09-25 17:57:29 +02:00
Output debug messages ( to stderr ) , repeat for even more output
2014-12-15 13:11:50 +00:00
= back
= head1 DESCRIPTION
This script will read a Fortran file and attempt to add doxify comments
to both FUNCTIONs and SUBROUTINEs .
= cut
use strict ;
use warnings ;
2020-10-19 10:30:35 +02:00
use open qw( :std :utf8 ) ;
2014-12-15 13:11:50 +00:00
use FindBin ;
use lib "$FindBin::RealBin/lib" ;
use Pod::Usage qw( pod2usage ) ;
2019-09-25 17:57:29 +02:00
use Getopt::Long ;
2014-12-15 13:11:50 +00:00
2019-09-25 17:57:29 +02:00
my $ verbose = 0 ;
my $ help = 0 ;
2014-12-15 13:11:50 +00:00
2019-09-25 17:57:29 +02:00
GetOptions (
'verbose+' = > \ $ verbose ,
'help|?' = > \ $ help ) or pod2usage ( 2 ) ;
pod2usage ( 1 ) if $ help ;
2014-12-15 13:11:50 +00:00
2019-09-25 17:57:29 +02:00
sub print_debug {
print ( STDERR "DEBUG: @_\n" ) if ( $ verbose > 0 ) ;
2015-06-24 15:57:29 +00:00
}
2014-12-15 13:11:50 +00:00
# Regular expressions for matching
my $ DOXYGEN_HEADER = "^!>" ;
# The empty string
2019-09-25 17:57:29 +02:00
my $ EMPTY = q{ } ;
2014-12-15 13:11:50 +00:00
# Toggle variables to keep track of which doxygen item is being processed
# in the current header.
my ( $ hasParam ,
$ hasBrief ,
$ hasPar ,
$ hasDate ,
$ hasVersion ,
$ hasAuthor ,
$ hasNote ,
$ hasRetVal ,
2018-03-13 17:10:34 +00:00
$ hasReturn ,
2014-12-15 13:11:50 +00:00
$ hasRandom ,
$ hasRemainder ) ;
# Variables with s at the end contains the actual text read in from
# existing doxygen header
my ( % params ,
$ briefs ,
$ dates ,
$ versions ,
$ pars ,
$ authors ,
$ notes ,
$ retVals ,
2018-03-13 17:10:34 +00:00
$ returns ,
2014-12-15 13:11:50 +00:00
$ randoms ,
$ remainders ) ;
# If currently in INTERFACE block
my $ insideInterface = 0 ;
# Whether the SUBROUTINE/FUNCTION definition contains an & line continuation
my $ hasAmpersand = 0 ;
# Whether when processing subroutine definition we are in brackets
my $ inParentheses = 0 ;
# Whether the procedure definition contains the RETURN value
2018-03-13 17:10:34 +00:00
my $ hasReturnAsArg = 0 ;
2014-12-15 13:11:50 +00:00
# Keeps track of which parameters in the current procedure have
# a matching doxygen header
my % matched ;
# Buffer for lines
my $ buffer = $ EMPTY ;
# The old doxygen header as read from file
my $ oldheader = $ EMPTY ;
# Any unprocessed lines
my $ leftoverlines = $ EMPTY ;
# The name of the procedure being processed
my $ procedureName = $ EMPTY ;
# Whether the procedure is a function
my $ isFunction ;
initVariables ( ) ;
# While there are still lines to read in our INPUT file
2019-09-25 17:57:29 +02:00
while ( < > ) {
2014-12-15 13:11:50 +00:00
# Get the line we've just read in
my $ currline = $ _ ;
# If an existing doxygen header is encountered then process
if ( matchDoxygenHeaderDefinition ( $ currline ) ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Matched doxygen header" ) ;
2014-12-15 13:11:50 +00:00
$ currline = processDoxygenHeader ( $ currline ) ;
}
# Are we inside an interface block?
# If so we don't want to add any comments here
if ( $ currline =~ m/^\s*INTERFACE\s*\n/xms ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Start of INTERFACE encountered" ) ;
2014-12-15 13:11:50 +00:00
$ insideInterface = 1 ;
}
if ( $ currline =~ m/^\s*END\s+INTERFACE\s*\n/xms ) {
2015-06-24 15:57:29 +00:00
print_debug ( "End of INTERFACE encountered" ) ;
2014-12-15 13:11:50 +00:00
$ insideInterface = 0 ;
}
# Look for procedure (SUBROUTINE/FUNCTION definitions)
2020-03-12 22:05:25 +09:00
# These can be the initial definition line, or a continuation line
2014-12-15 13:11:50 +00:00
# We don't add comments to code inside an interface block
if ( ( matchSubroutineDefinition ( $ currline ) || $ hasAmpersand )
&& ! ( $ insideInterface ) ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Matched subroutine definition" ) ;
2014-12-15 13:11:50 +00:00
processSubroutineDefinition ( $ currline ) ;
} else {
2015-06-24 15:57:29 +00:00
print_debug ( "Subroutine definition not matched" ) ;
2014-12-15 13:11:50 +00:00
if ( $ oldheader eq $ EMPTY ) {
# No header remaining so just print out line as read
2015-06-24 15:57:29 +00:00
print_debug ( "Empty old header, writing out line:" ) ;
print_debug ( $ currline ) ;
2019-09-25 17:57:29 +02:00
print $ currline ;
2014-12-15 13:11:50 +00:00
} else {
# Header has been processed, need to do something with remaining lines
2015-06-24 15:57:29 +00:00
print_debug ( "Non-empty old header for line:" ) ;
print_debug ( $ currline ) ;
2014-12-15 13:11:50 +00:00
if ( ( $ currline =~ m/^\s*$/xms ) ||
( $ currline =~ m/\#if/xms ) ) {
# Blank line or #if line, so store for printing later
2015-06-24 15:57:29 +00:00
print_debug ( "Empty line or #if, storing for later" ) ;
2014-12-15 13:11:50 +00:00
$ leftoverlines = $ leftoverlines . $ currline ;
} elsif ( $ currline =~ m/^\s*!\s*/xms ) {
# Have a comment so add to header
2015-06-24 15:57:29 +00:00
print_debug ( "Inline comment, adding to header" ) ;
2014-12-15 13:11:50 +00:00
$ oldheader = $ oldheader . $ currline ;
} else {
# If it was a MODULE or TYPE header we still need to write
# it back out to file.
2015-06-24 15:57:29 +00:00
print_debug ( "Writing existing non FUNCTION/SUBROUTINE header to file" ) ;
2019-09-25 17:57:29 +02:00
print $ oldheader ;
print $ leftoverlines ;
print $ currline ;
2014-12-15 13:11:50 +00:00
# Reset variables after output
initVariables ( ) ;
2014-06-16 15:11:57 +00:00
}
2014-12-15 13:11:50 +00:00
}
} # End of the if (($currline is SUBROUTINE or FUNCTION ) block
} # End of main while loop
# Perhaps do a second pass to remove any double occurrences of !> *** type lines
sub initToggles {
$ hasParam = 0 ;
$ hasBrief = 0 ;
$ hasPar = 0 ;
$ hasDate = 0 ;
$ hasVersion = 0 ;
$ hasAuthor = 0 ;
$ hasNote = 0 ;
$ hasRetVal = 0 ;
$ hasRandom = 0 ;
$ hasRemainder = 0 ;
return ;
}
sub initVariables {
initToggles ( ) ;
undef % params ;
undef % matched ;
$ briefs = $ EMPTY ;
$ dates = $ EMPTY ;
$ versions = $ EMPTY ;
$ pars = $ EMPTY ;
$ authors = $ EMPTY ;
$ notes = $ EMPTY ;
$ retVals = $ EMPTY ;
2018-03-13 17:10:34 +00:00
$ returns = $ EMPTY ;
2014-12-15 13:11:50 +00:00
$ randoms = $ EMPTY ;
$ remainders = $ EMPTY ;
$ buffer = $ EMPTY ;
$ oldheader = $ EMPTY ;
$ leftoverlines = $ EMPTY ;
$ procedureName = $ EMPTY ;
$ isFunction = 0 ;
$ inParentheses = 0 ;
return ;
}
# Look for a doxygen header definition in an input line.
sub matchDoxygenHeaderDefinition {
my ( $ lineToProcess ) = @ _ ;
2015-06-24 15:57:29 +00:00
print_debug ( "Trying to match doxygen header $DOXYGEN_HEADER against line" ) ;
print_debug ( $ lineToProcess ) ;
2014-12-15 13:11:50 +00:00
my $ match = 0 ;
if ( $ lineToProcess =~ m/$DOXYGEN_HEADER/xms ) {
$ match = 1 ;
}
2015-06-24 15:57:29 +00:00
print_debug ( "Doxygen header match value: $match" ) ;
2014-12-15 13:11:50 +00:00
return $ match ;
}
# Look for SUBROUTINE or FUNCTION definitions.
sub matchSubroutineDefinition {
my ( $ lineToProcess ) = @ _ ;
2015-06-24 15:57:29 +00:00
print_debug ( "Trying to match subroutine definition against line:" ) ;
print_debug ( $ lineToProcess ) ;
2014-12-15 13:11:50 +00:00
# Immediately discount lines with comments at the start
my $ commentPattern = '^\!' ;
if ( $ lineToProcess =~ m/$commentPattern/xms ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Matched comment" ) ;
2014-12-15 13:11:50 +00:00
return 0 ;
}
# Assume that lines contain SUBROUTINE or FUNCTION followed by space
# and then the name of the procedure which may have a space before the (
my $ patternProcWithBrackets = '(SUBROUTINE|FUNCTION)' # Subroutine or function
. '\s+' # followed by one or more whitespace characters
. '(\w|\[|\])+' # followed by one or more of (word or [ or ])
. '\s*' # followed by zero or more whitespace characters
. '\(' ; # followed by open bracket.
# Need to allow for SUBROUTINE without any arguments or brackets too
my $ patternSubNoBracketsAtStartOfLine = '^\s*' # Start with zero or more whitespace
. 'SUBROUTINE' # then subroutine
. '\s*' # then zero or more whitespace
. '(\w|\[|\])+' # then one or more of (word or [ or ])
. '\s*.*' ; # then zero or more whitespace
# We also protect against adding comments to commented out SUBROUTINE/FUNCTION calls
my $ patternCommentedOut = '!\s*(SUBROUTINE|FUNCTION)' ;
# Protect against definitions inside quotes
my $ patternInQuotes = '"\s*(SUBROUTINE|FUNCTION)' ;
my $ match1 = 0 ;
my $ match2 = 0 ;
my $ match3 = 0 ;
my $ match4 = 0 ;
if ( $ lineToProcess =~ m/$patternProcWithBrackets/xms ) {
$ match1 = 1 ;
2014-06-16 15:11:57 +00:00
}
2014-12-15 13:11:50 +00:00
if ( $ lineToProcess =~ m/$patternSubNoBracketsAtStartOfLine/xms ) {
$ match2 = 1 ;
}
if ( $ lineToProcess =~ m/$patternCommentedOut/xms ) {
$ match3 = 1 ;
}
if ( $ lineToProcess =~ m/$patternInQuotes/xms ) {
$ match4 = 1 ;
}
my $ match = ( ( $ match1 || $ match2 ) && ! ( $ match3 ) && ! ( $ match4 ) ) ;
2015-06-24 15:57:29 +00:00
print_debug ( "Subroutine definition match value: $match" ) ;
2014-12-15 13:11:50 +00:00
return $ match
}
sub processDoxygenHeader {
my ( $ currline ) = @ _ ;
2015-06-24 15:57:29 +00:00
print_debug ( "Processing Doxygen Header" ) ;
2014-06-16 15:11:57 +00:00
2014-12-15 13:11:50 +00:00
# Each time we find a header we need to reset toggles and their data
initVariables ( ) ;
my $ paramName = $ EMPTY ;
# Start of do-while over match on ($currline =~ m/^\s*!/i)
do {
2015-06-24 15:57:29 +00:00
print_debug ( "Processing header line:" ) ;
print_debug ( $ currline ) ;
2014-12-15 13:11:50 +00:00
# Keep the headers safe, may need them! We use the oldheader variable to
# keep the complete headers for MODULE and TYPE intact such that we
# can just dump them straight back out without making any changes.
$ oldheader = $ oldheader . $ currline ;
# Pick up parameters - matches the word param separated by spaces
if ( ( $ currline =~ m/!>\s\\param\s+(\S+)\s*/xms ) ||
( $ currline =~ m/!>\s\\param\[.*\]\s+(\S+)\s*/xms ) ) {
# Get the param name
$ paramName = $ 1 ;
2015-06-24 15:57:29 +00:00
print_debug ( "Got header for parameter $paramName" ) ;
2014-12-15 13:11:50 +00:00
# Cover the case where two arguments have the same name
if ( exists $ params { $ paramName } ) {
$ paramName = $ paramName . "new" ;
}
# Store the param into a hash indexed by its name
$ params { $ paramName } = $ currline ;
# Set matched for this param to zero as we don't
# yet have a match in the argument list
$ matched { $ paramName } = 0 ;
initToggles ( ) ;
$ hasParam = 1 ;
} elsif ( $ currline =~ m/!>\s\\brief\s*/xms ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Got briefs header" ) ;
2014-12-15 13:11:50 +00:00
$ briefs = $ briefs . $ currline ;
initToggles ( ) ;
$ hasBrief = 1 ;
} elsif ( $ currline =~ m/!>\s\\date*/xms ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Got dates header" ) ;
2014-12-15 13:11:50 +00:00
$ dates = $ dates . $ currline ;
initToggles ( ) ;
$ hasDate = 1 ;
} elsif ( $ currline =~ m/!>\s\\version\s*/xms ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Got version header" ) ;
2014-12-15 13:11:50 +00:00
$ versions = $ versions . $ currline ;
initToggles ( ) ;
$ hasVersion = 1 ;
} elsif ( $ currline =~ m/!>\s\\par\s*/xms ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Got par header" ) ;
2014-12-15 13:11:50 +00:00
$ pars = $ pars . $ currline ;
initToggles ( ) ;
$ hasPar = 1 ;
} elsif ( $ currline =~ m/!>\s\\author\s*/xms ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Got author header" ) ;
2014-12-15 13:11:50 +00:00
$ authors = $ authors . $ currline ;
initToggles ( ) ;
$ hasAuthor = 1 ;
} elsif ( $ currline =~ m/!>\s\\note\s*/xms ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Got note header" ) ;
2014-12-15 13:11:50 +00:00
$ notes = $ notes . $ currline ;
initToggles ( ) ;
$ hasNote = 1 ;
} elsif ( $ currline =~ m/!>\s\\retval\s*/xms ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Got retval header" ) ;
2014-12-15 13:11:50 +00:00
$ retVals = $ retVals . $ currline ;
initToggles ( ) ;
$ hasRetVal = 1 ;
2018-03-13 17:10:34 +00:00
} elsif ( $ currline =~ m/!>\s\\returns\s*/xms ) {
print_debug ( "Got return header" ) ;
$ returns = $ returns . $ currline ;
initToggles ( ) ;
$ hasReturn = 1 ;
} elsif ( $ currline =~ m/!>\s\\return\s*/xms ) {
print_debug ( "Got return header" ) ;
$ returns = $ returns . $ currline ;
initToggles ( ) ;
$ hasReturn = 1 ;
2014-12-15 13:11:50 +00:00
} elsif ( $ currline =~ m/!>\s\\\S+/xms ) {
# Randoms contains anything else that looks
# like a DOXYGEN header. with a \whatever
# Must check to see if line has already been commented.
# We also avoid commenting a blank \param line
2015-06-24 15:57:29 +00:00
print_debug ( "Got random header" ) ;
2014-12-15 13:11:50 +00:00
if ( ( $ currline !~ m/UNKNOWN_DOXYGEN_COMMENT/xms ) &&
( $ currline !~ m/UNKNOWN_COMMENT/xms ) &&
( $ currline !~ m/!>\s*\\param\s*\n/xms ) ) {
$ randoms = $ randoms . $ currline ;
chomp ( $ randoms ) ;
# Add on text for output
$ randoms = $ randoms . " UNKNOWN_DOXYGEN_COMMENT\n" ;
2014-06-16 15:11:57 +00:00
} else {
2014-12-15 13:11:50 +00:00
# Otherwise just add to randoms
$ randoms = $ randoms . $ currline ;
2014-06-16 15:11:57 +00:00
}
2014-12-15 13:11:50 +00:00
initToggles ( ) ;
$ hasRandom = 1 ;
} elsif ( $ currline =~ m/^!>\s*/xms ) {
# Handle multi line entries. Append what you find onto the
# previous one read in until you get to another \
# The \brief, \param, \par, \author, random and remainder
# entries can all be multi-line.
# The \version and \date entries should be single line and
# thus we don't have an elsif for them.
2015-06-24 15:57:29 +00:00
print_debug ( "Got line continuation" ) ;
2014-12-15 13:11:50 +00:00
if ( $ hasParam ) {
$ params { $ paramName } = $ params { $ paramName } . $ currline ;
} elsif ( $ hasBrief ) {
$ briefs = $ briefs . $ currline ;
} elsif ( $ hasPar ) {
$ pars = $ pars . $ currline ;
} elsif ( $ hasAuthor ) {
$ authors = $ authors . $ currline ;
} elsif ( $ hasNote ) {
$ notes = $ notes . $ currline ;
} elsif ( $ hasRetVal ) {
$ retVals = $ retVals . $ currline ;
2018-03-13 17:10:34 +00:00
} elsif ( $ hasReturn ) {
$ returns = $ returns . $ currline ;
2014-12-15 13:11:50 +00:00
} elsif ( $ hasRandom ) {
# Must check to see if line has already been commented
if ( ( $ currline !~ m/UNKNOWN_DOXYGEN_COMMENT/xms ) &&
( $ currline !~ m/UNKNOWN_COMMENT/xms ) ) {
$ randoms = $ randoms . $ currline ;
chomp ( $ randoms ) ;
# Add on text for output
$ randoms = $ randoms . " UNKNOWN_DOXYGEN_COMMENT\n" ;
} else {
$ randoms = $ randoms . $ currline ;
}
2014-06-16 15:11:57 +00:00
} else {
2014-12-15 13:11:50 +00:00
# Get any header lines beginning with "!> some text but
# no \ thus not in DOXYGEN format
$ hasRemainder = 1 ;
# Must check to see if the line has already been
# commented and also that it's not empty
if ( ( $ currline !~ m/UNKNOWN_COMMENT/xms ) &&
( $ currline !~ m/!>\s*\n/xms ) ) {
# Add on text for output
$ currline =~ s/!>/!> \\note UNKNOWN_COMMENT /gx ;
$ remainders = $ remainders . $ currline ;
} else {
$ remainders = $ remainders . $ currline ;
}
2014-06-16 15:11:57 +00:00
}
2014-12-15 13:11:50 +00:00
} elsif ( $ currline !~ m/^!\s*\*/xms ) {
# Any other header line that's not "***..." or a Doxygen
# comment, ie "! some comment or other"
2015-06-24 15:57:29 +00:00
print_debug ( "Got non-doxygen line" ) ;
2014-12-15 13:11:50 +00:00
if ( $ hasBrief ) {
# Put comment in brief, replacing ! with !>
$ currline =~ s/!/!>/xms ;
$ briefs = $ briefs . $ currline ;
2014-06-16 15:11:57 +00:00
} else {
2014-12-15 13:11:50 +00:00
$ currline =~ s/^\s+// ;
# Must check to see if line has already been commented
if ( $ currline !~ m/UNKNOWN_COMMENT/xms ) {
# Add on text for output, changing ! for !>
$ currline =~ s/!/!> \\note UNKNOWN_COMMENT /gx ;
$ remainders = $ remainders . $ currline ;
} else {
$ remainders = $ remainders . $ currline ;
}
2014-06-16 15:11:57 +00:00
}
}
2014-12-15 13:11:50 +00:00
# Get the next line in the header block
2019-09-25 17:57:29 +02:00
$ currline = < > ;
2014-12-15 13:11:50 +00:00
} while ( $ currline =~ m/^\s*!/xms ) ; # Rule as to when you have finished a header. Currently Anything beginning with !
return $ currline ;
} # End of header processing subroutine
sub processSubroutineDefinition {
my ( $ currline ) = @ _ ;
2015-06-24 15:57:29 +00:00
print_debug ( "Processing Subroutine line:" ) ;
print_debug ( $ currline ) ;
2014-12-15 13:11:50 +00:00
# functionLine will contain the procedure definition with any
# unrequired text stripped off, $currline will still contain the actual code
my $ functionLine = $ EMPTY ;
if ( ! ( $ hasAmpersand ) ) {
2020-03-12 22:05:25 +09:00
# Remove anything preceding the SUBROUTINE or FUNCTION definition
2014-12-15 13:11:50 +00:00
# e.g. RECURSIVE, REAL, INTEGER, (KIND=dp), ELEMENTAL etc etc.
$ currline =~ /((\bFUNCTION\b|\bSUBROUTINE\b).+)/x ;
# $1 contains whatever remains after removing anything before the
# word SUBROUTINE or FUNCTION
$ functionLine = $ 1 ;
2014-06-16 15:11:57 +00:00
} else {
2014-12-15 13:11:50 +00:00
$ functionLine = $ currline ;
}
# Remove BIND(*) from the definition
my $ tmp = $ functionLine ;
$ tmp =~ s/\bBIND\b\(\w+\W*\w*\W*\w*\W*\)//x ;
$ functionLine = $ tmp ;
# Strip the newline char from the end
chomp ( $ functionLine ) ;
$ hasAmpersand = 0 ;
2018-03-13 17:10:34 +00:00
$ hasReturnAsArg = 0 ;
2014-12-15 13:11:50 +00:00
my $ lelement = $ EMPTY ;
# Split the subroutine or function definition by space or comma
my @ string = split ( /([,\(\)\s+]+)/x , $ functionLine ) ;
2019-09-30 17:35:22 +02:00
2026-05-10 01:34:44 +02:00
for ( my $ idx = 0 ; $ idx < scalar ( @ string ) ; $ idx + + ) {
my $ p = $ string [ $ idx ] ;
2019-09-30 17:35:22 +02:00
defined $ p or next ; # continue with the next item if $p is undefined
2014-12-15 13:11:50 +00:00
$ p =~ s/^\s+|\s+$//gx ;
2019-09-30 17:35:22 +02:00
$ p ne $ EMPTY or next ; # continue with the next item if empty
$ p ne "," or next ; # or simply the ','
2015-06-24 15:57:29 +00:00
print_debug ( "Processing item $p" ) ;
2014-12-15 13:11:50 +00:00
if ( $ p eq "&" ) {
2019-09-30 17:35:22 +02:00
# If we encounter an & as the last element in this line it starts a line continuation,
# otherwise it is the continuation point of a previous one and we can ignore it
$ idx == $# string or next ;
2015-06-24 15:57:29 +00:00
print_debug ( "Got & line continuation" ) ;
2014-12-15 13:11:50 +00:00
$ hasAmpersand = 1 ;
# Buffer the line details as we can't print out yet
$ buffer = "$buffer$currline" ;
} elsif ( $ p eq "(" ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Entered parentheses" ) ;
2014-12-15 13:11:50 +00:00
$ inParentheses = 1 ;
} elsif ( $ p eq ")" ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Left parentheses" ) ;
2014-12-15 13:11:50 +00:00
$ inParentheses = 0 ;
} elsif ( $ p eq "," ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Comma" ) ;
2014-07-23 09:42:53 +00:00
} else {
2014-12-15 13:11:50 +00:00
if ( $ inParentheses ) {
2015-06-24 15:57:29 +00:00
print_debug ( "In parentheses, parameter: $p, previous parameter: $lelement" ) ;
2014-12-15 13:11:50 +00:00
# Must have either a parameter of a function return value
2018-03-13 17:10:34 +00:00
if ( ( $ lelement =~ m/RESULT/ixms ) && ( $ hasReturnAsArg ) ) {
2015-06-24 15:57:29 +00:00
print_debug ( "Got return parameter $p" ) ;
2018-03-13 17:10:34 +00:00
if ( $ returns eq $ EMPTY ) {
2014-12-15 13:11:50 +00:00
# If the previous element was RESULT outside of
# parantheses then this element must be whatever
# gets returned by the procedure.
2018-03-13 17:10:34 +00:00
# if no return data is available print out the ...
2014-12-15 13:11:50 +00:00
# to header
# Only stored for now so it is always printed
# in the same place (after the \params)
2018-03-13 17:10:34 +00:00
$ returns = "!> \\return ...\n" ;
2014-12-15 13:11:50 +00:00
}
} else {
# Must be parameter
# Update the matched hash table so that all arguments
# in subroutine/function header are set as true
2015-06-24 15:57:29 +00:00
print_debug ( "Processing parameter $p" ) ;
2014-12-15 13:11:50 +00:00
$ matched { $ p } = 1 ;
if ( ! ( exists ( $ params { $ p } ) ) || ! ( defined ( $ params { $ p } ) ) || ( $ params { $ p } eq $ EMPTY ) ) {
# If the entry for this parameter is missing we use
# the standard text for a missing entry
2015-06-24 15:57:29 +00:00
print_debug ( "Missing entry for parameter $p, creating blank" ) ;
2019-09-25 17:57:29 +02:00
print "!> \\param $p ...\n" ;
2014-12-15 13:11:50 +00:00
} else {
2015-06-24 15:57:29 +00:00
print_debug ( "Using existing entry for parameter $p" ) ;
2014-12-15 13:11:50 +00:00
if ( $ params { $ p } !~ m/\\param\s*(\w+|\[.*\]\s+\w+)\s*\n/xms ) {
# Entry must contain some text after the parameter name
2015-06-24 15:57:29 +00:00
print_debug ( "Using entry unchanged" ) ;
2019-09-25 17:57:29 +02:00
print $ params { $ p } ;
2014-12-15 13:11:50 +00:00
} else {
if ( $ params { $ p } =~ m/!>\s*\n$/x ) {
# We need to guard against \param entries which have
# no text but have a blank line "!>" line appended
# Need to split this parameter into it's individual lines
my @ tmpString = split ( /\n/x , $ params { $ p } ) ;
# Find out how many lines we have in this entry
my $ tmpLen = scalar ( @ tmpString ) ;
# Append on the ... to the first line
$ tmpString [ 0 ] = $ tmpString [ 0 ] . " ..." ;
for ( my $ i = 0 ; $ i < $ tmpLen ; $ i + + ) {
# Re-add the carriage return to each line
2019-09-25 17:57:29 +02:00
print "$tmpString[$i]\n" ;
2014-12-15 13:11:50 +00:00
}
} else {
chomp ( $ params { $ p } ) ;
2019-09-25 17:57:29 +02:00
print $ params { $ p } , " ...\n" ;
2014-12-15 13:11:50 +00:00
}
}
}
}
} else {
2015-06-24 15:57:29 +00:00
print_debug ( "Processing element not in parentheses: $p" ) ;
2014-12-15 13:11:50 +00:00
if ( $ lelement =~ m/(SUBROUTINE|FUNCTION)/ixms ) {
# Previous element is FUNCTION or SUBROUTINE so this must be name
$ procedureName = $ p ;
2015-06-24 15:57:29 +00:00
print_debug ( "Got procedure name $procedureName" ) ;
2014-12-15 13:11:50 +00:00
if ( $ lelement =~ m/FUNCTION/ixms ) {
$ isFunction = 1 ;
}
if ( ( $ briefs eq $ EMPTY ) or ( $ briefs eq "!> \\brief\n" ) ) {
# \brief does not exist or is present but empty - add text
2019-09-25 17:57:29 +02:00
print "! **************************************************************************************************\n" ;
print "!> \\brief ...\n" ;
2014-12-15 13:11:50 +00:00
} else {
# \brief exists and contains text
2019-09-25 17:57:29 +02:00
print $ briefs ;
2014-12-15 13:11:50 +00:00
}
} elsif ( $ p =~ m/RESULT/ixms ) {
# Check to see if parameter is RESULT for a FUNCTION
2015-06-24 15:57:29 +00:00
print_debug ( "Need to get function result parameter" ) ;
2018-03-13 17:10:34 +00:00
$ hasReturnAsArg = 1 ;
2014-12-15 13:11:50 +00:00
}
}
$ lelement = $ p ; # Take a note of the array element for comparison, ignoring & and ()
2014-07-23 09:42:53 +00:00
}
2014-12-15 13:11:50 +00:00
} # End of for loop over @string
2014-06-16 15:11:57 +00:00
2014-12-15 13:11:50 +00:00
# Code to loop over all the elements of matched hash table. If any
# remain that were not in the function/subroutine header we want to
# make sure we keep this data in the header. It could be a comment
# or it could refer to old arguments that no longer exist.
# However, we don't want to throw the text away. We only print out
# if the argument doesn't match one of the arguments *and* there is
# no more of the procedure definition left to read, i.e. hasAmpersand is false
foreach my $ paramName ( sort keys % params ) {
# We need to sort the keys otherwise the output order of the hash
# is given in internal order
if ( ! ( $ matched { $ paramName } ) && ! ( $ hasAmpersand ) ) {
if ( $ params { $ paramName } eq '!> \param ' . $ paramName . " ...\n" ) {
# there was no comment, just drop parameter
} elsif ( $ params { $ paramName } !~ m/UNMATCHED_PROCEDURE_ARGUMENT/ ) {
# Must protect against updating an existing comment
# Get rid of \n so UNMATCHED* text can be appended on.
chomp ( $ params { $ paramName } ) ;
2019-09-25 17:57:29 +02:00
print $ params { $ paramName } . " UNMATCHED_PROCEDURE_ARGUMENT: please check \n" ;
2014-12-15 13:11:50 +00:00
} else {
2019-09-25 17:57:29 +02:00
print $ params { $ paramName } ;
2014-12-15 13:11:50 +00:00
}
}
}
2014-06-16 15:11:57 +00:00
2014-12-15 13:11:50 +00:00
# If after looping through the elements there is no ampersand
# we close off the comment and write out the procedure definition
if ( ! ( $ hasAmpersand ) ) {
2018-03-13 17:10:34 +00:00
if ( $ returns ne $ EMPTY ) {
2014-12-15 13:11:50 +00:00
# Print RESULT value first so that it should come straight after the \param definitions
2019-09-25 17:57:29 +02:00
print $ returns ;
2014-12-15 13:11:50 +00:00
} else {
# Get return value from function name
if ( $ isFunction ) {
2019-09-25 17:57:29 +02:00
print "!> \\return ...\n" ;
2014-12-15 13:11:50 +00:00
}
}
2018-03-13 17:10:34 +00:00
if ( $ retVals ne $ EMPTY ) {
# Print return values definitions second so that they come after any \return definitions
2019-09-25 17:57:29 +02:00
print $ retVals ;
2018-03-13 17:10:34 +00:00
}
2014-12-15 13:11:50 +00:00
if ( ( $ dates eq $ EMPTY ) || ( $ dates eq "!> \\date\n" ) ) {
# dates entry empty or exists and contains no text
2019-09-25 17:57:29 +02:00
### print "!> \\date MISSING_COMMENT: Unknown\n"; # Use this line if you want to add text to the entry
print $ dates ;
2014-12-15 13:11:50 +00:00
} else {
2019-09-25 17:57:29 +02:00
print $ dates ;
2014-12-15 13:11:50 +00:00
}
if ( ( $ pars eq $ EMPTY ) || ( $ pars eq "!> \\par History\n" ) ) {
# pars entry empty or exists but contains no text
2019-09-25 17:57:29 +02:00
### print "!> \\par History\n"; # Use this line if you want to add text to the entry
### print "!> MISSING_COMMENT: Unknown\n"; # Use this line if you want to add text to the entry
print $ pars ;
2014-12-15 13:11:50 +00:00
} else {
2019-09-25 17:57:29 +02:00
print $ pars ;
2014-12-15 13:11:50 +00:00
}
if ( ( $ authors eq $ EMPTY ) || ( $ authors eq "!> \\author\n" ) ) {
# authors empty or exists but contains no text
2019-09-25 17:57:29 +02:00
### print "!> \\author MISSING_COMMENT: Unknown\n"; # Use this line if you want to add text to the entry
print $ authors ;
2014-12-15 13:11:50 +00:00
} else {
2019-09-25 17:57:29 +02:00
print $ authors ;
2014-12-15 13:11:50 +00:00
}
if ( $ versions ne $ EMPTY ) {
2019-09-25 17:57:29 +02:00
print $ versions ;
2014-12-15 13:11:50 +00:00
}
if ( $ randoms ne $ EMPTY ) {
2019-09-25 17:57:29 +02:00
print $ randoms ;
2014-12-15 13:11:50 +00:00
}
if ( $ notes ne $ EMPTY ) {
2019-09-25 17:57:29 +02:00
print $ notes ;
2014-12-15 13:11:50 +00:00
}
if ( $ remainders ne $ EMPTY ) {
2020-03-12 22:05:25 +09:00
# Dumps out whatever else remainded in the header (e.g. stuff beginning !> without a \ or stuff beginning with just a !) for the SUBROUTINE/FUNCTION at the end
2019-09-25 17:57:29 +02:00
print $ remainders ;
2014-12-15 13:11:50 +00:00
}
2019-09-25 17:57:29 +02:00
print "! **************************************************************************************************\n" ;
print "$leftoverlines$buffer$currline" ;
2014-12-15 13:11:50 +00:00
# Reset all the variables after writing out the header
initVariables ( ) ;
return ;
}
}