动态组合Scala的解析器组合器的功能是什么?

时间:2014-06-20 22:51:31

标签: scala parsing parser-combinators

我正在寻找动态组合Scala Parser Combinators的功能。例如,如果我想静态地做,我可以写:

def aDirective: Parser[String] = "a" ^^ { case _ => "a" }
def bDirective: Parser[String] = "b" ^^ { case _ => "b" }

def combinedDirective: Parser[List[String]] =
  aDirective ~ bDirective ^^ { case a ~ b => List(a, b) }

然而,为了生成解析器的组合,我希望能够动态地执行此操作,而不是静态编码。

例如:

def aDirective: Parser[String] = "a" ^^ { case _ => "a" }
def bDirective: Parser[String] = "b" ^^ { case _ => "b" }

def combinedDirective: Parser[List[String]] =
  combine(List(aDirective, bDirective))

def combine(parsers: List[Parser[T]): Parser[List[T]] = ???

我需要从解析器列表转到结果列表的解析器。所以我试图为名为combine的函数编写签名。

目前我无法弄清楚如何实现combine功能。无论我采用哪种方式,它似乎都充满了我现在无法思考如何解决的问题。例如,如何构建折叠的初始解析器?我试图尝试各种foldLeftreduceLeft构造,但似乎无法完全实现。

我正在使用Scala 2.11。有什么想法吗?

1 个答案:

答案 0 :(得分:3)

这是sequencing operationScalaz提供了一种快捷方式(通常您不需要使用Scalaz的显式实例定义样板,但是this is a special case):

import scala.util.parsing.combinator.RegexParsers
import scalaz._, Scalaz._

object MyParser extends RegexParsers {
  implicit val pm = std.util.parsing.combinator.parser.parserMonad(this)

  def aDirective: Parser[String] = "a" ^^ { case _ => "a" }
  def bDirective: Parser[String] = "b" ^^ { case _ => "b" }

  def combine[T](parsers: List[Parser[T]]): Parser[List[T]] = parsers.sequenceU

  def combinedDirective: Parser[List[String]] =
    combine(List(aDirective, bDirective))
}

然后:

scala> MyParser.parseAll(MyParser.combinedDirective, "ab")
res0: MyParser.ParseResult[List[String]] = [1.3] parsed: List(a, b)

您也可以使用折叠自己定义:

import scala.util.parsing.combinator.RegexParsers

object MyParser extends RegexParsers {
  def aDirective: Parser[String] = "a" ^^ { case _ => "a" }
  def bDirective: Parser[String] = "b" ^^ { case _ => "b" }

  def combine[T](parsers: List[Parser[T]]): Parser[List[T]] =
    parsers.foldRight(success(List.empty[T])) {
      case (p, acc) => for {
        pRes   <- p
        accRes <- acc
      } yield pRes :: accRes
    }

  def combinedDirective: Parser[List[String]] =
    combine(List(aDirective, bDirective))
}

它的工作方式完全一样。诀窍就是让基础正确 - 它需要是一个解析器,总是以空列表作为其值成功。


更新:如果您正在定义一个类而不是一个对象,那么上面的Scalaz方法将无法正常工作(出于一些奇怪的原因 - 简而言之this并非如此足够稳定)。不过,您可以非常轻松地定义自己的monad实例:

class MyParser extends RegexParsers {
  implicit val pm = new Monad[Parser] {
    def point[A](a: => A): Parser[A] = success(a)
    def bind[A, B](fa: Parser[A])(f: A => Parser[B]): Parser[B] = fa flatMap f
  }

  def aDirective: Parser[String] = "a" ^^ { case _ => "a" }
  def bDirective: Parser[String] = "b" ^^ { case _ => "b" }

  def combine[T](parsers: List[Parser[T]]): Parser[List[T]] = parsers.sequenceU

  def combinedDirective: Parser[List[String]] =
    combine(List(aDirective, bDirective))
}

你实际上不需要monad实例来使用sequence,只是一个应用程序仿函数,但是定义实际上更方便一点,monad实例在其他情况下可能会有用。