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