ユニコード文字列のリテラルを使う [OCaml]
抽象データ型とリテラルというのがそもそも相容れないのかもしれないが Camomile の文字列型にはリテラルがない。ソースを書いた時点で固定的な情報をランタイムに計算するのはちょっと癪なのでいろいろ考えてみたのだけど、結局文字列型としては UTF8.t を、リテラルとしては通常の OCaml の文字列リテラルを使って abstraction barrier を破って UTF8.t と認識させるのが一番簡単であろうという結論に達した。
ただしこれだとソースファイルも UTF-8 で書かないといけないという問題がある。これを解決するために Java でいう native2ascii のような簡単なツールを作った。
(* wchar2latin.ml *)
open CamomileLibrary.Default.Camomile;;
open CamomileLibrary.Default.Camomile.CharEncoding;;
open Queue;;
open Printf;;
let from_encoding_name = ref "UTF-8";;
let files = Queue.create ();;
let set_from x = from_encoding_name := x in
let set_file x = Queue.add x files in
let spec = [("-encoding", Arg.String set_from, "Encoding of the source file")] in
Arg.parse spec set_file "usage:";;
let from_encoding = CharEncoding.of_name !from_encoding_name in
let in_chan = if is_empty files then stdin else open_in (take files) in
let out_chan = if is_empty files then stdout else open_out (take files) in
let in_uchan = new CharEncoding.in_channel from_encoding in_chan in
try
while true do
let uc = in_uchan#get () in
try
output_char out_chan (UChar.char_of uc)
with UChar.Out_of_range ->
let s = UTF8.init 1 (fun _ -> uc) in
String.iter (fun c -> fprintf out_chan "\\%03d" (int_of_char c)) s
done
with End_of_file -> close_out out_chan
これを使うと任意の文字エンコーディングでリテラルが書かれたソースを UTF8 文字列とみなしてコンパイルできる。例えば以下のようなソースを書いてみる。
open CamomileLibrary.Default.Camomile;;
let a = "表示" in
print_int (UTF8.length a);;
ここで「表示」は Shift_JIS とする。これをコンパイル、実行しても当然うまくいかない。
KURO-BOX% ocamlc bigarray.cma camomile.cma tryme.ml File "tryme.ml", line 3, characters 10-12: Warning X: illegal backslash escape in string. KURO-BOX% ./a.out Fatal error: exception Invalid_argument("UTF8.length")
先ほどのツールをプリプロセッサに使うとうまくいく。
KURO-BOX% ocamlc -pp './wchar2latin -encoding SHIFT_JIS' bigarray.cma camomile.cma tryme.ml KURO-BOX% ./a.out 2
ツールが出力する生の情報としては以下のようになっている。
KURO-BOX% ./wchar2latin -encoding SHIFT_JIS tryme.ml open CamomileLibrary.Default.Camomile;; let a = "\232\161\168\231\164\186" in print_int (UTF8.length a);;
コメント 0