Ripper でパースエラーを検知しようとしてハマった

Ruby でパースエラーになっている箇所を調べたくて Ripper を使ったらハマったので覚書。

Ripper を使う

Ripper とは Ruby に標準で付属している Ruby のコードをパースするためのライブラリです。
Ripper では Ripper というクラスがあり、これを継承してパース時の処理を実装していく感じになります。
例えば、以下のような感じで『任意の Ruby のテキスト』をパースしてエラーを検知することが出来ます

require "ripper"

class FizzBuzz < Ripper
    def compile_error(msg)
        pp __method__
        pp msg
    end
end

FizzBuzz.parse("@%")     # :compile_error
# output:
# :compile_error
# "`@%' is not allowed as an instance variable name"

こんな感じでコンパイルエラーを検知する事が出来ます。

パースエラーを検知する

さて、先ほどと同じように次のような Ruby のコードをパースしてみました。

require "ripper"

class FizzBuzz < Ripper
    def compile_error(msg)
        pp __method__
        pp msg
    end
end

FizzBuzz.parse(<<~EOS)
    def hoge
        1 +
    end

    def foo
EOS

しかし、上記の場合は #compile_error は呼ばれません。
上記のような Ruby のコードでエラー箇所を検知する場合は #compile_error ではなくて #on_parse_error というメソッドが呼ばれます。

require "ripper"

class FizzBuzz < Ripper
    def compile_error(msg)
        pp __method__
        pp msg
    end

    def on_parse_error(msg)
        pp __method__
        pp msg
    end
end

FizzBuzz.parse(<<~EOS)
    def hoge
        1 +
    end

    def foo
EOS
# output:
# :on_parse_error
# "syntax error, unexpected end"
# :on_parse_error
# "syntax error, unexpected end-of-input"

これでいい感じに Ruby のコードのエラーを検知する事が出来ます。

参考