[commands] Refactor quoted_word free function to a StringView method.
Technically a breaking change, however this interface was not documented or guaranteed to exist.
This commit is contained in:
		| @@ -33,7 +33,6 @@ import discord | |||||||
|  |  | ||||||
| from .errors import * | from .errors import * | ||||||
| from .cooldowns import Cooldown, BucketType, CooldownMapping | from .cooldowns import Cooldown, BucketType, CooldownMapping | ||||||
| from .view import quoted_word |  | ||||||
| from . import converter as converters | from . import converter as converters | ||||||
| from ._types import _BaseCommand | from ._types import _BaseCommand | ||||||
| from .cog import Cog | from .cog import Cog | ||||||
| @@ -421,7 +420,7 @@ class Command(_BaseCommand): | |||||||
|         if consume_rest_is_special: |         if consume_rest_is_special: | ||||||
|             argument = view.read_rest().strip() |             argument = view.read_rest().strip() | ||||||
|         else: |         else: | ||||||
|             argument = quoted_word(view) |             argument = view.get_quoted_word() | ||||||
|         view.previous = previous |         view.previous = previous | ||||||
|  |  | ||||||
|         return await self.do_conversion(ctx, converter, argument, param) |         return await self.do_conversion(ctx, converter, argument, param) | ||||||
| @@ -434,7 +433,7 @@ class Command(_BaseCommand): | |||||||
|             previous = view.index |             previous = view.index | ||||||
|  |  | ||||||
|             view.skip_ws() |             view.skip_ws() | ||||||
|             argument = quoted_word(view) |             argument = view.get_quoted_word() | ||||||
|             try: |             try: | ||||||
|                 value = await self.do_conversion(ctx, converter, argument, param) |                 value = await self.do_conversion(ctx, converter, argument, param) | ||||||
|             except CommandError: |             except CommandError: | ||||||
| @@ -450,7 +449,7 @@ class Command(_BaseCommand): | |||||||
|     async def _transform_greedy_var_pos(self, ctx, param, converter): |     async def _transform_greedy_var_pos(self, ctx, param, converter): | ||||||
|         view = ctx.view |         view = ctx.view | ||||||
|         previous = view.index |         previous = view.index | ||||||
|         argument = quoted_word(view) |         argument = view.get_quoted_word() | ||||||
|         try: |         try: | ||||||
|             value = await self.do_conversion(ctx, converter, argument, param) |             value = await self.do_conversion(ctx, converter, argument, param) | ||||||
|         except CommandError: |         except CommandError: | ||||||
|   | |||||||
| @@ -26,6 +26,28 @@ DEALINGS IN THE SOFTWARE. | |||||||
|  |  | ||||||
| from .errors import UnexpectedQuoteError, InvalidEndOfQuotedStringError, ExpectedClosingQuoteError | from .errors import UnexpectedQuoteError, InvalidEndOfQuotedStringError, ExpectedClosingQuoteError | ||||||
|  |  | ||||||
|  | # map from opening quotes to closing quotes | ||||||
|  | _quotes = { | ||||||
|  |     '"': '"', | ||||||
|  |     "‘": "’", | ||||||
|  |     "‚": "‛", | ||||||
|  |     "“": "”", | ||||||
|  |     "„": "‟", | ||||||
|  |     "⹂": "⹂", | ||||||
|  |     "「": "」", | ||||||
|  |     "『": "』", | ||||||
|  |     "〝": "〞", | ||||||
|  |     "﹁": "﹂", | ||||||
|  |     "﹃": "﹄", | ||||||
|  |     """: """, | ||||||
|  |     "「": "」", | ||||||
|  |     "«": "»", | ||||||
|  |     "‹": "›", | ||||||
|  |     "《": "》", | ||||||
|  |     "〈": "〉", | ||||||
|  | } | ||||||
|  | _all_quotes = set(_quotes.keys()) | set(_quotes.values()) | ||||||
|  |  | ||||||
| class StringView: | class StringView: | ||||||
|     def __init__(self, buffer): |     def __init__(self, buffer): | ||||||
|         self.index = 0 |         self.index = 0 | ||||||
| @@ -104,93 +126,69 @@ class StringView: | |||||||
|         self.index += pos |         self.index += pos | ||||||
|         return result |         return result | ||||||
|  |  | ||||||
|     def __repr__(self): |     def get_quoted_word(self): | ||||||
|         return '<StringView pos: {0.index} prev: {0.previous} end: {0.end} eof: {0.eof}>'.format(self) |         current = self.current | ||||||
|  |         if current is None: | ||||||
|  |             return None | ||||||
|  |  | ||||||
| # Parser |         close_quote = _quotes.get(current) | ||||||
|  |         is_quoted = bool(close_quote) | ||||||
|  |         if is_quoted: | ||||||
|  |             result = [] | ||||||
|  |             _escaped_quotes = (current, close_quote) | ||||||
|  |         else: | ||||||
|  |             result = [current] | ||||||
|  |             _escaped_quotes = _all_quotes | ||||||
|  |  | ||||||
| # map from opening quotes to closing quotes |         while not self.eof: | ||||||
| _quotes = { |             current = self.get() | ||||||
|     '"': '"', |             if not current: | ||||||
|     "‘": "’", |  | ||||||
|     "‚": "‛", |  | ||||||
|     "“": "”", |  | ||||||
|     "„": "‟", |  | ||||||
|     "⹂": "⹂", |  | ||||||
|     "「": "」", |  | ||||||
|     "『": "』", |  | ||||||
|     "〝": "〞", |  | ||||||
|     "﹁": "﹂", |  | ||||||
|     "﹃": "﹄", |  | ||||||
|     """: """, |  | ||||||
|     "「": "」", |  | ||||||
|     "«": "»", |  | ||||||
|     "‹": "›", |  | ||||||
|     "《": "》", |  | ||||||
|     "〈": "〉", |  | ||||||
| } |  | ||||||
| _all_quotes = set(_quotes.keys()) | set(_quotes.values()) |  | ||||||
|  |  | ||||||
| def quoted_word(view): |  | ||||||
|     current = view.current |  | ||||||
|  |  | ||||||
|     if current is None: |  | ||||||
|         return None |  | ||||||
|  |  | ||||||
|     close_quote = _quotes.get(current) |  | ||||||
|     is_quoted = bool(close_quote) |  | ||||||
|     if is_quoted: |  | ||||||
|         result = [] |  | ||||||
|         _escaped_quotes = (current, close_quote) |  | ||||||
|     else: |  | ||||||
|         result = [current] |  | ||||||
|         _escaped_quotes = _all_quotes |  | ||||||
|  |  | ||||||
|     while not view.eof: |  | ||||||
|         current = view.get() |  | ||||||
|         if not current: |  | ||||||
|             if is_quoted: |  | ||||||
|                 # unexpected EOF |  | ||||||
|                 raise ExpectedClosingQuoteError(close_quote) |  | ||||||
|             return ''.join(result) |  | ||||||
|  |  | ||||||
|         # currently we accept strings in the format of "hello world" |  | ||||||
|         # to embed a quote inside the string you must escape it: "a \"world\"" |  | ||||||
|         if current == '\\': |  | ||||||
|             next_char = view.get() |  | ||||||
|             if not next_char: |  | ||||||
|                 # string ends with \ and no character after it |  | ||||||
|                 if is_quoted: |                 if is_quoted: | ||||||
|                     # if we're quoted then we're expecting a closing quote |                     # unexpected EOF | ||||||
|                     raise ExpectedClosingQuoteError(close_quote) |                     raise ExpectedClosingQuoteError(close_quote) | ||||||
|                 # if we aren't then we just let it through |  | ||||||
|                 return ''.join(result) |                 return ''.join(result) | ||||||
|  |  | ||||||
|             if next_char in _escaped_quotes: |             # currently we accept strings in the format of "hello world" | ||||||
|                 # escaped quote |             # to embed a quote inside the string you must escape it: "a \"world\"" | ||||||
|                 result.append(next_char) |             if current == '\\': | ||||||
|             else: |                 next_char = self.get() | ||||||
|                 # different escape character, ignore it |                 if not next_char: | ||||||
|                 view.undo() |                     # string ends with \ and no character after it | ||||||
|                 result.append(current) |                     if is_quoted: | ||||||
|             continue |                         # if we're quoted then we're expecting a closing quote | ||||||
|  |                         raise ExpectedClosingQuoteError(close_quote) | ||||||
|  |                     # if we aren't then we just let it through | ||||||
|  |                     return ''.join(result) | ||||||
|  |  | ||||||
|         if not is_quoted and current in _all_quotes: |                 if next_char in _escaped_quotes: | ||||||
|             # we aren't quoted |                     # escaped quote | ||||||
|             raise UnexpectedQuoteError(current) |                     result.append(next_char) | ||||||
|  |                 else: | ||||||
|  |                     # different escape character, ignore it | ||||||
|  |                     self.undo() | ||||||
|  |                     result.append(current) | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|         # closing quote |             if not is_quoted and current in _all_quotes: | ||||||
|         if is_quoted and current == close_quote: |                 # we aren't quoted | ||||||
|             next_char = view.get() |                 raise UnexpectedQuoteError(current) | ||||||
|             valid_eof = not next_char or next_char.isspace() |  | ||||||
|             if not valid_eof: |  | ||||||
|                 raise InvalidEndOfQuotedStringError(next_char) |  | ||||||
|  |  | ||||||
|             # we're quoted so it's okay |             # closing quote | ||||||
|             return ''.join(result) |             if is_quoted and current == close_quote: | ||||||
|  |                 next_char = self.get() | ||||||
|  |                 valid_eof = not next_char or next_char.isspace() | ||||||
|  |                 if not valid_eof: | ||||||
|  |                     raise InvalidEndOfQuotedStringError(next_char) | ||||||
|  |  | ||||||
|         if current.isspace() and not is_quoted: |                 # we're quoted so it's okay | ||||||
|             # end of word found |                 return ''.join(result) | ||||||
|             return ''.join(result) |  | ||||||
|  |  | ||||||
|         result.append(current) |             if current.isspace() and not is_quoted: | ||||||
|  |                 # end of word found | ||||||
|  |                 return ''.join(result) | ||||||
|  |  | ||||||
|  |             result.append(current) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     def __repr__(self): | ||||||
|  |         return '<StringView pos: {0.index} prev: {0.previous} end: {0.end} eof: {0.eof}>'.format(self) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user