【问题标题】:Scala PackratParsers (parser combinators) and left-associativityScala PackratParsers(解析器组合器)和左关联性
【发布时间】:2016-03-11 04:39:10
【问题描述】:

我正在使用具有以下形式的左递归语法的 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 解析器组合器和运算符关联性的讨论,但它似乎没有在这里回答我的问题。

【问题讨论】:

  • 我处理了同样的问题,但我能够使用this pdf 解决它。第 21 页有一个很好的例子。

标签: scala parser-combinators left-recursion


【解决方案1】:

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)

【讨论】:

  • 你必须使用带有 PackratParsers 而不是 defs 的惰性 val - 那么左递归本身就不是问题
  • @MartinStuder 这对我来说仍然是个问题。 object EP extends JavaTokenParsers with PackratParsers { lazy val expr: Parser[_] = expr ~ ("+" ~> expr) | ident } ; println("left: " + EP.parseAll(EP.expr, "a + b + c + d")) 溢出堆栈,我看不到在哪里可以将更多的 defs 替换为 val。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-17
  • 1970-01-01
  • 1970-01-01
  • 2010-10-06
  • 1970-01-01
  • 1970-01-01
  • 2012-05-03
相关资源
最近更新 更多