forked from standardese/cppast
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvisitor.hpp
More file actions
242 lines (210 loc) · 8.06 KB
/
Copy pathvisitor.hpp
File metadata and controls
242 lines (210 loc) · 8.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
// Copyright (C) 2017-2018 Jonathan Müller <[email protected]>
// This file is subject to the license terms in the LICENSE file
// found in the top-level directory of this distribution.
#ifndef CPPAST_VISITOR_HPP_INCLUDED
#define CPPAST_VISITOR_HPP_INCLUDED
#include <type_traits>
#include <cppast/cpp_class.hpp>
#include <cppast/cpp_entity.hpp>
#include <cppast/cpp_entity_kind.hpp>
namespace cppast
{
/// Information about the state of a visit operation.
struct visitor_info
{
enum event_type
{
leaf_entity, //< Callback called for a leaf entity without children.
/// If callback returns `false`, visit operation will be aborted.
container_entity_enter, //< Callback called for a container entity before the children.
/// If callback returns `false`, none of the children will be visited,
/// going immediately to the exit event.
container_entity_exit, //< Callback called for a container entity after the children.
/// If callback returns `false`, visit operation will be aborted.
} event;
cpp_access_specifier_kind access; //< The current access specifier.
/// True when the current entity is the last child of the visited parent entity.
/// \notes It will always be `false` for the initial entity.
bool last_child;
/// \returns `true` if the entity was not visited already.
bool is_new_entity() const noexcept
{
return event != container_entity_exit;
}
/// \returns `true` if the entity was visited already.
bool is_old_entity() const noexcept
{
return !is_new_entity();
}
};
/// A more expressive way to specify the return of a visit operation.
enum visitor_result : bool
{
/// Visit should continue.
/// \group continue
continue_visit = true,
/// \group continue
continue_visit_children = true,
/// Visit should not visit the children.
/// \notes This only happens when the event is [cppast::visitor_info::container_entity_enter]().
continue_visit_no_children = false,
/// Visit should be aborted.
/// \notes This only happens when the event is not
/// [cppast::visitor_info::container_entity_enter]().
abort_visit = false,
};
/// \exclude
namespace detail
{
using visitor_callback_t = bool (*)(void* mem, const cpp_entity&, visitor_info info);
struct visitor_info_void
{};
struct visitor_info_bool : visitor_info_void
{};
template <typename Func>
visitor_callback_t get_visitor_callback(
visitor_info_bool,
decltype(std::declval<Func>()(std::declval<const cpp_entity&>(), visitor_info{})) = true)
{
return [](void* mem, const cpp_entity& e, visitor_info info) {
auto& func = *static_cast<Func*>(mem);
return func(e, info);
};
}
template <typename Func>
visitor_callback_t get_visitor_callback(
visitor_info_void,
decltype(std::declval<Func>()(std::declval<const cpp_entity&>(), visitor_info{}), 0) = 0)
{
return [](void* mem, const cpp_entity& e, visitor_info info) {
auto& func = *static_cast<Func*>(mem);
func(e, info);
return true;
};
}
template <typename Func>
visitor_callback_t get_visitor_callback()
{
return get_visitor_callback<Func>(visitor_info_bool{});
}
bool visit(const cpp_entity& e, visitor_callback_t cb, void* functor,
cpp_access_specifier_kind cur_access, bool last_child);
} // namespace detail
/// Visits a [cppast::cpp_entity]() and children.
///
/// \effects It will invoke the callback - the visitor - for the current entity and children,
/// as controlled by the [cppast::visitor_result]().
/// The visitor is given a reference to the currently visited entity and the
/// [cppast::visitor_info]().
///
/// \requires The visitor must be callable as specified and must either return `bool` or nothing.
/// If it returns nothing, a return value [cppast::visitor_result::continue_visit]() is assumed.
template <typename Func>
void visit(const cpp_entity& e, Func f)
{
detail::visit(e, detail::get_visitor_callback<Func>(), &f, cpp_public, false);
}
/// The result of a visitor filter operation.
enum class visit_filter
{
include = true, //< The entity is included.
exclude = false, //< The entity is excluded and will not be visited.
exclude_and_children
= 2, //< The entity and all direct children are excluded and will not be visited.
};
namespace detail
{
using visitor_filter_t = visit_filter (*)(const cpp_entity&);
template <typename Filter>
auto invoke_visit_filter(int, Filter f, const cpp_entity& e, cpp_access_specifier_kind access)
-> decltype(static_cast<visit_filter>(f(e, access)))
{
return static_cast<visit_filter>(f(e, access));
}
template <typename Filter>
auto invoke_visit_filter(short, Filter f, const cpp_entity& e, cpp_access_specifier_kind)
-> decltype(static_cast<visit_filter>(f(e)))
{
return static_cast<visit_filter>(f(e));
}
} // namespace detail
/// Visits a [cppast::cpp_entity]() and children that pass a given filter.
///
/// \effects It behaves like the non-filtered visit except it will only invoke the visitor for
/// entities that match the filter. The filter is a predicate that will be invoked with the current
/// entity and access specifier first and the result converted to [cppast::visit_filter](). Visit
/// will behave accordingly.
///
/// \requires The visitor must be as specified for the other overload.
/// The filter must be a function taking a [cppast::cpp_entity]() as first parameter and optionally
/// a [cppast::cpp_access_specifier_kind]() as second. It must return a `bool` or a
/// [cppast::visit_filter]().
template <typename Func, typename Filter>
void visit(const cpp_entity& e, Filter filter, Func f)
{
visit(e, [&](const cpp_entity& e, const visitor_info& info) -> bool {
auto result = detail::invoke_visit_filter(0, filter, e, info.access);
switch (result)
{
case visit_filter::include:
return detail::get_visitor_callback<Func>()(&f, e, info);
case visit_filter::exclude:
return continue_visit;
case visit_filter::exclude_and_children:
// exclude children if entering
if (info.event == visitor_info::container_entity_enter)
return continue_visit_no_children;
else
return continue_visit;
}
return continue_visit;
});
}
/// \exclude
namespace detail
{
template <cpp_entity_kind... K>
bool has_one_of_kind(const cpp_entity& e)
{
static_assert(sizeof...(K) > 0, "At least one entity kind must be specified");
bool result = false;
// poor men's fold
int dummy[]{(result |= (K == e.kind()), 0)...};
(void)dummy;
return result;
}
} // namespace detail
/// Generates a blacklist visitor filter.
///
/// \returns A visitor filter that excludes all entities having one of the specified kinds.
template <cpp_entity_kind... Kinds>
detail::visitor_filter_t blacklist()
{
return [](const cpp_entity& e) {
return detail::has_one_of_kind<Kinds...>(e) ? visit_filter::exclude : visit_filter::include;
};
}
/// Generates a blacklist visitor filter.
///
/// \returns A visitor filter that excludes all entities having one of the specified kinds and
/// children of those entities.
template <cpp_entity_kind... Kinds>
detail::visitor_filter_t blacklist_and_children()
{
return [](const cpp_entity& e) {
return detail::has_one_of_kind<Kinds...>(e) ? visit_filter::exclude_and_children
: visit_filter::include;
};
}
/// Generates a whitelist visitor filter.
///
/// \returns A visitor filter that excludes all entities having not one of the specified kinds.
template <cpp_entity_kind... Kinds>
detail::visitor_filter_t whitelist()
{
return [](const cpp_entity& e) {
return detail::has_one_of_kind<Kinds...>(e) ? visit_filter::include : visit_filter::exclude;
};
}
} // namespace cppast
#endif // CPPAST_VISITOR_HPP_INCLUDED