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 def _binary_parser(s):
140 match = _BINARY_MATCHER.match(s)
143 return _shared.ParseResult(
145 value = binascii.unhexlify(match.group(1)),
146 remaining = s[match.end():],
149 return _shared._FAILED_PARSE_RESULT
151 def _make_utf_parser(encoding):
152 matcher = re.compile(r'"(.*?)"' + encoding)
155 match = matcher.match(s)
158 return _shared.ParseResult(
160 value = match.group(1),
161 remaining = s[match.end():],
164 return _shared._FAILED_PARSE_RESULT
168 def _prefix_with_comma(parser):
170 if s.startswith(','):
174 if not result.success:
175 raise Exception('Trailing comma before "{}"'.format(s))
179 return _shared._FAILED_PARSE_RESULT
183 def _comma_separate_and_wrap(wrapped_parser, start_wrap, end_wrap, typecaster):
184 parser_prefixed_with_comma = _prefix_with_comma(wrapped_parser)
187 if s.startswith(start_wrap):
190 return _shared._FAILED_PARSE_RESULT
195 parse_result = wrapped_parser(s)
197 while parse_result.success:
198 value.append(parse_result.value)
199 s = parse_result.remaining
200 parse_result = parser_prefixed_with_comma(s)
202 if s.startswith(end_wrap):
205 return _shared._FAILED_PARSE_RESULT
207 return _shared.ParseResult(
209 value = typecaster(value),
215 # This uses _PARSERS which has not been defined yet, but is defined here so it can be used in
216 # the definition of _list_parser
217 def _object_parser(source):
218 for parser in _PARSERS:
219 result = parser(source)
224 return _shared._FAILED_PARSE_RESULT
226 _list_parser = _comma_separate_and_wrap(_object_parser, '[', ']', list)
229 key_parse_result = _object_parser(s)
230 if key_parse_result.success:
231 s = key_parse_result.remaining
233 return _shared._FAILED_PARSE_RESULT
235 if s.startswith(':'):
238 return _shared._FAILED_PARSE_RESULT
240 value_parse_result = _object_parser(s)
241 if value_parse_result.success:
242 s = value_parse_result.remaining
244 return _shared._FAILED_PARSE_RESULT
246 return _shared.ParseResult(
248 value = (key_parse_result.value, value_parse_result.value),
252 _dictionary_parser = _comma_separate_and_wrap(_kvp_parser, '{', '}', collections.OrderedDict)
256 _make_constant_parser('null', None),
257 _make_constant_parser('true', True),
258 _make_constant_parser('false', False),
259 _make_integer_parser(8),
260 _make_integer_parser(16),
261 _make_integer_parser(32),
262 _make_integer_parser(64),
266 _make_utf_parser('utf8'),
267 _make_utf_parser('utf16'),
268 _make_utf_parser('utf32'),
273 def _parse(parser, source):
274 result = parser(source)
277 if result.remaining.strip() == '':
280 raise Exception('Unparsed trailing characters: "{}"'.format(result.remaining))
282 raise Exception('Unable to parse: "{}"'.format(source))
285 return _parse(_object_parser, s)