Improvement on format strings, take two.
renoX
renosky at free.fr
Thu Feb 22 07:36:52 PST 2007
Here's some code which allow to embed the variable name inside the format string, the syntax is "text %{variable} text " and "text %08d{variable2} text" (inspired from Ruby) IMHO this is better than the normal D format string as the 'embedding' of the variable improves readability: with this syntax one rarely forget a space or a comma around the variable for example.
The code is below, it's main problem is that it doesn't handle correctly non-const char[] variable (if you know how to detect const char[], please tell me).
The code needs improvement in the format string detection to allow '% d{}' and to allow to mix printf style format string and %{}: currently the mix provides weird result: don't mix them.
renoX
import std.stdio;
template FindChar(char[] A, char B) {
static if (A.length == 0) {
const int FindChar = -1;
} else static if (A[0] == B) {
const int FindChar = 0;
} else static if (-1 == FindChar!(A[1..$], B)) {
const int FindChar = -1;
} else {
const int FindChar = 1 + FindChar!(A[1..$], B);
}
}
// match the .*{ part in a %.*{, without any % or space in the .*
template FindRestFmt(char[] A) {
static if (A.length == 0) {
const int FindRestFmt = -1;
} else static if (A[0] == '{') {
const int FindRestFmt = 0;
} else static if (A[0] == ' ') { // TODO in fact % {x} should be authorised: equivalent to "% d",x
const int FindRestFmt = -1;
} else static if (A[0] == '%') {
const int FindRestFmt = -1;
} else static if (-1 == FindRestFmt!(A[1..$])) {
const int FindRestFmt = -1;
} else {
const int FindRestFmt = 1 + FindRestFmt!(A[1..$]);
}
}
// find a %.*{ without any % or space in the .*
template FindStartFmt(char[] A) {
static if (A.length == 0) {
const int FindStartFmt = -1;
} else static if (A[0] != '%') {
const int FindStartFmt = -1;
} else static if (-1 == FindRestFmt!(A[1..$])) {
const int FindStartFmt = -1;
} else {
const int FindStartFmt = 1 + FindRestFmt!(A[1..$]);
}
}
template FmtString(char[] F, A...)
{
// static pragma(msg, "FmtString in, F is '"~F~"'");
static if (F.length == 0)
const char[] FmtString = "\"," ~ Fmt!(A);
else static if (F.length == 1)
const char[] FmtString = F[0] ~ "\"," ~ Fmt!(A);
// need to escape the %% otherwise doesn't work correctly.
else static if (F[0..2] == "%%") //TODO must do full parsing of format strings
const char[] FmtString = "%%" ~ FmtString!(F[2..$],A);
else static if (FindStartFmt!(F) != -1)
static if (FindChar!(F,'}') < FindStartFmt!(F))
static assert(0, "format %[format]{<var>} incorrect in '" ~ F ~ "'");
else static if (F[1] == '{')
const char[] FmtString = "%s\"," ~ F[2..FindChar!(F,'}')] ~ ",\"" ~
FmtString!(F[(1+FindChar!(F,'}'))..$],A);
else
const char[] FmtString = F[0..FindStartFmt!(F)] ~ "\"," ~ F[1+FindStartFmt!(F)..FindChar!(F,'}')] ~ ",\"" ~
FmtString!(F[1+FindChar!(F,'}')..$],A);
else
const char[] FmtString = F[0] ~ FmtString!(F[1..$],A);
// static pragma(msg, "FmtString out is '"~FmtString~"'");
}
template Fmt(A...)
{
static if (A.length == 0)
const char[] Fmt = "";
else static if (is(typeof(A[0]) : char[])) //TODO: how to parse static string only??
const char[] Fmt = "\"" ~ FmtString!(A[0], A[1..$]);
else
const char[] Fmt = A[0].stringof ~ "," ~ Fmt!(A[1..$]);
// static pragma(msg, "Fmt out is '"~Fmt~"'");
}
template Putf(A...)
{
// 0..$-1 to remove the last ','
const char[] Putf = "writef(" ~ Fmt!(A)[0..$-1] ~ ");";
// static pragma(msg, "Putf out is'"~Putf~"'");
}
int main()
{
int x = 10;
int y = 20;
const char[] csimple = "const bonjour simple";
char[] simple = "bonjour simple";
// text containg %
char[] text="percent d: %d percent{x}: %{x}";
const char[] ctext="const percent d: %d percent{x}: %{x}";
writef("BEFORE'");
mixin(Putf!("coucou simple"));
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!("ah que","coucou"));
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!(x));
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!("foo\n",x,y));
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!(x,"foo\n"));
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!(csimple)); // works for const.
// mixin(Putf!(simple)); Doesn't work for non-const char.
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!("%{simple}"));
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!("%s{simple}"));
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!("a is %x",10));
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!("test double percent escape: should seen one percent then {x}: %%{x}"));
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!("test avant %{x} apres"));
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!("test format hexa:%08x{x}after"));
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!("test text containing percent: '%{text}' apres",30));
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!(ctext,30)); //TODO fix the erroneous inversion between the %d and %{x} replacement.
// mixin(Putf!(text,30)); Doesn't work for non-const string.
writef("'AFTER\n");
writef("BEFORE'");
mixin(Putf!("test avant '%{text}'"," apres %{x} encore apres ",x,y));
writef("'AFTER\n");
return 0;
}
More information about the Digitalmars-d
mailing list