Files
fluxengine/dep/lexy/include/lexy/callback/string.hpp
2025-08-14 23:36:31 +02:00

211 lines
7.2 KiB
C++

// Copyright (C) 2020-2025 Jonathan Müller and lexy contributors
// SPDX-License-Identifier: BSL-1.0
#ifndef LEXY_CALLBACK_STRING_HPP_INCLUDED
#define LEXY_CALLBACK_STRING_HPP_INCLUDED
#include <lexy/_detail/code_point.hpp>
#include <lexy/callback/base.hpp>
#include <lexy/code_point.hpp>
#include <lexy/encoding.hpp>
#include <lexy/input/base.hpp>
#include <lexy/lexeme.hpp>
namespace lexy
{
struct nullopt;
template <typename String>
using _string_char_type = LEXY_DECAY_DECLTYPE(LEXY_DECLVAL(String)[0]);
template <typename String, typename Encoding, typename CaseFoldingDSL = void>
struct _as_string
{
using return_type = String;
using _char_type = _string_char_type<String>;
static_assert(lexy::_detail::is_compatible_char_type<Encoding, _char_type>,
"invalid character type/encoding combination");
static constexpr String&& _case_folding(String&& str)
{
if constexpr (std::is_void_v<CaseFoldingDSL>)
{
return LEXY_MOV(str);
}
else if constexpr (CaseFoldingDSL::template is_inplace<Encoding>)
{
// We can change the string in place.
auto original_reader = lexy::_range_reader<Encoding>(str.begin(), str.end());
auto reader = typename CaseFoldingDSL::template case_folding<decltype(original_reader)>{
original_reader};
for (auto ptr = str.data(); true; ++ptr)
{
auto cur = reader.peek();
if (cur == Encoding::eof())
break;
reader.bump();
// Once we've bumped it, we're not looking at it again.
*ptr = static_cast<_char_type>(cur);
}
return LEXY_MOV(str);
}
else
{
// We store the existing string somewhere else and clear it.
// Then we can read the case folded string and append each code unit.
auto original = LEXY_MOV(str);
str = String();
str.reserve(original.size());
auto original_reader = lexy::_range_reader<Encoding>(original.begin(), original.end());
auto reader = typename CaseFoldingDSL::template case_folding<decltype(original_reader)>{
original_reader};
while (true)
{
auto cur = reader.peek();
if (cur == Encoding::eof())
break;
str.push_back(static_cast<_char_type>(cur));
reader.bump();
}
return LEXY_MOV(str);
}
}
template <typename NewCaseFoldingDSL>
constexpr auto case_folding(NewCaseFoldingDSL) const
{
return _as_string<String, Encoding, NewCaseFoldingDSL>{};
}
constexpr String operator()(nullopt&&) const
{
return String();
}
constexpr String&& operator()(String&& str) const
{
return _case_folding(LEXY_MOV(str));
}
template <typename Iterator>
constexpr auto operator()(Iterator begin, Iterator end) const -> decltype(String(begin, end))
{
return _case_folding(String(begin, end));
}
template <typename Str = String, typename Iterator>
constexpr auto operator()(const typename Str::allocator_type& allocator, Iterator begin,
Iterator end) const -> decltype(String(begin, end, allocator))
{
return _case_folding(String(begin, end, allocator));
}
template <typename Reader>
constexpr String operator()(lexeme<Reader> lex) const
{
static_assert(lexy::char_type_compatible_with_reader<Reader, _char_type>,
"cannot convert lexeme to this string type");
using iterator = typename lexeme<Reader>::iterator;
if constexpr (std::is_convertible_v<iterator, const _char_type*>)
return _case_folding(String(lex.data(), lex.size()));
else
return _case_folding(String(lex.begin(), lex.end()));
}
template <typename Str = String, typename Reader>
constexpr String operator()(const typename Str::allocator_type& allocator,
lexeme<Reader> lex) const
{
static_assert(lexy::char_type_compatible_with_reader<Reader, _char_type>,
"cannot convert lexeme to this string type");
using iterator = typename lexeme<Reader>::iterator;
if constexpr (std::is_convertible_v<iterator, const _char_type*>)
return _case_folding(String(lex.data(), lex.size(), allocator));
else
return _case_folding(String(lex.begin(), lex.end(), allocator));
}
constexpr String operator()(code_point cp) const
{
typename Encoding::char_type buffer[4] = {};
auto size = _detail::encode_code_point<Encoding>(cp.value(), buffer, 4);
return _case_folding(String(buffer, buffer + size));
}
template <typename Str = String>
constexpr String operator()(const typename Str::allocator_type& allocator, code_point cp) const
{
typename Encoding::char_type buffer[4] = {};
auto size = _detail::encode_code_point<Encoding>(cp.value(), buffer, 4);
return _case_folding(String(buffer, buffer + size, allocator));
}
struct _sink
{
String _result;
using return_type = String;
template <typename CharT, typename = decltype(LEXY_DECLVAL(String).push_back(CharT()))>
constexpr void operator()(CharT c)
{
_result.push_back(c);
}
constexpr void operator()(String&& str)
{
_result.append(LEXY_MOV(str));
}
template <typename Str = String, typename Iterator>
constexpr auto operator()(Iterator begin, Iterator end)
-> decltype(void(LEXY_DECLVAL(Str).append(begin, end)))
{
_result.append(begin, end);
}
template <typename Reader>
constexpr void operator()(lexeme<Reader> lex)
{
static_assert(lexy::char_type_compatible_with_reader<Reader, _char_type>,
"cannot convert lexeme to this string type");
_result.append(lex.begin(), lex.end());
}
constexpr void operator()(code_point cp)
{
typename Encoding::char_type buffer[4] = {};
auto size = _detail::encode_code_point<Encoding>(cp.value(), buffer, 4);
_result.append(buffer, buffer + size);
}
constexpr String&& finish() &&
{
return _case_folding(LEXY_MOV(_result));
}
};
constexpr auto sink() const
{
return _sink{String()};
}
template <typename S = String>
constexpr auto sink(const typename S::allocator_type& allocator) const
{
return _sink{String(allocator)};
}
};
/// A callback with sink that creates a string (e.g. `std::string`).
/// As a callback, it converts a lexeme into the string.
/// As a sink, it repeatedly calls `.push_back()` for individual characters,
/// or `.append()` for lexemes or other strings.
template <typename String, typename Encoding = deduce_encoding<_string_char_type<String>>>
constexpr auto as_string = _as_string<String, Encoding>{};
} // namespace lexy
#endif // LEXY_CALLBACK_STRING_HPP_INCLUDED