Orcus
json_parser.hpp
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 */
7
8#ifndef INCLUDED_ORCUS_JSON_PARSER_HPP
9#define INCLUDED_ORCUS_JSON_PARSER_HPP
10
11#include "orcus/json_parser_base.hpp"
12
13#include <cassert>
14#include <cmath>
15
16namespace orcus {
17
22template<typename _Handler>
24{
25public:
26 typedef _Handler handler_type;
27
35 json_parser(const char* p, size_t n, handler_type& hdl);
36
40 void parse();
41
42private:
43 void root_value();
44 void value();
45 void array();
46 void end_array();
47 void object();
48 void number();
49 void string();
50
51private:
52 handler_type& m_handler;
53};
54
55template<typename _Handler>
57 const char* p, size_t n, handler_type& hdl) :
58 json::parser_base(p, n), m_handler(hdl) {}
59
60template<typename _Handler>
62{
63 m_handler.begin_parse();
64
65 skip_ws();
66 if (has_char())
67 root_value();
68 else
69 throw json::parse_error("parse: no json content could be found in file", offset());
70
71 if (has_char())
72 throw json::parse_error("parse: unexpected trailing string segment.", offset());
73
74 m_handler.end_parse();
75}
76
77template<typename _Handler>
79{
80 char c = cur_char();
81
82 switch (c)
83 {
84 case '[':
85 array();
86 break;
87 case '{':
88 object();
89 break;
90 default:
91 json::parse_error::throw_with(
92 "root_value: either '[' or '{' was expected, but '", cur_char(), "' was found.", offset());
93 }
94}
95
96template<typename _Handler>
97void json_parser<_Handler>::value()
98{
99 char c = cur_char();
100 if (is_numeric(c))
101 {
102 number();
103 return;
104 }
105
106 switch (c)
107 {
108 case '-':
109 number();
110 break;
111 case '[':
112 array();
113 break;
114 case '{':
115 object();
116 break;
117 case 't':
118 parse_true();
119 m_handler.boolean_true();
120 break;
121 case 'f':
122 parse_false();
123 m_handler.boolean_false();
124 break;
125 case 'n':
126 parse_null();
127 m_handler.null();
128 break;
129 case '"':
130 string();
131 break;
132 default:
133 json::parse_error::throw_with("value: failed to parse '", cur_char(), "'.", offset());
134 }
135}
136
137template<typename _Handler>
138void json_parser<_Handler>::array()
139{
140 assert(cur_char() == '[');
141
142 m_handler.begin_array();
143 for (next(); has_char(); next())
144 {
145 skip_ws();
146
147 if (cur_char() == ']')
148 {
149 end_array();
150 return;
151 }
152
153 value();
154 skip_ws();
155
156 if (has_char())
157 {
158 switch (cur_char())
159 {
160 case ']':
161 end_array();
162 return;
163 case ',':
164 if (next_char() == ']')
165 {
166 json::parse_error::throw_with(
167 "array: ']' expected but '", cur_char(), "' found.", offset() );
168 }
169 continue;
170 default:
171 json::parse_error::throw_with(
172 "array: either ']' or ',' expected, but '", cur_char(), "' found.", offset());
173 }
174 }
175 else
176 {
177 // needs to be handled here,
178 // we would call next() before checking again with has_char() which
179 // is already past the end
180 break;
181 }
182 }
183
184 throw json::parse_error("array: failed to parse array.", offset());
185}
186
187template<typename _Handler>
188void json_parser<_Handler>::end_array()
189{
190 m_handler.end_array();
191 next();
192 skip_ws();
193}
194
195template<typename _Handler>
196void json_parser<_Handler>::object()
197{
198 assert(cur_char() == '{');
199
200 bool require_new_key = false;
201 m_handler.begin_object();
202 for (next(); has_char(); next())
203 {
204 skip_ws();
205 if (!has_char())
206 throw json::parse_error("object: stream ended prematurely before reaching a key.", offset());
207
208 switch (cur_char())
209 {
210 case '}':
211 if (require_new_key)
212 {
213 json::parse_error::throw_with(
214 "object: new key expected, but '", cur_char(), "' found.", offset());
215 }
216 m_handler.end_object();
217 next();
218 skip_ws();
219 return;
220 case '"':
221 break;
222 default:
223 json::parse_error::throw_with(
224 "object: '\"' was expected, but '", cur_char(), "' found.", offset());
225 }
226 require_new_key = false;
227
228 parse_quoted_string_state res = parse_string();
229 if (!res.str)
230 {
231 // Parsing was unsuccessful.
232 if (res.length == parse_quoted_string_state::error_no_closing_quote)
233 throw json::parse_error("object: stream ended prematurely before reaching the closing quote of a key.", offset());
234 else if (res.length == parse_quoted_string_state::error_illegal_escape_char)
235 json::parse_error::throw_with(
236 "object: illegal escape character '", cur_char(), "' in key value.", offset());
237 else
238 throw json::parse_error("object: unknown error while parsing a key value.", offset());
239 }
240
241 m_handler.object_key(res.str, res.length, res.transient);
242
243 skip_ws();
244 if (cur_char() != ':')
245 json::parse_error::throw_with(
246 "object: ':' was expected, but '", cur_char(), "' found.", offset());
247
248 next();
249 skip_ws();
250
251 if (!has_char())
252 throw json::parse_error("object: stream ended prematurely before reaching a value.", offset());
253
254 value();
255
256 skip_ws();
257 if (!has_char())
258 throw json::parse_error("object: stream ended prematurely before reaching either '}' or ','.", offset());
259
260 switch (cur_char())
261 {
262 case '}':
263 m_handler.end_object();
264 next();
265 skip_ws();
266 return;
267 case ',':
268 require_new_key = true;
269 continue;
270 default:
271 json::parse_error::throw_with(
272 "object: either '}' or ',' expected, but '", cur_char(), "' found.", offset());
273 }
274 }
275
276 throw json::parse_error("object: closing '}' was never reached.", offset());
277}
278
279template<typename _Handler>
280void json_parser<_Handler>::number()
281{
282 assert(is_numeric(cur_char()) || cur_char() == '-');
283
284 double val = parse_double_or_throw();
285 m_handler.number(val);
286 skip_ws();
287}
288
289template<typename _Handler>
290void json_parser<_Handler>::string()
291{
292 parse_quoted_string_state res = parse_string();
293 if (res.str)
294 {
295 m_handler.string(res.str, res.length, res.transient);
296 return;
297 }
298
299 // Parsing was unsuccessful.
300 if (res.length == parse_quoted_string_state::error_no_closing_quote)
301 throw json::parse_error("string: stream ended prematurely before reaching the closing quote.", offset());
302 else if (res.length == parse_quoted_string_state::error_illegal_escape_char)
303 json::parse_error::throw_with("string: illegal escape character '", cur_char(), "'.", offset());
304 else
305 throw json::parse_error("string: unknown error.", offset());
306}
307
308}
309
310#endif
311
312/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Definition: json_parser_base.hpp:19
Definition: json_parser_base.hpp:31
Definition: json_parser.hpp:24
json_parser(const char *p, size_t n, handler_type &hdl)
Definition: json_parser.hpp:56
void parse()
Definition: json_parser.hpp:61
Definition: parser_base.hpp:40