PHP-style (embedded variables) print statements
Ary Manzana
ary at esperanto.org.ar
Thu May 31 08:48:05 PDT 2007
Can this be used for non-constant values?
Don Clugston escribió:
> With CTFE and text mixins, it is now reasonably straightforward to
> implement PHP-style 'dollar' embedded variables, in a type-safe manner.
>
> For example, the attached code (rough draft) allows you to write things
> like:
>
> int i;
> const k = 8;
> char [] str;
> mixin(dprint("current=$i next=$(i+1) const=$(k*3) $str\n"));
>
> which is typesafe, converts all compile-time constants directly into the
> format string, and converts everything else into a simple printf() call.
> The mixin line above becomes
>
> printf("current=%d next=%d const=24 %.*s\n", i, i+1, str);
>
> Note that unlike a tuple solution, it does not cause any template bloat.
> Also doesn't have some of writefln's pitfalls (the case where a string
> contains a % character).
>
> Incidentally, this could do a lot better than printf, because it could
> categorise the expressions. Eg, if there are no floating point
> variables, it could translate it to a simple print function which
> doesn't have the FP conversion code. Ditto for outputting arrays and
> objects. Other optimisations are possible - eg, it could also estimate
> how much buffer space is going to be required.
>
> Downsides:
> (1) need to use "mixin()" until we get macros.
> (2) doesn't look like anything that's in D right now.
> (3) how are IDE's going to know when a string contains embedded variables?
>
> Opinions? Is something like this worth considering as an alternative to
> writefln, and to Tango's C# formatting and whisper syntax?
>
>
> ------------------------------------------------------------------------
>
> module DPrint;
>
> /// Convert an integer of type T to string.
> /// Note: this function is CTFE-compatible, but not very efficient at runtime
> char [] ct_itoa(T)(T x)
> {
> char [] s="";
> static if (is(T==byte)||is(T==short)||is(T==int)||is(T==long)) {
> if (x<0) {
> s = "-";
> x = -x;
> }
> }
> do {
> s = cast(char)('0' + (x%10)) ~ s;
> x/=10;
> } while (x>0);
> return s;
> }
>
> // Templates to return string representation of floating-point constant.
> // This works, but only for floating-point constants -- should really create a
> // CTFE-compatible ftoa().
> char [] mixin_ftoa(real x)() {
> return `"` ~ x.stringof ~ `"`;
> }
>
> char [] mixin_ftoa(ireal x)() {
> return `"` ~ x.stringof ~ `"`;
> }
> char [] mixin_ftoa(creal x)() {
> return `"` ~ x.stringof ~ `"`;
> }
>
> /** Evaluate a textual expression of type T, and return it as a textual literal.
> *
> */
> char [] dollar_convert(T)(char [] x)
> {
> static if (is(T ==long))
> return `"%Ld"`;
> else static if (is(T==ulong)) return `"%Lu"`;
> else static if (is(T:int)) return `"%d"`;
> else static if (is(T:uint)) return `"%u"`;
> else static if (is(T==real)) return `"%Lg"`;
> else static if (is(T:double)) return `"%g"`;
> else static if (is(T==char[])) return `"%.*s"`;
> else return x;
> }
>
> private {
> // BUGS: Many other UTF chars should be allowed inside identifiers.
> bool isIdentifierChar(char c)
> {
> return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || (c=='_');
> }
> }
>
> /** Evaluate a string containing embedded 'dollar expressions'.
> *
> * Given a string containing embedded expression marked with 'dollar' indicators,
> * returns a string which, when mixed in, will evaluate to a new string with all
> * of the expressions evaluated.
> * The expressions can be strings, or any integral type, or a floating-point
> * constant (real, imaginary or complex) expression.
> * Dollar expressions consist of a dollar sign, followed by a variable name
> * (the first non-identifier character marks the end), or a dollar sign followed
> * by an expression in parentheses. Two consecutive dollar signs become a dollar
> * character.
> */
> char [] dprint(char [] s)
> {
> char [] result=`printf(("`;
> char [] vars="";
> int i=0;
> while (i<s.length) {
> if (i<s.length-1 && s[i]=='$') {
> if (s[i+1]=='$') {
> result~='$'; // replace "$$" with '$'.
> i+=2;
> } else { // replace "$expression" with the expression
> int start=i+1;
> int parenCount=0;
> if (s[start]=='(') {
> ++start;
> parenCount++;
> }
> for (i=start; i<s.length; ++i) {
> if (s[i]=='(') ++parenCount;
> if (s[i]==')') { --parenCount; if (parenCount==0) break; }
> if (parenCount==0 && !isIdentifierChar(s[i])) break;
> }
> // Evaluate the expression, and convert it to a string
> vars ~="," ~ s[start..i];
> result ~= `"~mixin(dollar_convert!(typeof(`
> ~ s[start..i] ~ `))("` ~ s[start..i] ~ `"))~"`;
>
>
> if (s[i]==')') ++i;
> }
> }
> else {
> if (s[i]=='"') result ~= `\"`; // Retain embedded quotes.
> else result ~= s[i];
> ++i;
> }
> }
> return result~ `").ptr`~vars ~ `);`;
> }
>
> //-----------------
> // Example
>
> void main()
> {
> int k=56;
> const int k2=7;
> const double q = 4.1432e58;
> const q2 = 4.1432e58;
> char [] x ="this is a string";
> const x2 ="this is a const string";
> mixin(dprint(`The value of q2 is: $(q2*7) and k dollars is $$$k, but k2 is $k2 followed by $(k2 *5+1) and the floating point expression is $(q*2.18), string x is: $x and $x2 `));
> }
More information about the Digitalmars-d
mailing list