home *** CD-ROM | disk | FTP | other *** search
/ PC Extra 07 & 08 / pca1507.iso / Software / psp8 / Data1.cab / JascScriptParse.py < prev    next >
Encoding:
Python Source  |  2003-04-22  |  27.9 KB  |  780 lines

  1. # JascScriptParse.py...  This file does the parsing for the script editor.
  2. #
  3.  
  4. import sys, re, string
  5. import JascScriptMsgs
  6. import JascScriptPyBlocks
  7.  
  8.  
  9. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  10. #
  11. #   this section contains the re pattern matching statements...
  12. #
  13. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  14.  
  15. PattImportJasc = re.compile(r"\bfrom\s*JascApp\s*import\s*\*")
  16. PattImportGeneral = re.compile(r"\bfrom\s*\w*\s*import\s*\*")
  17. PattDefDo = re.compile(r"\bdef\s*Do\(Environment\)\:")
  18. PattScriptProp = re.compile(r"\bdef\s*ScriptProperties\(\)\:\s*")
  19. PattScriptPropLine2 = re.compile(r"\s*\breturn\s*{\s*")
  20. PattBlankLine = re.compile(r"\s*\n")
  21. PattCommentLine = re.compile(r"\s*#")
  22. PattCloseParen = re.compile(r"\s*\)")
  23. PattDocBlock = re.compile(r"\s*'''")
  24. PattCloseBracket = re.compile(r"\s*\}")
  25. PattBlankCmtLine = re.compile("\s*#\s*\n")
  26. PattCloseBracketCmtLine = re.compile("\s*#\s*}\)")
  27. #   match PSPCommand.
  28. #       group(0) -> entire string
  29. #       group(1) -> App.do(
  30. #       group(2) -> quoted PSP Command Name
  31. #       group(3) -> {
  32. PattPSPCmd = re.compile(r"(\s*App\.Do\(\s*Environment,\s*)" +
  33.                         r"(\'[\w*\s]+\')+\," +
  34.                         r"(\s*{\s*)")
  35. PattCR = re.compile(r"\s*\n")
  36. PattBracketParen = re.compile(r"\s*}\)")
  37.  
  38. #   match simple PSP parameter.  Match through the ':'
  39. #       group(1) -> parameter name
  40. #  PattPSPParmSimple = re.compile(r"(\s*\'\s??\w*\s??\w*\')+\s*:\s*")
  41. '''
  42. PattPSPParmSimple = re.compile(r"(\s*\')" +
  43.                                 r"(\s??\w*\s??\w*)" +
  44.                                 r"\'+\s*:\s*")
  45. '''
  46.  
  47. PattPSPParmSimple = re.compile(r"(\s*\')" +
  48.                                r"([\w*\s]+)" +
  49.                                r"\'+\s*:\s*")
  50.  
  51. #   match PSP parameter Start Dictionary.  Find parm name and match
  52. #       up through the opening brace
  53. #       group(1) -> parameter name
  54. PattPSPParmStrDict = re.compile(r"(\s*\'\w*\')+\s*:\s*{")
  55.  
  56.  
  57. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  58. # Class: ParseScript
  59. # Author: Carl Lestor
  60. # Purpose: This class handles the parsing of the python script file.  Its main job
  61. #   is to parse the file and build up 'pyBlocks'.  These objects represent blocks
  62. #   of python code, psp commands, script descriptions and the like.  
  63. # Returns: none
  64. # Parameters:   fp - file handle for the file to parse.
  65. #               pyBlockPtr = place to store the python blocks parsed   
  66. # Notes: Errors are sent to std out; the script output window.
  67. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  68. class ParseScript:
  69.     def __init__(self):
  70.         self.pyBlocks = []      # a place to pack hold the python blocks representing the script
  71.         self.fp = 0             # handle of the file
  72.         self.line = ""          # line being parsed
  73.         self.block = ""         # blocks of code built up during parse
  74.         self.linepos = 0        # line position during parse
  75.         #print 'IN PARSE SCRIPT'
  76.  
  77.         # following data is used for understanding parse failures
  78.         self.prevLine = ""      # previous line
  79.         self.prevPrevLine = ""  # previous previous line
  80.         self.lineNum = 0        # current line being parsed
  81.         self.errorString = ""   # set when a parse fails
  82.         self.filename = ""      # store the file name in case we need to invoke a text editor
  83.  
  84. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  85. # Class: ParseScript::openFile
  86. # Author: Steve Neumeyer
  87. # Purpose: initialize the file pointer for reading
  88. # Returns: none
  89. # Parameters:   name - the filename to open
  90. # Notes: Simply opens the file and stores the reference in the file member
  91. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  92.     def openFile(self, name):
  93.         try:
  94.             self.filename = name
  95.             self.fp = open(name, 'r')
  96.             return 1
  97.         except:
  98.             return 0        
  99.  
  100. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  101. # Class: ParseScript::getLine
  102. # Author: Carl Lestor
  103. # Purpose: get a new line of text from the file
  104. # Returns: none
  105. # Parameters:   cmtflag - indicates if we are handling a commented psp command
  106. # Notes: Much of the parsing shares the logic if we are parsing a commented psp
  107. #       command or a non-commented one; hence the input flag.  This method
  108. #       silently skips the splat if we are doing comments.
  109. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  110.     def getLine(self, cmtFlag):
  111.         # if requested, handle comment lines by moving the line position past the opening comment char
  112.         self.prevPrevLine = self.prevLine       # save prev lines for diagnosis...
  113.         self.prevLine = self.line
  114.         self.line = self.fp.readline()
  115.         self.linepos = 0
  116.         self.lineNum = self.lineNum + 1
  117.         if cmtFlag:
  118.             # caller indicated we are in a state parsing commented material.  skip over the comment
  119.             # portion of the line.  Return 0 if we do not find a comment....
  120.             match = PattCommentLine.match(self.line)
  121.             if match:
  122.                 self.linepos = match.end(0)
  123.                 return (self.line)
  124.             else:
  125.                 return 0
  126.         else:
  127.             return (self.line)
  128.         
  129.  
  130. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  131. # Class: ParseScript::addPyBlock
  132. # Author: Carl Lestor
  133. # Purpose: get a new line of text from the file
  134. # Returns: none
  135. # Parameters:   type - type of block to add.
  136. # Notes: 
  137. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  138.     def addPyBlock(self, type):
  139.         # set up the block that was found.
  140.         pyb = JascScriptPyBlocks.PyBlock(type, self.block)
  141.         self.pyBlocks = self.pyBlocks + [pyb]
  142.         self.block = ""
  143.  
  144.  
  145. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  146. # Class: ParseScript::parseFail
  147. # Author: Carl Lestor
  148. # Purpose: simply a place to handle parse errors consistently.
  149. # Returns: none
  150. # Parameters:   error.  String describing the type of error ecountered.  Abstracted to
  151. #       a unique file for translation ease.
  152. # Notes: 
  153. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  154.     def parseFail(self, error):
  155.         # the parse failed...
  156.         self.errorString = JascScriptMsgs.JascSEmsgs(error)
  157.  
  158. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  159. # Class: ParseScript::getNextNonEscapedQuoteOld
  160. # Author: Steve Neumeyer
  161. # Purpose: get the next quote that is not escaped with a \ preceeding it
  162. # Returns: the position of the next non-escaped quote mark
  163. #            -1 for error condition
  164. # Parameters:   currentPosition - the current position in the string
  165. #                string - the line of text being searched
  166. # Notes: this could be moved to JascTools
  167. #            this version only handles single quote marks '
  168. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  169.     def getNextNonEscapedQuoteOld(self, currentPosition, str):
  170.         escapedQuote = "\\'"
  171.  
  172.         while 1:
  173.             nextQuotePos = string.find(str, "'", currentPosition)
  174.             nextEscapedQuotePos = string.find(str, escapedQuote, currentPosition)
  175.             if nextEscapedQuotePos != -1:
  176.                 nextEscapedQuotePos += 1 
  177.  
  178.             # if the next quote is escaped, search forward from that position until
  179.             # we find a non-escaped quote
  180.             if nextQuotePos == nextEscapedQuotePos:
  181.                 currentPosition = 1+nextQuotePos # now we are searching from the last found quote pos
  182.                 continue
  183.             else:
  184.                 return nextQuotePos
  185.  
  186. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  187. # Class: ParseScript::getNextNonEscapedQuote
  188. # Author: Steve Neumeyer
  189. # Purpose: get the next quote mark ' or " that is not escaped with a \ preceeding it
  190. # Returns: the position of the next non-escaped quote mark
  191. #            -1 for error condition
  192. # Parameters:   currentPosition - the current position in the string
  193. #                string - the line of text being searched
  194. #                doublequote - 1 or nonzero - we are dealing with a double quote
  195. #                            - 0 - single quote
  196. # Notes: updated version to handle ' and " quotes
  197. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  198.     def getNextNonEscapedQuote(self, currentPosition, str, doublequote):
  199.         if doublequote:
  200.             escapedQuote = "\\'"
  201.         else:
  202.             escapedQuote = '\\"'
  203.  
  204.         while 1:
  205.             if doublequote:
  206.                 nextQuotePos = string.find(str, '"', currentPosition)                
  207.             else:
  208.                 nextQuotePos = string.find(str, "'", currentPosition)
  209.  
  210.  
  211.             nextEscapedQuotePos = string.find(str, escapedQuote, currentPosition)
  212.             if nextEscapedQuotePos != -1:
  213.                 nextEscapedQuotePos += 1 
  214.  
  215.             # if the next quote is escaped, search forward from that position until
  216.             # we find a non-escaped quote
  217.             if nextQuotePos == nextEscapedQuotePos:
  218.                 currentPosition = 1+nextQuotePos # now we are searching from the last found quote pos
  219.                 continue
  220.             else:
  221.                 return nextQuotePos
  222.  
  223. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  224. # Class: ParseScript::EatValue
  225. # Author: Carl Lestor
  226. # Purpose: eats the value portion of a dictionary
  227. #       entry.  We simply consume all characters until the
  228. #       comma indicating the value has ended.  We must ignore
  229. #       commas embedded in tuples, dicts, and lists....  When
  230. #       successful, we add the value to the codeblock.
  231. # Returns:  success - 1 if good, 0 if parse fails
  232. #           moreLeft - 1 if true, 0 if end of dictionary
  233. #           Value - value parsed
  234. # Parameters:  cmtFlag - indicates if we are parsing a comment. 
  235. # Notes: 
  236. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  237.     def EatValue(self, cmtFlag):
  238.  
  239.         startoff = self.linepos
  240.         fragToAdd = self.line[startoff:len(self.line)]
  241.         Value = ''
  242.  
  243.         embedded = 0
  244.         stringEating = 0
  245.         singleQuote = 0
  246.  
  247.         # store the position of the final quote mark in a string
  248.         # this is an index to the last ' or " in a string, depending
  249.         # on what type of quote we started with.  default index is
  250.         # the position of the last ' mark
  251.         #finalQuotePos = string.rfind(self.line, "'")
  252.         nextQuotePos = self.getNextNonEscapedQuote(self.linepos+1, self.line, 0)
  253.  
  254.         while 1:
  255.             # get the character and process it....                
  256.             ch = self.line[self.linepos]
  257.  
  258.             # handle strings... end chars in strings must be ignored...            
  259.             if stringEating:
  260.                 if singleQuote and ch =="\'" and self.linepos == nextQuotePos: #finalQuotePos:
  261.                         stringEating = 0
  262.                         singleQuote = 0
  263.                 elif not singleQuote and ch == '\"' and self.linepos == nextQuotePos: #finalQuotePos:
  264.                         stringEating = 0
  265.                 self.linepos = self.linepos + 1
  266.             elif ch =="\'": 
  267.                 stringEating = 1
  268.                 singleQuote = 1
  269.                 #finalQuotePos = string.rfind(self.line, "'")
  270.                 nextQuotePos = self.getNextNonEscapedQuote(self.linepos+1, self.line, 0)
  271.                 self.linepos = self.linepos + 1
  272.             elif ch =='\"': 
  273.                 stringEating = 1
  274.                 singleQuote = 0
  275.                 #finalQuotePos = string.rfind(self.line, '"')
  276.                 nextQuotePos = self.getNextNonEscapedQuote(self.linepos+1, self.line, 1)
  277.                 self.linepos = self.linepos + 1
  278.             # check for ending characters                                    
  279.             elif ch =="," and embedded == 0:
  280.                 # end of value, more dictionary to handle....
  281.                 Value = Value + fragToAdd[0:self.linepos - startoff]
  282.                 self.block = self.block + Value + ch
  283.                 self.linepos = self.linepos + 1
  284.                 self.chkEOLN(cmtFlag)
  285.                 return (1, 1, Value)
  286.             elif ch == "}" and embedded == 0:
  287.                 # end of value, end of dictionary....
  288.                 Value = Value + fragToAdd[0:self.linepos - startoff]
  289.                 self.block = self.block + Value + ch
  290.                 self.linepos = self.linepos + 1
  291.                 self.chkEOLN(cmtFlag)
  292.                 return (1, 0, Value)
  293.  
  294.             # handle being imbedded in dicts, tuples, or lists...            
  295.             elif (ch == '(' or ch =='{' or ch == '['):
  296.                 embedded = embedded + 1
  297.                 self.linepos = self.linepos + 1
  298.             elif ch == ')' or ch =='}' or ch == ']':
  299.                 embedded = embedded - 1
  300.                 self.linepos = self.linepos + 1
  301.                 
  302.             # go right through EOLN for longer parms...
  303.             elif ch == '\0' or ch == '\n':
  304.                 # we hit end of line
  305.                 Value = Value + fragToAdd[0:self.linepos + 1 - startoff]
  306.                 self.getLine(cmtFlag)
  307.                 if not self.line:
  308.                     # we hit eof in error. incomplete dict or imbalanced parens
  309.                     self.block = self.block + Value
  310.                     return (0, 0, Value)
  311.                 fragToAdd = self.line
  312.                 self.linepos = 0
  313.                 startoff = self.linepos
  314.             else:
  315.                 # any old character
  316.                 self.linepos = self.linepos + 1
  317.  
  318.  
  319. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  320. # Class: ParseScript::EatDocBlock
  321. # Author: Carl Lestor
  322. # Purpose: eats the contents of a triple quoted documentation block
  323. #       The first triple quoted chars have been found..  When
  324. #       successful, we add the value to the codeblock.
  325. # Returns: success - 1 if good, 0 if parse failure
  326. # Parameters:   none
  327. # Notes: 
  328. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  329.     def eatDocBlock(self):
  330.  
  331.         startoff = self.linepos
  332.         fragToAdd = self.line[startoff:len(self.line)]
  333.         singleQuoteDepth = 0
  334.       
  335.         while singleQuoteDepth < 3:
  336.             ch = self.line[self.linepos]
  337.             self.linepos = self.linepos + 1
  338.  
  339.             if ch == "\'":
  340.                 singleQuoteDepth = singleQuoteDepth + 1
  341.  
  342.             # handle multi line doc blocks...
  343.             elif ch == '\0' or ch == '\n':
  344.                 # we hit end of line
  345.                 self.block = self.block + fragToAdd[0:self.linepos + 1 - startoff]
  346.                 self.getLine(0)
  347.                 if not self.line:
  348.                     # we hit eof in error. incomplete doc block.
  349.                     return (0)
  350.                 fragToAdd = self.line
  351.                 self.linepos = 0
  352.                 startoff = 0
  353.                 singleQuoteDepth = 0
  354.             else:
  355.                 # any old character
  356.                 singleQuoteDepth = 0
  357.         
  358.         # end of doc block found....
  359.         self.block = self.block + fragToAdd[0:self.linepos + 1 - startoff]
  360.         self.chkEOLN(0)
  361.         return (1)
  362.  
  363.  
  364. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  365. # Class: ParseScript::EatDocBlock
  366. # Author: Carl Lestor
  367. # Purpose: check for end of line.  consume white space as we go...
  368. # Returns: 
  369. # Parameters: chkCmtFlag - indicates if we are parsing a comment or not.
  370. # Notes: 
  371. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  372.     def chkEOLN(self, chkCmtFlag):
  373.         
  374.         while 1:
  375.             ch = self.line[self.linepos]
  376.             if ch == " " or ch == "\n":
  377.                 # consume it
  378.                 self.linepos = self.linepos + 1
  379.                 self.block = self.block + ch
  380.                 if self.linepos == len(self.line):
  381.                     # we have reached the end of line.
  382.                     self.getLine(chkCmtFlag)
  383.                     if (chkCmtFlag):
  384.                         # we found a new commented line. need to pick up the splat...
  385.                         self.block = self.block + self.line[0:self.linepos]
  386.                     return (1)
  387.             else: 
  388.                 return (1)
  389.     
  390.  
  391. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  392. # Class: ParseScript::ParseDictionary
  393. # Author: Carl Lestor
  394. # Purpose: parse a dictionary.  
  395. # Returns: success - 1 if good, 0 if parse failure
  396. # Parameters:   OurParms - dictionary or parms being built.  
  397. #               cmtFlag - indicates if we are parsing a comment or not.
  398. # Notes: Finds a dictionary entry and adds it to OurParms
  399. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  400.     def parseDictionary(self, OurParms, cmtFlag):
  401.  
  402.         # assumes open brace has been consumed. match all
  403.         # and consume the end brace.
  404.         while 1:
  405.             # we either have consumed the line with the re match or there are some
  406.             #   parms also on that line.  if at the end, get a new one....
  407.             if self.linepos >= len(self.line):
  408.                 self.getLine(cmtFlag)
  409.                 if not self.line:
  410.                     return 0
  411.                 if (cmtFlag):
  412.                     # we found a new commented line. need to pick up the splat...
  413.                     self.block = self.block + self.line[0:self.linepos]
  414.             # find a parmName:value pair.  first the name.
  415.             match = PattPSPParmSimple.match(self.line, self.linepos)
  416.             if match:
  417.                 # parmName found.  update block and get value.  
  418.                 self.block = self.block + match.group(0)
  419.                 self.linepos = match.end(0)                
  420.                 self.chkEOLN(cmtFlag)
  421.                 ok, moreLeft, Value = self.EatValue(cmtFlag)
  422.                 if not ok:
  423.                     return 0
  424.  
  425.                 # need to trim the spaces from match group1
  426.                 OurParms.append([match.group(2), Value])    
  427.                 if not moreLeft:
  428.                     return 1
  429.             else:
  430.                 return 0
  431.  
  432.             
  433. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  434. # Class: ParseScript::checkState
  435. # Author: Carl Lestor
  436. # Purpose: checks to see if the statement found is valid in the current state
  437. # Returns: 
  438. # Parameters:   state - current state of the parse
  439. #               StatementFound - indicates what type of statement was discovered.
  440. # Notes: The purpose of this method is to provide the user with a more meaningful
  441. #   error msg if the script obviously will not run.  Hopefully this will aid users
  442. #   new to hand editing scripts.  When they invoke the Script Editor, some simple
  443. #   error will be discovered.  This is light and not thorough syntax checking.
  444. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  445.     def checkState(self, state, StatementFound):
  446.  
  447.         #
  448.         #   state tables.  for each state, the dictionary contains the pair of 'received statement type' and
  449.         #       'new state'.  If the new type contains 'ERROR:' in it. the parse failed.  Error message text
  450.         #       is isloated in JascScriptMsgs for translation....
  451.         #
  452.         ExpectImport = {"Import":"DefDo",
  453.                         "Comment":"Import",
  454.                         "Script":"ERROR:Parse11",
  455.                         "CmtPSPCmd":"ERROR:Parse12",
  456.                         "DefDo":"ERROR:Parse13",
  457.                         "PSPCmd":"ERROR:Parse14"}
  458.  
  459.         ExpectDefDo = {"Import":"ERROR:Parse21",
  460.                         "Comment":"DefDo",
  461.                         "Script":"DefDo",
  462.                         "CmtPSPCmd":"ERROR:Parse22",
  463.                         "DefDo":"PSPCmd",
  464.                         "PSPCmd":"ERROR:Parse23"}
  465.         
  466.         ExpectPSPCmd = {"Import":"ERROR:Parse31",
  467.                         "Comment":"PSPCmd",
  468.                         "Script":"PSPCmd",
  469.                         "CmtPSPCmd":"PSPCmd",
  470.                         "DefDo":"ERROR:Parse32",
  471.                         "PSPCmd":"PSPCmd"}
  472.  
  473.         if state == "Import":
  474.             newstate = ExpectImport[StatementFound]
  475.         elif state == "DefDo":
  476.             newstate = ExpectDefDo[StatementFound]
  477.         else:
  478.             newstate = ExpectPSPCmd[StatementFound]
  479.                             
  480.         if newstate.count("ERROR"):
  481.             return (0, newstate)
  482.         else:
  483.             return (1, newstate)
  484.             
  485.  
  486.     # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  487.     # Class: ParseScript::parseScript
  488.     # Author: Carl Lestor
  489.     # Purpose: This is the main method that parses the script.
  490.     # Returns:
  491.     #           success - 1 if good, 0 if bad
  492.     #           PSPCmdCount - number of psp commands found
  493.     # Parameters:   none
  494.     # Notes: The method is a basic loop processing a line at a time of the input file.  Top
  495.     #   level line recognition is done with re pattern matching.  Outer loop looks for
  496.     #   basic types of statements and then parses the details.
  497.     # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  498.     def parseScript(self):
  499.  
  500.         PSPCmdCount = 0
  501.         ExpectedStmt = "Import"
  502.         
  503.         while 1:
  504.             self.getLine(0)
  505.             if not self.line:
  506.                 return (1, PSPCmdCount)
  507.             #
  508.             # try to match our line to one of our patterns....  first the import statement
  509.             #
  510.             match = PattImportJasc.match(self.line)
  511.             if match:
  512.                 parseOK, ExpectedStmt = self.checkState(ExpectedStmt, "Import")
  513.                 if not parseOK:
  514.                     self.parseFail(ExpectedStmt)
  515.                     return (0, PSPCmdCount)
  516.                 self.block = self.line
  517.                 self.addPyBlock ("ImportJasc")
  518.                 continue
  519.             #
  520.             # match a def do statement
  521.             #
  522.             match = PattDefDo.match(self.line)
  523.             if match:
  524.                 parseOK, ExpectedStmt = self.checkState(ExpectedStmt, "DefDo")
  525.                 if not parseOK:
  526.                     self.parseFail(ExpectedStmt)
  527.                     return (0, PSPCmdCount)
  528.                 self.block = self.line
  529.                 self.addPyBlock ("DefDo Match")
  530.                 continue
  531.             #
  532.             # match the script properties
  533.             #
  534.             match = PattScriptProp.match(self.line)
  535.             if match:
  536.                 parseOK, ExpectedStmt = self.checkState(ExpectedStmt, "Script")
  537.                 if not parseOK:
  538.                     self.parseFail(ExpectedStmt)
  539.                     return (0, PSPCmdCount)
  540.                 self.block = self.line
  541.                 # line 1 matches.  match line2.
  542.                 self.getLine(0)
  543.                 self.linepos = 0
  544.                 match = PattScriptPropLine2.match(self.line)
  545.                 if match:
  546. #                    self.block = self.line
  547.                     self.block = self.block + match.group(0)
  548.                     self.linepos = match.end(0)
  549.                     parmList = []
  550.                     if self.parseDictionary(parmList, 0):
  551.                         self.addPyBlock ("Script Properties")
  552. #                        print "parmList = ", parmList      # may not need parmList of dict items...
  553.                     else:
  554.                         # failed to parse script properties parameters
  555.                         self.parseFail("ERROR:Parse41")
  556.                         return (0, PSPCmdCount)
  557.                 else:
  558.                     # failed to parse script properties return statement
  559.                     self.parseFail("ERROR:Parse42")
  560.                     return (0, PSPCmdCount)
  561.             #
  562.             # match a psp command
  563.             #
  564.             match = PattPSPCmd.match(self.line)
  565.             if match:
  566.                 parseOK, ExpectedStmt = self.checkState(ExpectedStmt, "PSPCmd")
  567.                 if not parseOK:
  568.                     self.parseFail(ExpectedStmt)
  569.                     return (0, PSPCmdCount)
  570.                 # set the end to the last match position.  group 3....
  571.                 self.block = match.group(0)
  572.                 self.linepos = match.end(0)
  573.                 parmList = []
  574.                 if self.parseDictionary(parmList, 0):
  575.                     # we have parsed the dictionary. pick up the trailing paren
  576.                     match = PattCloseParen.match(self.line, self.linepos)
  577.                     if match:
  578.                         self.block = self.block + match.group(0)
  579.                         self.linepos = match.end(0)
  580.                     self.addPyBlock ("PSPCmd")
  581.                     PSPCmdCount = PSPCmdCount + 1
  582.                 else:
  583.                     # need to handle the case of a command with no dict/repository
  584.                     # match = PattCloseBracket.match(self.line, self.linepos)
  585.                     match = PattCR.match(self.line, self.linepos)
  586.                     # the command has no dict/repository
  587.                     if match: 
  588.                         self.block = self.block + match.group(0)
  589.                         self.linepos = match.end(0)
  590.                         self.getLine(0)
  591.                         match = PattBracketParen.match(self.line, self.linepos)
  592.                         if match:
  593.                             self.block = self.block + match.group(0)
  594.                             self.linepos = match.end(0)
  595.                         else:    # error
  596.                             self.parseFail("ERROR:Parse51")
  597.                             return (0, PSPCmdCount)
  598.                         self.addPyBlock("PSPCmd")
  599.                         PSPCmdCount += 1
  600.                     else:   
  601.                         # failed to parse psp command parameter dictionary
  602.                         
  603.                         # csl...  this error msg is non optimal.  mismatched parens can cause the parse
  604.                         #       to go deep into the file.  line shown in error obj could be far away from bad
  605.                         #       parm list.  (looking for ending paren down the file.)  improvement to add pspcmd to
  606.                         #       error msg....
  607.                     
  608.                         self.parseFail("ERROR:Parse51")
  609.                         return (0, PSPCmdCount)
  610.                 continue
  611.  
  612.             #
  613.             # match a commented line or commented psp command
  614.             #
  615.             match = PattCommentLine.match(self.line)
  616.             if match:
  617.                 self.linepos = match.end(0)
  618.                 if ExpectedStmt == "PSPCmd":
  619.                     # could be a commented psp command.  better check for that.  else just a cmt....
  620.                     self.block = self.block + match.group(0)
  621.                     keepLookingFrom = match.end(0)
  622.                     match = PattPSPCmd.match(self.line, keepLookingFrom)
  623.                     if match:
  624.                         # we have found a comment that starts off like a psp command.  check the parms.
  625.                         self.block = self.block + match.group(0)
  626.                         self.linepos = match.end(0)
  627.                         parmList = []
  628.                         if self.parseDictionary(parmList, 1):
  629.                             # we have parsed the dictionary. pick up the trailing paren
  630.                             match = PattCloseParen.match(self.line, self.linepos)
  631.                             if match:
  632.                                 self.block = self.block + match.group(0)
  633.                                 self.linepos = match.end(0)
  634.                                 self.addPyBlock ("CmtedPSPCmd")
  635.                                 PSPCmdCount = PSPCmdCount + 1
  636.                                 continue
  637.                         else: # commented command with empty dict/repository (ex: UndoLastCmd)
  638.                             match = PattBlankCmtLine.match(self.line, 0)
  639.                             if match:
  640.                                 self.block = self.block + match.group(0)[1:]
  641.                                 self.linepos = match.end(0)
  642.                                 self.getLine(1)
  643.                                 match = PattCloseBracketCmtLine.match(self.line, 0)
  644.                                 if match:
  645.                                     self.block = self.block + match.group(0)
  646.                                     self.linepos = match.end(0)
  647.                                     self.addPyBlock("CmtedPSPCmd")
  648.                                     PSPCmdCount += 1
  649.                                     continue
  650.                                 else:
  651.                                     self.parseFail("ERROR:Parse51")
  652.                                     return (0, PSPCmdCount)
  653.                             else:
  654.                                 self.parseFail("ERROR:Parse51")
  655.                                 return (0, PSPCmdCount)
  656.                     else:
  657.                         self.block = ''
  658.  
  659.                 # csl.... there are several ways to fall through to this.  UT all...        
  660.                 # ended up being a typical comment
  661.                 # self.block = self.block + self.line[self.linepos:len(self.line)]
  662.                 self.block = self.block + self.line[0:len(self.line)]
  663.                 self.addPyBlock ("PythonCmt")
  664.                 continue
  665.  
  666.             #
  667.             # match a doc block (python within a triple quoted block..)
  668.             #
  669.             match = PattDocBlock.match(self.line)
  670.             if match:
  671.                 self.block = match.group(0)
  672.                 self.linepos = match.end(0)
  673.                 if self.eatDocBlock():
  674.                     self.addPyBlock ("DocBlock")
  675.                 else:
  676.                     # failed to find the end of doc block
  677.                     self.parseFail("ERROR:Parse52")
  678.                     return (0, PSPCmdCount)
  679.                 continue
  680.  
  681.             #
  682.             # match a blank or new line.  these are allowed anywhere...
  683.             #
  684.             match = PattBlankLine.match(self.line)
  685.             if match:
  686.                 self.block = self.line
  687.                 self.addPyBlock ("BlankLine")
  688.                 continue
  689.             
  690.             else:
  691.                 #
  692.                 # everything else is a general python code block
  693.                 self.block = self.line
  694.                 self.addPyBlock ("PyCode")
  695.  
  696.  
  697. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  698. # Function: DoParse
  699. # Author: Steve Neumeyer
  700. # Purpose: Do a parse and return the result
  701. # Returns: the result of the parse
  702. # Parameters: fname - file to parse
  703. # Notes: 
  704. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  705. def DoParse(fname, sp):
  706.  
  707.     parseResult = 0
  708.     if sp.openFile(fname):
  709.         parseResult = sp.parseScript()
  710.  
  711.     return sp, parseResult        
  712.                 
  713.  
  714. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  715. # Function: DriveParse
  716. # Author: Carl Lestor
  717. # Purpose: This is a utility function to test the parse.  
  718. # Returns: 
  719. # Parameters:
  720. #       fname - file to parse
  721. #       printIt - true if we should print to stdout the parse details in addition to logging to the file
  722. # Notes: This is not part of the production product.  Drive UT from external script.  Write basic results
  723. #       to std out and details to output file name derived from input file name...
  724. # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  725. def DriveParse(fname, printIt, sp):
  726.  
  727.     # instance declared in calling function -- sp = ParseScript()
  728.     printString = ""
  729.     if sp.openFile(fname):
  730.         
  731.         # create output file name and set up for logging....
  732.         nameParts = string.split(fname, '.')
  733.         outName = nameParts[0] + "OUT.txt"
  734.         outf = open(outName, 'w')
  735.         
  736.         parseResult = sp.parseScript()
  737.  
  738.         # print the summary result to stdout and the details to the output file....        
  739.         if parseResult:
  740.             # parse was good
  741.             print "filename:" + fname + "  Parse Result:good"
  742.             outf.write("filename:" + fname + "  Parse Result:good\n\n\n")
  743.             for pyblock in sp.pyBlocks:
  744.                 block, cmd = pyblock.dump()
  745.                 outf.write("Pyblock:" + cmd + "\n" + block + "\n")
  746.                 printString = printString + "Pyblock:" + cmd + "\n" + block + "\n"
  747.         else:
  748.             # parse failure
  749.             print "filename:" + fname + "  " + sp.errorString
  750.             outf.write("filename:" + fname + "  " + sp.errorString + "\n")
  751.             outf.write(sp.prevPrevLine + sp.prevLine + sp.line)
  752.             outf.write("Failing line number:" + str(sp.lineNum) + "    Failing Char Position:" + str(sp.linepos))
  753.             printString = printString + sp.prevPrevLine + sp.prevLine + sp.line
  754.             printString = printString + "Failing line number:" + str(sp.lineNum) + "    Failing Char Position:" + str(sp.linepos)
  755.  
  756.         if printIt == "dump":
  757.             print printString
  758.                                                                                                                       
  759.         outf.close()
  760.  
  761.     else:
  762.         print "failed to open file:", fname
  763.  
  764.     return sp        
  765.     
  766. ######################
  767. if __name__ == '__main__':
  768.  
  769.     sp = ParseScript()
  770.  
  771.     DriveParse(fname, "dump", sp)
  772.  
  773. ##    if len(sys.argv) == 3:
  774. ##        DriveParse(sys.argv[1], sys.argv[2])
  775. ##    elif len(sys.argv) == 2:
  776. ##        # assume no stdout dumping is wanted
  777. ##        DriveParse(sys.argv[1], "NoDump")
  778. ##    else:
  779. ##        print "Needs 2 parameters: filename and dump option.  Second parm = 'dump' to send details to stdout."
  780.