Parboiled - 如何解析实数?
Parboiled - how to parse a real number?
我从一个声称解析实数的项目中获取了这个,但它以某种方式吃掉了小数前的部分:
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,因为我有一个更大的语法,必须重写。
这是一种解决方案,但它看起来很丑陋。可能有更好的方法:
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)
如 parboiled documentation 中所述,像 ~>
这样的操作规则匹配 紧接在前的对等规则 。在规则序列 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
}
}
我从一个声称解析实数的项目中获取了这个,但它以某种方式吃掉了小数前的部分:
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,因为我有一个更大的语法,必须重写。
这是一种解决方案,但它看起来很丑陋。可能有更好的方法:
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)
如 parboiled documentation 中所述,像 ~>
这样的操作规则匹配 紧接在前的对等规则 。在规则序列 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
}
}