Skip to content

Commit

Permalink
AK+Format: Support default index in replacement field.
Browse files Browse the repository at this point in the history
The following does now work:

    outf("{:0{}}", 1, 3);      // 001
  • Loading branch information
asynts authored and awesomekling committed Sep 28, 2020
1 parent afa2523 commit 13ce24d
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 26 deletions.
42 changes: 23 additions & 19 deletions AK/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class FormatStringParser : public GenericLexer {
return false;

if (!consume_number(index))
return use_next_index;
index = use_next_index;

if (!consume_specific('}'))
ASSERT_NOT_REACHED();
Expand All @@ -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();
write_escaped_literal(builder, literal);
Expand All @@ -158,23 +158,25 @@ void vformat_impl(StringBuilder& builder, FormatStringParser& parser, Span<const
}

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];
parameter.formatter(builder, parameter.value, specifier.flags, parameters);
context.set_flags(specifier.flags);

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)
TODO();
value = AK::StandardFormatter::value_from_arg + context.take_next_index();

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;
if (parameter.type == AK::TypeErasedParameter::Type::UInt8)
Expand Down Expand Up @@ -212,19 +214,21 @@ namespace AK {
void vformat(StringBuilder& builder, StringView fmtstr, Span<const TypeErasedParameter> parameters)
{
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)
{
StringBuilder builder;
FormatStringParser parser { fmtstr };
vformat_impl(builder, parser, parameters);
FormatterContext context { parameters };
vformat_impl(builder, parser, context);
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))) {
ASSERT(!parser.next_is(is_any_of("{}")));
Expand Down Expand Up @@ -253,7 +257,7 @@ void StandardFormatter::parse(StringView flags)

if (size_t index = 0; parser.consume_replacement_field(index)) {
if (index == use_next_index)
TODO();
index = context.take_next_index();

m_width = value_from_arg + index;
} else if (size_t width = 0; parser.consume_number(width)) {
Expand All @@ -263,7 +267,7 @@ void StandardFormatter::parse(StringView flags)
if (parser.consume_specific('.')) {
if (size_t index = 0; parser.consume_replacement_field(index)) {
if (index == use_next_index)
TODO();
index = context.take_next_index();

m_precision = value_from_arg + index;
} else if (size_t precision = 0; parser.consume_number(precision)) {
Expand Down Expand Up @@ -296,7 +300,7 @@ void StandardFormatter::parse(StringView flags)
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)
TODO();
Expand All @@ -317,7 +321,7 @@ void Formatter<StringView>::format(StringBuilder& builder, StringView value, Spa
}

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)
ASSERT_NOT_REACHED();
Expand All @@ -344,7 +348,7 @@ void Formatter<T, typename EnableIf<IsIntegral<T>::value>::Type>::format(StringB
ASSERT_NOT_REACHED();
}

auto width = decode_value(m_width, parameters);
auto width = decode_value(m_width, context);

PrintfImplementation::Align align;
if (m_align == Align::Left)
Expand Down
35 changes: 28 additions & 7 deletions AK/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,41 @@ struct TypeErasedParameter {

const void* value;
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::Detail::Format {

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.parse(flags);
formatter.format(builder, *static_cast<const T*>(value), parameters);
formatter.parse(context);
formatter.format(builder, *static_cast<const T*>(value), context);
}

} // namespace AK::Detail::Format
Expand Down Expand Up @@ -140,12 +161,12 @@ struct StandardFormatter {
size_t m_width = value_not_set;
size_t m_precision = value_not_set;

void parse(StringView flags);
void parse(FormatterContext&);
};

template<>
struct Formatter<StringView> : StandardFormatter {
void format(StringBuilder& builder, StringView value, Span<const TypeErasedParameter>);
void format(StringBuilder& builder, StringView value, FormatterContext&);
};
template<>
struct Formatter<const char*> : Formatter<StringView> {
Expand All @@ -162,7 +183,7 @@ struct Formatter<String> : Formatter<StringView> {

template<typename T>
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>
Expand Down
1 change: 1 addition & 0 deletions AK/Tests/TestFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ TEST_CASE(replacement_field)
EXPECT_EQ(String::formatted("{:*<{1}}", 7, 4), "7***");
EXPECT_EQ(String::formatted("{:{2}}", -5, 8, 16), " -5");
EXPECT_EQ(String::formatted("{{{:*^{1}}}}", 1, 3), "{*1*}");
EXPECT_EQ(String::formatted("{:0{}}", 1, 3), "001");
}

TEST_MAIN(Format)

0 comments on commit 13ce24d

Please sign in to comment.