カーネルネタとか思いつかなかったので、今日はGNU GLOBALの話をするよっ。
このGLOBALさん、emacsからのタグジャンプがetagsより賢いのがお気に入りで(特に同名関数がたくさんあるばあい、etags.elは問答無用で最初にマッチした関数にジャンプするので全然役に立たない)長いこと愛用してる。
で、最近困ってたのがrubyの開発しててrubyとC言語を行ったり来たりするので、ruby上でついいつものクセでC-. とか押して「イラッ」とかしてた。だってもうクセになってるんだもん
で、ソース見たら案外拡張が簡単そうだったのでさくっと対応してみた。2時間いらなかったんじゃないかな。調査時間含めても。いや、まじめにやるとRubyの構文解析は死ねるんだけど、なにせ今がgrepでしのいでいるぐらいだから、どんだけバカでもこれより下はないという・・・
そんなわけで、さっそく ダウンロードページ みながら、CVSからチェックアウトする。
ftpからダウンロードしてはいけない。そこには configure.ac とか Makefile.am とかが入ってなくて二度手間だ。
なお、Linuxでは普通にビルドするとgtags実行時に毎回以下の警告がでて非常にうっとうしい。
Warning: POSIX sort program not found. If available, the program will be speed up.
これはglobalのconfigureがsortコマンドが /usr/bin にあると決めうちしているが、Linuxでは
/bin にあるとか、そういう問題
僕はめんどくさいので configure.ac を直接書き換えたけど、たぶん正しいのは sort -k が動くかどうかを
configure中で実際にsortコマンド叩いてみて確認する方法。やらなかったけど
でまあ、このへん 見ながらおもむろに予約語一覧をガーーーっとコピペして ruby_res.in ファイルつくる。
alias word
and word
begin,BEGIN word
break word
case word
class word
def word
defined word
do word
else word
elsif word
end,END word
ensure word
false word
for word
if word
in word
module word
next word
nil word
not word
or word
redo word
rescue word
retry word
return word
self word
super word
then word
true word
undef word
unless word
until word
when word
while word
yield word
たぶん、gperfの制限だと思うけどglobalは予約語リストに書いた名前がそのままCのマクロ名の一部として
転用されるため"="とか"?"とかみたいな記号が使えない。
おかげで defined? のかわりに defined を予約語に登録するはめになったけど多分大過なかろう。こういうのでハマるのは凝ったことしたい人だけだ。・・・・たぶん
あとはパーサーだけど、まとまな構文解析はスッパリあきらめて、出現したシンボルを
何も考えずにタグファイルに書き込んでいくというアホアホ関数を作成
void
ruby(const struct parser_param *param)
{
int c;
int level; /* brace level */
const char *interested = NULL;
if (!opentoken(param->file))
die("'%s' cannot open.", param->file);
while ((c = nexttoken(interested, ruby_reserved_word)) != EOF) {
switch (c) {
case SYMBOL:
PUT(PARSER_REF_SYM, token, lineno, sp);
break;
case RUBY_CLASS:
case RUBY_MODULE:
case RUBY_DEF:
if ((c = nexttoken(interested, ruby_reserved_word)) == SYMBOL) {
PUT(PARSER_DEF, token, lineno, sp);
}
break;
default:
break;
}
}
closetoken();
}
あとは適当にMakefileやらparser.cのlang_switchやらを適当に追記していくだけ。
https://github.com/kosaki/gtags にソース入れといたんで興味ある人はどぞ。
余談1: あ、aliasの対応わすれてることに今気づいた。あとで直しておこう
余談2: 「接頭辞$, @、@@が先頭についたものは予約語 とは見なされません。」のルールは難しそうだったので未実装。てゆーか、そこまでして予約語変数に使いたい人いないよ(>_<
余談3: ほんとうはCのソースで rb_define_method() 関数読んだらタグファイルに登録とかもやってみたかったんだけど、うーん、C parserのgenericなルートを書き換えずにruby用のタグを作るときだけHook仕込むいい方法が思いつかんかった。global plug-in の仕組みだと総入れ替えっぽいから激しくメドイ気配が濃厚なんだよなぁ
余談4: プラグインといえば。globalには任意のコマンドを呼び出せるプラグイン機構があるので頑張ればripperベースのまっとうなrubyパーサを呼び出すこともできなくもないような気がした。もちろんripperを勉強するのがメドイのでやらなかったが。
余談5: 今回の調査中に気づいたのだけど、最近のバージョンアップでタグ作成時に各ソースファイルを2回ずつ舐める悪癖が修正され gtags コマンドの動作が劇的に開眼している。
どのぐらい速くなったかというと
global-5.7.5-1.fc11
% time gtags
gtags 142.60s user 48.30s system 50% cpu 6:19.00 total
global-5.9.3
% time gtags
gtags 140.16s user 42.14s system 80% cpu 3:47.16 total
圧倒的じゃないか我が軍は!
これに気づいただけでも、今回の作業をした価値があったよ。