💎

Ruby 3.2 - RubyVM::AbstractSyntaxTree

2022/12/21に公開

Ruby 3.2 アドベントカレンダーの21日目の記事です。

https://qiita.com/advent-calendar/2022/ruby32


RubyVM::AbstractSyntaxTree

RubyVM::AbstractSyntaxTree.parse に error_tolerant オプション追加

Feature #19013: Error Tolerant Parser - Ruby master - Ruby Issue Tracking System

RubyVM::AbstractSyntaxTree.parse とかに構文エラーがあるような文字列を食わせるとエラーになる。

s = <<EOS
def hoge
  p 1,
end
EOS
RubyVM::AbstractSyntaxTree.parse(s)
#=> SyntaxSuggest: Could not find filename from "syntax error, unexpected `end' (SyntaxError)"
#   <internal:ast>:33:in `parse': syntax error, unexpected `end' (SyntaxError)
#           from a.rb:6:in `<main>'

Ruby 3.2 では error_tolerant オプションに真を指定して、エラーにせずにエラーを含んだオブジェクトが返せるようになった。

s = <<EOS
def hoge
  p 1,
end
EOS
RubyVM::AbstractSyntaxTree.parse(s, error_tolerant: true)
#=> (SCOPE@1:0-3:3
#    tbl: []
#    args: nil
#    body:
#      (DEFN@1:0-3:3
#       mid: :hoge
#       body:
#         (SCOPE@1:0-3:3
#          tbl: []
#          args:
#            (ARGS@1:8-1:8
#             pre_num: 0
#             pre_init: nil
#             opt: nil
#             first_post: nil
#             post_num: 0
#             post_init: nil
#             rest: nil
#             kw: nil
#             kwrest: nil
#             block: nil)
#          body: (ERROR@2:2-3:3))))

不完全な状態をパースするようなツールとかにはいいのかもしれない。しらんけど。

RubyVM::AbstractSyntaxTree.parse に keep_tokens オプション追加

Feature #19070: Enhance keep_tokens option for RubyVM::AbstractSyntaxTree parsing methods - Ruby master - Ruby Issue Tracking System

keep_tokens に真を指定すると #tokens でトークンを得ることができるようになった。

s = <<EOS
def hoge
  p 1,2,3
end
EOS
ast = RubyVM::AbstractSyntaxTree.parse(s, keep_tokens: true)

pp ast.tokens
# [[0, :keyword_def, "def", [1, 0, 1, 3]],
#  [1, :tSP, " ", [1, 3, 1, 4]],
#  [2, :tIDENTIFIER, "hoge", [1, 4, 1, 8]],
#  [3, :nl, "\n", [1, 8, 1, 9]],
#  [4, :tSP, "  ", [2, 0, 2, 2]],
#  [5, :tIDENTIFIER, "p", [2, 2, 2, 3]],
#  [6, :tSP, " ", [2, 3, 2, 4]],
#  [7, :tINTEGER, "1", [2, 4, 2, 5]],
#  [8, :",", ",", [2, 5, 2, 6]],
#  [9, :tINTEGER, "2", [2, 6, 2, 7]],
#  [10, :",", ",", [2, 7, 2, 8]],
#  [11, :tINTEGER, "3", [2, 8, 2, 9]],
#  [12, :nl, "\n", [2, 9, 2, 10]],
#  [13, :keyword_end, "end", [3, 0, 3, 3]]]

puts ast.tokens.map{_1[2]}.join
# def hoge
#   p 1,2,3
# end

Discussion