From 6b658297d80d849afd11377d18eb6d70c3e8489b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Sun, 22 Jan 2017 12:26:28 +0100 Subject: [PATCH] Add visit operation --- include/cppast/visitor.hpp | 51 ++++++++++++++++++++++++++++++++++ src/visitor.cpp | 57 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 include/cppast/visitor.hpp create mode 100644 src/visitor.cpp diff --git a/include/cppast/visitor.hpp b/include/cppast/visitor.hpp new file mode 100644 index 0000000..1744392 --- /dev/null +++ b/include/cppast/visitor.hpp @@ -0,0 +1,51 @@ +// Copyright (C) 2017 Jonathan Müller +// 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 + +namespace cppast +{ + class cpp_entity; + + /// Information about the state of a visit operation. + enum class visitor_info + { + leave_entity, //< Callback called for a leave entity without children. + + container_entity_enter, //< Callback called for a container entity before the children. + container_entity_exit, //< Callback called for a container entity after the children. + }; + + /// \exclude + namespace detail + { + using visitor_callback_t = bool (*)(void* mem, const cpp_entity&, visitor_info info); + + template + bool visitor_callback(void* mem, const cpp_entity& e, visitor_info info) + { + auto& func = *static_cast(mem); + return func(e, info); + } + + bool visit(const cpp_entity& e, visitor_callback_t cb, void* functor); + } // namespace detail + + /// Visits a [cppast::cpp_entity](). + /// \effects If the given entity is a container, i.e. if it has child entities, + /// calls `f(e, visitor_info::container_entity_enter)`. + /// If that returns `true`, recursively calls `visit()` for all child entities, + /// followed by a call to `f(e, visitor_info::container_entity_exit)`. + /// If the given entity is not a container, calls `f(e, visitor_info::leave_entity)`. + /// If the functor returns `false` for [cppast::visitor_info]() other than `container_entity_enter`, + /// the visit operation is aborted. + template + void visit(const cpp_entity& e, Func f) + { + detail::visit(e, &detail::visitor_callback, &f); + } +} // namespace cppast + +#endif // CPPAST_VISITOR_HPP_INCLUDED diff --git a/src/visitor.cpp b/src/visitor.cpp new file mode 100644 index 0000000..9590128 --- /dev/null +++ b/src/visitor.cpp @@ -0,0 +1,57 @@ +// Copyright (C) 2017 Jonathan Müller +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#include + +#include +#include +#include +#include +#include + +using namespace cppast; + +namespace +{ + template + bool handle_container(const cpp_entity& e, detail::visitor_callback_t cb, void* functor) + { + auto& container = static_cast(e); + + auto handle_children = cb(functor, container, visitor_info::container_entity_enter); + if (handle_children) + { + for (auto& child : container) + if (!detail::visit(child, cb, functor)) + return false; + } + + return cb(functor, container, visitor_info::container_entity_exit); + } +} + +bool detail::visit(const cpp_entity& e, detail::visitor_callback_t cb, void* functor) +{ + switch (e.type()) + { + case cpp_entity_type::file_t: + return handle_container(e, cb, functor); + case cpp_entity_type::namespace_t: + return handle_container(e, cb, functor); + case cpp_entity_type::enum_t: + return handle_container(e, cb, functor); + + case cpp_entity_type::namespace_alias_t: + case cpp_entity_type::using_directive_t: + case cpp_entity_type::using_declaration_t: + case cpp_entity_type::enum_value_t: + return cb(functor, e, visitor_info::leave_entity); + + case cpp_entity_type::count: + break; + } + + DEBUG_UNREACHABLE(detail::assert_handler{}); + return true; +}