diff --git a/tests/test_vdf.py b/tests/test_vdf.py index f041578..fabc361 100644 --- a/tests/test_vdf.py +++ b/tests/test_vdf.py @@ -319,16 +319,27 @@ def test_hash_key(self): self.assertEqual(vdf.loads(INPUT, escaped=False), EXPECTED) def test_wierd_symbols_for_unquoted(self): - INPUT = 'a asd.vdf\nb language_*lol*\nc zxc_-*.sss//' + INPUT = 'a asd.vdf\nb language_*lol*\nc zxc_-*.sss//\nd<2?$% /cde/$fgh/' EXPECTED = { 'a': 'asd.vdf', 'b': 'language_*lol*', 'c': 'zxc_-*.sss', + 'd<2?$%': '/cde/$fgh/', } self.assertEqual(vdf.loads(INPUT), EXPECTED) self.assertEqual(vdf.loads(INPUT, escaped=False), EXPECTED) + def test_space_for_unquoted(self): + INPUT = 'a b c d \n efg h i\t // j k\n' + EXPECTED= { + 'a': 'b c d', + 'efg': 'h i', + } + + self.assertEqual(vdf.loads(INPUT), EXPECTED) + self.assertEqual(vdf.loads(INPUT, escaped=False), EXPECTED) + def test_merge_multiple_keys_on(self): INPUT = ''' a @@ -393,6 +404,74 @@ def test_escape_before_last_unescaped(self): self.assertEqual(vdf.loads(INPUT, escaped=False), EXPECTED) + def test_single_line_empty_block(self): + INPUT = ''' + "root1" + { + "key1" {} + key2 "value2" + "key3" value3 + } + root2 { } + root3 + { + "key1" "value1" + key2 { } + "key3" value3 + } + ''' + + EXPECTED = { + 'root1': { + 'key1': {}, + 'key2': 'value2', + 'key3': 'value3', + }, + 'root2': {}, + 'root3': { + 'key1': 'value1', + 'key2': {}, + 'key3': 'value3', + } + } + + self.assertEqual(vdf.loads(INPUT), EXPECTED) + self.assertEqual(vdf.loads(INPUT, escaped=False), EXPECTED) + + def test_inline_opening_bracker(self): + INPUT = ''' + "root1" { + "key1" { + } + key2 "value2" + "key3" value3 + } + root2 { } + root3 { + "key1" "value1" + key2 { + + } + "key3" value3 + } + ''' + + EXPECTED = { + 'root1': { + 'key1': {}, + 'key2': 'value2', + 'key3': 'value3', + }, + 'root2': {}, + 'root3': { + 'key1': 'value1', + 'key2': {}, + 'key3': 'value3', + } + } + + self.assertEqual(vdf.loads(INPUT), EXPECTED) + self.assertEqual(vdf.loads(INPUT, escaped=False), EXPECTED) class testcase_VDF_other(unittest.TestCase): def test_dumps_pretty_output(self): diff --git a/vdf/__init__.py b/vdf/__init__.py index 7bcd11c..a1c523a 100644 --- a/vdf/__init__.py +++ b/vdf/__init__.py @@ -86,10 +86,11 @@ def parse(fp, mapper=dict, merge_duplicate_keys=True, escaped=True): stack = [mapper()] expect_bracket = False - re_keyvalue = re.compile(r'^("(?P(?:\\.|[^\\"])+)"|(?P#?[a-z0-9\-\_\\\?]+))' + re_keyvalue = re.compile(r'^("(?P(?:\\.|[^\\"])*)"|(?P#?[a-z0-9\-\_\\\?$%<>]+))' r'([ \t]*(' r'"(?P(?:\\.|[^\\"])*)(?P")?' - r'|(?P[a-z0-9\-\_\\\?\*\.]+)' + r'|(?P(?:(? ])+)' + r'|(?P{[ \t]*)(?P})?' r'))?', flags=re.I) @@ -134,7 +135,13 @@ def parse(fp, mapper=dict, merge_duplicate_keys=True, escaped=True): (getattr(fp, 'name', '<%s>' % fp.__class__.__name__), lineno, 0, line)) key = match.group('key') if match.group('qkey') is None else match.group('qkey') - val = match.group('val') if match.group('qval') is None else match.group('qval') + val = match.group('qval') + if val is None: + val = match.group('val') + if val is not None: + val = val.rstrip() + if val == "": + val = None if escaped: key = _unescape(key) @@ -147,8 +154,11 @@ def parse(fp, mapper=dict, merge_duplicate_keys=True, escaped=True): _m = mapper() stack[-1][key] = _m - stack.append(_m) - expect_bracket = True + if match.group('eblock') is None: + # only expect a bracket if it's not already closed or on the same line + stack.append(_m) + if match.group('sblock') is None: + expect_bracket = True # we've matched a simple keyvalue pair, map it to the last dict obj in the stack else: