From 7b6048358ce604413386b0c080134466b90bd864 Mon Sep 17 00:00:00 2001 From: amirone Date: Mon, 10 Feb 2025 16:00:26 +0100 Subject: [PATCH 01/14] FIX parsing for Long numbers in scientific notation --- .../test/scala/jawn/ast/AstTestPlatform.scala | 11 ++++++- .../src/main/scala/jawn/ast/JValue.scala | 4 +-- .../src/test/scala/jawn/ArbitraryUtil.scala | 13 ++++++++ ast/shared/src/test/scala/jawn/AstTest.scala | 32 +++++++++++++++++-- 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/ast/jvm/src/test/scala/jawn/ast/AstTestPlatform.scala b/ast/jvm/src/test/scala/jawn/ast/AstTestPlatform.scala index a6eb18f0..5b42adb1 100644 --- a/ast/jvm/src/test/scala/jawn/ast/AstTestPlatform.scala +++ b/ast/jvm/src/test/scala/jawn/ast/AstTestPlatform.scala @@ -23,8 +23,11 @@ package org.typelevel.jawn package ast import org.scalacheck.Prop +import Prop.{forAll, forAllNoShrink} -import Prop.forAll +import scala.util.Try + +import ArbitraryUtil.expNotationNums private[jawn] trait AstTestPlatform { self: AstTest => @@ -37,4 +40,10 @@ private[jawn] trait AstTestPlatform { self: AstTest => ) } + property(".asDouble") = forAllNoShrink { (expForm: (String, Double)) => + Prop( + JParser.parseUnsafe(expForm._1).getDouble == Try(JParser.parseUnsafe(expForm._1).asDouble).toOption && + JParser.parseUnsafe(expForm._1).asDouble == expForm._2 + ) + } } diff --git a/ast/shared/src/main/scala/jawn/ast/JValue.scala b/ast/shared/src/main/scala/jawn/ast/JValue.scala index 59c9afc3..2c20a838 100644 --- a/ast/shared/src/main/scala/jawn/ast/JValue.scala +++ b/ast/shared/src/main/scala/jawn/ast/JValue.scala @@ -254,13 +254,13 @@ case class DeferNum(s: String) extends JNum { lazy val n: Double = java.lang.Double.parseDouble(s) final override def getInt: Option[Int] = Some(n.toInt) - final override def getLong: Option[Long] = Some(util.parseLongUnsafe(s)) + final override def getLong: Option[Long] = Some(n.toLong) final override def getDouble: Option[Double] = Some(n) final override def getBigInt: Option[BigInt] = Some(BigDecimal(s).toBigInt) final override def getBigDecimal: Option[BigDecimal] = Some(BigDecimal(s)) final override def asInt: Int = n.toInt - final override def asLong: Long = util.parseLongUnsafe(s) + final override def asLong: Long = n.toLong final override def asDouble: Double = n final override def asBigInt: BigInt = BigDecimal(s).toBigInt final override def asBigDecimal: BigDecimal = BigDecimal(s) diff --git a/ast/shared/src/test/scala/jawn/ArbitraryUtil.scala b/ast/shared/src/test/scala/jawn/ArbitraryUtil.scala index 83280bcd..8c2e8876 100644 --- a/ast/shared/src/test/scala/jawn/ArbitraryUtil.scala +++ b/ast/shared/src/test/scala/jawn/ArbitraryUtil.scala @@ -62,4 +62,17 @@ object ArbitraryUtil { implicit lazy val arbitraryJValue: Arbitrary[JValue] = Arbitrary(jvalue()) + + // Valid JSON numbers with an exact double representation and in the Long range + + implicit lazy val expNotationNums: Arbitrary[(String, Double)] = Arbitrary[(String, Double)] { + Gen.oneOf( + ("2e3", 2e3), + ("2.5e0", 2.5e0), + ("2e+3", 2e+3), + ("2.5e-1", 2.5e-1), + ("9.223372036854776e18", 9.223372036854776e18), + ("-9.223372036854776e+18", -9.223372036854776e18) + ) + } } diff --git a/ast/shared/src/test/scala/jawn/AstTest.scala b/ast/shared/src/test/scala/jawn/AstTest.scala index e7adc509..a67ac3dc 100644 --- a/ast/shared/src/test/scala/jawn/AstTest.scala +++ b/ast/shared/src/test/scala/jawn/AstTest.scala @@ -23,10 +23,10 @@ package org.typelevel.jawn package ast import org.scalacheck.{Prop, Properties} -import scala.util.{Success, Try} +import scala.util.{Success, Try} import ArbitraryUtil._ -import Prop.forAll +import Prop.{forAll, forAllNoShrink} class AstTest extends Properties("AstTest") with AstTestPlatform { @@ -62,6 +62,13 @@ class AstTest extends Properties("AstTest") with AstTestPlatform { ) } + property(".asInt") = forAllNoShrink { (expForm: (String, Double)) => + Prop( + JParser.parseUnsafe(expForm._1).getInt == Try(JParser.parseUnsafe(expForm._1).asInt).toOption && + JParser.parseUnsafe(expForm._1).asInt == expForm._2.intValue() + ) + } + property(".getLong") = forAll { (n: Long) => Prop( JNum(n).getLong == Some(n) && @@ -69,6 +76,13 @@ class AstTest extends Properties("AstTest") with AstTestPlatform { ) } + property(".asLong") = forAllNoShrink { (expForm: (String, Double)) => + Prop( + JParser.parseUnsafe(expForm._1).getLong == Try(JParser.parseUnsafe(expForm._1).asLong).toOption && + JParser.parseUnsafe(expForm._1).asLong == expForm._2.longValue() + ) + } + property(".getBigInt") = forAll { (n: BigInt) => Prop( JNum(n.toString).getBigInt == Some(n) && @@ -76,6 +90,13 @@ class AstTest extends Properties("AstTest") with AstTestPlatform { ) } + property(".asBigInt") = forAllNoShrink { (expForm: (String, Double)) => + Prop( + JParser.parseUnsafe(expForm._1).getBigInt == Try(JParser.parseUnsafe(expForm._1).asBigInt).toOption && + JParser.parseUnsafe(expForm._1).asBigInt == BigDecimal(expForm._2).toBigInt() + ) + } + property(".getBigDecimal") = forAll { (n: BigDecimal) => if (Try(BigDecimal(n.toString)) == Success(n)) Prop( @@ -85,4 +106,11 @@ class AstTest extends Properties("AstTest") with AstTestPlatform { else Prop(true) } + + property(".asBigDecimal") = forAllNoShrink { (expForm: (String, Double)) => + Prop( + JParser.parseUnsafe(expForm._1).getBigDecimal == Try(JParser.parseUnsafe(expForm._1).asBigDecimal).toOption && + JParser.parseUnsafe(expForm._1).asBigDecimal == BigDecimal(expForm._2) + ) + } } From 6eb482e799708370f7e2a90976a91539e5d2d661 Mon Sep 17 00:00:00 2001 From: amirone Date: Mon, 10 Feb 2025 16:47:59 +0100 Subject: [PATCH 02/14] FIX syntax --- ast/shared/src/test/scala/jawn/AstTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ast/shared/src/test/scala/jawn/AstTest.scala b/ast/shared/src/test/scala/jawn/AstTest.scala index a67ac3dc..058df03d 100644 --- a/ast/shared/src/test/scala/jawn/AstTest.scala +++ b/ast/shared/src/test/scala/jawn/AstTest.scala @@ -93,7 +93,7 @@ class AstTest extends Properties("AstTest") with AstTestPlatform { property(".asBigInt") = forAllNoShrink { (expForm: (String, Double)) => Prop( JParser.parseUnsafe(expForm._1).getBigInt == Try(JParser.parseUnsafe(expForm._1).asBigInt).toOption && - JParser.parseUnsafe(expForm._1).asBigInt == BigDecimal(expForm._2).toBigInt() + JParser.parseUnsafe(expForm._1).asBigInt == BigDecimal(expForm._2).toBigInt ) } From 978bbfcd2c2459002af6a8acdf59da79baf9ed55 Mon Sep 17 00:00:00 2001 From: amirone Date: Thu, 13 Feb 2025 12:38:33 +0100 Subject: [PATCH 03/14] Rewrote Arbitrary for scientific notation tests as lists --- .../test/scala/jawn/ast/AstTestPlatform.scala | 4 ++-- .../src/test/scala/jawn/ArbitraryUtil.scala | 4 +--- ast/shared/src/test/scala/jawn/AstTest.scala | 18 +++++++++--------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/ast/jvm/src/test/scala/jawn/ast/AstTestPlatform.scala b/ast/jvm/src/test/scala/jawn/ast/AstTestPlatform.scala index 5b42adb1..679f1ff2 100644 --- a/ast/jvm/src/test/scala/jawn/ast/AstTestPlatform.scala +++ b/ast/jvm/src/test/scala/jawn/ast/AstTestPlatform.scala @@ -40,8 +40,8 @@ private[jawn] trait AstTestPlatform { self: AstTest => ) } - property(".asDouble") = forAllNoShrink { (expForm: (String, Double)) => - Prop( + expNotationNums.foreach { (expForm: (String, Double)) => + property(s".asDouble ${expForm._1}") = Prop( JParser.parseUnsafe(expForm._1).getDouble == Try(JParser.parseUnsafe(expForm._1).asDouble).toOption && JParser.parseUnsafe(expForm._1).asDouble == expForm._2 ) diff --git a/ast/shared/src/test/scala/jawn/ArbitraryUtil.scala b/ast/shared/src/test/scala/jawn/ArbitraryUtil.scala index 8c2e8876..ff1c3249 100644 --- a/ast/shared/src/test/scala/jawn/ArbitraryUtil.scala +++ b/ast/shared/src/test/scala/jawn/ArbitraryUtil.scala @@ -65,8 +65,7 @@ object ArbitraryUtil { // Valid JSON numbers with an exact double representation and in the Long range - implicit lazy val expNotationNums: Arbitrary[(String, Double)] = Arbitrary[(String, Double)] { - Gen.oneOf( + implicit lazy val expNotationNums: List[(String, Double)] = List( ("2e3", 2e3), ("2.5e0", 2.5e0), ("2e+3", 2e+3), @@ -74,5 +73,4 @@ object ArbitraryUtil { ("9.223372036854776e18", 9.223372036854776e18), ("-9.223372036854776e+18", -9.223372036854776e18) ) - } } diff --git a/ast/shared/src/test/scala/jawn/AstTest.scala b/ast/shared/src/test/scala/jawn/AstTest.scala index 058df03d..bec03a64 100644 --- a/ast/shared/src/test/scala/jawn/AstTest.scala +++ b/ast/shared/src/test/scala/jawn/AstTest.scala @@ -26,7 +26,7 @@ import org.scalacheck.{Prop, Properties} import scala.util.{Success, Try} import ArbitraryUtil._ -import Prop.{forAll, forAllNoShrink} +import Prop.{forAll} class AstTest extends Properties("AstTest") with AstTestPlatform { @@ -62,8 +62,8 @@ class AstTest extends Properties("AstTest") with AstTestPlatform { ) } - property(".asInt") = forAllNoShrink { (expForm: (String, Double)) => - Prop( + expNotationNums.foreach { (expForm: (String, Double)) => + property(s".asInt ${expForm._1}") = Prop( JParser.parseUnsafe(expForm._1).getInt == Try(JParser.parseUnsafe(expForm._1).asInt).toOption && JParser.parseUnsafe(expForm._1).asInt == expForm._2.intValue() ) @@ -76,8 +76,8 @@ class AstTest extends Properties("AstTest") with AstTestPlatform { ) } - property(".asLong") = forAllNoShrink { (expForm: (String, Double)) => - Prop( + expNotationNums.foreach { (expForm: (String, Double)) => + property(s".asLong ${expForm._1}") = Prop( JParser.parseUnsafe(expForm._1).getLong == Try(JParser.parseUnsafe(expForm._1).asLong).toOption && JParser.parseUnsafe(expForm._1).asLong == expForm._2.longValue() ) @@ -90,8 +90,8 @@ class AstTest extends Properties("AstTest") with AstTestPlatform { ) } - property(".asBigInt") = forAllNoShrink { (expForm: (String, Double)) => - Prop( + expNotationNums.foreach { (expForm: (String, Double)) => + property(s".asBigInt ${expForm._1}") = Prop( JParser.parseUnsafe(expForm._1).getBigInt == Try(JParser.parseUnsafe(expForm._1).asBigInt).toOption && JParser.parseUnsafe(expForm._1).asBigInt == BigDecimal(expForm._2).toBigInt ) @@ -107,8 +107,8 @@ class AstTest extends Properties("AstTest") with AstTestPlatform { Prop(true) } - property(".asBigDecimal") = forAllNoShrink { (expForm: (String, Double)) => - Prop( + expNotationNums.foreach { (expForm: (String, Double)) => + property(s".asBigDecimal ${expForm._1}") = Prop( JParser.parseUnsafe(expForm._1).getBigDecimal == Try(JParser.parseUnsafe(expForm._1).asBigDecimal).toOption && JParser.parseUnsafe(expForm._1).asBigDecimal == BigDecimal(expForm._2) ) From f8c8a70e3062c7a1eece3bbaa72829e503e90dcb Mon Sep 17 00:00:00 2001 From: amirone Date: Fri, 14 Feb 2025 10:04:27 +0100 Subject: [PATCH 04/14] Swapped parseLongUnsafe for safer parseLong in DeferLong --- .../src/main/scala/jawn/ast/JValue.scala | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/ast/shared/src/main/scala/jawn/ast/JValue.scala b/ast/shared/src/main/scala/jawn/ast/JValue.scala index 2c20a838..edc09e65 100644 --- a/ast/shared/src/main/scala/jawn/ast/JValue.scala +++ b/ast/shared/src/main/scala/jawn/ast/JValue.scala @@ -25,6 +25,7 @@ package ast import java.lang.Double.{isInfinite, isNaN} import scala.collection.mutable import scala.reflect.ClassTag +import scala.util.Try import scala.util.hashing.MurmurHash3 class WrongValueException(e: String, g: String) extends Exception(s"expected $e, got $g") @@ -223,28 +224,29 @@ case class DoubleNum(n: Double) extends JNum { case class DeferLong(s: String) extends JNum { - lazy val n: Long = util.parseLongUnsafe(s) + lazy val nOpt: Option[Long] = Try(util.parseLong(s)).toOption - final override def getInt: Option[Int] = Some(n.toInt) - final override def getLong: Option[Long] = Some(n) - final override def getDouble: Option[Double] = Some(n.toDouble) + final override def getInt: Option[Int] = nOpt.map(_.toInt) + final override def getLong: Option[Long] = nOpt + final override def getDouble: Option[Double] = nOpt.map(_.toDouble) final override def getBigInt: Option[BigInt] = Some(BigInt(s)) final override def getBigDecimal: Option[BigDecimal] = Some(BigDecimal(s)) - final override def asInt: Int = n.toInt - final override def asLong: Long = n - final override def asDouble: Double = n.toDouble + final override def asInt: Int = nOpt.map(_.toInt).getOrElse(throw new InvalidNumException(s)) + final override def asLong: Long = nOpt.getOrElse(throw new InvalidNumException(s)) + final override def asDouble: Double = nOpt.map(_.toDouble).getOrElse(throw new InvalidNumException(s)) final override def asBigInt: BigInt = BigInt(s) final override def asBigDecimal: BigDecimal = BigDecimal(s) - final override def hashCode: Int = n.## + final override def hashCode: Int = if (nOpt.isEmpty) s.## else nOpt.get.## final override def equals(that: Any): Boolean = - that match { - case LongNum(n2) => n == n2 - case DoubleNum(n2) => JNum.hybridEq(n, n2) - case jn: DeferLong => n == jn.asLong - case jn: DeferNum => JNum.hybridEq(n, jn.asDouble) + (nOpt, that) match { + case (Option.empty, _) => false + case (Some(n), LongNum(n2)) => n == n2 + case (Some(n), DoubleNum(n2)) => JNum.hybridEq(n, n2) + case (Some(n), jn: DeferLong) => n == jn.asLong + case (Some(n), jn: DeferNum) => JNum.hybridEq(n, jn.asDouble) case _ => false } } @@ -271,7 +273,7 @@ case class DeferNum(s: String) extends JNum { that match { case LongNum(n2) => JNum.hybridEq(n2, n) case DoubleNum(n2) => n == n2 - case jn: DeferLong => JNum.hybridEq(jn.asLong, n) + case jn: DeferLong => try { JNum.hybridEq(jn.asLong, n) } catch { case _: InvalidNumException => false } case jn: DeferNum => n == jn.asDouble case _ => false } From 27ea0c3fc1f396dfa5c45477825c3f5ba0f03727 Mon Sep 17 00:00:00 2001 From: amirone Date: Fri, 14 Feb 2025 10:12:32 +0100 Subject: [PATCH 05/14] FIX formatting --- ast/shared/src/main/scala/jawn/ast/JValue.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ast/shared/src/main/scala/jawn/ast/JValue.scala b/ast/shared/src/main/scala/jawn/ast/JValue.scala index edc09e65..ea473249 100644 --- a/ast/shared/src/main/scala/jawn/ast/JValue.scala +++ b/ast/shared/src/main/scala/jawn/ast/JValue.scala @@ -273,7 +273,9 @@ case class DeferNum(s: String) extends JNum { that match { case LongNum(n2) => JNum.hybridEq(n2, n) case DoubleNum(n2) => n == n2 - case jn: DeferLong => try { JNum.hybridEq(jn.asLong, n) } catch { case _: InvalidNumException => false } + case jn: DeferLong => + try { JNum.hybridEq(jn.asLong, n) } + catch { case _: InvalidNumException => false } case jn: DeferNum => n == jn.asDouble case _ => false } From d2dd387b69610b1cb168f49c884798224d130034 Mon Sep 17 00:00:00 2001 From: amirone Date: Fri, 14 Feb 2025 10:16:27 +0100 Subject: [PATCH 06/14] FIX formatting (2) --- ast/shared/src/main/scala/jawn/ast/JValue.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ast/shared/src/main/scala/jawn/ast/JValue.scala b/ast/shared/src/main/scala/jawn/ast/JValue.scala index ea473249..37037b9d 100644 --- a/ast/shared/src/main/scala/jawn/ast/JValue.scala +++ b/ast/shared/src/main/scala/jawn/ast/JValue.scala @@ -273,8 +273,8 @@ case class DeferNum(s: String) extends JNum { that match { case LongNum(n2) => JNum.hybridEq(n2, n) case DoubleNum(n2) => n == n2 - case jn: DeferLong => - try { JNum.hybridEq(jn.asLong, n) } + case jn: DeferLong => + try JNum.hybridEq(jn.asLong, n) catch { case _: InvalidNumException => false } case jn: DeferNum => n == jn.asDouble case _ => false From bbebe504c095a1c2bc2701bc344c7baf251b9c5d Mon Sep 17 00:00:00 2001 From: amirone Date: Fri, 14 Feb 2025 10:21:11 +0100 Subject: [PATCH 07/14] FIX formatting (3) --- ast/shared/src/test/scala/jawn/ArbitraryUtil.scala | 14 +++++++------- ast/shared/src/test/scala/jawn/AstTest.scala | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ast/shared/src/test/scala/jawn/ArbitraryUtil.scala b/ast/shared/src/test/scala/jawn/ArbitraryUtil.scala index ff1c3249..205dfba7 100644 --- a/ast/shared/src/test/scala/jawn/ArbitraryUtil.scala +++ b/ast/shared/src/test/scala/jawn/ArbitraryUtil.scala @@ -66,11 +66,11 @@ object ArbitraryUtil { // Valid JSON numbers with an exact double representation and in the Long range implicit lazy val expNotationNums: List[(String, Double)] = List( - ("2e3", 2e3), - ("2.5e0", 2.5e0), - ("2e+3", 2e+3), - ("2.5e-1", 2.5e-1), - ("9.223372036854776e18", 9.223372036854776e18), - ("-9.223372036854776e+18", -9.223372036854776e18) - ) + ("2e3", 2e3), + ("2.5e0", 2.5e0), + ("2e+3", 2e+3), + ("2.5e-1", 2.5e-1), + ("9.223372036854776e18", 9.223372036854776e18), + ("-9.223372036854776e+18", -9.223372036854776e18) + ) } diff --git a/ast/shared/src/test/scala/jawn/AstTest.scala b/ast/shared/src/test/scala/jawn/AstTest.scala index bec03a64..83def44f 100644 --- a/ast/shared/src/test/scala/jawn/AstTest.scala +++ b/ast/shared/src/test/scala/jawn/AstTest.scala @@ -26,7 +26,7 @@ import org.scalacheck.{Prop, Properties} import scala.util.{Success, Try} import ArbitraryUtil._ -import Prop.{forAll} +import Prop.forAll class AstTest extends Properties("AstTest") with AstTestPlatform { From f56d0525e0a36fa949fc537ef7dde973c20443ad Mon Sep 17 00:00:00 2001 From: amirone Date: Fri, 14 Feb 2025 10:29:29 +0100 Subject: [PATCH 08/14] FIX match expression (stable type) --- ast/shared/src/main/scala/jawn/ast/JValue.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ast/shared/src/main/scala/jawn/ast/JValue.scala b/ast/shared/src/main/scala/jawn/ast/JValue.scala index 37037b9d..0515d9e1 100644 --- a/ast/shared/src/main/scala/jawn/ast/JValue.scala +++ b/ast/shared/src/main/scala/jawn/ast/JValue.scala @@ -242,7 +242,7 @@ case class DeferLong(s: String) extends JNum { final override def equals(that: Any): Boolean = (nOpt, that) match { - case (Option.empty, _) => false + case (None, _) => false case (Some(n), LongNum(n2)) => n == n2 case (Some(n), DoubleNum(n2)) => JNum.hybridEq(n, n2) case (Some(n), jn: DeferLong) => n == jn.asLong From b59ab4fbae7efe435c1779a26475435ffdf907ec Mon Sep 17 00:00:00 2001 From: amirone Date: Fri, 14 Feb 2025 10:49:27 +0100 Subject: [PATCH 09/14] FIX binary compatibility (resume old iface) --- ast/shared/src/main/scala/jawn/ast/JValue.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/ast/shared/src/main/scala/jawn/ast/JValue.scala b/ast/shared/src/main/scala/jawn/ast/JValue.scala index 0515d9e1..d0eff355 100644 --- a/ast/shared/src/main/scala/jawn/ast/JValue.scala +++ b/ast/shared/src/main/scala/jawn/ast/JValue.scala @@ -225,6 +225,7 @@ case class DoubleNum(n: Double) extends JNum { case class DeferLong(s: String) extends JNum { lazy val nOpt: Option[Long] = Try(util.parseLong(s)).toOption + lazy val n: Long = nOpt.orNull.asInstanceOf[Long] // would be better to throw an exception here? final override def getInt: Option[Int] = nOpt.map(_.toInt) final override def getLong: Option[Long] = nOpt From 5c54741636f5eb2555eb0067e65e0074990961bb Mon Sep 17 00:00:00 2001 From: amirone Date: Fri, 14 Feb 2025 10:54:04 +0100 Subject: [PATCH 10/14] FIX formatting --- ast/shared/src/main/scala/jawn/ast/JValue.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ast/shared/src/main/scala/jawn/ast/JValue.scala b/ast/shared/src/main/scala/jawn/ast/JValue.scala index d0eff355..ad9f52d4 100644 --- a/ast/shared/src/main/scala/jawn/ast/JValue.scala +++ b/ast/shared/src/main/scala/jawn/ast/JValue.scala @@ -225,7 +225,7 @@ case class DoubleNum(n: Double) extends JNum { case class DeferLong(s: String) extends JNum { lazy val nOpt: Option[Long] = Try(util.parseLong(s)).toOption - lazy val n: Long = nOpt.orNull.asInstanceOf[Long] // would be better to throw an exception here? + lazy val n: Long = nOpt.orNull.asInstanceOf[Long] // would be better to throw an exception here? final override def getInt: Option[Int] = nOpt.map(_.toInt) final override def getLong: Option[Long] = nOpt From 8350e79df449c8c7b23d27b184e79a55609001db Mon Sep 17 00:00:00 2001 From: amirone Date: Fri, 14 Feb 2025 11:13:39 +0100 Subject: [PATCH 11/14] FIX Forgot for a moment Long is not nullable :/ --- ast/shared/src/main/scala/jawn/ast/JValue.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ast/shared/src/main/scala/jawn/ast/JValue.scala b/ast/shared/src/main/scala/jawn/ast/JValue.scala index ad9f52d4..93519727 100644 --- a/ast/shared/src/main/scala/jawn/ast/JValue.scala +++ b/ast/shared/src/main/scala/jawn/ast/JValue.scala @@ -225,7 +225,7 @@ case class DoubleNum(n: Double) extends JNum { case class DeferLong(s: String) extends JNum { lazy val nOpt: Option[Long] = Try(util.parseLong(s)).toOption - lazy val n: Long = nOpt.orNull.asInstanceOf[Long] // would be better to throw an exception here? + lazy val n: Long = nOpt.getOrElse(throw new InvalidNumException(s)) final override def getInt: Option[Int] = nOpt.map(_.toInt) final override def getLong: Option[Long] = nOpt From 68704854c90d6cf6dc974ff2fe9db36814de9008 Mon Sep 17 00:00:00 2001 From: amirone Date: Fri, 21 Feb 2025 11:20:36 +0100 Subject: [PATCH 12/14] No idempotency on JS Charsequence/String rendering --- .../test/scala/jawn/ast/AstCheckPlatform.scala | 8 ++++++++ .../test/scala/jawn/ast/AstCheckPlatform.scala | 17 ++++++++++++++++- ast/shared/src/test/scala/jawn/AstCheck.scala | 8 -------- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/ast/jvm/src/test/scala/jawn/ast/AstCheckPlatform.scala b/ast/jvm/src/test/scala/jawn/ast/AstCheckPlatform.scala index de5e3f8e..a9c94fc2 100644 --- a/ast/jvm/src/test/scala/jawn/ast/AstCheckPlatform.scala +++ b/ast/jvm/src/test/scala/jawn/ast/AstCheckPlatform.scala @@ -56,6 +56,14 @@ private[jawn] trait AstCheckPlatform { self: AstCheck => p0 && p1 } + property("string/charSequence parsing") = forAll { (value: JValue) => + val s = CanonicalRenderer.render(value) + val j1 = JParser.parseFromString(s) + val cs = java.nio.CharBuffer.wrap(s.toCharArray) + val j2 = JParser.parseFromCharSequence(cs) + Prop(j1 == j2 && j1.## == j2.##) + } + import AsyncParser.SingleValue property("async parsing") = forAll { (v: JValue) => diff --git a/ast/native/src/test/scala/jawn/ast/AstCheckPlatform.scala b/ast/native/src/test/scala/jawn/ast/AstCheckPlatform.scala index 352051c8..4f90c127 100644 --- a/ast/native/src/test/scala/jawn/ast/AstCheckPlatform.scala +++ b/ast/native/src/test/scala/jawn/ast/AstCheckPlatform.scala @@ -22,4 +22,19 @@ package org.typelevel.jawn package ast -private[jawn] trait AstCheckPlatform +import org.typelevel.jawn.ast.ArbitraryUtil.arbitraryJValue + +import org.scalacheck.Prop +import org.scalacheck.Prop.forAll + +private[jawn] trait AstCheckPlatform { self: AstCheck => + + // Rendering/parsing numbers on JS isn't always idempotent + property("string/charSequence parsing") = forAll { (value: JValue) => + val s = CanonicalRenderer.render(value) + val j1 = JParser.parseFromString(s) + val cs = java.nio.CharBuffer.wrap(s.toCharArray) + val j2 = JParser.parseFromCharSequence(cs) + Prop(j1 == j2 && j1.## == j2.##) + } +} diff --git a/ast/shared/src/test/scala/jawn/AstCheck.scala b/ast/shared/src/test/scala/jawn/AstCheck.scala index 6da97e4f..d5a80a4f 100644 --- a/ast/shared/src/test/scala/jawn/AstCheck.scala +++ b/ast/shared/src/test/scala/jawn/AstCheck.scala @@ -42,14 +42,6 @@ class AstCheck extends Properties("AstCheck") with AstCheckPlatform { ) } - property("string/charSequence parsing") = forAll { (value: JValue) => - val s = CanonicalRenderer.render(value) - val j1 = JParser.parseFromString(s) - val cs = java.nio.CharBuffer.wrap(s.toCharArray) - val j2 = JParser.parseFromCharSequence(cs) - Prop(j1 == j2 && j1.## == j2.##) - } - implicit val facade: Facade[JValue] = JawnFacade val percs = List(0.0, 0.2, 0.4, 0.8, 1.0) From 70ea50ca5e5c348c966c176cabcc73c9f736d546 Mon Sep 17 00:00:00 2001 From: amirone Date: Fri, 21 Feb 2025 11:24:32 +0100 Subject: [PATCH 13/14] Formatting --- ast/native/src/test/scala/jawn/ast/AstCheckPlatform.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ast/native/src/test/scala/jawn/ast/AstCheckPlatform.scala b/ast/native/src/test/scala/jawn/ast/AstCheckPlatform.scala index 4f90c127..b425dcf7 100644 --- a/ast/native/src/test/scala/jawn/ast/AstCheckPlatform.scala +++ b/ast/native/src/test/scala/jawn/ast/AstCheckPlatform.scala @@ -29,7 +29,7 @@ import org.scalacheck.Prop.forAll private[jawn] trait AstCheckPlatform { self: AstCheck => - // Rendering/parsing numbers on JS isn't always idempotent + // Rendering/parsing numbers on JS isn't always idempotent property("string/charSequence parsing") = forAll { (value: JValue) => val s = CanonicalRenderer.render(value) val j1 = JParser.parseFromString(s) From f79eaa034f1b86851b652c5b52229ec8abab6cfc Mon Sep 17 00:00:00 2001 From: amirone Date: Tue, 4 Mar 2025 17:38:49 +0100 Subject: [PATCH 14/14] FIX equality for DeferLong --- ast/shared/src/main/scala/jawn/ast/JValue.scala | 3 +++ ast/shared/src/test/scala/jawn/AstCheck.scala | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/ast/shared/src/main/scala/jawn/ast/JValue.scala b/ast/shared/src/main/scala/jawn/ast/JValue.scala index 93519727..e7b9e030 100644 --- a/ast/shared/src/main/scala/jawn/ast/JValue.scala +++ b/ast/shared/src/main/scala/jawn/ast/JValue.scala @@ -243,7 +243,10 @@ case class DeferLong(s: String) extends JNum { final override def equals(that: Any): Boolean = (nOpt, that) match { + // JNum with same string representation and type will behave the same way + case (None, that: DeferLong) => this.s == that.s case (None, _) => false + case (_, None) => false case (Some(n), LongNum(n2)) => n == n2 case (Some(n), DoubleNum(n2)) => JNum.hybridEq(n, n2) case (Some(n), jn: DeferLong) => n == jn.asLong diff --git a/ast/shared/src/test/scala/jawn/AstCheck.scala b/ast/shared/src/test/scala/jawn/AstCheck.scala index d5a80a4f..6da97e4f 100644 --- a/ast/shared/src/test/scala/jawn/AstCheck.scala +++ b/ast/shared/src/test/scala/jawn/AstCheck.scala @@ -42,6 +42,14 @@ class AstCheck extends Properties("AstCheck") with AstCheckPlatform { ) } + property("string/charSequence parsing") = forAll { (value: JValue) => + val s = CanonicalRenderer.render(value) + val j1 = JParser.parseFromString(s) + val cs = java.nio.CharBuffer.wrap(s.toCharArray) + val j2 = JParser.parseFromCharSequence(cs) + Prop(j1 == j2 && j1.## == j2.##) + } + implicit val facade: Facade[JValue] = JawnFacade val percs = List(0.0, 0.2, 0.4, 0.8, 1.0)