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 
16 namespace orcus {
17 
22 template<typename _Handler>
24 {
25 public:
26  typedef _Handler handler_type;
27 
35  json_parser(const char* p, size_t n, handler_type& hdl);
36 
40  void parse();
41 
42 private:
43  void root_value();
44  void value();
45  void array();
46  void end_array();
47  void object();
48  void number();
49  void string();
50 
51 private:
52  handler_type& m_handler;
53 };
54 
55 template<typename _Handler>
57  const char* p, size_t n, handler_type& hdl) :
58  json::parser_base(p, n), m_handler(hdl) {}
59 
60 template<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 
77 template<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 
96 template<typename _Handler>
97 void 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 
137 template<typename _Handler>
138 void 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 
187 template<typename _Handler>
188 void json_parser<_Handler>::end_array()
189 {
190  m_handler.end_array();
191  next();
192  skip_ws();
193 }
194 
195 template<typename _Handler>
196 void 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 
279 template<typename _Handler>
280 void 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 
289 template<typename _Handler>
290 void 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: */
orcus::json::parse_error
Definition: json_parser_base.hpp:18
orcus::json_parser
Definition: json_parser.hpp:23
orcus::json_parser::parse
void parse()
Definition: json_parser.hpp:61
orcus::json::parser_base
Definition: json_parser_base.hpp:30
orcus::parser_base
Definition: parser_base.hpp:39
orcus::json_parser::json_parser
json_parser(const char *p, size_t n, handler_type &hdl)
Definition: json_parser.hpp:56