Ptron: ブロックリストを用いて外部ファイルにフィルタを記述する

2010-07-15 19:28:03 | Proxomitron
※ この記事には一部「無駄」な情報が含まれています(巻末参照…笑)。


さて前回は「可読性の悪さをどう取り除くか」という課題を残して話を終えた。ご覧の通り HTTP Header filter editor は入力欄がすべて単一行テキストボックスで構成されている。



フィルタが長くなってくると見えないどころか処理の流れすら掴めない。最大の欠点はデバッグが非常にやりづらいことである。可読性の悪さからメモ帳などに書いたコードをテストのたびにテキストボックスへコピペしている開発者も多いことだろう。この作業は煩わしい以外の何者でもないが、かくいう私も最初はそうであった(苦笑)。

この抜け道としてお薦めしたいのがブロックリストを使う方法である。「そんなのとっくに知っているよ」という人もいるだろうが、これ以外にもう一つトリックを用意しているので、煙たがらずに読み進めていただければと思う。

ブロックリストを使うと外部ファイルに記述したフィルタをヒアドキュメント的に読み込むことができる。今回の例でいうと、ゼロパディング部分のフィルタをテキストファイルとして保存し、ブロックリストに登録する。拡張子が .prx になっているのは筆者が愛用するテキストエディタの都合であり、それ以外に特別な意味は持たない。またブロックリスト名が func/date となっているのは前々回の log/iTunes と同様、Block List Configuration におけるソートを考慮して付けたものである。



あとは filter editor 側で $LST コマンドを使い、このフィルタを読み込ませるだけ。



これで filter editor にベタで入力したときと同じ処理を行わせることができる。またブロックリストはフィルタの実行時にリロードされるため、メモ帳などで編集作業を行っている場合、変更が即時反映されるというメリットもある。

ただこの「リロード」が曲者で、フィルタが実行されるたびに「ファイルの読み込み」というオーバーヘッドが発生することは念頭に置いておくべきであろう。これを避けるには URL フィルタをしっかりと掛けておき、必要なとき以外はファイルの読み込みが発生しないようにしておく。また頻繁に実行されるフィルタはデバッグが終了したらテキストボックスに直接書き込むといった対策も有効だ。このあたりの判断はユーザに任せたい。

そろそろ「もう一つのトリック」について触れておこう。これは複雑なフィルタを開発する人に是非お薦めしたい裏技である。ブロックリストを用いる方法であってもフィルタが長くなると可読性が悪くなることは避けられない。改行してフィルタを読みやすくしようとすると、改行コードがリストの区切りとみなされ、各行が独立したフィルタとして実行されてしまう。つまり単一行という制限からは逃れられないわけだ。

そこで色々と調べてみたところ、改行と認識するのは LF のみであって CR は空白文字列として無視されることがわかった。つまりこれを逆手に取るのである。テキストエディタを選ぶが、改行コードを任意に設定できるものであれば CR にしておけばよいということである。これにより見た目は改行されていても Proxomitron は単一行として認識してくれるので、可読性とフィルタ処理を両立させることができる。つまり、
$SET(M=$DTM(M))($TST(M=[#1-9])$SET(M=0$GET(M))|)$SET(D=$DTM(D))($TST(D=[#1-9])$SET(D=0$GET(D))|)$SET(E=$GET(D)/$GET(M)/$DTM(Y))$SET(d=$DTM(Y)-$GET(M)-$GET(D))

というフィルタは改行コードを CR にさえすれば
$SET(M=$DTM(M))($TST(M=[#1-9])$SET(M=0$GET(M))|)
$SET(D=$DTM(D))($TST(D=[#1-9])$SET(D=0$GET(D))|)
$SET(E=$GET(D)/$GET(M)/$DTM(Y))
$SET(d=$DTM(Y)-$GET(M)-$GET(D))

と書いても良いということだ。

CR で改行された複数行のフィルタをブロックリストとして作成し $LST コマンドで読み込めば、かなり複雑なフィルタの開発も行えるようになるだろう。ちなみに以下のコードは普通のプログラムっぽく見えるが、紛れもなく Proxomitron のヘッダフィルタである。
(
    $FILTER(True)
    ($URL((*(?*))2&s=([0-9]+)1) (
        $SET(q=)
        $SET(s=1)
        $SET(u=2)
    )|(
        $SET(q=q)
        $SET(u=u)
    ))

    ($RESP(?*) (
        ((^$URL(*(?|&)fn=)) (
            ($IHDR(Content-Length:)|)
            ($TST(=[#0-800]) (
                ($OHDR(X-Apple-Store-Front:([^-]+)2)|)
                $SET(s=2)
                $SET(list=143462,143441,143444,143455,143450,143442,143449,143457,143456)
                ($TST(list=*$TST(s),([0-9]+)3*) (
                    $JUMP($GET(u)&s=3)
                )|)
            )|)
        )|)
    )|(
        ((^$TST(s=?*)) (
            $SET(s=143462)
            ($KEYCHK(^C^S) (
                ($ADDLSTBOX(Artwork-List,iTunes Artwork Fixer,$TST($SET(url=))) (
                    $ADDLST(Artwork-List,$TST(q=$WESC($GET(q))(^?))n)
                )|)
            )|)
            ($LST(Artwork-List)|$SET(url=))
            ($TST(url=?*) (
                $LOG(Rmatched: q)
                $LOG(Rmerged: $GET(url))
            )|)
            ($TST(url=http://([^?]+)?1) (
                $SET(buf=1)
                ($TST(=itunes.apple.com/WebObjects/MZStore.woa/wa/viewAlbum) (
                    $SET(url=$GET(buf))
                )|(
                    $SET(q=?fn=$ESC($GET(url)))
                    $SET(url=)
                ))
            )|)
            ($SET(2=) $TST(url=(*?|)([^=]+)=([^&]+)1(&2|)) (
                ($TST(=id) $SET(key=p) | $SET(key=))
                $SET(val=1)
                $SET(prm=$GET(key)=$GET(val))
                ($TST(2=?*) $SET(url=2) | $SET(url=))
                ($TST(key=s) (
                    $SET(s=$GET(val))
                )|(
                    ($TST(q=$TST(key)=[^&]+1) (
                        $SET(q=$GET(prm)1)
                    )|(
                        $SET(q=$GET(q)&$GET(prm))
                    ))
                ))
            )|($TST(url=(?*)) (
                $SET(q=?fn=$ESC())
                $SET(url=)
            )|))
            ($SET(2=) $TST(url=(*?|)([^=]+)=([^&]+)1(&2|)) (
                ($TST(=id) $SET(key=p) | $SET(key=))
                $SET(val=1)
                $SET(prm=$GET(key)=$GET(val))
                ($TST(2=?*) $SET(url=2) | $SET(url=))
                ($TST(key=s) (
                    $SET(s=$GET(val))
                )|(
                    ($TST(q=$TST(key)=[^&]+1) (
                        $SET(q=$GET(prm)1)
                    )|(
                        $SET(q=$GET(q)&$GET(prm))
                    ))
                ))
            )|)
            ($SET(2=) $TST(url=(*?|)([^=]+)=([^&]+)1(&2|)) (
                ($TST(=id) $SET(key=p) | $SET(key=))
                $SET(val=1)
                $SET(prm=$GET(key)=$GET(val))
                ($TST(2=?*) $SET(url=2) | $SET(url=))
                ($TST(key=s) (
                    $SET(s=$GET(val))
                )|(
                    ($TST(q=$TST(key)=[^&]+1) (
                        $SET(q=$GET(prm)1)
                    )|(
                        $SET(q=$GET(q)&$GET(prm))
                    ))
                ))
            )|)
            $JUMP(http://hp$GET(q)&s=$GET(s))
        )|)
    ))
)&?*



※ 後日「複数行フィルタは行頭をインデントさせることで実現できる」という情報をいただきました(練炭さんありがとう!)。ヘルプファイルの Creating blocklists の項で詳しく解説されていたんですね。読んでみるとかなり特殊な使い方を想定した実装だったことがわかります。やっぱりこのツールの作者さん、凄いよねぇ。ちなみに面倒なので記事はそのままにしておきます(笑)。


最新の画像もっと見る