使用scala中的解析器组合器逐步评估

时间:2012-09-24 19:43:34

标签: parsing scala parser-combinators

我刚学习Scala解析器组合库。我已经尝试了一个工作解析器,它使用抽象语法树解析一些算术表达式。 所以当我打电话时

phrase(expr)(tokens)

我的解析器解析所有输入,然后给我一个评估。但是,我如何逐步评估呢?

  

3 + 4 * 7

打印

  

3 + 28

然后

  

31

分开排列。

我已经通过api扫描了但那里的文档并不是很有帮助... 谢谢你的帮助。

2 个答案:

答案 0 :(得分:5)

这是一个非常简单的实现你想要做的事情:

首先,我们定义一个表达式层次结构。您需要根据具体问题进行定制。

trait Expr {
  def eval: Int
}
case class IntLeaf(n: Int) extends Expr {
  def eval = n
  override def toString = "%d".format(n)
}
case class Sum(a: Expr, b: Expr) extends Expr {
  def eval = a.eval + b.eval
  override def toString = "(%s + %s)".format(a, b)
}

然后,一个只组合最底部分支的函数。

def combineLeaves(e: Expr): Expr = {
  e match {
    case IntLeaf(n) => IntLeaf(n)
    case Sum(IntLeaf(a), IntLeaf(b)) => IntLeaf(a + b)
    case Sum(a, b) => Sum(combineLeaves(a), combineLeaves(b))
  }
}

然后,一个将树一次组合在一起的功能,随时打印。

def printEval(e: Expr) {
  println(e)
  e match {
    case IntLeaf(n) =>
    case _ => printEval(combineLeaves(e))
  }
}

现在,Parser。同样,您必须根据您的数据进行定制。

object ArithmeticParser extends RegexParsers {
  private def int: Parser[IntLeaf] = regex(new Regex("""\d+""")).map(s => IntLeaf(s.toInt))
  private def sum: Parser[Sum] = ("(" ~> expr ~ "+" ~ expr <~ ")").map { case (a ~ _ ~ b) => Sum(a, b) }
  private def expr = int | sum
  def parse(str: String): ParseResult[Expr] = parseAll(expr, str)
  def apply(str: String): Expr = ArithmeticParser.parse(str) match {
    case ArithmeticParser.Success(result: Expr, _) => result
    case _ => sys.error("Could not parse the input string: " + str)
  }

}

以下是你如何使用它:

scala> printEval(ArithmeticParser("((1 + 7) + ((3 + 9) + 5))"))
((1 + 7) + ((3 + 9) + 5))
(8 + (12 + 5))
(8 + 17)
25

答案 1 :(得分:2)

Parser组合器从不给你任何评价。使用解析器组合器,您解析输入字符串并构建表示它的一些数据结构。评估是另一个步骤,您以某种方式处理数据结构并执行所需的简化。因此,使用表达式3+4*7,在解析阶段,您可以构建以下抽象语法树:

   +
  / \
 3   *   
    / \
   4   7

然后,在评估阶段,递归地遍历树,并且对于每个非叶节点,将节点操作应用于其左右子树的评估结果。

如果文档没有帮助,您可以参考parser combinators chapterProgramming in Scala,其第一版可免费获得。

我最近还在Scala中为解析器组合器写了一个blogpost,在那里我讨论了一个与你的类似的场景。