Chris@16: // ---------------------------------------------------------------------------- Chris@16: // Copyright (C) 2002-2006 Marcin Kalicinski Chris@16: // Chris@101: // Distributed under the Boost Software License, Version 1.0. Chris@101: // (See accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: // For more information, see www.boost.org Chris@16: // ---------------------------------------------------------------------------- Chris@16: #ifndef BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_READ_HPP_INCLUDED Chris@16: #define BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_READ_HPP_INCLUDED Chris@16: Chris@16: //#define BOOST_SPIRIT_DEBUG Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: namespace boost { namespace property_tree { namespace json_parser Chris@16: { Chris@16: Chris@16: /////////////////////////////////////////////////////////////////////// Chris@16: // Json parser context Chris@101: Chris@16: template Chris@16: struct context Chris@16: { Chris@101: typedef typename Ptree::key_type Str; Chris@101: typedef typename Str::value_type Ch; Chris@101: typedef typename std::vector::iterator It; Chris@16: Chris@16: Str string; Chris@16: Str name; Chris@16: Ptree root; Chris@16: std::vector stack; Chris@16: Chris@16: struct a_object_s Chris@16: { Chris@16: context &c; Chris@16: a_object_s(context &c): c(c) { } Chris@16: void operator()(Ch) const Chris@16: { Chris@16: if (c.stack.empty()) Chris@16: c.stack.push_back(&c.root); Chris@16: else Chris@16: { Chris@16: Ptree *parent = c.stack.back(); Chris@16: Ptree *child = &parent->push_back(std::make_pair(c.name, Ptree()))->second; Chris@16: c.stack.push_back(child); Chris@16: c.name.clear(); Chris@16: } Chris@16: } Chris@16: }; Chris@101: Chris@16: struct a_object_e Chris@16: { Chris@16: context &c; Chris@16: a_object_e(context &c): c(c) { } Chris@16: void operator()(Ch) const Chris@16: { Chris@16: BOOST_ASSERT(c.stack.size() >= 1); Chris@16: c.stack.pop_back(); Chris@16: } Chris@16: }; Chris@16: Chris@16: struct a_name Chris@16: { Chris@16: context &c; Chris@16: a_name(context &c): c(c) { } Chris@16: void operator()(It, It) const Chris@16: { Chris@16: c.name.swap(c.string); Chris@16: c.string.clear(); Chris@16: } Chris@16: }; Chris@16: Chris@16: struct a_string_val Chris@16: { Chris@16: context &c; Chris@16: a_string_val(context &c): c(c) { } Chris@16: void operator()(It, It) const Chris@16: { Chris@16: BOOST_ASSERT(c.stack.size() >= 1); Chris@16: c.stack.back()->push_back(std::make_pair(c.name, Ptree(c.string))); Chris@16: c.name.clear(); Chris@16: c.string.clear(); Chris@16: } Chris@16: }; Chris@16: Chris@16: struct a_literal_val Chris@16: { Chris@16: context &c; Chris@16: a_literal_val(context &c): c(c) { } Chris@16: void operator()(It b, It e) const Chris@16: { Chris@16: BOOST_ASSERT(c.stack.size() >= 1); Chris@16: c.stack.back()->push_back(std::make_pair(c.name, Chris@16: Ptree(Str(b, e)))); Chris@16: c.name.clear(); Chris@16: c.string.clear(); Chris@16: } Chris@16: }; Chris@16: Chris@16: struct a_char Chris@16: { Chris@16: context &c; Chris@16: a_char(context &c): c(c) { } Chris@101: void operator()(It b, It) const Chris@16: { Chris@16: c.string += *b; Chris@16: } Chris@16: }; Chris@16: Chris@16: struct a_escape Chris@16: { Chris@16: context &c; Chris@16: a_escape(context &c): c(c) { } Chris@16: void operator()(Ch ch) const Chris@16: { Chris@16: switch (ch) Chris@16: { Chris@16: case Ch('\"'): c.string += Ch('\"'); break; Chris@16: case Ch('\\'): c.string += Ch('\\'); break; Chris@16: case Ch('/'): c.string += Ch('/'); break; Chris@16: case Ch('b'): c.string += Ch('\b'); break; Chris@16: case Ch('f'): c.string += Ch('\f'); break; Chris@16: case Ch('n'): c.string += Ch('\n'); break; Chris@16: case Ch('r'): c.string += Ch('\r'); break; Chris@16: case Ch('t'): c.string += Ch('\t'); break; Chris@16: default: BOOST_ASSERT(0); Chris@16: } Chris@16: } Chris@16: }; Chris@16: Chris@16: struct a_unicode Chris@16: { Chris@16: context &c; Chris@16: a_unicode(context &c): c(c) { } Chris@16: void operator()(unsigned long u) const Chris@16: { Chris@16: u = (std::min)(u, static_cast((std::numeric_limits::max)())); Chris@16: c.string += Ch(u); Chris@16: } Chris@16: }; Chris@16: Chris@16: }; Chris@16: Chris@16: /////////////////////////////////////////////////////////////////////// Chris@16: // Json grammar Chris@16: Chris@16: template Chris@16: struct json_grammar : Chris@16: public boost::spirit::classic::grammar > Chris@16: { Chris@101: Chris@16: typedef context Context; Chris@101: typedef typename Ptree::key_type Str; Chris@101: typedef typename Str::value_type Ch; Chris@16: Chris@16: mutable Context c; Chris@101: Chris@16: template Chris@16: struct definition Chris@16: { Chris@101: Chris@16: boost::spirit::classic::rule Chris@16: root, object, member, array, item, value, string, number; Chris@16: boost::spirit::classic::rule< Chris@16: typename boost::spirit::classic::lexeme_scanner::type> Chris@16: character, escape; Chris@16: Chris@16: definition(const json_grammar &self) Chris@16: { Chris@16: Chris@16: using namespace boost::spirit::classic; Chris@16: // There's a boost::assertion too, so another explicit using Chris@16: // here: Chris@16: using boost::spirit::classic::assertion; Chris@16: Chris@16: // Assertions Chris@16: assertion expect_root("expected object or array"); Chris@16: assertion expect_eoi("expected end of input"); Chris@16: assertion expect_objclose("expected ',' or '}'"); Chris@16: assertion expect_arrclose("expected ',' or ']'"); Chris@16: assertion expect_name("expected object name"); Chris@16: assertion expect_colon("expected ':'"); Chris@16: assertion expect_value("expected value"); Chris@16: assertion expect_escape("invalid escape sequence"); Chris@16: Chris@16: // JSON grammar rules Chris@101: root Chris@101: = expect_root(object | array) Chris@16: >> expect_eoi(end_p) Chris@16: ; Chris@101: Chris@101: object Chris@16: = ch_p('{')[typename Context::a_object_s(self.c)] Chris@101: >> (ch_p('}')[typename Context::a_object_e(self.c)] Chris@16: | (list_p(member, ch_p(',')) Chris@16: >> expect_objclose(ch_p('}')[typename Context::a_object_e(self.c)]) Chris@16: ) Chris@16: ) Chris@16: ; Chris@101: Chris@101: member Chris@101: = expect_name(string[typename Context::a_name(self.c)]) Chris@101: >> expect_colon(ch_p(':')) Chris@16: >> expect_value(value) Chris@16: ; Chris@101: Chris@101: array Chris@16: = ch_p('[')[typename Context::a_object_s(self.c)] Chris@101: >> (ch_p(']')[typename Context::a_object_e(self.c)] Chris@16: | (list_p(item, ch_p(',')) Chris@16: >> expect_arrclose(ch_p(']')[typename Context::a_object_e(self.c)]) Chris@16: ) Chris@16: ) Chris@16: ; Chris@16: Chris@101: item Chris@16: = expect_value(value) Chris@16: ; Chris@16: Chris@101: value Chris@101: = string[typename Context::a_string_val(self.c)] Chris@16: | (number | str_p("true") | "false" | "null")[typename Context::a_literal_val(self.c)] Chris@101: | object Chris@16: | array Chris@16: ; Chris@101: Chris@101: number Chris@16: = !ch_p("-") >> Chris@16: (ch_p("0") | (range_p(Ch('1'), Ch('9')) >> *digit_p)) >> Chris@16: !(ch_p(".") >> +digit_p) >> Chris@101: !(chset_p(detail::widen("eE").c_str()) >> Chris@101: !chset_p(detail::widen("-+").c_str()) >> Chris@16: +digit_p) Chris@16: ; Chris@16: Chris@16: string Chris@16: = +(lexeme_d[confix_p('\"', *character, '\"')]) Chris@16: ; Chris@16: Chris@16: character Chris@16: = (anychar_p - "\\" - "\"") Chris@16: [typename Context::a_char(self.c)] Chris@16: | ch_p("\\") >> expect_escape(escape) Chris@16: ; Chris@16: Chris@16: escape Chris@101: = chset_p(detail::widen("\"\\/bfnrt").c_str()) Chris@16: [typename Context::a_escape(self.c)] Chris@16: | 'u' >> uint_parser() Chris@16: [typename Context::a_unicode(self.c)] Chris@16: ; Chris@16: Chris@16: // Debug Chris@16: BOOST_SPIRIT_DEBUG_RULE(root); Chris@16: BOOST_SPIRIT_DEBUG_RULE(object); Chris@16: BOOST_SPIRIT_DEBUG_RULE(member); Chris@16: BOOST_SPIRIT_DEBUG_RULE(array); Chris@16: BOOST_SPIRIT_DEBUG_RULE(item); Chris@16: BOOST_SPIRIT_DEBUG_RULE(value); Chris@16: BOOST_SPIRIT_DEBUG_RULE(string); Chris@16: BOOST_SPIRIT_DEBUG_RULE(number); Chris@16: BOOST_SPIRIT_DEBUG_RULE(escape); Chris@16: BOOST_SPIRIT_DEBUG_RULE(character); Chris@16: Chris@16: } Chris@16: Chris@16: const boost::spirit::classic::rule &start() const Chris@16: { Chris@16: return root; Chris@16: } Chris@16: Chris@16: }; Chris@16: Chris@16: }; Chris@16: Chris@16: template Chris@16: unsigned long count_lines(It begin, It end) Chris@16: { Chris@16: return static_cast(std::count(begin, end, Ch('\n')) + 1); Chris@16: } Chris@16: Chris@16: template Chris@16: void read_json_internal(std::basic_istream &stream, Chris@16: Ptree &pt, Chris@16: const std::string &filename) Chris@16: { Chris@16: Chris@16: using namespace boost::spirit::classic; Chris@16: typedef typename Ptree::key_type::value_type Ch; Chris@16: typedef typename std::vector::iterator It; Chris@16: Chris@16: // Load data into vector Chris@16: std::vector v(std::istreambuf_iterator(stream.rdbuf()), Chris@16: std::istreambuf_iterator()); Chris@16: if (!stream.good()) Chris@16: BOOST_PROPERTY_TREE_THROW(json_parser_error("read error", filename, 0)); Chris@101: Chris@16: // Prepare grammar Chris@16: json_grammar g; Chris@16: Chris@16: // Parse Chris@16: try Chris@16: { Chris@101: parse_info pi = parse(v.begin(), v.end(), g, Chris@16: space_p | comment_p("//") | comment_p("/*", "*/")); Chris@16: if (!pi.hit || !pi.full) Chris@16: BOOST_PROPERTY_TREE_THROW((parser_error(v.begin(), "syntax error"))); Chris@16: } Chris@16: catch (parser_error &e) Chris@16: { Chris@16: BOOST_PROPERTY_TREE_THROW(json_parser_error(e.descriptor, filename, count_lines(v.begin(), e.where))); Chris@16: } Chris@16: Chris@16: // Swap grammar context root and pt Chris@16: pt.swap(g.c.root); Chris@16: Chris@16: } Chris@16: Chris@16: } } } Chris@16: Chris@16: #endif