JavaScript上で動作するコードを出力できるパーサージェネレーター
これが結構困ります。
C++ならflex+bisonでいいや、となりますが、あいにくJavaScriptは門外漢です。
Wikipediaに比較ページがあり、さらにこのページなど参考にしてみると、
あたりが有力という事がわかりました。
Jisonが実際にCoffeScriptで使われているということで良さげでしたが、更新が止まっていることと、PEG.jsのほうが簡単そうで使いやすかったため、当初はPEG.jsで実装を始めました。しかし、結局以下の点がネックでANTLR4に切り替えました。
- 左再帰ができない
- エラー時復帰処理がない(あるいは貧弱)
Parsing Expression Grammar (PEG)というのは初めて使いましたが、楽なのでちょっとしたファイルのパーシングなどには良いんですが、流石に言語解析には厳しいと感じました。
ANTLR4はLLパーサーで、同様に左再帰ができないはずなのですが、なんと簡単なレベルであれば左再帰が扱える拡張がされており、エラー復帰やメッセージもそれなりにしっかりしています。メンテナンスも頻繁です。
また、VSCodeにANTLR4拡張があり、これが大変使いやすく決め手となりました。
ANTLR4によるパーサー実装
ANTLR4の入力文法ファイルはBNFに似ているので簡単に書けます。
SystemVerilogの文法は公式の仕様書PDFの他、ここと、ここが参考になります。Webベースのほうがハイパーリンクで辿れて便利です(間違いもあるので注意)。
見てもらうとわかりますが、SystemVerilogのBNFはかなり大きいです。キガクルットル。
ハードウェアを記述する抽象度の低い言語なので仕方ないとは思いますが・・・すごく汚い言語です。
すべてを実装はできませんでした。なぜなら私も使ったことがない文法が結構あり、効果が不明であった部分があったからです。
assertion関連だと、もはや使い方が不明な記法も多くありました。
実装はgithubにありますので、よければ参考にどうそ。
- Lexer ファイル
- https://github.com/Rockdoor/systemverilog/blob/master/src/sv_parser/grammar/SystemVerilogLexer.g4
- Parser ファイル
- https://github.com/Rockdoor/systemverilog/blob/master/src/sv_parser/grammar/SystemVerilogParser.g4
ANTLRの左再帰を成立させるために小変更した箇所があり、完全準拠とはなりません。
パフォーマンスの問題もあって、こんなの絶対使わない、という文法は削っています。
また、式の解析はサボっており、演算子の優先度を考慮した構文木にはなりません。
JavaScriptがそもそも遅いことに加え、この文法の大きさがゆえに最終的なパーサーの実行速度が落ちてしまっていると思われ、残念ながら1000行超えのソースだと1パース1秒くらいかかってしまいます。幸か不幸か、1ファイル1000行を目処に設計する強制力が意図せず発生しています。
ANTLRは使いやすいのですが、人口が少ないのかサンプルが少ないことなどがあってハマりどころも多いので、SystemVerilogパーサー実装の記事は別にします。