| // 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 "common/strings/comment_utils.h" | |
| #include <algorithm> | |
| #include <cstddef> | |
| #include <iterator> | |
| #include "absl/strings/ascii.h" | |
| #include "absl/strings/string_view.h" | |
| #include "common/util/logging.h" | |
| #include "common/util/range.h" | |
| namespace verible { | |
| // Returns the number of occurences of a character c in the text's prefix. | |
| static size_t CountLeadingChars(absl::string_view text, char c) { | |
| const auto rpos = text.find_first_not_of(c); | |
| if (rpos == absl::string_view::npos) return text.length(); | |
| return rpos; | |
| } | |
| // Returns the number of occurences of a character c in the text's suffix. | |
| static size_t CountTrailingChars(absl::string_view text, char c) { | |
| const auto rpos = std::find_if(text.rbegin(), text.rend(), | |
| [=](const char ch) { return ch != c; }); | |
| // if rpos == text.rend(), then return == text.length(). | |
| return std::distance(text.rbegin(), rpos); | |
| } | |
| // Strips away leading /*** and trailing ***/ from block comments. | |
| // Precondition: 'text' must begin with "/*" and end with "*/". | |
| // TODO(fangism): strip away extra spaces and *s on each intermediate line, | |
| // e.g. | |
| // /*** | |
| // * keep this | |
| // * and this | |
| // **/ | |
| // Returns a substring within text, even if it is equivalent to empty. | |
| static absl::string_view StripBlockComment(absl::string_view text) { | |
| // Adjust for multiple *'s like /**** and ****/ . | |
| // Strip off /* and */ first and then remove leading/trailing *'s. | |
| const size_t lpos = CountLeadingChars(text.substr(2), '*') + 2; | |
| auto text_slice = text; | |
| text_slice.remove_suffix(2); | |
| const size_t rtrim = CountTrailingChars(text_slice, '*') + 2; | |
| const size_t rpos = text.length() - rtrim; | |
| if (lpos > rpos) { | |
| // This can occur if comment looks like: /*******/ | |
| if (lpos == 2 && rpos == 1) { | |
| // /*/ is not a valid block comment, so do not strip it. | |
| return text; | |
| } | |
| // Return an empty string_view ("") whose range is within text. | |
| return text.substr(2, 0); | |
| } | |
| return text.substr(lpos, rpos - lpos); | |
| } | |
| absl::string_view StripComment(absl::string_view text) { | |
| if (text.length() < 2) return text; // cannot be an endline comment | |
| const absl::string_view start = text.substr(0, 2); | |
| const absl::string_view end = text.substr(text.length() - 2); | |
| if (start == "//") { | |
| const auto ltrim = CountLeadingChars(text.substr(2), '/') + 2; | |
| return text.substr(ltrim); | |
| } else if (start == "/*" && end == "*/") { | |
| return StripBlockComment(text); | |
| } | |
| // else is not a well-formed comment | |
| return text; | |
| } | |
| absl::string_view StripCommentAndSpacePadding(absl::string_view text) { | |
| const auto stripped_text = StripComment(text); | |
| CHECK(verible::IsSubRange(stripped_text, text)); | |
| const auto return_text = absl::StripAsciiWhitespace(stripped_text); | |
| CHECK(verible::IsSubRange(return_text, stripped_text)); | |
| return return_text; | |
| } | |
| } // namespace verible |