diff options
-rwxr-xr-x | cmd2/cmd2.py | 46 | ||||
-rw-r--r-- | cmd2/parsing.py | 67 | ||||
-rw-r--r-- | tests/test_shlexparsing.py | 2 |
3 files changed, 71 insertions, 44 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 3dc0cb5f..4e2c053f 100755 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -386,20 +386,21 @@ def write_to_paste_buffer(txt: str) -> None: pyperclip.copy(txt) -class ParsedString(str): - """Subclass of str which also stores a pyparsing.ParseResults object containing structured parse results.""" - # pyarsing.ParseResults - structured parse results, to provide multiple means of access to the parsed data - parsed = None +# deleteme +# class ParsedString(str): +# """Subclass of str which also stores a pyparsing.ParseResults object containing structured parse results.""" +# # pyarsing.ParseResults - structured parse results, to provide multiple means of access to the parsed data +# parsed = None - # Function which did the parsing - parser = None +# # Function which did the parsing +# parser = None - def full_parsed_statement(self): - """Used to reconstruct the full parsed statement when a command isn't recognized.""" - new = ParsedString('%s %s' % (self.parsed.command, self.parsed.args)) - new.parsed = self.parsed - new.parser = self.parser - return new +# def full_parsed_statement(self): +# """Used to reconstruct the full parsed statement when a command isn't recognized.""" +# new = ParsedString('%s %s' % (self.parsed.command, self.parsed.args)) +# new.parsed = self.parsed +# new.parser = self.parser +# return new def replace_with_file_contents(fname: str) -> str: @@ -2010,8 +2011,8 @@ class Cmd(cmd.Cmd): def precmd(self, statement): """Hook method executed just before the command is processed by ``onecmd()`` and after adding it to the history. - :param statement: ParsedString - subclass of str which also contains pyparsing ParseResults instance - :return: ParsedString - a potentially modified version of the input ParsedString statement + :param statement: Statement - subclass of str which also contains the parsed input + :return: Statement - a potentially modified version of the input Statement object """ return statement @@ -2027,11 +2028,11 @@ class Cmd(cmd.Cmd): return raw # noinspection PyMethodMayBeStatic - def postparse(self, parse_result): - """Hook that runs immediately after parsing the command-line but before ``parsed()`` returns a ParsedString. + def postparse(self, statement): + """Hook that runs immediately after parsing the user input. - :param parse_result: pyparsing.ParseResults - parsing results output by the pyparsing parser - :return: pyparsing.ParseResults - potentially modified ParseResults object + :param statement: Statement object populated by parsing + :return: Statement - potentially modified Statement object """ return parse_result @@ -2048,8 +2049,8 @@ class Cmd(cmd.Cmd): - raise EmptyStatement - will silently fail and do nothing - raise <AnyOtherException> - will fail and print an error message - :param statement: - the parsed command-line statement - :return: (bool, statement) - (stop, statement) containing a potentially modified version of the statement + :param statement: - the parsed command-line statement as a Statement object + :return: (bool, statement) - (stop, statement) containing a potentially modified version of the statement object """ stop = False return stop, statement @@ -2325,10 +2326,9 @@ class Cmd(cmd.Cmd): If the command provided doesn't exist, then it executes _default() instead. - :param line: Command - a parsed command from the input stream + :param statement: Command - a parsed command from the input stream :return: bool - a flag indicating whether the interpretation of commands should stop """ - #statement = self.parser_manager.parsed(line) # deleteme funcname = self._func_named(statement.command) if not funcname: return self.default(statement) @@ -2342,7 +2342,7 @@ class Cmd(cmd.Cmd): except AttributeError: return self.default(statement) - stop = func("{} {}".format(statement.command, statement.args)) + stop = func(statement) return stop def default(self, statement): diff --git a/cmd2/parsing.py b/cmd2/parsing.py index 41ce5743..164c7735 100644 --- a/cmd2/parsing.py +++ b/cmd2/parsing.py @@ -7,10 +7,19 @@ import shlex import cmd2 -class Command(): - """Store the results of a parsed command.""" - def __init__(self, rawinput): - self.raw = rawinput +class Statement(str): + """String subclass with additional attributes to store the results of parsing. + + The cmd module in the standard library passes commands around as a + string. To retain backwards compatibility, cmd2 does the same. However, we + need a place to capture the additional output of the command parsing, so we add + our own attributes to this subclass. + + The string portion of the class contains the arguments, but not the command, nor + the output redirection clauses. + """ + def __init__(self, object): + self.raw = str(object) self.command = None self.multilineCommand = None self.args = None @@ -37,7 +46,7 @@ class CommandParser(): self.multilineCommands = multilineCommands def parseString(self, rawinput): - result = Command(rawinput) + #result = Statement(rawinput) # strip C-style and C++-style comments # shlex will handle the python/shell style comments for us @@ -67,6 +76,7 @@ class CommandParser(): if pos < terminator_pos: terminator_pos = pos terminator = test_terminator + break except ValueError: # the terminator is not in the tokens pass @@ -74,35 +84,37 @@ class CommandParser(): if terminator: terminator_pos = tokens.index(terminator) # everything before the first terminator is the command and the args - (result.command, result.args) = self._command_and_args(tokens[:terminator_pos]) - result.terminator = tokens[terminator_pos] + (command, args) = self._command_and_args(tokens[:terminator_pos]) + #terminator = tokens[terminator_pos] # we will set the suffix later # remove all the tokens before and including the terminator tokens = tokens[terminator_pos+1:] # check for input from file + inputFrom = None try: if tokens[0] == '<': - result.inputFrom = ' '.join(tokens[1:]) + inputFrom = ' '.join(tokens[1:]) tokens = [] except IndexError: - # no input from file pass + # check for output redirect try: output_pos = tokens.index('>') - result.output = '>' - result.outputTo = ' '.join(tokens[output_pos+1:]) + output = '>' + outputTo = ' '.join(tokens[output_pos+1:]) # remove all the tokens after the output redirect tokens = tokens[:output_pos] except ValueError: - pass + output = None + outputTo = None # check for paste buffer try: output_pos = tokens.index('>>') - result.output = '>>' + output = '>>' # remove all tokens after the output redirect tokens = tokens[:output_pos] except ValueError: @@ -113,23 +125,36 @@ class CommandParser(): # find the first pipe if it exists pipe_pos = tokens.index('|') # set everything after the first pipe to result.pipeTo - result.pipeTo = ' '.join(tokens[pipe_pos+1:]) + pipeTo = ' '.join(tokens[pipe_pos+1:]) # remove all the tokens after the pipe tokens = tokens[:pipe_pos] except ValueError: # no pipe in the tokens - pass + pipeTo = None - if result.terminator: + if terminator: # whatever is left is the suffix - result.suffix = ' '.join(tokens) + suffix = ' '.join(tokens) else: # no terminator, so whatever is left is the command and the args - (result.command, result.args) = self._command_and_args(tokens) - - if result.command in self.multilineCommands: - result.multilineCommand = result.command + suffix = None + (command, args) = self._command_and_args(tokens) + if command in self.multilineCommands: + multilineCommand = command + else: + multilineCommand = None + + result = Statement(args) + result.command = command + result.args = args + result.terminator = terminator + result.inputFrom = inputFrom + result.output = output + result.outputTo = outputTo + result.pipeTo = pipeTo + result.suffix = suffix + result.multilineCommand = multilineCommand return result def _command_and_args(self, tokens): diff --git a/tests/test_shlexparsing.py b/tests/test_shlexparsing.py index 5d3c9546..9142e178 100644 --- a/tests/test_shlexparsing.py +++ b/tests/test_shlexparsing.py @@ -5,6 +5,8 @@ Unit/functional testing for ply based parsing in cmd2 Todo List - multiline - case sensitive flag +- checkout Cmd2.parseline() function which parses and expands shortcuts and such + this code should probably be included in CommandParser Notes: |