Added string and list parsing
[ton] / don / string.py
index 15ffc10..53551df 100644 (file)
@@ -1,4 +1,5 @@
 import binascii
+import re
 
 from don import tags, _shared
 
@@ -72,10 +73,146 @@ def _make_constant_parser(constant, value):
 
     return parser
 
+def _make_integer_parser(width):
+    matcher = re.compile(r'(-?\d+)i' + str(width))
+
+    def parser(s):
+        match = matcher.match(s)
+
+        if match:
+            # TODO Validate that the integer is in range
+            return _shared.ParseResult(
+                success = True,
+                value = int(match.group(1)),
+                remaining = s[match.end():],
+            )
+
+        return _shared._FAILED_PARSE_RESULT
+
+    return parser
+
+_BINARY32_MATCHER = re.compile(r'(-?\d+\.\d+)f')
+_BINARY64_MATCHER = re.compile(r'(-?\d+\.\d+)d')
+
+def _binary32_parser(s):
+    match = _BINARY32_MATCHER.match(s)
+
+    if match:
+        # TODO Validate that the float is in range
+        return _shared.ParseResult(
+            success = True,
+            value = float(match.group(1)),
+            remaining = s[match.end():],
+        )
+
+    return _shared._FAILED_PARSE_RESULT
+
+def _binary64_parser(s):
+    match = _BINARY64_MATCHER.match(s)
+
+    if match:
+        # TODO Validate that the double is in range
+        return _shared.ParseResult(
+            success = True,
+            value = float(match.group(1)),
+            remaining = s[match.end():],
+        )
+
+    return _shared._FAILED_PARSE_RESULT
+
+_BINARY_MATCHER = re.compile(r'"([\da-f]*)"b')
+
+def _binary_parser(s):
+    match = _BINARY_MATCHER.match(s)
+
+    if match:
+        return _shared.ParseResult(
+            success = True,
+            value = binascii.unhexlify(match.group(1)),
+            remaining = s[match.end():],
+        )
+
+    return _shared._FAILED_PARSE_RESULT
+
+def _make_utf_parser(encoding):
+    matcher = re.compile(r'"(.*?)"' + encoding)
+
+    def parser(s):
+        match = matcher.match(s)
+
+        if match:
+            return _shared.ParseResult(
+                success = True,
+                value = match.group(1),
+                remaining = s[match.end():],
+            )
+
+        return _shared._FAILED_PARSE_RESULT
+
+    return 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))
+
+            return result
+
+        return _shared._FAILED_PARSE_RESULT
+
+    return wrapped
+
+def _list_parser(s):
+    # TODO Assert they are all the same type
+    if not s.startswith('['):
+        return _shared._FAILED_PARSE_RESULT
+    s = s[1:]
+
+    value = []
+
+    first = True
+    parse_result = _object_parser(s)
+
+    while parse_result.success:
+        value.append(parse_result.value)
+        s = parse_result.remaining
+        parse_result = _prefix_with_comma(_object_parser)(s)
+
+    if not s.startswith(']'):
+        return _shared._FAILED_PARSE_RESULT
+
+    return _shared.ParseResult(
+        success = True,
+        value = value,
+        remaining = s[1:],
+    )
+
+
+
+def _dictionary_parser(s):
+    return _shared._FAILED_PARSE_RESULT
+
+
 _PARSERS = [
     _make_constant_parser('null', None),
     _make_constant_parser('true', True),
     _make_constant_parser('false', False),
+    _make_integer_parser(8),
+    _make_integer_parser(16),
+    _make_integer_parser(32),
+    _make_integer_parser(64),
+    _binary32_parser,
+    _binary64_parser,
+    _binary_parser,
+    _make_utf_parser('utf8'),
+    _make_utf_parser('utf16'),
+    _make_utf_parser('utf32'),
+    _list_parser,
+    _dictionary_parser,
 ]
 
 def _object_parser(source):
@@ -90,10 +227,13 @@ def _object_parser(source):
 def _parse(parser, source):
     result = parser(source)
 
-    if result.success and result.remaining.strip() == '':
-        return result.value
+    if result.success:
+        if result.remaining.strip() == '':
+            return result.value
+
+        raise Exception('Unparsed trailing characters: "{}"'.format(result.remaining))
 
-    raise Exception('Unparsed trailing characters: "{}"'.format(result.remaining))
+    raise Exception('Unable to parse: "{}"'.format(source))
 
 def deserialize(s):
     return _parse(_object_parser, s)