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 object();
47  void number();
48  void number_with_exp(double base);
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_blanks();
66  if (has_char())
67  root_value();
68 
69  if (has_char())
70  throw json::parse_error("parse: unexpected trailing string segment.", offset());
71 
72  m_handler.end_parse();
73 }
74 
75 template<typename _Handler>
77 {
78  char c = cur_char();
79 
80  switch (c)
81  {
82  case '[':
83  array();
84  break;
85  case '{':
86  object();
87  break;
88  default:
89  json::parse_error::throw_with(
90  "root_value: either '[' or '{' was expected, but '", cur_char(), "' was found.", offset());
91  }
92 }
93 
94 template<typename _Handler>
96 {
97  char c = cur_char();
98  if (is_numeric(c))
99  {
100  number();
101  return;
102  }
103 
104  switch (c)
105  {
106  case '-':
107  number();
108  break;
109  case '[':
110  array();
111  break;
112  case '{':
113  object();
114  break;
115  case 't':
116  parse_true();
117  m_handler.boolean_true();
118  break;
119  case 'f':
120  parse_false();
121  m_handler.boolean_false();
122  break;
123  case 'n':
124  parse_null();
125  m_handler.null();
126  break;
127  case '"':
128  string();
129  break;
130  default:
131  json::parse_error::throw_with("value: failed to parse '", cur_char(), "'.", offset());
132  }
133 }
134 
135 template<typename _Handler>
137 {
138  assert(cur_char() == '[');
139 
140  m_handler.begin_array();
141  for (next(); has_char(); next())
142  {
143  if (cur_char() == ']')
144  {
145  m_handler.end_array();
146  next();
147  skip_blanks();
148  return;
149  }
150 
151  skip_blanks();
152  value();
153  skip_blanks();
154 
155  if (has_char())
156  {
157  switch (cur_char())
158  {
159  case ']':
160  m_handler.end_array();
161  next();
162  skip_blanks();
163  return;
164  case ',':
165  continue;
166  default:
167  json::parse_error::throw_with(
168  "array: either ']' or ',' expected, but '", cur_char(), "' found.", offset());
169  }
170  }
171  }
172 
173  throw json::parse_error("array: failed to parse array.", offset());
174 }
175 
176 template<typename _Handler>
178 {
179  assert(cur_char() == '{');
180 
181  m_handler.begin_object();
182  for (next(); has_char(); next())
183  {
184  skip_blanks();
185  if (!has_char())
186  throw json::parse_error("object: stream ended prematurely before reaching a key.", offset());
187 
188  switch (cur_char())
189  {
190  case '}':
191  m_handler.end_object();
192  next();
193  skip_blanks();
194  return;
195  case '"':
196  break;
197  default:
198  json::parse_error::throw_with(
199  "object: '\"' was expected, but '", cur_char(), "' found.", offset());
200  }
201 
202  parse_quoted_string_state res = parse_string();
203  if (!res.str)
204  {
205  // Parsing was unsuccessful.
206  if (res.length == parse_quoted_string_state::error_no_closing_quote)
207  throw json::parse_error("object: stream ended prematurely before reaching the closing quote of a key.", offset());
208  else if (res.length == parse_quoted_string_state::error_illegal_escape_char)
209  json::parse_error::throw_with(
210  "object: illegal escape character '", cur_char(), "' in key value.", offset());
211  else
212  throw json::parse_error("object: unknown error while parsing a key value.", offset());
213  }
214 
215  m_handler.object_key(res.str, res.length, res.transient);
216 
217  skip_blanks();
218  if (cur_char() != ':')
219  json::parse_error::throw_with(
220  "object: ':' was expected, but '", cur_char(), "' found.", offset());
221 
222  next();
223  skip_blanks();
224 
225  if (!has_char())
226  throw json::parse_error("object: stream ended prematurely before reaching a value.", offset());
227 
228  value();
229 
230  skip_blanks();
231  if (!has_char())
232  throw json::parse_error("object: stream ended prematurely before reaching either ']' or ','.", offset());
233 
234  switch (cur_char())
235  {
236  case '}':
237  m_handler.end_object();
238  next();
239  skip_blanks();
240  return;
241  case ',':
242  continue;
243  default:
244  json::parse_error::throw_with(
245  "object: either ']' or ',' expected, but '", cur_char(), "' found.", offset());
246  }
247  }
248 
249  throw json::parse_error("object: closing '}' was never reached.", offset());
250 }
251 
252 template<typename _Handler>
254 {
255  assert(is_numeric(cur_char()) || cur_char() == '-');
256 
257  double val = parse_double_or_throw();
258  switch (cur_char())
259  {
260  case 'e':
261  case 'E':
262  number_with_exp(val);
263  return;
264  default:
265  ;
266  }
267  m_handler.number(val);
268  skip_blanks();
269 }
270 
271 template<typename _Handler>
273 {
274  assert(cur_char() == 'e' || cur_char() == 'E');
275  next();
276  if (!has_char())
277  throw json::parse_error("number_with_exp: illegal exponent value.", offset());
278 
279  long exp = parse_long_or_throw();
280  base *= std::pow(10.0, exp);
281  m_handler.number(base);
282  skip_blanks();
283 }
284 
285 template<typename _Handler>
287 {
288  parse_quoted_string_state res = parse_string();
289  if (res.str)
290  {
291  m_handler.string(res.str, res.length, res.transient);
292  return;
293  }
294 
295  // Parsing was unsuccessful.
296  if (res.length == parse_quoted_string_state::error_no_closing_quote)
297  throw json::parse_error("string: stream ended prematurely before reaching the closing quote.", offset());
298  else if (res.length == parse_quoted_string_state::error_illegal_escape_char)
299  json::parse_error::throw_with("string: illegal escape character '", cur_char(), "'.", offset());
300  else
301  throw json::parse_error("string: unknown error.", offset());
302 }
303 
304 }
305 
306 #endif
307 
308 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void parse()
Definition: json_parser.hpp:61
bool transient
Definition: parser_global.hpp:50
json_parser(const char *p, size_t n, handler_type &hdl)
Definition: json_parser.hpp:56
Definition: json_parser_base.hpp:30
std::ptrdiff_t offset() const
Definition: json_parser_base.hpp:18
Definition: json_parser.hpp:23
Definition: parser_base.hpp:34
Definition: parser_global.hpp:32
Definition: base64.hpp:15