「Implementing "++" operator」を試してみて、parse.yと少し近づけた？

RubyKaigi2023にオンライン参加しました。いくつかセッションを聞いた中で、こちらのセッションが個人的にとてもおもしろかったです。
世は大パーサー時代ですし、今回は前々から少し見てみたかった parse.y を触ってみることにしました。

https://rubykaigi.org/2023/presentations/coe401_.html#day2

このセッションでは実際のコードを示しながら、ステップに分けて説明・実装しており、このスライドをベースに触ってみました。

https://speakerdeck.com/coe401_/implementing-plus-plus-operator-stepping-into-parse-dot-y

<script defer class="speakerdeck-embed" data-id="20a197beb8a7497cbf7960a1b7d708f2" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"></script>

## セッションの内容を軽く説明

Rubyには `i = 0; i++` のようにインクリメントの演算子がありません。そこで、Rubyの文法規則が書かれた`parse.y`というファイルをいじって実装するというものです。

`++`というトークンに対応するsymbolを設定したり、そのsymbolを使った新しい文法規則を追加したり、色々な方法で実装を試しています。

## 知れたこと・感想

- ASTを作るまでの規則がどのように実装されているのか
    - Scanner: tokenで条件分岐をして、symbolとvalueをParserに渡す
    - Parser: symbolを用いて、多くの文法からマッチするものを見つけて、actionに則ってRNodeを作る
- parse.yをいじれば、Scannerの状態をいじったり、既存の別のメソッドコールをするRNodeを作ったり、全く異なるsymbol (`+`→`tINTEGER`) を生み出したり、色々できちゃう
- Rubyを触って、文法をいじって、それが実際に実行できるの嬉しい！
- コードの雰囲気が少し感じられるようになったので、もう少し詳しく知りたい
    - [Rubyソースコード完全解説](https://i.loveruby.net/ja/rhg/book/) を読んでみたい
- 一見シンプルな文法でも簡単に他の文法を壊してしまうことがあり、すごいバランスの上で実装されているんだと知った
    - この量の文法規則をうまく組み立てているのは本当にすごいと感じた
- > there are no "variable objects"
    - たしかになと思った
    - 言語デザインの話もあったが、意識せずに使っていたが、意識すると面白いなと感じた

今まで、Rubyのしくみは読んだことがありましたが、何回読んでもあんまり理解が深まっていない感覚がありました。

今回のセッション自体も、当日聞いてあまり理解していなかったのですが、スライドを何度も読んで、自分で手を動かしてみて、やっとparse.yとの距離が一気に近づいた感覚があり、とても楽しかったです。
(セッションはなんとなくの理解でもめちゃくちゃおもしろかったです！)

セッションを見ていない方は、まずは動画のアーカイブが公開されたら見てください！
そしてparse.yを読んで触ってみることをとても強くおすすめします。

https://rubykaigi.org/2023/presentations/coe401_.html#day2

(RubyKaigiでは他のパーサー関連のセッションを聞けず、大パーサー時代の波に乗れていないので、アーカイブが公開されるのが待ち遠しい...)

## その他補足

セッションの内容については実際にスライドの内容をじっくり読んでもらうのが一番かと思います。
ただ、実装しながら、Scannerの実装の話に飛んだり、Parserの話に飛んだりするので、個人的には途中に結構混乱していました。

そのため、少し整理してみます。

Scannerでは、プログラムコード (例えば `++`)を少しずつ読み進めながら、以下のswitch文を使ってsymbolを特定します。

(スライド49ページ目)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143317/30c98936-3c8c-201b-89af-943ede418c4b.png)

Parserでは以下のように、symbolが`:`の右側にマッチした時に左側のsymbolへreductionをします。このときに実行するactionを定義します。ここでRNodeを作成したりします。

(スライド31ページ目)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143317/ee4766f5-69d1-af00-9734-758ded91e2bd.png)

`i++`をパースするにしても、(Scannerで)`++`をまとめた新しいトークンとして扱うか、(Scannerで)`+`,`+`と別々のsymbolとしてParserに渡して処理をするかどちらもでき、それぞれScanner、Parserでどのようにロジックを組むのかが勝負なのだなとわかりました。

### Rubyのビルドについて

今までRubyをビルドして実行したことがありませんでした。
そこで僕と同じように、今回始めてRubyをビルドする人もいるかもしれないのでメモとして残します。

---

[Ruby](https://github.com/ruby/ruby)リポジトリをForkして開発できる環境を整える。
まずはドキュメント通りにRubyをビルド。(ほぼこのドキュメントだけで完了です)

https://docs.ruby-lang.org/en/master/contributing/building_ruby_md.html

適当なスクリプトを用意してビルド済みのrubyで実行

```bash
$ ./ruby -v
ruby 3.3.0dev (2023-05-12T09:36:02Z plus-plus c803e92d66) [arm64-darwin22]
$ cat  ~/Desktop/plus-plus.rb
puts 'i++'
$ ./ruby ~/Desktop/plus-plus.rb
i++
```

`i++`を実行

```bash
$ cat  ~/Desktop/plus-plus.rb
i = 0
puts i++

$ ./ruby ~/Desktop/plus-plus.rb
/Users/kyntk/Desktop/plus-plus.rb: --> /Users/kyntk/Desktop/plus-plus.rb
syntax error, unexpected end-of-input
> 1  i = 0
> 2  puts i++

/Users/kyntk/Desktop/plus-plus.rb:2: syntax error, unexpected end-of-input (SyntaxError)
```

Rubyを実装しながらSyntaxErrorが出ないようにしていく

```bash
$ make install

略

$ ./ruby ~/Desktop/plus-plus.rb
1
```