2295.1.1
by Brian Aker
Fix up stewart JSON patch. |
1 |
/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
|
2 |
*
|
|
3 |
* JSON Library, originally from http://jsoncpp.sourceforge.net/
|
|
4 |
*
|
|
5 |
* Copyright (C) 2011 Stewart Smith
|
|
6 |
* All rights reserved.
|
|
7 |
*
|
|
8 |
* Redistribution and use in source and binary forms, with or without
|
|
9 |
* modification, are permitted provided that the following conditions are
|
|
10 |
* met:
|
|
11 |
*
|
|
12 |
* * Redistributions of source code must retain the above copyright
|
|
13 |
* notice, this list of conditions and the following disclaimer.
|
|
14 |
*
|
|
15 |
* * Redistributions in binary form must reproduce the above
|
|
16 |
* copyright notice, this list of conditions and the following disclaimer
|
|
17 |
* in the documentation and/or other materials provided with the
|
|
18 |
* distribution.
|
|
19 |
*
|
|
20 |
* * The names of its contributors may not be used to endorse or
|
|
21 |
* promote products derived from this software without specific prior
|
|
22 |
* written permission.
|
|
23 |
*
|
|
24 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
25 |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
26 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
27 |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
28 |
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
29 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
30 |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
31 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
32 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
33 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
34 |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
35 |
*
|
|
36 |
*/
|
|
37 |
||
38 |
#include <config.h> |
|
39 |
||
2283.4.3
by Stewart Smith
add JSON library to json_server, fix up for building in our tree, and add simple /0.1/version JSON response as well as latest_api_version snippet to / |
40 |
#include <plugin/json_server/json/reader.h> |
41 |
#include <plugin/json_server/json/value.h> |
|
2295.1.1
by Brian Aker
Fix up stewart JSON patch. |
42 |
|
43 |
#include <cassert> |
|
2283.4.3
by Stewart Smith
add JSON library to json_server, fix up for building in our tree, and add simple /0.1/version JSON response as well as latest_api_version snippet to / |
44 |
#include <cstdio> |
45 |
#include <cstring> |
|
46 |
#include <iostream> |
|
47 |
#include <stdexcept> |
|
2295.1.1
by Brian Aker
Fix up stewart JSON patch. |
48 |
#include <utility> |
2283.4.3
by Stewart Smith
add JSON library to json_server, fix up for building in our tree, and add simple /0.1/version JSON response as well as latest_api_version snippet to / |
49 |
|
50 |
namespace Json { |
|
51 |
||
52 |
// Implementation of class Features
|
|
53 |
// ////////////////////////////////
|
|
54 |
||
55 |
Features::Features() |
|
56 |
: allowComments_( true ) |
|
57 |
, strictRoot_( false ) |
|
58 |
{
|
|
59 |
}
|
|
60 |
||
61 |
||
62 |
Features
|
|
63 |
Features::all() |
|
64 |
{
|
|
65 |
return Features(); |
|
66 |
}
|
|
67 |
||
68 |
||
69 |
Features
|
|
70 |
Features::strictMode() |
|
71 |
{
|
|
72 |
Features features; |
|
73 |
features.allowComments_ = false; |
|
74 |
features.strictRoot_ = true; |
|
75 |
return features; |
|
76 |
}
|
|
77 |
||
78 |
// Implementation of class Reader
|
|
79 |
// ////////////////////////////////
|
|
80 |
||
81 |
||
82 |
static inline bool |
|
83 |
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) |
|
84 |
{
|
|
85 |
return c == c1 || c == c2 || c == c3 || c == c4; |
|
86 |
}
|
|
87 |
||
88 |
static inline bool |
|
89 |
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) |
|
90 |
{
|
|
91 |
return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; |
|
92 |
}
|
|
93 |
||
94 |
||
95 |
static bool |
|
96 |
containsNewLine( Reader::Location begin, |
|
97 |
Reader::Location end ) |
|
98 |
{
|
|
99 |
for ( ;begin < end; ++begin ) |
|
100 |
if ( *begin == '\n' || *begin == '\r' ) |
|
101 |
return true; |
|
102 |
return false; |
|
103 |
}
|
|
104 |
||
105 |
static std::string codePointToUTF8(unsigned int cp) |
|
106 |
{
|
|
107 |
std::string result; |
|
108 |
||
109 |
// based on description from http://en.wikipedia.org/wiki/UTF-8
|
|
110 |
||
111 |
if (cp <= 0x7f) |
|
112 |
{
|
|
113 |
result.resize(1); |
|
114 |
result[0] = static_cast<char>(cp); |
|
115 |
}
|
|
116 |
else if (cp <= 0x7FF) |
|
117 |
{
|
|
118 |
result.resize(2); |
|
119 |
result[1] = static_cast<char>(0x80 | (0x3f & cp)); |
|
120 |
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6))); |
|
121 |
}
|
|
122 |
else if (cp <= 0xFFFF) |
|
123 |
{
|
|
124 |
result.resize(3); |
|
125 |
result[2] = static_cast<char>(0x80 | (0x3f & cp)); |
|
126 |
result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6))); |
|
127 |
result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12))); |
|
128 |
}
|
|
129 |
else if (cp <= 0x10FFFF) |
|
130 |
{
|
|
131 |
result.resize(4); |
|
132 |
result[3] = static_cast<char>(0x80 | (0x3f & cp)); |
|
133 |
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); |
|
134 |
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12))); |
|
135 |
result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18))); |
|
136 |
}
|
|
137 |
||
138 |
return result; |
|
139 |
}
|
|
140 |
||
141 |
||
142 |
// Class Reader
|
|
143 |
// //////////////////////////////////////////////////////////////////
|
|
144 |
||
145 |
Reader::Reader() |
|
146 |
: features_( Features::all() ) |
|
147 |
{
|
|
148 |
}
|
|
149 |
||
150 |
||
151 |
Reader::Reader( const Features &features ) |
|
152 |
: features_( features ) |
|
153 |
{
|
|
154 |
}
|
|
155 |
||
156 |
||
157 |
bool
|
|
158 |
Reader::parse( const std::string &document, |
|
159 |
Value &root, |
|
160 |
bool collectComments ) |
|
161 |
{
|
|
162 |
document_ = document; |
|
163 |
const char *begin = document_.c_str(); |
|
164 |
const char *end = begin + document_.length(); |
|
165 |
return parse( begin, end, root, collectComments ); |
|
166 |
}
|
|
167 |
||
168 |
||
169 |
bool
|
|
170 |
Reader::parse( std::istream& sin, |
|
171 |
Value &root, |
|
172 |
bool collectComments ) |
|
173 |
{
|
|
174 |
//std::istream_iterator<char> begin(sin);
|
|
175 |
//std::istream_iterator<char> end;
|
|
176 |
// Those would allow streamed input from a file, if parse() were a
|
|
177 |
// template function.
|
|
178 |
||
179 |
// Since std::string is reference-counted, this at least does not
|
|
180 |
// create an extra copy.
|
|
181 |
std::string doc; |
|
182 |
std::getline(sin, doc, (char)EOF); |
|
183 |
return parse( doc, root, collectComments ); |
|
184 |
}
|
|
185 |
||
186 |
bool
|
|
187 |
Reader::parse( const char *beginDoc, const char *endDoc, |
|
188 |
Value &root, |
|
189 |
bool collectComments ) |
|
190 |
{
|
|
191 |
if ( !features_.allowComments_ ) |
|
192 |
{
|
|
193 |
collectComments = false; |
|
194 |
}
|
|
195 |
||
196 |
begin_ = beginDoc; |
|
197 |
end_ = endDoc; |
|
198 |
collectComments_ = collectComments; |
|
199 |
current_ = begin_; |
|
200 |
lastValueEnd_ = 0; |
|
201 |
lastValue_ = 0; |
|
202 |
commentsBefore_ = ""; |
|
203 |
errors_.clear(); |
|
204 |
while ( !nodes_.empty() ) |
|
205 |
nodes_.pop(); |
|
206 |
nodes_.push( &root ); |
|
207 |
||
208 |
bool successful = readValue(); |
|
209 |
Token token; |
|
210 |
skipCommentTokens( token ); |
|
211 |
if ( collectComments_ && !commentsBefore_.empty() ) |
|
212 |
root.setComment( commentsBefore_, commentAfter ); |
|
213 |
if ( features_.strictRoot_ ) |
|
214 |
{
|
|
215 |
if ( !root.isArray() && !root.isObject() ) |
|
216 |
{
|
|
217 |
// Set error location to start of doc, ideally should be first token found in doc
|
|
218 |
token.type_ = tokenError; |
|
219 |
token.start_ = beginDoc; |
|
220 |
token.end_ = endDoc; |
|
221 |
addError( "A valid JSON document must be either an array or an object value.", |
|
222 |
token ); |
|
223 |
return false; |
|
224 |
}
|
|
225 |
}
|
|
226 |
return successful; |
|
227 |
}
|
|
228 |
||
229 |
||
230 |
bool
|
|
231 |
Reader::readValue() |
|
232 |
{
|
|
233 |
Token token; |
|
234 |
skipCommentTokens( token ); |
|
235 |
bool successful = true; |
|
236 |
||
237 |
if ( collectComments_ && !commentsBefore_.empty() ) |
|
238 |
{
|
|
239 |
currentValue().setComment( commentsBefore_, commentBefore ); |
|
240 |
commentsBefore_ = ""; |
|
241 |
}
|
|
242 |
||
243 |
||
244 |
switch ( token.type_ ) |
|
245 |
{
|
|
246 |
case tokenObjectBegin: |
|
247 |
successful = readObject( token ); |
|
248 |
break; |
|
249 |
case tokenArrayBegin: |
|
250 |
successful = readArray( token ); |
|
251 |
break; |
|
252 |
case tokenNumber: |
|
253 |
successful = decodeNumber( token ); |
|
254 |
break; |
|
255 |
case tokenString: |
|
256 |
successful = decodeString( token ); |
|
257 |
break; |
|
258 |
case tokenTrue: |
|
259 |
currentValue() = true; |
|
260 |
break; |
|
261 |
case tokenFalse: |
|
262 |
currentValue() = false; |
|
263 |
break; |
|
264 |
case tokenNull: |
|
265 |
currentValue() = Value(); |
|
266 |
break; |
|
267 |
default: |
|
268 |
return addError( "Syntax error: value, object or array expected.", token ); |
|
269 |
}
|
|
270 |
||
271 |
if ( collectComments_ ) |
|
272 |
{
|
|
273 |
lastValueEnd_ = current_; |
|
274 |
lastValue_ = ¤tValue(); |
|
275 |
}
|
|
276 |
||
277 |
return successful; |
|
278 |
}
|
|
279 |
||
280 |
||
281 |
void
|
|
282 |
Reader::skipCommentTokens( Token &token ) |
|
283 |
{
|
|
284 |
if ( features_.allowComments_ ) |
|
285 |
{
|
|
286 |
do
|
|
287 |
{
|
|
288 |
readToken( token ); |
|
289 |
}
|
|
290 |
while ( token.type_ == tokenComment ); |
|
291 |
}
|
|
292 |
else
|
|
293 |
{
|
|
294 |
readToken( token ); |
|
295 |
}
|
|
296 |
}
|
|
297 |
||
298 |
||
299 |
bool
|
|
300 |
Reader::expectToken( TokenType type, Token &token, const char *message ) |
|
301 |
{
|
|
302 |
readToken( token ); |
|
303 |
if ( token.type_ != type ) |
|
304 |
return addError( message, token ); |
|
305 |
return true; |
|
306 |
}
|
|
307 |
||
308 |
||
309 |
bool
|
|
310 |
Reader::readToken( Token &token ) |
|
311 |
{
|
|
312 |
skipSpaces(); |
|
313 |
token.start_ = current_; |
|
314 |
Char c = getNextChar(); |
|
315 |
bool ok = true; |
|
316 |
switch ( c ) |
|
317 |
{
|
|
318 |
case '{': |
|
319 |
token.type_ = tokenObjectBegin; |
|
320 |
break; |
|
321 |
case '}': |
|
322 |
token.type_ = tokenObjectEnd; |
|
323 |
break; |
|
324 |
case '[': |
|
325 |
token.type_ = tokenArrayBegin; |
|
326 |
break; |
|
327 |
case ']': |
|
328 |
token.type_ = tokenArrayEnd; |
|
329 |
break; |
|
330 |
case '"': |
|
331 |
token.type_ = tokenString; |
|
332 |
ok = readString(); |
|
333 |
break; |
|
334 |
case '/': |
|
335 |
token.type_ = tokenComment; |
|
336 |
ok = readComment(); |
|
337 |
break; |
|
338 |
case '0': |
|
339 |
case '1': |
|
340 |
case '2': |
|
341 |
case '3': |
|
342 |
case '4': |
|
343 |
case '5': |
|
344 |
case '6': |
|
345 |
case '7': |
|
346 |
case '8': |
|
347 |
case '9': |
|
348 |
case '-': |
|
349 |
token.type_ = tokenNumber; |
|
350 |
readNumber(); |
|
351 |
break; |
|
352 |
case 't': |
|
353 |
token.type_ = tokenTrue; |
|
354 |
ok = match( "rue", 3 ); |
|
355 |
break; |
|
356 |
case 'f': |
|
357 |
token.type_ = tokenFalse; |
|
358 |
ok = match( "alse", 4 ); |
|
359 |
break; |
|
360 |
case 'n': |
|
361 |
token.type_ = tokenNull; |
|
362 |
ok = match( "ull", 3 ); |
|
363 |
break; |
|
364 |
case ',': |
|
365 |
token.type_ = tokenArraySeparator; |
|
366 |
break; |
|
367 |
case ':': |
|
368 |
token.type_ = tokenMemberSeparator; |
|
369 |
break; |
|
370 |
case 0: |
|
371 |
token.type_ = tokenEndOfStream; |
|
372 |
break; |
|
373 |
default: |
|
374 |
ok = false; |
|
375 |
break; |
|
376 |
}
|
|
377 |
if ( !ok ) |
|
378 |
token.type_ = tokenError; |
|
379 |
token.end_ = current_; |
|
380 |
return true; |
|
381 |
}
|
|
382 |
||
383 |
||
384 |
void
|
|
385 |
Reader::skipSpaces() |
|
386 |
{
|
|
387 |
while ( current_ != end_ ) |
|
388 |
{
|
|
389 |
Char c = *current_; |
|
390 |
if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) |
|
391 |
++current_; |
|
392 |
else
|
|
393 |
break; |
|
394 |
}
|
|
395 |
}
|
|
396 |
||
397 |
||
398 |
bool
|
|
399 |
Reader::match( Location pattern, |
|
400 |
int patternLength ) |
|
401 |
{
|
|
402 |
if ( end_ - current_ < patternLength ) |
|
403 |
return false; |
|
404 |
int index = patternLength; |
|
405 |
while ( index-- ) |
|
406 |
if ( current_[index] != pattern[index] ) |
|
407 |
return false; |
|
408 |
current_ += patternLength; |
|
409 |
return true; |
|
410 |
}
|
|
411 |
||
412 |
||
413 |
bool
|
|
414 |
Reader::readComment() |
|
415 |
{
|
|
416 |
Location commentBegin = current_ - 1; |
|
417 |
Char c = getNextChar(); |
|
418 |
bool successful = false; |
|
419 |
if ( c == '*' ) |
|
420 |
successful = readCStyleComment(); |
|
421 |
else if ( c == '/' ) |
|
422 |
successful = readCppStyleComment(); |
|
423 |
if ( !successful ) |
|
424 |
return false; |
|
425 |
||
426 |
if ( collectComments_ ) |
|
427 |
{
|
|
428 |
CommentPlacement placement = commentBefore; |
|
429 |
if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) |
|
430 |
{
|
|
431 |
if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) |
|
432 |
placement = commentAfterOnSameLine; |
|
433 |
}
|
|
434 |
||
435 |
addComment( commentBegin, current_, placement ); |
|
436 |
}
|
|
437 |
return true; |
|
438 |
}
|
|
439 |
||
440 |
||
441 |
void
|
|
442 |
Reader::addComment( Location begin, |
|
443 |
Location end, |
|
444 |
CommentPlacement placement ) |
|
445 |
{
|
|
446 |
assert( collectComments_ ); |
|
447 |
if ( placement == commentAfterOnSameLine ) |
|
448 |
{
|
|
449 |
assert( lastValue_ != 0 ); |
|
450 |
lastValue_->setComment( std::string( begin, end ), placement ); |
|
451 |
}
|
|
452 |
else
|
|
453 |
{
|
|
454 |
if ( !commentsBefore_.empty() ) |
|
455 |
commentsBefore_ += "\n"; |
|
456 |
commentsBefore_ += std::string( begin, end ); |
|
457 |
}
|
|
458 |
}
|
|
459 |
||
460 |
||
461 |
bool
|
|
462 |
Reader::readCStyleComment() |
|
463 |
{
|
|
464 |
while ( current_ != end_ ) |
|
465 |
{
|
|
466 |
Char c = getNextChar(); |
|
467 |
if ( c == '*' && *current_ == '/' ) |
|
468 |
break; |
|
469 |
}
|
|
470 |
return getNextChar() == '/'; |
|
471 |
}
|
|
472 |
||
473 |
||
474 |
bool
|
|
475 |
Reader::readCppStyleComment() |
|
476 |
{
|
|
477 |
while ( current_ != end_ ) |
|
478 |
{
|
|
479 |
Char c = getNextChar(); |
|
480 |
if ( c == '\r' || c == '\n' ) |
|
481 |
break; |
|
482 |
}
|
|
483 |
return true; |
|
484 |
}
|
|
485 |
||
486 |
||
487 |
void
|
|
488 |
Reader::readNumber() |
|
489 |
{
|
|
490 |
while ( current_ != end_ ) |
|
491 |
{
|
|
492 |
if ( !(*current_ >= '0' && *current_ <= '9') && |
|
493 |
!in( *current_, '.', 'e', 'E', '+', '-' ) ) |
|
494 |
break; |
|
495 |
++current_; |
|
496 |
}
|
|
497 |
}
|
|
498 |
||
499 |
bool
|
|
500 |
Reader::readString() |
|
501 |
{
|
|
502 |
Char c = 0; |
|
503 |
while ( current_ != end_ ) |
|
504 |
{
|
|
505 |
c = getNextChar(); |
|
506 |
if ( c == '\\' ) |
|
507 |
getNextChar(); |
|
508 |
else if ( c == '"' ) |
|
509 |
break; |
|
510 |
}
|
|
511 |
return c == '"'; |
|
512 |
}
|
|
513 |
||
514 |
||
515 |
bool
|
|
516 |
Reader::readObject( Token & ) |
|
517 |
{
|
|
518 |
Token tokenName; |
|
519 |
std::string name; |
|
520 |
currentValue() = Value( objectValue ); |
|
521 |
while ( readToken( tokenName ) ) |
|
522 |
{
|
|
523 |
bool initialTokenOk = true; |
|
524 |
while ( tokenName.type_ == tokenComment && initialTokenOk ) |
|
525 |
initialTokenOk = readToken( tokenName ); |
|
526 |
if ( !initialTokenOk ) |
|
527 |
break; |
|
528 |
if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object |
|
529 |
return true; |
|
530 |
if ( tokenName.type_ != tokenString ) |
|
531 |
break; |
|
532 |
||
533 |
name = ""; |
|
534 |
if ( !decodeString( tokenName, name ) ) |
|
535 |
return recoverFromError( tokenObjectEnd ); |
|
536 |
||
537 |
Token colon; |
|
538 |
if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) |
|
539 |
{
|
|
540 |
return addErrorAndRecover( "Missing ':' after object member name", |
|
541 |
colon, |
|
542 |
tokenObjectEnd ); |
|
543 |
}
|
|
544 |
Value &value = currentValue()[ name ]; |
|
545 |
nodes_.push( &value ); |
|
546 |
bool ok = readValue(); |
|
547 |
nodes_.pop(); |
|
548 |
if ( !ok ) // error already set |
|
549 |
return recoverFromError( tokenObjectEnd ); |
|
550 |
||
551 |
Token comma; |
|
552 |
if ( !readToken( comma ) |
|
553 |
|| ( comma.type_ != tokenObjectEnd && |
|
554 |
comma.type_ != tokenArraySeparator && |
|
555 |
comma.type_ != tokenComment ) ) |
|
556 |
{
|
|
557 |
return addErrorAndRecover( "Missing ',' or '}' in object declaration", |
|
558 |
comma, |
|
559 |
tokenObjectEnd ); |
|
560 |
}
|
|
561 |
bool finalizeTokenOk = true; |
|
562 |
while ( comma.type_ == tokenComment && |
|
563 |
finalizeTokenOk ) |
|
564 |
finalizeTokenOk = readToken( comma ); |
|
565 |
if ( comma.type_ == tokenObjectEnd ) |
|
566 |
return true; |
|
567 |
}
|
|
568 |
return addErrorAndRecover( "Missing '}' or object member name", |
|
569 |
tokenName, |
|
570 |
tokenObjectEnd ); |
|
571 |
}
|
|
572 |
||
573 |
||
574 |
bool
|
|
575 |
Reader::readArray( Token & ) |
|
576 |
{
|
|
577 |
currentValue() = Value( arrayValue ); |
|
578 |
skipSpaces(); |
|
579 |
if ( *current_ == ']' ) // empty array |
|
580 |
{
|
|
581 |
Token endArray; |
|
582 |
readToken( endArray ); |
|
583 |
return true; |
|
584 |
}
|
|
585 |
int index = 0; |
|
586 |
while ( true ) |
|
587 |
{
|
|
588 |
Value &value = currentValue()[ index++ ]; |
|
589 |
nodes_.push( &value ); |
|
590 |
bool ok = readValue(); |
|
591 |
nodes_.pop(); |
|
592 |
if ( !ok ) // error already set |
|
593 |
return recoverFromError( tokenArrayEnd ); |
|
594 |
||
595 |
Token token; |
|
596 |
// Accept Comment after last item in the array.
|
|
597 |
ok = readToken( token ); |
|
598 |
while ( token.type_ == tokenComment && ok ) |
|
599 |
{
|
|
600 |
ok = readToken( token ); |
|
601 |
}
|
|
602 |
bool badTokenType = ( token.type_ == tokenArraySeparator && |
|
603 |
token.type_ == tokenArrayEnd ); |
|
604 |
if ( !ok || badTokenType ) |
|
605 |
{
|
|
606 |
return addErrorAndRecover( "Missing ',' or ']' in array declaration", |
|
607 |
token, |
|
608 |
tokenArrayEnd ); |
|
609 |
}
|
|
610 |
if ( token.type_ == tokenArrayEnd ) |
|
611 |
break; |
|
612 |
}
|
|
613 |
return true; |
|
614 |
}
|
|
615 |
||
616 |
||
617 |
bool
|
|
618 |
Reader::decodeNumber( Token &token ) |
|
619 |
{
|
|
620 |
bool isDouble = false; |
|
621 |
for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) |
|
622 |
{
|
|
623 |
isDouble = isDouble |
|
624 |
|| in( *inspect, '.', 'e', 'E', '+' ) |
|
625 |
|| ( *inspect == '-' && inspect != token.start_ ); |
|
626 |
}
|
|
627 |
if ( isDouble ) |
|
628 |
return decodeDouble( token ); |
|
629 |
Location current = token.start_; |
|
630 |
bool isNegative = *current == '-'; |
|
631 |
if ( isNegative ) |
|
632 |
++current; |
|
633 |
Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt) |
|
634 |
: Value::maxUInt) / 10; |
|
635 |
Value::UInt value = 0; |
|
636 |
while ( current < token.end_ ) |
|
637 |
{
|
|
638 |
Char c = *current++; |
|
639 |
if ( c < '0' || c > '9' ) |
|
640 |
return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); |
|
641 |
if ( value >= threshold ) |
|
642 |
return decodeDouble( token ); |
|
643 |
value = value * 10 + Value::UInt(c - '0'); |
|
644 |
}
|
|
645 |
if ( isNegative ) |
|
646 |
currentValue() = -Value::Int( value ); |
|
647 |
else if ( value <= Value::UInt(Value::maxInt) ) |
|
648 |
currentValue() = Value::Int( value ); |
|
649 |
else
|
|
650 |
currentValue() = value; |
|
651 |
return true; |
|
652 |
}
|
|
653 |
||
654 |
||
655 |
bool
|
|
656 |
Reader::decodeDouble( Token &token ) |
|
657 |
{
|
|
658 |
double value = 0; |
|
659 |
const int bufferSize = 32; |
|
660 |
int count; |
|
661 |
int length = int(token.end_ - token.start_); |
|
662 |
if ( length <= bufferSize ) |
|
663 |
{
|
|
664 |
Char buffer[bufferSize]; |
|
665 |
memcpy( buffer, token.start_, length ); |
|
666 |
buffer[length] = 0; |
|
667 |
count = sscanf( buffer, "%lf", &value ); |
|
668 |
}
|
|
669 |
else
|
|
670 |
{
|
|
671 |
std::string buffer( token.start_, token.end_ ); |
|
672 |
count = sscanf( buffer.c_str(), "%lf", &value ); |
|
673 |
}
|
|
674 |
||
675 |
if ( count != 1 ) |
|
676 |
return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); |
|
677 |
currentValue() = value; |
|
678 |
return true; |
|
679 |
}
|
|
680 |
||
681 |
||
682 |
bool
|
|
683 |
Reader::decodeString( Token &token ) |
|
684 |
{
|
|
685 |
std::string decoded; |
|
686 |
if ( !decodeString( token, decoded ) ) |
|
687 |
return false; |
|
688 |
currentValue() = decoded; |
|
689 |
return true; |
|
690 |
}
|
|
691 |
||
692 |
||
693 |
bool
|
|
694 |
Reader::decodeString( Token &token, std::string &decoded ) |
|
695 |
{
|
|
696 |
decoded.reserve( token.end_ - token.start_ - 2 ); |
|
697 |
Location current = token.start_ + 1; // skip '"' |
|
698 |
Location end = token.end_ - 1; // do not include '"' |
|
699 |
while ( current != end ) |
|
700 |
{
|
|
701 |
Char c = *current++; |
|
702 |
if ( c == '"' ) |
|
703 |
break; |
|
704 |
else if ( c == '\\' ) |
|
705 |
{
|
|
706 |
if ( current == end ) |
|
707 |
return addError( "Empty escape sequence in string", token, current ); |
|
708 |
Char escape = *current++; |
|
709 |
switch ( escape ) |
|
710 |
{
|
|
711 |
case '"': decoded += '"'; break; |
|
712 |
case '/': decoded += '/'; break; |
|
713 |
case '\\': decoded += '\\'; break; |
|
714 |
case 'b': decoded += '\b'; break; |
|
715 |
case 'f': decoded += '\f'; break; |
|
716 |
case 'n': decoded += '\n'; break; |
|
717 |
case 'r': decoded += '\r'; break; |
|
718 |
case 't': decoded += '\t'; break; |
|
719 |
case 'u': |
|
720 |
{
|
|
721 |
unsigned int unicode; |
|
722 |
if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) |
|
723 |
return false; |
|
724 |
decoded += codePointToUTF8(unicode); |
|
725 |
}
|
|
726 |
break; |
|
727 |
default: |
|
728 |
return addError( "Bad escape sequence in string", token, current ); |
|
729 |
}
|
|
730 |
}
|
|
731 |
else
|
|
732 |
{
|
|
733 |
decoded += c; |
|
734 |
}
|
|
735 |
}
|
|
736 |
return true; |
|
737 |
}
|
|
738 |
||
739 |
bool
|
|
740 |
Reader::decodeUnicodeCodePoint( Token &token, |
|
741 |
Location ¤t, |
|
742 |
Location end, |
|
743 |
unsigned int &unicode ) |
|
744 |
{
|
|
745 |
||
746 |
if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) |
|
747 |
return false; |
|
748 |
if (unicode >= 0xD800 && unicode <= 0xDBFF) |
|
749 |
{
|
|
750 |
// surrogate pairs
|
|
751 |
if (end - current < 6) |
|
752 |
return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); |
|
753 |
unsigned int surrogatePair; |
|
754 |
if (*(current++) == '\\' && *(current++)== 'u') |
|
755 |
{
|
|
756 |
if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) |
|
757 |
{
|
|
758 |
unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); |
|
759 |
}
|
|
760 |
else
|
|
761 |
return false; |
|
762 |
}
|
|
763 |
else
|
|
764 |
return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); |
|
765 |
}
|
|
766 |
return true; |
|
767 |
}
|
|
768 |
||
769 |
bool
|
|
770 |
Reader::decodeUnicodeEscapeSequence( Token &token, |
|
771 |
Location ¤t, |
|
772 |
Location end, |
|
773 |
unsigned int &unicode ) |
|
774 |
{
|
|
775 |
if ( end - current < 4 ) |
|
776 |
return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); |
|
777 |
unicode = 0; |
|
778 |
for ( int index =0; index < 4; ++index ) |
|
779 |
{
|
|
780 |
Char c = *current++; |
|
781 |
unicode *= 16; |
|
782 |
if ( c >= '0' && c <= '9' ) |
|
783 |
unicode += c - '0'; |
|
784 |
else if ( c >= 'a' && c <= 'f' ) |
|
785 |
unicode += c - 'a' + 10; |
|
786 |
else if ( c >= 'A' && c <= 'F' ) |
|
787 |
unicode += c - 'A' + 10; |
|
788 |
else
|
|
789 |
return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); |
|
790 |
}
|
|
791 |
return true; |
|
792 |
}
|
|
793 |
||
794 |
||
795 |
bool
|
|
796 |
Reader::addError( const std::string &message, |
|
797 |
Token &token, |
|
798 |
Location extra ) |
|
799 |
{
|
|
800 |
ErrorInfo info; |
|
801 |
info.token_ = token; |
|
802 |
info.message_ = message; |
|
803 |
info.extra_ = extra; |
|
804 |
errors_.push_back( info ); |
|
805 |
return false; |
|
806 |
}
|
|
807 |
||
808 |
||
809 |
bool
|
|
810 |
Reader::recoverFromError( TokenType skipUntilToken ) |
|
811 |
{
|
|
812 |
int errorCount = int(errors_.size()); |
|
813 |
Token skip; |
|
814 |
while ( true ) |
|
815 |
{
|
|
816 |
if ( !readToken(skip) ) |
|
817 |
errors_.resize( errorCount ); // discard errors caused by recovery |
|
818 |
if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) |
|
819 |
break; |
|
820 |
}
|
|
821 |
errors_.resize( errorCount ); |
|
822 |
return false; |
|
823 |
}
|
|
824 |
||
825 |
||
826 |
bool
|
|
827 |
Reader::addErrorAndRecover( const std::string &message, |
|
828 |
Token &token, |
|
829 |
TokenType skipUntilToken ) |
|
830 |
{
|
|
831 |
addError( message, token ); |
|
832 |
return recoverFromError( skipUntilToken ); |
|
833 |
}
|
|
834 |
||
835 |
||
836 |
Value & |
|
837 |
Reader::currentValue() |
|
838 |
{
|
|
839 |
return *(nodes_.top()); |
|
840 |
}
|
|
841 |
||
842 |
||
843 |
Reader::Char |
|
844 |
Reader::getNextChar() |
|
845 |
{
|
|
846 |
if ( current_ == end_ ) |
|
847 |
return 0; |
|
848 |
return *current_++; |
|
849 |
}
|
|
850 |
||
851 |
||
852 |
void
|
|
853 |
Reader::getLocationLineAndColumn( Location location, |
|
854 |
int &line, |
|
855 |
int &column ) const |
|
856 |
{
|
|
857 |
Location current = begin_; |
|
858 |
Location lastLineStart = current; |
|
859 |
line = 0; |
|
860 |
while ( current < location && current != end_ ) |
|
861 |
{
|
|
862 |
Char c = *current++; |
|
863 |
if ( c == '\r' ) |
|
864 |
{
|
|
865 |
if ( *current == '\n' ) |
|
866 |
++current; |
|
867 |
lastLineStart = current; |
|
868 |
++line; |
|
869 |
}
|
|
870 |
else if ( c == '\n' ) |
|
871 |
{
|
|
872 |
lastLineStart = current; |
|
873 |
++line; |
|
874 |
}
|
|
875 |
}
|
|
876 |
// column & line start at 1
|
|
877 |
column = int(location - lastLineStart) + 1; |
|
878 |
++line; |
|
879 |
}
|
|
880 |
||
881 |
||
882 |
std::string |
|
883 |
Reader::getLocationLineAndColumn( Location location ) const |
|
884 |
{
|
|
885 |
int line, column; |
|
886 |
getLocationLineAndColumn( location, line, column ); |
|
887 |
char buffer[18+16+16+1]; |
|
888 |
sprintf( buffer, "Line %d, Column %d", line, column ); |
|
889 |
return buffer; |
|
890 |
}
|
|
891 |
||
892 |
||
893 |
std::string |
|
894 |
Reader::getFormatedErrorMessages() const |
|
895 |
{
|
|
896 |
std::string formattedMessage; |
|
897 |
for ( Errors::const_iterator itError = errors_.begin(); |
|
898 |
itError != errors_.end(); |
|
899 |
++itError ) |
|
900 |
{
|
|
901 |
const ErrorInfo &error = *itError; |
|
902 |
formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; |
|
903 |
formattedMessage += " " + error.message_ + "\n"; |
|
904 |
if ( error.extra_ ) |
|
905 |
formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; |
|
906 |
}
|
|
907 |
return formattedMessage; |
|
908 |
}
|
|
909 |
||
910 |
||
911 |
std::istream& operator>>( std::istream &sin, Value &root ) |
|
912 |
{
|
|
913 |
Json::Reader reader; |
|
914 |
bool ok = reader.parse(sin, root, true); |
|
915 |
//JSON_ASSERT( ok );
|
|
916 |
if (!ok) throw std::runtime_error(reader.getFormatedErrorMessages()); |
|
917 |
return sin; |
|
918 |
}
|
|
919 |
||
920 |
||
921 |
} // namespace Json |