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
24 changes: 9 additions & 15 deletions include/iris/x4/char/any_char.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,42 +34,36 @@ struct any_char : char_parser<any_char<Encoding>, Encoding>

template<std::same_as<char_type> CharT>
[[nodiscard]] static constexpr literal_char<Encoding>
operator()(CharT ch) noexcept
operator()(CharT ch)
{
return {ch};
return literal_char<Encoding>{ch};
}

template<std::same_as<char_type> CharT>
[[nodiscard]] static constexpr literal_char<Encoding>
operator()(CharT const (&ch)[2]) noexcept
operator()(CharT const (&ch)[2])
{
return {ch[0]};
return literal_char<Encoding>{ch[0]};
}

template<std::same_as<char_type> CharT, std::size_t N>
[[nodiscard]] static constexpr char_set<Encoding>
operator()(CharT const (&ch)[N])
[[nodiscard]] static constexpr char_set<Encoding, detail::chset_for<classify_type, N - 1>>
operator()(CharT const (&str)[N])
{
static_assert(N >= 3);
return char_set<Encoding>{ch};
static_assert(N >= 3, "x4::char_: the character set is empty");
return char_set<Encoding, detail::chset_for<classify_type, N - 1>>{std::basic_string_view<char_type>(str, N - 1)};
}

template<std::same_as<char_type> CharT>
[[nodiscard]] static constexpr char_range<Encoding>
operator()(CharT from, CharT to) noexcept
operator()(CharT from, CharT to)
{
return {from, to};
}

template<class From, std::size_t FromN, class To, std::size_t ToN>
static constexpr void
operator()(From const (&)[FromN], To const (&)[ToN]) = delete; // Use single character literal to define character range

[[nodiscard]] static char_set<Encoding>
operator()(std::basic_string_view<char_type> sv)
{
return char_set<Encoding>{std::move(sv)};
}
};

} // iris::x4
Expand Down
10 changes: 7 additions & 3 deletions include/iris/x4/char/char_class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,15 @@ struct char_class_parser : char_parser<char_class_parser<Encoding, Tag>, Encodin
using encoding_type = Encoding;
using tag = Tag;
using char_type = Encoding::char_type;
using classify_type = Encoding::classify_type;
using attribute_type = char_type;

static constexpr bool has_attribute = true;

[[nodiscard]] static constexpr bool
test(Encoding::classify_type const classify_ch) noexcept
test(std::same_as<char_type> auto const ch) noexcept
{
auto const classify_ch = static_cast<classify_type>(ch);
return encoding_type::ischar(classify_ch)
&& detail::char_class_base<Encoding>::is(
tag{},
Expand All @@ -87,8 +90,9 @@ struct char_class_parser : char_parser<char_class_parser<Encoding, Tag>, Encodin
}

[[nodiscard]] static constexpr bool
test(Encoding::classify_type const classify_ch, auto const& ctx) noexcept
test(std::same_as<typename Encoding::char_type> auto const ch, auto const& ctx) noexcept
{
auto const classify_ch = static_cast<Encoding::classify_type>(ch);
return encoding_type::ischar(classify_ch)
&& detail::char_class_base<Encoding>::is(
x4::get_case_compare<Encoding>(ctx).get_char_class_tag(tag{}),
Expand Down Expand Up @@ -172,7 +176,7 @@ constexpr void builtin_skip_over(It& first, Se const& last) noexcept
using Encoding = char_encoding_for<CharT>;
using Parser = char_class_parser<Encoding, CharClassTag>;

while (first != last && Parser::test(static_cast<Encoding::classify_type>(*first))) {
while (first != last && Parser::test(*first)) {
++first;
}
}
Expand Down
44 changes: 24 additions & 20 deletions include/iris/x4/char/char_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ struct char_parser : parser<char_parser<Derived, Encoding>>

private:
template<class Context>
static constexpr bool has_static_test = requires(classify_type classify_ch, Context const& ctx) {
{ Derived::test(classify_ch, ctx) } -> std::same_as<bool>;
static constexpr bool has_static_test = requires(char_type const ch, Context const& ctx) {
{ Derived::test(ch, ctx) } -> std::same_as<bool>;
};

public:
Expand All @@ -85,16 +85,18 @@ struct char_parser : parser<char_parser<Derived, Encoding>>
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");

auto it = first;
x4::skip_over(it, last, ctx);

if (it != last && Derived::test(static_cast<classify_type>(*it), ctx)) {
if constexpr (x4::has_attribute_v<Derived>) {
x4::write_attribute(attr, std::iter_value_t<It>{*it});
auto local_it = first;
x4::skip_over(local_it, last, ctx);

if (local_it != last) {
if (auto const ch = *local_it; Derived::test(ch, ctx)) {
if constexpr (x4::has_attribute_v<Derived>) {
x4::write_attribute(attr, ch);
}
++local_it;
first = local_it;
return true;
}
++it;
first = it;
return true;
}
return false;
}
Expand All @@ -107,16 +109,18 @@ struct char_parser : parser<char_parser<Derived, Encoding>>
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");

auto it = first;
x4::skip_over(it, last, ctx);

if (it != last && self.test(static_cast<classify_type>(*it), ctx)) {
if constexpr (x4::has_attribute_v<Derived>) {
x4::write_attribute(attr, std::iter_value_t<It>{*it});
auto local_it = first;
x4::skip_over(local_it, last, ctx);

if (local_it != last) {
if (auto const ch = *local_it; self.test(ch, ctx)) {
if constexpr (x4::has_attribute_v<Derived>) {
x4::write_attribute(attr, ch);
}
++local_it;
first = local_it;
return true;
}
++it;
first = it;
return true;
}
return false;
}
Expand Down
102 changes: 56 additions & 46 deletions include/iris/x4/char/char_set.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,91 +11,83 @@
==============================================================================*/

#include <iris/x4/char/char_parser.hpp>
#include <iris/x4/char/detail/basic_chset.hpp>
#include <iris/x4/char/detail/check_char.hpp>
#include <iris/x4/char/detail/chset.hpp>
#include <iris/x4/string/case_compare.hpp>

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

#include <iris/unicode/string.hpp>

#include <string_view>
#include <ranges>
#include <type_traits>

#include <cstddef> // IWYU pragma: keep

namespace iris::x4 {

// Parser for a character range
template<class Encoding, X4Attribute Attr = typename Encoding::char_type>
template<class Encoding, class Attr = typename Encoding::char_type>
struct char_range : char_parser<char_range<Encoding, Attr>, Encoding>
{
using char_type = Encoding::char_type;
static_assert(X4Attribute<Attr>);

using encoding_type = Encoding;
using char_type = Encoding::char_type;
using classify_type = Encoding::classify_type;
using attribute_type = Attr;

static constexpr bool has_attribute = !std::is_same_v<unused_type, attribute_type>;

constexpr char_range(char_type from_, char_type to_) noexcept
: from(from_), to(to_)
{}
constexpr char_range(std::same_as<char_type> auto const from, std::same_as<char_type> auto const to)
: from_(static_cast<classify_type>(from)), to_(static_cast<classify_type>(to))
{
detail::check_char_range<Encoding>(from, to);
}

template<class Char, class Context>
requires (std::is_convertible_v<std::remove_cvref_t<Char>, char_type>)
[[nodiscard]] constexpr bool test(Char ch_, Context const& ctx) const noexcept
template<class Context>
[[nodiscard]] constexpr bool test(std::same_as<char_type> auto const ch, Context const& ctx) const noexcept
{
char_type ch = static_cast<char_type>(ch_); // optimize for token based parsing
static_assert(noexcept(x4::get_case_compare<encoding_type>(ctx)(ch, from)));
auto const classify_ch = static_cast<classify_type>(ch);
static_assert(noexcept(x4::get_case_compare<encoding_type>(ctx)(classify_ch, from_)));

return x4::get_case_compare<encoding_type>(ctx)(ch, from) >= 0
&& x4::get_case_compare<encoding_type>(ctx)(ch , to) <= 0;
return x4::get_case_compare<encoding_type>(ctx)(classify_ch, from_) >= 0 &&
x4::get_case_compare<encoding_type>(ctx)(classify_ch, to_) <= 0;
}

char_type from, to;

[[nodiscard]] std::string get_x4_info() const
{
// TODO: escape
return std::string("char_(\"")
+ iris::unicode::transcode<char>(typename Encoding::string_type(1, this->from))
+ '-' + iris::unicode::transcode<char>(typename Encoding::string_type(1, this->to))
+ iris::unicode::transcode<char>(typename Encoding::string_type(1, static_cast<char_type>(from_)))
+ '-' + iris::unicode::transcode<char>(typename Encoding::string_type(1, static_cast<char_type>(to_)))
+ "\")";
}

private:
classify_type from_, to_;
};

// Parser for a character set
template<class Encoding, X4Attribute Attr = typename Encoding::char_type>
struct char_set : char_parser<char_set<Encoding, Attr>, Encoding>
template<class Encoding, class Chset, X4Attribute Attr = typename Encoding::char_type>
struct char_set : char_parser<char_set<Encoding, Chset, Attr>, Encoding>
{
using char_type = Encoding::char_type;
using classify_type = Encoding::classify_type;
using encoding_type = Encoding;
using attribute_type = Attr;

static constexpr bool has_attribute = !std::is_same_v<unused_type, attribute_type>;

constexpr explicit char_set(std::basic_string_view<char_type> const str)
constexpr explicit char_set(std::basic_string_view<char_type> const definition)
{
for (auto definition = std::ranges::begin(str); definition != std::ranges::end(str);) {
auto const ch = *definition;
auto next_definition = std::next(definition);
if (next_definition == std::ranges::end(str)) {
chset_.set(ch);
break;
}

auto next_ch = *next_definition;
if (next_ch == detail::char_tokens<char_type>::hyphen) {
next_definition = std::next(next_definition);
if (next_definition == std::ranges::end(str)) {
chset_.set(ch);
chset_.set(detail::char_tokens<char_type>::hyphen);
break;
}
chset_.set(ch, *next_definition);

} else {
chset_.set(ch);
}

definition = next_definition;
for (std::size_t i = 0; i < definition.size();) {
bool const is_range = i + 2 < definition.size() && definition[i + 1] == hyphen;
char_type const from = definition[i];
char_type const to = definition[is_range ? i + 2 : i];
detail::check_char_range<Encoding>(from, to);
chset_.set(static_cast<classify_type>(from), static_cast<classify_type>(to));
i += is_range ? 3 : 1;
}
}

Expand All @@ -109,11 +101,29 @@ struct char_set : char_parser<char_set<Encoding, Attr>, Encoding>
[[nodiscard]] std::string get_x4_info() const
{
// TODO: escape
return "char-set"; // TODO: make more user-friendly
typename Encoding::string_type definition;
bool has_hyphen = false;
chset_.for_each_range([&](classify_type const first, classify_type const last) {
if (first == last && static_cast<char_type>(first) == hyphen) {
has_hyphen = true;
return;
}
definition += static_cast<char_type>(first);
if (first != last) {
definition += hyphen;
definition += static_cast<char_type>(last);
}
});
if (has_hyphen) {
definition += hyphen;
}
return "char_(\"" + iris::unicode::transcode<char>(definition) + "\")";
}

private:
detail::basic_chset<char_type> chset_;
static constexpr char_type hyphen = detail::char_tokens<char_type>::hyphen;

Chset chset_;
};

} // iris::x4
Expand Down
Loading
Loading