| // Copyright 2017-2020 The Verible Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // FlexLexerAdapter class adapts Flex-generated lexers to Lexer interface. | |
| // | |
| // Template parameter L must be a Flex-generated lexer (yyFlexLexer). | |
| // The adapter inherits from this generated class to override functions. | |
| // | |
| // Main lexing function yylex() must be defined in a subclass. | |
| // | |
| // Example usage: | |
| // in verilog_lexer.h: | |
| // class verilogFlexLexer; // generated by flex | |
| // class VerilogLexer : public verible::FlexLexerAdapter<verilogFlexLexer> { | |
| // ... | |
| // }; | |
| // | |
| // and in verilog.lex: | |
| // %option yyclass="verilog::VerilogLexer" | |
| #ifndef VERIBLE_COMMON_LEXER_FLEX_LEXER_ADAPTER_H_ | |
| #define VERIBLE_COMMON_LEXER_FLEX_LEXER_ADAPTER_H_ | |
| #include <cstdlib> | |
| #include <iostream> | |
| #include <sstream> // IWYU pragma: keep // for ostringstream | |
| #include <string> | |
| #include "absl/strings/string_view.h" | |
| #include "common/lexer/lexer.h" | |
| #include "common/text/token_info.h" | |
| #include "common/util/logging.h" | |
| namespace verible { | |
| // The "L" base class of FlexLexerAdaptor needs to use code_stream_ in its | |
| // constructor, which means that code_stream_ must be initialized first. All | |
| // base classes are initialized before any non-static data members, so to | |
| // achieve that, we need to also put code_stream_ in a base class that is | |
| // ordered before "L" in FlexLexerAdaptor's base classes. | |
| class CodeStreamHolder { | |
| protected: | |
| // The stream object conforms to the FlexLexer input interface. | |
| // Even though scanning is done on the stream's internal copy of the input | |
| // string, the byte offsets being tracked can be used to construct | |
| // string_views based on the original string's start address. | |
| // Using the standard istream interface also lets us switch buffers, e.g. | |
| // during preprocessing. | |
| std::istringstream code_stream_; | |
| }; | |
| // L is a (flex-generated) yyFlexLexer-like class. | |
| template <typename L> | |
| class FlexLexerAdapter : private CodeStreamHolder, protected L, public Lexer { | |
| public: | |
| explicit FlexLexerAdapter(absl::string_view code) | |
| : L(&code_stream_), | |
| code_(code), | |
| // last_token_ points to the beginning of the code_ buffer | |
| last_token_(0 /* enum doesn't matter */, code_.substr(0, 0)) { | |
| code_stream_.str(std::string(code)); | |
| // istringstream copies text into its own internal buffer. | |
| } | |
| // Returns the token associated with the last UpdateLocation() call. | |
| const TokenInfo& GetLastToken() const final { return last_token_; } | |
| // Returns next token and updates its location. | |
| const TokenInfo& DoNextToken() override { | |
| if (at_eof_) { | |
| // Do not call yylex(), because that will result in the fatal error: | |
| // "fatal flex scanner internal error--end of buffer missed" | |
| last_token_ = TokenInfo::EOFToken(code_); | |
| } else { | |
| // In normal operation, call yylex() to extract the next token. | |
| last_token_.set_token_enum(this->yylex()); | |
| } | |
| // yylex has already called UpdateLocation() | |
| return last_token_; | |
| } | |
| protected: | |
| // Must be called by subclasses to update location of the current token. | |
| void UpdateLocation() { last_token_.AdvanceText(this->YYLeng()); } | |
| // EOF needs special handling because yyleng is set to include a terminating | |
| // \0 (NUL) character. Once EOF is encountered it is also not possible to | |
| // yyless-rewind the window -- doing so messes up the internal state machine, | |
| // and causes (flex) errors like: | |
| // "fatal flex scanner internal error--end of buffer missed" | |
| // We advance the token text without spanning the NUL character. | |
| // This should only be needed in lexer states that need to explicitly | |
| // handle <<EOF>>. | |
| void UpdateLocationEOF() { | |
| last_token_.AdvanceText(this->YYLeng() - 1); | |
| at_eof_ = true; | |
| } | |
| // Restart lexer by pointing to new input stream, and reset all state. | |
| void Restart(absl::string_view code) override { | |
| at_eof_ = false; | |
| code_ = code; | |
| code_stream_.str(std::string(code_)); | |
| last_token_ = TokenInfo(0, code_.substr(0, 0)); | |
| // Reset buffer stack. | |
| while (L::yy_buffer_stack_top > 1) { // Keep bottom buffer only. | |
| L::yypop_buffer_state(); | |
| } | |
| // Reset the current buffer to use new stream. | |
| L::yyrestart(&code_stream_); | |
| // Reset start condition stack. | |
| while (L::yy_start_stack_ptr > 1) { // Keep INITIAL state. | |
| L::yy_pop_state(); | |
| } | |
| } | |
| // Overrides yyFlexLexer's implementation to handle unrecognized chars. | |
| void LexerOutput(const char* buf, int size) final { | |
| VLOG(1) << "LexerOutput: rejected text: \"" << std::string(buf, size) | |
| << '\"'; | |
| // Update location by the size of the unrecognized sequence. | |
| // Note, this is a last-resort guard. The preferred way | |
| // to handle unrecognized chars is to add wildcard rule | |
| // at the end of the lexer definition that just calls | |
| // UpdateLocation(). | |
| last_token_.AdvanceText(size); | |
| // TODO(fangism): Communicate some sort of error token to the consumer. | |
| } | |
| // Overrides yyFlexLexer's implementation to do proper error handling. | |
| void LexerError(const char* msg) final { | |
| std::cerr << "Fatal LexerError: " << msg; | |
| abort(); | |
| } | |
| private: | |
| // A read-only view of the entire text to be scanned. | |
| absl::string_view code_; | |
| // Contains the enumeration and the substring slice of the last lexed token. | |
| TokenInfo last_token_; | |
| // Kludge: the generated FlexLexer (subclass) doesn't expose a way to | |
| // determine whether and EOF has already been encountered: | |
| // (yy_buffer_stack[yy_buffer_stack_top]->yy_buffer_status | |
| // == YY_BUFFER_EOF_PENDING) | |
| // because yy_buffer_state's implementation is private. | |
| // Thus, we manually set this bit upon encountering <<EOF>>. | |
| bool at_eof_ = false; | |
| }; | |
| } // namespace verible | |
| #endif // VERIBLE_COMMON_LEXER_FLEX_LEXER_ADAPTER_H_ |