BVE5の構文はどう処理されているのか気になったので、今回から実際にC#でパーサを実装してみることにします。
成果物のリポジトリはこちら -> Bve5_Parsing
字句解析と構文解析にはANTLR4というパーサジェネレータを用います。
ANTLRは歴史のあるライブラリで情報が充実しており、大抵のことはドキュメントに書いてあるか、ググれば出てくるので初学者でもとっつきやすかったです。
今回はマップ構文の字句と構文定義を行ってみます。
なお、この記事の内容は誤っている可能性もあるので悪しからず。
0. 環境の構築
ANTLR4はnugetでインストール。
作成するライブラリはVisualStudio2017で開発してますが、ANTLR4の文法ファイルはeclipseの方が適しているので別途eclipse上で開発します。
eclipseにはANTLR4 IDEをインストール。ANTLR4 IDEはANTLR文法のチェック、テスト、構文図や構文木の表示をサポートする便利なプラグインです。
1. 簡単な構文の定義
まずは、簡単な例としてマップ構文のCurve.BeginTransition()
構文を実装してみます。
字句を定義するMapGrammarLexer.g4
と、文法を定義するMapGrammarParser.g4
をそれぞれ作ります。これらは一つのファイルで記述することもできますが、今回は見やすさを重視して別ファイルで記述します。
MapGrammarLexer.g4
上記のMapGrammarLexer.g4
では、4行目でこれがLexerだということを示し、続いて字句の定義を行っています。
ANTLRでは、字句の定義は字句名 : '字句'
で行います。ここでは、CurveやBeginTransition、括弧といった字句を定義してますね。
13行目のWHITESPACE
は空白や改行など、無視する字句の定義。skipというアクションを指定することでその字句は読み飛ばすことが出来ます。
続いて文法の定義。
MapGrammarParser.g4
6行目のtokenVocab
オプションに先程の字句を定義したMapGrammarLexer
を指定し、文法の定義を記述しています。
8行目、root
はその名の通りルートの文法。ステートメントが0回以上続き、EOF
(ファイルの終わり)が来ることを意味します。
ステートメントは取り敢えずCurve.BeginTransition()
構文のみ。
でEclipseの構文解析ツリーにて試しにテストしてみます。
はい、ちゃんと構文として認識されているようです。
2. 字句の大文字小文字を区別しないようにする
マップ構文の字句は大文字小文字の区別がないのに対し、現時点でのパーサは大文字小文字を区別してしまいます。例えばCURVE
、cuRVe
といった入力は、現時点では字句として認識されないのでこれを修正します。
やり方は割りとゴリ押し。
A~Zの字句を個別に定義します。
MapGrammarLexer.g4
14~39行目で、A~Zの大文字小文字のどちらでもマッチする字句を定義しました。
fragment
は参照のみでマッチする定義のこと。この場合はfragment
を参照しているCURVE
とBEGIN_TRANSITION
のみでA~Zにマッチします。
もっといいやり方がありそうですが、検索してもいまいち分からなかったので取り敢えずこの方法でゴリ押してます。
3. コメントを無視する
マップ構文では’#
‘もしくは’//
‘から改行までの文字列はコメントとして無視されます。
そのため、コメントの終わりまで字句を読み飛ばす定義を追加。
MapGrammarLexer.g4
WHITESPACE
と同じ要領でskipアクションを指定しているため、この定義を追加するだけでコメントは無視してくれます。便利ですね。
4. 今後の拡張を考慮して構文定義を改良
現時点ではCurve.BeginTransition
構文のみ対応してますが、今後構文を追加していくことを考え、構文定義を改良します。
MapGrammarParser.g4
変更点はstatement文法内で完結させていたCurve.BeginTransition
構文の定義を、新たに作ったcurve文法に移動させた所。これからは構文の種類によって分類して定義していきたいと思います。
とりあえず今回はここまで。
次は数式関係の定義をしていきたいと思います。