add new format flag @, make it escape always (never toggle)

master
Daniel Kolesa 2017-02-25 18:56:41 +01:00
parent e90b0868aa
commit e714e5f3fb
2 changed files with 52 additions and 56 deletions

View File

@ -42,24 +42,26 @@ struct Bar {
int main() {
std::vector<int> x = { 5, 10, 15, 20 };
/* prints [5|10|15|20] (using | as delimiter and %s for each item),
/* prints [5|10|15|20] (using | as the delimiter and %s for each item),
* the syntax for ranges is %(CONTENTS%) where CONTENTS is a sequence
* up until and including the last format mark followed by delimiter,
* so for example "%s, " has "%s" for formatting and ", " for delimiter
* and "%d: %s, " has "%d: %s" for format and ", " for delimiter; if you
* need to specify a complicated manual delimiter, you can use the
* up until and including the last format mark followed by a delimiter,
* so for example "%s, " has "%s" for formatting and ", " for the delimiter
* and "%d: %s, " has "%d: %s" for format and ", " for the delimiter; if
* you need to specify a complicated manual delimiter, you can use the
* "FORMAT%|DELIMITER" syntax, where %(%s, %) equals %(%s%|, %)
*/
writefln("[%(%s|%)]", x);
/* prints a range with default format {item, item, item, ...} */
/* prints a range with default format {item, item, item, ...}
* you can enable item escaping by passing the @ flag
*/
writefln("%s", x);
int y[] = { 2, 4, 8, 16, 32 };
/* prints { 2, 4, 8, 16, 32 } using ", " as delimiter and %s for items */
/* prints { 2, 4, 8, 16, 32 } using ", " as the delimiter */
writefln("{ %(%s, %) }", y);
/* nested range printing - prints each item of the main
* range with [ %(%s, %) ] and ",\n" as a delimiter
* range with [ %(%s, %) ] and ",\n" as the delimiter
*/
writefln("[\n%([ %(%s, %) ]%|,\n%)\n]", map(range(10), [](int v) {
return range(v + 1);
@ -72,42 +74,41 @@ int main() {
};
/* prints something like { "baz": 15, "bar": 10, "foo": 5 }, note that
* the tuple is expanded into two formats (using the # flag) and the
* items are escaped automatically (strings and chars)
* items are escaped with the @ flag (applies to strings and chars)
*/
writefln("{ %#(%@s: %d, %) }", m);
/* not escaped, you get { baz: 15, bar: 10, foo: 5} */
writefln("{ %#(%s: %d, %) }", m);
/* the - flag toggles escaping, so you get { baz: 15, bar: 10, foo: 5} */
writefln("{ %-#(%s: %d, %) }", m);
/* no expansion of the items, print entire tuple with default format,
* gets you something like { <"baz", 15>, <"bar", 10>, <"foo", 5> }
* because the default tuple format is <item, item, item, ...>
*/
writefln("{ %(%s, %) }", m);
/* as the - flag toggles escaping on strings and chars, when auto-escape
* is turned on for some reason, you can toggle it for specific items,
* and you can even manually force escaping on things
/* as the @ flag enables escaping on strings and chars,
* you can use it standalone outside of range/tuple format
*/
writefln("not escaped: %s, escaped: %-s", "foo", "bar");
writefln("not escaped: %s, escaped: %@s", "foo", "bar");
/* you can expand tuples similarly to ranges, with %<CONTENTS%> where
* CONTENTS is a regular format string like if the tuple was formatted
* separately with each item of the tuple passed as a separate argument,
* keep in mind that this auto-escapes strings/chars just like ranges
* separately with each item of the tuple passed as a separate argument
*/
std::tuple<std::string, int, float, std::string> tup{
"hello world", 1337, 3.14f, "test"
};
writefln("the tuple contains %<%s, %d, %f, %-s%>.", tup);
writefln("the tuple contains %<%@s, %d, %f, %s%>.", tup);
writefln("auto tuple: %s", tup);
writefln("auto tuple with escape: %@s", tup);
/* formatting a range of tuples, with each tuple expanded
* with a nested tuple expansion inside a range expansion
/* formatting a range of tuples, with each tuple expanded using #
*/
std::tuple<int, float, char const *> xt[] = {
std::make_tuple(5, 3.14f, "foo"),
std::make_tuple(3, 1.23f, "bar"),
std::make_tuple(9, 8.66f, "baz")
};
writefln("[ %#(<%d|%f|%s>%|, %) ]", xt);
writefln("[ %#(<%d|%f|%@s>%|, %) ]", xt);
/* formatting custom objects, the information about the format mark
* is passed into the to_format function and the object can read it

View File

@ -26,7 +26,8 @@ enum format_flags {
FMT_FLAG_ZERO = 1 << 1,
FMT_FLAG_SPACE = 1 << 2,
FMT_FLAG_PLUS = 1 << 3,
FMT_FLAG_HASH = 1 << 4
FMT_FLAG_HASH = 1 << 4,
FMT_FLAG_AT = 1 << 5
};
struct format_error: std::runtime_error {
@ -54,6 +55,7 @@ namespace detail {
case '-': ret |= FMT_FLAG_DASH; fmt.pop_front(); break;
case '+': ret |= FMT_FLAG_PLUS; fmt.pop_front(); break;
case '#': ret |= FMT_FLAG_HASH; fmt.pop_front(); break;
case '@': ret |= FMT_FLAG_AT; fmt.pop_front(); break;
case '0': ret |= FMT_FLAG_ZERO; fmt.pop_front(); break;
case ' ': ret |= FMT_FLAG_SPACE; fmt.pop_front(); break;
default: goto retflags;
@ -187,10 +189,8 @@ namespace detail {
}
struct format_spec {
format_spec(): p_nested_escape(false), p_fmt() {}
format_spec(string_range fmt, bool escape = false):
p_nested_escape(escape), p_fmt(fmt)
{}
format_spec(): p_fmt() {}
format_spec(string_range fmt): p_fmt(fmt) {}
format_spec(char spec, int flags = 0):
p_flags(flags),
@ -265,7 +265,6 @@ struct format_spec {
bool is_tuple() const { return p_is_tuple; }
bool is_nested() const { return p_is_nested; }
bool nested_escape() const { return p_nested_escape; }
template<typename R, typename ...A>
inline R &&format(R &&writer, A const &...args) {
@ -284,6 +283,8 @@ private:
string_range p_nested_sep;
int p_flags = 0;
/* internal, for initial set of flags */
int p_gflags = 0;
int p_width = 0;
int p_precision = 0;
@ -300,7 +301,6 @@ private:
bool p_is_tuple = false;
bool p_is_nested = false;
bool p_nested_escape = false;
bool read_until_dummy() {
while (!p_fmt.empty()) {
@ -319,12 +319,6 @@ private:
bool read_spec_range(bool tuple = false) {
int sflags = p_flags;
/* printing ranges or tuples toggles escaping mode by default,
* but as the dash flag also toggles, it means no change
*/
if (!(sflags & FMT_FLAG_DASH)) {
p_nested_escape = !p_nested_escape;
}
p_fmt.pop_front();
string_range begin_inner(p_fmt);
if (!read_until_dummy()) {
@ -337,6 +331,7 @@ private:
curfmt = p_fmt;
}
p_fmt = curfmt;
/* restore in case the inner spec read changed them */
p_flags = sflags;
/* find delimiter or ending */
string_range begin_delim(p_fmt);
@ -409,10 +404,10 @@ private:
}
/* parse flags */
p_flags = 0;
p_flags = p_gflags;
size_t skipd = 0;
if (havepos || !ndig) {
p_flags = detail::parse_fmt_flags(p_fmt, 0);
p_flags |= detail::parse_fmt_flags(p_fmt, 0);
} else {
for (size_t i = 0; i < ndig; ++i) {
if (p_buf[i] != '0') {
@ -421,10 +416,10 @@ private:
++skipd;
}
if (skipd) {
p_flags = FMT_FLAG_ZERO;
p_flags |= FMT_FLAG_ZERO;
}
if (skipd == ndig) {
p_flags = detail::parse_fmt_flags(p_fmt, p_flags);
p_flags |= detail::parse_fmt_flags(p_fmt, p_flags);
}
}
@ -491,9 +486,6 @@ private:
if (has_precision()) {
n = std::min(n, size_t(precision()));
}
if (p_flags & FMT_FLAG_DASH) {
escape = !escape;
}
write_spaces(writer, n, true);
if (escape) {
writer.put('"');
@ -520,9 +512,6 @@ private:
/* char values */
template<typename R>
void write_char(R &writer, bool escape, char val) const {
if (p_flags & FMT_FLAG_DASH) {
escape = !escape;
}
if (escape) {
char const *esc = detail::escape_fmt_char(val, '\'');
if (esc) {
@ -689,7 +678,7 @@ private:
}
writer.put('<');
write_tuple_val<0, std::tuple_size<T>::value>(
writer, !(flags() & FMT_FLAG_DASH), ", ", val
writer, escape, ", ", val
);
writer.put('>');
return;
@ -700,9 +689,7 @@ private:
throw format_error{"tuples need the '%s' spec"};
}
writer.put('{');
write_range_val(
writer, !(flags() & FMT_FLAG_DASH), false, "%s", ", ", val
);
write_range_val(writer, escape, false, "%s", ", ", val);
writer.put('}');
return;
}
@ -766,7 +753,7 @@ private:
write_arg(writer, idx - 1, args...);
}
} else {
write_val(writer, p_nested_escape, val);
write_val(writer, p_flags & FMT_FLAG_AT, val);
}
}
@ -779,13 +766,19 @@ private:
std::apply([&writer, escape, &fmt](
auto const &...args
) mutable {
format_spec sp{fmt, escape};
format_spec sp{fmt};
if (escape) {
sp.p_gflags |= FMT_FLAG_AT;
}
sp.write_fmt(writer, args...);
}, item);
return;
}
}
format_spec sp{fmt, escape};
format_spec sp{fmt};
if (escape) {
sp.p_gflags |= FMT_FLAG_AT;
}
sp.write_fmt(writer, item);
}
@ -826,7 +819,9 @@ private:
write_range(writer, idx - 1, expandval, sep, args...);
}
} else {
write_range_val(writer, nested_escape(), expandval, rest(), sep, val);
write_range_val(
writer, p_gflags & FMT_FLAG_AT, expandval, rest(), sep, val
);
}
}
@ -834,8 +829,8 @@ private:
void write_tuple_val(
R &writer, bool escape, string_range sep, T const &tup
) const {
format_spec sp{"%s", escape};
sp.write_fmt(writer, std::get<I>(tup));
format_spec sp{'s', escape ? FMT_FLAG_AT : 0};
sp.write_arg(writer, 0, std::get<I>(tup));
if constexpr(I < (N - 1)) {
range_put_all(writer, sep);
write_tuple_val<I + 1, N>(writer, escape, sep, tup);
@ -872,8 +867,8 @@ private:
if (!argpos) {
argpos = argidx++;
}
/* FIXME: figure out a better way */
format_spec nspec(nested(), nested_escape());
format_spec nspec(nested());
nspec.p_gflags |= (p_flags & FMT_FLAG_AT);
if (is_tuple()) {
nspec.write_tuple(writer, argpos - 1, args...);
} else {