1

我从一个声称解析实数的项目中获取了这个,但它以某种方式吃掉了小数点前的部分:

object Main extends App {
  import org.parboiled.scala._

  val res = TestParser.parseDouble("2.3")
  println(s"RESULT: ${res.result}")

  object TestParser extends Parser {
    def RealNumber = rule { 
      oneOrMore(Digit) ~ optional( "." ~ oneOrMore(Digit) ) ~> { s =>
        println(s"CAPTURED '$s'")
        s.toDouble
      }
    }
    def Digit = rule { "0" - "9" }

    def parseDouble(input: String): ParsingResult[Double] =
      ReportingParseRunner(RealNumber).run(input)
  }
}

这打印:

CAPTURED '.3'
RESULT: Some(0.3)

这里有什么问题?请注意,目前我不能从 Parboiled-1 转到 Parboiled-2,因为我有一个更大的语法需要重写。

4

2 回答 2

2

半熟文档中所述,动作规则与前面~>的对等规则相匹配。在规则序列中,紧随其后的规则是,因此您只能在操作规则中获得它的匹配项。oneOrMore(Digit) ~ optional( "." ~ oneOrMore(Digit) )optional( "." ~ oneOrMore(Digit) )".3"

例如,要解决此问题,您可以将前两个元素提取到单独的规则中:

def RealNumberString = rule { 
  oneOrMore(Digit) ~ optional( "." ~ oneOrMore(Digit) ) 
}

def RealNumber = rule {
  RealNumberString ~> { s =>
    println(s"CAPTURED '$s'")
    s.toDouble
  }
}

或者将两个部分都推入堆栈,然后将它们组合起来:

def RealNumber = rule {
  oneOrMore(Digit) ~> identity ~ 
  optional( "." ~ oneOrMore(Digit) ) ~> identity ~~> { (s1, s2) =>
    val s = s1 + s2
    println(s"CAPTURED '$s'")
    s.toDouble
  }
}
于 2016-05-06T13:45:46.877 回答
0

这是一种解决方案,但它看起来很丑陋。可能有更好的方法:

def Decimal = rule {
  Integer ~ optional[Int]("." ~ PosInteger) ~~> { (a: Int, bOpt: Option[Int]) =>
    bOpt.fold(a.toDouble)(b => s"$a.$b".toDouble) /* ??? */
}}
def PosInteger  = rule { Digits ~> (_.toInt) }
def Integer     = rule { optional[Unit]("-" ~> (_ => ())) /* ??? */ ~
  PosInteger ~~> { (neg: Option[Unit], num: Int) =>
    if (neg.isDefined) -num else num
  }
}
def Digit       = rule { "0" - "9" }
def Digits      = rule { oneOrMore(Digit) }

def parseDouble(input: String): ParsingResult[Double] =
  ReportingParseRunner(Decimal).run(input)
于 2016-05-06T12:33:00.817 回答