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(¤t_.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