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  if (res.length == parse_quoted_string_state::error_no_closing_quote)
208  throw json::parse_error("object: stream ended prematurely before reaching the closing quote of a key.", offset());
209  else if (res.length == parse_quoted_string_state::error_illegal_escape_char)
210  json::parse_error::throw_with(
211  "object: illegal escape character '", cur_char(), "' in key value.", offset());
212  else
213  throw json::parse_error("object: unknown error while parsing a key value.", offset());
214  }
215 
216  m_handler.object_key(res.str, res.length, res.transient);
217 
218  skip_blanks();
219  if (cur_char() != ':')
220  json::parse_error::throw_with(
221  "object: ':' was expected, but '", cur_char(), "' found.", offset());
222 
223  next();
224  skip_blanks();
225 
226  if (!has_char())
227  throw json::parse_error("object: stream ended prematurely before reaching a value.", offset());
228 
229  value();
230 
231  skip_blanks();
232  if (!has_char())
233  throw json::parse_error("object: stream ended prematurely before reaching either ']' or ','.", offset());
234 
235  switch (cur_char())
236  {
237  case '}':
238  m_handler.end_object();
239  next();
240  skip_blanks();
241  return;
242  case ',':
243  continue;
244  default:
245  json::parse_error::throw_with(
246  "object: either ']' or ',' expected, but '", cur_char(), "' found.", offset());
247  }
248  }
249 
250  throw json::parse_error("object: closing '}' was never reached.", offset());
251 }
252 
253 template<typename _Handler>
255 {
256  assert(is_numeric(cur_char()) || cur_char() == '-');
257 
258  double val = parse_double_or_throw();
259  switch (cur_char())
260  {
261  case 'e':
262  case 'E':
263  number_with_exp(val);
264  return;
265  default:
266  ;
267  }
268  m_handler.number(val);
269  skip_blanks();
270 }
271 
272 template<typename _Handler>
274 {
275  assert(cur_char() == 'e' || cur_char() == 'E');
276  next();
277  if (!has_char())
278  throw json::parse_error("number_with_exp: illegal exponent value.", offset());
279 
280  long exp = parse_long_or_throw();
281  base *= std::pow(10.0, exp);
282  m_handler.number(base);
283  skip_blanks();
284 }
285 
286 template<typename _Handler>
288 {
289  parse_quoted_string_state res = parse_string();
290  if (res.str)
291  {
292  m_handler.string(res.str, res.length, res.transient);
293  return;
294  }
295 
296  // Parsing was unsuccessful.
297  if (res.length == parse_quoted_string_state::error_no_closing_quote)
298  throw json::parse_error("string: stream ended prematurely before reaching the closing quote.", offset());
299  else if (res.length == parse_quoted_string_state::error_illegal_escape_char)
300  json::parse_error::throw_with("string: illegal escape character '", cur_char(), "'.", offset());
301  else
302  throw json::parse_error("string: unknown error.", offset());
303 }
304 
305 }
306 
307 #endif
308 
309 /* 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