| // 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. | |
| #include "verilog/preprocessor/verilog_preprocess.h" | |
| #include <functional> | |
| #include <iterator> | |
| #include <memory> | |
| #include <utility> | |
| #include <vector> | |
| #include "absl/memory/memory.h" | |
| #include "absl/status/status.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/string_view.h" | |
| #include "common/lexer/token_generator.h" | |
| #include "common/lexer/token_stream_adapter.h" | |
| #include "common/text/macro_definition.h" | |
| #include "common/text/token_info.h" | |
| #include "common/text/token_stream_view.h" | |
| #include "common/util/container_util.h" | |
| #include "common/util/logging.h" | |
| #include "verilog/parser/verilog_parser.h" // for verilog_symbol_name() | |
| #include "verilog/parser/verilog_token_enum.h" | |
| namespace verilog { | |
| using verible::TokenGenerator; | |
| using verible::TokenInfo; | |
| using verible::TokenStreamView; | |
| using verible::container::InsertOrUpdate; | |
| // Copies `define token iterators into a temporary buffer. | |
| // Assumes that the last token of a definition is the un-lexed definition body. | |
| // Tokens are copied from the 'generator' into 'define_tokens'. | |
| std::unique_ptr<VerilogPreprocessError> | |
| VerilogPreprocess::ConsumeMacroDefinition( | |
| const StreamIteratorGenerator& generator, TokenStreamView* define_tokens) { | |
| // Next token to expect is macro definition name. | |
| TokenStreamView::const_iterator token_iter = generator(); | |
| if ((*token_iter)->isEOF()) { | |
| return absl::make_unique<VerilogPreprocessError>( | |
| **token_iter, "unexpected EOF where expecting macro definition name"); | |
| } | |
| const auto macro_name = *token_iter; | |
| if (macro_name->token_enum() != PP_Identifier) { | |
| return absl::make_unique<VerilogPreprocessError>( | |
| **token_iter, | |
| absl::StrCat("Expected identifier for macro name, but got \"", | |
| macro_name->text(), "...\"")); | |
| } | |
| define_tokens->push_back(*token_iter); | |
| // Everything else covers macro parameters and the definition body. | |
| do { | |
| token_iter = generator(); | |
| if ((*token_iter)->isEOF()) { | |
| // Diagnose unexpected EOF downstream instead of erroring here. | |
| // Other subroutines can give better context about the parsing state. | |
| define_tokens->push_back(*token_iter); | |
| return nullptr; | |
| } | |
| define_tokens->push_back(*token_iter); | |
| } while ((*token_iter)->token_enum() != PP_define_body); | |
| return nullptr; | |
| } | |
| // Interprets a single macro definition parameter. | |
| // Tokens are scanned by advancing the token_scan iterator (by-reference). | |
| std::unique_ptr<VerilogPreprocessError> VerilogPreprocess::ParseMacroParameter( | |
| TokenStreamView::const_iterator* token_scan, | |
| MacroParameterInfo* macro_parameter) { | |
| auto advance = [](TokenStreamView::const_iterator* scan) { return *++*scan; }; | |
| auto token_iter = **token_scan; | |
| // Extract macro name. | |
| if (token_iter->token_enum() != PP_Identifier) { | |
| return absl::make_unique<VerilogPreprocessError>( | |
| *token_iter, | |
| absl::StrCat("expected identifier for macro parameter, but got: ", | |
| token_iter->ToString())); | |
| } | |
| macro_parameter->name = *token_iter; | |
| // Check for separator or default text. | |
| token_iter = advance(token_scan); | |
| if (token_iter->isEOF()) { | |
| return absl::make_unique<VerilogPreprocessError>( | |
| *token_iter, "unexpected EOF while parsing macro parameter"); | |
| } | |
| if (token_iter->token_enum() == '=') { | |
| token_iter = advance(token_scan); | |
| if (token_iter->isEOF()) { | |
| return absl::make_unique<VerilogPreprocessError>( | |
| *token_iter, | |
| "unexpected EOF where macro parameter default text is expected"); | |
| } | |
| if (token_iter->token_enum() != PP_default_text) { | |
| return absl::make_unique<VerilogPreprocessError>( | |
| *token_iter, | |
| absl::StrCat("expected macro parameter default text, but got: ", | |
| token_iter->ToString())); | |
| } | |
| // Note: the default parameter text is allowed to be empty. | |
| macro_parameter->default_value = *token_iter; | |
| token_iter = advance(token_scan); | |
| } | |
| if (token_iter->isEOF()) { | |
| return absl::make_unique<VerilogPreprocessError>( | |
| *token_iter, | |
| "unexpected EOF where expecting macro parameter separator"); | |
| } | |
| if (token_iter->token_enum() == ',') { | |
| advance(token_scan); // Advance to next parameter identifier. | |
| } else if (token_iter->token_enum() == ')') { | |
| // Do not advance. | |
| } else { | |
| // This case covers an unexpected EOF token. | |
| return absl::make_unique<VerilogPreprocessError>( | |
| *token_iter, | |
| absl::StrCat( | |
| "expecting macro parameter separator ',', or terminator ')', " | |
| "but got: ", | |
| verilog_symbol_name(token_iter->token_enum()))); | |
| } | |
| return nullptr; | |
| } | |
| // Parses an entire macro definition from header through body text. | |
| // The span of tokens that covers a macro definition is expected to | |
| // be in define_tokens. | |
| std::unique_ptr<VerilogPreprocessError> VerilogPreprocess::ParseMacroDefinition( | |
| const TokenStreamView& define_tokens, MacroDefinition* macro_definition) { | |
| auto token_scan = define_tokens.begin() + 2; // skip `define and the name | |
| auto token_iter = *token_scan; | |
| if (token_iter->token_enum() == '(') { | |
| token_iter = *++token_scan; | |
| // Scan for macro parameters. | |
| while (token_iter->token_enum() != ')') { | |
| MacroParameterInfo macro_parameter; | |
| auto error_ptr = ParseMacroParameter(&token_scan, ¯o_parameter); | |
| if (error_ptr) return error_ptr; | |
| macro_definition->AppendParameter(macro_parameter); | |
| token_iter = *token_scan; | |
| } // while there are macro parameters | |
| // Advance past final ')'. | |
| token_iter = *++token_scan; | |
| } | |
| // The macro definition body follows. | |
| if (token_iter->token_enum() != PP_define_body) { | |
| return absl::make_unique<VerilogPreprocessError>( | |
| *token_iter, | |
| absl::StrCat("expected macro definition body text, but got: ", | |
| token_iter->ToString())); | |
| } | |
| macro_definition->SetDefinitionText(*token_iter); | |
| ++token_scan; | |
| if (token_scan != define_tokens.end()) { | |
| token_iter = *token_scan; | |
| return absl::make_unique<VerilogPreprocessError>( | |
| *token_iter, | |
| absl::StrCat("expected no more tokens from macro definition, but got: ", | |
| token_iter->ToString())); | |
| } | |
| return nullptr; | |
| } | |
| // Interprets preprocessor tokens as directives that act on this preprocessor | |
| // object and possibly transform the input token stream. | |
| absl::Status VerilogPreprocess::HandleTokenIterator( | |
| const TokenStreamView::const_iterator iter, | |
| const StreamIteratorGenerator& generator) { | |
| // For now, pass through all macro definition tokens to next consumer | |
| // (parser). | |
| switch ((*iter)->token_enum()) { | |
| case PP_define: | |
| return HandleDefine(iter, generator); | |
| default: | |
| // All other tokens are passed through unmodified. | |
| preprocess_data_.preprocessed_token_stream.push_back(*iter); | |
| return absl::OkStatus(); | |
| } | |
| } | |
| // Stores a macro definition for later use. | |
| void VerilogPreprocess::RegisterMacroDefinition( | |
| const MacroDefinition& definition) { | |
| // For now, unconditionally register the macro definition, keeping the last | |
| // definition if macro is re-defined. | |
| const bool inserted = InsertOrUpdate(&preprocess_data_.macro_definitions, | |
| definition.Name(), definition); | |
| if (!inserted) { | |
| LOG(INFO) << "Re-defining macro " << definition.Name(); | |
| } | |
| // TODO(fangism): diagnose re-definitions | |
| } | |
| // Responds to `define directives. Macro definitions are parsed and saved | |
| // for use within the same file. | |
| absl::Status VerilogPreprocess::HandleDefine( | |
| const TokenStreamView::const_iterator iter, // points to `define token | |
| const StreamIteratorGenerator& generator) { | |
| TokenStreamView define_tokens; | |
| define_tokens.push_back(*iter); | |
| const auto consume_error_ptr = | |
| ConsumeMacroDefinition(generator, &define_tokens); | |
| if (consume_error_ptr) { | |
| preprocess_data_.errors.push_back(*consume_error_ptr); | |
| return absl::InvalidArgumentError("Error parsing macro definition."); | |
| } | |
| CHECK_GE(define_tokens.size(), 3) | |
| << "Macro definition should span at least 3 tokens, but only got " | |
| << define_tokens.size(); | |
| const verible::TokenSequence::const_iterator macro_name = define_tokens[1]; | |
| verible::MacroDefinition macro_definition(*define_tokens[0], *macro_name); | |
| const auto parse_error_ptr = | |
| ParseMacroDefinition(define_tokens, ¯o_definition); | |
| if (parse_error_ptr) { | |
| preprocess_data_.errors.push_back(*parse_error_ptr); | |
| return absl::InvalidArgumentError("Error parsing macro definition."); | |
| } | |
| // For now, forward all definition tokens. | |
| RegisterMacroDefinition(macro_definition); | |
| for (const auto& token : define_tokens) { | |
| preprocess_data_.preprocessed_token_stream.push_back(token); | |
| } | |
| return absl::OkStatus(); | |
| } | |
| VerilogPreprocessData VerilogPreprocess::ScanStream( | |
| const TokenStreamView& token_stream) { | |
| preprocess_data_.preprocessed_token_stream.reserve(token_stream.size()); | |
| auto iter_generator = verible::MakeConstIteratorStreamer(token_stream); | |
| const auto end = token_stream.end(); | |
| auto iter = iter_generator(); | |
| // Token-pulling loop. | |
| while (iter != end) { | |
| const auto status = HandleTokenIterator(iter, iter_generator); | |
| if (!status.ok()) { | |
| // Detailed errors are already in preprocessor_data_.errors. | |
| break; // For now, stop after first error. | |
| } | |
| iter = iter_generator(); | |
| } | |
| return std::move(preprocess_data_); | |
| } | |
| } // namespace verilog |