解析器上的~
方法将两个解析器合并为一个,它将连续应用两个原始解析器并返回两个结果。这可能是简单的(在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 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是因为它的优先级(它紧紧地绑定)与解析器上的其他运算符很好地配合。
最后一点,有辅助运算符~>
和<~
,它们丢弃操作数之一的结果,通常是规则中不携带有用数据的常量部分。所以人们宁愿写
"class" ~> ID <~ ")" ~ formals <~ ")"
并只得到在结果中的ID和形式的值。
非常感谢您的解释。我对scala的“隐式转换”功能并不熟悉。 关于该主题的一篇很好的文章可以在这里找到:http://scalada.blogspot.com/2008/03/implicit-conversions-magical-and.html – Jano