忽略 JavaToken 组合器解析器中的前缀

Ignoring prefixes in a JavaToken combinator parser

我正在尝试使用 JavaToken 组合器解析器提取位于较大字符串中间的特定匹配项(即忽略一组随机前缀字符)。但是我无法让它工作并且认为我被贪婪的解析器 and/or CRs LFs 抓住了。 (前缀字符基本上可以是任何东西)。我有:

class RuleHandler extends JavaTokenParsers {

  def allowedPrefixChars = """[a-zA-Z0-9=*+-/<>!\_(){}~\s]*""".r

  def findX: Parser[Double] = allowedPrefixChars ~ "(x=" ~> floatingPointNumber <~ ")" ^^ { case num => num.toDouble}

}

然后在我的测试用例中..

    "when looking for the X value" in {
  "must find and correctly interpret X" in {
    val testString =
      """
        |Looking (only)
        |for (x=45) within
        |this string
      """.stripMargin
    val answer = ruleHandler.parse(ruleHandler.findX, testString)
    System.out.println(" X value is : " + answer.toString)
  }
}

我认为它类似于 this SO question。任何人都可以看到有什么问题吗?谢谢。

首先,你不应该在 """ """:

中两次转义 "\s"
def allowedPrefixChars = """[a-zA-Z0-9=*+-/<>!\_(){}~\s]*?""".r

在你的情况下,它被单独解释 "\""s"s 作为符号,而不是 \s

其次,您的 allowedPrefixChars 解析器包括 (x=,因此它捕获了整个字符串,包括 (x=,没有留下任何内容到后续的解析器。

解决方案是更具体地说明您想要的前缀:

object ruleHandler extends JavaTokenParsers {

  def allowedPrefixChar: Parser[String] = """[a-zA-Z0-9=*+-/<>!\_){}~\s]""".r //no "(" here

  def findX: Parser[Double] = rep(allowedPrefixChar | "\((?!x=)".r ) ~ "(x=" ~> floatingPointNumber <~ ")" ^^ { case num => num.toDouble}
}

ruleHandler.parse(ruleHandler.findX, testString)
res14: ruleHandler.ParseResult[Double] = [3.11] parsed: 45.0

我已经告诉解析器忽略 (,后面有 x=(只是 negative lookahead)。

选择:

"""\(x=(.*?)\)""".r.findAllMatchIn(testString).map(_.group(1).toDouble).toList
res22: List[Double] = List(45.0)

如果您想正确使用解析器,我建议您描述整个 BNF 语法(包括所有可能的 ()= 用法)——而不仅仅是片段.例如,如果它是关键字,则将 (only) 包含到您的解析器中,"(" ~> valueName <~ "=" ~ value 以获得值。不要忘记 scala-parser 旨在 return 你的 AST,而不仅仅是一些匹配的值。纯正则表达式更适合从非结构化数据进行正则匹配。

如何以正确的方式使用解析器的示例(未尝试编译):

trait Command
case class Rule(name: String, value: Double) extends Command
case class Directive(name: String) extends Command

class RuleHandler extends JavaTokenParsers { //why `JavaTokenParsers` (not `RegexParsers`) if you don't use tokens from Java Language Specification ?

  def string = """[a-zA-Z0-9*+-/<>!\_{}~\s]*""".r //it's still wrong you should use some predefined Java-like literals from **JavaToken**Parsers    
  def rule = "(" ~> string <~ "=" ~> string <~ ")" ^^ { case name ~ num => Rule(name, num.toDouble} }   
  def directive = "(" ~> string <~ ")" ^^ { case name => Directive(name) }   
  def commands: Parser[Command] =  repsep(rule | directive, string)

}

如果您需要处理自然语言(Chomsky type-0), scalanlp 或类似的东西更合适。