Scala PackratParsers(解析器组合器)和左关联性

时间:2015-12-06 15:16:07

标签: scala parser-combinators left-recursion

我使用Scala的PackratParsers(解析器组合器),使用以下形式的左递归语法

lazy val expr: PackratParser[Expr] = (
    ...
  | expr ~ (":" ~ expr).+ ^^ {
      case expr ~ rest => (expr /: rest)(combineBinary)
    }
  | ...
)

def combineBinary(acc: Expr, next: String ~ Expr) = next match {
  case op ~ expr => FunctionCall(op, acc, expr)
}

我想要二元运算符":"为左关联,使x1:x2:...:xn形式的表达式解析为(((x1:x2):x3):...:xn),即导致FunctionCall(":", FunctionCall(":", FunctionCall(":", x1, x2), x3), ...)形式的AST。

令人惊讶的是,使用上面定义的PackratParsers语法,得到的AST仍然是右关联的。为什么会这样,可以采取哪些措施来改变它?

我发现了this关于Scala解析器组合器和运算符关联性的讨论,但它似乎没有给出我的问题答案。

1 个答案:

答案 0 :(得分:0)

TL;博士 我不知道packrat能否帮助您解决两个大问题。 It did save me from stackoverflow但是我没有那种明显的左撇子。

我的意思是你的递归expr + expr永远不会终止。我知道你在某处有一些归纳基础,即expr = expr + expr | term

现在,您可以轻松地通过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))
}

堆栈溢出。 It saved me once但我没有那种明显的左撇子。而且,我不知道如何从你提出的第二个问题中拯救你。

无论如何,您必须消除将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)