proposal: a new string litteral to embed variables in a string

H. S. Teoh hsteoh at quickfur.ath.cx
Wed Nov 6 14:32:14 PST 2013


On Wed, Nov 06, 2013 at 10:50:35PM +0100, Daniel Davidson wrote:
> On Wednesday, 6 November 2013 at 21:37:14 UTC, Dmitry Olshansky
> wrote:
[...]
> >Challenge accepted.
> >50 lines in current D2:
> >
> >import std.algorithm, std.conv, std.array, std.uni, std.stdio;
> >
> >auto embed(Vars...)(string tmpl)
> >{
> >    auto app = appender!string();
> >    alias Put = void delegate();
> >    Put[string] fns;
> >    foreach(i, v; Vars)
> >    {
> >        //strangely v.stringof is "v" - BUG/FEATURE ? :)
> >	//may do better then to!string
> >        fns[Vars[i].stringof] = (){ app.put(to!string(v)); };
> >    }
> >
> >    auto slice = tmpl;
> >    while(!slice.empty)
> >    {
> >        auto anchor = find(slice, '@');
> >        app.put(slice[0..$-anchor.length]);
> >        if(anchor.empty)
> >            break;
> >        slice = find!(a => !isAlpha(a))(anchor[1..$]);
> >        auto name = anchor[1..$-slice.length];
> >        if(!slice.empty)
> >        {
> >            if(name in fns)
> >                fns[name]();
> >        }
> >    }
> >    return app.data;
> >}
> >
> >void main(){
> >    int a = 2;
> >    float b = 3.14;
> >    string c = "hello";
> >    auto x = q{var=@a, second=@b, third=@c!}.embed!(a,b,c);
> >    writeln(x);
> >    int age = 45;
> >    string address = "Fleet st. 15";
> >    dstring fieldName = "my_field";
> >    auto y = q{
> >      "firstName": "@firstName",
> >      "age": @age,
> >      "address": {
> >        @fieldName: "@address",
> >      }
> >    }.embed!(age, fieldName, address);
> >    writeln(y);
> >}
> >
> >Prints as expected:
> >var=2, second=3.14, third=hello!
> >
> >      "firstName": "",
> >      "age": 45,
> >      "address": {
> >        my_field: "Fleet st. 15",
> >      }
> >
> >
> >Now isn't it already nice beyond proportions?
> 
> That is cool, but it is not DRY. Interpolation is well established
> and clearly adds value - else why would almost every language
> provide it?
> 
> You should not have to pass (age, fieldName, address) into it -
> since it already has it in the string. That is the real challenge,
> get those variables and interpolate them. Maybe I am missing
> something, but the format approach advocated by you and dicebot
> means breaking up strings (ugly and potentially error prone) and/or
> repeating yourself.
[...]

Challenge accepted. ;-) Here is an adaptation of Dmitri's code that
doesn't require you to explicitly pass in variables:

	import std.algorithm;
	import std.array;
	import std.stdio;
	import std.uni;
	
	string interpolate(string fmt) {
	     // The bulk of this code is copied from Dmitri's version;
	     // thanks, Dmitri!
	     auto app = appender!string();
	
	     app.put("\"");
	     auto slice = fmt;
	     while (!slice.empty)
	     {
	         auto anchor = find(slice, '@');
	
		 // Escape unsafe characters
	         foreach(c; slice[0..$-anchor.length]) {
	            if (c == '\"')
	                app.put("\\\"");
	            else
	                app.put(c);
	         }
	
	         if (anchor.empty)
	             break;
	         slice = find!(a => !isAlpha(a))(anchor[1..$]);
	         auto name = anchor[1..$-slice.length];
	         if (!slice.empty)
	         {
	             app.put("\"~" ~ name ~ "~\"");
	         }
	     }
	     app.put("\"");
	     return app.data;
	}
	
	void main() {
	    string a = "aye";
	    string b = "bee";
	    string c = "cee";
	
	    writeln(mixin(interpolate(q{
	        "myobject" : {
	            "fieldA": "@a",
	            "fieldB": "@b",
	            "fieldC": "@c",
	        }
	    })));
	}

Here's the output:

	"myobject" : {
	    "fieldA": "aye",
	    "fieldB": "bee",
	    "fieldC": "cee",
	}
    

Is that acceptable to you? :)

Of course, the above code is just a proof-of-concept; it doesn't handle
integer or other types of variables, and it currently only escapes '"',
(it should be extended to also escape '\', etc.). But all of these would
be easily addressed by a proper implementation using std.conv and by
handling metacharacters properly. The point is that you *can* do string
interpolation in D without needing language-level support.


T

-- 
May you live all the days of your life. -- Jonathan Swift


More information about the Digitalmars-d mailing list