1 //===-- include/flang/Parser/char-block.h -----------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef FORTRAN_PARSER_CHAR_BLOCK_H_ 10 #define FORTRAN_PARSER_CHAR_BLOCK_H_ 11 12 // Describes a contiguous block of characters; does not own their storage. 13 14 #include "flang/Common/interval.h" 15 #include <algorithm> 16 #include <cstddef> 17 #include <cstring> 18 #include <iosfwd> 19 #include <string> 20 #include <utility> 21 22 namespace llvm { 23 class raw_ostream; 24 } 25 26 namespace Fortran::parser { 27 28 class CharBlock { 29 public: CharBlock()30 constexpr CharBlock() {} 31 constexpr CharBlock(const char *x, std::size_t n = 1) : interval_{x, n} {} CharBlock(const char * b,const char * ep1)32 constexpr CharBlock(const char *b, const char *ep1) 33 : interval_{b, static_cast<std::size_t>(ep1 - b)} {} CharBlock(const std::string & s)34 CharBlock(const std::string &s) : interval_{s.data(), s.size()} {} 35 constexpr CharBlock(const CharBlock &) = default; 36 constexpr CharBlock(CharBlock &&) = default; 37 constexpr CharBlock &operator=(const CharBlock &) = default; 38 constexpr CharBlock &operator=(CharBlock &&) = default; 39 empty()40 constexpr bool empty() const { return interval_.empty(); } size()41 constexpr std::size_t size() const { return interval_.size(); } begin()42 constexpr const char *begin() const { return interval_.start(); } end()43 constexpr const char *end() const { 44 return interval_.start() + interval_.size(); 45 } 46 constexpr const char &operator[](std::size_t j) const { 47 return interval_.start()[j]; 48 } 49 Contains(const CharBlock & that)50 bool Contains(const CharBlock &that) const { 51 return interval_.Contains(that.interval_); 52 } 53 ExtendToCover(const CharBlock & that)54 void ExtendToCover(const CharBlock &that) { 55 interval_.ExtendToCover(that.interval_); 56 } 57 58 // Returns the block's first non-blank character, if it has 59 // one; otherwise ' '. FirstNonBlank()60 char FirstNonBlank() const { 61 for (char ch : *this) { 62 if (ch != ' ' && ch != '\t') { 63 return ch; 64 } 65 } 66 return ' '; // non no-blank character 67 } 68 69 // Returns the block's only non-blank character, if it has 70 // exactly one non-blank character; otherwise ' '. OnlyNonBlank()71 char OnlyNonBlank() const { 72 char result{' '}; 73 for (char ch : *this) { 74 if (ch != ' ' && ch != '\t') { 75 if (result == ' ') { 76 result = ch; 77 } else { 78 return ' '; 79 } 80 } 81 } 82 return result; 83 } 84 CountLeadingBlanks()85 std::size_t CountLeadingBlanks() const { 86 std::size_t n{size()}; 87 std::size_t j{0}; 88 for (; j < n; ++j) { 89 char ch{(*this)[j]}; 90 if (ch != ' ' && ch != '\t') { 91 break; 92 } 93 } 94 return j; 95 } 96 IsBlank()97 bool IsBlank() const { return FirstNonBlank() == ' '; } 98 ToString()99 std::string ToString() const { 100 return std::string{interval_.start(), interval_.size()}; 101 } 102 103 // Convert to string, stopping early at any embedded '\0'. NULTerminatedToString()104 std::string NULTerminatedToString() const { 105 return std::string{interval_.start(), 106 /*not in std::*/ strnlen(interval_.start(), interval_.size())}; 107 } 108 109 bool operator<(const CharBlock &that) const { return Compare(that) < 0; } 110 bool operator<=(const CharBlock &that) const { return Compare(that) <= 0; } 111 bool operator==(const CharBlock &that) const { return Compare(that) == 0; } 112 bool operator!=(const CharBlock &that) const { return Compare(that) != 0; } 113 bool operator>=(const CharBlock &that) const { return Compare(that) >= 0; } 114 bool operator>(const CharBlock &that) const { return Compare(that) > 0; } 115 116 bool operator<(const char *that) const { return Compare(that) < 0; } 117 bool operator<=(const char *that) const { return Compare(that) <= 0; } 118 bool operator==(const char *that) const { return Compare(that) == 0; } 119 bool operator!=(const char *that) const { return Compare(that) != 0; } 120 bool operator>=(const char *that) const { return Compare(that) >= 0; } 121 bool operator>(const char *that) const { return Compare(that) > 0; } 122 123 friend bool operator<(const char *, const CharBlock &); 124 friend bool operator<=(const char *, const CharBlock &); 125 friend bool operator==(const char *, const CharBlock &); 126 friend bool operator!=(const char *, const CharBlock &); 127 friend bool operator>=(const char *, const CharBlock &); 128 friend bool operator>(const char *, const CharBlock &); 129 130 private: Compare(const CharBlock & that)131 int Compare(const CharBlock &that) const { 132 // "memcmp" in glibc has "nonnull" attributes on the input pointers. 133 // Avoid passing null pointers, since it would result in an undefined 134 // behavior. 135 if (size() == 0) { 136 return that.size() == 0 ? 0 : -1; 137 } else if (that.size() == 0) { 138 return 1; 139 } else { 140 std::size_t bytes{std::min(size(), that.size())}; 141 int cmp{std::memcmp(static_cast<const void *>(begin()), 142 static_cast<const void *>(that.begin()), bytes)}; 143 if (cmp != 0) { 144 return cmp; 145 } else { 146 return size() < that.size() ? -1 : size() > that.size(); 147 } 148 } 149 } 150 Compare(const char * that)151 int Compare(const char *that) const { 152 std::size_t bytes{size()}; 153 if (int cmp{std::strncmp(begin(), that, bytes)}) { 154 return cmp; 155 } 156 return that[bytes] == '\0' ? 0 : -1; 157 } 158 159 common::Interval<const char *> interval_{nullptr, 0}; 160 }; 161 162 inline bool operator<(const char *left, const CharBlock &right) { 163 return right > left; 164 } 165 inline bool operator<=(const char *left, const CharBlock &right) { 166 return right >= left; 167 } 168 inline bool operator==(const char *left, const CharBlock &right) { 169 return right == left; 170 } 171 inline bool operator!=(const char *left, const CharBlock &right) { 172 return right != left; 173 } 174 inline bool operator>=(const char *left, const CharBlock &right) { 175 return right <= left; 176 } 177 inline bool operator>(const char *left, const CharBlock &right) { 178 return right < left; 179 } 180 181 // An alternative comparator based on pointer values; use with care! 182 struct CharBlockPointerComparator { operatorCharBlockPointerComparator183 bool operator()(CharBlock x, CharBlock y) const { 184 return x.end() < y.begin(); 185 } 186 }; 187 188 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const CharBlock &x); 189 190 } // namespace Fortran::parser 191 192 // Specializations to enable std::unordered_map<CharBlock, ...> &c. 193 template <> struct std::hash<Fortran::parser::CharBlock> { 194 std::size_t operator()(const Fortran::parser::CharBlock &x) const { 195 std::size_t hash{0}, bytes{x.size()}; 196 for (std::size_t j{0}; j < bytes; ++j) { 197 hash = (hash * 31) ^ x[j]; 198 } 199 return hash; 200 } 201 }; 202 #endif // FORTRAN_PARSER_CHAR_BLOCK_H_ 203