Chris@16
|
1 // ----------------------------------------------------------------------------
|
Chris@16
|
2 // Copyright (C) 2002-2006 Marcin Kalicinski
|
Chris@16
|
3 //
|
Chris@101
|
4 // Distributed under the Boost Software License, Version 1.0.
|
Chris@101
|
5 // (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
6 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
7 //
|
Chris@16
|
8 // For more information, see www.boost.org
|
Chris@16
|
9 // ----------------------------------------------------------------------------
|
Chris@16
|
10 #ifndef BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_READ_HPP_INCLUDED
|
Chris@16
|
11 #define BOOST_PROPERTY_TREE_DETAIL_JSON_PARSER_READ_HPP_INCLUDED
|
Chris@16
|
12
|
Chris@16
|
13 //#define BOOST_SPIRIT_DEBUG
|
Chris@16
|
14
|
Chris@16
|
15 #include <boost/property_tree/ptree.hpp>
|
Chris@16
|
16 #include <boost/property_tree/detail/ptree_utils.hpp>
|
Chris@16
|
17 #include <boost/property_tree/detail/json_parser_error.hpp>
|
Chris@16
|
18 #include <boost/spirit/include/classic.hpp>
|
Chris@16
|
19 #include <boost/limits.hpp>
|
Chris@16
|
20 #include <string>
|
Chris@16
|
21 #include <locale>
|
Chris@16
|
22 #include <istream>
|
Chris@16
|
23 #include <vector>
|
Chris@16
|
24 #include <algorithm>
|
Chris@16
|
25
|
Chris@16
|
26 namespace boost { namespace property_tree { namespace json_parser
|
Chris@16
|
27 {
|
Chris@16
|
28
|
Chris@16
|
29 ///////////////////////////////////////////////////////////////////////
|
Chris@16
|
30 // Json parser context
|
Chris@101
|
31
|
Chris@16
|
32 template<class Ptree>
|
Chris@16
|
33 struct context
|
Chris@16
|
34 {
|
Chris@101
|
35 typedef typename Ptree::key_type Str;
|
Chris@101
|
36 typedef typename Str::value_type Ch;
|
Chris@101
|
37 typedef typename std::vector<Ch>::iterator It;
|
Chris@16
|
38
|
Chris@16
|
39 Str string;
|
Chris@16
|
40 Str name;
|
Chris@16
|
41 Ptree root;
|
Chris@16
|
42 std::vector<Ptree *> stack;
|
Chris@16
|
43
|
Chris@16
|
44 struct a_object_s
|
Chris@16
|
45 {
|
Chris@16
|
46 context &c;
|
Chris@16
|
47 a_object_s(context &c): c(c) { }
|
Chris@16
|
48 void operator()(Ch) const
|
Chris@16
|
49 {
|
Chris@16
|
50 if (c.stack.empty())
|
Chris@16
|
51 c.stack.push_back(&c.root);
|
Chris@16
|
52 else
|
Chris@16
|
53 {
|
Chris@16
|
54 Ptree *parent = c.stack.back();
|
Chris@16
|
55 Ptree *child = &parent->push_back(std::make_pair(c.name, Ptree()))->second;
|
Chris@16
|
56 c.stack.push_back(child);
|
Chris@16
|
57 c.name.clear();
|
Chris@16
|
58 }
|
Chris@16
|
59 }
|
Chris@16
|
60 };
|
Chris@101
|
61
|
Chris@16
|
62 struct a_object_e
|
Chris@16
|
63 {
|
Chris@16
|
64 context &c;
|
Chris@16
|
65 a_object_e(context &c): c(c) { }
|
Chris@16
|
66 void operator()(Ch) const
|
Chris@16
|
67 {
|
Chris@16
|
68 BOOST_ASSERT(c.stack.size() >= 1);
|
Chris@16
|
69 c.stack.pop_back();
|
Chris@16
|
70 }
|
Chris@16
|
71 };
|
Chris@16
|
72
|
Chris@16
|
73 struct a_name
|
Chris@16
|
74 {
|
Chris@16
|
75 context &c;
|
Chris@16
|
76 a_name(context &c): c(c) { }
|
Chris@16
|
77 void operator()(It, It) const
|
Chris@16
|
78 {
|
Chris@16
|
79 c.name.swap(c.string);
|
Chris@16
|
80 c.string.clear();
|
Chris@16
|
81 }
|
Chris@16
|
82 };
|
Chris@16
|
83
|
Chris@16
|
84 struct a_string_val
|
Chris@16
|
85 {
|
Chris@16
|
86 context &c;
|
Chris@16
|
87 a_string_val(context &c): c(c) { }
|
Chris@16
|
88 void operator()(It, It) const
|
Chris@16
|
89 {
|
Chris@16
|
90 BOOST_ASSERT(c.stack.size() >= 1);
|
Chris@16
|
91 c.stack.back()->push_back(std::make_pair(c.name, Ptree(c.string)));
|
Chris@16
|
92 c.name.clear();
|
Chris@16
|
93 c.string.clear();
|
Chris@16
|
94 }
|
Chris@16
|
95 };
|
Chris@16
|
96
|
Chris@16
|
97 struct a_literal_val
|
Chris@16
|
98 {
|
Chris@16
|
99 context &c;
|
Chris@16
|
100 a_literal_val(context &c): c(c) { }
|
Chris@16
|
101 void operator()(It b, It e) const
|
Chris@16
|
102 {
|
Chris@16
|
103 BOOST_ASSERT(c.stack.size() >= 1);
|
Chris@16
|
104 c.stack.back()->push_back(std::make_pair(c.name,
|
Chris@16
|
105 Ptree(Str(b, e))));
|
Chris@16
|
106 c.name.clear();
|
Chris@16
|
107 c.string.clear();
|
Chris@16
|
108 }
|
Chris@16
|
109 };
|
Chris@16
|
110
|
Chris@16
|
111 struct a_char
|
Chris@16
|
112 {
|
Chris@16
|
113 context &c;
|
Chris@16
|
114 a_char(context &c): c(c) { }
|
Chris@101
|
115 void operator()(It b, It) const
|
Chris@16
|
116 {
|
Chris@16
|
117 c.string += *b;
|
Chris@16
|
118 }
|
Chris@16
|
119 };
|
Chris@16
|
120
|
Chris@16
|
121 struct a_escape
|
Chris@16
|
122 {
|
Chris@16
|
123 context &c;
|
Chris@16
|
124 a_escape(context &c): c(c) { }
|
Chris@16
|
125 void operator()(Ch ch) const
|
Chris@16
|
126 {
|
Chris@16
|
127 switch (ch)
|
Chris@16
|
128 {
|
Chris@16
|
129 case Ch('\"'): c.string += Ch('\"'); break;
|
Chris@16
|
130 case Ch('\\'): c.string += Ch('\\'); break;
|
Chris@16
|
131 case Ch('/'): c.string += Ch('/'); break;
|
Chris@16
|
132 case Ch('b'): c.string += Ch('\b'); break;
|
Chris@16
|
133 case Ch('f'): c.string += Ch('\f'); break;
|
Chris@16
|
134 case Ch('n'): c.string += Ch('\n'); break;
|
Chris@16
|
135 case Ch('r'): c.string += Ch('\r'); break;
|
Chris@16
|
136 case Ch('t'): c.string += Ch('\t'); break;
|
Chris@16
|
137 default: BOOST_ASSERT(0);
|
Chris@16
|
138 }
|
Chris@16
|
139 }
|
Chris@16
|
140 };
|
Chris@16
|
141
|
Chris@16
|
142 struct a_unicode
|
Chris@16
|
143 {
|
Chris@16
|
144 context &c;
|
Chris@16
|
145 a_unicode(context &c): c(c) { }
|
Chris@16
|
146 void operator()(unsigned long u) const
|
Chris@16
|
147 {
|
Chris@16
|
148 u = (std::min)(u, static_cast<unsigned long>((std::numeric_limits<Ch>::max)()));
|
Chris@16
|
149 c.string += Ch(u);
|
Chris@16
|
150 }
|
Chris@16
|
151 };
|
Chris@16
|
152
|
Chris@16
|
153 };
|
Chris@16
|
154
|
Chris@16
|
155 ///////////////////////////////////////////////////////////////////////
|
Chris@16
|
156 // Json grammar
|
Chris@16
|
157
|
Chris@16
|
158 template<class Ptree>
|
Chris@16
|
159 struct json_grammar :
|
Chris@16
|
160 public boost::spirit::classic::grammar<json_grammar<Ptree> >
|
Chris@16
|
161 {
|
Chris@101
|
162
|
Chris@16
|
163 typedef context<Ptree> Context;
|
Chris@101
|
164 typedef typename Ptree::key_type Str;
|
Chris@101
|
165 typedef typename Str::value_type Ch;
|
Chris@16
|
166
|
Chris@16
|
167 mutable Context c;
|
Chris@101
|
168
|
Chris@16
|
169 template<class Scanner>
|
Chris@16
|
170 struct definition
|
Chris@16
|
171 {
|
Chris@101
|
172
|
Chris@16
|
173 boost::spirit::classic::rule<Scanner>
|
Chris@16
|
174 root, object, member, array, item, value, string, number;
|
Chris@16
|
175 boost::spirit::classic::rule<
|
Chris@16
|
176 typename boost::spirit::classic::lexeme_scanner<Scanner>::type>
|
Chris@16
|
177 character, escape;
|
Chris@16
|
178
|
Chris@16
|
179 definition(const json_grammar &self)
|
Chris@16
|
180 {
|
Chris@16
|
181
|
Chris@16
|
182 using namespace boost::spirit::classic;
|
Chris@16
|
183 // There's a boost::assertion too, so another explicit using
|
Chris@16
|
184 // here:
|
Chris@16
|
185 using boost::spirit::classic::assertion;
|
Chris@16
|
186
|
Chris@16
|
187 // Assertions
|
Chris@16
|
188 assertion<std::string> expect_root("expected object or array");
|
Chris@16
|
189 assertion<std::string> expect_eoi("expected end of input");
|
Chris@16
|
190 assertion<std::string> expect_objclose("expected ',' or '}'");
|
Chris@16
|
191 assertion<std::string> expect_arrclose("expected ',' or ']'");
|
Chris@16
|
192 assertion<std::string> expect_name("expected object name");
|
Chris@16
|
193 assertion<std::string> expect_colon("expected ':'");
|
Chris@16
|
194 assertion<std::string> expect_value("expected value");
|
Chris@16
|
195 assertion<std::string> expect_escape("invalid escape sequence");
|
Chris@16
|
196
|
Chris@16
|
197 // JSON grammar rules
|
Chris@101
|
198 root
|
Chris@101
|
199 = expect_root(object | array)
|
Chris@16
|
200 >> expect_eoi(end_p)
|
Chris@16
|
201 ;
|
Chris@101
|
202
|
Chris@101
|
203 object
|
Chris@16
|
204 = ch_p('{')[typename Context::a_object_s(self.c)]
|
Chris@101
|
205 >> (ch_p('}')[typename Context::a_object_e(self.c)]
|
Chris@16
|
206 | (list_p(member, ch_p(','))
|
Chris@16
|
207 >> expect_objclose(ch_p('}')[typename Context::a_object_e(self.c)])
|
Chris@16
|
208 )
|
Chris@16
|
209 )
|
Chris@16
|
210 ;
|
Chris@101
|
211
|
Chris@101
|
212 member
|
Chris@101
|
213 = expect_name(string[typename Context::a_name(self.c)])
|
Chris@101
|
214 >> expect_colon(ch_p(':'))
|
Chris@16
|
215 >> expect_value(value)
|
Chris@16
|
216 ;
|
Chris@101
|
217
|
Chris@101
|
218 array
|
Chris@16
|
219 = ch_p('[')[typename Context::a_object_s(self.c)]
|
Chris@101
|
220 >> (ch_p(']')[typename Context::a_object_e(self.c)]
|
Chris@16
|
221 | (list_p(item, ch_p(','))
|
Chris@16
|
222 >> expect_arrclose(ch_p(']')[typename Context::a_object_e(self.c)])
|
Chris@16
|
223 )
|
Chris@16
|
224 )
|
Chris@16
|
225 ;
|
Chris@16
|
226
|
Chris@101
|
227 item
|
Chris@16
|
228 = expect_value(value)
|
Chris@16
|
229 ;
|
Chris@16
|
230
|
Chris@101
|
231 value
|
Chris@101
|
232 = string[typename Context::a_string_val(self.c)]
|
Chris@16
|
233 | (number | str_p("true") | "false" | "null")[typename Context::a_literal_val(self.c)]
|
Chris@101
|
234 | object
|
Chris@16
|
235 | array
|
Chris@16
|
236 ;
|
Chris@101
|
237
|
Chris@101
|
238 number
|
Chris@16
|
239 = !ch_p("-") >>
|
Chris@16
|
240 (ch_p("0") | (range_p(Ch('1'), Ch('9')) >> *digit_p)) >>
|
Chris@16
|
241 !(ch_p(".") >> +digit_p) >>
|
Chris@101
|
242 !(chset_p(detail::widen<Str>("eE").c_str()) >>
|
Chris@101
|
243 !chset_p(detail::widen<Str>("-+").c_str()) >>
|
Chris@16
|
244 +digit_p)
|
Chris@16
|
245 ;
|
Chris@16
|
246
|
Chris@16
|
247 string
|
Chris@16
|
248 = +(lexeme_d[confix_p('\"', *character, '\"')])
|
Chris@16
|
249 ;
|
Chris@16
|
250
|
Chris@16
|
251 character
|
Chris@16
|
252 = (anychar_p - "\\" - "\"")
|
Chris@16
|
253 [typename Context::a_char(self.c)]
|
Chris@16
|
254 | ch_p("\\") >> expect_escape(escape)
|
Chris@16
|
255 ;
|
Chris@16
|
256
|
Chris@16
|
257 escape
|
Chris@101
|
258 = chset_p(detail::widen<Str>("\"\\/bfnrt").c_str())
|
Chris@16
|
259 [typename Context::a_escape(self.c)]
|
Chris@16
|
260 | 'u' >> uint_parser<unsigned long, 16, 4, 4>()
|
Chris@16
|
261 [typename Context::a_unicode(self.c)]
|
Chris@16
|
262 ;
|
Chris@16
|
263
|
Chris@16
|
264 // Debug
|
Chris@16
|
265 BOOST_SPIRIT_DEBUG_RULE(root);
|
Chris@16
|
266 BOOST_SPIRIT_DEBUG_RULE(object);
|
Chris@16
|
267 BOOST_SPIRIT_DEBUG_RULE(member);
|
Chris@16
|
268 BOOST_SPIRIT_DEBUG_RULE(array);
|
Chris@16
|
269 BOOST_SPIRIT_DEBUG_RULE(item);
|
Chris@16
|
270 BOOST_SPIRIT_DEBUG_RULE(value);
|
Chris@16
|
271 BOOST_SPIRIT_DEBUG_RULE(string);
|
Chris@16
|
272 BOOST_SPIRIT_DEBUG_RULE(number);
|
Chris@16
|
273 BOOST_SPIRIT_DEBUG_RULE(escape);
|
Chris@16
|
274 BOOST_SPIRIT_DEBUG_RULE(character);
|
Chris@16
|
275
|
Chris@16
|
276 }
|
Chris@16
|
277
|
Chris@16
|
278 const boost::spirit::classic::rule<Scanner> &start() const
|
Chris@16
|
279 {
|
Chris@16
|
280 return root;
|
Chris@16
|
281 }
|
Chris@16
|
282
|
Chris@16
|
283 };
|
Chris@16
|
284
|
Chris@16
|
285 };
|
Chris@16
|
286
|
Chris@16
|
287 template<class It, class Ch>
|
Chris@16
|
288 unsigned long count_lines(It begin, It end)
|
Chris@16
|
289 {
|
Chris@16
|
290 return static_cast<unsigned long>(std::count(begin, end, Ch('\n')) + 1);
|
Chris@16
|
291 }
|
Chris@16
|
292
|
Chris@16
|
293 template<class Ptree>
|
Chris@16
|
294 void read_json_internal(std::basic_istream<typename Ptree::key_type::value_type> &stream,
|
Chris@16
|
295 Ptree &pt,
|
Chris@16
|
296 const std::string &filename)
|
Chris@16
|
297 {
|
Chris@16
|
298
|
Chris@16
|
299 using namespace boost::spirit::classic;
|
Chris@16
|
300 typedef typename Ptree::key_type::value_type Ch;
|
Chris@16
|
301 typedef typename std::vector<Ch>::iterator It;
|
Chris@16
|
302
|
Chris@16
|
303 // Load data into vector
|
Chris@16
|
304 std::vector<Ch> v(std::istreambuf_iterator<Ch>(stream.rdbuf()),
|
Chris@16
|
305 std::istreambuf_iterator<Ch>());
|
Chris@16
|
306 if (!stream.good())
|
Chris@16
|
307 BOOST_PROPERTY_TREE_THROW(json_parser_error("read error", filename, 0));
|
Chris@101
|
308
|
Chris@16
|
309 // Prepare grammar
|
Chris@16
|
310 json_grammar<Ptree> g;
|
Chris@16
|
311
|
Chris@16
|
312 // Parse
|
Chris@16
|
313 try
|
Chris@16
|
314 {
|
Chris@101
|
315 parse_info<It> pi = parse(v.begin(), v.end(), g,
|
Chris@16
|
316 space_p | comment_p("//") | comment_p("/*", "*/"));
|
Chris@16
|
317 if (!pi.hit || !pi.full)
|
Chris@16
|
318 BOOST_PROPERTY_TREE_THROW((parser_error<std::string, It>(v.begin(), "syntax error")));
|
Chris@16
|
319 }
|
Chris@16
|
320 catch (parser_error<std::string, It> &e)
|
Chris@16
|
321 {
|
Chris@16
|
322 BOOST_PROPERTY_TREE_THROW(json_parser_error(e.descriptor, filename, count_lines<It, Ch>(v.begin(), e.where)));
|
Chris@16
|
323 }
|
Chris@16
|
324
|
Chris@16
|
325 // Swap grammar context root and pt
|
Chris@16
|
326 pt.swap(g.c.root);
|
Chris@16
|
327
|
Chris@16
|
328 }
|
Chris@16
|
329
|
Chris@16
|
330 } } }
|
Chris@16
|
331
|
Chris@16
|
332 #endif
|