やっはろー。もう日付は過ぎていますけど、飛び入りで参加します。
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
と組み合わせたら化けそうです。お粗末さまでした。