diff cmd2.py @ 157:10e917acf787

wow, parsing is broken
author catherine@dellzilla
date Mon, 24 Nov 2008 18:53:25 -0500
parents 7e5a57df88aa
children 87d3f3203b96
line wrap: on
line diff
--- a/cmd2.py	Fri Nov 21 17:56:28 2008 -0500
+++ b/cmd2.py	Mon Nov 24 18:53:25 2008 -0500
@@ -51,8 +51,8 @@
         optionParser.set_usage("%s [options] arg" % func.__name__.strip('do_'))
         def newFunc(instance, arg):
             try:
-                opts, arg = optionParser.parse_args(arg.split())
-                arg = ' '.join(arg)
+                opts, newArgs = optionParser.parse_args(arg.split())
+                arg.parsed['args'] = arg[arg.find(newArgs[0]):]
             except (optparse.OptionValueError, optparse.BadOptionError,
                     optparse.OptionError, optparse.AmbiguousOptionError,
                     optparse.OptionConflictError), e:
@@ -167,10 +167,14 @@
         return self.onecmd('%s%s%s' % (statement.fullStatement[:start], 
                             newinput, statement.fullStatement[end:]))
 
+class ParsedString(str):
+    pass
+
 class Cmd(cmd.Cmd):
     echo = False
     caseInsensitive = True
-    continuationPrompt = '> '    
+    continuationPrompt = '> '  
+    legalChars = '!#$%.:?@_'   # make sure your terminators are not in here!
     shortcuts = {'?': 'help', '!': 'shell', '@': 'load' }
     excludeFromHistory = '''run r list l history hi ed edit li eof'''.split()
     noSpecialParse = 'set ed edit exit'.split()
@@ -229,19 +233,19 @@
         >>> c.multilineCommands = ['multiline']
         >>> c.caseInsensitive = True
         >>> c._init_parser()
+        >>> print c.parser.parseString('termbare;').dump()
+        >>> print c.parser.parseString('termbare; suffx').dump()
         >>> print c.parser.parseString('barecommand').dump()
         ['barecommand', '']
-        - args:
         - command: barecommand
         - statement: ['barecommand', '']
-          - args:
           - command: barecommand
         >>> print c.parser.parseString('COMmand with args').dump()
-        ['command', ' with args']
-        - args:  with args
+        ['command', 'with args']
+        - args: with args
         - command: command
-        - statement: ['command', ' with args']
-          - args:  with args
+        - statement: ['command', 'with args']
+          - args: with args
           - command: command
         >>> print c.parser.parseString('command with args and terminator; and suffix').dump()
         ['command', 'with args and terminator', ';', ' and suffix']
@@ -254,18 +258,16 @@
         - suffix:  and suffix
         - terminator: ;        
         >>> print c.parser.parseString('simple | piped').dump()
-        ['simple', '', '|', 'piped']
-        - args:
+        ['simple', '', '|', ' piped']
         - command: simple
-        - pipeTo: piped
+        - pipeTo:  piped
         - statement: ['simple', '']
-          - args:
           - command: simple
         >>> print c.parser.parseString('command with args, terminator;sufx | piped').dump()
-        ['command', 'with args, terminator', ';', 'sufx', '|', 'piped']
+        ['command', 'with args, terminator', ';', 'sufx', '|', ' piped']
         - args: with args, terminator
         - command: command
-        - pipeTo: piped
+        - pipeTo:  piped
         - statement: ['command', 'with args, terminator', ';']
           - args: with args, terminator
           - command: command
@@ -273,21 +275,21 @@
         - suffix: sufx
         - terminator: ;
         >>> print c.parser.parseString('output into > afile.txt').dump()
-        ['output', ' into', '>', 'afile.txt']
-        - args:  into
-        - command: output
-        - output: >
-        - outputTo: afile.txt
-        - statement: ['output', ' into']
-          - args:  into
-          - command: output      
-        >>> print c.parser.parseString('output into;sufx | pipethrume plz > afile.txt').dump()
-        ['output', 'into', ';', 'sufx', '|', 'pipethrume plz', '>', 'afile.txt']
+        ['output', 'into', '>', 'afile.txt']
         - args: into
         - command: output
         - output: >
         - outputTo: afile.txt
-        - pipeTo: pipethrume plz
+        - statement: ['output', 'into']
+          - args: into
+          - command: output   
+        >>> print c.parser.parseString('output into;sufx | pipethrume plz > afile.txt').dump()
+        ['output', 'into', ';', 'sufx', '|', ' pipethrume plz', '>', 'afile.txt']
+        - args: into
+        - command: output
+        - output: >
+        - outputTo: afile.txt
+        - pipeTo:  pipethrume plz
         - statement: ['output', 'into', ';']
           - args: into
           - command: output
@@ -295,12 +297,12 @@
         - suffix: sufx
         - terminator: ;
         >>> print c.parser.parseString('output to paste buffer >> ').dump()
-        ['output', ' to paste buffer', '>>', '']
-        - args:  to paste buffer
+        ['output', 'to paste buffer', '>>', '']
+        - args: to paste buffer
         - command: output
         - output: >>
-        - statement: ['output', ' to paste buffer']
-          - args:  to paste buffer
+        - statement: ['output', 'to paste buffer']
+          - args: to paste buffer
           - command: output
         >>> print c.parser.parseString('ignore the /* commented | > */ stuff;').dump()
         ['ignore', 'the /* commented | > */ stuff', ';', '']
@@ -353,7 +355,7 @@
         terminatorParser = pyparsing.oneOf(self.terminators)('terminator')
         stringEnd = pyparsing.stringEnd ^ '\nEOF'
         multilineCommand = pyparsing.Or([pyparsing.Keyword(c, caseless=self.caseInsensitive) for c in self.multilineCommands])('multilineCommand')
-        oneLineCommand = pyparsing.Word(pyparsing.printables)('command')
+        oneLineCommand = pyparsing.Word(self.legalChars)('command')
         afterElements = \
             pyparsing.Optional('|' + pyparsing.SkipTo(outputParser ^ stringEnd)('pipeTo')) + \
             pyparsing.Optional(outputParser + pyparsing.SkipTo(stringEnd).setParseAction(lambda x: x[0].strip())('outputTo'))
@@ -361,13 +363,13 @@
             multilineCommand.setParseAction(lambda x: x[0].lower())
             oneLineCommand.setParseAction(lambda x: x[0].lower())
         self.parser = (
-            (((multilineCommand ^ oneLineCommand) + pyparsing.SkipTo(terminatorParser)('args') + terminatorParser)('statement') +
+            (((multilineCommand ^ oneLineCommand) + pyparsing.SkipTo(terminatorParser).setParseAction(lambda x: x[0].strip())('args') + terminatorParser)('statement') +
              pyparsing.SkipTo(outputParser ^ '|' ^ stringEnd)('suffix') + afterElements)
             ^
             multilineCommand + pyparsing.SkipTo(pyparsing.stringEnd)
             ^
-            ((oneLineCommand + pyparsing.SkipTo(terminatorParser ^ stringEnd ^ '|' ^ outputParser)('args'))('statement') +
-            afterElements)
+            ((oneLineCommand + pyparsing.SkipTo(terminatorParser ^ stringEnd ^ '|' ^ outputParser).setParseAction(lambda x:x[0].strip())('args'))('statement') +
+            pyparsing.Optional(terminatorParser) + afterElements)
             )
         self.commentGrammars.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).setParseAction(lambda x: '')
         self.commentInProgress.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).ignore(pyparsing.cStyleComment)       
@@ -375,12 +377,12 @@
         
         inputMark = pyparsing.Literal('<')
         inputMark.setParseAction(lambda x: '')
-        inputFrom = pyparsing.Word(pyparsing.printables)('inputFrom')
+        inputFrom = pyparsing.Word(self.legalChars)('inputFrom')
         inputFrom.setParseAction(lambda x: (x and open(x[0]).read()) or getPasteBuffer())
         self.inputParser = inputMark + pyparsing.Optional(inputFrom)
         self.inputParser.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).ignore(self.commentGrammars).ignore(self.commentInProgress)               
     
-    def parsed(self, raw):
+    def parsed(self, raw, useTerminatorFrom=None):
         s = self.inputParser.transformString(raw)
         for (shortcut, expansion) in self.shortcuts.items():
             if s.startswith(shortcut):
@@ -388,21 +390,18 @@
                 break
         result = self.parser.parseString(s)
         result['command'] = result.multilineCommand or result.command
-        result['statement'] = ' '.join(result.statement)
+        #result['statement'] = ' '.join(result.statement)
         result['raw'] = raw
-        result['clean'] = self.commentGrammars.transformString(result.statement)
-        result['cleanArgs'] = self.commentGrammars.transformString(result.args)
-        return result
-        
-    def extractCommand(self, statement):
-        try:
-            (command, args) = statement.split(None,1)
-        except ValueError:
-            (command, args) = statement, ''
-        if self.caseInsensitive:
-            command = command.lower()
-        return command, args
-       
+        #result['clean'] = self.commentGrammars.transformString(result.statement)
+        result['clean'] = self.commentGrammars.transformString(result.args)
+        result['expanded'] = s
+        if useTerminatorFrom:
+            result['terminator'] = useTerminatorFrom.parsed.terminator
+            result['suffix'] = useTerminatorFrom.parsed.suffix
+        p = ParsedString(result.args)
+        p.parsed = result
+        return p
+              
     def onecmd(self, line, assumeComplete=False):
         """Interpret the argument as though it had been typed in response
         to the prompt.
@@ -411,21 +410,23 @@
         see the precmd() and postcmd() methods for useful execution hooks.
         The return value is a flag indicating whether interpretation of
         commands by the interpreter should stop.
+        
+        This (`cmd2`) version of `onecmd` already override's `cmd`'s `onecmd`.
 
         """
         line = line.strip()
         if not line:
-            return
+            return self.emptyline()
         if not pyparsing.Or(self.commentGrammars).setParseAction(lambda x: '').transformString(line):
-            return
+            return 0
         try:
             statement = self.parsed(line)
             if assumeComplete:
-                if statement.multilineCommand and not statement.terminator:
-                    statement = self.parsed(statement.raw + self.terminators[0])
+                if statement.parsed.multilineCommand and not statement.parsed.terminator:
+                    statement.parsed.terminator = self.terminators[0]
             else:
-                while statement.multilineCommand and not statement.terminator:
-                    statement = self.parsed('%s\n%s' % (statement.raw, 
+                while statement.parsed.multilineCommand and not statement.parsed.terminator:
+                    statement = self.parsed('%s\n%s' % (statement.parsed.raw, 
                                             self.pseudo_raw_input(self.continuationPrompt)))
         except Exception, e:
             print e
@@ -434,42 +435,48 @@
         statekeeper = None
         stop = 0
 
-        if statement.pipeTo:
-            redirect = subprocess.Popen(statement.pipeTo, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
+        if statement.parsed.pipeTo:
+            redirect = subprocess.Popen(statement.parsed.pipeTo, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
             statekeeper = Statekeeper(self, ('stdout',))   
             self.stdout = redirect.stdin
-        elif statement.output:
+        elif statement.parsed.output:
             statekeeper = Statekeeper(self, ('stdout',))            
-            if statement.outputTo:
+            if statement.parsed.outputTo:
                 mode = 'w'
-                if statement.output == '>>':
+                if statement.parsed.output == '>>':
                     mode = 'a'
                 try:
-                    self.stdout = open(statement.outputTo, mode)                            
+                    self.stdout = open(statement.parsed.outputTo, mode)                            
                 except OSError, e:
                     print e
                     return 0                    
             else:
                 statekeeper = Statekeeper(self, ('stdout',))
                 self.stdout = tempfile.TemporaryFile()
-                if statement.output == '>>':
+                if statement.parsed.output == '>>':
                     self.stdout.write(getPasteBuffer())
         try:
-            stop = cmd.Cmd.onecmd(self, statement.clean)
+            # "heart" of the command, replace's cmd's onecmd()
+            self.lastcmd = statement.parsed.expanded
+            try:
+                func = getattr(self, 'do_' + statement.parsed.command)
+            except AttributeError:
+                return self.default(statement)
+            stop = func(statement)                        
         except Exception, e:
             print e
         try:
-            if statement.command not in self.excludeFromHistory:
-                self.history.append(statement.raw)
+            if statement.parsed.command not in self.excludeFromHistory:
+                self.history.append(statement.parsed.raw)
         finally:
             if statekeeper:
-                if statement.output and not statement.outputTo:
+                if statement.parsed.output and not statement.parsed.outputTo:
                     self.stdout.seek(0)
                     try:
                         writeToPasteBuffer(self.stdout.read())
                     except Exception, e:
                         print str(e)
-                elif statement.pipeTo:
+                elif statement.parsed.pipeTo:
                     for result in redirect.communicate():              
                         statekeeper.stdout.write(result or '')                        
                 self.stdout.close()
@@ -654,7 +661,7 @@
     do_edit = do_ed
     
     saveparser = (pyparsing.Optional(pyparsing.Word(pyparsing.nums)^'*')("idx") + 
-                  pyparsing.Optional(pyparsing.Word(pyparsing.printables))("fname") +
+                  pyparsing.Optional(pyparsing.Word(legalChars + '/\\'))("fname") +
                   pyparsing.stringEnd)    
     def do_save(self, arg):
         """`save [N] [filename.ext]`
@@ -905,4 +912,5 @@
             self.outputTrap.tearDown()
         
 if __name__ == '__main__':
-    doctest.testmod(optionflags = doctest.NORMALIZE_WHITESPACE)
+    #doctest.testmod(optionflags = doctest.NORMALIZE_WHITESPACE)
+    c = Cmd()