tl; 博士,我不知道 Packrat 如何让您免于遇到的两个大问题。它确实将我从 stackoverflow 中解救了出来,但我并没有如此明目张胆的左顾右盼。
我的意思是你的递归expr + expr
永远不应该终止。我知道你在某处有一些归纳基础,即expr = expr + expr | term
。
现在,您可以通过 for right associative 轻松建立右关联性,term + expr | term
因为当找到最后一项时,您处于 + 递归之下。同样,您使左关联性expr + term | term
。左结合导致左递归,你永远不会在最后期限。甚至 Packrat 也没有从中拯救。我不明白你是如何得到你的结果的。矿
object EP extends JavaTokenParsers with PackratParsers {
def expr: Parser[_] = expr ~ ("+" ~> expr) | ident /*^^ {
case ident ~ rest => (ident /: rest){case (acc, e) => acc + s" + (${e.toString})"}
} | ident*/
}
List("a", "a + b", "a + b + c+ d") foreach {input =>
println("left: " + EP.parseAll(EP.expr, input))
}
堆栈溢出。它救了我一次,但我没有如此明目张胆的左翼回避。而且,我不知道它如何使您免于您提出的第二个问题。
无论如何,您必须消除递归更改expr + term | term
为
def left: Parser[_] = ident ~ appendix
def appendix = "+" ~> left | ""
但这又是正确的递归,因为我们再次看到 ident 是第一个节点。
解决方案:因此,您只需使用所有人都会做的事情:使用rep
解析器,它为您提供一个列表,可从左侧迭代:
def right: Parser[_] = ident ~ ("+" ~> right) ^^ {case head ~ tail => s"Right($head, $tail)"} | ident
lazy val left: Parser[_] = ident ~ rep("+" ~> ident) ^^
{case head ~ tail => (head /: tail){case (acc, expr) => s"Left($acc, $expr)"}}
println("right => " + parseAll(right, "a + b + c+ d"))
println("left => " + parseAll(left, "a + b + c+ d"))
生产
right => [1.13] parsed: Right(a, Right(b, Right(c, d)))
left => [1.13] parsed: Left(Left(Left(a, b), c), d)