埋め込みドキュメント
D言語は既に、"契約" や "単体テスト" のコードを実際のコードに埋め込んで、 それら全ての一貫性を常に保ちやすいような設計になっていました。 ここで欠けていたのは、埋め込みドキュメントです。 普通のコメントは自動抽出してマニュアルへと整形するのには あまり向いていませんでした。 ドキュメントをソースコードの中に埋め込むことには、重要な利点があります。 ドキュメントを2度書く必要がなくなることや、 コードとドキュメントを無矛盾に保ちやすくなることが主な利点です。
これに関する既存のアプローチとしては:
- Doxygen は既にD言語対応です
- Java の Javadoc は最もよく知られた例でしょう
- C# の embedded XML
- そのほかの ドキュメント化ツール
Dでの埋め込みドキュメントの目標は:
- 抽出して変換処理せずとも、 埋め込みドキュメントとしても綺麗に読めること
- 自然にそして簡単に書けること。 <タグ> やその他生成後のドキュメントには現れないようなごちゃごちゃした 要素への依存は最小にする。
- コンパイラが構文解析によって把握できる情報を 繰り返し書く必要をなくすこと
- 他の目的の情報抽出の邪魔にならないよう、 HTML埋め込みには頼らないこと
- 既存のDのコメント形式に基づいて、 Dのコードだけを使う他のパーザは完全に独立に実装できるようにすること
- コードと混同されることのないよう、 見た目はコード部分とは異なって見えるようにすること
- もし必要なら、ユーザーが Doxygen やその他のドキュメント抽出ツールも使えるようにすること
仕様
埋め込みドキュメントコメントの形式に関する以下の仕様は、 コンパイラに対してどのように情報が伝わるのか、のみを定めています。 その情報が最終的にどのように使われるのかは実装毎に定義されます。 最終的な出力がHTMLウェブページなのか、manページなのか、あるいは PDFファイルなのか、などは プログラミング言語Dの規格としては定義されません。
処理フェーズ
埋め込みドキュメントコメントは、以下の一連のフェーズに沿って処理されます:
- Lexical - コード中のドキュメントコメント部分が認識されます。
- Parsing - ドキュメントコメントが、特定の宣言に関連付けられ、 結合されます。
- Sections - それぞれのドキュメントコメントを、 幾つかのセクションに分解します。
- 特殊セクションが処理されます。
- 非特殊セクションのHighlightingが実行されます。
- モジュール用の全てのセクションが結合されます。
- マクロ置換が実行され、最終結果が生成されます。
Lexical
埋め込みドキュメントコメントは、以下のうちいずれかの形式で書かれます:
- /** ... */ 先頭スラッシュの後に2つの *
- /++ ... +/ 先頭スラッシュの後に2つの +
- /// 3つのスラッシュ
以下の例は全て埋め込みドキュメントコメントです:
/// これは一行ドキュメントコメントです。 /** これも同じく。 */ /++ これも。 +/ /** これは短いドキュメントコメントです。 */ /** * この行の先頭の * はドキュメントコメントの一部としては扱われません。 */ /********************************* /**の後に続いている余分な*は、 ドキュメントコメントの一部ではありません。 */ /++ これは短いドキュメントコメントです。 +/ /++ + この行の先頭の + はドキュメントコメントの一部としては扱われません。 +/ /+++++++++++++++++++++++++++++++++ /++の後に続いている余分な+は、 ドキュメントコメントの一部ではありません。 +/ /**************** 閉じ*も一部ではありません *****************/
コメント開始部、終了部及び左マージンの余分な * や + は、 無視され、 埋め込みドキュメントとしては処理されません。 以上の形式のどれにも沿っていないコメントは、ドキュメントコメントではありません。
Parsing
ドキュメントコメントはそれぞれ、宣言へと関連付けられます。 ドキュメントコメントと同じ行に空白文字しか残っていない場合、 次に出現する 宣言を参照するものとします。 同じ宣言に適用されるドキュメントコメントが複数あるときは、 それらは結合されます。 宣言に関連付いていないドキュメントコメントは無視されます。 ModuleDeclaration の前にあるドキュメントコメントは、 モジュール全体に適用されます。 ドキュメントコメントが宣言と同じ行でその右側に出現するときは、 その宣言に適用されます。
宣言に対するドキュメントコメントが ditto という識別子のみからなる場合、 同じスコープにある直前のドキュメントコメントが、 その宣言に対しても同様に適用されます。
宣言に関連するドキュメントコメントがひとつも無い場合、 その宣言はドキュメントに出力されない可能性があります。確実に出力したい場合、 空のドキュメントコメントを付けておく必要があります。
int a; /// aに関するドキュメント; bはドキュメントなし int b; /** cとdに関するドキュメント */ /** さらにcとdに関するドキュメント */ int c; /** ditto */ int d; /** eとfに関するドキュメント */ int e; int f; /// ditto /** gに関するドキュメント */ int g; /// さらにgに関するドキュメント /// C と D に関するドキュメント class C { int x; /// C.x に関するドキュメント /** C.y と C.z に関するドキュメン */ int y; int z; /// ditto } /// ditto class D { }
Sections
ドキュメントコメントは、一連の Section で構成されています。 Section とは、行の最初の非空白文字であって、直後に ':' が続いている名前のことを言います。この名前がセクション名です。 セクション名については、大文字小文字が区別されません。
Summary
最初のセクションは Summary です。セクション名はありません。 空行かセクション名が現れるまでの最初の段落です。 Summaryがどれだけ長くても仕様上は問題ありませんが、出来る限り一行に収めるべきです。 Summary セクションは省略可能です。
Description
次の無名のセクションが、Description です。 Summary の後、セクション名かコメント終端が現れるまでの 全ての段落がこのセクションに含まれます。
Description セクションは省略可能ですが、 Summary セクションなしに Description を書くことはできません。
/*********************************** * myfunc 関数の短い要旨で、 * ここがsummaryセクションになります。 * * 概要の説明の第一段落です。 * * 概要の説明の * 第二段落です。ここまでdescriptionセクション。 */ void myfunc() { }
無名セクション Summary と Description の後に、名前つきセクションが続きます。
標準セクション
一貫性やわかりやすさのために、幾つか標準的なセクションが規定されています。 どれも、必須の要素ではありません。
- Authors:
- コードを書いた人のリスト
/** * Authors: Melvin D. Nerd, melvin@mailinator.com */
- Bugs:
- 既知のバグのリスト
/** * Bugs: 負の数では動作しない */
- Date:
- 現在のバージョンの更新日時。
std.date で解析可能な形式で記述する
/** * Date: March 14, 2003 */
- Deprecated:
- 非推奨な宣言について、
代替手段についてなどの説明
/** * Deprecated: 関数 bar() に置き換えられました */ deprecated void foo() { ... }
- Examples:
- 使い方の例
/** * Examples: * -------------------- * writefln("3"); // 標準出力に '3' を表示 * -------------------- */
- History:
- 更新履歴
/** * History: * V1 最初のバージョン * * V2 機能Xを追加 */
- License:
- 著作権のあるコードに関して、ライセンスの情報
/** * License: どんな用途にもご自由にお使いください */ void bar() { ... }
- Returns:
- 関数の返値に関する説明。
void を返す場合、無駄にそうドキュメントを書かないこと
/** * ファイルを読み込みます * Returns: ファイルの内容 */ void[] readFile(char[] filename) { ... }
- See_Also:
- 関連する識別子や、URLのリスト
/** * See_Also: * foo, bar, http://www.digitalmars.com/d/phobos/index.html */
- Standards:
- 何らかの標準規格に準拠した宣言の場合、
そのことに関する説明はここに
/** * Standards: DSPEC-1234 準拠 */
- Throws:
- 送出される可能性のある例外と、例外が起きる場合のリスト
/** * ファイルに書き込みます * Throws: 失敗時,WriteException */ void writeFile(char[] filename) { ... }
- Version:
- 現在のバージョン番号を指定します
/** * Version: 1.6a */
特殊セクション
幾つかのセクションには、特別な意味と構文が定義されています。
- Copyright:
- 著作権表示のセクションです。モジュール宣言のドキュメントとして使われると、
このセクションの内容が COPYRIGHT マクロにセットされます。
Copyrightセクションは、
モジュール宣言に対して使われたときのみ特別な動作になります。
/** Copyright: Public Domain */ module foo;
- Params:
- 関数の引数は、Params セクションにリストアップすることでドキュメント化します。
変数名、そして '=' と続く行が、新しい引数の説明の開始となります。
説明は
複数行にまたがっても構いません。
/*********************************** * foo はこれこれの動作をします。 * Params: * x = は、これに使われます。 * あれではありません。 * y = は、あれに使われます。 */ void foo(int x, int y) { }
- Macros:
- マクロセクションは、Params: セクションと同じ構文で書かれます。
つまり、NAME=value の組が複数並ぶことになります。
NAME がマクロ名で、value
が置換先の文字列です。
/** * Macros: * FOO = 本日は * 晴天なり * BAR = bar * MAGENTA = <font color=magenta>$0</font> */
Highlighting
埋め込みコメント
ドキュメントコメントの中に、 <!-- comment text --> 構文でコメントを入れることができます。 このコメントをネストすることはできません。
埋め込みコード
D のコードは、コードセクションを区切る 三個以上のハイフンを含む行によって埋め込むことができます。
/++++++++++++++++++++++++
+ 我々の関数
+ Example:
+ --------------------------
+ import std.stdio;
+
+ void foo()
+ {
+ writefln("foo!"); /* 文字列を表示 */
+ }
+ --------------------------
+/
コードセクションの中で /* ... */ を使えるようにするため、ここではドキュメント化コメントとして /++ ... +/ 形式を使いました。
埋め込みHTML
ドキュメント化コメントの中にHTMLを埋め込むことも可能で、 HTML出力の際には変更されずそのまま出力されます。 しかし、必ずしも出力形式がHTMLであるとは限らないため、 実際には これは使わないでおくべきでしょう。
/** HTML 埋め込みの例:
* <ol>
* <li> <a href="http://www.digitalmars.com">Digital Mars</a> </li>
* <li> <a href="http://www.classicempire.com">Empire</a> </li>
* </ol>
*/
強調
ドキュメントコメント内の識別子で、関数の引数や、 その他関連する宣言のスコープで定義されている名前となっているものは 出力中で強調表示されます。 強調は、斜体や太字、ハイパーリンクなどで表現されます。 どのように強調されるかは、それが関数引数なのか、型なのか、 あるいはDの予約語なのか、などに依存します。 意図しない強調表示を避けるためには、単語の直前に下線 (_) をつけます。 この下線は出力時に自動的に除去されます。
文字実体
ドキュメントプロセッサにとって特別な意味を持つ文字が幾つか存在します。 混乱を避けるには、 それらの文字を対応するエンティティに置き換えるのがベストでしょう:
文字 | エンティティ |
---|---|
< | < |
> | > |
& | & |
コードセクションの中でこの置換をしておく必要はありません。 あるいは特殊文字の直後に#や英数字が来ない場合も不要です。
ドキュメント生成されない場合
たとえドキュメントコメントがあったとしても、 以下の要素についてはドキュメントは生成されません。
- invariant 不変条件
- postblit
- デストラクタ
- 静的コンストラクタと静的デストラクタ
- Class info, type info, module info
マクロ
ドキュメント化コメントの処理系は、 単純なマクロテキストのプリプロセッサを備えています。 $(NAME) という形がセクションの文章に現れると、 対応するマクロ NAME の指す文字列へと置換されます。 置換後の文字列は再帰的に、さらなるマクロ展開のために走査されます。 再帰的に 同じマクロが同じ引数で出現した場合は、 空のテキストへと置換されます。 置換テキストの境界をまたぐようなマクロ起動は、 展開されません。 マクロ名が未定義の時は、 空文字列へと置換されます。 マクロ展開されずに $(NAME) という文字列そのものを出力したい場合は、 $ の代わりに $ と記述してください。
マクロは引数を取ることもできます。マクロ名の後ろから 閉じ括弧 ')' までの全ての文字列が $0 引数です。 置換文字列の中の $0 が、 引数文字列に置き換えられます。 引数の中にカンマがあると、$1 が最初のカンマまでの文字列を表し、 $2 が最初のカンマから2番目のカンマまでを表し…、 と $9 まで用意されています。 $+ は、最初のカンマから閉じ ')' までの文字列です。 引数文字列内にはネストした括弧や、"" や '' による文字列、 <!-- ... --> コメントやタグも含むことができます。 対応していない括弧が必要な場合は、 ( の代わりに文字実体 (、) の代わりに ) を使用します。
マクロ定義は、 以下の位置から以下の順番で取得されます:
- 定義済みマクロ
- sc.ini や dmd.conf の DDOCFILE 設定での定義
- コマンドラインで指定された *.ddoc ファイルでの定義
- Ddoc によって生成される実行時定義
- Macros: セクションでの定義
マクロの再定義によって、前に定義された同名のマクロを置き換えることができます。 つまり、様々な定義元から取得された一連のマクロ定義は階層構造を なすことになります。
"D_" や "DDOC_" で始まるマクロ名は予約されています。
定義済みマクロ
これらは、Ddoc内部に組み込まれ、 Ddocが整形とハイライト処理を行うために必要な最小の定義セット となっているものです。 定義は、簡単なHTMLを出力するものとなっています。
B = <b>$0</b> I = <i>$0</i> U = <u>$0</u> P = <p>$0</p> DL = <dl>$0</dl> DT = <dt>$0</dt> DD = <dd>$0</dd> TABLE = <table>$0</table> TR = <tr>$0</tr> TH = <th>$0</th> TD = <td>$0</td> OL = <ol>$0</ol> UL = <ul>$0</ul> LI = <li>$0</li> BIG = <big>$0</big> SMALL = <small>$0</small> BR = <br> LINK = <a href="$0">$0</a> LINK2 = <a href="$1">$+</a> LPAREN= ( RPAREN= ) RED = <font color=red>$0</font> BLUE = <font color=blue>$0</font> GREEN = <font color=green>$0</font> YELLOW =<font color=yellow>$0</font> BLACK = <font color=black>$0</font> WHITE = <font color=white>$0</font> D_CODE = <pre class="d_code">$0</pre> D_COMMENT = $(GREEN $0) D_STRING = $(RED $0) D_KEYWORD = $(BLUE $0) D_PSYMBOL = $(U $0) D_PARAM = $(I $0) DDOC = <html><head> <META http-equiv="content-type" content="text/html; charset=utf-8"> <title>$(TITLE)</title> </head><body> <h1>$(TITLE)</h1> $(BODY) </body></html> DDOC_COMMENT = <!-- $0 --> DDOC_DECL = $(DT $(BIG $0)) DDOC_DECL_DD = $(DD $0) DDOC_DITTO = $(BR)$0 DDOC_SECTIONS = $0 DDOC_SUMMARY = $0$(BR)$(BR) DDOC_DESCRIPTION = $0$(BR)$(BR) DDOC_AUTHORS = $(B Authors:)$(BR) $0$(BR)$(BR) DDOC_BUGS = $(RED BUGS:)$(BR) $0$(BR)$(BR) DDOC_COPYRIGHT = $(B Copyright:)$(BR) $0$(BR)$(BR) DDOC_DATE = $(B Date:)$(BR) $0$(BR)$(BR) DDOC_DEPRECATED = $(RED Deprecated:)$(BR) $0$(BR)$(BR) DDOC_EXAMPLES = $(B Examples:)$(BR) $0$(BR)$(BR) DDOC_HISTORY = $(B History:)$(BR) $0$(BR)$(BR) DDOC_LICENSE = $(B License:)$(BR) $0$(BR)$(BR) DDOC_RETURNS = $(B Returns:)$(BR) $0$(BR)$(BR) DDOC_SEE_ALSO = $(B See Also:)$(BR) $0$(BR)$(BR) DDOC_STANDARDS = $(B Standards:)$(BR) $0$(BR)$(BR) DDOC_THROWS = $(B Throws:)$(BR) $0$(BR)$(BR) DDOC_VERSION = $(B Version:)$(BR) $0$(BR)$(BR) DDOC_SECTION_H = $(B $0)$(BR)$(BR) DDOC_SECTION = $0$(BR)$(BR) DDOC_MEMBERS = $(DL $0) DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0) DDOC_CLASS_MEMBERS = $(DDOC_MEMBERS $0) DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0) DDOC_ENUM_MEMBERS = $(DDOC_MEMBERS $0) DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0) DDOC_PARAMS = $(B Params:)$(BR)\n$(TABLE $0)$(BR) DDOC_PARAM_ROW = $(TR $0) DDOC_PARAM_ID = $(TD $0) DDOC_PARAM_DESC = $(TD $0) DDOC_BLANKLINE = $(BR)$(BR) DDOC_PSYMBOL = $(U $0) DDOC_KEYWORD = $(B $0) DDOC_PARAM = $(I $0)
Ddoc は HTML コードを生成するわけではありません。Ddoc は基本書式化マクロを生成し、(その定義済み形式に従って) HTMLへの展開を行います。 HTML以外の形式での出力が必要な場合、 以下のマクロを再定義する必要があります。
B | 引数を太字にする |
I | 引数を斜体にする |
U | 引数に下線をひく |
P | 引数をひとつの段落とする |
DL | 引数は定義リスト |
DT | 引数は定義リスト内の定義名 |
DD | 引数は定義リスト内の定義説明部分 |
TABLE | 引数は表 |
TR | 引数は表の列 |
TH | 引数は表の列のヘッダ項目 |
TD | 引数は表の列のデータ項目 |
OL | 引数は順序つきリスト |
UL | 引数は順序なしリスト |
LI | 引数はリストの要素 |
BIG | 引数を一回り大きく表示 |
SMALL | 引数を一回り小さく表示 |
BR | 改行 |
LINK | 引数をクリック可能なリンクとする |
LINK2 | 引数をクリック可能なリンクとする。第一引数がアドレス |
RED | 引数を赤く表示 |
BLUE | 引数を青く表示 |
GREEN | 引数を緑に表示 |
YELLOW | 引数を黄色く表示 |
BLACK | 引数を黒く表示 |
WHITE | 引数を白く表示 |
D_CODE | 引数をDソースコードとして処理 |
DDOC | 出力全体のテンプレート |
DDOC は、生成されたテキスト全体(Ddocの生成するマクロ BODY で参照できます)が挿入される雛形になるという意味で、特殊なマクロです。 例えばスタイルシートを使うには、 DDOC を次のように再定義します:
DDOC = <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html><head> <META http-equiv="content-type" content="text/html; charset=utf-8"> <title>$(TITLE)</title> <link rel="stylesheet" type="text/css" href="style.css"> </head><body> <h1>$(TITLE)</h1> $(BODY) </body></html>
DDOC_COMMENT は出力ファイルにコメントを挿入するのに使えます。
Dのソースコードのハイライト表示は以下のマクロで行われます:
D_COMMENT | コメントのハイライト |
D_STRING | 文字列リテラルのハイライト |
D_KEYWORD | 予約語のハイライト |
D_PSYMBOL | 現在の宣言名のハイライト |
D_PARAM | 現在の関数引数のハイライト |
ハイライト表示マクロは DDOC_ で始まります。 これらによって表示の個々のパートの整形を制御します。
DDOC_DECL | 宣言のハイライト |
DDOC_DECL_DD | 宣言の説明部のハイライト |
DDOC_DITTO | ditto 宣言のハイライト |
DDOC_SECTIONS | 全セクションのハイライト |
DDOC_SUMMARY | Summaryセクションのハイライト |
DDOC_DESCRIPTION | Descriptionセクションのハイライト |
DDOC_AUTHORS .. DDOC_VERSION | それぞれ対応する標準セクションのハイライト |
DDOC_SECTION_H | 非標準セクションのセクション名のハイライト |
DDOC_SECTION | 非標準セクションの内容のハイライト |
DDOC_MEMBERS | クラスや構造体などのメンバのデフォルトのハイライト |
DDOC_MODULE_MEMBERS | モジュールメンバのハイライト |
DDOC_CLASS_MEMBERS | クラスメンバのハイライト |
DDOC_STRUCT_MEMBERS | 構造体メンバのハイライト |
DDOC_ENUM_MEMBERS | 列挙対メンバのハイライト |
DDOC_TEMPLATE_MEMBERS | テンプレートメンバのハイライト |
DDOC_PARAMS | 関数引数セクションのハイライト |
DDOC_PARAM_ROW | name=value 関数引数のハイライト |
DDOC_PARAM_ID | 引数nameのハイライト |
DDOC_PARAM_DESC | 引数valueのハイライト |
DDOC_PSYMBOL | 特定のセクションから参照されている宣言名のハイライト |
DDOC_KEYWORD | D予約語のハイライト |
DDOC_PARAM | 関数引数のハイライト |
DDOC_BLANKLINE | 空行の挿入 |
例えば、DDOC_SUMMARY を次のように再定義できます:
DDOC_SUMMARY = $(GREEN $0)
これで、Summaryセクションが全て緑色になります。
sc.ini/dmd.conf の DDOCFILE によるマクロ定義
マクロ定義のテキストファイルを作っておくことができ、 sc.ini や dmd.conf で指定可能です:
DDOCFILE=myproject.ddoc
コマンドライン指定による .ddoc ファイルからのマクロ定義
拡張子 .ddoc を持つファイルが DMD のコマンドラインに渡ると、 順番に読み込まれ処理されます。
Ddoc の生成するマクロ定義
マクロ名 | 内容 |
---|---|
BODY | ドキュメントテキスト本文が入ります |
TITLE | モジュール名が入ります |
DATETIME | 現在の日時が入ります |
YEAR | 現在の年が入ります |
COPYRIGHT | モジュールコメントの Copyright: セクションの内容が入ります |
DOCFILENAME | 生成されるファイルの名前が入ります |
他のドキュメント生成に Ddoc を使う
当初は Ddoc は、埋め込みコメントからのドキュメント生成用に作られました。 しかしながら、 その他の一般的なドキュメントの処理にも使用できます。 埋め込みコメント以外でも利用する利点としては、 Ddoc のマクロ機能や、 ソースコードの構文ハイライト機能があります。
.d ソースファイルが Ddoc という文字列で開始している場合、 Dのソースコードではなく一般的なドキュメントファイルとして扱われます。 "Ddoc" 文字列の直後から、ファイル終端か "Macros:" セクションまでがドキュメントとなります。 テキストには、---行 で囲まれた埋め込みソースコード部分以外は、 自動的構文ハイライト処理は行われません。 マクロ処理だけが実行されます。
このページを含め、 Dのドキュメントそのものも、多くはこのようにして生成されています。 そのようなドキュメントには、一番下に Ddoc で生成された旨表示してあります。(※訳注:原文と違い日本語訳は直接HTMLを書いているため、この表示はありません。(^^;)
参考文献
CandyDoc は、マクロとスタイルシートによって Ddoc の出力をカスタマイズする非常に良い例です。