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
8 changes: 4 additions & 4 deletions include/iris/x4/auxiliary/attr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@

#include <iris/x4/core/parser.hpp>
#include <iris/x4/core/move_to.hpp>
#include <iris/x4/core/char_traits.hpp>

#include <iris/x4/traits/container_traits.hpp>
#include <iris/x4/traits/string_traits.hpp>

#include <string>
#include <string_view>
Expand Down Expand Up @@ -104,15 +104,15 @@ struct attr_parser<T, void> : parser<attr_parser<T, void>>

namespace detail {

template<traits::CharArray R>
template<CharArray R>
using string_array_attr_parser_t = attr_parser<
std::basic_string<std::remove_extent_t<std::remove_cvref_t<R>>>,
std::basic_string_view<std::remove_extent_t<std::remove_cvref_t<R>>>
>;

} // detail

template<traits::CharArray R>
template<CharArray R>
attr_parser(R const&) -> attr_parser<
std::basic_string<std::remove_extent_t<std::remove_cvref_t<R>>>,
std::basic_string_view<std::remove_extent_t<std::remove_cvref_t<R>>>
Expand Down Expand Up @@ -141,7 +141,7 @@ struct attr_gen
return attr_parser<std::remove_cvref_t<T>>{std::forward<T>(value)};
}

template<traits::CharArray R>
template<CharArray R>
[[nodiscard]] static constexpr string_array_attr_parser_t<R>
operator()(R&& value)
noexcept(std::is_nothrow_constructible_v<string_array_attr_parser_t<R>, R>)
Expand Down
8 changes: 4 additions & 4 deletions include/iris/x4/char/any_char.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/

#include <iris/x4/core/char_traits.hpp>
#include <iris/x4/char/literal_char.hpp>
#include <iris/x4/char/char_set.hpp>
#include <iris/x4/traits/string_traits.hpp>

namespace iris::x4 {

Expand Down Expand Up @@ -43,7 +43,7 @@ struct any_char : char_parser<Encoding, any_char<Encoding>>
return {ch};
}

template<traits::CharIncompatibleWith<char_type> CharT>
template<CharIncompatibleWith<char_type> CharT>
static constexpr void operator()(CharT) = delete; // Mixing incompatible char types is not allowed

[[nodiscard]] static constexpr literal_char<Encoding>
Expand All @@ -59,7 +59,7 @@ struct any_char : char_parser<Encoding, any_char<Encoding>>
return char_set<Encoding>{ch};
}

template<traits::CharIncompatibleWith<char_type> CharT, std::size_t N>
template<CharIncompatibleWith<char_type> CharT, std::size_t N>
static constexpr void
operator()(CharT const (&)[N]) = delete; // Mixing incompatible char types is not allowed

Expand All @@ -71,7 +71,7 @@ struct any_char : char_parser<Encoding, any_char<Encoding>>
}

template<class From, class To>
requires traits::CharIncompatibleWith<From, char_type> || traits::CharIncompatibleWith<To, char_type>
requires CharIncompatibleWith<From, char_type> || CharIncompatibleWith<To, char_type>
static constexpr void operator()(From, To) = delete; // Mixing incompatible char types is not allowed

template<class From, std::size_t FromN, class To, std::size_t ToN>
Expand Down
56 changes: 42 additions & 14 deletions include/iris/x4/char/char.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/

#include <iris/x4/core/char_traits.hpp>
#include <iris/x4/char/any_char.hpp>
#include <iris/x4/char/literal_char.hpp>
#include <iris/x4/traits/string_traits.hpp>

#include <iris/x4/char_encoding/standard.hpp>

Expand All @@ -39,7 +39,7 @@ lit(char ch) noexcept
}

[[nodiscard]] constexpr literal_char<char_encoding::standard, unused_type>
lit(traits::X4VagueArrayOf2Chars<char> auto const& ch) noexcept
lit(X4VagueArrayOf2Chars<char> auto const& ch) noexcept
{
return {ch[0]};
}
Expand All @@ -51,8 +51,8 @@ lit(traits::X4VagueArrayOf2Chars<char> auto const& ch) noexcept
// If you still see errors after the inclusion, that might be due to
// mixing incompatible string literals. Don't do that.

constexpr void lit(traits::CharIncompatibleWith<char> auto const*) = delete; // Mixing incompatible character types is not allowed
constexpr void lit(traits::CharIncompatibleWith<char> auto) = delete; // Mixing incompatible character types is not allowed
constexpr void lit(CharIncompatibleWith<char> auto const*) = delete; // Mixing incompatible character types is not allowed
constexpr void lit(CharIncompatibleWith<char> auto) = delete; // Mixing incompatible character types is not allowed

} // standard

Expand All @@ -76,15 +76,15 @@ lit(wchar_t ch) noexcept
}

[[nodiscard]] constexpr literal_char<char_encoding::standard_wide, unused_type>
lit(traits::X4VagueArrayOf2Chars<wchar_t> auto const& ch) noexcept
lit(X4VagueArrayOf2Chars<wchar_t> auto const& ch) noexcept
{
return {ch[0]};
}

} // helpers

constexpr void lit(traits::CharIncompatibleWith<wchar_t> auto const*) = delete; // Mixing incompatible character types is not allowed
constexpr void lit(traits::CharIncompatibleWith<wchar_t> auto) = delete; // Mixing incompatible character types is not allowed
constexpr void lit(CharIncompatibleWith<wchar_t> auto const*) = delete; // Mixing incompatible character types is not allowed
constexpr void lit(CharIncompatibleWith<wchar_t> auto) = delete; // Mixing incompatible character types is not allowed

} // standard_wide

Expand All @@ -108,15 +108,15 @@ lit(char32_t ch) noexcept
}

[[nodiscard]] constexpr literal_char<char_encoding::unicode, unused_type>
lit(traits::X4VagueArrayOf2Chars<char32_t> auto const& ch) noexcept
lit(X4VagueArrayOf2Chars<char32_t> auto const& ch) noexcept
{
return {ch[0]};
}

} // helpers

constexpr void lit(traits::CharIncompatibleWith<char32_t> auto const*) = delete; // Mixing incompatible character types is not allowed
constexpr void lit(traits::CharIncompatibleWith<char32_t> auto) = delete; // Mixing incompatible character types is not allowed
constexpr void lit(CharIncompatibleWith<char32_t> auto const*) = delete; // Mixing incompatible character types is not allowed
constexpr void lit(CharIncompatibleWith<char32_t> auto) = delete; // Mixing incompatible character types is not allowed

} // unicode

Expand All @@ -143,7 +143,7 @@ using x4::standard_wide::lit;
namespace unicode {
using x4::unicode::char_;
using x4::unicode::lit;
} // standard_wide
} // unicode
#endif

using x4::char_; // TODO: make `any_parser` encoding-agnostic
Expand Down Expand Up @@ -178,10 +178,24 @@ struct as_parser<wchar_t>
return {ch};
}
};
#endif
#endif // IRIS_X4_NO_STANDARD_WIDE

#ifdef IRIS_X4_UNICODE
template<>
struct as_parser<char32_t>
{
using type = literal_char<char_encoding::unicode, unused_type>;
using value_type = type;

[[nodiscard]] static constexpr type call(char32_t ch) noexcept
{
return {ch};
}
};
#endif // IRIS_X4_UNICODE

template<>
struct as_parser<char [2]>
struct as_parser<char[2]>
{
using type = literal_char<char_encoding::standard, unused_type>;
using value_type = type;
Expand All @@ -194,7 +208,7 @@ struct as_parser<char [2]>

#ifndef IRIS_X4_NO_STANDARD_WIDE
template<>
struct as_parser<wchar_t [2]>
struct as_parser<wchar_t[2]>
{
using type = literal_char<char_encoding::standard_wide, unused_type>;
using value_type = type;
Expand All @@ -206,6 +220,20 @@ struct as_parser<wchar_t [2]>
};
#endif // IRIS_X4_NO_STANDARD_WIDE

#ifdef IRIS_X4_UNICODE
template<>
struct as_parser<char32_t[2]>
{
using type = literal_char<char_encoding::unicode, unused_type>;
using value_type = type;

[[nodiscard]] static constexpr type call(char32_t const ch[]) noexcept
{
return {ch[0]};
}
};
#endif // IRIS_X4_UNICODE

} // extension

} // iris::x4
Expand Down
5 changes: 3 additions & 2 deletions include/iris/x4/char/char_class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@
==============================================================================*/

#include <iris/x4/core/skip_over.hpp>
#include <iris/x4/core/char_traits.hpp>

#include <iris/x4/char/char_parser.hpp>
#include <iris/x4/char/char_class_tags.hpp>
#include <iris/x4/char/detail/cast_char.hpp>

#include <iris/x4/string/case_compare.hpp>

#include <iris/x4/traits/string_traits.hpp>
#include <iris/x4/traits/char_encoding_traits.hpp>

#include <iris/x4/char_encoding/standard.hpp>

Expand Down Expand Up @@ -183,7 +184,7 @@ template<class CharClassTag, std::forward_iterator It, std::sentinel_for<It> Se>
constexpr void builtin_skip_over(It& first, Se const& last) noexcept
{
using CharT = std::remove_cvref_t<std::iter_value_t<It>>;
static_assert(traits::CharLike<CharT>);
static_assert(CharLike<CharT>);

using Encoding = traits::char_encoding_for<CharT>;
using Parser = char_class_parser<Encoding, CharClassTag>;
Expand Down
11 changes: 5 additions & 6 deletions include/iris/x4/char/char_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
#include <iris/x4/core/parser.hpp>
#include <iris/x4/core/skip_over.hpp>
#include <iris/x4/core/move_to.hpp>

#include <iris/x4/traits/string_traits.hpp>
#include <iris/x4/core/char_traits.hpp>

#include <concepts>
#include <iterator>
Expand Down Expand Up @@ -47,8 +46,8 @@ struct char_parser : parser<Derived>
noexcept(++first)
)
{
static_assert(std::same_as<std::iter_value_t<It>, char_type>, "Mixing incompatible char types is not allowed");
static_assert(!traits::CharLike<Attr> || std::same_as<Attr, char_type>, "Mixing incompatible char types is not allowed");
static_assert(!CharIncompatibleWith<std::iter_value_t<It>, char_type>, "Mixing incompatible char types is not allowed");
static_assert(!CharLike<Attr> || !CharIncompatibleWith<Attr, char_type>, "Mixing incompatible char types is not allowed");

x4::skip_over(first, last, ctx);

Expand All @@ -72,8 +71,8 @@ struct char_parser : parser<Derived>
noexcept(++first)
)
{
static_assert(std::same_as<std::iter_value_t<It>, char_type>, "Mixing incompatible char types is not allowed");
static_assert(!traits::CharLike<Attr> || std::same_as<Attr, char_type>, "Mixing incompatible char types is not allowed");
static_assert(!CharIncompatibleWith<std::iter_value_t<It>, char_type>, "Mixing incompatible char types is not allowed");
static_assert(!CharLike<Attr> || !CharIncompatibleWith<Attr, char_type>, "Mixing incompatible char types is not allowed");

x4::skip_over(first, last, ctx);

Expand Down
60 changes: 60 additions & 0 deletions include/iris/x4/core/char_traits.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#ifndef IRIS_ZZ_X4_CORE_CHAR_TRAITS_HPP
#define IRIS_ZZ_X4_CORE_CHAR_TRAITS_HPP

/*=============================================================================
Copyright (c) 2026 The Iris Project Contributors

Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================_==============================*/

#include <iris/config.hpp>

#include <concepts>
#include <type_traits>

namespace iris::x4 {

template<class T>
concept CharLike =
std::same_as<std::remove_cvref_t<T>, char> ||
std::same_as<std::remove_cvref_t<T>, wchar_t> ||
std::same_as<std::remove_cvref_t<T>, char8_t> ||
std::same_as<std::remove_cvref_t<T>, char16_t> ||
std::same_as<std::remove_cvref_t<T>, char32_t>;

template<class T>
concept CharArray =
std::is_array_v<std::remove_cvref_t<T>> &&
CharLike<std::remove_extent_t<std::remove_cvref_t<T>>>;

// Spirit has historically converted "c" to 'c'.
//
// While we think it's still useful to retain the conversion,
// we need to avoid further conversion to `std::basic_string_view`,
// which leads to performance overhead. This trait enables
// detection of such arrays.
//
// Note that the status quo introduces ambiguity in determining
// {'c', '\0'} and {'c', 'd'}, but we're not aware of any practical
// usage of non-null-terminated character array in the context of
// DSL on parser combinator.
//
// However, if compelling use cases emerge, we may revise these
// semantics. Versioned as `X4` for forward compatibility.
template<class T, class CharT>
concept X4VagueArrayOf2Chars =
std::same_as<std::remove_extent_t<std::remove_cvref_t<T>>, CharT> &&
std::is_bounded_array_v<std::remove_cvref_t<T>> &&
std::extent_v<std::remove_cvref_t<T>> == 2;

// Mixing incompatible character types is semantically wrong.
// Don't do that. It may even lead to security vulnerabilities.
template<class T, class ExpectedCharT>
concept CharIncompatibleWith =
CharLike<T> &&
!std::same_as<T, ExpectedCharT>;

} // iris::x4

#endif
14 changes: 12 additions & 2 deletions include/iris/x4/core/move_to.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
=============================================================================*/

#include <iris/config.hpp>
#include <iris/type_traits.hpp>

#include <iris/x4/core/char_traits.hpp>

#include <iris/x4/traits/attribute_category.hpp>
#include <iris/x4/traits/tuple_traits.hpp>
Expand Down Expand Up @@ -74,7 +75,7 @@ constexpr void move_to(T&, T&) noexcept
);

static_assert(
false,
!std::is_lvalue_reference_v<T&>,
"lvalue reference detected on the `src` argument of `x4::move_to`. "
"The caller is definitely lacking `std::move` or `std::forward`. If you "
"intend to *copy* the mutable value, apply `x4::move_to(std::as_const(attr_), attr)`."
Expand Down Expand Up @@ -134,6 +135,8 @@ move_to(Source&& src, Dest& dest)
{
static_assert(!std::same_as<std::remove_cvref_t<Source>, Dest>, "[BUG] This call should instead resolve to the overload handling identical types");
static_assert(std::is_assignable_v<Dest&, Source>);
static_assert((!CharLike<Dest> && !CharLike<std::remove_cvref_t<Source>>) || !CharIncompatibleWith<Dest, std::remove_cvref_t<Source>>, "Mixing incompatible char types is not allowed");

dest = std::forward<Source>(src);
}

Expand Down Expand Up @@ -206,6 +209,13 @@ move_to(It first, Se last, Dest& dest)
{
static_assert(!std::same_as<std::remove_const_t<Dest>, unused_type>);
static_assert(!std::same_as<std::remove_const_t<Dest>, unused_container_type>);
static_assert(
// If either `It` or `Dest` is relevant to any character type,
(!CharLike<std::remove_cvref_t<std::iter_value_t<It>>> && !CharLike<std::remove_cvref_t<typename traits::container_value<Dest>::type>>) ||
// ... then do the check below:
!CharIncompatibleWith<std::remove_cvref_t<std::iter_value_t<It>>, std::remove_cvref_t<typename traits::container_value<Dest>::type>>,
"Mixing incompatible char types is not allowed"
);

if constexpr (!is_ttp_specialization_of_v<Dest, container_appender>) {
if (!traits::is_empty(dest)) {
Expand Down
Loading
Loading