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 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

# User Rev Content
1 pippijn 1.2 /*
2     * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
3 pippijn 1.3 * Rights to this code are as documented in doc/pod/license.pod.
4 pippijn 1.2 *
5     * JSON-RPC for Ermyth
6     *
7 pippijn 1.3 * $Id: reader.C,v 1.2 2007-08-28 17:08:12 pippijn Exp $
8 pippijn 1.2 */
9    
10 pippijn 1.1 //>>>>>>>>>>> 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