<div dir="ltr"><br><div class="gmail_extra"><br><br><div class="gmail_quote">On Wed, Nov 6, 2013 at 2:32 PM, H. S. Teoh <span dir="ltr"><<a href="mailto:hsteoh@quickfur.ath.cx" target="_blank">hsteoh@quickfur.ath.cx</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div class="im">On Wed, Nov 06, 2013 at 10:50:35PM +0100, Daniel Davidson wrote:<br>

> On Wednesday, 6 November 2013 at 21:37:14 UTC, Dmitry Olshansky<br>
> wrote:<br>
</div>[...]<br>
<div><div class="h5">> >Challenge accepted.<br>
> >50 lines in current D2:<br>
> ><br>
> >import std.algorithm, std.conv, std.array, std.uni, std.stdio;<br>
> ><br>
> >auto embed(Vars...)(string tmpl)<br>
> >{<br>
> >    auto app = appender!string();<br>
> >    alias Put = void delegate();<br>
> >    Put[string] fns;<br>
> >    foreach(i, v; Vars)<br>
> >    {<br>
> >        //strangely v.stringof is "v" - BUG/FEATURE ? :)<br>
> >     //may do better then to!string<br>
> >        fns[Vars[i].stringof] = (){ app.put(to!string(v)); };<br>
> >    }<br>
> ><br>
> >    auto slice = tmpl;<br>
> >    while(!slice.empty)<br>
> >    {<br>
> >        auto anchor = find(slice, '@');<br>
> >        app.put(slice[0..$-anchor.length]);<br>
> >        if(anchor.empty)<br>
> >            break;<br>
> >        slice = find!(a => !isAlpha(a))(anchor[1..$]);<br>
> >        auto name = anchor[1..$-slice.length];<br>
> >        if(!slice.empty)<br>
> >        {<br>
> >            if(name in fns)<br>
> >                fns[name]();<br>
> >        }<br>
> >    }<br>
> >    return app.data;<br>
> >}<br>
> ><br>
> >void main(){<br>
> >    int a = 2;<br>
> >    float b = 3.14;<br>
> >    string c = "hello";<br>
> >    auto x = q{var=@a, second=@b, third=@c!}.embed!(a,b,c);<br>
> >    writeln(x);<br>
> >    int age = 45;<br>
> >    string address = "Fleet st. 15";<br>
> >    dstring fieldName = "my_field";<br>
> >    auto y = q{<br>
> >      "firstName": "@firstName",<br>
> >      "age": @age,<br>
> >      "address": {<br>
> >        @fieldName: "@address",<br>
> >      }<br>
> >    }.embed!(age, fieldName, address);<br>
> >    writeln(y);<br>
> >}<br>
> ><br>
> >Prints as expected:<br>
> >var=2, second=3.14, third=hello!<br>
> ><br>
> >      "firstName": "",<br>
> >      "age": 45,<br>
> >      "address": {<br>
> >        my_field: "Fleet st. 15",<br>
> >      }<br>
> ><br>
> ><br>
> >Now isn't it already nice beyond proportions?<br>
><br>
> That is cool, but it is not DRY. Interpolation is well established<br>
> and clearly adds value - else why would almost every language<br>
> provide it?<br>
><br>
> You should not have to pass (age, fieldName, address) into it -<br>
> since it already has it in the string. That is the real challenge,<br>
> get those variables and interpolate them. Maybe I am missing<br>
> something, but the format approach advocated by you and dicebot<br>
> means breaking up strings (ugly and potentially error prone) and/or<br>
> repeating yourself.<br>
</div></div>[...]<br>
<br>
Challenge accepted. ;-) Here is an adaptation of Dmitri's code that<br>
doesn't require you to explicitly pass in variables:<br>
<br>
        import std.algorithm;<br>
        import std.array;<br>
        import std.stdio;<br>
        import std.uni;<br>
<br>
        string interpolate(string fmt) {<br>
             // The bulk of this code is copied from Dmitri's version;<br>
             // thanks, Dmitri!<br>
             auto app = appender!string();<br>
<br>
             app.put("\"");<br>
             auto slice = fmt;<br>
             while (!slice.empty)<br>
<div class="im">             {<br>
                 auto anchor = find(slice, '@');<br>
<br>
</div>                 // Escape unsafe characters<br>
                 foreach(c; slice[0..$-anchor.length]) {<br>
                    if (c == '\"')<br>
                        app.put("\\\"");<br>
                    else<br>
                        app.put(c);<br>
                 }<br>
<br>
                 if (anchor.empty)<br>
<div class="im">                     break;<br>
                 slice = find!(a => !isAlpha(a))(anchor[1..$]);<br>
                 auto name = anchor[1..$-slice.length];<br>
                 if (!slice.empty)<br>
                 {<br>
</div>                     app.put("\"~" ~ name ~ "~\"");<br>
                 }<br>
             }<br>
             app.put("\"");<br>
             return app.data;<br>
        }<br>
<br>
        void main() {<br>
            string a = "aye";<br>
            string b = "bee";<br>
            string c = "cee";<br>
<br>
            writeln(mixin(interpolate(q{<br>
                "myobject" : {<br>
                    "fieldA": "@a",<br>
                    "fieldB": "@b",<br>
                    "fieldC": "@c",<br>
                }<br>
            })));<br>
        }<br>
<br>
Here's the output:<br>
<br>
        "myobject" : {<br>
            "fieldA": "aye",<br>
            "fieldB": "bee",<br>
            "fieldC": "cee",<br>
        }<br>
<br>
<br>
Is that acceptable to you? :)<br>
<br>
Of course, the above code is just a proof-of-concept; it doesn't handle<br>
integer or other types of variables, and it currently only escapes '"',<br>
(it should be extended to also escape '\', etc.). But all of these would<br>
be easily addressed by a proper implementation using std.conv and by<br>
handling metacharacters properly. The point is that you *can* do string<br>
interpolation in D without needing language-level support.<br>
<span class=""><font color="#888888"><br></font></span></blockquote><div><br></div><div>As I mentioned in my OT, this is what I already have implemented ("<span style="font-size:12.727272033691406px;font-family:arial,sans-serif">I've actually already implemented this feature via a mixin, and find it extremely useful, but ...</span><span style="font-size:12.727272033691406px;font-family:arial,sans-serif"> "); and I did take care of proper escaping, etc. I am using it extensively, however the requirement for using a mixin makes things uglier than they should: </span></div>
<div><span style="font-size:12.727272033691406px;font-family:arial,sans-serif"><br></span></div><div><span style="font-size:12.727272033691406px;font-family:arial,sans-serif">* cryptic ctfe error msgs upon wrong variable names</span><br>
</div><div><span style="font-size:12.727272033691406px;font-family:arial,sans-serif">* mixin can't be used in UFCS chains; additional () nesting</span></div><div><span style="font-family:arial,sans-serif;font-size:12.727272033691406px">to the point that I only use it in cases where the alternative is uglier.</span><br>
</div><div><span style="font-family:arial,sans-serif;font-size:12.727272033691406px">* potentially more strain on ctfe</span></div><div><span style="font-family:arial,sans-serif;font-size:12.727272033691406px">* mixins in general should be used sparingly</span></div>
<div><span style="font-family:arial,sans-serif;font-size:12.727272033691406px"><br></span></div><div><font face="arial, sans-serif">Sure we can use existing mixins to fill this need, but to me this is exactly the same as the situation with lambda literal syntax:</font></div>
<div><font face="arial, sans-serif"><br></font></div><div><font face="arial, sans-serif">a=>a*2 </font></div><div><font face="arial, sans-serif">instead of </font></div><div><span style="font-family:arial,sans-serif;font-size:12.727272033691406px">(a){return a*2;}</span></div>
<div><span style="font-family:arial,sans-serif;font-size:12.727272033691406px"><br></span></div><div><span style="font-family:arial,sans-serif;font-size:12.727272033691406px">or lazy parameters:</span></div><div><span style="font-family:arial,sans-serif;font-size:12.727272033691406px">void fun(lazy string a)</span></div>
<div><span style="font-family:arial,sans-serif;font-size:12.727272033691406px">vs:</span></div><div><div><span style="font-family:arial,sans-serif;font-size:12.727272033691406px">void fun(string delegate() a)</span></div>
</div><div><span style="font-family:arial,sans-serif;font-size:12.727272033691406px"><br></span></div><div><font face="arial, sans-serif">A little of syntax sugar can provide huge benifits.</font></div><div><font face="arial, sans-serif">It's use case would apply to all assert error messages, DSLs etc.</font></div>
<div><span style="font-family:arial,sans-serif;font-size:12.727272033691406px"><br></span></div><div><span style="font-size:12.727272033691406px;font-family:arial,sans-serif"><br></span></div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
<span class=""><font color="#888888">
<br>
T<br>
<br>
--<br>
May you live all the days of your life. -- Jonathan Swift<br>
</font></span></blockquote></div><br></div></div>