Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyでの静的分析入門!parser gemでメソッド定義を抽出する方法

ソースコードを解析して、すべてのメソッドがどこで定義され、どんな引数を受け取るのかを把握したいと思ったことはありませんか?

どうすれば実現できるのでしょうか?

最初に思いつくのは、正規表現(regexp)を書くことかもしれません。

しかし、もっと良い方法があるとしたらどうでしょう?

Rubyでの静的分析入門!parser gemでメソッド定義を抽出する方法

答えは「あります」!

静的解析(Static Analysis)とは、ソースコードそのものから情報を抽出するためのテクニックです。

これは、ソースコードをトークンへと変換する(パースする)ことで実現されます。

それでは早速見ていきましょう!

parser gemを使う

Rubyには標準ライブラリとしてRipperというパーサーが用意されています。しかし、その出力は扱いにくいため、私は優秀なparser gemを使うことをおすすめします。有名なRubocopも、このgemを使って静的解析を実現しています。

このgemにはバイナリも含まれており、コードを直接パースして、生成される構文木(パースツリー)を確認することができます。

以下がその例です

ruby-parse -e '%w(hello world).map { |c| c.upcase }'

出力は次のようになります。

(block
  (send
    (array
      (str "hello")
      (str "world")) :map)
  (args
    (arg :c))
  (send
    (lvar :c) :upcase))

これは、Rubyがコードをどのように解釈しているのかを理解したいときに役立ちます。しかし、独自の解析ツールを作りたい場合は、ソースファイルを読み込み、パースし、生成された木構造を走査する必要があります。

require 'parser/current'

code = File.read('app.rb')
parsed_code = Parser::CurrentRuby.parse(code)

パーサーは、コードのAST(抽象構文木:Abstract Syntax Tree)を返します。名前だけ聞くと難しそうですが、実際は思っているよりシンプルですよ 🙂

ASTを走査する

parser gemでコードをパースできたら、次は生成されたASTを走査します。

これを行うには、AST::Processorを継承したクラスを作成します。

class Processor < AST::Processor
end

そして、このクラスをインスタンス化し、.processメソッドを呼び出します。

ast = Processor.new
ast.process(parsed_code)

次に、いくつかのon_メソッドを定義する必要があります。これらのメソッドは、AST内のノード名に対応しています。

どのメソッドを定義すべきかを見つけるには、Processorクラスにhandler_missingメソッドを追加しましょう。また、on_beginメソッドも必要です。

class Processor < AST::Processor
  def on_begin(node)
    node.children.each { |c| process(c) }
  end

  def handler_missing(node)
    puts "missing #{node.type}"
  end
end

ここまでの状況を整理すると

RubyのASTと基本的なプロセッサが手元にあり、このコードを実行すると、ASTのノードタイプが表示されます。

さて、次のステップ

使用したいすべてのon_メソッドを実装していきます。たとえば、すべてのインスタンスメソッド名とその行番号を取得したい場合は、次のように書けます。

def on_def(node)
  line_num    = node.loc.line
  method_name = node.children[0]

  puts "Found #{method_name} at line #{line_num}"
end

プログラムを実行すると、見つかったすべてのメソッド名が出力されるはずです。

まとめ

Rubyの静的解析ツールを作るのは、見た目ほど難しくありません。より完全な実装例を見たい方は、class_indexer gemをチェックしてみてください。さあ、あなたも自分だけの解析ツール作りに挑戦してみましょう!

この記事が役に立ったら、ぜひSNSなどでシェアしてくださいね 🙂


  1. Rubyで学ぶNLP:Nグラム分析の楽しさと実践

    大量のテキストデータが手元にあり、そこから何らかの意味を抽出したい——そんな場面に遭遇したことはありませんか? そんなときにまず試したいのが、テキストをNグラム(n-gram)に分割するという手法です。 Nグラムとは何か? Wikipediaには次のように定義されています。 計算言語学および確率論の分野において、n-gramとは与えられたテキスト列から取り出した、n個の連続した要素からなる列のことである。—— Wikipedia 具体例を見てみましょう。 Hello there, how are you? というフレーズを例にとると、ユニグラム(1要素のNグラム)は Hello, there,

  2. Rubyでの静的分析入門!parser gemでメソッド定義を抽出する方法

    ソースコードを解析して、すべてのメソッドがどこで定義され、どんな引数を受け取るのかを把握したいと思ったことはありませんか? どうすれば実現できるのでしょうか? 最初に思いつくのは、正規表現(regexp)を書くことかもしれません。 しかし、もっと良い方法があるとしたらどうでしょう? 答えは「あります」! 静的解析(Static Analysis)とは、ソースコードそのものから情報を抽出するためのテクニックです。 これは、ソースコードをトークンへと変換する(パースする)ことで実現されます。 それでは早速見ていきましょう! parser gemを使う Rubyには標準ライブラリとしてRipper