2014-06-20 40 views
3

我正在寻找一个动态组合Scala Parser Combinators的功能。例如,如果我想静做,我可以这样写:动态组合Scala的解析器组合器的功能是什么?

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。有任何想法吗?

回答

3

这是一个sequencing operation,并Scalaz提供了一个快捷方式(通常你会不会需要明确的实例定义样板与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不够稳定)。你可以很容易地定义自己的单子例如,虽然:

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

你其实并不需要一个单子实例这里使用sequence,只是一个适用函子,但定义实际上是一个小更方便,单子实例在其他情况下可能会有用。

+0

对于您提供的Scalaz示例,我收到了一些编译错误。我正在使用Scala 2.11和Scalaz 7.0.6。你和我的唯一区别就是我使用'class'而不是'object',不知道这个问题应该重要吗?我看到的编译错误是: – adamretter

+0

错误:(14,71)隐式未找到:scalaz.Unapply [scalaz.Applicative,TestParser.this.Parser [T]]。无法不应用类型'TestParser.this.Parser [T]'到类型'scalaz.Applicative'分类的类型为'M [_]'的类型构造器中。通过编译'隐式[scalaz.Applicative [type constructor]]'来检查类型类是否定义,并检查对象UnApply中的含义,该对象只覆盖常见类型的“形状”。 def combine [T](parsers:List [Parser [T]]):Parser [List [T]] =解析器。序列U ^ – adamretter

+0

错误:(14,71)方法sequenceU没有足够的参数:(隐式G:scalaz.Unapply [scalaz.Applicative,TestParser.this.Parser [T]])G.M [List [G.A]]。 未指定的值参数G. def combine [T](解析器:List [Parser [T]]):Parser [List [T]] = parsers.sequenceU ^ – adamretter

相关问题