简体   繁体   English

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

[英]What would be the function to dynamically combine Scala's parser combinators?

I am looking for a function to dynamically combine Scala Parser Combinators. 我正在寻找一个动态组合Scala Parser Combinators的功能。 For example, if I want to do it statically I can write: 例如,如果我想静态地做,我可以写:

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) }

However rather than coding this statically I want to be able to do this dynamically, for the purposes of generating combinations of parsers. 然而,为了生成解析器的组合,我希望能够动态地执行此操作而不是静态编码。

For example: 例如:

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]] = ???

I think I need to go from a List of parsers to a Parser of List of Results. 我需要从解析器列表转到结果列表的解析器。 So I have attempted to write a signature for a function named combine . 所以我试图为名为combine的函数编写签名。

At the moment I cannot figure out how to implement the combine function. 目前我无法弄清楚如何实现combine功能。 Whichever way I attempt it, it seems to be fraught with problems that I cannot think how to solve at the moment. 无论我采用哪种方式,它似乎都充满了我现在无法思考如何解决的问题。 For example how do I construct an initial parser for a fold? 例如,如何构建折叠的初始解析器? I have tried to experiment with various foldLeft and reduceLeft constructs, but cannot seem to quite get there. 我试图尝试各种foldLeftreduceLeft结构,但似乎无法完全实现。

I am using Scala 2.11. 我正在使用Scala 2.11。 Any ideas? 有任何想法吗?

This is a sequencing operation , and Scalaz provides a shortcut (normally you wouldn't need the explicit instance definition boilerplate with Scalaz, but this is a special case ): 这是一个排序操作Scalaz提供了一个快捷方式(通常你不需要使用Scalaz的显式实例定义样板,但这是一个特例 ):

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))
}

And then: 然后:

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

You can also define it yourself with a fold: 你也可以用折叠自己定义它:

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))
}

And it'll work exactly the same. 它的工作方式完全相同。 The trick is just getting the base right—it needs to be a parser that always succeeds with the empty list as its value. 诀窍就是让基础正确 - 它需要是一个解析器,总是以空列表作为其值成功。


Update: if you're defining a class, not an object, the Scalaz approach above won't work (for a number of weird reasons—in short the this isn't stable enough). 更新:如果你定义的是一个类,而不是一个对象,那么上面的Scalaz方法将不起作用(出于一些奇怪的原因 - 简而言之, this不够稳定)。 You can define your own monad instance pretty easily, though: 您可以非常轻松地定义自己的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))
}

You actually don't need a monad instance here to use sequence , just an applicative functor, but the definition is actually a little more convenient and the monad instance may be useful in other cases. 你实际上不需要monad实例来使用sequence ,只需要一个应用程序仿函数,但实际上定义更方便,monad实例在其他情况下可能会有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM