3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PARENTHESIS_LEFT_PARENTHESES というトークン

Last updated at Posted at 2025-12-01

💎 はじめに

はじめまして。私は Shikumi.rb というコミュニティのメンバーとして活動していて、みんなで楽しく Ruby の言語処理系について学んでいます。

今回は Shikumi.rb の主催者である @utsubo さんから教えていただいた言語処理系の話題をテーマに記事を書いてみました。

Ruby の言語処理系にあまり馴染みがない方でも理解しやすい題材を選んだつもりですが、全く馴染みがないという方は @udzura さんの資料を一読することをオススメします。1

⚙️ 環境

  • macOS Ventura 13.7.8
  • Ruby 3.4.5

💡 クイズ

早速ですが Ruby に関するクイズです。以下の A と B に入る数は何でしょう。

# irb で 2 乗を計算するメソッドを定義
irb(main):001* def foo(n)
irb(main):002*   n ** 2
irb(main):003> end
=> :foo

# A は何か?
irb(main):004> foo(3) * 3
=> A

# B は何か?
irb(main):005> foo (3) * 3
=> B
答え

A は 27、 B は 81 です。

(スペースの有無で結果が変わることに注意しましょう ⚠️)

# (3 ** 2) * 3 = 27
irb(main):004> foo(3) * 3
=> 27

# (3 * 3) ** 2 = 81
irb(main):005> foo (3) * 3
=> 81

🤔 深掘りしてみる

このクイズについてもう少し深掘りしてみます。2

スペースの有無で結果が変わるということは、パーサによって異なる AST が作られているはずです。

(以下では、メソッドが親、レシーバが左の子、引数が右の子になるような木構造を図示しています)3

# foo(3) * 3

      *
     / \
   foo  3
   / \
self  3 
# foo (3) * 3

     foo
     / \
  self  *
       / \
      3   3 

では、レキサーが生成するトークン列はどうなるでしょうか。

  • foo(3) * 3
  • foo (3) * 3

もし仮に上の 2 つをレキサーが同じトークン列に変換したとすると、パーサは同じ AST を出力することになって矛盾します。

したがって、レキサーは上の 2 つを異なるトークン列に変換するはずです。

⚙️ 検証してみる

この仮説はどのようにして検証できるでしょうか。

Prism のレキサーを使って実際にトークン列を出力すれば検証できそうです。

irb を使って実際に確認してみます。

irb(main):001> Prism.lex('foo(3) * 3')
=> 
(略)
 @value=
  [[IDENTIFIER(1,0)-(1,3)("foo"), 32],
   [PARENTHESIS_LEFT(1,3)-(1,4)("("), 1025],
   [INTEGER(1,4)-(1,5)("3"), 2],
   [PARENTHESIS_RIGHT(1,5)-(1,6)(")"), 8],
   [STAR(1,7)-(1,8)("*"), 1],
   [INTEGER(1,9)-(1,10)("3"), 2],
   [EOF(1,10)-(1,10)(""), 2]],
(略)
irb(main):002> Prism.lex('foo (3) * 3')
=> 
(略)
 @value=
  [[IDENTIFIER(1,0)-(1,3)("foo"), 32],
   [PARENTHESIS_LEFT_PARENTHESES(1,4)-(1,5)("("), 1025],
   [INTEGER(1,5)-(1,6)("3"), 2],
   [PARENTHESIS_RIGHT(1,6)-(1,7)(")"), 8],
   [STAR(1,8)-(1,9)("*"), 1],
   [INTEGER(1,10)-(1,11)("3"), 2],
   [EOF(1,11)-(1,11)(""), 2]],
(略)

ほぼ同じトークン列に見えますが、左カッコを表すトークンに差分があることがわかります。

(予想通りですね 🎉)

  • PARENTHESIS_LEFT
  • PARENTHESIS_LEFT_PARENTHESES

⛰️ さらなる仮説

@utsubo さんとの会話の中で、PARENTHESIS_LEFT_PARENTHESES は上の 2 パターンを区別するためだけに導入されたトークンではないかという予想が出てきました。

(この予想は何となく当たっていそうな気がしますが果たして...)

仮説からさらに仮説が出てきて、Ruby の言語処理系について考えるだけで永遠に遊べそうですね 🥳

💎 最後に

今回は Ruby に関するクイズから言語処理系の世界を少しだけ覗いてみました。

これを読んでいる方で少しでも面白いと思った方は Shikumi.rb で一緒に楽しく言語処理でワイワイしてみませんか?

「おーい、磯野、野球やろうぜ」みたいな気楽さで、言語処理の楽しさを共有できれば嬉しいです。

🎄 追記

@utsubo さんとの会話の中で、「PARENTHESIS_LEFT_PARENTHESES というトークン名に似た名前の曲があった気がするなぁ」というのがずっと気になっていたのですが、坂本龍一の「Thatness and Thereness」でした。

B-2ユニット - Wikipedia

  1. Ruby の言語処理系の全体像が掴めます。

  2. ここからは Ruby の言語処理系の知識を前提としています。必要に応じて上でご紹介した @udzura さんの資料をご参照ください。

  3. この図は説明のための簡易的なもので、Ruby の言語処理系が実際に生成する AST とは無関係です。

3
0
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
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?