I've made a mock version of Silverlight in D2. It is meant to output HTML, but since I don't know HTML yet, its current output is in. HTML is not the reason I'm here, though. The goal of my project was to create a framework that uses XAML and MVVM to create a site bound to a viewmodel. It doesn't do too much and isn't dynamic as of yet, but right now I'm just at the first milestone. Before I go any further with the project I'd like some feedback on what I have so far. Specifically, my use of template mixins to achieve polymorphism and override base class methods, even if they contain the exact same code (this problem is explained in code-comments). But at the same time this is my first forray into D and I'm sure there are other things I could use help with, so I'm open to any suggestions.The code is on GitHub for anyone interested. I know it's not very useful but I'm having fun with it.In my opinion, the code explains itself with comments, but I can add more if it's unclear.module darklight;import std.stdio: writeln;enum string __module = darklight;//A sample view model class//Can compound classes other than OddVM as well, this is just a democlass OddVM {    string text;    OddVM inner;    OddVM list[];    string amethod(string name) {        return my name is: ~name;    }    this(string te) {        this.text = te;    }}//showing off the different controls, as well as binding features and some of the xml capacityenum string _xml = <StackPanel name=viewModel.text        binding=viewModel.inner>    <TextBox name=\itext1\        text=binding.text>    </TextBox>    <TextBox name=\itext2\        binding=viewModel.inner        text=binding.text        />    <TextBox name=\itext3\        binding=viewModel.inner        text=binding.amethod(\robot\) />    <TextBox name=\itext4\        binding=viewModel.inner.inner        text=\too deep, would error if I tried binding\/>    <ListPanel name=\somename\        binding=viewModel.list>        <TextBox name=\itextinside\            text=binding.text/>    </ListPanel>    <DoubleText name=\double\        binding=viewModel.inner/></StackPanel>;//viewModel when accessed provides the passed in vm//binding is the new one if set, or the same as viewModel if notint main(string[] args) {    //create a sample viewModel    OddVM  vm = new OddVM (bound);    vm.inner = new OddVM (bound inner);    vm.inner.inner = new OddVM (bound inner inner);    OddVM vm1 = new OddVM(first);    OddVM vm2 = new OddVM(second);    OddVM vm3 = new OddVM(third);    vm.inner.list = [vm1, vm2, vm3];    //The vm could be any type of object    //It just needs the proper variables as defined in the xml    auto res = Control.BuildControl!(_xml)(vm);    writeln(res.content());    //change some values in the viewmodel    vm.text = new text;    vm.inner.text = new inner text;    //can add things to a list and the templates won't be disturbed    vm.inner.list ~= vm1;    //need to rebuild controls to bind new values    res = Control.BuildControl!(_xml)(vm);    writeln(res.content());    return 0;}abstract class Control {    string name;    mixin template codegen(string _xml) {        //provides access to some parsed sections of the xml        static const string extr[] = parsexml!(_xml);        static const string _tag = extr == [] ?  : extr[0];        static const string _attr = extr == [] ?  : extr[1];        static const string _inner = extr == [] ?  : extr[2];        static const string _rem = extr == [] ?  : extr[3];        static const string _elem = extr == [] ?  : extr[4];    }    mixin template setvarsgen() {        private auto setvars(string _xml, T)(T viewModel) {            //for access to attr            mixin codegen!(_xml);            //add the string of assignments for binding to the viewModel            mixin(assignstring(_attr));            return binding;        }    }    //This is in a mixin template because when I want it to be called by a subclass    //the subclass needs to have it's own implementation of it or the method bindings will crash.    //ex: if this was called from TextBox without beind re-mixed-in, it would fail on assigning this.text    //This might be a failure of polymorphism with templated methods, or just me    mixin template extractgen(string _newxml = null) {        static if (_newxml == null) {            //a base extraction method, sets variables to the bindings outlined in xml            private auto extract(string _xml, T)(T viewModel) {                this.setvars!(_xml)(viewModel);                return this;            }        } else {            // This one is for controls with custom xaml, usually you shouldn't need to override this            // But you'll probably have to do Control.extractgen!(_newxaml) to bypass any overrides            private auto extract(string _xml, T)(T viewModel) {                // The var setting from this gets overwritten, but we need the binding                auto binding = setvars!(_xml)(viewModel);                // Distinction here is that _newxml replaces xml                auto ret = super.extract!(_newxml)(binding);                // This lets xml bindings (ex: name) from the outside override anything on the inside                setvars!(_xml)(viewModel);                return ret;            }        }    }    mixin setvarsgen!();    mixin extractgen!();    //Outputs the html/other things representing the control    abstract pure string content();    static auto BuildControl(string _xml, T)(T viewModel) {        mixin codegen!(_xml);        mixin(_tag~ control = cast(~_tag~) Object.factory(\~__module~.~_tag~\););        return control.extract!(_xml)(viewModel);    }}class TextBox : Control {    string text;    //the new class variable text means these two have to be re-added    mixin setvarsgen!();    mixin extractgen!();    pure string content() {        return <p ~this.name~> ~ this.text ~ <p>\n;    }}class StackPanel : Control {    Control controls[];    mixin template extractgen() {        //overrides the extract method, adds inner controls        private auto extract(string _xml, T)(T viewModel) {            mixin codegen!(_xml);            auto binding = setvars!(_xml)(viewModel);            this.controls = this.chain!(_inner)(binding);            return this;        }    }    mixin setvarsgen!();    mixin extractgen!();    pure string content() {        string ret;        foreach(contr; controls) {            ret ~= contr.content();        }        return <div ~this.name~>\n~ret~<div>\n;    }    private Control[] chain(string _xml, T)(T viewModel) {        mixin codegen!(_xml);        //this 5 is completely arbitrary, limits you w.r.t. the xml        static if (_rem.length > 5) {            return [cast(Control)BuildControl!(_elem)(viewModel)] ~ chain!(_rem)(viewModel);        } else {            return [cast(Control)BuildControl!(_xml)(viewModel)];        }    }}//example specialization of existing controlclass ListPanel : StackPanel {    //this is necessary because templates don't override well...    //without this, the this.chain! call doesn't call ListPanel.chain! it calls StackPanel.chain!    //I described this earlier    mixin extractgen!();    //setvarsgen! doesn't need to be here since there are no new class variables    private Control[] chain(string _xml, T)(T viewModel) {        Control ret[];        foreach(vm; viewModel) {            ret ~= BuildControl!(_xml)(vm);        }        return ret;    }}//example of user defined xml for a new controlclass DoubleText : StackPanel {    enum _xmlstring =     <StackPanel name=\doubletext type\>        <TextBox name=\itext1\            text=binding.text>        </TextBox>        <TextBox name=\itext2\            text=\danger zone!\>        </TextBox>    </StackPanel>;    //this will call super.extract, while using the new xml    //Control. is necessary for this control and sometimes others because their parents overwrite extractgen! without defining a string constructor    //in other cases you might need to rewrite extract!    mixin Control.extractgen!(_xmlstring);}/* * THE CODE BEYOND THIS POINT IS EMBARRASSING AND SHAKY, BUT SERVES IT'S PURPOSE * It's for slightly picky xml parsing * It's not as bad as it once was... */ //returns a string of code to be mixin'd, that will do the binding //text=\hi\ -> this.text=hi; //text=method() -> this.text=method(); //binding=something -> auto binding = something; //if not defined, auto binding = viewModel; is added.  //binding is always set on the first line //could additionally be modified to allow for method calls without assignment, but not sure if neededpure string assignstring(string content) {    string ret;    int start, mid, i;    bool tracking, pastmid, strtracker, bound;    void falsify() {        tracking = false;        pastmid = false;        strtracker = false;    }    pure bool isformatchar(char a) {        return (a == ' ' || a == '\n' || a=='\t' || a=='<' || a=='>' || a=='/' || a=='\\');    }    string bindingconcat(string ret, string content, int start, int mid, int i) {        string first = content[start..mid];        string second = content[mid+1..i];        assert(first~=~second == content[start..i]);        if (first == binding) {            bound = true;            return auto binding = ~second~;\n~ret;        } else {            return ret~this.~content[start..i]~;\n;        }    }    for(; i < content.length; i++) {        if (!tracking && !isformatchar(content[i])) {            start = i;            tracking = true;        }        if (tracking && content[i]=='=') {            pastmid = true;            mid = i;            if (i+1 < content.length && content[i+1] == '') {                strtracker = true;                i++;            }        }        else if (tracking && !pastmid && isformatchar(content[i])) {            falsify();        }        else if (tracking && pastmid && strtracker && content[i] == '') {            falsify();            ret ~= this.~content[start..i+1]~;\n;        }        else if (tracking && pastmid && !strtracker && isformatchar(content[i])) {            falsify();            ret = bindingconcat(ret,content,start,mid,i);        }    }    if (tracking && pastmid) {        ret = bindingconcat(ret,content,start,mid,i);    }    if (!bound) {        ret = auto binding = viewModel;\n ~ ret;    }    return ret;}template parsexml(string xml) {    enum parsexml = xmlparse(xml);}pure string[] xmlparse(string xml) {    //tag, attr, inner, remainder, elem    int b[5]; //|<jkh|  |>  |</jkh|>;    int open;    int i = -1;    while(++i < xml.length) {        if (xml[i] == '<') {            b[0] = i;            open = 1;            break;        }    }    while(++i < xml.length) {        if ((xml[i] == ' ' || xml[i] == '\n') && b[1] == 0) {            b[1] = i;        }        if (xml[i] == '>') {            break;        };        if (open == 1 && xml[i] == '/' && xml[i+1] == '>') {            --open;            b[3] = i;            b[4] = i+1;            break;        }    }    b[1] = b[1] == 0 ? i : b[1];    b[2] = i;    while(open > 0 && ++i < xml.length-1) {        if (xml[i] == '<') {            open += xml[i+1]=='/' ? -1 : 1;        }        if (xml[i] == '/' && xml[i+1] == '>') {            --open;        }    }    b[3] = b[3] == 0 ? i : b[3];    while (b[4] == 0 && ++i < xml.length) {        if (xml[i] == '>') {            break;        }    }    b[4] = b[4] == 0 ? i : b[4];    //|<jkh|  |>  |</jkh|>;    string ret[]; //tag, attr, inner, remainder, elem    ret ~= xml[b[0]+1..b[1]];    ret ~= xml[b[1]..b[2]];    ret = ret ~ ((b[2]==b[3]) ?  : xml[b[2]+1..b[3]]);    ret ~= xml[b[4]+1..$];    ret ~= xml[b[0]..b[4]+1];    if (b[2] != b[3]) {        assert(ret[0] == xml[b[3]+2..b[3]+(b[1]-b[0])+1]);    }    return ret;}The output when this is run is the following, which contains two runs of the control builder, to demonstrate viewModel changes. I know it's not real HTML; that's something I have yet to learn.<div bound><p itext1>bound inner<p><p itext2>bound inner inner<p><p itext3>my name is: robot<p><p itext4>too deep, would error if I tried binding<p><div somename><p itextinside>first<p><p itextinside>second<p><p itextinside>third<p><div><div double><p itext1>bound inner inner<p><p itext2>danger zone!<p><div><div><div new text><p itext1>new inner text<p><p itext2>bound inner inner<p><p itext3>my name is: robot<p><p itext4>too deep, would error if I tried binding<p><div somename><p itextinside>first<p><p itextinside>second<p><p itextinside>third<p><p itextinside>first<p><div><div double><p itext1>bound inner inner<p><p itext2>danger zone!<p><div><div>
Mixin and template-heavy code for a Silverlight clone
template meta programming;mixins;d;silverlight
Here's what I've settled with in case anyone is interested:Using the factory pattern I was able to build an injectable factory. Then using dependency injection I forward the factory through all the control extraction steps. This has the advantage of allowing a control class to instantiate a sub-control inside itself without worrying about importing the proper modules (and avoiding circular references).The extract method is now able to access all the member variables because it is passed the type (as ForwardRefT), instead of using the class's own, bypassing the lack of polymorphism for templated methods. Thankfully, this also removes the need to add template mixins everywhere, which was ugly.Now that you've read it and are fully prepared for the wackyness to come, here is the code://This factory is mixed in once, at the highest level of your web app.//This way it can reference every module with controls in it and inject itself into them, giving them that ability as well.mixin template FactoryGen() {    import darklight.parse : parse_tag;    struct ControlFactory    {        static auto Extract(string _xml, T)(T viewModel)        {            //parse_tag takes <StackPanel ...> ... </StackPanel> and returns StackPanel            enum Ty = parse_tag!(_xml);            mixin(return ~Ty~.extract!(ControlFactory, _xml, ~Ty~, T)(viewModel););        }    }}abstract class Control{    override abstract string toString();    static auto extract(alias F, string _xml, ForwardRefT, T)(T viewModel)    {        ForwardRefT self = new ForwardRefT();        //parse_attr takes <StackPanel {0}> ... </StackPanel> and returns {0}        mixin(assignstring(parse_attr!(_xml)));         return self;    }}abstract class ContainerControl(int I=0) : Control{    static if (I == 0)    {        Control[] controls;    }    else    {        Control[I] controls;    }    override string toString()    {        string content;        static if (I == 0)        {            content = reduce!(a~b.toString())(,this.controls);        }        else        {            foreach(control; this.controls)            {                content ~= control.toString();            }        }        return format(`<div>%s</div>`, content);    }}abstract class StackControl(int I=0) : ContainerControl!(I){    static auto extract(alias F, string _xml, ForwardRefT, T)(T viewModel)    {        ForwardRefT self = new ForwardRefT();        mixin(assignstring(parse_attr!(_xml)));        //parse_inner takes <StackPanel ...> {0} </StackPanel> and returns {0}        //subparse takes <1></1><2></2> and returns [<1></1>,<2></2>]        enum contents = subparse(parse_inner!(_xml));        static assert(I == 0 || contents.length == I);        Chain!(F, contents)(self, vm);        return self;    }    static void Chain(alias F, alias inners, ForwardRefT, T)(ForwardRefT self, T viewModel)    {        static if (inners.length > 0)        {            Control next = F.Extract!(inners[0])(viewModel);            static if (I == 0)            {                self.controls ~= next;            }            else            {                self.controls[I-inners.length] = next;            }            Chain!(F, inners[1..$])(self, viewModel);        }    }}class StackPanel : StackControl!() { }class ListPanel : ContainerControl!(0){       static auto extract(alias F, string _xml, ForwardRefT, T)(T viewModel)    {        ForwardRefT self = new ForwardRefT();        //assignstringPlus is likeassignstring but makes sure there is a variable named list, defaulting it to null if it isn't in the xml        mixin(assignstringPlus(parse_attr!(_xml),list,null));        enum contents = subparse(parse_inner!(_xml));        static assert(contents.length == 1);        foreach(item; list)        {            self.controls ~= F.Extract!(contents[0])(item);        }        return self;    }}I'm very pleased with the results as it makes user code much more manageable and cleaner without the mixins. The extract method may look nasty, but the only really scary part is the method signature. Users shouldn't ever have to write another extract method, but if they do it should be much easier than it was in the past.