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