2016-05-06 57 views
1

我把這個從聲稱解析實數的一個項目,但它在某種程度上吃小數部分前:Parboiled - 如何解析一個實數?

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,因爲我有一個更大的語法,必須重寫。

回答

2

正如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 
    } 
} 
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)