diff options
Diffstat (limited to 'asciidoc/attrs.py')
-rw-r--r-- | asciidoc/attrs.py | 77 |
1 files changed, 73 insertions, 4 deletions
diff --git a/asciidoc/attrs.py b/asciidoc/attrs.py index f61b62b..42a6aae 100644 --- a/asciidoc/attrs.py +++ b/asciidoc/attrs.py @@ -1,6 +1,7 @@ import re import typing +from . import get_compat_mode from .utils import get_args, get_kwargs @@ -15,16 +16,85 @@ def parse_attributes(attrs: str, output_dict: typing.Dict) -> None: output_dict: {} attrs: 'hello,world' - output_dict: {'2': 'world', '0': 'hello,world', '1': 'hello'} + output_dict: {'0': 'hello,world', '1': 'hello', '2': 'world',} attrs: '"hello", planet="earth"' - output_dict: {'planet': 'earth', '0': '"hello", planet="earth"', '1': 'hello'} + output_dict: {'0': '"hello", planet="earth"', '1': 'hello' 'planet': 'earth', } """ if not attrs: return output_dict['0'] = attrs # Replace line separators with spaces so line spanning works. s = re.sub(r'\s', ' ', attrs) + d = legacy_parse(s) if get_compat_mode() == 1 else future_parse(s) + output_dict.update(d) + assert len(d) > 0 + + +def future_parse(s: str) -> dict: + d = {} + key = '' + value = '' + count = 1 + quote = None + in_quotes = False + had_quotes = False + + def add_value(): + nonlocal count, d, key, value + key = key.strip() + value = value.strip() + if had_quotes: + value = value[1:-1] + + if not value and not had_quotes: + value = None + + if key: + d[key] = value if value else '' + key = '' + else: + d["{}".format(count)] = value + count += 1 + value = '' + + for i in range(len(s)): + char = s[i] + + if char == ',' and not in_quotes: + add_value() + had_quotes = False + elif value and char == '=' and not in_quotes: + key = value + value = '' + elif not in_quotes and (char == '"' or char == "'") \ + and (i == 0 or s[i - 1] != '\\'): + in_quotes = True + quote = char + value += char + elif in_quotes and char == quote and (i == 0 or s[i - 1] != '\\'): + in_quotes = False + had_quotes = True + quote = None + value += char + elif char == '\\' and i < len(s) - 1 and (s[i + 1] == '"' or s[i + 1] == "'"): + pass + else: + value += char + + if key and key[0] == '=' and not value: + value = key + "=" + key = "" + + if not value and s.rstrip()[-1] == ',': + value = ' ' + + if had_quotes or value or key: + add_value() + return d + + +def legacy_parse(s: str) -> dict: d = {} try: d.update(get_args(s)) @@ -47,5 +117,4 @@ def parse_attributes(attrs: str, output_dict: typing.Dict) -> None: for k in list(d.keys()): # Drop any empty positional arguments. if d[k] == '': del d[k] - output_dict.update(d) - assert len(d) > 0 + return d |