From 3e66f7dbcc1ed83ae27197d982bb14b32d8e4d02 Mon Sep 17 00:00:00 2001 From: David Kerkeslager Date: Fri, 14 Apr 2017 11:21:26 -0400 Subject: [PATCH] Implemented dictionary parsing --- don/string.py | 89 +++++++++++++++++++++++++++++++++++---------------- test_don.py | 9 ++++++ 2 files changed, 70 insertions(+), 28 deletions(-) diff --git a/don/string.py b/don/string.py index 53551df..e22b328 100644 --- a/don/string.py +++ b/don/string.py @@ -1,4 +1,5 @@ import binascii +import collections import re from don import tags, _shared @@ -166,35 +167,76 @@ def _prefix_with_comma(parser): 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:] +def _comma_separate_and_wrap(wrapped_parser, start_wrap, end_wrap, typecaster): + parser_prefixed_with_comma = _prefix_with_comma(wrapped_parser) + + def parser(s): + if s.startswith(start_wrap): + s = s[1:] + else: + return _shared._FAILED_PARSE_RESULT + + value = [] + first = True + + parse_result = wrapped_parser(s) + + while parse_result.success: + value.append(parse_result.value) + s = parse_result.remaining + parse_result = parser_prefixed_with_comma(s) + + if s.startswith(end_wrap): + s = s[1:] + else: + return _shared._FAILED_PARSE_RESULT + + return _shared.ParseResult( + success = True, + value = typecaster(value), + remaining = s, + ) + + return parser + +# This uses _PARSERS which has not been defined yet, but is defined here so it can be used in +# the definition of _list_parser +def _object_parser(source): + for parser in _PARSERS: + result = parser(source) - value = [] + if result.success: + return result + + return _shared._FAILED_PARSE_RESULT + +_list_parser = _comma_separate_and_wrap(_object_parser, '[', ']', list) - first = True - parse_result = _object_parser(s) +def _kvp_parser(s): + key_parse_result = _object_parser(s) + if key_parse_result.success: + s = key_parse_result.remaining + else: + return _shared._FAILED_PARSE_RESULT - while parse_result.success: - value.append(parse_result.value) - s = parse_result.remaining - parse_result = _prefix_with_comma(_object_parser)(s) + if s.startswith(':'): + s = s[1:] + else: + return _shared._FAILED_PARSE_RESULT - if not s.startswith(']'): + value_parse_result = _object_parser(s) + if value_parse_result.success: + s = value_parse_result.remaining + else: return _shared._FAILED_PARSE_RESULT return _shared.ParseResult( success = True, - value = value, - remaining = s[1:], + value = (key_parse_result.value, value_parse_result.value), + remaining = s, ) - - -def _dictionary_parser(s): - return _shared._FAILED_PARSE_RESULT +_dictionary_parser = _comma_separate_and_wrap(_kvp_parser, '{', '}', collections.OrderedDict) _PARSERS = [ @@ -215,15 +257,6 @@ _PARSERS = [ _dictionary_parser, ] -def _object_parser(source): - for parser in _PARSERS: - result = parser(source) - - if result.success: - return result - - return _shared._FAILED_PARSE_RESULT - def _parse(parser, source): result = parser(source) diff --git a/test_don.py b/test_don.py index fe46bbc..c4e9549 100644 --- a/test_don.py +++ b/test_don.py @@ -244,4 +244,13 @@ class TestStringDeserialize(unittest.TestCase): string.deserialize("[1i8,2i8,3i8,4i8,5i8]"), ) + def test_deserializes_dictionary(self): + self.assertEqual( + collections.OrderedDict([ + ('foo', 1), + ('bar', 'baz'), + ]), + string.deserialize('{"foo"utf8:1i32,"bar"utf8:"baz"utf8}'), + ) + unittest.main() -- 2.20.1