Erlangには、EDocという、javadocとよく似たドキュメンテーション生成ツールがあります。
EDocはErlangソースファイル内のドキュメンテーションコメントからマニュアルを生成します。が、そのままでは日本語が使えないんですよね。
症状
まず、Shift JISのような(非Unicodeな)ネイティブエンコーディングで書くと、
のようなエラーになります。EDoc内で使っているXMLパーザー(xmerl、「Erlang実験室:ErlangのXML処理」を参照)が文句を言っているようです。
error in XML parser: {ucs,{bad_utf8_character_code}}.
しょうがないので、入力ファイル(Erlangソースファイル)内ではUTF-8を使うことにして再実行。
ハァー!?
Bad value on output port 'efile'
Bad value云々はIOシステムの深い部分から出ているエラーメッセージのようです。メモリ内にデータ構造を作ることは成功しています。その証拠に、edoc:get_doc("utf8comments.erl").とすると、XMLデータ構造を取得できます。このデータ構造をファイルに書き出すときに何か不都合が起きているようです。
調査
EDocのソースファイルは、$ERLANG_HOME/lib/edoc-0.7.2/src/ 内にあります。ファイルへの書き出しは、edoc_lib:write_file/4 で行いますが、出力の実行にはファイルディスクリプタFDとテキストデータTextに対してio:put_chars(FD, Text)を呼び出します。
io:put_chars/2に渡されるTextを眺めたら、[..., 32,26085,26412,35486,46]なんてのがあります。IOはバイト単位なので、0から255の整数値でないと"Bad value on output port"ですわな、確かに。
なんですが、http://www.unicode.org/charts/PDF/U4E00.pdfを調べてみると、
26085 = 0x65e5
26412 = 0x672c
35486 = 0x8a9e
- U+65E5は「日」(U4E00.pdfのP.25)
- U+672Cは「本」(U4E00.pdfのp.27)
- U+8A9Eは「語」(U4E00.pdfのp.62)
つまり、XMLバーザーが、Unicode文字番号(コードポイント)そのままの整数値を使ったリスト(DOMのテキストノード相当のデータ構造)を作っていたわけです。
26085のような大きな整数値は、出力の手前で、適当なエンコーディング・スキームによるバイト列(に相当する整数列)に変換しなくてはなりません。
対処
モジュールxmerl_ucs内にto_utf8/1という関数があります。
> xmerl_ucs:to_utf8(26085).
[230,151,165]
> xmerl_ucs:to_utf8([26085,26412,35486]).
[230,151,165,230,156,172,232,170,158]
このxmerl_ucs:to_utf8/1で出力直前に変換をほどこすことにします。to_utf8は入れ子のリストをフラットにしてしまいますが実害はありません。[追記 date="2007-10-02"]もう少しマシなパッチはコチラ[/追記]
修正の差分:
--- edoc_lib.erl.orig Wed Mar 28 03:44:18 2007
+++ edoc_lib.erl Fri Sep 28 10:24:14 2007
@@ -41,7 +41,7 @@
-import(edoc_report, [report/2, warning/2]).
-include("edoc.hrl").
--include("xmerl.hrl").
+-include_lib("xmerl/include/xmerl.hrl").
-define(FILE_BASE, "/").
@@ -660,9 +660,10 @@
Dir1 = filename:join([Dir | packages:split(Package)]),
File = filename:join(Dir1, Name),
filelib:ensure_dir(File),
+ Utf8Text = xmerl_ucs:to_utf8(Text),
case file:open(File, [write]) of
{ok, FD} ->
- io:put_chars(FD, Text),
+ io:put_chars(FD, Utf8Text),
file:close(FD),
ok;
{error, R} ->事実上、Utf8Text = xmerl_ucs:to_utf8(Text),の1行を追加しただけです。-includeから-include_libへの変更はコンパイルを楽にするためです。なお、コマンドラインからの再コンパイルは:
> erlc -o ../ebin edoc_lib.erl
[追記]「Erlang EDoc、一筋縄ではいかないや」、「Erlang EDoc、やんなっちゃう」も参照。[/追記]