ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/rpc/json/reader.C
Revision: 1.3
Committed: Wed Sep 5 11:23:15 2007 UTC (19 years, 1 month ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +2 -2 lines
Log Message:
removed GPLed code and put license back to BSD

File Contents

# Content
1 /*
2 * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
3 * Rights to this code are as documented in doc/pod/license.pod.
4 *
5 * JSON-RPC for Ermyth
6 *
7 * $Id: reader.C,v 1.2 2007-08-28 17:08:12 pippijn Exp $
8 */
9
10 //>>>>>>>>>>> reader.C <<<<<<<<<<<//
11 #include "json/reader.h"
12 #include "json/value.h"
13
14 #include <utility>
15 #include <iostream>
16 #include <stdexcept>
17
18 #include <cassert>
19 #include <cstdio>
20
21 namespace json
22 {
23 static bool
24 in (char c, char c1, char c2, char c3, char c4)
25 {
26 return c == c1 || c == c2 || c == c3 || c == c4;
27 }
28
29 static bool
30 in (char c, char c1, char c2, char c3, char c4, char c5)
31 {
32 return c == c1 || c == c2 || c == c3 || c == c4 || c == c5;
33 }
34
35
36 static bool
37 containsNewLine (Reader::Location begin,
38 Reader::Location end)
39 {
40 for (;begin < end; ++begin)
41 if (*begin == '\012' || *begin == '\015')
42 return true;
43 return false;
44 }
45
46
47 // Class Reader
48 Reader::Reader ()
49 {
50 }
51
52 bool
53 Reader::parse (const std::string &document,
54 Value &root,
55 bool collectComments)
56 {
57 document_ = document;
58 char const *begin = document_.c_str ();
59 char const *end = begin + document_.length ();
60 return parse (begin, end, root, collectComments);
61 }
62
63 bool
64 Reader::parse (std::istream& sin,
65 Value &root,
66 bool collectComments)
67 {
68 #if 0
69 std::istream_iterator<char> begin (sin);
70 std::istream_iterator<char> end;
71 #endif
72 // Those would allow streamed input from a file, if parse () were a
73 // template function.
74
75 // Since std::string is reference-counted, this at least does not
76 // create an extra copy.
77 std::string doc;
78 std::getline (sin, doc, (char)EOF);
79 return parse (doc, root, collectComments);
80 }
81
82 bool
83 Reader::parse (char const *beginDoc, char const *endDOc,
84 Value &root,
85 bool collectComments)
86 {
87 begin_ = beginDoc;
88 end_ = endDOc;
89 collectComments_ = collectComments;
90 current_ = begin_;
91 lastValueEnd_ = 0;
92 lastValue_ = 0;
93 commentsBefore_ = "";
94 errors_.clear ();
95 while (!nodes_.empty ())
96 nodes_.pop ();
97 nodes_.push (&root);
98
99 bool successful = readValue ();
100 Token token;
101 skipCommentTokens (token);
102 if (collectComments_ && !commentsBefore_.empty ())
103 root.setComment (commentsBefore_, commentAfter);
104 return successful;
105 }
106
107
108 bool
109 Reader::readValue ()
110 {
111 Token token;
112 skipCommentTokens (token);
113 bool successful = true;
114
115 if (collectComments_ && !commentsBefore_.empty ())
116 {
117 currentValue ().setComment (commentsBefore_, commentBefore);
118 commentsBefore_ = "";
119 }
120
121
122 switch (token.type_)
123 {
124 case tokenObjectBegin:
125 successful = readObject ();
126 break;
127 case tokenArrayBegin:
128 successful = readArray ();
129 break;
130 case tokenNumber:
131 successful = decodeNumber (token);
132 break;
133 case tokenString:
134 successful = decodeString (token);
135 break;
136 case tokenTrue:
137 currentValue () = true;
138 break;
139 case tokenFalse:
140 currentValue () = false;
141 break;
142 case tokenNull:
143 currentValue () = Value ();
144 break;
145 default:
146 return addError ("Syntax error: value, object or array expected.", token);
147 }
148
149 if (collectComments_)
150 {
151 lastValueEnd_ = current_;
152 lastValue_ = &currentValue ();
153 }
154
155 return successful;
156 }
157
158
159 void
160 Reader::skipCommentTokens (Token &token)
161 {
162 do
163 {
164 readToken (token);
165 }
166 while (token.type_ == tokenComment);
167 }
168
169
170 bool
171 Reader::expectToken (TokenType type, Token &token, char const *message)
172 {
173 readToken (token);
174 if (token.type_ != type)
175 return addError (message, token);
176 return true;
177 }
178
179
180 bool
181 Reader::readToken (Token &token)
182 {
183 skipSpaces ();
184 token.start_ = current_;
185 char c = getNextChar ();
186 bool ok = true;
187 switch (c)
188 {
189 case '{':
190 token.type_ = tokenObjectBegin;
191 break;
192 case '}':
193 token.type_ = tokenObjectEnd;
194 break;
195 case '[':
196 token.type_ = tokenArrayBegin;
197 break;
198 case ']':
199 token.type_ = tokenArrayEnd;
200 break;
201 case '"':
202 token.type_ = tokenString;
203 ok = readString ();
204 break;
205 case '/':
206 token.type_ = tokenComment;
207 ok = readComment ();
208 break;
209 #if 0
210 #ifdef __GNUC__
211 case '0'...'9':
212 #endif
213 #else
214 case '0': case '1': case '2': case '3':
215 case '4': case '5': case '6': case '7':
216 case '8': case '9':
217 #endif
218 case '-':
219 token.type_ = tokenNumber;
220 readNumber ();
221 break;
222 case 't':
223 token.type_ = tokenTrue;
224 ok = match ("rue", 3);
225 break;
226 case 'f':
227 token.type_ = tokenFalse;
228 ok = match ("alse", 4);
229 break;
230 case 'n':
231 token.type_ = tokenNull;
232 ok = match ("ull", 3);
233 break;
234 case ',':
235 token.type_ = tokenArraySeparator;
236 break;
237 case ':':
238 token.type_ = tokenMemberSeparator;
239 break;
240 case 0:
241 token.type_ = tokenEndOfStream;
242 break;
243 default:
244 ok = false;
245 break;
246 }
247 if (!ok)
248 token.type_ = tokenError;
249 token.end_ = current_;
250 return true;
251 }
252
253
254 void
255 Reader::skipSpaces ()
256 {
257 while (current_ != end_)
258 {
259 char c = *current_;
260 if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
261 ++current_;
262 else
263 break;
264 }
265 }
266
267
268 bool
269 Reader::match (Location pattern, int patternLength)
270 {
271 if (end_ - current_ < patternLength)
272 return false;
273 int index = patternLength;
274 while (index--)
275 if (current_[index] != pattern[index])
276 return false;
277 current_ += patternLength;
278 return true;
279 }
280
281
282 bool
283 Reader::readComment ()
284 {
285 Location commentBegin = current_ - 1;
286 char c = getNextChar ();
287 bool successful = false;
288 if (c == '*')
289 successful = readCStyleComment ();
290 else if (c == '/')
291 successful = readCppStyleComment ();
292 if (!successful)
293 return false;
294
295 if (collectComments_)
296 {
297 CommentPlacement placement = commentBefore;
298 if (lastValueEnd_ && !containsNewLine (lastValueEnd_, commentBegin))
299 {
300 if (c != '*' || !containsNewLine (commentBegin, current_))
301 placement = commentAfterOnSameLine;
302 }
303
304 addComment (commentBegin, current_, placement);
305 }
306 return true;
307 }
308
309
310 void
311 Reader::addComment (Location begin,
312 Location end,
313 CommentPlacement placement)
314 {
315 assert (collectComments_);
316 if (placement == commentAfterOnSameLine)
317 {
318 assert (lastValue_ != 0);
319 lastValue_->setComment (std::string (begin, end), placement);
320 }
321 else
322 {
323 if (!commentsBefore_.empty ())
324 commentsBefore_ += "\n";
325 commentsBefore_ += std::string (begin, end);
326 }
327 }
328
329
330 bool
331 Reader::readCStyleComment ()
332 {
333 while (current_ != end_)
334 {
335 char c = getNextChar ();
336 if (c == '*' && *current_ == '/')
337 break;
338 }
339 return getNextChar () == '/';
340 }
341
342
343 bool
344 Reader::readCppStyleComment ()
345 {
346 while (current_ != end_)
347 {
348 char c = getNextChar ();
349 if (c == '\r' || c == '\n')
350 break;
351 }
352 return true;
353 }
354
355
356 void
357 Reader::readNumber ()
358 {
359 while (current_ != end_)
360 {
361 if (!(*current_ >= '0' && *current_ <= '9') &&
362 !in (*current_, '.', 'e', 'E', '+', '-'))
363 break;
364 ++current_;
365 }
366 }
367
368 bool
369 Reader::readString ()
370 {
371 char c = 0;
372 while (current_ != end_)
373 {
374 c = getNextChar ();
375 if (c == '\\')
376 getNextChar ();
377 else if (c == '"')
378 break;
379 }
380 return c == '"';
381 }
382
383
384 bool
385 Reader::readObject ()
386 {
387 Token tokenName;
388 std::string name;
389 currentValue () = Value (objectValue);
390 while (readToken (tokenName))
391 {
392 bool initialTokenOk = true;
393 while (tokenName.type_ == tokenComment && initialTokenOk)
394 initialTokenOk = readToken (tokenName);
395 if (!initialTokenOk)
396 break;
397 if (tokenName.type_ == tokenObjectEnd && name.empty ()) // empty object
398 return true;
399 if (tokenName.type_ != tokenString)
400 break;
401
402 name = "";
403 if (!decodeString (tokenName, name))
404 return recoverFromError (tokenObjectEnd);
405
406 Token colon;
407 if (!readToken (colon) || colon.type_ != tokenMemberSeparator)
408 {
409 return addErrorAndRecover ("Missing ':' after object member name",
410 colon,
411 tokenObjectEnd);
412 }
413 Value &value = currentValue ()[ name ];
414 nodes_.push (&value);
415 bool ok = readValue ();
416 nodes_.pop ();
417 if (!ok) // error already set
418 return recoverFromError (tokenObjectEnd);
419
420 Token comma;
421 if (!readToken (comma)
422 || (comma.type_ != tokenObjectEnd &&
423 comma.type_ != tokenArraySeparator &&
424 comma.type_ != tokenComment))
425 {
426 return addErrorAndRecover ("Missing ',' or '}' in object declaration",
427 comma,
428 tokenObjectEnd);
429 }
430 bool finalizeTokenOk = true;
431 while (comma.type_ == tokenComment &&
432 finalizeTokenOk)
433 finalizeTokenOk = readToken (comma);
434 if (comma.type_ == tokenObjectEnd)
435 return true;
436 }
437 return addErrorAndRecover ("Missing '}' or object member name",
438 tokenName,
439 tokenObjectEnd);
440 }
441
442
443 bool
444 Reader::readArray ()
445 {
446 currentValue () = Value (arrayValue);
447 skipSpaces ();
448 if (*current_ == ']') // empty array
449 {
450 Token endArray;
451 readToken (endArray);
452 return true;
453 }
454 int index = 0;
455 while (true)
456 {
457 Value &value = currentValue ()[ index++ ];
458 nodes_.push (&value);
459 bool ok = readValue ();
460 nodes_.pop ();
461 if (!ok) // error already set
462 return recoverFromError (tokenArrayEnd);
463
464 Token token;
465 if (!readToken (token)
466 || (token.type_ != tokenArraySeparator &&
467 token.type_ != tokenArrayEnd))
468 {
469 return addErrorAndRecover ("Missing ',' or ']' in array declaration",
470 token,
471 tokenArrayEnd);
472 }
473 if (token.type_ == tokenArrayEnd)
474 break;
475 }
476 return true;
477 }
478
479
480 bool
481 Reader::decodeNumber (Token &token)
482 {
483 bool isDouble = false;
484 for (Location inspect = token.start_; inspect != token.end_; ++inspect)
485 {
486 isDouble = isDouble
487 || in (*inspect, '.', 'e', 'E', '+')
488 || (*inspect == '-' && inspect != token.start_);
489 }
490 if (isDouble)
491 return decodeDouble (token);
492 Location current = token.start_;
493 bool isNegative = *current == '-';
494 if (isNegative)
495 ++current;
496 unsigned threshold = (isNegative ? unsigned (-Value::minInt)
497 : Value::maxUInt) / 10;
498 unsigned value = 0;
499 while (current < token.end_)
500 {
501 char c = *current++;
502 if (c < '0' || c > '9')
503 return addError ("'" + std::string (token.start_, token.end_) + "' is not a number.", token);
504 if (value >= threshold)
505 return decodeDouble (token);
506 value = value * 10 + unsigned (c - '0');
507 }
508 if (isNegative)
509 currentValue () = -int (value);
510 else if (value <= unsigned (Value::maxInt))
511 currentValue () = int (value);
512 else
513 currentValue () = value;
514 return true;
515 }
516
517
518 bool
519 Reader::decodeDouble (Token &token)
520 {
521 double value = 0;
522 const int bufferSize = 32;
523 int count;
524 int length = int (token.end_ - token.start_);
525 if (length <= bufferSize)
526 {
527 char buffer[bufferSize];
528 memcpy (buffer, token.start_, length);
529 buffer[length] = 0;
530 count = sscanf (buffer, "%lf", &value);
531 }
532 else
533 {
534 std::string buffer (token.start_, token.end_);
535 count = sscanf (buffer.c_str (), "%lf", &value);
536 }
537
538 if (count != 1)
539 return addError ("'" + std::string (token.start_, token.end_) + "' is not a number.", token);
540 currentValue () = value;
541 return true;
542 }
543
544
545 bool
546 Reader::decodeString (Token &token)
547 {
548 std::string decoded;
549 if (!decodeString (token, decoded))
550 return false;
551 currentValue () = decoded;
552 return true;
553 }
554
555
556 bool
557 Reader::decodeString (Token &token, std::string &decoded)
558 {
559 Location current = token.start_ + 1; // skip '"'
560 Location end = token.end_ - 1; // do not include '"'
561 decoded.reserve (long (end - current));
562
563 while (current != end)
564 {
565 char c = *current++;
566 if (expect_false (c == '"'))
567 break;
568 else if (expect_false (c == '\\'))
569 {
570 if (expect_false (current == end))
571 return addError ("Empty escape sequence in string", token, current);
572 char escape = *current++;
573 switch (escape)
574 {
575 case '"':
576 case '/':
577 case '\\': decoded += escape; break;
578
579 case 'b': decoded += '\010'; break;
580 case 't': decoded += '\011'; break;
581 case 'n': decoded += '\012'; break;
582 case 'f': decoded += '\014'; break;
583 case 'r': decoded += '\015'; break;
584 case 'u':
585 {
586 unsigned unicode;
587 if (!decodeUnicodeEscapeSequence (token, current, end, unicode))
588 return false;
589 // @todo encode unicode as utf8.
590 // @todo remember to alter the writer too.
591 }
592 break;
593 default:
594 return addError ("Bad escape sequence in string", token, current);
595 }
596 }
597 else
598 {
599 decoded += c;
600 }
601 }
602
603 return true;
604 }
605
606
607 bool
608 Reader::decodeUnicodeEscapeSequence (Token &token,
609 Location &current,
610 Location end,
611 unsigned &unicode)
612 {
613 if (end - current < 4)
614 return addError ("Bad unicode escape sequence in string: four digits expected.", token, current);
615 unicode = 0;
616 for (int index = 0; index < 4; ++index)
617 {
618 char c = *current++;
619 unicode *= 16;
620 if (c >= '0' && c <= '9')
621 unicode += c - '0';
622 else if (c >= 'a' && c <= 'f')
623 unicode += c - 'a' + 10;
624 else if (c >= 'A' && c <= 'F')
625 unicode += c - 'A' + 10;
626 else
627 return addError ("Bad unicode escape sequence in string: hexadecimal digit expected.", token, current);
628 }
629 return true;
630 }
631
632
633 bool
634 Reader::addError (const std::string &message,
635 Token &token,
636 Location extra)
637 {
638 ErrorInfo info;
639 info.token_ = token;
640 info.message_ = message;
641 info.extra_ = extra;
642 errors_.push_back (info);
643 return false;
644 }
645
646
647 bool
648 Reader::recoverFromError (TokenType skipUntilToken)
649 {
650 int errorCount = int (errors_.size ());
651 Token skip;
652 while (true)
653 {
654 if (!readToken (skip))
655 errors_.resize (errorCount); // discard errors caused by recovery
656 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
657 break;
658 }
659 errors_.resize (errorCount);
660 return false;
661 }
662
663
664 bool
665 Reader::addErrorAndRecover (const std::string &message,
666 Token &token,
667 TokenType skipUntilToken)
668 {
669 addError (message, token);
670 return recoverFromError (skipUntilToken);
671 }
672
673
674 Value &
675 Reader::currentValue ()
676 {
677 return *(nodes_.top ());
678 }
679
680
681 char
682 Reader::getNextChar ()
683 {
684 if (current_ == end_)
685 return 0;
686 return *current_++;
687 }
688
689
690 void
691 Reader::getLocationLineAndColumn (Location location,
692 int &line,
693 int &column) const
694 {
695 Location current = begin_;
696 Location lastLineStart = current;
697 line = 0;
698 while (current < location && current != end_)
699 {
700 char c = *current++;
701 if (c == '\r')
702 {
703 if (*current == '\n')
704 ++current;
705 lastLineStart = current;
706 ++line;
707 }
708 else if (c == '\n')
709 {
710 lastLineStart = current;
711 ++line;
712 }
713 }
714 // column & line start at 1
715 column = int (location - lastLineStart) + 1;
716 ++line;
717 }
718
719
720 std::string
721 Reader::getLocationLineAndColumn (Location location) const
722 {
723 int line, column;
724 getLocationLineAndColumn (location, line, column);
725 char buffer[18+16+16+1];
726 sprintf (buffer, "Line %d, Column %d", line, column);
727 return buffer;
728 }
729
730
731 std::string
732 Reader::error_msgs () const
733 {
734 std::string formattedMessage;
735 for (Errors::const_iterator itError = errors_.begin ();
736 itError != errors_.end ();
737 ++itError)
738 {
739 const ErrorInfo &error = *itError;
740 formattedMessage += "* " + getLocationLineAndColumn (error.token_.start_) + "\n";
741 formattedMessage += " " + error.message_ + "\n";
742 if (error.extra_)
743 formattedMessage += "See " + getLocationLineAndColumn (error.extra_) + " for detail.\n";
744 }
745 return formattedMessage;
746 }
747
748
749 std::istream& operator >> (std::istream &sin, Value &root)
750 {
751 Reader reader;
752 bool ok = reader.parse (sin, root, true);
753 #if 0
754 throw_unless (ok);
755 #endif
756 if (!ok) throw std::runtime_error (reader.error_msgs ());
757 return sin;
758 }
759 } // namespace json