Thursday, 2 September 2010

Tokenizer の続き

昨日今日と余裕があったので Tokenizer を、もう少し調べてみました。

StreamTokenizer は、当然だけど細かい仕様変更に耐えない。例えば、""中に改行を許すとかね。それ以外は、簡単に書けてよろしい。

Scanner は、Delimiter を食っちゃう。食われたものに関する情報を取る方法がない。nextByte() で取って来ることはできるんだろうけど、それじゃぁ、うれしくないです。Delimiter を""とか"."に指定することもできないらしい。"[\\s]"で良いだろうとか思うけど、そうすると、 f() とかが分離できない。かと言って"[()]"をdelimiterにすると、() が食われちゃう。

自分で書いた Matcher を使う奴は、ほとんどScannerと同じ実装になっていて、おそらく、当時(2007年)同じ問題にぶつかって、Scannerを読んだんじゃなかろうかと思います。Scanner を継承して変更と言う手もあるんだが、JDK依存経験的に避けた方が良いので、別に書いたのだと思う。まぁ、今見ると「そうじゃないだろ」ってところはあるんだけど、まぁ、動いているから良いか。

  protected String next(Pattern pattern) {
    String s = null;
    while(true) {
      Boolean match = scan.usePattern(pattern).lookingAt();
      if (scan.hitEnd()) {
        if (extendInput()) {
            scan.reset();
            continue;
        }
      }
      if (match) {
        s = scan.group(1);
        cb.position(cb.position()+scan.end());
      }
      if (scan.hitEnd()) {
        extendInput();scan.hitEnd();
      }
      return s;

パターンに()を置いて、group(1)を使うのは変。このlookingAt() を見つけるのに時間がかかった記憶がある。

CharBuffer を一つ一つ見る方法は、割と簡単に書けました。でも、Matcher より遅い感じがする。計ってないけど。

  while(true) {
    if (!hasRemaining()) return nextToken;
    while(Character.isSpaceChar(ch)) {
      if (!hasRemaining()) return nextToken;
      ch = nextChar();
    }
    CharBuffer w = CharBuffer.allocate(BufferSize);
    if (Character.isJavaIdentifierStart(ch)) {

とかやるんだが、Character.isSpaceChar(ch)が見るからに遅そう〜 ch==' ' とかすれば早いのかも :-p

ただ、これだと、数値のパターンとか結構複雑なので、そのあたりに苦労しそうです。

Tokenizerの実装を三種類試すのに、interface切って、共有部分を super class に抜き出して。最近は別classのが流行りかな。 この手のRefactoringは、Eclipseで楽勝。楽しいので、はなうた歌いながらやってたら、伯母に画面をのぞきこまれて「画面は全然楽しくない」と言われた。いや、楽しいんですが。

No comments: