123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- // Protocol Buffers - Google's data interchange format
- // Copyright 2008 Google Inc. All rights reserved.
- // https://developers.google.com/protocol-buffers/
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are
- // met:
- //
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above
- // copyright notice, this list of conditions and the following disclaimer
- // in the documentation and/or other materials provided with the
- // distribution.
- // * Neither the name of Google Inc. nor the names of its
- // contributors may be used to endorse or promote products derived from
- // this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- // Author: kenton@google.com (Kenton Varda)
- // Based on original Protocol Buffers design by
- // Sanjay Ghemawat, Jeff Dean, and others.
- //
- // Class for parsing tokenized text from a ZeroCopyInputStream.
- #ifndef GOOGLE_PROTOBUF_IO_TOKENIZER_H__
- #define GOOGLE_PROTOBUF_IO_TOKENIZER_H__
- #include <string>
- #include <vector>
- #include <google/protobuf/stubs/common.h>
- #include <google/protobuf/stubs/logging.h>
- #include <google/protobuf/port_def.inc>
- namespace google {
- namespace protobuf {
- namespace io {
- class ZeroCopyInputStream; // zero_copy_stream.h
- // Defined in this file.
- class ErrorCollector;
- class Tokenizer;
- // By "column number", the proto compiler refers to a count of the number
- // of bytes before a given byte, except that a tab character advances to
- // the next multiple of 8 bytes. Note in particular that column numbers
- // are zero-based, while many user interfaces use one-based column numbers.
- typedef int ColumnNumber;
- // Abstract interface for an object which collects the errors that occur
- // during parsing. A typical implementation might simply print the errors
- // to stdout.
- class PROTOBUF_EXPORT ErrorCollector {
- public:
- inline ErrorCollector() {}
- virtual ~ErrorCollector();
- // Indicates that there was an error in the input at the given line and
- // column numbers. The numbers are zero-based, so you may want to add
- // 1 to each before printing them.
- virtual void AddError(int line, ColumnNumber column,
- const std::string& message) = 0;
- // Indicates that there was a warning in the input at the given line and
- // column numbers. The numbers are zero-based, so you may want to add
- // 1 to each before printing them.
- virtual void AddWarning(int /* line */, ColumnNumber /* column */,
- const std::string& /* message */) {}
- private:
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ErrorCollector);
- };
- // This class converts a stream of raw text into a stream of tokens for
- // the protocol definition parser to parse. The tokens recognized are
- // similar to those that make up the C language; see the TokenType enum for
- // precise descriptions. Whitespace and comments are skipped. By default,
- // C- and C++-style comments are recognized, but other styles can be used by
- // calling set_comment_style().
- class PROTOBUF_EXPORT Tokenizer {
- public:
- // Construct a Tokenizer that reads and tokenizes text from the given
- // input stream and writes errors to the given error_collector.
- // The caller keeps ownership of input and error_collector.
- Tokenizer(ZeroCopyInputStream* input, ErrorCollector* error_collector);
- ~Tokenizer();
- enum TokenType {
- TYPE_START, // Next() has not yet been called.
- TYPE_END, // End of input reached. "text" is empty.
- TYPE_IDENTIFIER, // A sequence of letters, digits, and underscores, not
- // starting with a digit. It is an error for a number
- // to be followed by an identifier with no space in
- // between.
- TYPE_INTEGER, // A sequence of digits representing an integer. Normally
- // the digits are decimal, but a prefix of "0x" indicates
- // a hex number and a leading zero indicates octal, just
- // like with C numeric literals. A leading negative sign
- // is NOT included in the token; it's up to the parser to
- // interpret the unary minus operator on its own.
- TYPE_FLOAT, // A floating point literal, with a fractional part and/or
- // an exponent. Always in decimal. Again, never
- // negative.
- TYPE_STRING, // A quoted sequence of escaped characters. Either single
- // or double quotes can be used, but they must match.
- // A string literal cannot cross a line break.
- TYPE_SYMBOL, // Any other printable character, like '!' or '+'.
- // Symbols are always a single character, so "!+$%" is
- // four tokens.
- };
- // Structure representing a token read from the token stream.
- struct Token {
- TokenType type;
- std::string text; // The exact text of the token as it appeared in
- // the input. e.g. tokens of TYPE_STRING will still
- // be escaped and in quotes.
- // "line" and "column" specify the position of the first character of
- // the token within the input stream. They are zero-based.
- int line;
- ColumnNumber column;
- ColumnNumber end_column;
- };
- // Get the current token. This is updated when Next() is called. Before
- // the first call to Next(), current() has type TYPE_START and no contents.
- const Token& current();
- // Return the previous token -- i.e. what current() returned before the
- // previous call to Next().
- const Token& previous();
- // Advance to the next token. Returns false if the end of the input is
- // reached.
- bool Next();
- // Like Next(), but also collects comments which appear between the previous
- // and next tokens.
- //
- // Comments which appear to be attached to the previous token are stored
- // in *prev_tailing_comments. Comments which appear to be attached to the
- // next token are stored in *next_leading_comments. Comments appearing in
- // between which do not appear to be attached to either will be added to
- // detached_comments. Any of these parameters can be NULL to simply discard
- // the comments.
- //
- // A series of line comments appearing on consecutive lines, with no other
- // tokens appearing on those lines, will be treated as a single comment.
- //
- // Only the comment content is returned; comment markers (e.g. //) are
- // stripped out. For block comments, leading whitespace and an asterisk will
- // be stripped from the beginning of each line other than the first. Newlines
- // are included in the output.
- //
- // Examples:
- //
- // optional int32 foo = 1; // Comment attached to foo.
- // // Comment attached to bar.
- // optional int32 bar = 2;
- //
- // optional string baz = 3;
- // // Comment attached to baz.
- // // Another line attached to baz.
- //
- // // Comment attached to qux.
- // //
- // // Another line attached to qux.
- // optional double qux = 4;
- //
- // // Detached comment. This is not attached to qux or corge
- // // because there are blank lines separating it from both.
- //
- // optional string corge = 5;
- // /* Block comment attached
- // * to corge. Leading asterisks
- // * will be removed. */
- // /* Block comment attached to
- // * grault. */
- // optional int32 grault = 6;
- bool NextWithComments(std::string* prev_trailing_comments,
- std::vector<std::string>* detached_comments,
- std::string* next_leading_comments);
- // Parse helpers ---------------------------------------------------
- // Parses a TYPE_FLOAT token. This never fails, so long as the text actually
- // comes from a TYPE_FLOAT token parsed by Tokenizer. If it doesn't, the
- // result is undefined (possibly an assert failure).
- static double ParseFloat(const std::string& text);
- // Parses a TYPE_STRING token. This never fails, so long as the text actually
- // comes from a TYPE_STRING token parsed by Tokenizer. If it doesn't, the
- // result is undefined (possibly an assert failure).
- static void ParseString(const std::string& text, std::string* output);
- // Identical to ParseString, but appends to output.
- static void ParseStringAppend(const std::string& text, std::string* output);
- // Parses a TYPE_INTEGER token. Returns false if the result would be
- // greater than max_value. Otherwise, returns true and sets *output to the
- // result. If the text is not from a Token of type TYPE_INTEGER originally
- // parsed by a Tokenizer, the result is undefined (possibly an assert
- // failure).
- static bool ParseInteger(const std::string& text, uint64 max_value,
- uint64* output);
- // Options ---------------------------------------------------------
- // Set true to allow floats to be suffixed with the letter 'f'. Tokens
- // which would otherwise be integers but which have the 'f' suffix will be
- // forced to be interpreted as floats. For all other purposes, the 'f' is
- // ignored.
- void set_allow_f_after_float(bool value) { allow_f_after_float_ = value; }
- // Valid values for set_comment_style().
- enum CommentStyle {
- // Line comments begin with "//", block comments are delimited by "/*" and
- // "*/".
- CPP_COMMENT_STYLE,
- // Line comments begin with "#". No way to write block comments.
- SH_COMMENT_STYLE
- };
- // Sets the comment style.
- void set_comment_style(CommentStyle style) { comment_style_ = style; }
- // Whether to require whitespace between a number and a field name.
- // Default is true. Do not use this; for Google-internal cleanup only.
- void set_require_space_after_number(bool require) {
- require_space_after_number_ = require;
- }
- // Whether to allow string literals to span multiple lines. Default is false.
- // Do not use this; for Google-internal cleanup only.
- void set_allow_multiline_strings(bool allow) {
- allow_multiline_strings_ = allow;
- }
- // External helper: validate an identifier.
- static bool IsIdentifier(const std::string& text);
- // -----------------------------------------------------------------
- private:
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Tokenizer);
- Token current_; // Returned by current().
- Token previous_; // Returned by previous().
- ZeroCopyInputStream* input_;
- ErrorCollector* error_collector_;
- char current_char_; // == buffer_[buffer_pos_], updated by NextChar().
- const char* buffer_; // Current buffer returned from input_.
- int buffer_size_; // Size of buffer_.
- int buffer_pos_; // Current position within the buffer.
- bool read_error_; // Did we previously encounter a read error?
- // Line and column number of current_char_ within the whole input stream.
- int line_;
- ColumnNumber column_;
- // String to which text should be appended as we advance through it.
- // Call RecordTo(&str) to start recording and StopRecording() to stop.
- // E.g. StartToken() calls RecordTo(¤t_.text). record_start_ is the
- // position within the current buffer where recording started.
- std::string* record_target_;
- int record_start_;
- // Options.
- bool allow_f_after_float_;
- CommentStyle comment_style_;
- bool require_space_after_number_;
- bool allow_multiline_strings_;
- // Since we count columns we need to interpret tabs somehow. We'll take
- // the standard 8-character definition for lack of any way to do better.
- // This must match the documentation of ColumnNumber.
- static const int kTabWidth = 8;
- // -----------------------------------------------------------------
- // Helper methods.
- // Consume this character and advance to the next one.
- void NextChar();
- // Read a new buffer from the input.
- void Refresh();
- inline void RecordTo(std::string* target);
- inline void StopRecording();
- // Called when the current character is the first character of a new
- // token (not including whitespace or comments).
- inline void StartToken();
- // Called when the current character is the first character after the
- // end of the last token. After this returns, current_.text will
- // contain all text consumed since StartToken() was called.
- inline void EndToken();
- // Convenience method to add an error at the current line and column.
- void AddError(const std::string& message) {
- error_collector_->AddError(line_, column_, message);
- }
- // -----------------------------------------------------------------
- // The following four methods are used to consume tokens of specific
- // types. They are actually used to consume all characters *after*
- // the first, since the calling function consumes the first character
- // in order to decide what kind of token is being read.
- // Read and consume a string, ending when the given delimiter is
- // consumed.
- void ConsumeString(char delimiter);
- // Read and consume a number, returning TYPE_FLOAT or TYPE_INTEGER
- // depending on what was read. This needs to know if the first
- // character was a zero in order to correctly recognize hex and octal
- // numbers.
- // It also needs to know if the first character was a . to parse floating
- // point correctly.
- TokenType ConsumeNumber(bool started_with_zero, bool started_with_dot);
- // Consume the rest of a line.
- void ConsumeLineComment(std::string* content);
- // Consume until "*/".
- void ConsumeBlockComment(std::string* content);
- enum NextCommentStatus {
- // Started a line comment.
- LINE_COMMENT,
- // Started a block comment.
- BLOCK_COMMENT,
- // Consumed a slash, then realized it wasn't a comment. current_ has
- // been filled in with a slash token. The caller should return it.
- SLASH_NOT_COMMENT,
- // We do not appear to be starting a comment here.
- NO_COMMENT
- };
- // If we're at the start of a new comment, consume it and return what kind
- // of comment it is.
- NextCommentStatus TryConsumeCommentStart();
- // -----------------------------------------------------------------
- // These helper methods make the parsing code more readable. The
- // "character classes" referred to are defined at the top of the .cc file.
- // Basically it is a C++ class with one method:
- // static bool InClass(char c);
- // The method returns true if c is a member of this "class", like "Letter"
- // or "Digit".
- // Returns true if the current character is of the given character
- // class, but does not consume anything.
- template <typename CharacterClass>
- inline bool LookingAt();
- // If the current character is in the given class, consume it and return
- // true. Otherwise return false.
- // e.g. TryConsumeOne<Letter>()
- template <typename CharacterClass>
- inline bool TryConsumeOne();
- // Like above, but try to consume the specific character indicated.
- inline bool TryConsume(char c);
- // Consume zero or more of the given character class.
- template <typename CharacterClass>
- inline void ConsumeZeroOrMore();
- // Consume one or more of the given character class or log the given
- // error message.
- // e.g. ConsumeOneOrMore<Digit>("Expected digits.");
- template <typename CharacterClass>
- inline void ConsumeOneOrMore(const char* error);
- };
- // inline methods ====================================================
- inline const Tokenizer::Token& Tokenizer::current() { return current_; }
- inline const Tokenizer::Token& Tokenizer::previous() { return previous_; }
- inline void Tokenizer::ParseString(const std::string& text,
- std::string* output) {
- output->clear();
- ParseStringAppend(text, output);
- }
- } // namespace io
- } // namespace protobuf
- } // namespace google
- #include <google/protobuf/port_undef.inc>
- #endif // GOOGLE_PROTOBUF_IO_TOKENIZER_H__
|