// ---------------------------------------------------------------------------- | |
// Copyright (C) 2002-2006 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/include/classic.hpp> | |
#include <boost/limits.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::key_type::value_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('/'): c.string += Ch('/'); 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::classic::grammar<json_grammar<Ptree> > | |
{ | |
typedef context<Ptree> Context; | |
typedef typename Ptree::key_type::value_type Ch; | |
mutable Context c; | |
template<class Scanner> | |
struct definition | |
{ | |
boost::spirit::classic::rule<Scanner> | |
root, object, member, array, item, value, string, number; | |
boost::spirit::classic::rule< | |
typename boost::spirit::classic::lexeme_scanner<Scanner>::type> | |
character, escape; | |
definition(const json_grammar &self) | |
{ | |
using namespace boost::spirit::classic; | |
// There's a boost::assertion too, so another explicit using | |
// here: | |
using boost::spirit::classic::assertion; | |
// 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 | |
= !ch_p("-") >> | |
(ch_p("0") | (range_p(Ch('1'), Ch('9')) >> *digit_p)) >> | |
!(ch_p(".") >> +digit_p) >> | |
!(chset_p(detail::widen<Ch>("eE").c_str()) >> | |
!chset_p(detail::widen<Ch>("-+").c_str()) >> | |
+digit_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>("\"\\/bfnrt").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::classic::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::key_type::value_type> &stream, | |
Ptree &pt, | |
const std::string &filename) | |
{ | |
using namespace boost::spirit::classic; | |
typedef typename Ptree::key_type::value_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()) | |
BOOST_PROPERTY_TREE_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) | |
BOOST_PROPERTY_TREE_THROW((parser_error<std::string, It>(v.begin(), "syntax error"))); | |
} | |
catch (parser_error<std::string, It> &e) | |
{ | |
BOOST_PROPERTY_TREE_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 |