www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - PHP-style (embedded variables) print statements

reply Don Clugston <dac nospam.com.au> writes:
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

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?
May 31 2007
next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Don Clugston" <dac nospam.com.au> wrote in message 
news:f3lt8d$2je5$1 digitalmars.com...
 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).

You can't print out the string using %s?
 Opinions? Is something like this worth considering as an alternative to
 writefln, and to Tango's C# formatting and whisper syntax?

Not to burst your bubble, but I've just never found variable interpolation to be all that useful, mostly because it's not really obvious (unless you have some kind of fancy code highlighter) when something's being interpolated, and because there's already syntaxes that look fine to me. Some people like it though.
May 31 2007
parent reply Don Clugston <dac nospam.com.au> writes:
Jarrett Billingsley wrote:
 "Don Clugston" <dac nospam.com.au> wrote in message 
 news:f3lt8d$2je5$1 digitalmars.com...
 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).

You can't print out the string using %s?

The problem is that it's so easy to forget the %s, and have a latent bug that lies undetected.
 
 Opinions? Is something like this worth considering as an alternative to
 writefln, and to Tango's C# formatting and whisper syntax?

Not to burst your bubble, but I've just never found variable interpolation to be all that useful, mostly because it's not really obvious (unless you have some kind of fancy code highlighter) when something's being interpolated, and because there's already syntaxes that look fine to me. Some people like it though.

Yes, these are the main issues. I know that people have requested it in the past (and it was also touted as something Nemerle can do which D can't <g>). Personally, I thought it was one of very few interesting features of PHP. There's probably a syntax which would be a bit more obvious, but still... BTW, do you feel the same way about the C# syntax? Is it obvious enough? mixin interpolation: mixin(dprint("current=$i next=$(i+1) const=$(k*3) $str")); C# (available in Tango): print("current={0} next={1} const={2} {3}", i, i+1, k+3, str)); Phobos: writef("current=", i, " next=", i+1, " const=", k+3, " ", str);
May 31 2007
parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
Don Clugston wrote:

 Jarrett Billingsley wrote:
 "Don Clugston" <dac nospam.com.au> wrote in message
 news:f3lt8d$2je5$1 digitalmars.com...
 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).

You can't print out the string using %s?

The problem is that it's so easy to forget the %s, and have a latent bug that lies undetected.
 
 Opinions? Is something like this worth considering as an alternative to
 writefln, and to Tango's C# formatting and whisper syntax?

Not to burst your bubble, but I've just never found variable interpolation to be all that useful, mostly because it's not really obvious (unless you have some kind of fancy code highlighter) when something's being interpolated, and because there's already syntaxes that look fine to me. Some people like it though.

Yes, these are the main issues. I know that people have requested it in the past (and it was also touted as something Nemerle can do which D can't <g>). Personally, I thought it was one of very few interesting features of PHP. There's probably a syntax which would be a bit more obvious, but still... BTW, do you feel the same way about the C# syntax? Is it obvious enough? mixin interpolation: mixin(dprint("current=$i next=$(i+1) const=$(k*3) $str")); C# (available in Tango): print("current={0} next={1} const={2} {3}", i, i+1, k+3, str));

To compare, you should use Stdout("current=")(i)(" next=")(i+1)(" const=")(k*3)(" ")(str); Since I'm not too happy about $vars , I would find it more interesting if it was mixin(dprint("current={i} next={i+1,5} const={k*3:C} {str})); which would allow for the specifiers in the C# format, like padding (,5) or print as currency (:C). Even more interesting would be to use Sprint to make it usable in logging. Would have to get rid of the mixin, though :) -- Lars Ivar Igesund blog at http://larsivi.net DSource, #d.tango & #D: larsivi Dancing the Tango
May 31 2007
next sibling parent Don Clugston <dac nospam.com.au> writes:
Lars Ivar Igesund wrote:
 Don Clugston wrote:
 
 Jarrett Billingsley wrote:
 "Don Clugston" <dac nospam.com.au> wrote in message
 news:f3lt8d$2je5$1 digitalmars.com...
 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).


that lies undetected.
 Opinions? Is something like this worth considering as an alternative to
 writefln, and to Tango's C# formatting and whisper syntax?

interpolation to be all that useful, mostly because it's not really obvious (unless you have some kind of fancy code highlighter) when something's being interpolated, and because there's already syntaxes that look fine to me. Some people like it though.

the past (and it was also touted as something Nemerle can do which D can't <g>). Personally, I thought it was one of very few interesting features of PHP. There's probably a syntax which would be a bit more obvious, but still... BTW, do you feel the same way about the C# syntax? Is it obvious enough? mixin interpolation: mixin(dprint("current=$i next=$(i+1) const=$(k*3) $str")); C# (available in Tango): print("current={0} next={1} const={2} {3}", i, i+1, k+3, str));

To compare, you should use Stdout("current=")(i)(" next=")(i+1)(" const=")(k*3)(" ")(str); Since I'm not too happy about $vars , I would find it more interesting if it was mixin(dprint("current={i} next={i+1,5} const={k*3:C} {str})); which would allow for the specifiers in the C# format, like padding (,5) or print as currency (:C).

It's all possible. And that mountain of localisation stuff would not be linked in, if it was never used in any format strings. The :C syntax might be confusing, though, because it looks like the ?: operator. "const={b?c:C:C}" Even more interesting would be to use Sprint to
 make it usable in logging.

Should be trivial.
 
 Would have to get rid of the mixin, though :)

May 31 2007
prev sibling parent reply Dejan Lekic <dejan.lekic gmail.com> writes:
I pray to God I will never have to write code like:

      Stdout("current=")(i)(" next=")(i+1)(" const=")(k*3)(" ")(str);

May 31 2007
next sibling parent "Bent Rasmussen" <incredibleshrinkingsphere gmail.com> writes:
Agreed. The Phobos and Tango methods are highly readable.

"Dejan Lekic" <dejan.lekic gmail.com> wrote in message 
news:f3ngam$2d4b$1 digitalmars.com...
I pray to God I will never have to write code like:

      Stdout("current=")(i)(" next=")(i+1)(" const=")(k*3)(" ")(str); 


Jun 01 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Dejan Lekic wrote:
 I pray to God I will never have to write code like:
 
      Stdout("current=")(i)(" next=")(i+1)(" const=")(k*3)(" ")(str);


I think the Phobos syntax could probably be added to Tango using variadic templates: void opCall( Args... )( Args args ) { foreach( arg; args ) this.print( arg ); } Something like that. But I personally don't find the "whisper" syntax to be particularly annoying. Sean
Jun 01 2007
parent Don Clugston <dac nospam.com.au> writes:
Sean Kelly wrote:
 Dejan Lekic wrote:
 I pray to God I will never have to write code like:

      Stdout("current=")(i)(" next=")(i+1)(" const=")(k*3)(" ")(str);


I think the Phobos syntax could probably be added to Tango using variadic templates: void opCall( Args... )( Args args ) { foreach( arg; args ) this.print( arg ); } Something like that.

Unfortunately, that adds template bloat, since every combination of types gets its own instantation... But I personally don't find the "whisper" syntax
 to be particularly annoying.

I don't like it myself, but aside from macros/mixins it's the only typesafe option not requiring RTTI or template bloat. With macros, we should be able to do much the same thing with writefln syntax. (ie, convert it to a sequence of whisper calls).
Jun 02 2007
prev sibling next sibling parent reply Gregor Richards <Richards codu.org> writes:
Dejan Lekic wrote:
 I pray to God I will never have to write code like:
 
      Stdout("current=")(i)(" next=")(i+1)(" const=")(k*3)(" ")(str);


Are you kidding me? This is the most readable syntax available in D to date. - Gregor Richards
Jun 01 2007
parent Leandro Lucarella <llucax gmail.com> writes:
Gregor Richards, el  1 de junio a las 12:11 me escribiste:
 Dejan Lekic wrote:
I pray to God I will never have to write code like:
     Stdout("current=")(i)(" next=")(i+1)(" const=")(k*3)(" ")(str);


Are you kidding me? This is the most readable syntax available in D to date.

It still sucks =/ I'm sorry, I don't want to troll, but I think event printf() has a much nicer syntax. -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ .------------------------------------------------------------------------, \ GPG: 5F5A8D05 // F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05 / '--------------------------------------------------------------------' "Lidiar" no es lo mismo que "holguear"; ya que "lidiar" es relativo a "lidia" y "holguear" es relativo a "olga". -- Ricardo Vaporeso
Jun 02 2007
prev sibling parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Dejan Lekic wrote:
 I pray to God I will never have to write code like:
 
      Stdout("current=")(i)(" next=")(i+1)(" const=")(k*3)(" ")(str);


"Mummy, why are my eyes bleeding?" -- Daniel -- int getRandomNumber() { return 4; // chosen by fair dice roll. // guaranteed to be random. } http://xkcd.com/ v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Jun 01 2007
prev sibling next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Don Clugston wrote:
 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"));
 

 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?

That's pretty cool. I would probably use that over other alternatives, but not if it requires the mixin(...) syntax to use. --bb
May 31 2007
prev sibling next sibling parent reply Witold Baryluk <baryluk mpi.int.pl> writes:
http://smp.if.uj.edu.pl/~baryluk/d/echo/echo.d
http://smp.if.uj.edu.pl/~baryluk/d/echo/echo.html



-- 
Witold Baryluk
MAIL: baryluk smp.if.uj.edu.pl, baryluk mpi.int.pl
JID: movax jabber.autocom.pl
May 31 2007
parent Don Clugston <dac nospam.com.au> writes:
Witold Baryluk wrote:
 http://smp.if.uj.edu.pl/~baryluk/d/echo/echo.d
 http://smp.if.uj.edu.pl/~baryluk/d/echo/echo.html

We're obviously thinking the same way! Yours is better presented than mine. I use a significant trick, though: the text which gets mixed in, contains a _second_ mixin. This allows the CTFE function to know the type of each variable, and its value if it is a compile-time constant. (although use of the value is currently incomplete, and disabled in the code I posted). Consequently, my version does not require RTTI (yours does, inside writefln). Feel free to steal ideas from it.
May 31 2007
prev sibling next sibling parent Ary Manzana <ary esperanto.org.ar> writes:
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 `));
 }

May 31 2007
prev sibling parent reply Unknown W. Brackets <unknown simplemachines.org> writes:
I hate it when people use this in PHP and Perl, it can be un-greppable and
there's so much ambiguity when using arrays and similar.

Still, pretty cool to be able to do it in D.  Just shows D's strengths.

-[Unknown]


Don Clugston Wrote:

 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?

Jun 01 2007
parent reply Don Clugston <dac nospam.com.au> writes:
Unknown W. Brackets wrote:
 I hate it when people use this in PHP and Perl, it can be un-greppable 

This is a really important problem. I think it is relevant to almost any domain-specific language. (Find all references to variable xxx). Inside an IDE, you could theoretically grep on the source the compiler sees after mixins are evaluated, but it's pretty nasty otherwise.
 and there's so much ambiguity when using arrays and similar.
 
 Still, pretty cool to be able to do it in D.  Just shows D's strengths.
 
 -[Unknown]
 
 Don Clugston Wrote:
 
 With CTFE and text mixins, it is now reasonably straightforward to 
 implement PHP-style 'dollar' embedded variables, in a type-safe manner.


Jun 02 2007
parent "Unknown W. Brackets" <unknown simplemachines.org> writes:
That becomes impractical when you have real-time code completion to deal 
with (example: struct.member... "$struct.member" should then work too 
but it's unlikely to be practical to figure that per keystroke.)

-[Unknown]


Don Clugston wrote:
 Unknown W. Brackets wrote:
 I hate it when people use this in PHP and Perl, it can be un-greppable 

This is a really important problem. I think it is relevant to almost any domain-specific language. (Find all references to variable xxx). Inside an IDE, you could theoretically grep on the source the compiler sees after mixins are evaluated, but it's pretty nasty otherwise.
 and there's so much ambiguity when using arrays and similar.

 Still, pretty cool to be able to do it in D.  Just shows D's strengths.

 -[Unknown]

 Don Clugston Wrote:

 With CTFE and text mixins, it is now reasonably straightforward to 
 implement PHP-style 'dollar' embedded variables, in a type-safe manner.



Jun 05 2007