1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 // Author: kenton@google.com (Kenton Varda)
32 //  Based on original Protocol Buffers design by
33 //  Sanjay Ghemawat, Jeff Dean, and others.
34 //
35 // Class for parsing tokenized text from a ZeroCopyInputStream.
36 
37 #ifndef GOOGLE_PROTOBUF_IO_TOKENIZER_H__
38 #define GOOGLE_PROTOBUF_IO_TOKENIZER_H__
39 
40 
41 #include <string>
42 #include <vector>
43 
44 #include <google/protobuf/stubs/common.h>
45 #include <google/protobuf/stubs/logging.h>
46 #include <google/protobuf/port_def.inc>
47 
48 namespace google {
49 namespace protobuf {
50 namespace io {
51 
52 class ZeroCopyInputStream;  // zero_copy_stream.h
53 
54 // Defined in this file.
55 class ErrorCollector;
56 class Tokenizer;
57 
58 // By "column number", the proto compiler refers to a count of the number
59 // of bytes before a given byte, except that a tab character advances to
60 // the next multiple of 8 bytes.  Note in particular that column numbers
61 // are zero-based, while many user interfaces use one-based column numbers.
62 typedef int ColumnNumber;
63 
64 // Abstract interface for an object which collects the errors that occur
65 // during parsing.  A typical implementation might simply print the errors
66 // to stdout.
67 class PROTOBUF_EXPORT ErrorCollector {
68  public:
ErrorCollector()69   inline ErrorCollector() {}
70   virtual ~ErrorCollector();
71 
72   // Indicates that there was an error in the input at the given line and
73   // column numbers.  The numbers are zero-based, so you may want to add
74   // 1 to each before printing them.
75   virtual void AddError(int line, ColumnNumber column,
76                         const std::string& message) = 0;
77 
78   // Indicates that there was a warning in the input at the given line and
79   // column numbers.  The numbers are zero-based, so you may want to add
80   // 1 to each before printing them.
AddWarning(int line,ColumnNumber column,const std::string & message)81   virtual void AddWarning(int line, ColumnNumber column,
82                           const std::string& message) {}
83 
84  private:
85   GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ErrorCollector);
86 };
87 
88 // This class converts a stream of raw text into a stream of tokens for
89 // the protocol definition parser to parse.  The tokens recognized are
90 // similar to those that make up the C language; see the TokenType enum for
91 // precise descriptions.  Whitespace and comments are skipped.  By default,
92 // C- and C++-style comments are recognized, but other styles can be used by
93 // calling set_comment_style().
94 class PROTOBUF_EXPORT Tokenizer {
95  public:
96   // Construct a Tokenizer that reads and tokenizes text from the given
97   // input stream and writes errors to the given error_collector.
98   // The caller keeps ownership of input and error_collector.
99   Tokenizer(ZeroCopyInputStream* input, ErrorCollector* error_collector);
100   ~Tokenizer();
101 
102   enum TokenType {
103     TYPE_START,  // Next() has not yet been called.
104     TYPE_END,    // End of input reached.  "text" is empty.
105 
106     TYPE_IDENTIFIER,  // A sequence of letters, digits, and underscores, not
107                       // starting with a digit.  It is an error for a number
108                       // to be followed by an identifier with no space in
109                       // between.
110     TYPE_INTEGER,     // A sequence of digits representing an integer.  Normally
111                       // the digits are decimal, but a prefix of "0x" indicates
112                       // a hex number and a leading zero indicates octal, just
113                       // like with C numeric literals.  A leading negative sign
114                       // is NOT included in the token; it's up to the parser to
115                       // interpret the unary minus operator on its own.
116     TYPE_FLOAT,       // A floating point literal, with a fractional part and/or
117                       // an exponent.  Always in decimal.  Again, never
118                       // negative.
119     TYPE_STRING,      // A quoted sequence of escaped characters.  Either single
120                       // or double quotes can be used, but they must match.
121                       // A string literal cannot cross a line break.
122     TYPE_SYMBOL,      // Any other printable character, like '!' or '+'.
123                       // Symbols are always a single character, so "!+$%" is
124                       // four tokens.
125   };
126 
127   // Structure representing a token read from the token stream.
128   struct Token {
129     TokenType type;
130     std::string text;  // The exact text of the token as it appeared in
131                        // the input.  e.g. tokens of TYPE_STRING will still
132                        // be escaped and in quotes.
133 
134     // "line" and "column" specify the position of the first character of
135     // the token within the input stream.  They are zero-based.
136     int line;
137     ColumnNumber column;
138     ColumnNumber end_column;
139   };
140 
141   // Get the current token.  This is updated when Next() is called.  Before
142   // the first call to Next(), current() has type TYPE_START and no contents.
143   const Token& current();
144 
145   // Return the previous token -- i.e. what current() returned before the
146   // previous call to Next().
147   const Token& previous();
148 
149   // Advance to the next token.  Returns false if the end of the input is
150   // reached.
151   bool Next();
152 
153   // Like Next(), but also collects comments which appear between the previous
154   // and next tokens.
155   //
156   // Comments which appear to be attached to the previous token are stored
157   // in *prev_tailing_comments.  Comments which appear to be attached to the
158   // next token are stored in *next_leading_comments.  Comments appearing in
159   // between which do not appear to be attached to either will be added to
160   // detached_comments.  Any of these parameters can be NULL to simply discard
161   // the comments.
162   //
163   // A series of line comments appearing on consecutive lines, with no other
164   // tokens appearing on those lines, will be treated as a single comment.
165   //
166   // Only the comment content is returned; comment markers (e.g. //) are
167   // stripped out.  For block comments, leading whitespace and an asterisk will
168   // be stripped from the beginning of each line other than the first.  Newlines
169   // are included in the output.
170   //
171   // Examples:
172   //
173   //   optional int32 foo = 1;  // Comment attached to foo.
174   //   // Comment attached to bar.
175   //   optional int32 bar = 2;
176   //
177   //   optional string baz = 3;
178   //   // Comment attached to baz.
179   //   // Another line attached to baz.
180   //
181   //   // Comment attached to qux.
182   //   //
183   //   // Another line attached to qux.
184   //   optional double qux = 4;
185   //
186   //   // Detached comment.  This is not attached to qux or corge
187   //   // because there are blank lines separating it from both.
188   //
189   //   optional string corge = 5;
190   //   /* Block comment attached
191   //    * to corge.  Leading asterisks
192   //    * will be removed. */
193   //   /* Block comment attached to
194   //    * grault. */
195   //   optional int32 grault = 6;
196   bool NextWithComments(std::string* prev_trailing_comments,
197                         std::vector<std::string>* detached_comments,
198                         std::string* next_leading_comments);
199 
200   // Parse helpers ---------------------------------------------------
201 
202   // Parses a TYPE_FLOAT token.  This never fails, so long as the text actually
203   // comes from a TYPE_FLOAT token parsed by Tokenizer.  If it doesn't, the
204   // result is undefined (possibly an assert failure).
205   static double ParseFloat(const std::string& text);
206 
207   // Parses a TYPE_STRING token.  This never fails, so long as the text actually
208   // comes from a TYPE_STRING token parsed by Tokenizer.  If it doesn't, the
209   // result is undefined (possibly an assert failure).
210   static void ParseString(const std::string& text, std::string* output);
211 
212   // Identical to ParseString, but appends to output.
213   static void ParseStringAppend(const std::string& text, std::string* output);
214 
215   // Parses a TYPE_INTEGER token.  Returns false if the result would be
216   // greater than max_value.  Otherwise, returns true and sets *output to the
217   // result.  If the text is not from a Token of type TYPE_INTEGER originally
218   // parsed by a Tokenizer, the result is undefined (possibly an assert
219   // failure).
220   static bool ParseInteger(const std::string& text, uint64 max_value,
221                            uint64* output);
222 
223   // Options ---------------------------------------------------------
224 
225   // Set true to allow floats to be suffixed with the letter 'f'.  Tokens
226   // which would otherwise be integers but which have the 'f' suffix will be
227   // forced to be interpreted as floats.  For all other purposes, the 'f' is
228   // ignored.
set_allow_f_after_float(bool value)229   void set_allow_f_after_float(bool value) { allow_f_after_float_ = value; }
230 
231   // Valid values for set_comment_style().
232   enum CommentStyle {
233     // Line comments begin with "//", block comments are delimited by "/*" and
234     // "*/".
235     CPP_COMMENT_STYLE,
236     // Line comments begin with "#".  No way to write block comments.
237     SH_COMMENT_STYLE
238   };
239 
240   // Sets the comment style.
set_comment_style(CommentStyle style)241   void set_comment_style(CommentStyle style) { comment_style_ = style; }
242 
243   // Whether to require whitespace between a number and a field name.
244   // Default is true. Do not use this; for Google-internal cleanup only.
set_require_space_after_number(bool require)245   void set_require_space_after_number(bool require) {
246     require_space_after_number_ = require;
247   }
248 
249   // Whether to allow string literals to span multiple lines. Default is false.
250   // Do not use this; for Google-internal cleanup only.
set_allow_multiline_strings(bool allow)251   void set_allow_multiline_strings(bool allow) {
252     allow_multiline_strings_ = allow;
253   }
254 
255   // External helper: validate an identifier.
256   static bool IsIdentifier(const std::string& text);
257 
258   // -----------------------------------------------------------------
259  private:
260   GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Tokenizer);
261 
262   Token current_;   // Returned by current().
263   Token previous_;  // Returned by previous().
264 
265   ZeroCopyInputStream* input_;
266   ErrorCollector* error_collector_;
267 
268   char current_char_;   // == buffer_[buffer_pos_], updated by NextChar().
269   const char* buffer_;  // Current buffer returned from input_.
270   int buffer_size_;     // Size of buffer_.
271   int buffer_pos_;      // Current position within the buffer.
272   bool read_error_;     // Did we previously encounter a read error?
273 
274   // Line and column number of current_char_ within the whole input stream.
275   int line_;
276   ColumnNumber column_;
277 
278   // String to which text should be appended as we advance through it.
279   // Call RecordTo(&str) to start recording and StopRecording() to stop.
280   // E.g. StartToken() calls RecordTo(&current_.text).  record_start_ is the
281   // position within the current buffer where recording started.
282   std::string* record_target_;
283   int record_start_;
284 
285   // Options.
286   bool allow_f_after_float_;
287   CommentStyle comment_style_;
288   bool require_space_after_number_;
289   bool allow_multiline_strings_;
290 
291   // Since we count columns we need to interpret tabs somehow.  We'll take
292   // the standard 8-character definition for lack of any way to do better.
293   // This must match the documentation of ColumnNumber.
294   static const int kTabWidth = 8;
295 
296   // -----------------------------------------------------------------
297   // Helper methods.
298 
299   // Consume this character and advance to the next one.
300   void NextChar();
301 
302   // Read a new buffer from the input.
303   void Refresh();
304 
305   inline void RecordTo(std::string* target);
306   inline void StopRecording();
307 
308   // Called when the current character is the first character of a new
309   // token (not including whitespace or comments).
310   inline void StartToken();
311   // Called when the current character is the first character after the
312   // end of the last token.  After this returns, current_.text will
313   // contain all text consumed since StartToken() was called.
314   inline void EndToken();
315 
316   // Convenience method to add an error at the current line and column.
AddError(const std::string & message)317   void AddError(const std::string& message) {
318     error_collector_->AddError(line_, column_, message);
319   }
320 
321   // -----------------------------------------------------------------
322   // The following four methods are used to consume tokens of specific
323   // types.  They are actually used to consume all characters *after*
324   // the first, since the calling function consumes the first character
325   // in order to decide what kind of token is being read.
326 
327   // Read and consume a string, ending when the given delimiter is
328   // consumed.
329   void ConsumeString(char delimiter);
330 
331   // Read and consume a number, returning TYPE_FLOAT or TYPE_INTEGER
332   // depending on what was read.  This needs to know if the first
333   // character was a zero in order to correctly recognize hex and octal
334   // numbers.
335   // It also needs to know if the first character was a . to parse floating
336   // point correctly.
337   TokenType ConsumeNumber(bool started_with_zero, bool started_with_dot);
338 
339   // Consume the rest of a line.
340   void ConsumeLineComment(std::string* content);
341   // Consume until "*/".
342   void ConsumeBlockComment(std::string* content);
343 
344   enum NextCommentStatus {
345     // Started a line comment.
346     LINE_COMMENT,
347 
348     // Started a block comment.
349     BLOCK_COMMENT,
350 
351     // Consumed a slash, then realized it wasn't a comment.  current_ has
352     // been filled in with a slash token.  The caller should return it.
353     SLASH_NOT_COMMENT,
354 
355     // We do not appear to be starting a comment here.
356     NO_COMMENT
357   };
358 
359   // If we're at the start of a new comment, consume it and return what kind
360   // of comment it is.
361   NextCommentStatus TryConsumeCommentStart();
362 
363   // -----------------------------------------------------------------
364   // These helper methods make the parsing code more readable.  The
365   // "character classes" referred to are defined at the top of the .cc file.
366   // Basically it is a C++ class with one method:
367   //   static bool InClass(char c);
368   // The method returns true if c is a member of this "class", like "Letter"
369   // or "Digit".
370 
371   // Returns true if the current character is of the given character
372   // class, but does not consume anything.
373   template <typename CharacterClass>
374   inline bool LookingAt();
375 
376   // If the current character is in the given class, consume it and return
377   // true.  Otherwise return false.
378   // e.g. TryConsumeOne<Letter>()
379   template <typename CharacterClass>
380   inline bool TryConsumeOne();
381 
382   // Like above, but try to consume the specific character indicated.
383   inline bool TryConsume(char c);
384 
385   // Consume zero or more of the given character class.
386   template <typename CharacterClass>
387   inline void ConsumeZeroOrMore();
388 
389   // Consume one or more of the given character class or log the given
390   // error message.
391   // e.g. ConsumeOneOrMore<Digit>("Expected digits.");
392   template <typename CharacterClass>
393   inline void ConsumeOneOrMore(const char* error);
394 };
395 
396 // inline methods ====================================================
current()397 inline const Tokenizer::Token& Tokenizer::current() { return current_; }
398 
previous()399 inline const Tokenizer::Token& Tokenizer::previous() { return previous_; }
400 
ParseString(const std::string & text,std::string * output)401 inline void Tokenizer::ParseString(const std::string& text,
402                                    std::string* output) {
403   output->clear();
404   ParseStringAppend(text, output);
405 }
406 
407 }  // namespace io
408 }  // namespace protobuf
409 }  // namespace google
410 
411 #include <google/protobuf/port_undef.inc>
412 
413 #endif  // GOOGLE_PROTOBUF_IO_TOKENIZER_H__
414