Diary?

2009-07-24
Fri

(09:34)

一昨日あたりから、また ANTLR を使ったタスクに突入しているのだが、今日は軽く ANTLR ネタ。いや、 ANTLR に限った話じゃないか。

ANTLR 2.x 系(3.x は知らん)では、多分次のように Parser のサブクラスのコードを文法ファイルに埋め込んでしまうことが結構あると思う。

class HogeParser extends Parser;
{
    // ホスト言語のコード
    // 主に共通で使われるメソッドなどの定義
}
parse:(
    t1:TOKEN1 {
        // TOKEN1 を処理するコード
    }
    | t2:TOKEN2 {
        // TOKEN2 を処理するコード
    }
)*

ところがこれだと以下のような問題が出てきてしまう。

  • 文法ファイルがゴチャゴチャになって保守性が下がる
  • ANTLR の文法ファイルにコードを埋め込むため、エディタのシンタックスハイライトなどが動作しない
  • 文法への変更ではなくトークンのハンドリングルーチンの変更でも文法ファイルの更新となり、余計なビルドプロセスが走る

というわけで、文法ファイルからなるべくホスト言語のロジックを切り分けたいと思うのが普通だろう。最初にパッと思いつくやり方は、次のようにトークン毎に処理メソッドを分けてしまうことだ。

class HogeParser extends Parser;
{
    def handle_token1(self, token): raise NotImplementedError
    def handle_token2(self, token): raise NotImplementedError
}
parse:(
    t1:TOKEN1 {
        self.handle_token1(t1);
    }
    | t2:TOKEN2 {
        self.handle_token2(t2);
    }
)*

あとはホスト言語の方で HogeParser を継承して、宣言されたメソッドを実装していけばよい。要するにジェネレーションギャップパターンの変化球みたいなもんだ。というかこの程度のレベルだったら Parser のベースクラスも自動生成できそうなもんだが。

Creative Commons
この怪文書はクリエイティブ・コモンズ・ライセンスの元でライセンスされています。引用した文章など Kuwata Chikara に著作権のないものについては、それらの著作権保持者に帰属します。