まともな国際化を。
minilang は, Unicodeベースです。
ASCII文字だけを特別扱いするのではなく, 漢字・ひらがなを含め, 多様な文字を使えるようにします。
Rubyに合わせて, 大文字で始まるのは定数, 小文字はローカル変数です。とりあえず, 次のようにしてみました。\p{ } は, Unicodeプロパティ名 (Unicode property name) です。
CapitalLetter = \p{Lu} | \p{Lt} NonCapitalLetter = \p{Ll} | \p{Lm} | \p{Lo} | "_" | [\x3400-\x4db5] | [\x4e00-\x9fcc] IdentifierPart = ( CapitalLetter | NonCapitalLetter | \p{Mn} | \p{Mc} | \p{Nd} | \p{Nl} )* ConstIdentifier = CapitalLetter IdentifierPart Identifier = NonCapitalLetter IdentifierPart ( "!" | "?" )?
次の文字は空白として扱います。当然, 全角空白もホワイトスペースです。
WhiteSpace = "\t" | \x000B | \x000C | \p{Zs}
まずどのぐらいのレベルを目指すか, です。
よくある状況として, 一枚のWebページに、いろいろな国の人が書き込んだ内容を表示することを考えてみてください。
1枚の、という時点で、一つの文字列にいろいろな国の文字を含められるようにしなければならない, と分かります。
現実的にこれを実現する方法は, 一つの文字列に複数の文字集合を含めるか, すべての文字を含む文字集合を使うか, のいずれかです。
Emacs は, 古くから, 一つのテキストの中で実用的に多言語を扱えました。
この機能は, 当初は Emacs に対する拡張として提供されていて, 1995年には Emacs 19.28ベースの Mule 2.3 (末摘花) がリリースされていました。末摘花, ということは, その前に5つバージョンがあります。(整理されたページがありました; History of Emacs and Mule)
Mule は, それぞれの文字を, (文字集合を表すコード + その文字集合内のコード) という組で表現したようです。これにより, 一つのテキスト (文字列) 内で, 複数の文字集合の文字を同居させ, 多言語を実現していました。
その後, 1999年頃 (?) には UTF-8 などを扱う Mule-UCS が出ていたようです。
そこからだいぶ経って, Emacs23で, 上の (文字集合 + その中でのコード) というやり方を捨て, 内部コードが Unicode になりました。これが2009年7月.
ruby 1.9.1は2009年1月にリリースされました。くしくも Emacsがバージョン23でUnicodeベースになった年です。
ruby 1.9では, 文字列がその文字コード (エンコーディング) を保持するようになりました。
とても残念なことですが, 文字列が文字コードを持つ, ただそれだけ。多用字系ですらない。こんなものを多言語 (Multilingual) と呼ぶのは, どうかしているとしか思えません。
ruby 1.9 では, Encoding::Converter クラスが文字コードの変換を担当します。
これも, Citrus Project --- a Comprehensive I18n framework Towards Respectable Unix Systems. がはるか昔に通った道。これがいつ頃の話かクリアではありませんが, Solaris7 (1998年11月) よりは後で, 2003年時点には NetBSD-current にマージされていたようです。
大きな文字集合にするか, 小さな文字集合を切り替えるか, ですが, 効率性の面から, 大きな文字集合のほうがいいでしょう。
2012-3年現在、上に書いた要求を実用的にこなせるのは、Unicodeだけです。Character Set Independence (CSI) でもいいのですが、でも、いつもUnicodeを選択している状態になります。
ということで、minilangでは、文字列をUnicodeベースにしてみました。
minilang における「文字」は、UTF-16 code unitで、文字列はその並びです。UTF-16はサロゲートペアがあるので, 1または2 code unitで Unicode code point になります。
サロゲートペアは、開発者 (minilangの利用者) が適切に対応してください。
本物の文字とUnicodeコードポイントは, 一致することもあれば, 一致しないこともあります。Forms of Unicode
ruby 1.9 は, String#[] が文字列を返すように変更されましたが, これもコンテナとのインターフェイスの整合性の観点から, code unitを返すべきです。
どちらかというと、IOへのインパクトのほうが大きいです。
Ruby 1.9のStringはバイト列でもいいので、テキストファイルもバイナリファイルも、IOの各メソッドが同じ型を返せます。
minilangでは、StringをUnicode文字列と決めたので、バイナリファイルでは使えません。
バイト列はどのように表現すべきでしょうか。
ファイル (IO) の用途を考えると、次に分けれるでしょう。
1番目のケースは、単純に、読み書きのときに変換すればよろし。読み込み、書き込みの型はString。
2番目、3番目のケースは、String は使えません。
minilang では, 型 (インターフェイス) が違うので、バイナリファイルを扱うIOと、テキストファイルを扱うTextReaderWriter の二つのクラスに分割しました。
File.open() がその引数に応じて、IO かTextReaderWriterのいずれかのオブジェクトを返します。
IOが軽くなる, という利点もあります。
昔ならロケールにもとづく文字コードと見なすのがよかったでしょうが、現在では、文字コードが決め打ちのファイルも考えられるので、適切ではありません。
外部エンコードが指定されていないときは、バイナリファイルと見なすようにします。