Started implementing object serialization
authorDavid Kerkeslager <kerkeslager@gmail.com>
Sun, 9 Oct 2016 23:21:17 +0000 (19:21 -0400)
committerDavid Kerkeslager <kerkeslager@gmail.com>
Sun, 9 Oct 2016 23:21:17 +0000 (19:21 -0400)
serial/serial/binary.py
serial/serial/tags.py
serial/serial/text.py
serial/test.py

index d19a221..dd1b990 100644 (file)
@@ -61,6 +61,9 @@ def _serialize_list(to):
 
     return struct.pack(fmt, tags.LIST, list_tag, len(payload)) + payload
 
+def _serialize_object(to):
+    raise Exception('Not implemented')
+
 _TAGS_TO_SERIALIZERS = {
     tags.NULL: _make_tag_only_serializer(tags.NULL, None),
     tags.TRUE: _make_tag_only_serializer(tags.TRUE, True),
@@ -79,6 +82,7 @@ _TAGS_TO_SERIALIZERS = {
     tags.UTF32: _make_string_serializer(lambda s: s.encode('utf-32')),
     tags.TUPLE: _serialize_tuple,
     tags.LIST: _serialize_list,
+    tags.OBJECT: _serialize_object,
 }
 
 def serialize(to):
@@ -160,6 +164,9 @@ def _deserialize_list(b):
     # TODO Return tags = (tags.LIST, list_tag) to function like a generic type
     return bytes_read, tags.TaggedObject(tag = tags.LIST, instance = instance)
 
+def _deserialize_object(b):
+    raise Exception('Not implemented')
+
 _TAGS_TO_PARSERS = {
     tags.NULL: _make_tag_only_parser(tags.NULL, None),
     tags.TRUE: _make_tag_only_parser(tags.TRUE, True),
@@ -178,6 +185,7 @@ _TAGS_TO_PARSERS = {
     tags.UTF32: _make_string_deserializer(tags.UTF32, lambda b: b.decode('utf-32')),
     tags.TUPLE: _deserialize_tuple,
     tags.LIST: _deserialize_list,
+    tags.OBJECT: _deserialize_object,
 }
 
 def _deserialize_partial(b):
index b1edfb7..4136c77 100644 (file)
@@ -17,6 +17,7 @@ UTF16 = 0x22
 UTF32 = 0x23
 TUPLE = 0x30
 LIST = 0x31
+OBJECT = 0x32
 
 TaggedObject = collections.namedtuple(
     'TaggedObject',
index 2d957d6..2746ba1 100644 (file)
@@ -54,6 +54,20 @@ def _serialize_list(to):
 
     return '[\n' + _indent(',\n'.join(serialize(i) for i in to.instance)) + '\n]'
 
+def _serialize_key_value_pair(kvp):
+    assert isinstance(kvp, tuple)
+    key, value = kvp
+
+    assert key.tag in [tags.UTF8, tags.UTF16, tags.UTF32]
+
+    return '{}: {}'.format(serialize(key), serialize(value))
+
+def _serialize_object(to):
+    assert isinstance(to.instance, list)
+
+    return '{\n' + _indent(',\n'.join(_serialize_key_value_pair(kvp) for kvp in to.instance)) + '\n}'
+    raise Exception('Not implemented')
+
 _SERIALIZERS = {
     tags.NULL: _make_literal_serializer(None, 'null'),
     tags.TRUE: _make_literal_serializer(True, 'true'),
@@ -71,6 +85,7 @@ _SERIALIZERS = {
     tags.UTF16: _make_string_serializer('utf16'),
     tags.UTF32: _make_string_serializer('utf32'),
     tags.LIST: _serialize_list,
+    tags.OBJECT: _serialize_object,
 }
 
 def serialize(to):
@@ -181,8 +196,6 @@ def _make_string_deserializer(tag, prefix):
     return deserializer
 
 def _deserialize_list(s):
-    s = s.lstrip()
-
     if not s.startswith('['):
         return False, None, None
 
@@ -213,6 +226,54 @@ def _deserialize_list(s):
     assert s.startswith(']')
     return True, tags.TaggedObject(tag = tags.LIST, instance = instance), s[1:]
 
+def _deserialize_object(s):
+    if not s.startswith('{'):
+        return False, None, None
+
+    instance = []
+
+    s = s[1:].lstrip()
+
+    # TODO Handle empty objects
+
+    succeeded, key, s = _deserialize_one(s)
+
+    assert succeeded
+    assert key.tag in [tags.UTF8, tags.UTF16, tags.UTF32]
+
+    s = s.lstrip()
+    assert s.startswith(':')
+
+    s = s[1:].lstrip()
+
+    succeeded, value, s = _deserialize_one(s)
+
+    assert succeeded
+    instance.append((key, value))
+
+    s = s.lstrip()
+
+    while s.startswith(','):
+        succeeded, key, s = _deserialize_one(s)
+
+        assert succeeded
+        assert key.tag in [tags.UTF8, tags.UTF16, tags.UTF32]
+
+        s = s.lstrip()
+        assert s.startswith(':')
+
+        s = s[1:].lstrip()
+
+        succeeded, value, s = _deserialize_one(s)
+
+        assert succeeded
+        instance.append((key, value))
+
+        s = s.lstrip()
+
+    assert s.startswith('}')
+    return True, tags.TaggedObject(tag = tags.LIST, instance = instance), s[1:]
+
 _DESERIALIZERS = [
     _make_literal_deserializer(tags.NULL, None, 'null'),
     _make_literal_deserializer(tags.TRUE, True, 'true'),
@@ -230,9 +291,12 @@ _DESERIALIZERS = [
     _make_string_deserializer(tags.UTF16, 'utf16'),
     _make_string_deserializer(tags.UTF32, 'utf32'),
     _deserialize_list,
+    _deserialize_object,
 ]
 
 def _deserialize_one(s):
+    s = s.lstrip()
+
     for deserializer in _DESERIALIZERS:
         succeeded, result, remaining = deserializer(s)
 
index 239c694..72c7b35 100644 (file)
@@ -53,6 +53,22 @@ EXAMPLE_BINARY_REPRESENTATIONS = [
         ),
         b'\x31\x03\x00\x00\x00\x03\x09\x16\x24',
     ),
+    (
+        tags.TaggedObject(
+            tag = tags.OBJECT,
+            instance = [
+                (
+                    tags.TaggedObject(tag = tags.UTF8, instance = 'foo'),
+                    tags.TaggedObject(tag = tags.UTF8, instance = 'bar'),
+                ),
+                (
+                    tags.TaggedObject(tag = tags.UTF8, instance = 'baz'),
+                    tags.TaggedObject(tag = tags.UINT8, instance = 42),
+                ),
+            ],
+        ),
+        b'\x32\x21\x00\x00\x00\x03foo\x21\x00\x00\x00\x03bar\x00\x00\x00\x03baz\x03\x2a',
+    ),
 ]
 
 class BinarySerializeTests(unittest.TestCase):
@@ -98,6 +114,22 @@ EXAMPLE_TEXT_REPRESENTATIONS = [
         ),
         '[\n  9u8,\n  22u8,\n  36u8\n]'
     ),
+    (
+        tags.TaggedObject(
+            tag = tags.OBJECT,
+            instance = [
+                (
+                    tags.TaggedObject(tag = tags.UTF8, instance = 'foo'),
+                    tags.TaggedObject(tag = tags.UTF8, instance = 'bar'),
+                ),
+                (
+                    tags.TaggedObject(tag = tags.UTF8, instance = 'baz'),
+                    tags.TaggedObject(tag = tags.UINT8, instance = 42),
+                ),
+            ],
+        ),
+        '{\n  utf8"foo": utf8"bar",\n  utf8"baz": 42u8\n}',
+    ),
 ]
 
 class TextSerializeTests(unittest.TestCase):