Scala Combinator Parser映射问题

时间:2015-11-02 20:27:08

标签: functional-programming scala parsing

我一直在靠墙撞墙,试图找出如何让它发挥作用。初始解析器工作得很好,但是当我尝试从中获取Map时,它只给出初始数值,但不是值块。

格式为“float:[label float:[optinallabel float:] ...]”如:

     412285.556: [Label 0.0:[Label1 1.0:][Label2 2.0:]

要列出的解析器是:

object ParseList extends JavaTokenParsers {
    def sep : Parser[String] = ":"
    def string : Parser[Any] = """[\.a-zA-Z0-9]+""".r
    def num: Parser[Any] =  floatingPointNumber <~ sep
    def valueBlock: Parser[Any] =  "["~>rep(valueBlock)<~"]" | string ~ floatingPointNumber <~ sep
    def expr: Parser[Any] = num ~ rep(valueBlock )
    def apply(in: String) = parseAll(expr,in)
}

测试给出:

scala> ParseList("""412285.556: """)
res150: ParseList.ParseResult[Any] = [1.13] parsed: (412285.556~List())

scala> ParseList("""412285.556: [Label 1.0:]""")
res151: ParseList.ParseResult[Any] = [1.25] parsed: (412285.556~List(List((Label~1.0))))

scala> ParseList("""412285.556: [Label 0.0:[Label1 1.0:][Label2 2.0:]]""")
res152: ParseList.ParseResult[Any] = [1.51] parsed: (412285.556~List(List((Label~0.0), List((Label1~1.0)), List((Label2~2.0)))))

当我尝试将其设为Map时,如果只返回数字,则调用成员例程。请参阅调试输出。

地图解析器:

object ParseMap extends JavaTokenParsers {
  // Seperator
  def sep : Parser[String] = ":"
  // string
  def string : Parser[String] = """[a-zA-Z][a-zA-Z0-9]+""".r
  // Block within [] with label value: and option additional blocks
  def valueBlock: Parser[(String,Any)] =
      member <~ rep(obj)
  // Member - value pair within a block
  def member: Parser[(String, Any)] =
    string ~ floatingPointNumber <~ sep ^^
        { case s ~ n => (s, n); println("In Member s=" +s+" n="+n); (s, n)} 
  def obj: Parser[Map[String,Any]] =
    "["~> rep(valueBlock) <~"]"  ^^ {Map() ++ _}
  // Initial number value of the data
  def num: Parser[(String, Any)] =
          floatingPointNumber <~ sep ~ rep(obj) ^^
          {  case floatingPointNumber => ("Num", floatingPointNumber) }
  // order of operations
  def value: Parser[Any] = (
      num
      | obj
      | member
      | floatingPointNumber
      | string
      )
  def apply(in: String) = parseAll(value,in)
}

测试给出:

scala> ParseMap("""412285.556: """)
res154: ParseMap.ParseResult[Any] = [1.13] parsed: (Num,412285.556)

scala> ParseMap("""412285.556: [Label 1.0:]""")
In Member s=Label n=1.0
res155: ParseMap.ParseResult[Any] = [1.25] parsed: (Num,412285.556)

scala> ParseMap("""412285.556: [Label 0.0:[Label1 1.0:][Label2 2.0:]]""")
In Member s=Label n=0.0
In Member s=Label1 n=1.0
In Member s=Label2 n=2.0
res156: ParseMap.ParseResult[Any] = [1.51] parsed: (Num,412285.556)

我尝试从中获取单个地图的尝试都失败了。任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:3)

Scala的解析器组合器不是最容易使用的,它们非常慢。请改为fastparse

我无法从这些数据中找出你想要的东西(这是一种非常奇怪的格式!),但是有一个很好的guide to getting started

您的核心标签解释器可能看起来像

val Pair = P(Label ~ " " ~ Num ~ ":")
val MapLine = P("[" Pair ~ ("[" ~ Pair ~ "]").rep ~ "]").
  map{ case (pair, pairs) => pair :: pairs.toList }

答案 1 :(得分:2)

主要问题是您在<~方法中使用num组合器 - 这会抛弃所有解析后的数据(包括rep(obj)解析你想要的地图结果)。将该行修改为:

def num: Parser[(String, Any)] =
      floatingPointNumber ~ sep ~ rep(obj) ^^
      {  case floatingPointNumber ~ sep ~ objs => ("Num", (floatingPointNumber, objs)) }

然后你开始得到如下结果:

scala> ParseMap("""412285.556: [Label 0.0:[Label1 1.0:][Label2 2.0:]]""")
In Member s=Label n=0.0
In Member s=Label1 n=1.0
In Member s=Label2 n=2.0
res3: ParseMap2.ParseResult[Any] = [1.51] parsed: (Num,(412285.556,List(Map(Label -> 0.0))))

虽然它可能看起来不像你想要的那样,但应该给你一个进一步发展的起点。