From 11219acb0cebed987df9c2a85d29098d04f71b8d Mon Sep 17 00:00:00 2001 From: Ross Goldberg <484615+rgoldberg@users.noreply.github.com> Date: Wed, 15 Oct 2025 22:56:48 -0400 Subject: [PATCH 1/2] Support floating point encoding by changing extension generic type bounds from `BinaryInteger` to `Numeric & CustomStringConvertible` for `JSON.Literal where Value:` & `JSONEncodable where Self:`. Signed-off-by: Ross Goldberg <484615+rgoldberg@users.noreply.github.com> Support floating point encoding by changing extension generic type bounds from `BinaryInteger` to `Numeric & CustomStringConvertible` for `JSON.Literal where Value:` & `JSONEncodable where Self:`. Signed-off-by: Ross Goldberg <484615+rgoldberg@users.noreply.github.com> --- Sources/JSONEncoding/Conformances/Double (ext).swift | 2 ++ Sources/JSONEncoding/Conformances/Float (ext).swift | 2 ++ Sources/JSONEncoding/Conformances/Float16 (ext).swift | 3 +++ Sources/JSONEncoding/Conformances/Float80 (ext).swift | 4 ++++ Sources/JSONEncoding/Encoders/JSON.Literal (ext).swift | 4 ++-- Sources/JSONEncoding/JSONEncodable.swift | 2 +- 6 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 Sources/JSONEncoding/Conformances/Double (ext).swift create mode 100644 Sources/JSONEncoding/Conformances/Float (ext).swift create mode 100644 Sources/JSONEncoding/Conformances/Float16 (ext).swift create mode 100644 Sources/JSONEncoding/Conformances/Float80 (ext).swift diff --git a/Sources/JSONEncoding/Conformances/Double (ext).swift b/Sources/JSONEncoding/Conformances/Double (ext).swift new file mode 100644 index 0000000..02ef479 --- /dev/null +++ b/Sources/JSONEncoding/Conformances/Double (ext).swift @@ -0,0 +1,2 @@ +extension Double: JSONEncodable { +} diff --git a/Sources/JSONEncoding/Conformances/Float (ext).swift b/Sources/JSONEncoding/Conformances/Float (ext).swift new file mode 100644 index 0000000..4ba767c --- /dev/null +++ b/Sources/JSONEncoding/Conformances/Float (ext).swift @@ -0,0 +1,2 @@ +extension Float: JSONEncodable { +} diff --git a/Sources/JSONEncoding/Conformances/Float16 (ext).swift b/Sources/JSONEncoding/Conformances/Float16 (ext).swift new file mode 100644 index 0000000..ceef6ca --- /dev/null +++ b/Sources/JSONEncoding/Conformances/Float16 (ext).swift @@ -0,0 +1,3 @@ +@available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) +extension Float16: JSONEncodable { +} diff --git a/Sources/JSONEncoding/Conformances/Float80 (ext).swift b/Sources/JSONEncoding/Conformances/Float80 (ext).swift new file mode 100644 index 0000000..ab5e434 --- /dev/null +++ b/Sources/JSONEncoding/Conformances/Float80 (ext).swift @@ -0,0 +1,4 @@ +#if (os(Linux) || os(macOS)) && arch(x86_64) +extension Float80: JSONEncodable { +} +#endif diff --git a/Sources/JSONEncoding/Encoders/JSON.Literal (ext).swift b/Sources/JSONEncoding/Encoders/JSON.Literal (ext).swift index 522c4fb..c6476ab 100644 --- a/Sources/JSONEncoding/Encoders/JSON.Literal (ext).swift +++ b/Sources/JSONEncoding/Encoders/JSON.Literal (ext).swift @@ -12,8 +12,8 @@ extension JSON.Literal { json.utf8 += (self.value ? "true" : "false").utf8 } } -extension JSON.Literal where Value: BinaryInteger { - /// Encodes this literal’s integer ``value`` to the provided JSON stream. The value’s +extension JSON.Literal where Value: Numeric & CustomStringConvertible { + /// Encodes this literal’s numeric ``value`` to the provided JSON stream. The value’s /// ``CustomStringConvertible description`` witness must format the value in base-10. @inlinable internal static func += (json: inout JSON, self: Self) { json.utf8 += self.value.description.utf8 diff --git a/Sources/JSONEncoding/JSONEncodable.swift b/Sources/JSONEncoding/JSONEncodable.swift index 07a49b5..f0e9ae8 100644 --- a/Sources/JSONEncoding/JSONEncodable.swift +++ b/Sources/JSONEncoding/JSONEncodable.swift @@ -9,7 +9,7 @@ extension JSONEncodable where Self: StringProtocol { json += JSON.Literal.init(self) } } -extension JSONEncodable where Self: BinaryInteger { +extension JSONEncodable where Self: Numeric & CustomStringConvertible { @inlinable public func encode(to json: inout JSON) { json += JSON.Literal.init(self) } From 76a8b9d21484224aece17539f9e239ab40088c2e Mon Sep 17 00:00:00 2001 From: Ross Goldberg <484615+rgoldberg@users.noreply.github.com> Date: Mon, 29 Dec 2025 06:37:36 -0500 Subject: [PATCH 2/2] Conform floating points to `JSONDecodable`. Signed-off-by: Ross Goldberg <484615+rgoldberg@users.noreply.github.com> --- Sources/JSONAST/JSON.Node.swift | 9 ++++++++ Sources/JSONAST/JSON.Number.swift | 6 +++++ .../Conformances/Float16 (ext).swift | 6 +++++ Sources/JSONDecoding/JSONDecodable.swift | 23 +++++++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 Sources/JSONDecoding/Conformances/Float16 (ext).swift diff --git a/Sources/JSONAST/JSON.Node.swift b/Sources/JSONAST/JSON.Node.swift index 831dac1..8fad977 100644 --- a/Sources/JSONAST/JSON.Node.swift +++ b/Sources/JSONAST/JSON.Node.swift @@ -231,6 +231,15 @@ extension JSON.Node { @inlinable public func `as`(_: Float.Type) -> Float? { self.as(JSON.Number.self)?.as(Float.self) } + /// Attempts to load an instance of ``Float16`` from this variant. + /// + /// - Returns: + /// The closest value of ``Float16`` to the payload of this variant if it matches + /// ``number(_:) [case]``, `nil` otherwise. + @available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) + @inlinable public func `as`(_: Float16.Type) -> Float16? { + self.as(JSON.Number.self)?.as(Float16.self) + } /// Attempts to load an instance of ``Number`` from this variant. /// /// - Returns: diff --git a/Sources/JSONAST/JSON.Number.swift b/Sources/JSONAST/JSON.Number.swift index b8baa78..d48aa4b 100644 --- a/Sources/JSONAST/JSON.Number.swift +++ b/Sources/JSONAST/JSON.Number.swift @@ -158,6 +158,12 @@ extension JSON.Number { public func `as`(_: Float.Type) -> Float { self.nearest(Float.self) } + /// Converts this numeric literal to a ``Float16`` value, or its closest + /// floating-point representation. + @available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) + public func `as`(_: Float16.Type) -> Float16 { + self.nearest(Float16.self) + } /// Converts this numeric literal to a floating-point value, or its closest /// floating-point representation. diff --git a/Sources/JSONDecoding/Conformances/Float16 (ext).swift b/Sources/JSONDecoding/Conformances/Float16 (ext).swift new file mode 100644 index 0000000..e05d061 --- /dev/null +++ b/Sources/JSONDecoding/Conformances/Float16 (ext).swift @@ -0,0 +1,6 @@ +@available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) +extension Float16: JSONDecodable { + @inlinable public init(json: JSON.Node) throws { + self = try json.cast { $0.as(Self.self) } + } +} diff --git a/Sources/JSONDecoding/JSONDecodable.swift b/Sources/JSONDecoding/JSONDecodable.swift index bf5f964..e02085e 100644 --- a/Sources/JSONDecoding/JSONDecodable.swift +++ b/Sources/JSONDecoding/JSONDecodable.swift @@ -15,6 +15,29 @@ extension JSONDecodable where Self: UnsignedInteger & FixedWidthInteger { self = try json.cast { try $0.as(Self.self) } } } +#if (os(Linux) || os(macOS)) && arch(x86_64) +extension JSONDecodable where Self == Float80 { + @inlinable public init(json: JSON.Node) throws { + self = try json.cast { $0.as(Self.self) } + } +} +#endif +extension JSONDecodable where Self == Double { + @inlinable public init(json: JSON.Node) throws { + self = try json.cast { $0.as(Self.self) } + } +} +extension JSONDecodable where Self == Float { + @inlinable public init(json: JSON.Node) throws { + self = try json.cast { $0.as(Self.self) } + } +} +@available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) +extension JSONDecodable where Self == Float16 { + @inlinable public init(json: JSON.Node) throws { + self = try json.cast { $0.as(Self.self) } + } +} extension JSONDecodable where Self: RawRepresentable, RawValue: JSONDecodable & Sendable { @inlinable public init(json: JSON.Node) throws { let rawValue: RawValue = try .init(json: json)