Implement integer parsing
[ton] / don / string.py
1 import binascii
2 import re
3
4 from don import tags, _shared
5
6 def _integer_size_to_string_serializer(integer_size):
7     minimum = -(2 ** (integer_size - 1))
8     maximum = 2 ** (integer_size - 1) - 1
9     
10     def serializer(integer):
11         assert minimum <= integer and integer <= maximum
12         return '{}i{}'.format(integer, integer_size)
13
14     return serializer
15
16 def _serialize_float(f):
17     return '{}f'.format(f)
18
19 def _serialize_double(d):
20     return '{}d'.format(d)
21
22 def _serialize_binary(b):
23     return '"{}"b'.format(binascii.hexlify(b).decode('ascii'))
24
25 def _utf_encoding_to_serializer(utf_encoding):
26     def serializer(s):
27         return '"{}"{}'.format(s, utf_encoding)
28
29     return serializer
30
31 def _string_serialize_list(l):
32     return '[{}]'.format(', '.join(map(serialize, l)))
33
34 def _string_serialize_dictionary(d):
35     def serialize_kvp(kvp):
36         return serialize(kvp[0]) + ': ' + serialize(kvp[1])
37     return '{ ' + ', '.join(map(serialize_kvp, d.items())) + ' }'
38
39 _STRING_SERIALIZERS = {
40     tags.VOID: lambda o: 'null',
41     tags.TRUE: lambda o: 'true',
42     tags.FALSE: lambda o: 'false',
43     tags.INT8: _integer_size_to_string_serializer(8),
44     tags.INT16: _integer_size_to_string_serializer(16),
45     tags.INT32: _integer_size_to_string_serializer(32),
46     tags.INT64: _integer_size_to_string_serializer(64),
47     tags.FLOAT: _serialize_float,
48     tags.DOUBLE: _serialize_double,
49     tags.BINARY: _serialize_binary,
50     tags.UTF8: _utf_encoding_to_serializer('utf8'),
51     tags.UTF16: _utf_encoding_to_serializer('utf16'),
52     tags.UTF32: _utf_encoding_to_serializer('utf32'),
53     tags.LIST: _string_serialize_list,
54     tags.DICTIONARY: _string_serialize_dictionary,
55 }
56
57 def serialize(o):
58     o = tags._tag(o)
59     
60     return _STRING_SERIALIZERS[o.tag](o.value)
61
62 def _make_constant_parser(constant, value):
63     def parser(s):
64         if s.startswith(constant):
65             result = _shared.ParseResult(
66                 success = True,
67                 value = value,
68                 remaining = s[len(constant):],
69             )
70             return result
71
72         return _shared._FAILED_PARSE_RESULT
73
74     return parser
75
76 def _make_integer_parser(width):
77     matcher = re.compile(r'(-?\d+)i' + str(width))
78
79     def parser(s):
80         match = matcher.match(s)
81
82         if match:
83             return _shared.ParseResult(
84                 success = True,
85                 value = int(match.group(1)),
86                 remaining = s[match.end():],
87             )
88
89         return _shared._FAILED_PARSE_RESULT
90
91     return parser
92
93 _PARSERS = [
94     _make_constant_parser('null', None),
95     _make_constant_parser('true', True),
96     _make_constant_parser('false', False),
97     _make_integer_parser(8),
98     _make_integer_parser(16),
99     _make_integer_parser(32),
100     _make_integer_parser(64),
101 ]
102
103 def _object_parser(source):
104     for parser in _PARSERS:
105         result = parser(source)
106
107         if result.success:
108             return result
109
110     return _shared._FAILED_PARSE_RESULT
111
112 def _parse(parser, source):
113     result = parser(source)
114
115     if result.success:
116         if result.remaining.strip() == '':
117             return result.value
118
119         raise Exception('Unparsed trailing characters: "{}"'.format(result.remaining))
120
121     raise Exception('Unable to parse: "{}"'.format(source))
122
123 def deserialize(s):
124     return _parse(_object_parser, s)