パーサーの~
メソッドは、2つのパーサーを1つに結合し、2つの元のパーサーを連続して適用し、2つの結果を返します。それは単純に(でParser[T]
)
def ~[U](q: =>Parser[U]): Parser[(T,U)].
2つ以上のパーサーを組み合わせたことがない場合は、問題ありません。ただし、それらの3つをチェーンするp1
とp2
、、、、、リターンp3
タイプ、、、、、、、つまりタイプは。になりT1
ます。そして、あなたの例のようにそれらの5つを組み合わせると、それはになります。次に、結果のパターンマッチを行うと、これらすべての括弧も作成されます。T2
T3
p1 ~ p2 ~ p3
p1.~(p2).~(p3)
Parser[((T1, T2), T3)]
Parser[((((T1, T2), T3), T4), T5)]
case ((((_, id), _), formals), _) => ...
これは非常に不快です。
次に、巧妙な構文上のトリックがあります。ケースクラスに2つのパラメーターがある場合、パターンのプレフィックス位置ではなく、インフィックスで表示される可能性があります。つまり、があれば
case class X(a: A, b: B)
、とのパターンマッチングが可能ですが、。とのパターンマッチングcase X(a, b)
も可能case a X b
です。(これは、x::xs
空でないリストに一致するパターンで行われることで::
あり、ケースクラスです)。ケースを書くときa ~ b ~ c
、それはを意味しますcase ~(~(a,b), c)
が、それよりもはるかに快適で、快適であり、case ((a,b), c)
正しく理解するのは難しいです。
したがって、パーサーのメソッドは、の代わりにを~
返すので、複数の〜の結果でパターンマッチングを簡単に行うことができます。それに加えて、 そしてあなたが得ることができるのと同じくらい同型である、そしてほとんど同じことです。Parser[~[T,U]]
Parser[(T,U)]
~[T,U]
(T,U)
結果のコードは読みやすいため、パーサーの結合メソッドと結果タイプには同じ名前が選択されます。結果処理の各部分が文法規則の項目にどのように関連しているかがすぐにわかります。
parser1 ~ parser2 ~ parser3 ^^ {case part1 ~ part2 ~ part3 => ...}
Tildaが選択されたのは、その優先順位(緊密にバインドされている)がパーサー上の他の演算子とうまく機能するためです。
最後にもう1つ、補助演算子が~>
あり<~
、オペランドの1つの結果、通常は有用なデータを持たないルールの定数部分を破棄します。だから、むしろ書く
"class" ~> ID <~ ")" ~ formals <~ ")"
結果のIDとフォーマルの値のみを取得します。