6 from don import tags, _shared
8 def _integer_size_to_string_serializer(integer_size):
9 minimum = -(2 ** (integer_size - 1))
10 maximum = 2 ** (integer_size - 1) - 1
12 def serializer(integer):
13 assert minimum <= integer and integer <= maximum
14 return '{}i{}'.format(integer, integer_size)
18 def _serialize_float(f):
19 return '{}f'.format(f)
21 def _serialize_double(d):
22 return '{}d'.format(d)
24 def _serialize_binary(b):
25 return '"{}"b'.format(binascii.hexlify(b).decode('ascii'))
27 def _utf_encoding_to_serializer(utf_encoding):
29 return '"{}"{}'.format(s, utf_encoding)
33 def _string_serialize_list(l):
34 return '[{}]'.format(', '.join(map(serialize, l)))
36 def _string_serialize_dictionary(d):
37 def serialize_kvp(kvp):
38 return serialize(kvp[0]) + ': ' + serialize(kvp[1])
39 return '{ ' + ', '.join(map(serialize_kvp, d.items())) + ' }'
41 _STRING_SERIALIZERS = {
42 tags.VOID: lambda o: 'null',
43 tags.TRUE: lambda o: 'true',
44 tags.FALSE: lambda o: 'false',
45 tags.INT8: _integer_size_to_string_serializer(8),
46 tags.INT16: _integer_size_to_string_serializer(16),
47 tags.INT32: _integer_size_to_string_serializer(32),
48 tags.INT64: _integer_size_to_string_serializer(64),
49 tags.FLOAT: _serialize_float,
50 tags.DOUBLE: _serialize_double,
51 tags.BINARY: _serialize_binary,
52 tags.UTF8: _utf_encoding_to_serializer('utf8'),
53 tags.UTF16: _utf_encoding_to_serializer('utf16'),
54 tags.UTF32: _utf_encoding_to_serializer('utf32'),
55 tags.LIST: _string_serialize_list,
56 tags.DICTIONARY: _string_serialize_dictionary,
62 return _STRING_SERIALIZERS[o.tag](o.value)
64 def _consume_leading_whitespace(wrapped_parser):
65 @functools.wraps(wrapped_parser)
68 return wrapped_parser(s)
72 def _make_constant_parser(constant, value):
73 @_consume_leading_whitespace
74 def constant_parser(s):
75 if s.startswith(constant):
76 result = _shared.ParseResult(
79 remaining = s[len(constant):],
83 return _shared._FAILED_PARSE_RESULT
85 return constant_parser
87 def _make_integer_parser(width):
88 matcher = re.compile(r'(-?\d+)i' + str(width))
90 @_consume_leading_whitespace
91 def integer_parser(s):
92 match = matcher.match(s)
95 # TODO Validate that the integer is in range
96 return _shared.ParseResult(
98 value = int(match.group(1)),
99 remaining = s[match.end():],
102 return _shared._FAILED_PARSE_RESULT
104 return integer_parser
106 _BINARY32_MATCHER = re.compile(r'(-?\d+\.\d+)f')
107 _BINARY64_MATCHER = re.compile(r'(-?\d+\.\d+)d')
109 @_consume_leading_whitespace
110 def _binary32_parser(s):
111 match = _BINARY32_MATCHER.match(s)
114 # TODO Validate that the float 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 @_consume_leading_whitespace
124 def _binary64_parser(s):
125 match = _BINARY64_MATCHER.match(s)
128 # TODO Validate that the double is in range
129 return _shared.ParseResult(
131 value = float(match.group(1)),
132 remaining = s[match.end():],
135 return _shared._FAILED_PARSE_RESULT
137 _BINARY_MATCHER = re.compile(r'"([\da-f]*)"b')
139 @_consume_leading_whitespace
140 def _binary_parser(s):
141 match = _BINARY_MATCHER.match(s)
144 return _shared.ParseResult(
146 value = binascii.unhexlify(match.group(1)),
147 remaining = s[match.end():],
150 return _shared._FAILED_PARSE_RESULT
152 def _make_utf_parser(encoding):
153 matcher = re.compile(r'"(.*?)"' + encoding)
155 @_consume_leading_whitespace
157 match = matcher.match(s)
160 return _shared.ParseResult(
162 value = match.group(1),
163 remaining = s[match.end():],
166 return _shared._FAILED_PARSE_RESULT
170 def _make_consume_constant_parser(constant):
171 @_consume_leading_whitespace
172 def consume_character_parser(s):
173 if s.startswith(constant):
174 return _shared.ParseResult(
177 remaining = s[len(constant):],
179 return _shared._FAILED_PARSE_RESULT
181 return consume_character_parser
183 _consume_comma_parser = _make_consume_constant_parser(',')
185 def _prefix_with_comma(parser):
187 result = _consume_comma_parser(s)
191 return _shared._FAILED_PARSE_RESULT
194 if not result.success:
195 raise Exception('Trailing comma before "{}"'.format(s))
201 def _comma_separate_and_wrap(wrapped_parser, start_wrap, end_wrap, typecaster):
202 parser_prefixed_with_comma = _prefix_with_comma(wrapped_parser)
203 start_wrap_parser = _make_consume_constant_parser(start_wrap)
204 end_wrap_parser = _make_consume_constant_parser(end_wrap)
207 result = start_wrap_parser(s)
211 return _shared._FAILED_PARSE_RESULT
216 parse_result = wrapped_parser(s)
218 while parse_result.success:
219 value.append(parse_result.value)
220 s = parse_result.remaining
221 parse_result = parser_prefixed_with_comma(s)
223 result = end_wrap_parser(s)
227 return _shared._FAILED_PARSE_RESULT
229 return _shared.ParseResult(
231 value = typecaster(value),
237 # This uses _PARSERS which has not been defined yet, but is defined here so it can be used in
238 # the definition of _list_parser
239 def _object_parser(source):
240 for parser in _PARSERS:
241 result = parser(source)
246 return _shared._FAILED_PARSE_RESULT
248 _list_parser = _comma_separate_and_wrap(_object_parser, '[', ']', list)
250 _consume_colon_parser = _make_consume_constant_parser(':')
253 key_parse_result = _object_parser(s)
254 if key_parse_result.success:
255 s = key_parse_result.remaining
257 return _shared._FAILED_PARSE_RESULT
259 result = _consume_colon_parser(s)
263 return _shared._FAILED_PARSE_RESULT
265 value_parse_result = _object_parser(s)
266 if value_parse_result.success:
267 s = value_parse_result.remaining
269 return _shared._FAILED_PARSE_RESULT
271 return _shared.ParseResult(
273 value = (key_parse_result.value, value_parse_result.value),
277 _dictionary_parser = _comma_separate_and_wrap(_kvp_parser, '{', '}', collections.OrderedDict)
281 _make_constant_parser('null', None),
282 _make_constant_parser('true', True),
283 _make_constant_parser('false', False),
284 _make_integer_parser(8),
285 _make_integer_parser(16),
286 _make_integer_parser(32),
287 _make_integer_parser(64),
291 _make_utf_parser('utf8'),
292 _make_utf_parser('utf16'),
293 _make_utf_parser('utf32'),
298 def _parse(parser, source):
299 result = parser(source)
302 if result.remaining.strip() == '':
305 raise Exception('Unparsed trailing characters: "{}"'.format(result.remaining))
307 raise Exception('Unable to parse: "{}"'.format(source))
310 return _parse(_object_parser, s)