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