@@ -45,21 +45,16 @@ class fqname {
4545
4646 constexpr const_iterator (char const * first) noexcept :
4747 _first(first),
48- _last(fixup_last(first))
48+ _last(fixup_last(first)) {}
4949
50- [[nodiscard]] constexpr bool operator==(const_iterator const & rhs) noexcept
50+ [[nodiscard]] constexpr friend bool operator ==(const_iterator const & lhs, const_iterator const & rhs) noexcept
5151 {
52- return _first == rhs._first ;
52+ return lhs. _first == rhs._first ;
5353 }
5454
55- [[nodiscard]] constexpr bool operator ==(std::default_sentinel_t ) noexcept
55+ [[nodiscard]] constexpr friend bool operator ==(const_iterator const & lhs, std::default_sentinel_t ) noexcept
5656 {
57- return _first == nullptr or *_first == ' \0 ' ;
58- }
59-
60- [[nodiscard]] constexpr std::strong_ordering operator <=>(const_iterator const & rhs) noexcept
61- {
62- return _first <=> rhs._first ;
57+ return lhs._first == nullptr or *lhs._first == ' \0 ' ;
6358 }
6459
6560 constexpr const_iterator& operator ++()
@@ -118,75 +113,158 @@ class fqname {
118113 constexpr fqname& operator =(fqname const &) = default ;
119114 constexpr fqname& operator =(fqname&&) = default ;
120115
116+ constexpr fqname (std::string_view other) : _str(other) {}
117+ constexpr fqname (std::string other) : _str(std::move(other)) {}
118+ constexpr fqname (char const *other) : _str(other) {}
119+
120+ [[nodiscard]] constexpr friend bool operator ==(fqname const & lhs, fqname const & rhs) noexcept
121+ {
122+ return std::equal (lhs.begin (), lhs.end (), rhs.begin (), rhs.end ());
123+ }
124+
125+ [[nodiscard]] constexpr friend std::strong_ordering operator <=>(fqname const & lhs, fqname const & rhs) noexcept
126+ {
127+ return std::lexicographical_compare_three_way (lhs.begin (), lhs.end (), rhs.begin (), rhs.end ());
128+ }
129+
121130 [[nodiscard]] constexpr std::string const & string () const noexcept
122131 {
123132 return _str;
124133 }
125134
126- [[nodiscard]] const_iterator begin () const noexcept
135+ [[nodiscard]] constexpr size_t prefix () const noexcept
136+ {
137+ if (auto const i = _str.find_first_not_of (' .' ); i != _str.npos ) {
138+ return i;
139+ } else {
140+ return _str.size ();
141+ }
142+ }
143+
144+ [[nodiscard]] constexpr bool is_absolute () const noexcept
145+ {
146+ return prefix () == 1 ;
147+ }
148+
149+ [[nodiscard]] constexpr bool is_relative () const noexcept
150+ {
151+ return not is_absolute ();
152+ }
153+
154+ [[nodiscard]] constexpr const_iterator begin () const noexcept
127155 {
128156 return const_iterator{_str.c_str ()};
129157 }
130158
131- [[nodiscard]] const_iterator end () const noexcept
159+ [[nodiscard]] constexpr const_iterator end () const noexcept
132160 {
133161 return const_iterator{_str.c_str () + _str.size ()};
134162 }
135163
136- bool pop_component ()
164+ /* * Pop the last component.
165+ *
166+ * Removes the last component, or:
167+ * - if empty, the result is: ..
168+ * - if absolute, the result is: .
169+ * - if there are only dots, add one more dot.
170+ */
171+ constexpr void pop_component ()
137172 {
173+ using namespace std ::literals;
174+
138175 if (_str.empty ()) {
139- return false ;
176+ _str = " .." s;
177+ } else if (auto const p = prefix (); p == _str.size ()) {
178+ if (p != 1 ) {
179+ _str += ' .' ;
180+ }
181+ } else {
182+ if (auto const i = _str.rfind (' .' ); i == _str.npos ) {
183+ _str.clear ();
184+ } else {
185+ _str.erase (i);
186+ }
140187 }
188+ }
189+
190+ constexpr fqname& add_component (std::string_view component)
191+ {
192+ assert (component.find (' .' ) != component.npos );
193+
194+ if (component.empty ()) {
195+ _str += ' .' ;
141196
142- if (auto const i = _str.rfind (' .' ); i == _str.npos ) {
143- _str.clear ();
144- return true ;
145197 } else {
146- _str.erase (i);
147- return true ;
198+ if (not _str.empty ()) {
199+ _str += ' .' ;
200+ }
201+ _str += component;
148202 }
203+ return *this ;
149204 }
150205
151- fqname& operator /=(std::string_view component )
206+ constexpr fqname& operator /=(fqname const & rhs )
152207 {
153- assert (component.empty () or (component.first () != ' .' and component.last () != ' .' ));
208+ if (rhs.is_absolute ()) {
209+ *this = rhs;
210+ return *this ;
211+ }
154212
155- if (not _str.empty ()) {
156- _str += ' .' ;
213+ for (auto component : rhs) {
214+ if (component.empty ()) {
215+ pop_component ();
216+ } else {
217+ add_component (component);
218+ }
157219 }
158- _str += component;
159220 return *this ;
160221 }
161222
162- [[nodiscard]] fqname operator /(std::string_view component )
223+ constexpr fqname operator /(fqname const & rhs )
163224 {
164225 auto tmp = *this ;
165- tmp /= component ;
226+ tmp /= rhs ;
166227 return tmp;
167228 }
168229
169- fqname& operator /=(fqname const & other )
230+ constexpr fqname& operator /=(std::string_view rhs )
170231 {
171- if (other.is_absolute ()) {
172- *this = other;
173- return *this ;
174- }
232+ return *this /= fqname{rhs};
233+ }
234+
235+ constexpr fqname operator /(std::string_view rhs)
236+ {
237+ return *this / fqname{rhs};
238+ }
175239
176- auto leading = true ;
177- for (auto component : other) {
240+ /* * Generate a lexically normal path.
241+ *
242+ */
243+ constexpr fqname lexically_normal () const
244+ {
245+ auto r = fqname{};
246+ r._str .reserve (_str.size ());
247+
248+ auto is_prefix = true ;
249+ for (auto component: *this ) {
178250 if (component.empty ()) {
179- assert (leading);
180- pop_component ();
251+ if (is_prefix) {
252+ r.add_component (component);
253+ } else {
254+ r.pop_component ();
255+ }
181256 } else {
182- leading = false ;
183- * this /= component ;
257+ r. add_component (component) ;
258+ is_prefix = false ;
184259 }
185260 }
261+
262+ return r;
186263 }
187264
188265private:
189266 std::string _str = {};
267+
190268};
191269
192270} // namespace hk
0 commit comments