ripperの使い方

どう書く?.orgのお題を解くときにripperの使い方を少し調べたのでメモ

ripperはRubyプログラムの解析を行うためのライブラリです。現在のところ字句解析のみがサポートされています(と思います...)。
Ruby 1.9.0に標準添付されていますが、驚くほどドキュメントが少ないです。

ripperには3つの使い方があります

  • filter     Rubyプログラムをイベントドリブンで扱う
  • lexer     Rubyプログラムのトークン列を得る
  • sexp     RubyプログラムをS式で得る

今回はfilterを使ってみました。
filerの使い方はサンプルプログラムのstrip-comment.rbがわかりやすいです。

# $Id: strip-comment.rb 11708 2007-02-12 23:01:19Z shyouhei $

require 'ripper/filter'

class CommentStripper < Ripper::Filter
  def CommentStripper.strip(src)
    new(src).parse(nil)
  end

  def on_default(event, token, data)
    print token
  end

  def on_comment(token, data)
    puts
  end
end

CommentStripper.strip(ARGF)

こんな感じで、Ripper::Filterを継承したクラスを作って、特別扱いしたいイベントの処理(on_commentとか)のメソッドを定義して、その他のイベントはon_defaultで処理するという感じでプログラムが作れます。on_defaultは次のような引数をとります。

  • event   イベントの名前
  • token   読み込んだトーク
  • data    複雑なので後述

個々のイベント処理ではeventが無く、tokenとdataをとります。
では、on_commentのほかのイベントの名前はというと・・・、残念ながらまとまったドキュメントは見つからなかったのですが、こんな感じのプログラムで調べられるんじゃないかなと思います。

require 'ripper'
class RipperTest<Ripper::Filter
  def on_default(event, token, data)
    print "#{token.inspect}\t #{event} \n"
  end
end

print RipperTest.new(ARGF).parse(nil)

今まで調べたイベントとしては、こんな感じです。ほかにもものすごくたくさんあります。

  • on_commnet    コメント
  • on_gvar      大域変数
  • on_cvar      クラス変数
  • on_ivar      インスタンス変数
  • on_ident      ローカル変数
  • on_tstring_content 文字列

このように定義したRipper:: Filterを継承したクラスは次のようにして使います。例えば、このようなクラスをFooとします。

インスタンスを作ります

  r = Foo.new(RubyソースコードのIOオブジェクト)

インスタンスが作成できたら、解析を行います

   r.parse(初期値)

parseで解析が始まると、各イベントメソッド(on_defaultとかon_comment)のdata引数(説明を後回しにしたもの)に初期値が渡ってきます。イベントメソッドでdataを書き換えることが出来ます。最終的なdataの値がparseの値になります。
例えば、

  r.parse('')

としておいて、各イベントメソッドで、

  data << token

とすると、r.parse('')はトークンをすべて並べたもの、つまりRubyプログラム全体を文字列として返します。