Метод ~
в анализаторе объединяет два анализатора в одном, который последовательно применяет два исходных анализатора и возвращает два результата.Это может быть просто (в Parser[T]
)
def ~[U](q: =>Parser[U]): Parser[(T,U)].
Если вы никогда не объединяете более двух парсеров, это будет нормально.Однако, если вы соедините три из них, p1
, p2
, p3
, с типами возврата T1
, T2
, T3
, то p1 ~ p2 ~ p3
, что означает p1.~(p2).~(p3)
, имеет тип Parser[((T1, T2), T3)]
.И если вы объедините пять из них, как в вашем примере, это будет Parser[((((T1, T2), T3), T4), T5)]
.Затем, когда вы сопоставляете результат с результатом, у вас также будут все эти паратезы:
case ((((_, id), _), formals), _) => ...
Это довольно неудобно.
Затем наступает хитрый синтаксический трюк.Когда у класса case есть два параметра, он может появляться в инфиксной, а не префиксной позиции в шаблоне.То есть, если у вас есть case class X(a: A, b: B)
, вы можете сопоставить шаблон с case X(a, b)
, но также с case a X b
.(Это то, что делается с шаблоном x::xs
для соответствия непустому списку, ::
- это класс case).Когда вы пишете регистр a ~ b ~ c
, это означает case ~(~(a,b), c)
, но гораздо приятнее и приятнее, чем case ((a,b), c)
, что сложно понять правильно.
Таким образом, метод ~
в Parser возвращает Parser[~[T,U]]
вместо Parser[(T,U)]
, так что вы можете легко сопоставить шаблон с результатом нескольких ~.Кроме того, ~[T,U]
и (T,U)
- это почти одно и то же, настолько изоморфное, насколько это возможно.
Одно и то же имя выбрано для метода объединения в синтаксическом анализаторе и для типа результата, потому что результирующийкод естественно читать.Сразу видно, как каждая часть в обработке результатов связана с элементами правила грамматики.
parser1 ~ parser2 ~ parser3 ^^ {case part1 ~ part2 ~ part3 => ...}
Тильда выбрана потому, что ее приоритет (он тесно связан) хорошо сочетается с другими операторами парсера.
И, наконец, есть вспомогательные операторы ~>
и <~
, которые отбрасывают результат одного из операндов, обычно это постоянные части в правиле, которое не несет полезных данных.Поэтому лучше написать
"class" ~> ID <~ ")" ~ formals <~ ")"
и получить только значения идентификатора и формальные значения в результате.