Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
f748c82
Fix incorrectly swapped template arguments
yaito3014 Feb 24, 2026
364dd0c
Fix more swapped argument
yaito3014 Feb 24, 2026
05a03e5
Restore `is_substitute` specialization for variant
yaito3014 Feb 24, 2026
f680cf2
Move `is_substitution` test to separate file
yaito3014 Feb 24, 2026
f2b5afb
Add `variant_has_substitute` test
yaito3014 Feb 24, 2026
7b6636f
Minor renaming
yaito3014 Feb 25, 2026
da4c936
Rename `is_substitute` to `can_hold`
yaito3014 Feb 25, 2026
2adb1f4
Remove test
yaito3014 Feb 25, 2026
d2dca4b
Disable test
yaito3014 Feb 25, 2026
bde2641
Remove variant_has_substitute
yaito3014 Feb 25, 2026
347796b
Replace specific usage of `can_hold` with `is_same`
yaito3014 Feb 25, 2026
1c28be8
Stricten `can_hold` for non-variant types
yaito3014 Feb 25, 2026
1959100
Merge branch 'main' into fix-swapped-template-argument
yaito3014 Feb 25, 2026
b53bc4b
Add comments
yaito3014 Feb 25, 2026
4c60498
Remove can_hold_impl
yaito3014 Feb 25, 2026
e37512f
Suppress warning
yaito3014 Feb 25, 2026
34bad4a
Add TODO comment
yaito3014 Feb 25, 2026
a192159
Loosen `can_hold` more
yaito3014 Feb 27, 2026
81af262
Revert "Loosen `can_hold` more"
yaito3014 Feb 27, 2026
0dded48
Fix incorrect test
yaito3014 Feb 28, 2026
ad13cf4
Fix `x_attr`
yaito3014 Feb 28, 2026
515aec3
Fix `container_value`
yaito3014 Feb 28, 2026
fe56942
Rewrite parse_into_container with constexpr if
yaito3014 Feb 28, 2026
3d9ed7d
Refine `custom_container`
yaito3014 Feb 28, 2026
087d53a
TEST
yaito3014 Feb 28, 2026
50038f6
Refactor container handling
yaito3014 Feb 28, 2026
47e8f66
Fix handling of container of container
yaito3014 Feb 28, 2026
0df97a3
Add test and comment
yaito3014 Feb 28, 2026
859dc94
Replace some rule parsers with as_parser
yaito3014 Mar 2, 2026
542ff6f
Make `as_directive` derive subject's `handles_container`
yaito3014 Mar 2, 2026
9d9ed37
Remove comment
yaito3014 Mar 2, 2026
e6ae585
Create both `rule` and `as` versions of tests
yaito3014 Mar 2, 2026
0fc320c
Revert one of changes made in 859dc949b94d60fbd964c9442b20d08ce41c61a4
yaito3014 Mar 2, 2026
ba6f835
Create both `rule` and `as` versions of tests 2
yaito3014 Mar 2, 2026
385be3f
Fix styling
saki7 Mar 2, 2026
e553854
Temporarily disable "rule version" of tests
saki7 Mar 2, 2026
beb8d24
Fix test
yaito3014 Mar 2, 2026
0281e1b
Fix stupid mistake
yaito3014 Mar 2, 2026
83431f3
Add special handling for optional
yaito3014 Mar 2, 2026
bceaf58
Move `can_hold`'s primary definition to top
yaito3014 Mar 2, 2026
7ac31fe
Use `handles_container`
yaito3014 Mar 2, 2026
c8f93a6
Disable recursive_tuple test
yaito3014 Mar 2, 2026
85b55ab
Rename `handles_container` to `maybe_handles_container`
yaito3014 Mar 2, 2026
0fd23e6
Add more strict `handles_container` and use it for container handling
yaito3014 Mar 4, 2026
16a3c64
Rename `build_container` to `default_container`
yaito3014 Mar 4, 2026
9c5fb77
Remove debug purpose test
yaito3014 Mar 4, 2026
78c8899
Remove `raw` directive
yaito3014 Mar 4, 2026
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
4 changes: 2 additions & 2 deletions include/iris/x4/auxiliary/attr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct attr_parser : parser<attr_parser<T, HeldValueT>>
using attribute_type = T;
using held_value_type = HeldValueT;

static constexpr bool handles_container = traits::is_container_v<T>;
static constexpr bool maybe_handles_container = traits::is_container_v<T>;

template<class U>
requires
Expand Down Expand Up @@ -77,7 +77,7 @@ struct attr_parser<T, void> : parser<attr_parser<T, void>>

using attribute_type = T;

static constexpr bool handles_container = traits::is_container_v<T>;
static constexpr bool maybe_handles_container = traits::is_container_v<T>;

template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4UnusedAttribute UnusedAttr>
[[nodiscard]] static constexpr bool
Expand Down
13 changes: 0 additions & 13 deletions include/iris/x4/core/action.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ namespace iris::x4 {

namespace detail {

struct raw_attribute_t;

template<class Context, X4Attribute Attr>
struct action_context;

Expand Down Expand Up @@ -207,17 +205,6 @@ struct action : proxy_parser<Subject, action<Subject, ActionF>>
first = saved_first;
return false;
}

// attr==raw, action wants iterator_range (see raw.hpp)
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context>
[[nodiscard]] constexpr bool
parse_main(It& first, Se const& last, Context const& ctx, detail::raw_attribute_t&) const
noexcept(false) // construction of `subrange` is never noexcept as per the standard
{
// synthesize the attribute since one is not supplied
std::ranges::subrange<It, It> rng; // This must be It-It pair, NOT It-Se pair
return this->parse_main(first, last, ctx, rng);
}
};

template<X4Subject Subject, class Action>
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 @@ -67,7 +67,7 @@ template<class Parser, X4Attribute Attr>
struct pass_parser_attribute
{
using attribute_type = parser_traits<Parser>::attribute_type;
using substitute_type = traits::variant_find_substitute_t<Attr, attribute_type>;
using substitute_type = traits::variant_find_holdable_type_t<Attr, attribute_type>;

using type = std::conditional_t<
std::same_as<Attr, substitute_type>,
Expand Down
244 changes: 63 additions & 181 deletions include/iris/x4/core/detail/parse_into_container.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,214 +16,96 @@
#include <iris/x4/core/container_appender.hpp>

#include <iris/x4/traits/container_traits.hpp>
#include <iris/x4/traits/substitution.hpp>
#include <iris/x4/traits/tuple_traits.hpp>
#include <iris/x4/traits/can_hold.hpp>

#include <iris/alloy/tuple.hpp>

#include <iterator>
#include <type_traits>
#include <utility>

namespace iris::x4::detail {
namespace iris::x4 {

template<class Parser, class Container>
struct parser_accepts_container
: traits::is_substitute<typename parser_traits<Parser>::attribute_type, Container>
{};
template<class Subject>
struct optional;

template<class Parser, class Container>
constexpr bool parser_accepts_container_v = parser_accepts_container<Parser, Container>::value;
} // iris::x4

template<class Parser>
struct parse_into_container_base_impl
{
// Parser has attribute (synthesize; Attribute is a container)
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4Attribute Attr>
requires (!parser_accepts_container_v<Parser, unwrap_recursive_type<Attr>>)
[[nodiscard]] static constexpr bool
call_synthesize(
Parser const& parser, It& first, Se const& last,
Context const& ctx, Attr& attr
) // never noexcept (requires container insertion)
{
static_assert(!std::same_as<std::remove_const_t<Attr>, unused_container_type>);

using value_type = traits::container_value_t<unwrap_recursive_type<Attr>>;
value_type val; // default-initialize

//static_assert(Parsable<Parser, It, Se, Context, value_type>);
if (!parser.parse(first, last, ctx, val)) return false;

// push the parsed value into our attribute
traits::push_back(unwrap_recursive(attr), std::move(val));
return true;
}

// unused_container_type
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context>
requires (!parser_accepts_container_v<Parser, unused_container_type>)
[[nodiscard]] static constexpr bool
call_synthesize(
Parser const& parser, It& first, Se const& last,
Context const& ctx, unused_container_type const&
) noexcept(is_nothrow_parsable_v<Parser, It, Se, Context, unused_type>)
{
//static_assert(Parsable<Parser, It, Se, Context, unused_type>);
return parser.parse(first, last, ctx, unused);
}

// Parser has attribute (synthesize; Attribute is a container)
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4Attribute Attr>
requires parser_accepts_container_v<Parser, unwrap_recursive_type<Attr>>
[[nodiscard]] static constexpr bool
call_synthesize(
Parser const& parser, It& first, Se const& last,
Context const& ctx, Attr& attr
) noexcept(is_nothrow_parsable_v<Parser, It, Se, Context, unwrap_recursive_type<Attr>>)
{
//static_assert(Parsable<Parser, It, Se, Context, unwrap_recursive_type<Attr>>);
return parser.parse(first, last, ctx, attr);
}

// ------------------------------------------------------

// Parser has attribute && it is NOT tuple-like
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4Attribute Attr>
requires
has_attribute_v<Parser> &&
(!alloy::is_tuple_like_v<Attr>)
[[nodiscard]] static constexpr bool
call(
Parser const& parser, It& first, Se const& last,
Context const& ctx, Attr& attr
)
{
// TODO: reduce call stack while keeping maintainability
return parse_into_container_base_impl::call_synthesize(parser, first, last, ctx, attr);
}
namespace iris::x4::detail {

// Parser has attribute && it is tuple-like
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4Attribute Attr>
requires
has_attribute_v<Parser> &&
alloy::is_tuple_like_v<Attr>
[[nodiscard]] static constexpr bool
call(
Parser const& parser, It& first, Se const& last,
Context const& ctx, Attr& attr
) noexcept(noexcept(parse_into_container_base_impl::call_synthesize(parser, first, last, ctx, alloy::get<0>(attr))))
{
static_assert(traits::has_size_v<Attr, 1>, "Expecting a single element tuple-like");
// TODO: reduce call stack while keeping maintainability
return parse_into_container_base_impl::call_synthesize(parser, first, last, ctx, alloy::get<0>(attr));
}
template<class Parser, class Container>
struct parser_accepts_container {};

// Parser has no attribute (pass unused)
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4Attribute Attr>
requires (!has_attribute_v<Parser>)
[[nodiscard]] static constexpr bool
call(
Parser const& parser, It& first, Se const& last,
Context const& ctx, Attr& /* attr */
) noexcept(is_nothrow_parsable_v<Parser, It, Se, Context, unused_container_type>)
{
// static_assert(Parsable<Parser, It, Se, Context, unused_container_type>);
return parser.parse(first, last, ctx, unused_container);
}
template<class Parser, traits::X4Container Container>
struct parser_accepts_container<Parser, Container>
{
static constexpr bool value = parser_traits<Parser>::template handles_container<Container>;
};

template<class Parser>
struct parse_into_container_impl : parse_into_container_base_impl<Parser> {};

template<class Parser, class Container>
inline constexpr bool parser_accepts_container_v = parser_accepts_container<Parser, Container>::value;

template<class Parser>
requires Parser::handles_container
struct parse_into_container_impl<Parser>
struct parse_into_container_impl_default
{
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4Attribute Attr>
static constexpr bool pass_attibute_as_is = std::disjunction_v<
parser_accepts_container<Parser, Attr>,

std::negation<traits::is_substitute< // parser attribute is substitute for container value?
typename traits::pseudo_attribute<
It, Se, Context,
typename parser_traits<Parser>::attribute_type
>::actual_type,
traits::container_value_t<Attr>
>>
>;

template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4Attribute Attr>
requires (!pass_attibute_as_is<It, Se, Context, Attr>)
[[nodiscard]] static constexpr bool
call(
Parser const& parser, It& first, Se const& last,
Context const& ctx, Attr& attr
) noexcept(noexcept(parse_into_container_base_impl<Parser>::call(
parser, first, last, ctx, attr
)))
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4NonUnusedAttribute Attr>
static constexpr bool call(Parser const& parser, It& first, Se const& last, Context& ctx, Attr& attr) // TODO: add noexcept
{
return parse_into_container_base_impl<Parser>::call(
parser, first, last, ctx, attr
);
}

template<std::forward_iterator It, std::sentinel_for<It> Se, class Context>
requires pass_attibute_as_is<It, Se, Context, unused_container_type>
[[nodiscard]] static constexpr bool
call(
Parser const& parser, It& first, Se const& last,
Context const& ctx, unused_container_type
) noexcept(is_nothrow_parsable_v<Parser, It, Se, Context, unused_container_type>)
{
static_assert(Parsable<Parser, It, Se, Context, unused_container_type>);
return parser.parse(first, last, ctx, unused_container);
}

template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4Attribute Attr>
requires pass_attibute_as_is<It, Se, Context, Attr>
[[nodiscard]] static constexpr bool
call(
Parser const& parser, It& first, Se const& last,
Context const& ctx, Attr& attr
) // never noexcept (requires container insertion)
{
static_assert(!std::same_as<std::remove_const_t<Attr>, unused_type>);
static_assert(!std::same_as<std::remove_const_t<Attr>, unused_container_type>);
static_assert(Parsable<Parser, It, Se, Context, Attr>);

auto&& appender = x4::make_container_appender(attr);
return parser.parse(first, last, ctx, appender);
using unwrapped_attribute_type = iris::unwrap_recursive_type<Attr>;
auto& unwrapped_attr = iris::unwrap_recursive(attr);

if constexpr (traits::is_container_v<unwrapped_attribute_type>) { // Attr is a container
if constexpr (parser_accepts_container_v<Parser, unwrapped_attribute_type>) { // parser accepts the container; make parser append directly
auto&& appender = x4::make_container_appender(unwrapped_attr);
return parser.parse(first, last, ctx, appender);
} else { // parser DOES NOT accept the container; parse into value type and append it
using value_type = traits::container_value_t<unwrapped_attribute_type>;
value_type value{}; // value-initialize
if (!parser.parse(first, last, ctx, value)) return false;
traits::push_back(unwrapped_attr, std::move(value));
return true;
}
} else {
if constexpr (traits::is_size_one_sequence_v<unwrapped_attribute_type>) { // attribute is single element tuple-like; unwrap and try again
return parse_into_container_impl_default<Parser>::call(parser, first, last, ctx, alloy::get<0>(unwrapped_attr));
} else {
static_assert(false, "parse_into_container accepts a container, a variant of container or a single element tuple-like of container");
return false;
}
}
}
};

template<
class Parser, std::forward_iterator It, std::sentinel_for<It> Se,
class Context, X4Attribute Attr
>
// internal customization point
template<class Parser>
struct parse_into_container_impl
: parse_into_container_impl_default<Parser>
{};

template<class Parser, std::forward_iterator It, std::sentinel_for<It> Se, class Context, X4Attribute Attr>
[[nodiscard]] constexpr bool
parse_into_container(
Parser const& parser, It& first, Se const& last,
Context const& ctx, Attr& attr
) noexcept(noexcept(parse_into_container_impl<Parser>::call(parser, first, last, ctx, attr)))
) // TODO: add noexcept
{
static_assert(
!std::same_as<Attr, unused_type>,
"`unused_type` should not be passed to `parse_into_container`. Use `x4::assume_container(attr)`"
);

if constexpr (traits::is_variant_v<Attr>) {
// e.g. `char` when the caller is `+char_`
using attribute_type = parser_traits<Parser>::attribute_type;

// e.g. `std::string` when the attribute_type is `char`
using substitute_type = traits::variant_find_substitute_t<Attr, traits::build_container_t<attribute_type>>;

// instead of creating a temporary `substitute_type`, append directly into the emplaced alternative
auto& variant_alt = attr.template emplace<substitute_type>();
return parse_into_container_impl<Parser>::call(parser, first, last, ctx, variant_alt);
if constexpr (X4UnusedAttribute<Attr> || !has_attribute_v<Parser>) { // handle unused types first
return parser.parse(first, last, ctx, unused);
} else {
return parse_into_container_impl<Parser>::call(parser, first, last, ctx, attr);
if constexpr (traits::is_variant_v<Attr>) {
// e.g. `char` when the caller is `+char_`
using attribute_type = parser_traits<Parser>::attribute_type;

// e.g. `std::string` when the attribute_type is `char`
using substitute_type = traits::variant_find_holdable_type_t<Attr, traits::default_container_t<attribute_type>>;

// instead of creating a temporary `substitute_type`, append directly into the emplaced alternative
auto& variant_alt = attr.template emplace<substitute_type>();
return parse_into_container_impl<Parser>::call(parser, first, last, ctx, variant_alt);
} else {
return parse_into_container_impl<Parser>::call(parser, first, last, ctx, attr);
}
}
}

Expand Down
Loading
Loading