Mercurial > nagslang
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'}, + ], + })