Skip to content

Commit eeb4e13

Browse files
committed
patch: optimize selects by extracting exclusive branches
1 parent 95afdd8 commit eeb4e13

File tree

3 files changed

+125
-3
lines changed

3 files changed

+125
-3
lines changed

include/ctll/list.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ template <typename T> struct item_matcher {
6161
static constexpr auto check(...) { return std::false_type{}; }
6262
static constexpr auto select(T) { return not_selected{}; }
6363
template <typename Y> static constexpr auto select(Y) { return wrapper<Y>{}; }
64+
static constexpr auto pick(std::true_type) { return wrapper<Y>{}; };
65+
static constexpr auto pick(std::false_type) { return not_selected{}; };
6466
};
6567

6668
template <typename T, typename... Ts> constexpr bool exists_in(T, list<Ts...>) noexcept {

include/ctre/atoms.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ struct any { };
2020
// actual AST of regexp
2121
template <auto... Str> struct string { };
2222
template <typename... Opts> struct select { };
23+
template <typename... Opts> struct non_exclusive_select { };
2324
template <typename... Content> struct sequence { };
2425
struct empty { };
2526

include/ctre/evaluation.hpp

+122-3
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,116 @@ constexpr CTRE_FORCE_INLINE R evaluate(const Iterator begin, Iterator current, c
135135
return evaluate(begin, result.position, last, consumed_something(f, sizeof...(String) > 0), captures, ctll::list<Tail...>());
136136
}
137137

138+
template <typename T, typename... Ts> constexpr auto collides_with(T, ctll::list<Ts...>) noexcept {
139+
return decltype((ctll::list<>{} + ... + ctll::item_matcher<Ts>::pick(
140+
typename std::conditional<collides(calculate_first(sequence<T>{}), calculate_first(sequence<Ts>{})), std::true_type, std::false_type>::type{}
141+
))){};
142+
}
143+
144+
template <typename T, typename... Ts> constexpr auto collides_with_negated(T, ctll::list<Ts...>) noexcept {
145+
return decltype((ctll::list<>{} + ... + ctll::item_matcher<Ts>::pick(
146+
typename std::conditional<collides(ctre::negative_set<decltype(calculate_first(sequence<T>{}))>{}, calculate_first(sequence<Ts>{})), std::true_type, std::false_type>::type{}
147+
))){};
148+
}
149+
150+
template <typename T, typename... Ts> constexpr auto mutually_exclusive_with(T, ctll::list<Ts...>) noexcept {
151+
return decltype((ctll::list<>{} + ... + ctll::item_matcher<Ts>::pick(
152+
typename std::conditional<!collides(calculate_first(sequence<T>{}), calculate_first(sequence<Ts>{})), std::true_type, std::false_type>::type{}
153+
))){};
154+
}
155+
156+
namespace detail {
157+
template <typename... Content>
158+
constexpr auto transform_into_set(ctll::list<Content...>) {
159+
return ctre::set<decltype(Content{})...>{};
160+
//return ctre::set<decltype(transform_into_set(Content{}))... > {};
161+
}
162+
163+
template <typename T>
164+
constexpr auto transform_into_set(T) {
165+
return T{};
166+
}
167+
168+
template<>
169+
constexpr auto transform_into_set(can_be_anything) {
170+
return ctre::char_range<std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max()>{};
171+
}
172+
173+
template <typename... Content>
174+
constexpr auto transform_into_select(ctll::list<Content...>) {
175+
return ctre::select<decltype(Content{})...>{};
176+
}
177+
}
178+
138179
// matching select in patterns
139180
template <typename R, typename Iterator, typename EndIterator, typename HeadOptions, typename... TailOptions, typename... Tail>
140181
constexpr CTRE_FORCE_INLINE R evaluate(const Iterator begin, Iterator current, const EndIterator last, const flags & f, R captures, ctll::list<select<HeadOptions, TailOptions...>, Tail...>) noexcept {
141-
if (auto r = evaluate(begin, current, last, f, captures, ctll::list<HeadOptions, Tail...>())) {
142-
return r;
182+
if constexpr (sizeof...(TailOptions) > 1) {
183+
constexpr auto collision_list = collides_with(sequence<HeadOptions, Tail...>{}, ctll::list<
184+
decltype(sequence<TailOptions, Tail...>{})...
185+
>{});
186+
187+
if constexpr (ctll::empty(collision_list)) {
188+
using set_type = decltype(detail::transform_into_set(calculate_first(sequence<HeadOptions, Tail...>{})));
189+
if constexpr (::std::is_same_v<set<>, set_type>) {
190+
//fail handle as normal
191+
if (auto r = evaluate(begin, current, last, f, captures, ctll::list<HeadOptions, Tail...>())) {
192+
return r;
193+
} else {
194+
return evaluate(begin, current, last, f, captures, ctll::list<select<TailOptions...>, Tail...>());
195+
}
196+
} else {
197+
//ok optimize into exclusive select
198+
if (auto r = evaluate(begin, current, last, f, captures, ctll::list<set_type, end_cycle_mark>{})) {
199+
return evaluate(begin, current, last, f, captures, ctll::list<HeadOptions, Tail...>{});
200+
} else {
201+
return evaluate(begin, current, last, f, captures, ctll::list<select<TailOptions...>, Tail...>());
202+
}
203+
}
204+
205+
return evaluate(begin, current, last, f, captures, ctll::list<HeadOptions, Tail...>());
206+
} else if constexpr (ctll::size(collision_list) == sizeof...(TailOptions)) {
207+
//continue as normal...we collided with everything
208+
if (auto r = evaluate(begin, current, last, f, captures, ctll::list<HeadOptions, Tail...>())) {
209+
return r;
210+
} else {
211+
return evaluate(begin, current, last, f, captures, ctll::list<select<TailOptions...>, Tail...>());
212+
}
213+
} else {
214+
//we collided with some things, but not others, bifricate on the first character we see
215+
//may be less computationally expensive to do a set subtraction from the complete list with the collision list, because we're evaluating collisions again
216+
constexpr auto negated_collision_list = collides_with_negated(sequence<HeadOptions, Tail...>{}, ctll::list<
217+
decltype(sequence<TailOptions, Tail...>{})...
218+
>{});
219+
220+
using set_type = decltype(detail::transform_into_set(calculate_first(sequence<HeadOptions, Tail...>{})));
221+
if constexpr (::std::is_same_v<set<>, set_type>) {
222+
//fail
223+
if (auto r = evaluate(begin, current, last, f, captures, ctll::list<HeadOptions, Tail...>())) {
224+
return r;
225+
} else {
226+
return evaluate(begin, current, last, f, captures, ctll::list<non_exclusive_select<TailOptions...>, Tail...>());
227+
}
228+
} else {
229+
//ok optimize into a split
230+
if (auto r = evaluate(begin, current, last, f, captures, ctll::list<set_type, end_cycle_mark>{})) {
231+
if (auto r2 = evaluate(begin, current, last, f, captures, ctll::list<HeadOptions, Tail...>{})) {
232+
return r2;
233+
} else {
234+
return evaluate(begin, current, last, f, captures, detail::transform_into_select(collision_list));
235+
}
236+
} else {
237+
return evaluate(begin, current, last, f, captures, detail::transform_into_select(negated_collision_list));
238+
}
239+
}
240+
}
143241
} else {
144-
return evaluate(begin, current, last, f, captures, ctll::list<select<TailOptions...>, Tail...>());
242+
//simple case, too few branches to pick handle as normal
243+
if (auto r = evaluate(begin, current, last, f, captures, ctll::list<HeadOptions, Tail...>())) {
244+
return r;
245+
} else {
246+
return evaluate(begin, current, last, f, captures, ctll::list<select<TailOptions...>, Tail...>());
247+
}
145248
}
146249
}
147250

@@ -151,6 +254,22 @@ constexpr CTRE_FORCE_INLINE R evaluate(const Iterator, Iterator, const EndIterat
151254
return not_matched;
152255
}
153256

257+
// non exclusive select (assume collisions)
258+
template <typename R, typename Iterator, typename EndIterator, typename HeadOptions, typename... TailOptions, typename... Tail>
259+
constexpr CTRE_FORCE_INLINE R evaluate(const Iterator begin, Iterator current, const EndIterator last, const flags & f, R captures, ctll::list<non_exclusive_select<HeadOptions, TailOptions...>, Tail...>) noexcept {
260+
if (auto r = evaluate(begin, current, last, f, captures, ctll::list<HeadOptions, Tail...>())) {
261+
return r;
262+
} else {
263+
return evaluate(begin, current, last, f, captures, ctll::list<non_exclusive_select<TailOptions...>, Tail...>());
264+
}
265+
}
266+
267+
template <typename R, typename Iterator, typename EndIterator, typename... Tail>
268+
constexpr CTRE_FORCE_INLINE R evaluate(const Iterator, Iterator, const EndIterator, flags, R, ctll::list<non_exclusive_select<>, Tail...>) noexcept {
269+
// no previous option was matched => REJECT
270+
return not_matched;
271+
}
272+
154273
// matching sequence in patterns
155274
template <typename R, typename Iterator, typename EndIterator, typename HeadContent, typename... TailContent, typename... Tail>
156275
constexpr CTRE_FORCE_INLINE R evaluate(const Iterator begin, Iterator current, const EndIterator last, const flags & f, R captures, ctll::list<sequence<HeadContent, TailContent...>, Tail...>) noexcept {

0 commit comments

Comments
 (0)