これは、沖電気の村田さんがJavaHouse MLに投稿された記事を元に修正して寄稿してくださった情報です。
村田@沖電気.関西研です。
> > 日本語を含んだ文字列を扱える
> > parserをJavaCCで記述している方にお伺いしたいのですが、どのよ
> > うな方法で日本語処理の問題を対処されていますか。
>
> options {
> JAVA_UNICODE_ESCAPE = true;
> }
> にしておいて、native2ascii を通して読み込むと言う方法を
> 使っています。
これが普通だと思います。
でも、非常にタイムリーなことに、昨日、ようやく、JavaCC で JDK1.1.x の Readerクラスが使えるようになりました。
以下、ちょっと長いですが。
まず、JavaCC の文法で日本語などのASCII以外の文字を扱うためには、native2ascii などを使って、Java Unicode Escape(\uxxxx) に変換する必要があります。
例) % native2ascii Test.euc.jj Test.jj
次に、日本語などのASCII以外の文字を含んだテキストをparsing する方法は、3通りあります。
解析したい入力ファイルをあらかじめ文法ファイルと同じように native2ascii で変換します。
さらに、文法ファイルの方では、オプションで、
JAVA_UNICODE_ESCAPE=true;を指定します。これで JDK1.0.2,JDK1.1.x 上で動作するパーザができあがります。
解析したい入力ファイルをあらかじめUnicode のファイルに変換します。
% native2ascii inputfile | native2ascii -reverse -encoding UnicodeBigUnmarked > inputfile.unicodeさらに、文法ファイルの方では、オプションで、
UNICODE_INPUT=true;を指定します。これで JDK1.0.2,JDK1.1.x 上で動作するパーザができあがります。
1, 2の方法では、入力ファイルの変換が必要になって、少し手間です。 JDK1.1.x で実行するなら Reader クラスが使えます。 このやりかたでやっと昨日うまく動作したところだったのです。
まず、JavaCC の最新版(注: このドキュメントは0.7pre5をベースにしている)を http://www.suntest.com/JavaCC/ から取り寄せてインストールします。
まず、以下のオプションを指定して、UCode_CharStream.java を生成させます。
UNICODE_INPUT=true; STATIC=true; ←これは任意。そして、このファイルを修正するのでファイル名を変更します。
% mv UCode_CharStream.java UCode_Char_CharStream.javaこのファイルを修正します。
- CharStream を implements するようにする。
- すべての byte 配列を char 配列に変更します。
- すべての InputStream の変数、型を Reader に変更します。
- FillBuff()にバグがあるので、サンプルのように ""throw e;" を catch ブロックの最後に追加します(注: これは0.7pre5の正式版では修正済)。
この修正を行った UCode_Char_CharStream.java のサンプルを、このメールの最後につけておきます。
文法ファイルは、
USER_CHAR_STREAM=true; UNICODE_INPUT=true; STATIC=true; ←これは任意。のオプションをつけて、パーザの起動のところは例えば以下のようにします。PARSER_BEGIN(TestJavaCC) import java.io.*; public class TestJavaCC { public static void main(String args[]) { TestJavaCC reader; if (args.length != 1) { System.out.println( "Usage : java TestJavaCC inputfile"); return; } try { reader = new TestJavaCC( new UCode_Char_CharStream( new InputStreamReader( new FileInputStream(args[0]), "JISAutoDetect"), 0, 0)); reader.Start(); } catch (FileNotFoundException e) { System.out.println("TestJavaCC: File " +args[0]+" not found."); return; } catch (UnsupportedEncodingException e) { System.out.println("TestJavaCC: UnsupportedEncode."); return; } catch (ParseError e) { System.out.println("TestJavaCC: File " +args[0]+" couldn't be parsed."); return; } } } PARSER_END(TestJavaCC)これでもう一度 JavaCC を起動すれば、JDK1.1.x で動作するパーザができあがります。
ぼくは、入力ファイルがそのまま使える3を使っています。 JavaCC も version 0.8 で Reader が標準で使えるようになるようです。 (JDK 1.1 にスイッチすると言っています。)
-- -------------------------------------- 村田稔樹@沖電気工業(株)関西総合研究所 mura@kansai.oki.co.jp --------------------------------------
村田%JavaCCユーザ@沖電気.関西研です。
> 現在、JavaCC(0.8pre1)とそのexampleについてくる。Java1.1.jj > (Java1.1の文法ファイル) を利用したプログラムを作成しています > が、問題を見つけたので相談させてください。 : > [原因] > JavaCCを通した後に出来るASCII_UCodeESC_CharStream.java > 中のreadCharメソッド中の10行目あたりに次のような条件文があります。 > > if (((buffer[bufpos] = c = (char)((char)0xff & ReadByte())) == > '\\')) : > [質問] > > 質問は、自分はこの行がバグだと思うのですが、本当にそうなのか?はい、バグです。 これを直したパッチをこのメールの最後につけておきます。
かなり前につくったパッチで、JavaCC のメーリングリストには流したのですが、このMLには流してなかったような気がします。 (流しておくべきでした。すみません。)
# 風間さんへ、
# http://www.ingrid.org/java/javacc/ のも
# このメールに更新しておいてください。JavaCC が生成する
ASCII_UCodeESC_CharStream.java UCode_CharStream.java UCode_UCodeESC_CharStream.javaの3つのソースに対するパッチです。これらのソースは JavaCC に対するオプションの組合せで、このうちの1つだけが生成されるものですので、必要なものを用いてください。
また、SJIS の Javaソースを JavaCC で解析したいときは、JavaCC.jj で、まずオプションを
options { UNICODE_INPUT=true; JAVA_UNICODE_ESCAPE=true; }というように UNICODE_INPUT を true にして、parser = new JavaCCParser(System.in);としているところを(デフォルトエンコーディングを用いるなら)parser = new JavaCCParser(new InputStreamReader(System.in));または、明示的にはparser = new JavaCCParser(new InputStreamReader(System.in, "SJIS"));というようにする必要があります。 (UnsupportedEncodingExceptionもcatchして。)コンストラクタに Reader が来るならUNICODE_INPUT=true が不要な気もしますが、たしか、これをしないと内部で生成される状態遷移表が1byte文字にのみ対応してしまっていたような気がします。
このパッチは JavaCC 0.8pre1 用に作ったものですが、0.8pre2 でもそのまま使えます。
-- -------------------------------------- 村田稔樹@沖電気工業(株)関西総合研究所 mura@kansai.oki.co.jp --------------------------------------
村田@沖電気.関西研です。
武川@富士ソフトABCさん: > ところで、また別にひっかかった部分があったので > メールします。 : > 今回、村田さんに教えられたオプションで生成される > UCode_UCodeESC_CharStream.java ですが、 > どうもReInitメソッドに対応していないようです。 > > 具体的には、 > public void ReInit(java.io.Reader dstream, > int startline, int startcolumn, int buffersize) > > で、フラグ関係の初期化を行っていません。 > ASCII_UCode_UCodeESC_CharStream.java > の同じメソッド見比べてみてください。 > > というわけで、二つ目のファイルを読込むと > 一つ目のファイルの次の位置を見にいって > 例外が発生してしまうようです。ReInit は使ってませんでしたので、このバグは知りませんでした。
おっしゃるように ASCII_UCodeESC_CharStream.java の ReInit のフラグ関係の初期化を UCode_CharStream.java と UCode_UCodeESC_CharStream.java に コピーしたものの最終的な JavaCC 0.8pre1(or pre2)に対するパッチを 再度添付しておきます。
ありがとうございました。
> >>>コンストラクタに Reader が来るなら > >>>UNICODE_INPUT=true が不要な気もしますが、 > >>>たしか、これをしないと内部で生成される状態遷移表が > >>>1byte文字にのみ対応してしまっていたような気がします。 > > よくわからないのですが、状態遷移表が1バイト文字のみ > だとどのような問題があるのでしょうか?中身の詳細まで見ていないのでよくわかりませんが、 おそらく入ってきた文字によって遷移する先を決定する表が char の下位8ビットだけで作られていて、 比較も下位8ビットだけで行なうのでしょう。
ということは、JavaCC の Lexicalルールに ASCII 以外の文字が入っていると間違った遷移を することがあることになりますね。 (想像上です。たぶん。。。)
> 今回はJavaのソースファイルを入力としているのですが、 > 日本語はコメント部分のみなので、特に気にしなくても > よいと考えていました。下位8ビットが */ となる日本語2文字があれば おかしくなる可能性がありますね。 (そんな文字があるかどうかは知りませんが。。。)
また、日本語はクラス名などにも仕様上は使えますので、 どんな Javaソースファイルでも正しく動作するためには
UNICODE_INPUT=trueにすべきですね。まあ、とりあえず UNICODE_INPUT=true が無難ですね。 別にパフォーマンスにそんなに違いはないでしょうし。
どうせなら UNICODE_INPUT というオプションはなくしてしまって どんなときでも UNICODE_INPUT=true になるようにしたらって JavaCC の作者に提案したんですが、 たしか却下されたような覚えがあります。 まあちょっとでもパフォーマンス(空間、速度)がいい方がいいのでしょう。
-- -------------------------------------- 村田稔樹@沖電気工業(株)関西総合研究所 mura@kansai.oki.co.jp --------------------------------------ReInit も直した新しいパッチ