Lramaで簡単な自作言語のパーサを書いた

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234055/70a91ee7-2675-bb30-3df6-728d1609daa3.png)

先日 Ruby にマージされた LALR(1)パーサジェネレータ Lrama を使ってみました。

https://github.com/ruby/lrama

参考: [RubyにlramaがマージされてBison依存がなくなった（RubyKaigi 2023）｜TechRacho by BPS株式会社](https://techracho.bpsinc.jp/hachi8833/2023_05_15/130116)





# できたもの

[https://github.com/sonota88/vm2gol-v2-c/tree/alt-parser-lrama](https://github.com/sonota88/vm2gol-v2-c/tree/alt-parser-lrama)

alt-parser-lrama ブランチに `mrcl_parser_lrama.y` が入っています。




# 概要

Mini Ruccola は私がコンパイラ実装に入門するために作った自作言語とその処理系です。原始的だけどその分入門者（＝私）視点では分かりやすい、という方向性のものです。私でも作れる簡単なコンパイラ。


https://github.com/sonota88/vm2gol-v2

作ったときに書いた備忘記事:

- [RubyでオレオレVMとアセンブラとコード生成器を2週間で作ってライフゲームを動かした話](https://qiita.com/sonota88/items/f9cb3fc4a496b354b729)
- [（2週間ちょっとで）Rubyでかんたんな自作言語のコンパイラを作った](https://qiita.com/sonota88/items/2b95378b43a22109513c)




---

コンパイラのパーサ部分は元々手書きの再帰下降パーサだったのですが、他のパーサライブラリも試したくて Racc 版と Parslet 版のパーサを以前書きました。

- [Raccでかんたんな自作言語のパーサを書いた](https://qiita.com/sonota88/items/97ba1f0c377fbe86d5b1)
- [Parsletでかんたんな自作言語のパーサを書いた](https://qiita.com/sonota88/items/edce05e86d3248f49401)

今回の Lrama 版はこのシリーズの延長です。

---

Mini Ruccola コンパイラはレキサ・パーサ・コード生成器が独立しています。そのため、レキサとコード生成器は Ruby 製のものをそのまま使い、パーサだけ別の言語で書いて一緒に動かすなんてことも可能です。

可能なのですが、C言語への移植版  
[github.com/sonota88/vm2gol-v2-c](https://github.com/sonota88/vm2gol-v2-c)  
を使った方が楽なので今回はこっちを使いました。

参考: [素朴な自作言語のコンパイラをCに移植した](https://memo88.hatenablog.com/entry/2020/09/06/043607)


---

Racc 版パーサ実装と C移植版がすでにありますから、あとは Racc 版パーサを Lrama + C 向けに書き直せば一丁あがり、という寸法です。

たとえば以下は関数定義の規則とアクションの記述の比較です。

```ruby
# Racc
func_def : "func" IDENT "(" args ")" "{" stmts "}"
             {
                 _, fn_name, _, args, _, _, stmts, _, = val
                 result = ["func", fn_name, args, stmts]
             }
```

```c
// Lrama
func_def : TS_KW_FUNC TS_IDENT TS_PAREN_L args TS_PAREN_R TS_SYM stmts TS_SYM
         // "func"    fn_name  "("        args ")"        "{"    stmts "}"
             {
                 NodeList* func_def = NodeList_new();
                 NodeList_add_str(func_def, "func");
                 NodeList_add_str(func_def, $2);
                 NodeList_add_list(func_def, $4);
                 NodeList_add_list(func_def, $7);

                 $$ = func_def;
             }
```

こんな感じで読み換えていきました。どちらも Yacc から派生した LALRパーサジェネレータなのでよく似ていますね。




# メモ

- 1日くらいでババッと書いたものなので雑だったり手抜きで済ませている部分があります。一応動いてる、という程度の出来です。
  - 警告もひとまず放置
- Yacc, Bison もそのうち触ってみようと思いつつ結局今まで触らずじまいだった
  - 今回ちょっとやってみて雰囲気が知れてよかった
  - Rubyソースコード完全解説の [第9章 速習yacc](https://i.loveruby.net/ja/rhg/book/yacc.html) を読んだらとりあえずなんとかなった
- Rubyには慣れているけどCは分からないという状態で LALR パーサジェネレータに入門したい人は、まず Racc で入門するのが良いのではないかと思います。Ruby の知識だけで使えるので、いきなり Yacc や Bison や Lrama に挑戦するよりはお手軽かと。
  - Racc の作者（青木峰郎さん）による解説本『Rubyを256倍使うための本 無道編』もまだ中古で入手可能（[Amazon](https://www.amazon.co.jp/dp/4756137091)）







# この記事を読んだ人は（ひょっとしたら）こちらも読んでいます

<!-- Ruby/Racc: パース時のスタックの動きをFlameGraphっぽくビジュアライズする - Qiita -->
https://qiita.com/sonota88/items/6a96d2bcea9d134e38b7

Racc の気持ちが分かるように↓こういう図を描かせてみたもの。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234055/f7feb984-1716-107d-e7bf-b3238dc0c49f.png)

---

<!-- Ruby: 入れ子の配列だけをパースできるパーサを作る（手書きの再帰下降パーサ） - Qiita -->
https://qiita.com/sonota88/items/816b6b8d7a362ef4e332

メソッドの再帰呼び出しについてすでに知っていれば1時間もかからず書ける内容。比較的簡単で時間がかからないので、LALR パーサにこだわる事情がなければ先に再帰下降パーサで入門するのも良いと思います。

---

<!-- 『RubyでつくるRuby』のMinRubyのパーサを書いた（手書きの再帰下降パーサ） - Qiita -->
https://qiita.com/sonota88/items/84e4c16e5602b9771e92

<!-- 四則演算と剰余のみのexprコマンドをRubyで作ってみた -->
https://zenn.dev/sonota88/articles/06c540eda79b98

<!-- 正規表現エンジン（ロブ・パイクのバックトラック実装）をRubyで写経した - Qiita -->
https://qiita.com/sonota88/items/5902f255e196b5b8b048