diff options
Diffstat (limited to 'cmd2/parsing.py')
-rw-r--r-- | cmd2/parsing.py | 73 |
1 files changed, 64 insertions, 9 deletions
diff --git a/cmd2/parsing.py b/cmd2/parsing.py index 1d22ccb8..82e8ee39 100644 --- a/cmd2/parsing.py +++ b/cmd2/parsing.py @@ -12,6 +12,51 @@ import attr from . import constants from . import utils +# Pattern used to find normal argument +# Match strings like: {5}, {{{{{4}, {2}}}}} +macro_normal_arg_pattern = re.compile(r'(?<!\{)\{\d+\}|\{\d+\}(?!\})') + +# Pattern used to find escaped arguments (2 or more braces on each side of digit) +# Match strings like: {{5}}, {{{{{4}}, {{2}}}}}, {{{4}}} +macro_escaped_arg_pattern = re.compile(r'\{{2}\d+\}{2}') + +# Finds a string of digits +digit_pattern = re.compile(r'\d+') + + +@attr.s(frozen=True) +class MacroArg: + """ + Information used to replace or unescape arguments in a macro value when the macro is resolved + Normal argument syntax : {5} + Escaped argument syntax: {{5}} + """ + # The starting index of this argument in the macro value + start_index = attr.ib(validator=attr.validators.instance_of(int), type=int) + + # The number that appears between the braces + number = attr.ib(validator=attr.validators.instance_of(int), type=int) + + # Tells if this argument is escaped and therefore needs to be unescaped + is_escaped = attr.ib(validator=attr.validators.instance_of(bool), type=bool) + + +@attr.s(frozen=True) +class Macro: + """Defines a cmd2 macro""" + + # Name of the macro + name = attr.ib(validator=attr.validators.instance_of(str), type=str) + + # The string the macro resolves to + value = attr.ib(validator=attr.validators.instance_of(str), type=str) + + # The required number of args the user has to pass to this macro + required_arg_count = attr.ib(validator=attr.validators.instance_of(int), type=int) + + # Used to fill in argument placeholders in the macro + arg_list = attr.ib(factory=list, validator=attr.validators.instance_of(list), type=List[MacroArg]) + @attr.s(frozen=True) class Statement(str): @@ -246,24 +291,34 @@ class StatementParser: expr = r'\A\s*(\S*?)({})'.format(second_group) self._command_pattern = re.compile(expr) - def is_valid_command(self, word: str) -> Tuple[bool, str]: - """Determine whether a word is a valid alias. + def is_valid_command(self, word: str, allow_shortcut: bool) -> Tuple[bool, str]: + """Determine whether a word is a valid name for a command. - Aliases can not include redirection characters, whitespace, - or termination characters. + Commands can not include redirection characters, whitespace, + or termination characters. They also cannot start with a + shortcut. - If word is not a valid command, return False and a comma - separated string of characters that can not appear in a command. + If word is not a valid command, return False and error text This string is suitable for inclusion in an error message of your choice: - valid, invalidchars = statement_parser.is_valid_command('>') + valid, errmsg = statement_parser.is_valid_command('>') if not valid: - errmsg = "Aliases can not contain: {}".format(invalidchars) + errmsg = "Aliases {}".format(errmsg) """ valid = False - errmsg = 'whitespace, quotes, ' + if not word: + return False, 'cannot be an empty string' + + if not allow_shortcut: + errmsg = 'cannot start with a shortcut: ' + errmsg += ', '.join(shortcut for (shortcut, expansion) in self.shortcuts) + for (shortcut, expansion) in self.shortcuts: + if word.startswith(shortcut): + return False, errmsg + + errmsg = 'cannot contain: whitespace, quotes, ' errchars = [] errchars.extend(constants.REDIRECTION_CHARS) errchars.extend(self.terminators) |