使用scala-parser-combinator进行递归定义

时间:2018-01-11 12:54:14

标签: scala parser-combinators left-recursion

我一直在尝试使用scala-parser-combinator库构建一个SQL解析器,我已将其大大简化为下面的代码。

class Expression
case class FalseExpr() extends Expression
case class TrueExpr() extends Expression
case class AndExpression(expr1: Expression, expr2: Expression) extends Expression

object SimpleSqlParser {
  def parse(sql: String): Try[Expression] = new SimpleSqlParser().parse(sql)
}

class SimpleSqlParser extends RegexParsers {
  def parse(sql: String): Try[_ <: Expression] = parseAll(expression, sql) match {
    case Success(matched,_) => scala.util.Success(matched)
    case Failure(msg,remaining) => scala.util.Failure(new Exception("Parser failed: "+msg + "remaining: "+ remaining.source.toString.drop(remaining.offset)))
    case Error(msg,_) => scala.util.Failure(new Exception(msg))
  }

  private def expression: Parser[_ <: Expression] =
    andExpr | falseExpr | trueExpr

  private def falseExpr: Parser[FalseExpr] =
    "false" ^^ (_ => FalseExpr())

  private def trueExpr: Parser[TrueExpr] = "true" ^^ (_ => TrueExpr())

  private def andExpr: Parser[Expression] =
    expression ~ "and" ~ expression ^^ { case e1 ~ and ~ e2 => AndExpression(e1,e2)}
}

没有&#39;和&#39;解析,它工作正常。但我希望能够解析诸如&#​​39; true AND(false或true)&#39;之类的内容。当我添加&#39;和&#39;部分到表达式的定义,我得到一个StackOverflowError,堆栈在&#39;和&#39;的定义之间交替。和&#39;表达&#39;。

我理解为什么会发生这种情况 - 表达式的定义以和开头,反之亦然。但这似乎是模拟这个问题最自然的方式。实际上,表达式也可以是LIKE,EQUALS等。为了解决递归定义的问题,是否存在另一种模拟此类事物的方法。

1 个答案:

答案 0 :(得分:6)

scala.util.parsing.combinator.RegexParsers无法处理left-recursive语法。您的语法可以通过以下生产规则进行总结:

expression -> andExpr | falseExpr | trueExpr
...
andExpr -> expression "and" expression

expression通过andExpr间接左递归。

为了避免无限递归,你需要重新设计语法,使其不再是左递归的。一种常用的方法是使用重复组合器,例如chainl1

  private def expression: Parser[_ <: Expression] =
    chainl1(falseExpr | trueExpr, "and" ^^^ { AndExpression(_, _) })

Live on Scastie

新的expression匹配一个或多个falseExpr / trueExpr,由"and"分隔,并将匹配的元素与AndExpression组合在一个左关联中办法。从概念上讲,它对应于以下生产规则:

expression -> (falseExpr | trueExpr) ("and" (falseExpr | trueExpr))*

如果您的语法包含许多纠结的左递归生成规则,您可能需要考虑直接支持左递归的其他解析器组合库,例如GLL combinators