LoginSignup
11
11

More than 5 years have passed since last update.

メンバ転送で内部DSLをつくる

Posted at

 やっはろー。もう日付は過ぎていますけど、飛び入りで参加します。

 Ruby の method_missing や Scala の applyDinamic のように、D言語でも opDispatch を定義することで存在しないメソッドを呼んだときの動作をフックすることができます。

import std.stdio, std.string;

struct Foo {
    string opDispatch(string name)() const {
        return ["Call", name, "!"].join(" ");
    }

    string opDispatch(string name)(string arg) const {
        return ["Call", name, "with", arg, "!"].join(" ");
    }
}

void main() {
    auto foo = new Foo();
    writeln(foo.bar()); // Call bar !
    writeln(foo.baz("qux")); // Call baz with qux !
}

テンプレートの第一引数にメソッド名を取る他は普通のメソッドと同じです。

 これを利用して、XML を出力する内部 DSL を書いてみました。

import std.algorithm, std.ascii, std.range, std.regex, std.string;

struct XMLGenerator {
    struct XMLElement {
        string elem;
        string[string] attr;
        string text;
        XMLElement[] children;

        string toString(int level = 0) const {
            auto indent = "  ".repeat(level).join;
            auto attribute = (attr.length)? " " ~ attr.keys.map!(k => k ~ `="` ~ attr[k] ~ `"`).join(" "): "";

            return [
                indent ~ "<" ~ elem ~ attribute ~ ">",
                (text.length)? indent ~ "  " ~ text: null,
                children.map!(x => x.toString(level + 1)).join(newline),
                indent ~ "</" ~ elem ~ ">"
            ].filter!(x => x).join(newline);
        }
    }

    alias E = XMLElement;

    static E opDispatch(string elem)(E[] children) const {
        return XMLGenerator.opDispatch!elem(null, null, children);
    }

    static E opDispatch(string elem)(string text, E[] children = null) const {
        return XMLGenerator.opDispatch!elem(null, text, children);
    }

    static E opDispatch(string elem)(string[string] attribute = null, string text = null, E[] children = null) const {
        return E(elem.replaceAll(regex("^_"), ""), attribute, text, children);
    }
}

こんな感じで使います。

import std.stdio;

void main() {
    alias XML = XMLGenerator;
    writeln(
        XML.html([
            XML.head([
                XML.title("D Programming Language")
            ]),
            XML._body([
                XML.h1([
                    XML.font(["color": "#ff0000"], "D "),
                    XML.span("Programming Language")
                ]),
                XML.b("Hello world!")
            ])
        ])
    );
}
<html>
  <head>
    <title>
      D Programming Language
    </title>
  </head>
  <body>
    <h1>
      <font color="#ff0000">
        D 
      </font>
      <span>
        Programming Language
      </span>
    </h1>
    <b>
      Hello world!
    </b>
  </body>
</html>

mixin と組み合わせたら化けそうです。お粗末さまでした。

Operator Overloading - D Programming Language

11
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11