changeset 89:102043902451

Simple (subset of) YAML parser
author Stefano Rivera <stefano@rivera.za.net>
date Mon, 02 Sep 2013 00:55:59 +0200
parents 62dc3f92d5c3
children a8d83de5b460
files nagslang/level_serializer.py nagslang/tests/test_level_serializer.py
diffstat 2 files changed, 128 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nagslang/level_serializer.py	Mon Sep 02 00:55:59 2013 +0200
@@ -0,0 +1,81 @@
+'''
+Serializer and dumper for a simple, YAMLish format (actually a YAML subset).
+The top level object is a dict.
+lists and dicts can contain lists, dicts, and strings.
+'''
+
+import re
+# TODO: Dump pyyaml
+import yaml
+
+
+def dump(data, file_object):
+    yaml.dump(data, file_object, default_flow_style=False)
+
+
+spaces_re = re.compile(r'^(\s*)(.*)')
+list_re = re.compile(r'^(-\s+)(.*)')
+dict_re = re.compile(r'^([^-:]+):\s?(.*)')
+
+
+def load(file_object):
+    # Stack of (indent level, container object)
+    stack = [(0, {})]
+    indent = lambda: stack[-1][0]
+    obj = lambda: stack[-1][1]
+
+    # When a dict's value is a nested block, remember the key
+    parent_key = None
+
+    for line in file_object:
+        spaces, line = spaces_re.match(line).groups()
+
+        while len(spaces) < indent():
+            stack.pop()
+
+        lm = list_re.match(line)
+        dm = dict_re.match(line)
+        if len(spaces) == indent():
+            if lm:
+                assert isinstance(obj(), list) or parent_key
+                if isinstance(obj(), dict) and parent_key:
+                    # Starting a list in a dict
+                    stack.append((indent(), []))
+                    stack[-2][1][parent_key] = obj()
+                    parent_key = None
+            elif dm and isinstance(obj(), list):
+                # Left an embedded list
+                stack.pop()
+
+        if len(spaces) > indent():
+            # Nested dict
+            assert dm
+            assert parent_key
+            stack.append((len(spaces), {}))
+            stack[-2][1][parent_key] = obj()
+            parent_key = None
+
+        while lm and lm.group(2).startswith('-'):
+            # Nested lists
+            prefix, line = lm.groups()
+            stack.append((indent() + len(prefix), []))
+            stack[-2][1].append(obj())
+            lm = list_re.match(line)
+
+        if lm:
+            prefix, line = lm.groups()
+            dm = dict_re.match(line)
+            if dm:
+                stack.append((indent() + len(prefix), {}))
+                stack[-2][1].append(obj())
+            else:
+                obj().append(line)
+
+        if dm:
+            key, value = dm.groups()
+            if value:
+                obj()[key] = value
+            else:
+                parent_key = key
+
+    return stack[0][1]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nagslang/tests/test_level_serializer.py	Mon Sep 02 00:55:59 2013 +0200
@@ -0,0 +1,47 @@
+from unittest import TestCase
+from StringIO import StringIO
+
+from nagslang.level_serializer import load, dump
+
+
+class TestSimple(TestCase):
+    def roundtrip(self, data):
+        f = StringIO()
+        dump(data, f)
+        f.seek(0)
+        #print '\n== Begin ==\n%s== End ==' % f.buf
+        self.assertEqual(load(f), data)
+
+    def test_simple_dict(self):
+        self.roundtrip({'foo': 'bar'})
+
+    def test_dict_of_dicts(self):
+        self.roundtrip({'foo': {'bar': 'baz'}})
+
+    def test_dict_tree(self):
+        self.roundtrip({
+            'foo': {
+                'bar': {
+                    'baz': 'qux'
+                },
+                'quux': 'corge',
+            }
+        })
+
+    def test_dict_list(self):
+        self.roundtrip({
+            'foo': ['bar', 'baz'],
+        })
+
+    def test_nested_lists(self):
+        self.roundtrip({
+            'foo': [['bar', 'baz', 'qux'], 'quux'],
+        })
+
+    def test_list_of_dicts(self):
+        self.roundtrip({
+            'foo': [
+                {'bar': 'baz'},
+                {'qux': 'quux'},
+            ],
+        })