13
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RubyVM::ASTさわってみた

Last updated at Posted at 2018-06-01

RubyVM::AST [Experimental]

Ruby 2.6では RubyVM::AST モジュールが導入されました。

このモジュールには、文字列をパースしてAST(抽象構文木)のNodeを返すparseメソッド、ファイルをパースするparse_fileメソッドが実装されています。 RubyVM::AST::Node も導入されました。このクラスのインスタンスから位置情報や子ノードを取得することができます。この機能はexperimentalであり、互換性は保証されていません。
https://www.ruby-lang.org/ja/news/2018/05/31/ruby-2-6-0-preview2-released/

API

RubyVM::AST

= RubyVM::AST

(from ruby core)
------------------------------------------------------------------------
= Class methods:

  parse, parse_file

RubyVM::AST::Node

= RubyVM::AST::Node < Object

(from ruby core)
------------------------------------------------------------------------
= Instance methods:

  children, first_column, first_lineno, inspect, last_column,
  last_lineno, type

RubyVM::AST.parse

Rubyのコードの文字列を渡すと、文字列をパースしてASTを返す。とりあえず無をparseしてみる。

root = RubyVM::AST.parse('')
# => #<RubyVM::AST::Node(NODE_SCOPE(0) 1:0, 2:0): >

root.type
# => "NODE_SCOPE"

[root.first_lineno, root.first_column, root.last_lineno, root.last_column]
# => [1, 0, 2, 0]

root.children
# => [nil, #<RubyVM::AST::Node(NODE_BEGIN(16) 2:0, 2:0): >]

root.children.class
# => Array

root.children.last.type
# => "NODE_BEGIN

root.children.last.children
# => [nil]

パースできない文字列を渡すとどうなるか

エラーが出て実行がとまる

RubyVM::AST.parse('^^')
# no file name:1: syntax error, unexpected '^'

rescueできない

begin
  RubyVM::AST.parse('^^')
rescue Exception
  puts "捕まえた〜!"
  p $!
end
# no file name:1: syntax error, unexpected '^'

RubyVM::AST::Node

#type

この辺で定義されてるenum node_typeを文字列で返す。
https://github.com/ruby/ruby/blob/v2_6_0_preview2/node.h#L22-L225

#children

抽象構文木の葉を返す。末端までたどって表示させてみると節点に演算子(NODE_OPCALL)が来ているのがわかる。

def walk(node, level = 0)
  print " " * (level * 2)
  p node
  return unless node&.children&.empty?&.!
  walk(node.children.first, level + 1)
  walk(node.children.last, level + 1)
end

walk(RubyVM::AST.parse('1 + 1'))
# #<RubyVM::AST::Node(NODE_SCOPE(0) 1:0, 1:5): >
#   nil
#   #<RubyVM::AST::Node(NODE_OPCALL(36) 1:0, 1:5): >
#     #<RubyVM::AST::Node(NODE_LIT(59) 1:0, 1:1): >
#     #<RubyVM::AST::Node(NODE_ARRAY(42) 1:4, 1:5): >
#       #<RubyVM::AST::Node(NODE_LIT(59) 1:4, 1:5): >
#       nil

RubyVM::AST::Nodeをnewできるか

newできない

RubyVM::AST::Node.new
Traceback (most recent call last):
        2: from /home/sei/local/bin/irb:11:in `<main>'
        1: from (irb):1
NoMethodError (undefined method `new' for RubyVM::AST::Node:Class)

自分でASTを組み立ててYARVのInstructionSequenceにコンパイルして実行できるか

YARVのInstructionSequenceに変換するAPIは公開されていないので出来なさそう。

13
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?