Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

X3: Support recursive rules that modify the context #237

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
  • Loading branch information
Tobias Germer committed Aug 1, 2017
commit 673c67949981d40115ab4ddda7d54b614994cc30
81 changes: 49 additions & 32 deletions include/boost/spirit/home/x3/support/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,57 +16,74 @@ namespace boost { namespace spirit { namespace x3
template <typename ID, typename T, typename Next = unused_type>
struct context
{
context(T& val, Next const& next)
: val(val), next(next) {}
T& val;
Next next;
};

T& get(mpl::identity<ID>) const
template <typename ID, typename T, typename Next>
struct context<ID, T, Next const&>
{
T& val;
Next const& next;
};

namespace detail {
template <typename ID, typename T, typename Next>
T& get(context<ID, T, Next> const& ctx, mpl::identity<ID>)
{
return val;
return ctx.val;
}

template <typename ID_>
decltype(auto) get(ID_ id) const
template <typename ID>
unused_type get(unused_type, mpl::identity<ID>)
{
return next.get(id);
return {};
}

T& val;
Next const& next;
};
template <typename ID, typename T, typename Next, typename DifferentID>
decltype(auto) get(context<ID, T, Next> const& ctx, mpl::identity<DifferentID> id)
{
return get(ctx.next, id);
}
}

template <typename ID, typename T>
struct context<ID, T, unused_type>
template <typename Tag, typename Context>
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<Tag>());
}

T& get(mpl::identity<ID>) const
namespace detail {
template <typename ID, typename T, typename Next>
auto const& remove(context<ID, T, Next> const& ctx, mpl::identity<ID>)
{
return val;
return ctx.next;
}

template <typename ID_>
unused_type get(ID_) const
template <typename ID, typename T, typename Next, typename DifferentID>
auto remove(context<ID, T, Next> const& ctx, mpl::identity<DifferentID> id)
{
return {};
return context<ID, T, decltype(remove(ctx.next, id))>{ ctx.val, remove(ctx.next, id) };
}

T& val;
};

template <typename Tag, typename Context>
inline decltype(auto) get(Context const& context)
{
return context.get(mpl::identity<Tag>());
template <typename ID, typename FoundVal, typename T, typename Next, std::enable_if_t<!std::is_same<FoundVal, unused_type>::value>* =nullptr>
inline auto make_context(T& val, Next const& next) -> context<ID, T, decltype(remove(next, mpl::identity<ID>()))>
{
return { val, remove(next, mpl::identity<ID>()) };
}

// optimization: don't rebuild the context when ID could not be found
template <typename ID, typename FoundVal, typename T, typename Next, std::enable_if_t<std::is_same<FoundVal, unused_type>::value>* =nullptr>
inline context<ID, T, Next const&> make_context(T& val, Next const& next)
{
return { val, next };
}
}

template <typename ID, typename T, typename Next>
inline context<ID, T, Next> make_context(T& val, Next const& next)
inline decltype(auto) make_context(T& val, Next const& next)
{
return { val, next };
return detail::make_context<ID, decltype(x3::get<ID>(next))>(val, next);
}

template <typename ID, typename T>
Expand All @@ -85,7 +102,7 @@ namespace boost { namespace spirit { namespace x3
}

template <typename ID, typename T, typename Next>
inline context<ID, T, Next>
inline context<ID, T, Next const&>
make_unique_context(T& val, Next const& next, unused_type)
{
return { val, next };
Expand Down
21 changes: 5 additions & 16 deletions include/boost/spirit/home/x3/support/subcontext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,6 @@ namespace boost { namespace spirit { namespace x3
template <typename Context>
subcontext(Context const& /*context*/)
{}

template <typename ID_>
unused_type
get(ID_) const
{
return unused;
}
};

template <typename T>
Expand All @@ -43,35 +36,31 @@ namespace boost { namespace spirit { namespace x3

template <typename Context>
subcontext(Context const& context)
: context_type(x3::get<typename T::first_type>(context))
: context_type{x3::get<typename T::first_type>(context), unused}
{}

using context_type::get;
};

template <typename T, typename... Tail>
struct subcontext<T, Tail...>
: subcontext<Tail...>
, context<
typename T::first_type, typename T::second_type
, subcontext<Tail...>
, subcontext<Tail...> const&
>
{
typedef subcontext<Tail...> base_type;
typedef context<
typename T::first_type, typename T::second_type
, base_type
, base_type const&
> context_type;

template <typename Context>
subcontext(Context const& context)
: base_type(context)
, context_type(
, context_type{
x3::get<typename T::first_type>(context)
, *static_cast<base_type*>(this))
, *static_cast<base_type*>(this)}
{}

using context_type::get;
};

}}}
Expand Down
8 changes: 0 additions & 8 deletions include/boost/spirit/home/x3/support/unused.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename ID>
unused_type get(ID) const
{
return {};
}
};

auto const unused = unused_type{};
Expand Down