2016-05-06 12 views
1

Ich nahm diese von einem Projekt, die reelle Zahlen zu analysieren behauptet, aber es frisst irgendwie die vorge Dezimalteil:Gekocht - wie man eine reelle Zahl parst?

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

Diese Drucke:

CAPTURED '.3' 
RESULT: Some(0.3) 

Was ist hier falsch? Beachten Sie, dass ich derzeit nicht von Parbooled-1 zu Parboiled-2 wechseln kann, da ich eine größere Grammatik habe, die neu geschrieben werden müsste.

Antwort

2

Wie in parboiled documentation angegeben, Handlungsregeln wie ~> nehmen, das Spiel der unmittelbar vorhergehende Peer Regel. In der Regelsequenz oneOrMore(Digit) ~ optional("." ~ oneOrMore(Digit)) ist die unmittelbar vorhergehende Regel optional("." ~ oneOrMore(Digit)), sodass Sie nur die Übereinstimmung ".3" in der Aktionsregel erhalten.

Um dies zu beheben, dass Sie kann zum Beispiel die ersten beiden Elemente in eine separate Regel extrahieren:

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

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

oder beide Teile auf den Stapel schieben und sie dann kombinieren:

def RealNumber = rule { 
    oneOrMore(Digit) ~> identity ~ 
    optional("." ~ oneOrMore(Digit)) ~> identity ~~> { (s1, s2) => 
    val s = s1 + s2 
    println(s"CAPTURED '$s'") 
    s.toDouble 
    } 
} 
0

Hier ist eine Lösung, aber es sieht sehr hässlich aus. Wahrscheinlich gibt es eine bessere Art und Weise:

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) 
Verwandte Themen