掛け算を追加しよう
構文解析
次は、掛け算を追加します。足し算と掛け算の違いは何でしょうか?
そう、優先順位です。掛け算は足し算よりも優先順位が高いですね。それでは、どのようにすれば優先順位を文法で表現出来るでしょうか?
具体例で考えてみましょう。1+2+3は、((1+2)+3)のようにマッチされるのでした。それでは、3*3+4*4はどのように構文解析されて欲しいでしょうか?((3*3)+(4*4))のように構文解析されるのが望ましいですね。つまり、+の左側、右側に来る式が、掛け算の式を含むようにすれば良いことが分かります。
それでは、これをPEG.jsのコードに落とし込んでいきましょう。
例えば次のようになります。+の左右にあるTermは、掛け算の式を含んでいますね。
example
Expression = Term ("+" Term)*
Term = Factor ("*" Factor)*
それでは、入力に足し算と掛け算を使った式を入れて、期待した結果が返ってくるか確かめてみましょう。優先順位の高い式の部分木が、木の深い場所にあれば正しく動いています。次はevalを書き換えます。
評価
次はeval関数に、ast.tagが"Mul"のときの分岐を追加しますが、評価規則は足し算の時とほぼ変わりません。足し算を参考にして実装してみましょう。
発展
引き算と割り算を追加して、四則演算が出来るようにしてみましょう。
そのためには、PEGの重要な文法定義方法である選択を知る必要があります。選択とは、e1, e2を文法として、e1 / e2のように文法同士が/で区切られた文法のことを言います。この文法は、e1をまず最初に適用して、e1が適合しなかったら、e1が適用される前の状態に戻して、e2を適用するという意味です。(e.g. "+" / "-"
は+か-に適合する文法、戻り値は先に適合した文法の戻り値)
また、"a" / "b" "c"
はどのような文字列にマッチすると思いますか?これは、"a"または"bc"にマッチします。これを、"ac"または"bc"にマッチさせたければ、("a" / "b") "c"
のようにする必要があります。(括弧で囲むことで、"a" / "b"
をグループ化することが出来ます。)
引き算のASTのtagは"Sub"、割り算は"Div"などにするのが良いでしょう。