From 673c67949981d40115ab4ddda7d54b614994cc30 Mon Sep 17 00:00:00 2001 From: Tobias Germer Date: Tue, 1 Aug 2017 13:36:48 +0200 Subject: [PATCH] Support recursive rules that modify the context make_context() always pushed a new node to the context list when the context is modified (e.g. for "with", "skip" or "no_skip" directives). If such directives are used in recursive rules, this leads to an infinite loop of template instantiations, effectively limiting the use of recursive rules in x3. To fix this, we first check if the tag for the new node is already contained in the context. If we find the tag, we remove the corresponding existing node from the context before pushing the new node with the requested tag. In order to remove a context entry, we have to rebuild all context nodes up to this entry. We can reuse (i.e. link to) the tail of the old context after this entry. In order to resolve life-time issues of newly created context nodes we added an aggregating implementation of struct context. --- .../boost/spirit/home/x3/support/context.hpp | 81 +++++++++++-------- .../spirit/home/x3/support/subcontext.hpp | 21 ++--- .../boost/spirit/home/x3/support/unused.hpp | 8 -- 3 files changed, 54 insertions(+), 56 deletions(-) diff --git a/include/boost/spirit/home/x3/support/context.hpp b/include/boost/spirit/home/x3/support/context.hpp index 9b24a3397c..f0490a239c 100644 --- a/include/boost/spirit/home/x3/support/context.hpp +++ b/include/boost/spirit/home/x3/support/context.hpp @@ -16,57 +16,74 @@ namespace boost { namespace spirit { namespace x3 template struct context { - context(T& val, Next const& next) - : val(val), next(next) {} + T& val; + Next next; + }; - T& get(mpl::identity) const + template + struct context + { + T& val; + Next const& next; + }; + + namespace detail { + template + T& get(context const& ctx, mpl::identity) { - return val; + return ctx.val; } - template - decltype(auto) get(ID_ id) const + template + unused_type get(unused_type, mpl::identity) { - return next.get(id); + return {}; } - T& val; - Next const& next; - }; + template + decltype(auto) get(context const& ctx, mpl::identity id) + { + return get(ctx.next, id); + } + } - template - struct context + template + inline decltype(auto) get(Context const& ctx) { - context(T& val) - : val(val) {} - - context(T& val, unused_type) - : val(val) {} + return detail::get(ctx, mpl::identity()); + } - T& get(mpl::identity) const + namespace detail { + template + auto const& remove(context const& ctx, mpl::identity) { - return val; + return ctx.next; } - template - unused_type get(ID_) const + template + auto remove(context const& ctx, mpl::identity id) { - return {}; + return context{ ctx.val, remove(ctx.next, id) }; } - T& val; - }; - - template - inline decltype(auto) get(Context const& context) - { - return context.get(mpl::identity()); + template ::value>* =nullptr> + inline auto make_context(T& val, Next const& next) -> context()))> + { + return { val, remove(next, mpl::identity()) }; + } + + // optimization: don't rebuild the context when ID could not be found + template ::value>* =nullptr> + inline context make_context(T& val, Next const& next) + { + return { val, next }; + } } template - inline context make_context(T& val, Next const& next) + inline decltype(auto) make_context(T& val, Next const& next) { - return { val, next }; + return detail::make_context(next))>(val, next); } template @@ -85,7 +102,7 @@ namespace boost { namespace spirit { namespace x3 } template - inline context + inline context make_unique_context(T& val, Next const& next, unused_type) { return { val, next }; diff --git a/include/boost/spirit/home/x3/support/subcontext.hpp b/include/boost/spirit/home/x3/support/subcontext.hpp index d4c60d084b..bfde77a20f 100644 --- a/include/boost/spirit/home/x3/support/subcontext.hpp +++ b/include/boost/spirit/home/x3/support/subcontext.hpp @@ -24,13 +24,6 @@ namespace boost { namespace spirit { namespace x3 template subcontext(Context const& /*context*/) {} - - template - unused_type - get(ID_) const - { - return unused; - } }; template @@ -43,10 +36,8 @@ namespace boost { namespace spirit { namespace x3 template subcontext(Context const& context) - : context_type(x3::get(context)) + : context_type{x3::get(context), unused} {} - - using context_type::get; }; template @@ -54,24 +45,22 @@ namespace boost { namespace spirit { namespace x3 : subcontext , context< typename T::first_type, typename T::second_type - , subcontext + , subcontext const& > { typedef subcontext base_type; typedef context< typename T::first_type, typename T::second_type - , base_type + , base_type const& > context_type; template subcontext(Context const& context) : base_type(context) - , context_type( + , context_type{ x3::get(context) - , *static_cast(this)) + , *static_cast(this)} {} - - using context_type::get; }; }}} diff --git a/include/boost/spirit/home/x3/support/unused.hpp b/include/boost/spirit/home/x3/support/unused.hpp index f7857e0dbd..bc788adc07 100644 --- a/include/boost/spirit/home/x3/support/unused.hpp +++ b/include/boost/spirit/home/x3/support/unused.hpp @@ -56,14 +56,6 @@ namespace boost { namespace spirit { namespace x3 { return *this; } - - // unused_type can also masquerade as an empty context (see context.hpp) - - template - unused_type get(ID) const - { - return {}; - } }; auto const unused = unused_type{};