1 // Copyright (c) 2018-2019, NVIDIA CORPORATION.  All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef FORTRAN_PARSER_CHAR_BLOCK_H_
16 #define FORTRAN_PARSER_CHAR_BLOCK_H_
17 
18 // Describes a contiguous block of characters; does not own their storage.
19 
20 #include "../common/interval.h"
21 #include <algorithm>
22 #include <cstddef>
23 #include <cstring>
24 #include <iosfwd>
25 #include <string>
26 #include <utility>
27 
28 namespace Fortran::parser {
29 
30 class CharBlock {
31 public:
CharBlock()32   constexpr CharBlock() {}
33   constexpr CharBlock(const char *x, std::size_t n = 1) : interval_{x, n} {}
CharBlock(const char * b,const char * ep1)34   constexpr CharBlock(const char *b, const char *ep1)
35     : interval_{b, static_cast<std::size_t>(ep1 - b)} {}
CharBlock(const std::string & s)36   CharBlock(const std::string &s) : interval_{s.data(), s.size()} {}
37   constexpr CharBlock(const CharBlock &) = default;
38   constexpr CharBlock(CharBlock &&) = default;
39   constexpr CharBlock &operator=(const CharBlock &) = default;
40   constexpr CharBlock &operator=(CharBlock &&) = default;
41 
empty()42   constexpr bool empty() const { return interval_.empty(); }
size()43   constexpr std::size_t size() const { return interval_.size(); }
begin()44   constexpr const char *begin() const { return interval_.start(); }
end()45   constexpr const char *end() const {
46     return interval_.start() + interval_.size();
47   }
48   constexpr const char &operator[](std::size_t j) const {
49     return interval_.start()[j];
50   }
51 
Contains(const CharBlock & that)52   bool Contains(const CharBlock &that) const {
53     return interval_.Contains(that.interval_);
54   }
55 
ExtendToCover(const CharBlock & that)56   void ExtendToCover(const CharBlock &that) {
57     interval_.ExtendToCover(that.interval_);
58   }
59 
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 
IsBlank()69   bool IsBlank() const { return FirstNonBlank() == ' '; }
70 
ToString()71   std::string ToString() const {
72     return std::string{interval_.start(), interval_.size()};
73   }
74 
75   // Convert to string, stopping early at any embedded '\0'.
NULTerminatedToString()76   std::string NULTerminatedToString() const {
77     return std::string{interval_.start(),
78         /*not in std::*/ strnlen(interval_.start(), interval_.size())};
79   }
80 
81   bool operator<(const CharBlock &that) const { return Compare(that) < 0; }
82   bool operator<=(const CharBlock &that) const { return Compare(that) <= 0; }
83   bool operator==(const CharBlock &that) const { return Compare(that) == 0; }
84   bool operator!=(const CharBlock &that) const { return Compare(that) != 0; }
85   bool operator>=(const CharBlock &that) const { return Compare(that) >= 0; }
86   bool operator>(const CharBlock &that) const { return Compare(that) > 0; }
87 
88   bool operator<(const char *that) const { return Compare(that) < 0; }
89   bool operator<=(const char *that) const { return Compare(that) <= 0; }
90   bool operator==(const char *that) const { return Compare(that) == 0; }
91   bool operator!=(const char *that) const { return Compare(that) != 0; }
92   bool operator>=(const char *that) const { return Compare(that) >= 0; }
93   bool operator>(const char *that) const { return Compare(that) > 0; }
94 
95   friend bool operator<(const char *, const CharBlock &);
96   friend bool operator<=(const char *, const CharBlock &);
97   friend bool operator==(const char *, const CharBlock &);
98   friend bool operator!=(const char *, const CharBlock &);
99   friend bool operator>=(const char *, const CharBlock &);
100   friend bool operator>(const char *, const CharBlock &);
101 
102 private:
Compare(const CharBlock & that)103   int Compare(const CharBlock &that) const {
104     std::size_t bytes{std::min(size(), that.size())};
105     int cmp{std::memcmp(static_cast<const void *>(begin()),
106         static_cast<const void *>(that.begin()), bytes)};
107     if (cmp != 0) {
108       return cmp;
109     }
110     return size() < that.size() ? -1 : size() > that.size();
111   }
112 
Compare(const char * that)113   int Compare(const char *that) const {
114     std::size_t bytes{size()};
115     if (int cmp{std::strncmp(begin(), that, bytes)}) {
116       return cmp;
117     }
118     return that[bytes] == '\0' ? 0 : -1;
119   }
120 
121   common::Interval<const char *> interval_{nullptr, 0};
122 };
123 
124 inline bool operator<(const char *left, const CharBlock &right) {
125   return right > left;
126 }
127 inline bool operator<=(const char *left, const CharBlock &right) {
128   return right >= left;
129 }
130 inline bool operator==(const char *left, const CharBlock &right) {
131   return right == left;
132 }
133 inline bool operator!=(const char *left, const CharBlock &right) {
134   return right != left;
135 }
136 inline bool operator>=(const char *left, const CharBlock &right) {
137   return right <= left;
138 }
139 inline bool operator>(const char *left, const CharBlock &right) {
140   return right < left;
141 }
142 
143 inline std::ostream &operator<<(std::ostream &os, const CharBlock &x) {
144   return os << x.ToString();
145 }
146 
147 }
148 
149 // Specializations to enable std::unordered_map<CharBlock, ...> &c.
150 template<> struct std::hash<Fortran::parser::CharBlock> {
151   std::size_t operator()(const Fortran::parser::CharBlock &x) const {
152     std::size_t hash{0}, bytes{x.size()};
153     for (std::size_t j{0}; j < bytes; ++j) {
154       hash = (hash * 31) ^ x[j];
155     }
156     return hash;
157   }
158 };
159 #endif  // FORTRAN_PARSER_CHAR_BLOCK_H_
160