【问题标题】:Grammars, Scala Parsing Combinators and Orderless Sets语法、Scala 解析组合器和无序集
【发布时间】:2009-11-23 08:03:05
【问题描述】:

我正在编写一个接收各种“命令”字符串的应用程序。我一直在查看 Scala 组合器库来标记命令。我发现在很多情况下我想说:“这些标记是无序的集合,因此它们可以按任何顺序出现,有些可能不会出现”。

以我目前的语法知识,我必须定义所有序列组合(伪语法):

command = action~content
action = alphanum
content  = (tokenA~tokenB~tokenC | tokenB~tokenC~tokenA | tokenC~tokenB~tokenA ....... )

所以我的问题是,考虑到 tokenA-C 是独一无二的,是否有更短的方法来使用语法定义一组任意顺序?

【问题讨论】:

    标签: scala grammar bnf ebnf


    【解决方案1】:

    您可以使用“解析器。^?”运算符来检查一组解析元素的重复项。

      def tokens = tokenA | tokenB | tokenC
      def uniqueTokens = (tokens*) ^? (
        { case t if (t == t.removeDuplicates) => t },
        { "duplicate tokens found: " + _ })
    

    这是一个示例,允许您以任意顺序输入四个 stooges 中的任何一个,但如果遇到重复则无法解析:

    package blevins.example
    
    import scala.util.parsing.combinator._  
    
    case class Stooge(name: String)
    
    object StoogesParser extends RegexParsers {
      def moe = "Moe".r
      def larry = "Larry".r
      def curly = "Curly".r
      def shemp = "Shemp".r
      def stooge = ( moe | larry | curly | shemp ) ^^ { case s => Stooge(s) }
      def certifiedStooge = stooge | """\w+""".r ^? (
        { case s: Stooge => s },
        { "not a stooge: " + _ })
    
      def stooges = (certifiedStooge*) ^? (
        { case x if (x == x.removeDuplicates) => x.toSet },
        { "duplicate stooge in: " + _ })
    
      def parse(s: String): String = {
        parseAll(stooges, new scala.util.parsing.input.CharSequenceReader(s)) match {
          case Success(r,_) => r.mkString(" ")
          case Failure(r,_) => "failure: " + r
          case Error(r,_) => "error: " + r
        }
      }
    
    }
    

    还有一些用法示例:

    package blevins.example
    
    object App extends Application {
    
      def printParse(s: String): Unit = println(StoogesParser.parse(s))
    
      printParse("Moe Shemp Larry")
      printParse("Moe Shemp Shemp")
      printParse("Curly Beyonce")
    
      /* Output:
         Stooge(Moe) Stooge(Shemp) Stooge(Larry)
         failure: duplicate stooge in: List(Stooge(Moe), Stooge(Shemp), Stooge(Shemp))
         failure: not a stooge: Beyonce
      */
    }
    

    【讨论】:

      【解决方案2】:

      有很多方法可以解决它。例如,看一下解析器here。它接受 4 个预定义的数字,可以出现在任何其他数字中,但必须出现一次,而且只能出现一次。

      OTOH,如果这种模式经常发生,你可以编写一个组合器:

      def comb3[A](a: Parser[A], b: Parser[A], c: Parser[A]) =
        a ~ b ~ c | a ~ c ~ b | b ~ a ~ c | b ~ c ~ a | c ~ a ~ b | c ~ b ~ a
      

      【讨论】:

        【解决方案3】:

        我不会尝试在语法上强制执行此要求。我会编写一个允许集合中允许多个标记的产品,然后使用非解析方法来确定实际给出的关键字的可接受性。除了允许更简单的语法之外,它还允许您在发出有关错误用法的诊断后更轻松地继续解析。

        兰德尔·舒尔茨

        【讨论】:

          【解决方案4】:

          我不知道您要支持哪种结构,但我认为您应该指定更具体的语法。从您的评论到另一个答案:

          待办事项消息:将待办事项类链接到数据库

          我猜你不想接受类似的东西

          待办事项消息:数据库待办事项链接类

          所以你可能想要定义一些消息级别的关键字,比如“link”和“to”...

          def token = alphanum~':'~ "link" ~ alphanum ~ "class" ~ "to" ~ alphanum 
            ^^ { (a:String,b:String,c:String) => /* a == "message", b="Todo", c="database" */ }
          

          我猜你必须在那个级别定义你的语法。

          【讨论】:

            【解决方案5】:

            如果您经常遇到这种情况,您当然可以编写一个组合规则为您执行此操作。

            另一方面,也许存在使“tokenA..C”只是“token”的选项,然后在“token”的处理程序内部进行区分

            【讨论】:

            • 在这种情况下,每个标记都是一个 json 样式的对象属性。所以一个命令可能看起来像“todo message:link Todo class to database” due: next tuesday”。所以在 scala 样式中定义的通用规则类似于“token = alphanum~':'~repsep(alphanum, '')。但我确实需要以不同的方式处理特定属性。
            • 而且您必须确保同一事件不会出现多次?
            • 是的,这是计划,有些属性是可选的,它们应该只出现一次。
            • 我认为如果不手动指定,这在语法层面上是行不通的。
            猜你喜欢
            • 2012-05-03
            • 1970-01-01
            • 1970-01-01
            • 2015-07-12
            • 1970-01-01
            • 2020-01-17
            • 2015-06-23
            • 2023-03-12
            • 2010-10-06
            相关资源
            最近更新 更多