Skip to content
Merged
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
2 changes: 1 addition & 1 deletion include/iris/x4/char/case_compare.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ template<class Encoding, class Context>
[[nodiscard]] constexpr auto
get_case_compare(Context const&) noexcept
{
if constexpr (has_context_of_v<Context, detail::case_compare_tag, detail::case_compare_no_case_t>) {
if constexpr (has_context_of<Context, detail::case_compare_tag, detail::case_compare_no_case_t>) {
return no_case_compare<Encoding>{};
} else {
return case_compare<Encoding>{};
Expand Down
7 changes: 5 additions & 2 deletions include/iris/x4/char/char_class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#include <concepts>
#include <iterator>
#include <utility>

#include <cassert>

Expand Down Expand Up @@ -216,14 +217,16 @@ constexpr void builtin_skip_over(It& first, Se const& last) noexcept
// Forward declaration in "skip_over.hpp"
// Need to sync this implementation with `skip_gen` ("skip.hpp")
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context>
requires std::same_as<get_context_plain_t<contexts::skipper, Context>, builtin_skipper_kind>
requires has_context_of<Context, contexts::skipper, builtin_skipper_kind>
constexpr void skip_over(It& first, Se const& last, Context const& ctx) noexcept
{
switch (x4::get<contexts::skipper>(ctx)) {
case builtin_skipper_kind::no_skip: return;
case builtin_skipper_kind::blank: return detail::builtin_skip_over<char_classes::blank_tag>(first, last);
case builtin_skipper_kind::space: return detail::builtin_skip_over<char_classes::space_tag>(first, last);
default: assert(false && "unsupported builtin skipper type"); break;
default:
assert(false && "unsupported builtin skipper type");
std::unreachable();
}
}

Expand Down
6 changes: 3 additions & 3 deletions include/iris/x4/core/action.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ struct action : proxy_parser<action<Subject, ActionF>, Subject>
first = std::move(local_it);
return true;
}
if constexpr (has_context_v<Context, contexts::expectation_failure>) {
if constexpr (has_context<Context, contexts::expectation_failure>) {
if (x4::has_expectation_failure(ctx)) {
// don't rollback iterator (mimicking exception-like behavior)
first = std::move(local_it);
Expand Down Expand Up @@ -228,7 +228,7 @@ struct action : proxy_parser<action<Subject, ActionF>, Subject>
first = std::move(local_it);
return true;
}
if constexpr (has_context_v<Context, contexts::expectation_failure>) {
if constexpr (has_context<Context, contexts::expectation_failure>) {
if (x4::has_expectation_failure(ctx)) {
// don't rollback iterator (mimicking exception-like behavior)
first = std::move(local_it);
Expand All @@ -249,7 +249,7 @@ struct action : proxy_parser<action<Subject, ActionF>, Subject>
first = std::move(local_it);
return true;
}
if constexpr (has_context_v<Context, contexts::expectation_failure>) {
if constexpr (has_context<Context, contexts::expectation_failure>) {
if (x4::has_expectation_failure(ctx)) {
// don't rollback iterator (mimicking exception-like behavior)
first = std::move(local_it);
Expand Down
48 changes: 21 additions & 27 deletions include/iris/x4/core/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,44 @@ template<class ID, class T, class Next>
struct context;


template<class Context, class ID_To_Search>
struct has_context;
namespace detail {

template<class Context, class ID_To_Search>
constexpr bool has_context_v = has_context<Context, ID_To_Search>::value;
struct has_context_impl;

template<class ID_To_Search>
struct has_context<unused_type, ID_To_Search>
struct has_context_impl<unused_type, ID_To_Search>
: std::false_type
{};

template<class T, class Next, class ID_To_Search>
struct has_context<context<ID_To_Search, T, Next>, ID_To_Search>
struct has_context_impl<context<ID_To_Search, T, Next>, ID_To_Search>
: std::true_type
{};
{
using context_value_type = T;
};

template<class ID, class T, class Next, class ID_To_Search>
requires (!std::same_as<ID, ID_To_Search>)
struct has_context<context<ID, T, Next>, ID_To_Search>
: has_context<std::remove_cvref_t<Next>, ID_To_Search>
struct has_context_impl<context<ID, T, Next>, ID_To_Search>
: has_context_impl<std::remove_cvref_t<Next>, ID_To_Search>
{};

} // detail

template<class Context, class ID_To_Search>
concept has_context = detail::has_context_impl<Context, ID_To_Search>::value;

template<class Context, class ID_To_Search, class T>
concept has_context_of =
has_context<Context, ID_To_Search> &&
std::same_as<std::remove_const_t<typename detail::has_context_impl<Context, ID_To_Search>::context_value_type>, T>;

template<class ID_To_Get, class ID, class T, class Next>
[[nodiscard]] constexpr decltype(auto)
get(context<ID, T, Next> const& ctx) noexcept
{
if constexpr (has_context_v<context<ID, T, Next>, ID_To_Get>) {
if constexpr (has_context<context<ID, T, Next>, ID_To_Get>) {
return ctx.get(std::type_identity<ID_To_Get>{});
} else {
return (unused); // return lvalue
Expand All @@ -74,22 +84,6 @@ void get(context<ID, T, Next> const&&) = delete; // dangling
template<class ID, class Context>
using get_context_plain_t = std::remove_cvref_t<decltype(x4::get<ID>(std::declval<Context const&>()))>;


template<class Context, class ID, class T>
struct has_context_of : std::false_type {};

template<class Context, class ID, class T>
requires has_context_v<Context, ID>
struct has_context_of<Context, ID, T>
{
static_assert(!std::is_reference_v<T>);
static_assert(!std::is_const_v<T>);
static constexpr bool value = std::same_as<get_context_plain_t<ID, Context>, T>;
};

template<class Context, class ID, class T>
constexpr bool has_context_of_v = has_context_of<Context, ID, T>::value;

namespace detail {

template<class ID>
Expand All @@ -99,7 +93,7 @@ template<class ID>
concept AllowUnusedContextID = requires { requires ID::allow_unused; };

template<class ID, class Next>
concept HasNoDuplicateContext = !UniqueContextID<ID> || !has_context_v<Next, ID>;
concept HasNoDuplicateContext = !UniqueContextID<ID> || !has_context<Next, ID>;

template<class T>
concept ContextValueType =
Expand Down Expand Up @@ -513,7 +507,7 @@ replace_first_context_impl(
(void)new_val; // == unused
return x4::remove_first_context<ID_To_Replace>(ctx);

} else if constexpr (!IsAppend && !has_context_v<context<ID, T, Next>, ID_To_Replace>) {
} else if constexpr (!IsAppend && !has_context<context<ID, T, Next>, ID_To_Replace>) {
return context<ID_To_Replace, NewVal, context<ID, T, Next> const&>{new_val, ctx};

} else if constexpr (std::same_as<ID, ID_To_Replace>) { // Match
Expand Down
2 changes: 1 addition & 1 deletion include/iris/x4/core/detail/parse_alternative.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ namespace iris::x4::detail {
template<class Context>
[[nodiscard]] constexpr bool alternative_should_stop(Context const& ctx) noexcept
{
if constexpr (has_context_v<Context, contexts::expectation_failure>) {
if constexpr (has_context<Context, contexts::expectation_failure>) {
return x4::has_expectation_failure(ctx);
} else {
return false;
Expand Down
10 changes: 5 additions & 5 deletions include/iris/x4/core/expectation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ template<class Context>
constexpr bool has_expectation_failure(Context const& ctx) noexcept
{
static_assert(
has_context_v<Context, contexts::expectation_failure>,
has_context<Context, contexts::expectation_failure>,
"Context type was not specified for `x4::contexts::expectation_failure`. "
"You probably forgot: `x4::with<x4::contexts::expectation_failure>(failure)[p]`. "
"Note that you must also bind the context to your skipper."
Expand All @@ -55,7 +55,7 @@ constexpr void set_expectation_failure(
)
{
static_assert(
has_context_v<Context, contexts::expectation_failure>,
has_context<Context, contexts::expectation_failure>,
"Context type was not specified for `x4::contexts::expectation_failure`. "
"You probably forgot: `x4::with<x4::contexts::expectation_failure>(failure)[p]`. "
"Note that you must also bind the context to your skipper."
Expand All @@ -70,7 +70,7 @@ constexpr void set_expectation_failure_if_empty(
)
{
static_assert(
has_context_v<Context, contexts::expectation_failure>,
has_context<Context, contexts::expectation_failure>,
"Context type was not specified for `x4::contexts::expectation_failure`. "
"You probably forgot: `x4::with<x4::contexts::expectation_failure>(failure)[p]`. "
"Note that you must also bind the context to your skipper."
Expand All @@ -84,7 +84,7 @@ template<class Context>
constexpr decltype(auto) get_expectation_failure(Context const& ctx) noexcept
{
static_assert(
has_context_v<Context, contexts::expectation_failure>,
has_context<Context, contexts::expectation_failure>,
"Context type was not specified for `x4::contexts::expectation_failure`. "
"You probably forgot: `x4::with<x4::contexts::expectation_failure>(failure)[p]`. "
"Note that you must also bind the context to your skipper."
Expand All @@ -97,7 +97,7 @@ template<class Context>
constexpr void clear_expectation_failure(Context const& ctx) noexcept
{
static_assert(
has_context_v<Context, contexts::expectation_failure>,
has_context<Context, contexts::expectation_failure>,
"Context type was not specified for `x4::contexts::expectation_failure`. "
"You probably forgot: `x4::with<x4::contexts::expectation_failure>(failure)[p]`. "
"Note that you must also bind the context to your skipper."
Expand Down
15 changes: 9 additions & 6 deletions include/iris/x4/core/skip_over.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ struct builtin_skipper_traits<builtin_skipper_kind::space>
} // detail

template<std::forward_iterator It, std::sentinel_for<It> Se, class Context>
requires X4Subject<get_context_plain_t<contexts::skipper, Context>>
requires
has_context<Context, contexts::skipper> &&
(!has_context_of<Context, contexts::skipper, builtin_skipper_kind>) &&
X4Subject<get_context_plain_t<contexts::skipper, Context>>
constexpr void skip_over(It& first, Se const& last, Context const& ctx)
{
auto const& skipper = x4::get<contexts::skipper>(ctx);
Expand All @@ -68,10 +71,10 @@ constexpr void skip_over(It& first, Se const& last, Context const& ctx)

// Declare a concrete alias type; MSVC prints the alias instead of actual type,
// which makes the compilation error significantly shorter.
using SkipperOnlyContext = std::remove_cvref_t<decltype(x4::remove_first_context<contexts::skipper>(ctx))>;
auto const& local_ctx = detail::named_context<SkipperOnlyContext>(x4::remove_first_context<contexts::skipper>(ctx));
using NoSkipperContext = std::remove_cvref_t<decltype(x4::remove_first_context<contexts::skipper>(ctx))>;
auto const& local_ctx = detail::named_context<NoSkipperContext>(x4::remove_first_context<contexts::skipper>(ctx));

if constexpr (x4::has_context_v<Context, contexts::error_handler>) {
if constexpr (has_context<Context, contexts::error_handler>) {
auto& error_handler = x4::get<contexts::error_handler>(ctx);
error_handler.on_skip(skipper_state::pre_skip);

Expand All @@ -86,12 +89,12 @@ constexpr void skip_over(It& first, Se const& last, Context const& ctx)

// Implemented in `char_class.hpp`
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context>
requires std::same_as<get_context_plain_t<contexts::skipper, Context>, builtin_skipper_kind>
requires has_context_of<Context, contexts::skipper, builtin_skipper_kind>
constexpr void skip_over(It& first, Se const& last, Context const& ctx) noexcept;


template<std::forward_iterator It, std::sentinel_for<It> Se, class Context>
requires (!has_context_v<Context, contexts::skipper>)
requires (!has_context<Context, contexts::skipper>)
constexpr void skip_over(It&, Se const&, Context const&) noexcept
{
}
Expand Down
2 changes: 1 addition & 1 deletion include/iris/x4/debug/error_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ struct has_on_expectation_failure<T, It, Se, Context> : std::true_type
"`on_error` is obsolete due to its confusing name; use `on_expectation_failure` instead."
);
static_assert(
has_context_v<Context, contexts::expectation_failure>,
has_context<Context, contexts::expectation_failure>,
"[BUG] The `on_expectation_failure` callback on the user-provided error handler "
"is well-defined, but the actual `Context` passed to the `parse(...)` function "
"does not contain a reference bound to `x4::contexts::expectation_failure`. "
Expand Down
2 changes: 1 addition & 1 deletion include/iris/x4/directive/expect.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ struct parse_into_container_impl<expect_directive<Subject>>
)
{
static_assert(
has_context_v<Context, contexts::expectation_failure>,
has_context<Context, contexts::expectation_failure>,
"Context type was not specified for `x4::contexts::expectation_failure`. "
"You probably forgot: `x4::with<x4::contexts::expectation_failure>(failure)[p]`. "
"Note that you must also bind the context to your skipper."
Expand Down
46 changes: 32 additions & 14 deletions include/iris/x4/directive/lexeme.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,26 @@
#include <type_traits>
#include <utility>

#include <cassert>

namespace iris::x4 {

namespace detail {

template<class Context>
[[nodiscard]] constexpr decltype(auto) make_lexeme_context(Context const& ctx) noexcept
[[nodiscard]] constexpr decltype(auto) make_lexeme_context(Context const& ctx, builtin_skipper_kind& skipper_kind) noexcept
{
static_assert(!has_context_of<Context, contexts::skipper, builtin_skipper_kind>);
assert(skipper_kind == builtin_skipper_kind::no_skip);

// Declare a concrete alias type; MSVC prints the alias instead of actual type,
// which makes the compilation error significantly shorter.
using T = std::remove_cvref_t<decltype(x4::remove_first_context<contexts::skipper>(ctx))>;
return detail::named_context<T>(x4::remove_first_context<contexts::skipper>(ctx));

// Note: we must use "append" here since the position of the skipper should be
// super stable in the context as the exact type is often referenced by
// `IRIS_X4_INSTANTIATE`.
using T = std::remove_cvref_t<decltype(x4::replace_first_or_append_context<contexts::skipper>(ctx, skipper_kind))>;
return detail::named_context<T>(x4::replace_first_or_append_context<contexts::skipper>(ctx, skipper_kind));
}

} // detail
Expand All @@ -43,22 +52,31 @@ struct lexeme_directive : proxy_parser<lexeme_directive<Subject>, Subject>
[[nodiscard]] constexpr bool
parse(It& first, Se const& last, Context const& ctx, Attr& attr) const
{
auto it = first;
x4::skip_over(it, last, ctx); // pre-skip

bool const ok = this->subject.parse(
it, last,
detail::make_lexeme_context(ctx), // no skipper
attr
);
auto local_it = first;
x4::skip_over(local_it, last, ctx); // pre-skip

bool ok;
if constexpr (has_context_of<Context, contexts::skipper, builtin_skipper_kind>) {
builtin_skipper_kind& skipper_kind = x4::get<contexts::skipper>(ctx);
auto const old_skipper_kind = skipper_kind;
skipper_kind = builtin_skipper_kind::no_skip;
ok = this->subject.parse(local_it, last, ctx, attr);
skipper_kind = old_skipper_kind;

} else {
// This value could be reset by some nested parsers, so it can't be const
/* constexpr */ builtin_skipper_kind skipper_kind = builtin_skipper_kind::no_skip;
ok = this->subject.parse(local_it, last, detail::make_lexeme_context(ctx, skipper_kind), attr);
}

if (ok) {
first = std::move(it);
first = std::move(local_it);
return true;
}
if constexpr (has_context_v<Context, contexts::expectation_failure>) {
if constexpr (has_context<Context, contexts::expectation_failure>) {
if (x4::has_expectation_failure(ctx)) {
// don't rollback iterator (mimicking exception-like behavior)
first = std::move(it);
first = std::move(local_it);
}
}
return false;
Expand Down
2 changes: 1 addition & 1 deletion include/iris/x4/directive/matches.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ struct matches_directive : unary_parser<matches_directive<Subject>, Subject>
{
bool const matched = this->subject.parse(first, last, ctx, unused);

if constexpr (has_context_v<Context, contexts::expectation_failure>) {
if constexpr (has_context<Context, contexts::expectation_failure>) {
if (x4::has_expectation_failure(ctx)) return false;
}

Expand Down
Loading
Loading