はじめに
ErlangやElixirの言語自体を解析する際に、パーサーは避けては通れません。
パーサーはプログラムのソースコードを、その言語の文法にしたかって(ソースコードの)テキストを解析し、プログラムで扱えるようなデータ構造に変換(構文解析)するプログラムです。
一般的にパーサーは非常に煩雑(でめんどくさい)処理なので、現代的なプログラミング言語では、パーサージェネレータを使って、言語のルールを記述したファイルを元に自動生成されます。
つまり、パース(構文解析)の処理をゼロから実装するのではなく、ルールを記述したファイルを入力として、パーサージェネレーターを通してパーサーのプログラム(ソースコード)を自動生成します。
有名なパーサージェネレータとしては、yacc(Yet Another Compiler Compiler)やbison等がありますが、ErlangやElixirでは、yaccに似たyeccというパーサージェネレーターが利用されています。
yecc
yeccはErlang用のパーサージェネレーターです。
名前からわかるようにyaccに似た仕様で、yrl(yeccのルールを記述)ファイルを受け取ってパーサーモジュールのerl(Erlangのソースコード)ファイルを自動生成します。
yrlファイルはerlcにそのままわたす事ができ、erlファイルが生成されます。
yeccの使い方は公式ドキュメントのyeccに詳細が記載されています。
Erlangのパーサー
Erlangのパーサーファイルはlib/stdlib/src/erl_parse.yrlです。
つまり、Erlangのプログラムの文法はここのファイルで定義されます。もしErlangの文法に手を加えたい場合はこのファイルを更新することで、Erlangの文法を変更する事ができます。
例えば、OTP-17でErlangにMapが導入された際は以下の様な変更がerl_parse.yrlに追加されました。(昔はErlangにはMapがなかったのです!)
+map_expr -> '#' map_tuple :
+ {map, ?line('$1'),'$2'}.
+map_expr -> expr_max '#' map_tuple :
+ {map, ?line('$2'),'$1','$3'}.
+map_expr -> map_expr '#' map_tuple :
+ {map, ?line('$2'),'$1','$3'}.
+map_expr -> map_expr '.' atom :
+ {map_field, ?line('$2'),'$1','$3'}.
+
+map_tuple -> '{' '}' : [].
+map_tuple -> '{' map_fields '}' : '$2'.
+
+map_fields -> map_field : ['$1'].
+map_fields -> map_field ',' map_fields : ['$1' | '$3'].
+
+map_field -> map_key '=>' expr :
+ {map_field,?line('$1'),'$1','$3'}.
+
+map_key -> expr : '$1'.
Elixirのパーサー
Elixirのパーサーファイルは、lib/elixir/src/elixir_parser.yrlです。
以下の変更は、関数呼び出し時に末尾にカンマ(,)がある場合にワーニングを出力する対応です。
kw_call -> kw_base : reverse('$1').
+kw_call -> kw_base ',' : warn_trailing_comma('$2'), reverse('$1').
kw_call -> kw_base ',' matched_expr : maybe_bad_keyword_call_follow_up('$2', '$1', '$3').
〜略〜
+%% TODO: Make this an error on v2.0
+warn_trailing_comma({',', {Line, Column, _}}) ->
+ elixir_errors:erl_warn({Line, Column}, ?file(),
+ "trailing commas are not allowed inside function/macro call arguments"
+ ).
まとめ
パーサー(のルールファイルであるyrl)関連のコードを読む事で、ErlangやElixirのシンタックスをより深いレベルで理解する事ができます。文法レベルの変更や大きな機能の追加の際、ほぼ確実にyrlファイルに手が入るのでyrlファイルを読める様になっておくと、どの様な変更をしようとしているのかについてより深く把握できます。
ErlangやElixirを改造したい人やコントリビュートをしたいと考えている方は、yrlファイルとyeccの概要を把握しておくとよいでしょう。