Advertisement
Kitomas

multipreproc.py

Aug 4th, 2023
863
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.77 KB | None | 0 0
  1. #!/usr/bin/python3
  2. #multipreproc.py
  3. if __name__ != "__main__": exit(0)
  4. from sys import argv
  5. from os import remove, getcwd, chdir
  6. from os.path import exists
  7. from time import time, sleep
  8. from clipboard import copy
  9. import subprocess
  10.  
  11.  
  12. NameOfScript=argv[0]
  13. NewLineSequence="|"
  14. SearchPaths=[]
  15. CopyToClipboard=False
  16. OutputFile=""
  17. CPPOutput="_PREPROC_OUTPUT"
  18. DelCPPOutput=True
  19.    
  20.  
  21. # /* removes comments like this */
  22. def removeLongComments(s):
  23.     sOut,Depth,LineNum,SecondChar = "",0,1,False
  24.     for i in range(len(s)-1):
  25.         CharPair=s[i:i+2]
  26.         if CharPair[0] == "\n": LineNum+=1
  27.         if SecondChar: SecondChar=False; continue
  28.         elif CharPair == "/*": Depth+=1
  29.         elif CharPair == "*/": Depth-=1; SecondChar=True; continue
  30.         if   Depth  > 0: continue
  31.         elif Depth  < 0: break
  32.         else: sOut+=CharPair[0]
  33.     if   Depth > 0: return None,"mismatched \"/*\" (EOF reached)"
  34.     elif Depth < 0: return None,"mismatched \"*/\" (Line {})".format(LineNum)
  35.     if s[-2:] != "*/": sOut+=s[-1]
  36.     return sOut,None
  37.  
  38. # separates by "\n", "\\n" (optional), and NewLineSequence (default value is "|")
  39. def splitLines(s):
  40.     lines=s.split("\n")
  41.     #lines=sum([line.split("\\n") for line in lines],[])
  42.     lines=sum([line.split(NewLineSequence) for line in lines],[])
  43.     #lines=[i for i in lines if i] (this would remove empty lines)
  44.     return lines
  45.  
  46. # recognizes both "//" and ";"
  47. def removeSingleLineComments(l):
  48.     lines=[line.split("//")[0] for line in l]
  49.     lines=[line.split(";")[0] for line in lines]
  50.     lines=[line.rstrip() for line in lines]
  51.     return lines
  52.  
  53.  
  54. def optCheckParamType(param):
  55.     if   type(param) == str: return True,None
  56.     elif type(param) == NoneType: return False,"no parameter given"
  57.     else: return False,"param is neither of type str nor NoneType??"
  58.  
  59. def opt_h(param):
  60.     print("\
  61. Usage: py {} <options> <input file path> \n\
  62. Options (case-insensitive, parsed from left to right): \n\
  63.  \"-h\": Print this text \n\
  64.  \"-l <new sequence>\": Change line break sequence (\"|\" by default) \n\
  65.  \"-o <output file path>\": Specify output file name \n\
  66.  \"-c\": Copy output to clipboard (-o isn't required if -c is enabled) \n\
  67.  \"-s <search path>\": Add path to search paths list (currently broken; don't use) \n\
  68.  \"-p\": Don't delete intermediate temp file (\"{}\")\
  69.    ".format(NameOfScript.split("\\")[-1],CPPOutput))
  70.     exit(0) #return True,None
  71.    
  72. def opt_l(param):
  73.     global NewLineSequence
  74.     isValid,err=optCheckParamType(param)
  75.     if isValid: NewLineSequence=param
  76.     return isValid,err
  77.    
  78. def opt_o(param):
  79.     global OutputFile
  80.     isValid,err=optCheckParamType(param)
  81.     if isValid: OutputFile=param
  82.     return isValid,err
  83.  
  84. def opt_c(param):
  85.     global CopyToClipboard
  86.     CopyToClipboard=True
  87.     return True,None
  88.  
  89. def opt_s(param):
  90.     global SearchPaths
  91.     isValid,err=optCheckParamType(param)
  92.     if isValid:
  93.         SearchPaths.append("-I")
  94.         SearchPaths.append("\"{}\"".format(param))
  95.     return isValid,err
  96.  
  97. def opt_p(param):
  98.     global DelCPPOutput
  99.     DelCPPOutput=False
  100.     return True,None
  101.  
  102. options={ #<function>,<'takes an argument?'>
  103.     "-h":(opt_h,False), #print help text
  104.     "-l":(opt_l,True),  #change new line sequence
  105.     "-o":(opt_o,True),  #set output file name
  106.     "-c":(opt_c,False), #copy output to clipboard
  107.     "-s":(opt_s,True),  #add search path
  108.     "-p":(opt_p,False)  #don't delete cpp output
  109. }
  110.  
  111.  
  112. def printExit(code,msg):
  113.     print("{} error: {}".format(NameOfScript,msg))
  114.     exit(code)
  115. def ifPrintExit(condition,code,msg):
  116.     if condition: printExit(code,msg)
  117.  
  118. #handle arguments
  119. start=time()
  120. if len(argv) < 2: options["-h"][0](None) #auto-exits
  121. if not exists(argv[-1]): #system error code 2=ERROR_FILE_NOT_FOUND
  122.     printExit(2,"file \"{}\" could not be found".format(argv[-1]))
  123. BeforeFileArg,IsParameter = len(argv)-2,False
  124. for i in range(1,BeforeFileArg+1):
  125.     if IsParameter: IsParameter=False; continue
  126.     Argument=argv[i].lower()
  127.     if not Argument in options:
  128.         printExit(1,"option \"{}\" doesn't exist".format(Argument))
  129.     option=options[Argument]
  130.     IsParameter=option[1]
  131.     if i == BeforeFileArg and IsParameter:
  132.         printExit(1,"option \"{}\" needs a parameter".format(argv[i]))
  133.     success,err=option[0](argv[i+1]) #won't matter if option doesn't use param
  134.     if not success: printExit(1,err)
  135.  
  136. #call the c preprocessor
  137. proc=subprocess.Popen(
  138.     ["cpp","-x","assembler-with-cpp","-nostdinc","-CC","-undef","-P",
  139.      "-o",CPPOutput,]+SearchPaths+[argv[-1]],
  140. stdout=subprocess.PIPE,shell=True)
  141. while proc.poll() == None: sleep(0.02)
  142. pExitCode=proc.poll()
  143. pOutput,pError = proc.communicate()
  144. if len(pOutput) != 0: print(pOutput.decode("utf-8"))
  145. if pExitCode != 0: exit(pExitCode)
  146.  
  147. #read cpp output data
  148. if not exists(CPPOutput): exit(1)
  149. infile=open(CPPOutput,"r")
  150. ifPrintExit(not infile,1,"failed to open cpp intermediate file")
  151. indata=infile.read()
  152. ifPrintExit(not indata,1,"failed to read cpp intermediate file data")
  153. infile.close()
  154.  
  155. #do the rest of the parsing
  156. indata,err=removeLongComments(indata) #returns str
  157. if not indata:
  158.     print("(check \"{}\\{}\" to see the error's cause)".format(getcwd(),CPPOutput))
  159.     printExit(1,err)
  160. indata=splitLines(indata) #returns list
  161. indata=removeSingleLineComments(indata) #returns list
  162. outdata="\n".join(indata)
  163.  
  164. #write output data
  165. if CopyToClipboard: copy(outdata)
  166. if OutputFile:
  167.     outfile=open(OutputFile,"w")
  168.     ifPrintExit(not outfile,1,"failed to open output file")
  169.     outfile.write(outdata)
  170.     outfile.close()
  171. elif not CopyToClipboard:
  172.     printExit(1,"no output file specified")
  173.  
  174. if DelCPPOutput and exists("_PREPROC_OUTPUT"): remove("_PREPROC_OUTPUT")
  175. print("finished successfully in {}ms".format(int((time()-start)*1000)))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement