// SPDX-License-Identifier: MIT
#include "MText.hh"
#include <algorithm>
#include <ostream>
#include "MFormat.hh"
#include "MUtil.hh"
TextElementsWriter::TextElementsWriter( std::ostream &stream, std::string &indent )
: Writer( stream, indent )
{
// blank
}
void TextElementsWriter::operator( )( const TextElement &text )
{
if ( text.Macro )
{
if ( ! text.Formats.empty( ) )
{
// copyfmt saves and restores flags, precision, width, fill and locale:
// a format never leaks into the text that follows.
_Stream << _Indent << '{' << std::endl;
right( );
_Stream << _Indent << "std::ios originalFormat( nullptr );" << std::endl
<< _Indent << "originalFormat.copyfmt( stream );" << std::endl;
}
std::for_each( text.Formats.begin( ), text.Formats.end( ), FormatWriter( _Stream, _Indent ) );
_Stream << _Indent << "stream << ( " << text.Text << " );" << std::endl;
if ( ! text.Formats.empty( ) )
{
_Stream << _Indent << "stream.copyfmt( originalFormat );" << std::endl;
left( );
_Stream << _Indent << '}' << std::endl;
}
}
else
{
_Stream << _Indent << "stream << ";
WriteStringLiteral( _Stream, text.Text, _Indent, 10 );
_Stream << ';' << std::endl;
}
}
const char *const AppendHelperName = "InternalAppend";
namespace
{
void AppendElement( std::ostream &stream, std::string &indent, const TextElement &text )
{
if ( ! text.Macro )
{
stream << indent << "internal_out += ";
WriteStringLiteral( stream, text.Text, indent, 16 );
stream << ';' << std::endl;
}
else if ( text.Formats.empty( ) )
{
stream << indent << Mode.Helper( AppendHelperName ) << "( internal_out, loc, ( " << text.Text << " ) );" << std::endl;
}
else
{
// Formats need a stream; a fresh one per value, so nothing leaks.
stream << indent << '{' << std::endl;
indent.append( " " );
stream << indent << "std::ostringstream stream;" << std::endl
<< indent << "stream.imbue( loc );" << std::endl;
std::for_each( text.Formats.begin( ), text.Formats.end( ), FormatWriter( stream, indent ) );
stream << indent << "stream << ( " << text.Text << " );" << std::endl
<< indent << "internal_out += stream.str( );" << std::endl;
indent.erase( 0, 2 );
stream << indent << '}' << std::endl;
}
}
}
void WriteLocaleAppend( std::ostream &stream, std::string &indent, const LocaleMessage &locale )
{
stream << indent << "// ENCODE : " << locale.Encode << std::endl;
for ( const TextElement &text : locale.TextElements )
{
AppendElement( stream, indent, text );
}
}
void WriteAppendHelper( std::ostream &stream )
{
Mode.OpenHelpers( stream, "APPEND" );
stream << " template <typename T>" << std::endl
<< " inline constexpr bool InternalIsCharacter =" << std::endl
<< " std::is_same_v<T, char> || std::is_same_v<T, signed char> || std::is_same_v<T, unsigned char> ||" << std::endl
<< " std::is_same_v<T, wchar_t> || std::is_same_v<T, char16_t> || std::is_same_v<T, char32_t>;" << std::endl
<< std::endl
<< " // Append a value exactly as `stream << value` on a stream imbued with" << std::endl
<< " // loc would write it, without a stream where that is possible." << std::endl
<< " template <typename T>" << std::endl
<< " inline void " << AppendHelperName << "( std::string &out, std::locale const &loc, T const &value )" << std::endl
<< " {" << std::endl
<< " if constexpr ( std::is_convertible_v<T const &, std::string_view> )" << std::endl
<< " {" << std::endl
<< " out += std::string_view( value );" << std::endl
<< " return;" << std::endl
<< " }" << std::endl
<< " else if constexpr ( std::is_integral_v<T> && ! std::is_same_v<T, bool> && ! InternalIsCharacter<T> )" << std::endl
<< " {" << std::endl
<< " if ( loc == std::locale::classic( ) )" << std::endl
<< " {" << std::endl
<< " char buffer[24];" << std::endl
<< " std::to_chars_result const written = std::to_chars( buffer, buffer + sizeof buffer, value );" << std::endl
<< " out.append( buffer, written.ptr );" << std::endl
<< " return;" << std::endl
<< " }" << std::endl
<< " }" << std::endl
<< std::endl
<< " std::ostringstream stream;" << std::endl
<< " stream.imbue( loc );" << std::endl
<< " stream << value;" << std::endl
<< " out += stream.str( );" << std::endl
<< " }" << std::endl;
Mode.CloseHelpers( stream );
}
void WriteLocaleText( std::ostream &stream, std::string &indent, const LocaleMessage &locale )
{
stream << indent << "// ENCODE : " << locale.Encode << std::endl;
std::for_each( locale.TextElements.begin( ), locale.TextElements.end( ),
TextElementsWriter( stream, indent ) );
}