Skip to content

Commit 02df5e4

Browse files
committed
Add string search utility
1 parent d0f3778 commit 02df5e4

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed

include/ctre/atoms.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct any { };
1919

2020
// actual AST of regexp
2121
template <auto... Str> struct string { };
22+
template <auto... Str> struct string_search { };
2223
template <typename... Opts> struct select { };
2324
template <typename... Content> struct sequence { };
2425
struct empty { };

include/ctre/evaluation.hpp

+100
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,106 @@ constexpr CTRE_FORCE_INLINE R evaluate(const Iterator begin, Iterator current, c
132132
return evaluate(begin, result.position, end, consumed_something(f, sizeof...(String) > 0), captures, ctll::list<Tail...>());
133133
}
134134

135+
template<typename Ty>
136+
constexpr bool is_prefix(Ty* word, size_t wordlen, ptrdiff_t pos) {
137+
ptrdiff_t suffixlen = wordlen - pos;
138+
for (int i = 0; i < suffixlen; i++) {
139+
if (word[i] != word[pos + i]) {
140+
return false;
141+
}
142+
}
143+
return true;
144+
}
145+
146+
template<typename Ty>
147+
constexpr size_t suffix_length(Ty* word, size_t wordlen, ptrdiff_t pos) {
148+
size_t i = 0;
149+
// increment suffix length i to the first mismatch or beginning of the word
150+
for (; (word[pos - i] == word[wordlen - 1 - i]) && (i < pos); i++);
151+
return i;
152+
}
153+
//MSVC workaround, array operator[] blows up in face if constexpr, use pointers instead
154+
template<typename Ty, auto... String>
155+
constexpr auto make_delta_2(string<String...>) {
156+
std::array<Ty, sizeof...(String)> chars{ String... };
157+
std::array<ptrdiff_t, sizeof...(String)> table;
158+
constexpr size_t patlen = sizeof...(String);
159+
size_t p = 0;
160+
size_t last_prefix_index = patlen - 1;
161+
162+
for (p = patlen - 1; p < patlen; p--) {
163+
if (is_prefix(chars.data(), patlen, p + 1)) {
164+
last_prefix_index = p + 1;
165+
}
166+
table.data()[p] = last_prefix_index + (patlen - 1 - p);
167+
}
168+
169+
for (p = 0; p < patlen - 1; p++) {
170+
size_t slen = suffix_length(chars.data(), patlen, p);
171+
if (chars.data()[p - slen] != chars.data()[patlen - 1 - slen]) {
172+
table.data()[patlen - 1 - slen] = patlen - 1 - p + slen;
173+
}
174+
}
175+
176+
return table;
177+
}
178+
179+
template <typename Iterator, typename EndIterator, auto... String>
180+
constexpr CTRE_FORCE_INLINE string_match_result<Iterator> evaluate_search_string(Iterator current, const EndIterator end, string<String...>) {
181+
if constexpr (sizeof...(String) > 2 && ::std::is_convertible_v<typename ::std::iterator_traits<Iterator>::iterator_category, ::std::random_access_iterator_tag>) {
182+
constexpr std::array<typename ::std::iterator_traits<Iterator>::value_type, sizeof...(String)> chars{ String... };
183+
constexpr std::array<ptrdiff_t, sizeof...(String)> delta_2 = make_delta_2<typename ::std::iterator_traits<Iterator>::value_type>(string<String...>());
184+
185+
size_t str_size = std::distance(current, end);
186+
if (str_size < sizeof...(String)) { //quick exit no way to match
187+
return { current, false };
188+
}
189+
190+
size_t i = sizeof...(String) - 1; //index over to the starting location
191+
for (; i < str_size;) {
192+
size_t j = sizeof...(String) - 1;
193+
for (; *(current + i) == *(chars.data() + j); --i, --j) { //match string in reverse
194+
if (j == 0) {
195+
return { current + i, true };
196+
}
197+
}
198+
size_t shift = enumeration<String...>::match_char(*(current + i)) ? static_cast<size_t>(*(delta_2.data() + j)) : sizeof...(String);
199+
i += shift;
200+
}
201+
202+
return { current + str_size, false };
203+
} else if (sizeof...(String)) {
204+
//fallback to plain string matching
205+
constexpr std::array<typename ::std::iterator_traits<Iterator>::value_type, sizeof...(String)> chars{ String... };
206+
constexpr typename ::std::iterator_traits<Iterator>::value_type first_char = chars.data()[0];
207+
while (current != end) {
208+
while (current != end && *current != first_char) {
209+
current++;
210+
}
211+
auto result = evaluate_match_string<String...>(current, end, std::make_index_sequence<sizeof...(String)>());
212+
if (result.match) {
213+
return result;
214+
} else {
215+
++current;
216+
}
217+
}
218+
return { current, false };
219+
} else {
220+
return { current, true };
221+
}
222+
}
223+
224+
template <typename R, typename Iterator, typename EndIterator, auto... String, typename... Tail>
225+
constexpr CTRE_FORCE_INLINE R evaluate(const Iterator begin, Iterator current, const EndIterator end, [[maybe_unused]] const flags& f, R captures, ctll::list<string_search<String...>, Tail...>) noexcept {
226+
auto result = evaluate_search_string(current, end, string<String...>());
227+
228+
if (!result.matched) {
229+
return not_matched;
230+
}
231+
232+
return evaluate(begin, std::advance(result.position, sizeof...(String)), end, consumed_something(f, sizeof...(String) > 0), captures, ctll::list<Tail...>());
233+
}
234+
135235
// matching select in patterns
136236
template <typename R, typename Iterator, typename EndIterator, typename HeadOptions, typename... TailOptions, typename... Tail>
137237
constexpr CTRE_FORCE_INLINE R evaluate(const Iterator begin, Iterator current, const EndIterator end, const flags & f, R captures, ctll::list<select<HeadOptions, TailOptions...>, Tail...>) noexcept {

0 commit comments

Comments
 (0)