Use autotag in don.string
[ton] / don / string.py
index e22b328..796cd07 100644 (file)
@@ -1,5 +1,6 @@
 import binascii
 import collections
+import functools
 import re
 
 from don import tags, _shared
@@ -56,12 +57,21 @@ _STRING_SERIALIZERS = {
 }
 
 def serialize(o):
-    o = tags._tag(o)
+    o = tags.autotag(o)
     
     return _STRING_SERIALIZERS[o.tag](o.value)
 
-def _make_constant_parser(constant, value):
+def _consume_leading_whitespace(wrapped_parser):
+    @functools.wraps(wrapped_parser)
     def parser(s):
+        s = s.lstrip()
+        return wrapped_parser(s)
+
+    return parser
+
+def _make_constant_parser(constant, value):
+    @_consume_leading_whitespace
+    def constant_parser(s):
         if s.startswith(constant):
             result = _shared.ParseResult(
                 success = True,
@@ -72,12 +82,13 @@ def _make_constant_parser(constant, value):
 
         return _shared._FAILED_PARSE_RESULT
 
-    return parser
+    return constant_parser
 
 def _make_integer_parser(width):
     matcher = re.compile(r'(-?\d+)i' + str(width))
 
-    def parser(s):
+    @_consume_leading_whitespace
+    def integer_parser(s):
         match = matcher.match(s)
 
         if match:
@@ -90,11 +101,12 @@ def _make_integer_parser(width):
 
         return _shared._FAILED_PARSE_RESULT
 
-    return parser
+    return integer_parser
 
 _BINARY32_MATCHER = re.compile(r'(-?\d+\.\d+)f')
 _BINARY64_MATCHER = re.compile(r'(-?\d+\.\d+)d')
 
+@_consume_leading_whitespace
 def _binary32_parser(s):
     match = _BINARY32_MATCHER.match(s)
 
@@ -108,6 +120,7 @@ def _binary32_parser(s):
 
     return _shared._FAILED_PARSE_RESULT
 
+@_consume_leading_whitespace
 def _binary64_parser(s):
     match = _BINARY64_MATCHER.match(s)
 
@@ -123,6 +136,7 @@ def _binary64_parser(s):
 
 _BINARY_MATCHER = re.compile(r'"([\da-f]*)"b')
 
+@_consume_leading_whitespace
 def _binary_parser(s):
     match = _BINARY_MATCHER.match(s)
 
@@ -138,7 +152,8 @@ def _binary_parser(s):
 def _make_utf_parser(encoding):
     matcher = re.compile(r'"(.*?)"' + encoding)
 
-    def parser(s):
+    @_consume_leading_whitespace
+    def utf_parser(s):
         match = matcher.match(s)
 
         if match:
@@ -150,29 +165,48 @@ def _make_utf_parser(encoding):
 
         return _shared._FAILED_PARSE_RESULT
 
-    return parser
+    return utf_parser
+
+def _make_consume_constant_parser(constant):
+    @_consume_leading_whitespace
+    def consume_character_parser(s):
+        if s.startswith(constant):
+            return _shared.ParseResult(
+                success = True,
+                value = None,
+                remaining = s[len(constant):],
+            )
+        return _shared._FAILED_PARSE_RESULT
+
+    return consume_character_parser
+
+_consume_comma_parser = _make_consume_constant_parser(',')
 
 def _prefix_with_comma(parser):
     def wrapped(s):
-        if s.startswith(','):
-            s = s[1:]
-
-            result = parser(s)
-            if not result.success:
-                raise Exception('Trailing comma before "{}"'.format(s))
+        result = _consume_comma_parser(s)
+        if result.success:
+            s = result.remaining
+        else:
+            return _shared._FAILED_PARSE_RESULT
 
-            return result
+        result = parser(s)
+        if not result.success:
+            raise Exception('Trailing comma before "{}"'.format(s))
 
-        return _shared._FAILED_PARSE_RESULT
+        return result
 
     return wrapped
 
 def _comma_separate_and_wrap(wrapped_parser, start_wrap, end_wrap, typecaster):
     parser_prefixed_with_comma = _prefix_with_comma(wrapped_parser)
+    start_wrap_parser = _make_consume_constant_parser(start_wrap)
+    end_wrap_parser = _make_consume_constant_parser(end_wrap)
 
     def parser(s):
-        if s.startswith(start_wrap):
-            s = s[1:]
+        result = start_wrap_parser(s)
+        if result.success:
+            s = result.remaining
         else:
             return _shared._FAILED_PARSE_RESULT
 
@@ -186,8 +220,9 @@ def _comma_separate_and_wrap(wrapped_parser, start_wrap, end_wrap, typecaster):
             s = parse_result.remaining
             parse_result = parser_prefixed_with_comma(s)
 
-        if s.startswith(end_wrap):
-            s = s[1:]
+        result = end_wrap_parser(s)
+        if result.success:
+            s = result.remaining
         else:
             return _shared._FAILED_PARSE_RESULT
 
@@ -212,6 +247,8 @@ def _object_parser(source):
 
 _list_parser = _comma_separate_and_wrap(_object_parser, '[', ']', list)
 
+_consume_colon_parser = _make_consume_constant_parser(':')
+
 def _kvp_parser(s):
     key_parse_result = _object_parser(s)
     if key_parse_result.success:
@@ -219,8 +256,9 @@ def _kvp_parser(s):
     else:
         return _shared._FAILED_PARSE_RESULT
 
-    if s.startswith(':'):
-        s = s[1:]
+    result = _consume_colon_parser(s)
+    if result.success:
+        s = result.remaining
     else:
         return _shared._FAILED_PARSE_RESULT