4 from don import tags, _shared
6 def _integer_size_to_string_serializer(integer_size):
7 minimum = -(2 ** (integer_size - 1))
8 maximum = 2 ** (integer_size - 1) - 1
10 def serializer(integer):
11 assert minimum <= integer and integer <= maximum
12 return '{}i{}'.format(integer, integer_size)
16 def _serialize_float(f):
17 return '{}f'.format(f)
19 def _serialize_double(d):
20 return '{}d'.format(d)
22 def _serialize_binary(b):
23 return '"{}"b'.format(binascii.hexlify(b).decode('ascii'))
25 def _utf_encoding_to_serializer(utf_encoding):
27 return '"{}"{}'.format(s, utf_encoding)
31 def _string_serialize_list(l):
32 return '[{}]'.format(', '.join(map(serialize, l)))
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())) + ' }'
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,
60 return _STRING_SERIALIZERS[o.tag](o.value)
62 def _make_constant_parser(constant, value):
64 if s.startswith(constant):
65 result = _shared.ParseResult(
68 remaining = s[len(constant):],
72 return _shared._FAILED_PARSE_RESULT
76 def _make_integer_parser(width):
77 matcher = re.compile(r'(-?\d+)i' + str(width))
80 match = matcher.match(s)
83 # TODO Validate that the integer is in range
84 return _shared.ParseResult(
86 value = int(match.group(1)),
87 remaining = s[match.end():],
90 return _shared._FAILED_PARSE_RESULT
94 _BINARY32_MATCHER = re.compile(r'(-?\d+\.\d+)f')
95 _BINARY64_MATCHER = re.compile(r'(-?\d+\.\d+)d')
97 def _binary32_parser(s):
98 match = _BINARY32_MATCHER.match(s)
101 # TODO Validate that the float is in range
102 return _shared.ParseResult(
104 value = float(match.group(1)),
105 remaining = s[match.end():],
108 return _shared._FAILED_PARSE_RESULT
110 def _binary64_parser(s):
111 match = _BINARY64_MATCHER.match(s)
114 # TODO Validate that the double is in range
115 return _shared.ParseResult(
117 value = float(match.group(1)),
118 remaining = s[match.end():],
121 return _shared._FAILED_PARSE_RESULT
123 _BINARY_MATCHER = re.compile(r'"([\da-f]*)"b')
125 def _binary_parser(s):
126 match = _BINARY_MATCHER.match(s)
129 return _shared.ParseResult(
131 value = binascii.unhexlify(match.group(1)),
132 remaining = s[match.end():],
135 return _shared._FAILED_PARSE_RESULT
137 def _make_utf_parser(encoding):
138 matcher = re.compile(r'"(.*?)"' + encoding)
141 match = matcher.match(s)
144 return _shared.ParseResult(
146 value = match.group(1),
147 remaining = s[match.end():],
150 return _shared._FAILED_PARSE_RESULT
154 def _prefix_with_comma(parser):
156 if s.startswith(','):
160 if not result.success:
161 raise Exception('Trailing comma before "{}"'.format(s))
165 return _shared._FAILED_PARSE_RESULT
170 # TODO Assert they are all the same type
171 if not s.startswith('['):
172 return _shared._FAILED_PARSE_RESULT
178 parse_result = _object_parser(s)
180 while parse_result.success:
181 value.append(parse_result.value)
182 s = parse_result.remaining
183 parse_result = _prefix_with_comma(_object_parser)(s)
185 if not s.startswith(']'):
186 return _shared._FAILED_PARSE_RESULT
188 return _shared.ParseResult(
196 def _dictionary_parser(s):
197 return _shared._FAILED_PARSE_RESULT
201 _make_constant_parser('null', None),
202 _make_constant_parser('true', True),
203 _make_constant_parser('false', False),
204 _make_integer_parser(8),
205 _make_integer_parser(16),
206 _make_integer_parser(32),
207 _make_integer_parser(64),
211 _make_utf_parser('utf8'),
212 _make_utf_parser('utf16'),
213 _make_utf_parser('utf32'),
218 def _object_parser(source):
219 for parser in _PARSERS:
220 result = parser(source)
225 return _shared._FAILED_PARSE_RESULT
227 def _parse(parser, source):
228 result = parser(source)
231 if result.remaining.strip() == '':
234 raise Exception('Unparsed trailing characters: "{}"'.format(result.remaining))
236 raise Exception('Unable to parse: "{}"'.format(source))
239 return _parse(_object_parser, s)