BASIS  version 1.2.3 (revision 2104)
doxyfilter-bash.py
00001 ##############################################################################
00002 # @file  doxyfilter-bash.py
00003 # @brief Doxygen filter for BASH scripts.
00004 #
00005 # Copyright (c) 2011 University of Pennsylvania. All rights reserved.<br />
00006 # See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file.
00007 #
00008 # Contact: SBIA Group <sbia-software at uphs.upenn.edu>
00009 #
00010 # @ingroup Tools
00011 ##############################################################################
00012 
00013 import sys
00014 import re
00015 
00016 if __name__ == "__main__":
00017     # parse arguments
00018     if len (sys.argv) != 2:
00019         sys.stderr.write ("No file specified to process!\n")
00020         sys.exit (1)
00021     fileName = sys.argv [1]
00022     # open input file
00023     f = open (fileName, 'r')
00024     if not f:
00025         sys.stderr.write ("Failed to open file " + fileName + " for reading!\n")
00026         sys.exit (1)
00027     # compile regular expressions
00028     reShaBang       = re.compile (r"#!\s*/usr/bin/env\s+bash$|#!\s*/bin/bash$")
00029     reInclude       = re.compile (r"source\s+[\"']?(?P<module>[^#|&]+)[\"']?(\s*#.*)?$")
00030     reFunctionStart = re.compile (r"function\s*(?P<name1>\w+)\s*{?|(?P<name2>\w+)\s*\(\s*\)\s*{?\s*$")
00031     reFunctionEnd   = re.compile (r"}$")
00032     reCommentStart  = re.compile (r"##+(?P<comment>.*)$")
00033     reCommentLine   = re.compile (r"#+(?P<comment>.*)$")
00034     reParamDoc      = re.compile (r"[\@\\]param\s*(\[\s*(in|out|in\s*,\s*out|out\s*,\s*in)\s*\]|\s*)\s+(?P<param>\w+)")
00035     reIfClauseStart = re.compile (r"if\s*\[")
00036     reIfClauseEnd   = re.compile (r"else[\s$]|elif\s*\[|;?fi$")
00037 
00038     # parse line-by-line and output pseudo C++ code to stdout
00039     ifClauseDepth = 0     # current depth of if-clauses
00040     commentDepth  = 0     # if-clause depth where comment was encountered
00041     previousBlock = ''    # name of previous code block
00042     currentBlock  = ''    # name of current code block
00043     params        = []
00044     for line in f:
00045         line = line.strip ()
00046         # skip sha-bang directive
00047         if reShaBang.match (line) is not None:
00048             sys.stdout.write ("\n")
00049             continue
00050         # next comment line,
00051         if currentBlock == 'comment':
00052             m = reCommentLine.match (line)
00053             if m is not None:
00054                 comment = m.group ('comment')
00055                 sys.stdout.write ("///" + comment + "\n")
00056                 m = reParamDoc.search (line)
00057                 if m is not None:
00058                     param = m.group ('param')
00059                     params.append (param)
00060                 continue
00061             else:
00062                 previousBlock = currentBlock
00063                 currentBlock = ''
00064                 # continue processing of this (yet unhandled) line
00065         # inside function definition
00066         if currentBlock == 'function':
00067             m = reFunctionEnd.match (line)
00068             if m is not None:
00069                 previousBlock = currentBlock
00070                 currentBlock = ''
00071             sys.stdout.write ("\n")
00072             continue
00073         # look for new comment block or block following a comment
00074         if currentBlock == '':
00075             # include
00076             m = reInclude.match (line)
00077             if m is not None:
00078                 module = m.group ('module')
00079                 module = module.replace ("\"", "")
00080                 module = module.replace ("/./", "/")
00081                 module = module.replace ("${_BASIS_DIR}/", "")
00082                 module = module.replace ("$(get_executable_directory)/", "")
00083                 module = module.replace ("$exec_dir/", "")
00084                 module = module.replace ("${exec_dir}/", "")
00085                 sys.stdout.write ("#include \"" + module + "\"\n")
00086                 continue
00087             # enter if-clause
00088             m = reIfClauseStart.match (line)
00089             if m is not None:
00090                 ifClauseDepth = ifClauseDepth + 1
00091                 sys.stdout.write ("\n")
00092                 continue
00093             # leave if-clause
00094             if ifClauseDepth > 0:
00095                 m = reIfClauseEnd.match (line)
00096                 if m is not None:
00097                     ifClauseDepth = ifClauseDepth - 1
00098                     if commentDepth > ifClauseDepth:
00099                         previousBlock = ''
00100                         currentBlock  = ''
00101                     sys.stdout.write ("\n")
00102                     continue
00103             # Doxygen comment
00104             m = reCommentStart.match (line)
00105             if m is not None:
00106                 comment = m.group ('comment')
00107                 sys.stdout.write ("///" + comment + "\n")
00108                 currentBlock = 'comment'
00109                 commentDepth = ifClauseDepth
00110                 m = reParamDoc.search (line)
00111                 if m is not None:
00112                     param = m.group ('param')
00113                     params.append (param)
00114                 continue
00115             # if previous block was a Doxygen comment process
00116             # supported following blocks such as variable setting
00117             # and function definition optionally only the one
00118             # inside the if-case of an if-else-clause
00119             if previousBlock == 'comment':
00120                 # function
00121                 m = reFunctionStart.match (line)
00122                 if m is not None:
00123                     name = m.group ('name1')
00124                     if not name:
00125                         name = m.group ('name2')
00126                     sys.stdout.write ("function " + name + " (")
00127                     for i in range (0, len (params)):
00128                         if i > 0:
00129                             sys.stdout.write (", ")
00130                         sys.stdout.write ("in " + params [i])
00131                     sys.stdout.write (");\n")
00132                     currentBlock = 'function'
00133                     params = []
00134                     continue
00135             # unhandled lines...
00136             if line != '':
00137                 if previousBlock == 'comment':
00138                     # prevent comments that are not associated with any
00139                     # processed block to be merged with subsequent comments
00140                     sys.stdout.write ("class COMMENT_DUMPED_BY_DOXYGEN_FILTER;\n")
00141                 else:
00142                     sys.stdout.write ("\n")
00143                 previousBlock = ''
00144             else:
00145                 sys.stdout.write ("\n")
00146         else:
00147             sys.stdout.write ("\n")
00148     # close input file
00149     f.close ()
00150     # done
00151     sys.exit (0)