我对 Fastparse 了解不多,但我还是会尝试回答您的问题。现在,你的语法看起来像这样:
expr ::= div | num
div ::= expr "/" expr
num ::= 0 | 1 | ...
因此,如果您想解析1/2
为表达式,它将首先尝试匹配div
. 为此,它会尝试expr
再次匹配,并且基本上无限地继续。正如上面评论中所建议的,我们可以通过放置num
before来解决这个问题:div
expr ::= num | div
Or
def expr[_: P]: P[Expr] = P(num | div)
成功!或者是吗?更仔细地查看结果后,您会发现它不是 aDiv(Num(1), Num(2))
而是只是 a Num(1)
。要解决此问题,请使用End
def expr[_: P]: P[Expr] = P((num | div) ~ End)
现在它失败了,说它找到了“/ 2”。它首先匹配成功num
,因此没有理由认为第一个数字是除法运算的一部分。所以我们毕竟必须使用div
before ,以确保使用更大的模式,但需要做一些事情来避免递归。num
我们可以这样重构它:
expr ::= div
div ::= num ("/" num)*
div
不仅匹配除法,它还可以匹配单个数字,但它会尽可能尝试匹配除法。在 Scala 中,这将是:
def div[_: P] = P(num ~ ("/" ~/ num).rep).map {
case (a, ops) => ops.foldLeft(a: Expr){ case (a, b) => Div(a, b) }
}
def expr[_: P]: P[Expr] = P(div ~ End)
这样,我们可以匹配“1/2”、“1/2/3”、“1/2/3/4”等。
输出parse("1/2/3/4", expr(_))
为Parsed.Success(Div(Div(Div(Num(1),Num(2)),Num(3)),Num(4)), 7)