Implemented dictionary parsing
authorDavid Kerkeslager <kerkeslager@gmail.com>
Fri, 14 Apr 2017 15:21:26 +0000 (11:21 -0400)
committerDavid Kerkeslager <kerkeslager@gmail.com>
Fri, 14 Apr 2017 15:21:26 +0000 (11:21 -0400)
don/string.py
test_don.py

index 53551df..e22b328 100644 (file)
@@ -1,4 +1,5 @@
 import binascii
+import collections
 import re
 
 from don import tags, _shared
@@ -166,35 +167,76 @@ def _prefix_with_comma(parser):
 
     return wrapped
 
-def _list_parser(s):
-    # TODO Assert they are all the same type
-    if not s.startswith('['):
-        return _shared._FAILED_PARSE_RESULT
-    s = s[1:]
+def _comma_separate_and_wrap(wrapped_parser, start_wrap, end_wrap, typecaster):
+    parser_prefixed_with_comma = _prefix_with_comma(wrapped_parser)
+
+    def parser(s):
+        if s.startswith(start_wrap):
+            s = s[1:]
+        else:
+            return _shared._FAILED_PARSE_RESULT
+
+        value = []
+        first = True
+
+        parse_result = wrapped_parser(s)
+
+        while parse_result.success:
+            value.append(parse_result.value)
+            s = parse_result.remaining
+            parse_result = parser_prefixed_with_comma(s)
+
+        if s.startswith(end_wrap):
+            s = s[1:]
+        else:
+            return _shared._FAILED_PARSE_RESULT
+
+        return _shared.ParseResult(
+            success = True,
+            value = typecaster(value),
+            remaining = s,
+        )
+
+    return parser
+
+# This uses _PARSERS which has not been defined yet, but is defined here so it can be used in
+# the definition of _list_parser
+def _object_parser(source):
+    for parser in _PARSERS:
+        result = parser(source)
 
-    value = []
+        if result.success:
+            return result
+
+    return _shared._FAILED_PARSE_RESULT
+
+_list_parser = _comma_separate_and_wrap(_object_parser, '[', ']', list)
 
-    first = True
-    parse_result = _object_parser(s)
+def _kvp_parser(s):
+    key_parse_result = _object_parser(s)
+    if key_parse_result.success:
+        s = key_parse_result.remaining
+    else:
+        return _shared._FAILED_PARSE_RESULT
 
-    while parse_result.success:
-        value.append(parse_result.value)
-        s = parse_result.remaining
-        parse_result = _prefix_with_comma(_object_parser)(s)
+    if s.startswith(':'):
+        s = s[1:]
+    else:
+        return _shared._FAILED_PARSE_RESULT
 
-    if not s.startswith(']'):
+    value_parse_result = _object_parser(s)
+    if value_parse_result.success:
+        s = value_parse_result.remaining
+    else:
         return _shared._FAILED_PARSE_RESULT
 
     return _shared.ParseResult(
         success = True,
-        value = value,
-        remaining = s[1:],
+        value = (key_parse_result.value, value_parse_result.value),
+        remaining = s,
     )
 
-
-
-def _dictionary_parser(s):
-    return _shared._FAILED_PARSE_RESULT
+_dictionary_parser = _comma_separate_and_wrap(_kvp_parser, '{', '}', collections.OrderedDict)
 
 
 _PARSERS = [
@@ -215,15 +257,6 @@ _PARSERS = [
     _dictionary_parser,
 ]
 
-def _object_parser(source):
-    for parser in _PARSERS:
-        result = parser(source)
-
-        if result.success:
-            return result
-
-    return _shared._FAILED_PARSE_RESULT
-
 def _parse(parser, source):
     result = parser(source)
 
index fe46bbc..c4e9549 100644 (file)
@@ -244,4 +244,13 @@ class TestStringDeserialize(unittest.TestCase):
             string.deserialize("[1i8,2i8,3i8,4i8,5i8]"),
         )
 
+    def test_deserializes_dictionary(self):
+        self.assertEqual(
+            collections.OrderedDict([
+                ('foo', 1),
+                ('bar', 'baz'),
+            ]),
+            string.deserialize('{"foo"utf8:1i32,"bar"utf8:"baz"utf8}'),
+        )
+
 unittest.main()