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