From 13675a9c058eaf90007bdf34d95bc5f6140ddd61 Mon Sep 17 00:00:00 2001 From: Tim Nieradzik Date: Tue, 20 Aug 2019 15:10:31 +0300 Subject: [PATCH] Rules: Fix parsing of floating point numbers - Parse floating point numbers containing fractional and exponent part - Support infinity and NaN constants Closes #11. --- shared/src/main/scala/toml/Rules.scala | 22 +++++-- shared/src/test/scala/toml/RulesSpec.scala | 72 ++++++++++++++++++++++ 2 files changed, 89 insertions(+), 5 deletions(-) diff --git a/shared/src/main/scala/toml/Rules.scala b/shared/src/main/scala/toml/Rules.scala index b6a8197..01e74cc 100644 --- a/shared/src/main/scala/toml/Rules.scala +++ b/shared/src/main/scala/toml/Rules.scala @@ -72,15 +72,27 @@ object Rules extends PlatformRules { def rmUnderscore(s: String) = s.replace("_", "") - val +- = P(CharIn(List('+', '-'))) + val sign = P(CharIn("+-")) val integral = P(digits.rep(min = 1, sep = "_")) val fractional = P("." ~ integral) - val exponent = P(CharIn("eE") ~ +-.? ~ integral) + val exponent = P(CharIn("eE") ~ sign.? ~ integral) val integer: Parser[Value.Num] = - P(+-.? ~ integral).!.map(s => Value.Num(rmUnderscore(s).toLong)) + P(sign.? ~ integral).!.map(s => Value.Num(rmUnderscore(s).toLong)) val double: Parser[Value.Real] = - P(+-.? ~ integral ~ (fractional | exponent)).!.map(s => - Value.Real(rmUnderscore(s).toDouble)) + P( + sign.?.! ~ + ( + P("inf").map(_ => Double.PositiveInfinity) | + P("nan").map(_ => Double.NaN) | + P(integral ~ ( + (fractional ~ exponent) | + fractional | + exponent + )).!.map(s => rmUnderscore(s).toDouble) + ) + ).map { case (sign, value) => + if (sign == "-") Value.Real(-value) else Value.Real(value) + } val `true` = P("true") .map(_ => Value.Bool(true)) val `false` = P("false").map(_ => Value.Bool(false)) diff --git a/shared/src/test/scala/toml/RulesSpec.scala b/shared/src/test/scala/toml/RulesSpec.scala index bb6f886..5279a93 100644 --- a/shared/src/test/scala/toml/RulesSpec.scala +++ b/shared/src/test/scala/toml/RulesSpec.scala @@ -131,4 +131,76 @@ class RulesSpec extends FunSuite with Matchers { val example = "number = 3.14 p" testFailure(example) } + + test("Parse floats with fractional part") { + val example = + """flt1 = +1.0 + |flt2 = 3.1415 + |flt3 = -0.01 + """.stripMargin + + assert(testSuccess(example) == Root(List( + Node.Pair("flt1", Value.Real(+1.0)), + Node.Pair("flt2", Value.Real(3.1415)), + Node.Pair("flt3", Value.Real(-0.01))))) + } + + test("Parse floats with exponent part") { + val example = + """flt4 = 5e+22 + |flt5 = 1e6 + |flt6 = -2E-2 + """.stripMargin + + assert(testSuccess(example) == Root(List( + Node.Pair("flt4", Value.Real(5e+22)), + Node.Pair("flt5", Value.Real(1e6)), + Node.Pair("flt6", Value.Real(-2E-2))))) + } + + test("Parse floats with fractional and exponent part") { + val example = "flt7 = 6.626e-34\n" + + "poly = -45.321e12" + + assert(testSuccess(example) == Root(List( + Node.Pair("flt7", Value.Real(6.626e-34)), + Node.Pair("poly", Value.Real(-45.321e12))))) + } + + test("Parse floats with underscores") { + val example = "flt8 = 224_617.445_991_228" + + assert(testSuccess(example) == Root(List( + Node.Pair("flt8", Value.Real(224617.445991228))))) + } + + test("Parse infinity constant") { + val example = + """sf1 = inf + |sf2 = +inf + |sf3 = -inf + |""".stripMargin + + assert(testSuccess(example) == Root(List( + Node.Pair("sf1", Value.Real(Double.PositiveInfinity)), + Node.Pair("sf2", Value.Real(Double.PositiveInfinity)), + Node.Pair("sf3", Value.Real(Double.NegativeInfinity))))) + } + + test("Parse NaN constant") { + val example = + """sf4 = nan + |sf5 = +nan + |sf6 = -nan + |""".stripMargin + + val result = testSuccess(example).nodes + assert(result.length == 3) + + // Cannot use `==` here since Double.NaN != Double.NaN + assert(result.forall { + case Node.Pair(_, Value.Real(v)) => v.equals(Double.NaN) + case _ => false + }) + } }