# _-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_ # Copyright (c) 2019 the ACME Software Deli. All rights reserved. # # Name: plmPreProc # # Description: # This is a rather simple-minded pre-processor for PL/M-86 source code which # translates the __FILE__ and __LINE__ pre-processor directives introduced in # the C language into a PL/M-86 equivalent. This is EXTREMELY helpful for # debugging and logging # # Revision History: # x001 rmg Fri Jul 21 13:30:03 2019 # initial coding # # x002 rmg Sun Dec 15 13:42:12 2019 # when writing to the output file, write the original line unless there # have been substitutions, then write those instead. That will help to preserve # the indentation # # x003 rmg Sun Jun 12 10:24:53 2022 # put the module ID string immediately after the logger.ext include. This # will remove the ID strings for modules where the logger has been # conditionally removed # _-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_-=^=-_ versionString = "x003" copyrightString = "Copyright (c) 2019, 2022 the ACME Software Deli.\nAll rights reserved" # ensure that the local common modules are visible import __init__ # common, from Python framework import os import sys import platform import argparse import fileinput # common, from the local python library import logger import fileIO # just some variables that more than one piece of the app will need inputFileName = None outputFileName = None # ======================================================================== # name: changeExtension() # # Description: # Using the filename specified, remove the extension and change it to the # newly specified extension. for instance, "navBuilder.py", "ini" would # translate to "navBuilder.ini" # ======================================================================== def changeExtension( rootName, newExtension ): splitName = os.path.splitext( rootName ) if ( ( newExtension == None ) or ( newExtension == "" ) ): returnName = splitName[ 0 ] else: returnName = splitName[ 0 ] + '.' + newExtension return ( returnName ) # end changeExtension() # ======================================================================== # name: simpleName() # # Description: # Using the complex (fully-qualified) filename, return only the basename # without the extension. for instance, "/usr/mgillmore/work/tools/navbuilder.py" # as the input, translates to "navbuilder" # ======================================================================== def simpleName( complexName ): filename = os.path.basename( complexName ) return ( os.path.splitext( filename )[ 0 ] ) # end simpleName() # ======================================================================== # Function: composeUserGreeting() # # Description: # this function queries the OS for its name, version and configuration # that information is combined with the version number and name # of the application along with the copyright statement # ======================================================================== def composeUserGreeting(): uname = platform.uname() returnStr = "\n{} {}({}) - {}, Version {}\n{}\n".format( str( uname.system ), str( uname.release ), str( uname.version ), simpleName( sys.argv[ 0 ] ), versionString, copyrightString ) return ( returnStr ) # end composeUserGreeting() # ======================================================================== # name: addArgDefinitions() # # Description: # this method is a helper, defining the command options # ======================================================================== def addArgDefinitions( parser ): # the argument parser has been created, now add the argument # definitions parser.add_argument( '-v', '--verbose', help="report additional instrumentation to the log", action="store_true", default=False ) parser.add_argument( '-if', '--inputFile', help="the name of the file to be parsed which may contain the macros " + \ "which are be processed", action="store", default=None ) parser.add_argument( '-of', '--outputFile', help="(optional) the name of the file in which the pre-processed output " + \ "will be stored. if no file is specified, the input file will be " + \ "renamed as .ppp, and the output file will contain the name of the " + \ "input file.", action="store", default=None ) # end addArgDefinitions() # ======================================================================== # name: parseCommandArgs() # # Description: # this method reads and interprets the command line # ======================================================================== def parseCommandArgs(): global inputFileName global outputFileName returnCode = 0 # Create the arg parser, then add the defined parameters parser = argparse.ArgumentParser( prefix_chars = '-' ) addArgDefinitions( parser ) # parse the args (defined in sys.argv by default) args = parser.parse_args() # since we have parsed the arguments, interpret and store them if ( args.verbose ): logger.changeLevel( logger.DEBUG_L ) inputFileName = args.inputFile if ( inputFileName == None ): # if an input file has not been specified, we have a failure parser.print_help() returnCode = 1 else: # we have a valid input file specified. the output file will be # managed as necessary outputFileName = args.outputFile return ( returnCode ) # end parseCommandArgs() # ======================================================================== # Function: preprocessor() # # Description: # read the input file, interpret and write to the output file # ======================================================================== def preprocessor( nameForLocationString, inputHandle, outputHandle ): sourceLineNumber = 1 moduleIDWritten = False for inputLine in inputHandle.readlines(): logger.addToLog( logLevel = logger.DEBUG_L, text = "read '{}'".format( inputLine ) ) if ( not moduleIDWritten ): # read from the source file, line by line until after the FIRST # "do;" statement. At that point, write the moduleIDString data # declaration to the file lineTokens = inputLine.split() logger.addToLog( logLevel = logger.DEBUG_L, text = "tokens in line: {}".format( lineTokens ) ) if ( len( lineTokens ) > 0 ): # look to see if this is the $include (logger.ext) line for tokenIndex in range( len( lineTokens ) ): logger.addToLog( logLevel = logger.DEBUG_L, text = "found '{}'".format( lineTokens[ tokenIndex ].lower() ) ) if ( 'logger.ext' in lineTokens[ tokenIndex ].lower() ): outputHandle.write( inputLine ) outputHandle.write( "declare moduleIDString (*) byte data ( '{}', 0 );\n".format( nameForLocationString ) ) moduleIDWritten = True # end foreach token if ( not moduleIDWritten ): outputHandle.write( inputLine ) else: outputHandle.write( inputLine ) # end ! moduleIDWritten else: lineTokens = inputLine.split() logger.addToLog( logLevel = logger.DEBUG_L, text = "tokens in line: {}".format( lineTokens ) ) if ( len( lineTokens ) > 0 ): # __FILE__ will be substituted with @moduleIDString # __LINE__ will be substituted with the sourceLineNumber tokenModified = False for tokenIndex in range( len( lineTokens ) ): logger.addToLog( logLevel = logger.DEBUG_L, text = "token[{}]: {}".format( tokenIndex, lineTokens[ tokenIndex ] ) ) if ( lineTokens[ tokenIndex ] == '__FILE__,' ): lineTokens[ tokenIndex ] = "{},".format( '@moduleIDString' ) tokenModified = True elif ( lineTokens[ tokenIndex ] == ',__FILE__,' ): lineTokens[ tokenIndex ] = ",{},".format( '@moduleIDString' ) tokenModified = True elif ( lineTokens[ tokenIndex ] == ',__FILE__' ): lineTokens[ tokenIndex ] = ",{}".format( '@moduleIDString' ) tokenModified = True elif ( lineTokens[ tokenIndex ] == '__FILE__' ): lineTokens[ tokenIndex ] = "{}".format( '@moduleIDString' ) tokenModified = True elif ( lineTokens[ tokenIndex ] == '__LINE__,' ): lineTokens[ tokenIndex ] = "{},".format( sourceLineNumber ) tokenModified = True elif ( lineTokens[ tokenIndex ] == ',__LINE__,' ): lineTokens[ tokenIndex ] = ",{},".format( sourceLineNumber ) tokenModified = True elif ( lineTokens[ tokenIndex ] == ',__LINE__' ): lineTokens[ tokenIndex ] = ",{}".format( sourceLineNumber ) tokenModified = True elif ( lineTokens[ tokenIndex ] == '__LINE__' ): lineTokens[ tokenIndex ] = "{}".format( sourceLineNumber ) tokenModified = True elif ( lineTokens[ tokenIndex ] == '__LINE__;' ): lineTokens[ tokenIndex ] = "{};".format( sourceLineNumber ) tokenModified = True # end for each token in the line if ( not tokenModified ): outputHandle.write( inputLine ) else: for token in lineTokens: outputHandle.write( "{} ".format( token ) ) outputHandle.write( "\n" ) else: outputHandle.write( inputLine ) # end moduleID already written sourceLineNumber += 1 # for each line in the input # end preprocessor() # ======================================================================== # Function: processFiles() # # Description: # create the output file, read the input file, translating each line # to the output file (note: a locationFileName variable will be # created in each file that is pre-processed) # ======================================================================== def processFiles(): global inputFileName global outputFileName # first thing, create the output file. if the output file's name is # the same as the input file, rename the input file before creating # the output file if ( outputFileName == inputFileName ): modifiedInputName = inputFileName + ".ppp" fileIO.renameFile( inputFileName, modifiedInputName ) elif ( outputFileName == None ): modifiedInputName = inputFileName + ".ppp" fileIO.renameFile( inputFileName, modifiedInputName ) outputFileName = inputFileName else: modifiedInputName = inputFileName logger.addToLog( logLevel=logger.DEBUG_L, text="inputName: '{}' outputName: '{}'".format( modifiedInputName, outputFileName ) ) # create the output file, and open the input file inputFileHandle = open( modifiedInputName, 'r' ) outputFileHandle = open( outputFileName, 'w' ) # do the work we have intended preprocessor( modifiedInputName, inputFileHandle, outputFileHandle ) # close the files, and name according to the user wishes inputFileHandle.close() outputFileHandle.close() # end processFiles() # ======================================================================== # Function: main() # # Description: # the top level function for this application (corresponds to main() # in C and C++ applications) # ======================================================================== def main(): userGreetingStr = composeUserGreeting() print( userGreetingStr, end='' ) if ( 0 == parseCommandArgs() ): processFiles() # end main() # ======================================================================== # the real program entry point # ======================================================================== if ( __name__ == "__main__" ): main()