mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 14:07:46 +00:00
AK+Format: Support default index in replacement field.
The following does now work: outf("{:0{}}", 1, 3); // 001
This commit is contained in:
parent
afa2523724
commit
13ce24de13
3 changed files with 52 additions and 26 deletions
|
@ -128,7 +128,7 @@ public:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!consume_number(index))
|
if (!consume_number(index))
|
||||||
return use_next_index;
|
index = use_next_index;
|
||||||
|
|
||||||
if (!consume_specific('}'))
|
if (!consume_specific('}'))
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
|
@ -146,7 +146,7 @@ void write_escaped_literal(StringBuilder& builder, StringView literal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void vformat_impl(StringBuilder& builder, FormatStringParser& parser, Span<const AK::TypeErasedParameter> parameters, size_t argument_index = 0)
|
void vformat_impl(StringBuilder& builder, FormatStringParser& parser, AK::FormatterContext& context)
|
||||||
{
|
{
|
||||||
const auto literal = parser.consume_literal();
|
const auto literal = parser.consume_literal();
|
||||||
write_escaped_literal(builder, literal);
|
write_escaped_literal(builder, literal);
|
||||||
|
@ -158,23 +158,25 @@ void vformat_impl(StringBuilder& builder, FormatStringParser& parser, Span<const
|
||||||
}
|
}
|
||||||
|
|
||||||
if (specifier.index == use_next_index)
|
if (specifier.index == use_next_index)
|
||||||
specifier.index = argument_index++;
|
specifier.index = context.take_next_index();
|
||||||
|
|
||||||
ASSERT(specifier.index < parameters.size());
|
ASSERT(specifier.index < context.parameter_count());
|
||||||
|
|
||||||
auto& parameter = parameters[specifier.index];
|
context.set_flags(specifier.flags);
|
||||||
parameter.formatter(builder, parameter.value, specifier.flags, parameters);
|
|
||||||
|
|
||||||
vformat_impl(builder, parser, parameters, argument_index);
|
auto& parameter = context.parameter_at(specifier.index);
|
||||||
|
parameter.formatter(builder, parameter.value, context);
|
||||||
|
|
||||||
|
vformat_impl(builder, parser, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t decode_value(size_t value, Span<const AK::TypeErasedParameter> parameters)
|
size_t decode_value(size_t value, AK::FormatterContext& context)
|
||||||
{
|
{
|
||||||
if (value == AK::StandardFormatter::value_from_next_arg)
|
if (value == AK::StandardFormatter::value_from_next_arg)
|
||||||
TODO();
|
value = AK::StandardFormatter::value_from_arg + context.take_next_index();
|
||||||
|
|
||||||
if (value >= AK::StandardFormatter::value_from_arg) {
|
if (value >= AK::StandardFormatter::value_from_arg) {
|
||||||
const auto parameter = parameters.at(value - AK::StandardFormatter::value_from_arg);
|
const auto parameter = context.parameter_at(value - AK::StandardFormatter::value_from_arg);
|
||||||
|
|
||||||
Optional<i64> svalue;
|
Optional<i64> svalue;
|
||||||
if (parameter.type == AK::TypeErasedParameter::Type::UInt8)
|
if (parameter.type == AK::TypeErasedParameter::Type::UInt8)
|
||||||
|
@ -212,19 +214,21 @@ namespace AK {
|
||||||
void vformat(StringBuilder& builder, StringView fmtstr, Span<const TypeErasedParameter> parameters)
|
void vformat(StringBuilder& builder, StringView fmtstr, Span<const TypeErasedParameter> parameters)
|
||||||
{
|
{
|
||||||
FormatStringParser parser { fmtstr };
|
FormatStringParser parser { fmtstr };
|
||||||
vformat_impl(builder, parser, parameters);
|
FormatterContext context { parameters };
|
||||||
|
vformat_impl(builder, parser, context);
|
||||||
}
|
}
|
||||||
void vformat(const LogStream& stream, StringView fmtstr, Span<const TypeErasedParameter> parameters)
|
void vformat(const LogStream& stream, StringView fmtstr, Span<const TypeErasedParameter> parameters)
|
||||||
{
|
{
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
FormatStringParser parser { fmtstr };
|
FormatStringParser parser { fmtstr };
|
||||||
vformat_impl(builder, parser, parameters);
|
FormatterContext context { parameters };
|
||||||
|
vformat_impl(builder, parser, context);
|
||||||
stream << builder.to_string();
|
stream << builder.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StandardFormatter::parse(StringView flags)
|
void StandardFormatter::parse(FormatterContext& context)
|
||||||
{
|
{
|
||||||
FormatStringParser parser { flags };
|
FormatStringParser parser { context.flags() };
|
||||||
|
|
||||||
if (StringView { "<^>" }.contains(parser.peek(1))) {
|
if (StringView { "<^>" }.contains(parser.peek(1))) {
|
||||||
ASSERT(!parser.next_is(is_any_of("{}")));
|
ASSERT(!parser.next_is(is_any_of("{}")));
|
||||||
|
@ -253,7 +257,7 @@ void StandardFormatter::parse(StringView flags)
|
||||||
|
|
||||||
if (size_t index = 0; parser.consume_replacement_field(index)) {
|
if (size_t index = 0; parser.consume_replacement_field(index)) {
|
||||||
if (index == use_next_index)
|
if (index == use_next_index)
|
||||||
TODO();
|
index = context.take_next_index();
|
||||||
|
|
||||||
m_width = value_from_arg + index;
|
m_width = value_from_arg + index;
|
||||||
} else if (size_t width = 0; parser.consume_number(width)) {
|
} else if (size_t width = 0; parser.consume_number(width)) {
|
||||||
|
@ -263,7 +267,7 @@ void StandardFormatter::parse(StringView flags)
|
||||||
if (parser.consume_specific('.')) {
|
if (parser.consume_specific('.')) {
|
||||||
if (size_t index = 0; parser.consume_replacement_field(index)) {
|
if (size_t index = 0; parser.consume_replacement_field(index)) {
|
||||||
if (index == use_next_index)
|
if (index == use_next_index)
|
||||||
TODO();
|
index = context.take_next_index();
|
||||||
|
|
||||||
m_precision = value_from_arg + index;
|
m_precision = value_from_arg + index;
|
||||||
} else if (size_t precision = 0; parser.consume_number(precision)) {
|
} else if (size_t precision = 0; parser.consume_number(precision)) {
|
||||||
|
@ -296,7 +300,7 @@ void StandardFormatter::parse(StringView flags)
|
||||||
ASSERT(parser.is_eof());
|
ASSERT(parser.is_eof());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Formatter<StringView>::format(StringBuilder& builder, StringView value, Span<const TypeErasedParameter>)
|
void Formatter<StringView>::format(StringBuilder& builder, StringView value, FormatterContext&)
|
||||||
{
|
{
|
||||||
if (m_align != Align::Default)
|
if (m_align != Align::Default)
|
||||||
TODO();
|
TODO();
|
||||||
|
@ -317,7 +321,7 @@ void Formatter<StringView>::format(StringBuilder& builder, StringView value, Spa
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void Formatter<T, typename EnableIf<IsIntegral<T>::value>::Type>::format(StringBuilder& builder, T value, Span<const TypeErasedParameter> parameters)
|
void Formatter<T, typename EnableIf<IsIntegral<T>::value>::Type>::format(StringBuilder& builder, T value, FormatterContext& context)
|
||||||
{
|
{
|
||||||
if (m_precision != value_not_set)
|
if (m_precision != value_not_set)
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
|
@ -344,7 +348,7 @@ void Formatter<T, typename EnableIf<IsIntegral<T>::value>::Type>::format(StringB
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto width = decode_value(m_width, parameters);
|
auto width = decode_value(m_width, context);
|
||||||
|
|
||||||
PrintfImplementation::Align align;
|
PrintfImplementation::Align align;
|
||||||
if (m_align == Align::Left)
|
if (m_align == Align::Left)
|
||||||
|
|
35
AK/Format.h
35
AK/Format.h
|
@ -76,7 +76,28 @@ struct TypeErasedParameter {
|
||||||
|
|
||||||
const void* value;
|
const void* value;
|
||||||
Type type;
|
Type type;
|
||||||
void (*formatter)(StringBuilder& builder, const void* value, StringView flags, Span<const TypeErasedParameter> parameters);
|
void (*formatter)(StringBuilder& builder, const void* value, class FormatterContext&);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormatterContext {
|
||||||
|
public:
|
||||||
|
FormatterContext(Span<const TypeErasedParameter> parameters)
|
||||||
|
: m_parameters(parameters)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const TypeErasedParameter& parameter_at(size_t index) const { return m_parameters.at(index); }
|
||||||
|
size_t parameter_count() const { return m_parameters.size(); }
|
||||||
|
|
||||||
|
StringView flags() const { return m_flags; }
|
||||||
|
void set_flags(StringView value) { m_flags = value; }
|
||||||
|
|
||||||
|
size_t take_next_index() { return m_next_index++; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Span<const TypeErasedParameter> m_parameters;
|
||||||
|
StringView m_flags;
|
||||||
|
size_t m_next_index { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AK
|
} // namespace AK
|
||||||
|
@ -84,12 +105,12 @@ struct TypeErasedParameter {
|
||||||
namespace AK::Detail::Format {
|
namespace AK::Detail::Format {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void format_value(StringBuilder& builder, const void* value, StringView flags, AK::Span<const TypeErasedParameter> parameters)
|
void format_value(StringBuilder& builder, const void* value, FormatterContext& context)
|
||||||
{
|
{
|
||||||
Formatter<T> formatter;
|
Formatter<T> formatter;
|
||||||
|
|
||||||
formatter.parse(flags);
|
formatter.parse(context);
|
||||||
formatter.format(builder, *static_cast<const T*>(value), parameters);
|
formatter.format(builder, *static_cast<const T*>(value), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AK::Detail::Format
|
} // namespace AK::Detail::Format
|
||||||
|
@ -140,12 +161,12 @@ struct StandardFormatter {
|
||||||
size_t m_width = value_not_set;
|
size_t m_width = value_not_set;
|
||||||
size_t m_precision = value_not_set;
|
size_t m_precision = value_not_set;
|
||||||
|
|
||||||
void parse(StringView flags);
|
void parse(FormatterContext&);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct Formatter<StringView> : StandardFormatter {
|
struct Formatter<StringView> : StandardFormatter {
|
||||||
void format(StringBuilder& builder, StringView value, Span<const TypeErasedParameter>);
|
void format(StringBuilder& builder, StringView value, FormatterContext&);
|
||||||
};
|
};
|
||||||
template<>
|
template<>
|
||||||
struct Formatter<const char*> : Formatter<StringView> {
|
struct Formatter<const char*> : Formatter<StringView> {
|
||||||
|
@ -162,7 +183,7 @@ struct Formatter<String> : Formatter<StringView> {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct Formatter<T, typename EnableIf<IsIntegral<T>::value>::Type> : StandardFormatter {
|
struct Formatter<T, typename EnableIf<IsIntegral<T>::value>::Type> : StandardFormatter {
|
||||||
void format(StringBuilder&, T value, Span<const TypeErasedParameter>);
|
void format(StringBuilder&, T value, FormatterContext&);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Parameters>
|
template<typename... Parameters>
|
||||||
|
|
|
@ -119,6 +119,7 @@ TEST_CASE(replacement_field)
|
||||||
EXPECT_EQ(String::formatted("{:*<{1}}", 7, 4), "7***");
|
EXPECT_EQ(String::formatted("{:*<{1}}", 7, 4), "7***");
|
||||||
EXPECT_EQ(String::formatted("{:{2}}", -5, 8, 16), " -5");
|
EXPECT_EQ(String::formatted("{:{2}}", -5, 8, 16), " -5");
|
||||||
EXPECT_EQ(String::formatted("{{{:*^{1}}}}", 1, 3), "{*1*}");
|
EXPECT_EQ(String::formatted("{{{:*^{1}}}}", 1, 3), "{*1*}");
|
||||||
|
EXPECT_EQ(String::formatted("{:0{}}", 1, 3), "001");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_MAIN(Format)
|
TEST_MAIN(Format)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue