先日 Ruby にマージされた LALR(1)パーサジェネレータ Lrama を使ってみました。
参考: RubyにlramaがマージされてBison依存がなくなった(RubyKaigi 2023)|TechRacho by BPS株式会社
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
作ったときに書いた備忘記事:
コンパイラのパーサ部分は元々手書きの再帰下降パーサだったのですが、他のパーサライブラリも試したくて Racc 版と Parslet 版のパーサを以前書きました。
今回の Lrama 版はこのシリーズの延長です。
Mini Ruccola コンパイラはレキサ・パーサ・コード生成器が独立しています。そのため、レキサとコード生成器は Ruby 製のものをそのまま使い、パーサだけ別の言語で書いて一緒に動かすなんてことも可能です。
可能なのですが、C言語への移植版
github.com/sonota88/vm2gol-v2-c
を使った方が楽なので今回はこっちを使いました。
Racc 版パーサ実装と C移植版がすでにありますから、あとは Racc 版パーサを Lrama + C 向けに書き直せば一丁あがり、という寸法です。
たとえば以下は関数定義の規則とアクションの記述の比較です。
# Racc
func_def : "func" IDENT "(" args ")" "{" stmts "}"
{
_, fn_name, _, args, _, _, stmts, _, = val
result = ["func", fn_name, args, stmts]
}
// 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パーサジェネレータなのでよく似ていますね。
Racc の気持ちが分かるように↓こういう図を描かせてみたもの。
メソッドの再帰呼び出しについてすでに知っていれば1時間もかからず書ける内容。比較的簡単で時間がかからないので、LALR パーサにこだわる事情がなければ先に再帰下降パーサで入門するのも良いと思います。
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント