Commits

Benoît Allard committed 117c489

Add recursivity to the custom YAML parser

  • Participants
  • Parent commits a73d0a7

Comments (0)

Files changed (2)

File galileo/parser.py

 
 Known limitations:
 - Only spaces, no tabs
-- The returned dictionary supports only one level
+- Blank lines in the middle of an indented block is pretty bad ...
 """
 
 import json
-
+import textwrap
 
 def _stripcomment(line):
     s = []
 
 def _addKey(d, key):
     if d is None and key:
-        d = {key: None}
+        d = {}
     d[key] = None
     return d
 
 
 def unJSONize(s):
-    """ json is not enough ...
+    """ json is not good enough ...
     "'a'" doesn't get decoded,
     even worst, "a" neither """
     try:
         return s
 
 
+def _dedent(lines, start):
+    res = [lines[start]]
+    idx = start + 1
+    minident = _getident(lines[start])
+    while idx < len(lines):
+        curident = _getident(lines[idx])
+        if curident < minident:
+            break
+        res.append(lines[idx])
+        idx += 1
+    return res
+
+
 def loads(s):
     res = None
     current_key = None
         i += 1
         if not line: continue
         if _getident(line) == 0:
-            if ':' in line:
+            if line.startswith('-'):
+                if res is None:
+                    res = []
+                line = line[1:].strip()
+                if line:
+                    res.append(loads(line))
+                elif i == len(lines):
+                    res.append(None)
+            elif ':' in line:
                 current_key = None
                 k, v = line.split(':')
                 res = _addKey(res, k)
             else:
                 return unJSONize(line)
         else:
-            assert current_key is not None
-            # value indented
-            line = line.lstrip()
-            if not line.startswith('- '):
-                res[current_key] = unJSONize(line)
+            subblock = _dedent(lines, i-1)
+            subres = loads(textwrap.dedent('\n'.join(subblock)))
+            if isinstance(res, dict):
+                res[current_key] = subres
+            elif isinstance(res, list):
+                res.append(subres)
             else:
-                if res[current_key] is None:
-                    res[current_key] = []
-                res[current_key].append(unJSONize(line[2:]))
+                raise ValueError(res, subres)
+            i += len(subblock) - 1
+
     return res
 
 
     import sys
     # For fun and quick test
     with open(sys.argv[1], 'rt') as f:
-        print load(f.read())
+        print load(f)

File tests/testYAMLParser.py

     def testStripCommentDoubleComment(self):
         self.assertEquals(parser._stripcomment("ab # cd # ef"), "ab")
 
+    def testdedent1(self):
+        self.assertEquals(parser._dedent("""\
+a:
+  - a
+  - b
+""".split('\n'), 1), ['  - a', '  - b'])
+    def testdedent2(self):
+        self.assertEquals(parser._dedent("""\
+-
+  a:
+    b
+  c:
+    5
+""".split('\n'), 1), ['  a:', '    b', '  c:', '    5'])
+
 class testload(unittest.TestCase):
 
     def testEmpty(self):
         self.assertEquals(parser.loads('5'), 5)
         self.assertEquals(parser.loads('a'), 'a')
         self.assertEquals(parser.loads('true'), True)
-        
+
+    def testOneArray(self):
+        self.assertEquals(parser.loads('- a\n- b'), ['a', 'b'])
+
     def testIntegerValue(self):
         self.assertEquals(parser.loads("t: 5"), {'t': 5})
     def testSimpleStringValue(self):
   - a
   - 5
 """), {'test': ['a', 5]})
+
+    def testDoubleDict(self):
+        self.assertEquals(parser.loads("""\
+a:
+  b: c
+"""), {'a': {'b': 'c'}})
+    def testDoubleDict2(self):
+        self.assertEquals(parser.loads("""\
+a:
+  b:
+    c
+"""), {'a': {'b': 'c'}})
+
+    def testMultiArray(self):
+        self.assertEquals(parser.loads("""\
+-
+  -
+    a:
+      b
+    c:
+      5
+  -
+    a:
+      8
+"""), [[{'a':'b', 'c': 5}, {'a': 8}]])
+
+    def testArrayOfDict(self):
+        self.assertEquals(parser.loads("""\
+- a: b
+"""), [{'a':'b'}])