diff --git a/Package.swift b/Package.swift
index 8593b38..1d768e8 100644
--- a/Package.swift
+++ b/Package.swift
@@ -3,10 +3,10 @@
import PackageDescription
let package = Package(
- name: "json-swift",
- targets: [
- Target(name: "JSONLib", dependencies: []),
- Target(name: "ParserTestHarness", dependencies: ["JSONLib"]),
- Target(name: "ParserPerfTestHarness", dependencies: ["JSONLib"])
- ]
+ name: "json-swift",
+ targets: [
+ Target(name: "JSONLib", dependencies: []),
+ Target(name: "ParserTestHarness", dependencies: ["JSONLib"]),
+ Target(name: "ParserPerfTestHarness", dependencies: ["JSONLib"]),
+ ]
)
diff --git a/Sources/JSONLib/Error.swift b/Sources/JSONLib/Error.swift
index 80a81d8..d2f33bb 100644
--- a/Sources/JSONLib/Error.swift
+++ b/Sources/JSONLib/Error.swift
@@ -5,44 +5,44 @@
/// Represents error information for JSON parsing issues.
public final class JsonParserError: Swift.Error {
- public typealias ErrorInfoDictionary = [String:String]
+ public typealias ErrorInfoDictionary = [String: String]
- /// The error code used to differentiate between various error states.
- public let code: Int
+ /// The error code used to differentiate between various error states.
+ public let code: Int
- /// A string that is used to group errors into related error buckets.
- public let domain: String
+ /// A string that is used to group errors into related error buckets.
+ public let domain: String
- /// A place to store any custom information that needs to be passed along with the error instance.
- public let userInfo: ErrorInfoDictionary?
+ /// A place to store any custom information that needs to be passed along with the error instance.
+ public let userInfo: ErrorInfoDictionary?
-
- /// Initializes a new `Error` instance.
- public init(code: Int, domain: String, userInfo: ErrorInfoDictionary?) {
- self.code = code
- self.domain = domain
- self.userInfo = userInfo
- }
+ /// Initializes a new `Error` instance.
+ public init(code: Int, domain: String, userInfo: ErrorInfoDictionary?) {
+ self.code = code
+ self.domain = domain
+ self.userInfo = userInfo
+ }
}
/// The standard keys used in `Error` and `userInfo`.
public struct ErrorKeys {
- private init() {}
-
- public static let LocalizedDescription = "NSLocalizedDescription"
- public static let LocalizedFailureReason = "NSLocalizedFailureReason"
- public static let LocalizedRecoverySuggestion = "NSLocalizedRecoverySuggestion"
- public static let LocalizedRecoveryOptions = "NSLocalizedRecoveryOptions"
- public static let RecoveryAttempter = "NSRecoveryAttempter"
- public static let HelpAnchor = "NSHelpAnchor"
-
- public static let StringEncoding = "NSStringEncoding"
- public static let URL = "NSURL"
- public static let FilePath = "NSFilePath"
+ private init() {}
+
+ public static let LocalizedDescription = "NSLocalizedDescription"
+ public static let LocalizedFailureReason = "NSLocalizedFailureReason"
+ public static let LocalizedRecoverySuggestion = "NSLocalizedRecoverySuggestion"
+ public static let LocalizedRecoveryOptions = "NSLocalizedRecoveryOptions"
+ public static let RecoveryAttempter = "NSRecoveryAttempter"
+ public static let HelpAnchor = "NSHelpAnchor"
+
+ public static let StringEncoding = "NSStringEncoding"
+ public static let URL = "NSURL"
+ public static let FilePath = "NSFilePath"
}
extension JsonParserError: CustomStringConvertible {
- public var description: String {
- return "Error code: \(self.code), domain: \(self.domain)\ninfo: \(String(describing: self.userInfo))"
- }
+ public var description: String {
+ return
+ "Error code: \(self.code), domain: \(self.domain)\ninfo: \(String(describing: self.userInfo))"
+ }
}
diff --git a/Sources/JSONLib/Functional.swift b/Sources/JSONLib/Functional.swift
index 910b876..61cc898 100644
--- a/Sources/JSONLib/Functional.swift
+++ b/Sources/JSONLib/Functional.swift
@@ -4,8 +4,8 @@
* ------------------------------------------------------------------------------------------ */
precedencegroup FunctionalPrecedence {
- associativity: left
- higherThan: MultiplicationPrecedence
+ associativity: left
+ higherThan: MultiplicationPrecedence
}
infix operator ⇒ : FunctionalPrecedence
@@ -16,13 +16,13 @@ infix operator ⇒ : FunctionalPrecedence
/// - parameter rhs: The value to apply to the function
/// - returns: The transformation of `rhs` using `lhs`.
public func ⇒ (lhs: ((A) -> B)?, rhs: A?) -> B? {
- if let lhs = lhs {
- if let rhs = rhs {
- return lhs(rhs)
- }
- }
-
- return nil
+ if let lhs = lhs {
+ if let rhs = rhs {
+ return lhs(rhs)
+ }
+ }
+
+ return nil
}
/// Allows for a value to be transformed by a function, allowing for optionals.
@@ -31,13 +31,13 @@ public func ⇒ (lhs: ((A) -> B)?, rhs: A?) -> B? {
/// - parameter rhs: The transformative function
/// - returns: The transformation of `lhs` using `rhs`.
public func ⇒ (lhs: A?, rhs: ((A) -> B)?) -> B? {
- if let lhs = lhs {
- if let rhs = rhs {
- return rhs(lhs)
- }
- }
-
- return nil
+ if let lhs = lhs {
+ if let rhs = rhs {
+ return rhs(lhs)
+ }
+ }
+
+ return nil
}
/// Allows for a transformative function to be applied to a value.
@@ -47,7 +47,6 @@ public func ⇒ (lhs: A?, rhs: ((A) -> B)?) -> B? {
/// - returns: The transformation of `rhs` using `lhs`.
public func ⇒ (lhs: (A) -> B, rhs: A) -> B { return lhs(rhs) }
-
/// Allows for a value to be transformed by a function.
///
/// - parameter lhs: The value to apply to the function
diff --git a/Sources/JSONLib/JSValue.Accessors.swift b/Sources/JSONLib/JSValue.Accessors.swift
index 50da339..27712b8 100644
--- a/Sources/JSONLib/JSValue.Accessors.swift
+++ b/Sources/JSONLib/JSValue.Accessors.swift
@@ -8,130 +8,128 @@
*/
extension JSValue {
-
- /// Attempts to retrieve a `String` out of the `JSValue`.
- ///
- /// - returns: If the `JSValue` is a `String`, then the stored `String` value is returned, otherwise `nil`.
- public var string: String? {
- switch self {
- case .string(let value): return value
- default: return nil
- }
- }
-
- /// Attempts to retrieve a `Double` out of the `JSValue`.
- ///
- /// - returns: If the `JSValue` is a `JSNumber`, then the stored `Double` value is returned, otherwise `nil`.
- public var number: Double? {
- switch self {
- case .number(let value): return value
- default: return nil
- }
- }
-
- /// Attempts to retrieve an `Int` out of the `JSValue`.
- ///
- /// - returns: If the `JSValue` is a `Double`, then the stored `Double` value is returned, otherwise `nil`.
- public var integer: Int? {
- switch self {
- case .number(let value): return Int(value)
- default: return nil
- }
- }
-
-
- /// Attempts to retrieve a `Bool` out of the `JSValue`.
- ///
- /// - returns: If the `JSValue` is a `Bool`, then the stored `Bool` value is returned, otherwise `nil`.
- public var bool: Bool? {
- switch self {
- case .bool(let value): return value
- default: return nil
- }
- }
-
- /// Attempts to retrieve a `[String:JSValue]` out of the `JSValue`.
- ///
- /// - returns: If the `JSValue` is a `[String:JSValue]`, then the stored `[String:JSValue]` value is returned, otherwise `nil`.
- public var object: [String:JSValue]? {
- switch self {
- case .object(let value): return value
- default: return nil
- }
- }
-
- /// Attempts to retrieve a `[JSValue]` out of the `JSValue`.
- ///
- /// - returns: If the `JSValue` is a `JSArray`, then the stored `[JSValue]` value is returned, otherwise `nil`.
- public var array: [JSValue]? {
- switch self {
- case .array(let value): return value
- default: return nil
- }
- }
-
- /// Used to determine if a `nil` value is stored within `JSValue`. There is no intrinsic type for this value.
- ///
- /// - returns: If the `JSValue` is a `JSNull`, then the `true` is returned, otherwise `false`.
- public var null: Bool {
- switch self {
- case .null: return true
- default: return false
- }
- }
+
+ /// Attempts to retrieve a `String` out of the `JSValue`.
+ ///
+ /// - returns: If the `JSValue` is a `String`, then the stored `String` value is returned, otherwise `nil`.
+ public var string: String? {
+ switch self {
+ case .string(let value): return value
+ default: return nil
+ }
+ }
+
+ /// Attempts to retrieve a `Double` out of the `JSValue`.
+ ///
+ /// - returns: If the `JSValue` is a `JSNumber`, then the stored `Double` value is returned, otherwise `nil`.
+ public var number: Double? {
+ switch self {
+ case .number(let value): return value
+ default: return nil
+ }
+ }
+
+ /// Attempts to retrieve an `Int` out of the `JSValue`.
+ ///
+ /// - returns: If the `JSValue` is a `Double`, then the stored `Double` value is returned, otherwise `nil`.
+ public var integer: Int? {
+ switch self {
+ case .number(let value): return Int(value)
+ default: return nil
+ }
+ }
+
+ /// Attempts to retrieve a `Bool` out of the `JSValue`.
+ ///
+ /// - returns: If the `JSValue` is a `Bool`, then the stored `Bool` value is returned, otherwise `nil`.
+ public var bool: Bool? {
+ switch self {
+ case .bool(let value): return value
+ default: return nil
+ }
+ }
+
+ /// Attempts to retrieve a `[String:JSValue]` out of the `JSValue`.
+ ///
+ /// - returns: If the `JSValue` is a `[String:JSValue]`, then the stored `[String:JSValue]` value is returned, otherwise `nil`.
+ public var object: [String: JSValue]? {
+ switch self {
+ case .object(let value): return value
+ default: return nil
+ }
+ }
+
+ /// Attempts to retrieve a `[JSValue]` out of the `JSValue`.
+ ///
+ /// - returns: If the `JSValue` is a `JSArray`, then the stored `[JSValue]` value is returned, otherwise `nil`.
+ public var array: [JSValue]? {
+ switch self {
+ case .array(let value): return value
+ default: return nil
+ }
+ }
+
+ /// Used to determine if a `nil` value is stored within `JSValue`. There is no intrinsic type for this value.
+ ///
+ /// - returns: If the `JSValue` is a `JSNull`, then the `true` is returned, otherwise `false`.
+ public var null: Bool {
+ switch self {
+ case .null: return true
+ default: return false
+ }
+ }
}
/// Provide a usability extension to allow chaining index accessors without having to
/// marked it as optional.
/// e.g. foo["hi"]?["this"]?["sucks"]?.string vs. foo["so"]["much"]["better"].string
extension Optional where Wrapped == JSValue {
- /// Attempts to retrieve a `String` out of the `JSValue`.
- ///
- /// - returns: If the `JSValue` is a `String`, then the stored `String` value is returned, otherwise `nil`.
- public var string: String? {
- return self?.string
- }
-
- /// Attempts to retrieve a `Double` out of the `JSValue`.
- ///
- /// - returns: If the `JSValue` is a `JSNumber`, then the stored `Double` value is returned, otherwise `nil`.
- public var number: Double? {
- return self?.number
- }
-
- /// Attempts to retrieve an `Int` out of the `JSValue`.
- ///
- /// - returns: If the `JSValue` is a `Double`, then the stored `Double` value is returned, otherwise `nil`.
- public var integer: Int? {
- return self?.integer
- }
-
-
- /// Attempts to retrieve a `Bool` out of the `JSValue`.
- ///
- /// - returns: If the `JSValue` is a `Bool`, then the stored `Bool` value is returned, otherwise `nil`.
- public var bool: Bool? {
- return self?.bool
- }
-
- /// Attempts to retrieve a `[String:JSValue]` out of the `JSValue`.
- ///
- /// - returns: If the `JSValue` is a `[String:JSValue]`, then the stored `[String:JSValue]` value is returned, otherwise `nil`.
- public var object: [String:JSValue]? {
- return self?.object
- }
-
- /// Attempts to retrieve a `[JSValue]` out of the `JSValue`.
- ///
- /// - returns: If the `JSValue` is a `JSArray`, then the stored `[JSValue]` value is returned, otherwise `nil`.
- public var array: [JSValue]? {
- return self?.array
- }
-
- /// Used to determine if a `nil` value is stored within `JSValue`. There is no intrinsic type for this value.
- ///
- /// - returns: If the `JSValue` is a `JSNull`, then the `true` is returned, otherwise `false`.
- public var null: Bool {
- return self?.null ?? false
- }
-}
\ No newline at end of file
+ /// Attempts to retrieve a `String` out of the `JSValue`.
+ ///
+ /// - returns: If the `JSValue` is a `String`, then the stored `String` value is returned, otherwise `nil`.
+ public var string: String? {
+ return self?.string
+ }
+
+ /// Attempts to retrieve a `Double` out of the `JSValue`.
+ ///
+ /// - returns: If the `JSValue` is a `JSNumber`, then the stored `Double` value is returned, otherwise `nil`.
+ public var number: Double? {
+ return self?.number
+ }
+
+ /// Attempts to retrieve an `Int` out of the `JSValue`.
+ ///
+ /// - returns: If the `JSValue` is a `Double`, then the stored `Double` value is returned, otherwise `nil`.
+ public var integer: Int? {
+ return self?.integer
+ }
+
+ /// Attempts to retrieve a `Bool` out of the `JSValue`.
+ ///
+ /// - returns: If the `JSValue` is a `Bool`, then the stored `Bool` value is returned, otherwise `nil`.
+ public var bool: Bool? {
+ return self?.bool
+ }
+
+ /// Attempts to retrieve a `[String:JSValue]` out of the `JSValue`.
+ ///
+ /// - returns: If the `JSValue` is a `[String:JSValue]`, then the stored `[String:JSValue]` value is returned, otherwise `nil`.
+ public var object: [String: JSValue]? {
+ return self?.object
+ }
+
+ /// Attempts to retrieve a `[JSValue]` out of the `JSValue`.
+ ///
+ /// - returns: If the `JSValue` is a `JSArray`, then the stored `[JSValue]` value is returned, otherwise `nil`.
+ public var array: [JSValue]? {
+ return self?.array
+ }
+
+ /// Used to determine if a `nil` value is stored within `JSValue`. There is no intrinsic type for this value.
+ ///
+ /// - returns: If the `JSValue` is a `JSNull`, then the `true` is returned, otherwise `false`.
+ public var null: Bool {
+ return self?.null ?? false
+ }
+}
diff --git a/Sources/JSONLib/JSValue.Encodings.swift b/Sources/JSONLib/JSValue.Encodings.swift
index 18749b8..5b4f0d0 100644
--- a/Sources/JSONLib/JSValue.Encodings.swift
+++ b/Sources/JSONLib/JSValue.Encodings.swift
@@ -11,7 +11,7 @@
// /// within the contents of a string value.
// public struct Encodings {
// private init() {}
-//
+//
// /// The encoding prefix for all base64 encoded values.
// public static let base64 = "data:text/plain;base64,"
// }
@@ -29,11 +29,11 @@
// // return [Byte](bytes)
// // }
// fatalError("nyi")
-//
+//
// default:
// return nil
// }
-//
+//
// return nil
// }
-//}
\ No newline at end of file
+//}
diff --git a/Sources/JSONLib/JSValue.ErrorHandling.swift b/Sources/JSONLib/JSValue.ErrorHandling.swift
index cc0f4b6..392cb0b 100644
--- a/Sources/JSONLib/JSValue.ErrorHandling.swift
+++ b/Sources/JSONLib/JSValue.ErrorHandling.swift
@@ -4,47 +4,53 @@
* ------------------------------------------------------------------------------------------ */
extension JSValue {
-
- /// A type that holds the error code and standard error message for the various types of failures
- /// a `JSValue` can have.
- public struct ErrorMessage {
- /// The numeric value of the error number.
- public let code: Int
-
- /// The default message describing the error.
- public let message: String
- }
-
- /// All of the error codes and standard error messages when parsing JSON.
- public struct ErrorCode {
- private init() {}
-
- /// A integer that is outside of the safe range was attempted to be set.
- public static let InvalidIntegerValue = ErrorMessage(
- code:1,
- message: "The specified number is not valid. Valid numbers are within the range: [\(JSValue.MinimumSafeInt), \(JSValue.MaximumSafeInt)]")
-
- /// Error when attempting to access an element from a `JSValue` backed by a dictionary and there is no
- /// value stored at the specified key.
- public static let KeyNotFound = ErrorMessage(
- code: 2,
- message: "The specified key cannot be found.")
-
- /// Error when attempting to index into a `JSValue` that is not backed by a dictionary or array.
- public static let IndexingIntoUnsupportedType = ErrorMessage(
- code: 3,
- message: "Indexing is only supported on arrays and dictionaries."
- )
-
- /// Error when attempting to access an element from a `JSValue` backed by an array and the index is
- /// out of range.
- public static let IndexOutOfRange = ErrorMessage(
- code: 4,
- message: "The specified index is out of range of the bounds for the array.")
-
- /// Error when a parsing error occurs.
- public static let ParsingError = ErrorMessage(
- code: 5,
- message: "The JSON string being parsed was invalid.")
- }
+
+ /// A type that holds the error code and standard error message for the various types of failures
+ /// a `JSValue` can have.
+ public struct ErrorMessage {
+ /// The numeric value of the error number.
+ public let code: Int
+
+ /// The default message describing the error.
+ public let message: String
+ }
+
+ /// All of the error codes and standard error messages when parsing JSON.
+ public struct ErrorCode {
+ private init() {}
+
+ /// A integer that is outside of the safe range was attempted to be set.
+ public static let InvalidIntegerValue = ErrorMessage(
+ code: 1,
+ message:
+ "The specified number is not valid. Valid numbers are within the range: [\(JSValue.MinimumSafeInt), \(JSValue.MaximumSafeInt)]"
+ )
+
+ /// Error when attempting to access an element from a `JSValue` backed by a dictionary and there is no
+ /// value stored at the specified key.
+ public static let KeyNotFound = ErrorMessage(
+ code: 2,
+ message: "The specified key cannot be found."
+ )
+
+ /// Error when attempting to index into a `JSValue` that is not backed by a dictionary or array.
+ public static let IndexingIntoUnsupportedType = ErrorMessage(
+ code: 3,
+ message: "Indexing is only supported on arrays and dictionaries."
+ )
+
+ /// Error when attempting to access an element from a `JSValue` backed by an array and the index is
+ /// out of range.
+ public static let IndexOutOfRange = ErrorMessage(
+ code: 4,
+ message:
+ "The specified index is out of range of the bounds for the array."
+ )
+
+ /// Error when a parsing error occurs.
+ public static let ParsingError = ErrorMessage(
+ code: 5,
+ message: "The JSON string being parsed was invalid."
+ )
+ }
}
diff --git a/Sources/JSONLib/JSValue.Indexers.swift b/Sources/JSONLib/JSValue.Indexers.swift
index 80b7d08..373b195 100644
--- a/Sources/JSONLib/JSValue.Indexers.swift
+++ b/Sources/JSONLib/JSValue.Indexers.swift
@@ -4,73 +4,73 @@
* ------------------------------------------------------------------------------------------ */
extension JSValue {
- /// Attempts to treat the `JSValue` as a `JSObject` and perform the lookup.
- ///
- /// - returns: A `JSValue` that represents the value found at `key`
- public subscript(key: String) -> JSValue? {
- get {
- if let dict = self.object {
- if let value = dict[key] {
- return value
- }
- }
+ /// Attempts to treat the `JSValue` as a `JSObject` and perform the lookup.
+ ///
+ /// - returns: A `JSValue` that represents the value found at `key`
+ public subscript(key: String) -> JSValue? {
+ get {
+ if let dict = self.object {
+ if let value = dict[key] {
+ return value
+ }
+ }
- return nil
- }
- set {
- if var dict = self.object {
- dict[key] = newValue
- self = JSValue(dict)
- }
- }
- }
-
- /// Attempts to treat the `JSValue` as an array and return the item at the index.
- public subscript(index: Int) -> JSValue? {
- get {
- if let array = self.array {
- if index >= 0 && index < array.count {
- return array[index]
- }
- }
-
- return nil
- }
- set {
- if var array = self.array {
- array[index] = newValue ?? .null
- self = JSValue(array)
- }
- }
- }
+ return nil
+ }
+ set {
+ if var dict = self.object {
+ dict[key] = newValue
+ self = JSValue(dict)
+ }
+ }
+ }
+
+ /// Attempts to treat the `JSValue` as an array and return the item at the index.
+ public subscript(index: Int) -> JSValue? {
+ get {
+ if let array = self.array {
+ if index >= 0 && index < array.count {
+ return array[index]
+ }
+ }
+
+ return nil
+ }
+ set {
+ if var array = self.array {
+ array[index] = newValue ?? .null
+ self = JSValue(array)
+ }
+ }
+ }
}
/// Provide a usability extension to allow chaining index accessors without having to
/// marked it as optional.
/// e.g. foo["hi"]?["this"]?["sucks"] vs. foo["so"]["much"]["better"]
extension Optional where Wrapped == JSValue {
- public subscript(key: String) -> JSValue? {
- get {
- return self?.object?[key]
- }
- set {
- if var dict = self?.object {
- dict[key] = newValue
- self = JSValue(dict)
- }
- }
- }
-
- /// Attempts to treat the `JSValue` as an array and return the item at the index.
- public subscript(index: Int) -> JSValue? {
- get {
- return self?.array?[index]
- }
- set {
- if var array = self?.array {
- array[index] = newValue ?? .null
- self = JSValue(array)
- }
- }
- }
-}
\ No newline at end of file
+ public subscript(key: String) -> JSValue? {
+ get {
+ return self?.object?[key]
+ }
+ set {
+ if var dict = self?.object {
+ dict[key] = newValue
+ self = JSValue(dict)
+ }
+ }
+ }
+
+ /// Attempts to treat the `JSValue` as an array and return the item at the index.
+ public subscript(index: Int) -> JSValue? {
+ get {
+ return self?.array?[index]
+ }
+ set {
+ if var array = self?.array {
+ array[index] = newValue ?? .null
+ self = JSValue(array)
+ }
+ }
+ }
+}
diff --git a/Sources/JSONLib/JSValue.Literals.swift b/Sources/JSONLib/JSValue.Literals.swift
index 5b30052..55572b6 100644
--- a/Sources/JSONLib/JSValue.Literals.swift
+++ b/Sources/JSONLib/JSValue.Literals.swift
@@ -3,61 +3,61 @@
* Licensed under the MIT License. See License in the project root for license information.
* ------------------------------------------------------------------------------------------ */
-extension JSValue : ExpressibleByIntegerLiteral {
- private static func convert(_ value: Int64) -> JSValue {
- return JSValue(Double(value))
- }
-
- public init(integerLiteral value: Int64) {
- self = JSValue.convert(value)
- }
+extension JSValue: ExpressibleByIntegerLiteral {
+ private static func convert(_ value: Int64) -> JSValue {
+ return JSValue(Double(value))
+ }
+
+ public init(integerLiteral value: Int64) {
+ self = JSValue.convert(value)
+ }
}
-extension JSValue : ExpressibleByFloatLiteral {
- public init(floatLiteral value: Double) {
- self = JSValue(value)
- }
+extension JSValue: ExpressibleByFloatLiteral {
+ public init(floatLiteral value: Double) {
+ self = JSValue(value)
+ }
}
-extension JSValue : ExpressibleByStringLiteral {
- public init(stringLiteral value: String) {
- self = JSValue(value)
- }
-
- public init(extendedGraphemeClusterLiteral value: String) {
- self = JSValue(value)
- }
-
- public init(unicodeScalarLiteral value: String) {
- self = JSValue(value)
- }
+extension JSValue: ExpressibleByStringLiteral {
+ public init(stringLiteral value: String) {
+ self = JSValue(value)
+ }
+
+ public init(extendedGraphemeClusterLiteral value: String) {
+ self = JSValue(value)
+ }
+
+ public init(unicodeScalarLiteral value: String) {
+ self = JSValue(value)
+ }
}
-extension JSValue : ExpressibleByArrayLiteral {
- public init(arrayLiteral elements: JSValue...) {
- self = JSValue(elements)
- }
+extension JSValue: ExpressibleByArrayLiteral {
+ public init(arrayLiteral elements: JSValue...) {
+ self = JSValue(elements)
+ }
}
-extension JSValue : ExpressibleByDictionaryLiteral {
- public init(dictionaryLiteral elements: (String, JSValue)...) {
- var dict = [String:JSValue]()
- for (k, v) in elements {
- dict[k] = v
- }
-
- self = JSValue(dict)
- }
+extension JSValue: ExpressibleByDictionaryLiteral {
+ public init(dictionaryLiteral elements: (String, JSValue)...) {
+ var dict = [String: JSValue]()
+ for (k, v) in elements {
+ dict[k] = v
+ }
+
+ self = JSValue(dict)
+ }
}
-extension JSValue : ExpressibleByNilLiteral {
- public init(nilLiteral: ()) {
- self = .null
- }
+extension JSValue: ExpressibleByNilLiteral {
+ public init(nilLiteral: ()) {
+ self = .null
+ }
}
extension JSValue: ExpressibleByBooleanLiteral {
- public init(booleanLiteral value: Bool) {
- self = JSValue(value)
- }
+ public init(booleanLiteral value: Bool) {
+ self = JSValue(value)
+ }
}
diff --git a/Sources/JSONLib/JSValue.Parsing.swift b/Sources/JSONLib/JSValue.Parsing.swift
index 9c3dd4f..6b628d2 100644
--- a/Sources/JSONLib/JSValue.Parsing.swift
+++ b/Sources/JSONLib/JSValue.Parsing.swift
@@ -6,851 +6,1265 @@
import Foundation
extension JSValue {
- public static func parse(_ string: String) throws -> JSValue {
- let data = string.data(using: String.Encoding.utf8, allowLossyConversion: false)!
- return try data.withUnsafeBytes { (ptr: UnsafePointer) -> JSValue in
- let buffer = UnsafeBufferPointer(start: ptr, count: data.count)
- let generator = ReplayableGenerator(buffer)
-
- let value = try parse(generator)
- try validateRemainingContent(generator)
- return value
- }
- }
-
- public static func parse(_ data: Data) throws -> JSValue {
- return try data.withUnsafeBytes { (ptr: UnsafePointer) -> JSValue in
- let buffer = UnsafeBufferPointer(start: ptr, count: data.count)
- let generator = ReplayableGenerator(buffer)
-
- let value = try parse(generator)
- try validateRemainingContent(generator)
- return value
- }
- }
-
- /// Parses the given sequence of UTF8 code points and attempts to return a `JSValue` from it.
- /// - parameter seq: The sequence of UTF8 code points.
- /// - returns: A `JSParsingResult` containing the parsed `JSValue` or error information.
- public static func parse(_ bytes: [UInt8]) throws -> JSValue {
- return try bytes.withUnsafeBufferPointer { ptr in
- let generator = ReplayableGenerator(ptr)
-
- let value = try parse(generator)
- try validateRemainingContent(generator)
- return value
- }
- }
-
- static func validateRemainingContent(_ generator: ReplayableGenerator) throws {
- for codeunit in generator {
- if codeunit.isWhitespace() { continue }
- else {
- let remainingText = substring(generator)
-
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Invalid characters after the last item in the JSON: \(remainingText)"]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
- }
- }
-
- static let maximumDepth = 1000
- static var depthGuard: Int = 0
- static func parse(_ generator: ReplayableGenerator) throws -> JSValue {
- for codeunit in generator {
- if codeunit.isWhitespace() { continue }
-
- if codeunit == Token.LeftCurly {
- depthGuard += 1
- defer { depthGuard -= 1 }
-
- if depthGuard > maximumDepth {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unable to process JSON file with a nested depth greater than \(maximumDepth)."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
- return try JSValue.parseObject(generator)
- }
- else if codeunit == Token.LeftBracket {
- depthGuard += 1
- defer { depthGuard -= 1 }
-
- if depthGuard > maximumDepth {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unable to process JSON file with a nested depth greater than \(maximumDepth)."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
- return try JSValue.parseArray(generator)
- }
- else if codeunit.isDigit() || codeunit == Token.Minus || codeunit == Token.Plus || codeunit == Token.Period {
- return try JSValue.parseNumber(generator)
- }
- else if codeunit == Token.t {
- return try JSValue.parseTrue(generator)
- }
- else if codeunit == Token.f {
- return try JSValue.parseFalse(generator)
- }
- else if codeunit == Token.n {
- return try JSValue.parseNull(generator)
- }
- else if codeunit == Token.DoubleQuote {
- return try JSValue.parseString(generator)
- }
- else {
- break
- }
- }
-
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "No valid JSON value was found to parse in string."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- enum ObjectParsingState {
- case initial
- case key
- case value
- }
-
- static func parseObject(_ generator: ReplayableGenerator) throws -> JSValue {
- var state = ObjectParsingState.initial
-
- var key = ""
- var dict = [String:JSValue]()
- var needsComma = false
- var lastParsedComma = false
-
- for (idx, codeunit) in generator.enumerated() {
- switch (idx, codeunit) {
- case (0, Token.LeftCurly): continue
- case (_, Token.RightCurly):
- if lastParsedComma {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "A trailing `,` is not supported. Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- switch state {
- case .initial: fallthrough
- case .value:
- let _ = generator.next() // eat the '}'
- return JSValue(dict)
-
- default:
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Expected token '}' at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- case (_, Token.DoubleQuote):
- if needsComma {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unable to parse object. Expected `,` to separate values. Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
+ public static func parse(_ string: String) throws -> JSValue {
+ let data = string.data(
+ using: String.Encoding.utf8,
+ allowLossyConversion: false
+ )!
+ return try data.withUnsafeBytes { (ptr: UnsafePointer) -> JSValue in
+ let buffer = UnsafeBufferPointer(start: ptr, count: data.count)
+ let generator = ReplayableGenerator(buffer)
+
+ let value = try parse(generator)
+ try validateRemainingContent(generator)
+ return value
+ }
+ }
- switch state {
- case .initial:
- state = .key
+ public static func parse(_ data: Data) throws -> JSValue {
+ return try data.withUnsafeBytes { (ptr: UnsafePointer) -> JSValue in
+ let buffer = UnsafeBufferPointer(start: ptr, count: data.count)
+ let generator = ReplayableGenerator(buffer)
- key = try parseString(generator).string!
- generator.replay()
+ let value = try parse(generator)
+ try validateRemainingContent(generator)
+ return value
+ }
+ }
+
+ /// Parses the given sequence of UTF8 code points and attempts to return a `JSValue` from it.
+ /// - parameter seq: The sequence of UTF8 code points.
+ /// - returns: A `JSParsingResult` containing the parsed `JSValue` or error information.
+ public static func parse(_ bytes: [UInt8]) throws -> JSValue {
+ return try bytes.withUnsafeBufferPointer { ptr in
+ let generator = ReplayableGenerator(ptr)
+
+ let value = try parse(generator)
+ try validateRemainingContent(generator)
+ return value
+ }
+ }
+
+ static func validateRemainingContent(_ generator: ReplayableGenerator) throws {
+ for codeunit in generator {
+ if codeunit.isWhitespace() {
+ continue
+ }
+ else {
+ let remainingText = substring(generator)
+
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode
+ .ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Invalid characters after the last item in the JSON: \(remainingText)",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+ }
+ }
+
+ static let maximumDepth = 1000
+ static var depthGuard: Int = 0
+ static func parse(_ generator: ReplayableGenerator) throws -> JSValue {
+ for codeunit in generator {
+ if codeunit.isWhitespace() { continue }
+
+ if codeunit == Token.LeftCurly {
+ depthGuard += 1
+ defer { depthGuard -= 1 }
+
+ if depthGuard > maximumDepth {
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unable to process JSON file with a nested depth greater than \(maximumDepth).",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+ return try JSValue.parseObject(generator)
+ }
+ else if codeunit == Token.LeftBracket {
+ depthGuard += 1
+ defer { depthGuard -= 1 }
+
+ if depthGuard > maximumDepth {
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unable to process JSON file with a nested depth greater than \(maximumDepth).",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+ return try JSValue.parseArray(generator)
+ }
+ else if codeunit.isDigit() || codeunit == Token.Minus
+ || codeunit == Token.Plus || codeunit == Token.Period
+ {
+ return try JSValue.parseNumber(generator)
+ }
+ else if codeunit == Token.t {
+ return try JSValue.parseTrue(generator)
+ }
+ else if codeunit == Token.f {
+ return try JSValue.parseFalse(generator)
+ }
+ else if codeunit == Token.n {
+ return try JSValue.parseNull(generator)
+ }
+ else if codeunit == Token.DoubleQuote {
+ return try JSValue.parseString(generator)
+ }
+ else {
+ break
+ }
+ }
- default:
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Expected token ''' (single quote) or '\"' at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- case (_, Token.Colon):
- switch state {
- case .key:
- state = .value
-
- let _ = generator.next() // eat the ':'
- let value = try parse(generator)
- dict[key] = value
- generator.replay()
- needsComma = true
- lastParsedComma = false
-
- default:
let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Expected token ':' at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
+ ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "No valid JSON value was found to parse in string.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ enum ObjectParsingState {
+ case initial
+ case key
+ case value
+ }
+
+ static func parseObject(_ generator: ReplayableGenerator) throws -> JSValue {
+ var state = ObjectParsingState.initial
+
+ var key = ""
+ var dict = [String: JSValue]()
+ var needsComma = false
+ var lastParsedComma = false
+
+ for (idx, codeunit) in generator.enumerated() {
+ switch (idx, codeunit) {
+ case (0, Token.LeftCurly): continue
+ case (_, Token.RightCurly):
+ if lastParsedComma {
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "A trailing `,` is not supported. Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ switch state {
+ case .initial, .value:
+ let _ = generator.next() // eat the '}'
+ return JSValue(dict)
+
+ default:
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "Expected token '}' at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ case (_, Token.DoubleQuote):
+ if needsComma {
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unable to parse object. Expected `,` to separate values. Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ switch state {
+ case .initial:
+ state = .key
+
+ key = try parseString(generator).string!
+ generator.replay()
+
+ default:
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "Expected token ''' (single quote) or '\"' at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ case (_, Token.Colon):
+ switch state {
+ case .key:
+ state = .value
+
+ let _ = generator.next() // eat the ':'
+ let value = try parse(generator)
+ dict[key] = value
+ generator.replay()
+ needsComma = true
+ lastParsedComma = false
+
+ default:
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "Expected token ':' at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ case (_, Token.Comma):
+ if !needsComma {
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "A `,` can only separate values. Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+ needsComma = false
+ lastParsedComma = true
+
+ switch state {
+ case .value:
+ state = .initial
+ key = ""
+
+ default:
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "Expected token ',' at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ default:
+ if codeunit.isWhitespace() {
+ continue
+ }
+ else {
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unexpected token at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+ }
+ }
- case (_, Token.Comma):
- if !needsComma {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "A `,` can only separate values. Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
- needsComma = false
- lastParsedComma = true
-
- switch state {
- case .value:
- state = .initial
- key = ""
-
- default:
let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Expected token ',' at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- default:
- if codeunit.isWhitespace() { continue }
- else {
+ ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unable to parse object. Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ static func parseArray(_ generator: ReplayableGenerator) throws -> JSValue {
+ var values = [JSValue]()
+ var needsComma = false
+ var lastParsedComma = false
+
+ for (idx, codeunit) in generator.enumerated() {
+ switch (idx, codeunit) {
+ case (0, Token.LeftBracket): continue
+ case (_, Token.RightBracket):
+ if lastParsedComma {
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "A trailing `,` is not supported. Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+ let _ = generator.next() // eat the ']'
+ return JSValue(values)
+ case (_, Token.Comma):
+ if !needsComma {
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "A `,` can only separate values. Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+ needsComma = false
+ lastParsedComma = true
+
+ default:
+ if codeunit.isWhitespace() {
+ continue
+ }
+ else {
+ if needsComma {
+ let info = [
+ ErrorKeys
+ .LocalizedDescription:
+ ErrorCode
+ .ParsingError
+ .message,
+ ErrorKeys
+ .LocalizedFailureReason:
+ "Unable to parse array. Expected `,` to separate values. Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError
+ .code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+ let value = try parse(generator)
+ values.append(value)
+ generator.replay()
+ needsComma = true
+ lastParsedComma = false
+ }
+ }
+ }
+
let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unexpected token at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
- }
- }
-
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unable to parse object. Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- static func parseArray(_ generator: ReplayableGenerator) throws -> JSValue {
- var values = [JSValue]()
- var needsComma = false
- var lastParsedComma = false
-
- for (idx, codeunit) in generator.enumerated() {
- switch (idx, codeunit) {
- case (0, Token.LeftBracket): continue
- case (_, Token.RightBracket):
- if lastParsedComma {
+ ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unable to parse array. Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ enum NumberParsingState {
+ case initial
+ case leadingZero
+ case leadingNegativeSign
+ case integer
+ case decimal
+ case fractional
+ case exponent
+ case exponentSign
+ case exponentDigits
+ }
+
+ static func parseNumber(_ generator: ReplayableGenerator) throws -> JSValue {
+ // The https://tools.ietf.org/html/rfc7159 spec for numbers.
+ // number = [ minus ] int [ frac ] [ exp ]
+ // decimal-point = %x2E ; .
+ // digit1-9 = %x31-39 ; 1-9
+ // e = %x65 / %x45 ; e E
+ // exp = e [ minus / plus ] 1*DIGIT
+ // frac = decimal-point 1*DIGIT
+ // int = zero / ( digit1-9 *DIGIT )
+ // minus = %x2D ; -
+ // plus = %x2B ; +
+ // zero = %x30 ; 0
+
+ var sign: Double = 1
+ var value: Double = 0
+ var fraction: Double = 0
+ var exponent: Double = 0
+ var exponentSign: Double = 1
+ var fractionMultiplier: Double = 10
+
+ var state: NumberParsingState = .initial
+ var valid = false
+
+ loop: for (idx, codeunit) in generator.enumerated() {
+ switch (idx, codeunit, state) {
+
+ case (_, Token.Minus, .initial):
+ sign = -1
+ state = .leadingNegativeSign
+
+ case (_, Token.Plus, .initial):
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode
+ .ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Leading plus is not supported. Index: \(idx). Token: \(codeunit). State: \(state). Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+
+ case (_, Token.Zero, .initial):
+ state = .leadingZero
+
+ case (_, Token.Zero, .leadingNegativeSign):
+ state = .leadingZero
+
+ case (_, Token.Zero...Token.Nine, .leadingZero):
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode
+ .ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Leading zeros are not allowed.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ case (_, Token.One...Token.Nine, .leadingNegativeSign),
+ (_, Token.One...Token.Nine, .initial):
+ value = value * 10 + Double(codeunit - Token.Zero)
+ state = .integer
+
+ case (_, Token.Zero...Token.Nine, .decimal):
+ fraction =
+ Double(codeunit - Token.Zero) / fractionMultiplier
+ fractionMultiplier *= 10
+ state = .fractional
+
+ case (_, Token.Zero...Token.Nine, .integer):
+ value = value * 10 + Double(codeunit - Token.Zero)
+
+ case (_, Token.Zero...Token.Nine, .fractional):
+ fraction =
+ fraction
+ + (Double(codeunit - Token.Zero)
+ / fractionMultiplier)
+ fractionMultiplier *= 10
+ case (_, Token.e, .leadingZero), (_, Token.E, .leadingZero),
+ (_, Token.e, .integer), (_, Token.E, .integer),
+ (_, Token.e, .fractional), (_, Token.E, .fractional):
+ state = .exponent
+
+ case (_, Token.Minus, .exponent):
+ exponentSign = -1
+ state = .exponentSign
+
+ case (_, Token.Plus, .exponent):
+ state = .exponentSign
+
+ case (_, Token.Zero...Token.Nine, .exponent),
+ (_, Token.Zero...Token.Nine, .exponentSign),
+ (_, Token.Zero...Token.Nine, .exponentDigits):
+ exponent = exponent * 10 + Double(codeunit - Token.Zero)
+ state = .exponentDigits
+ case (_, Token.Period, .leadingZero), (_, Token.Period, .integer):
+ state = .decimal
+
+ default:
+ if codeunit.isValidTerminator() {
+ generator.replay()
+ valid = true
+ break loop
+ }
+ else {
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unexpected token at index: \(idx). Token: \(codeunit). State: \(state). Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+ }
+ }
+
+ if generator.atEnd() || valid {
+ if state == .decimal {
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode
+ .ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "A trailing period is not allowed.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ if state == .exponent {
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode
+ .ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "A trailing 'e' or 'E' is not allowed.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ if state == .exponentSign {
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode
+ .ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "A exponent sign is not allowed.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ if state == .leadingNegativeSign {
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode
+ .ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "A number cannot just be a leading negative sign.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ guard let exponentValue = Int(exactly: exponentSign * exponent) else {
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode
+ .ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "The exponent is too large to process.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ return JSValue(sign * exp((value + fraction), exponentValue))
+ }
+
let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "A trailing `,` is not supported. Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
- let _ = generator.next() // eat the ']'
- return JSValue(values)
- case (_, Token.Comma):
- if !needsComma {
+ ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unable to parse array. Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ static func parseTrue(_ generator: ReplayableGenerator) throws -> JSValue {
+ for (idx, codeunit) in generator.enumerated() {
+ switch (idx, codeunit) {
+ case (0, Token.t): continue
+ case (1, Token.r): continue
+ case (2, Token.u): continue
+ case (3, Token.e): continue
+ case (4, _):
+ if codeunit.isValidTerminator() { return JSValue(true) }
+ fallthrough
+
+ default:
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode
+ .ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unexpected token at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+ }
+
+ if generator.atEnd() { return JSValue(true) }
+
let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "A `,` can only separate values. Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
- needsComma = false
- lastParsedComma = true
-
- default:
- if codeunit.isWhitespace() { continue }
- else {
- if needsComma {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unable to parse array. Expected `,` to separate values. Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
+ ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unable to parse 'true' literal. Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ static func parseFalse(_ generator: ReplayableGenerator) throws -> JSValue {
+ for (idx, codeunit) in generator.enumerated() {
+ switch (idx, codeunit) {
+ case (0, Token.f): continue
+ case (1, Token.a): continue
+ case (2, Token.l): continue
+ case (3, Token.s): continue
+ case (4, Token.e): continue
+ case (5, _):
+ if codeunit.isValidTerminator() { return JSValue(false) }
+ fallthrough
+
+ default:
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode
+ .ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unexpected token at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
}
- let value = try parse(generator)
- values.append(value)
- generator.replay()
- needsComma = true
- lastParsedComma = false
- }
- }
- }
-
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unable to parse array. Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- enum NumberParsingState {
- case initial
- case leadingZero
- case leadingNegativeSign
- case integer
- case decimal
- case fractional
- case exponent
- case exponentSign
- case exponentDigits
- }
-
- static func parseNumber(_ generator: ReplayableGenerator) throws -> JSValue {
- // The https://tools.ietf.org/html/rfc7159 spec for numbers.
- // number = [ minus ] int [ frac ] [ exp ]
- // decimal-point = %x2E ; .
- // digit1-9 = %x31-39 ; 1-9
- // e = %x65 / %x45 ; e E
- // exp = e [ minus / plus ] 1*DIGIT
- // frac = decimal-point 1*DIGIT
- // int = zero / ( digit1-9 *DIGIT )
- // minus = %x2D ; -
- // plus = %x2B ; +
- // zero = %x30 ; 0
-
-
- var sign: Double = 1
- var value: Double = 0
- var fraction: Double = 0
- var exponent: Double = 0
- var exponentSign: Double = 1
- var fractionMultiplier: Double = 10
-
- var state: NumberParsingState = .initial
- var valid = false
-
- loop: for (idx, codeunit) in generator.enumerated() {
- switch (idx, codeunit, state) {
-
- case (_, Token.Minus, .initial):
- sign = -1
- state = .leadingNegativeSign
-
- case (_, Token.Plus, .initial):
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Leading plus is not supported. Index: \(idx). Token: \(codeunit). State: \(state). Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
-
- case (_, Token.Zero, .initial):
- state = .leadingZero
-
- case (_, Token.Zero, .leadingNegativeSign):
- state = .leadingZero
-
- case (_, Token.Zero...Token.Nine, .leadingZero):
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Leading zeros are not allowed."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
-
- case (_, Token.One...Token.Nine, .leadingNegativeSign): fallthrough
- case (_, Token.One...Token.Nine, .initial):
- value = value * 10 + Double(codeunit - Token.Zero)
- state = .integer
-
- case (_, Token.Zero...Token.Nine, .decimal):
- fraction = Double(codeunit - Token.Zero) / fractionMultiplier
- fractionMultiplier *= 10
- state = .fractional
-
- case (_, Token.Zero...Token.Nine, .integer):
- value = value * 10 + Double(codeunit - Token.Zero)
-
- case (_, Token.Zero...Token.Nine, .fractional):
- fraction = fraction + (Double(codeunit - Token.Zero) / fractionMultiplier)
- fractionMultiplier *= 10
-
- case (_, Token.e, .leadingZero): fallthrough
- case (_, Token.E, .leadingZero): fallthrough
- case (_, Token.e, .integer): fallthrough
- case (_, Token.E, .integer): fallthrough
- case (_, Token.e, .fractional): fallthrough
- case (_, Token.E, .fractional):
- state = .exponent
-
- case (_, Token.Minus, .exponent):
- exponentSign = -1
- state = .exponentSign
-
- case (_, Token.Plus, .exponent):
- state = .exponentSign
-
- case (_, Token.Zero...Token.Nine, .exponent): fallthrough
- case (_, Token.Zero...Token.Nine, .exponentSign): fallthrough
- case (_, Token.Zero...Token.Nine, .exponentDigits):
- exponent = exponent * 10 + Double(codeunit - Token.Zero)
- state = .exponentDigits
-
- case (_, Token.Period, .leadingZero): fallthrough
- case (_, Token.Period, .integer):
- state = .decimal
-
- default:
- if codeunit.isValidTerminator() { generator.replay(); valid = true; break loop }
- else {
+
+ if generator.atEnd() { return JSValue(false) }
+
let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unexpected token at index: \(idx). Token: \(codeunit). State: \(state). Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
- }
- }
-
- if generator.atEnd() || valid {
- if state == .decimal {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "A trailing period is not allowed."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- if state == .exponent {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "A trailing 'e' or 'E' is not allowed."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- if state == .exponentSign {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "A exponent sign is not allowed."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- if state == .leadingNegativeSign {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "A number cannot just be a leading negative sign."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- guard let exponentValue = Int(exactly: exponentSign * exponent) else {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "The exponent is too large to process."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- return JSValue(sign * exp((value + fraction), exponentValue))
- }
-
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unable to parse array. Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- static func parseTrue(_ generator: ReplayableGenerator) throws -> JSValue {
- for (idx, codeunit) in generator.enumerated() {
- switch (idx, codeunit) {
- case (0, Token.t): continue
- case (1, Token.r): continue
- case (2, Token.u): continue
- case (3, Token.e): continue
- case (4, _):
- if codeunit.isValidTerminator() { return JSValue(true) }
- fallthrough
-
- default:
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unexpected token at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
- }
-
- if generator.atEnd() { return JSValue(true) }
-
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unable to parse 'true' literal. Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- static func parseFalse(_ generator: ReplayableGenerator) throws -> JSValue {
- for (idx, codeunit) in generator.enumerated() {
- switch (idx, codeunit) {
- case (0, Token.f): continue
- case (1, Token.a): continue
- case (2, Token.l): continue
- case (3, Token.s): continue
- case (4, Token.e): continue
- case (5, _):
- if codeunit.isValidTerminator() { return JSValue(false) }
- fallthrough
-
- default:
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unexpected token at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
- }
-
- if generator.atEnd() { return JSValue(false) }
-
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unable to parse 'false' literal. Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- static func parseNull(_ generator: ReplayableGenerator) throws -> JSValue {
- for (idx, codeunit) in generator.enumerated() {
- switch (idx, codeunit) {
- case (0, Token.n): continue
- case (1, Token.u): continue
- case (2, Token.l): continue
- case (3, Token.l): continue
- case (4, _):
- if codeunit.isValidTerminator() { return .null }
- fallthrough
-
- default:
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unexpected token at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
- }
-
- if generator.atEnd() { return .null }
-
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unable to parse 'null' literal. Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- fileprivate static func parseHexDigit(_ digit: UInt8) -> Int? {
- if Token.Zero <= digit && digit <= Token.Nine {
- return Int(digit) - Int(Token.Zero)
- } else if Token.a <= digit && digit <= Token.f {
- return 10 + Int(digit) - Int(Token.a)
- } else if Token.A <= digit && digit <= Token.F {
- return 10 + Int(digit) - Int(Token.A)
- } else {
- return nil
- }
- }
-
- // Implementation Note: This is move outside here to avoid the re-allocation of this buffer each time.
- // This has a significant impact on performance.
- static var stringStorage: [UInt8] = []
- static func parseString(_ generator: ReplayableGenerator) throws -> JSValue {
- stringStorage.removeAll(keepingCapacity: true)
-
- for (idx, codeunit) in generator.enumerated() {
- switch (idx, codeunit) {
- case (0, Token.DoubleQuote): continue
- case (_, Token.DoubleQuote):
- let _ = generator.next() // eat the quote
-
- if let string = String(bytes: stringStorage, encoding: .utf8) {
- return JSValue(string)
- }
- else {
+ ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unable to parse 'false' literal. Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ static func parseNull(_ generator: ReplayableGenerator) throws -> JSValue {
+ for (idx, codeunit) in generator.enumerated() {
+ switch (idx, codeunit) {
+ case (0, Token.n): continue
+ case (1, Token.u): continue
+ case (2, Token.l): continue
+ case (3, Token.l): continue
+ case (4, _):
+ if codeunit.isValidTerminator() { return .null }
+ fallthrough
+
+ default:
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode
+ .ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unexpected token at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+ }
+
+ if generator.atEnd() { return .null }
+
let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unable to convert the parsed bytes into a string. Bytes: \(stringStorage)'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- case (_, Token.Null...Token.ShiftIn):
+ ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unable to parse 'null' literal. Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ fileprivate static func parseHexDigit(_ digit: UInt8) -> Int? {
+ if Token.Zero <= digit && digit <= Token.Nine {
+ return Int(digit) - Int(Token.Zero)
+ }
+ else if Token.a <= digit && digit <= Token.f {
+ return 10 + Int(digit) - Int(Token.a)
+ }
+ else if Token.A <= digit && digit <= Token.F {
+ return 10 + Int(digit) - Int(Token.A)
+ }
+ else {
+ return nil
+ }
+ }
+
+ // Implementation Note: This is move outside here to avoid the re-allocation of this buffer each time.
+ // This has a significant impact on performance.
+ static var stringStorage: [UInt8] = []
+ static func parseString(_ generator: ReplayableGenerator) throws -> JSValue {
+ stringStorage.removeAll(keepingCapacity: true)
+
+ for (idx, codeunit) in generator.enumerated() {
+ switch (idx, codeunit) {
+ case (0, Token.DoubleQuote): continue
+ case (_, Token.DoubleQuote):
+ let _ = generator.next() // eat the quote
+
+ if let string = String(
+ bytes: stringStorage,
+ encoding: .utf8
+ ) {
+ return JSValue(string)
+ }
+ else {
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unable to convert the parsed bytes into a string. Bytes: \(stringStorage)'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ case (_, Token.Null...Token.ShiftIn):
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode
+ .ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "An unescaped control character is not allowed in a string.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+
+ case (_, Token.Backslash):
+ let next = generator.next()
+
+ if let next = next {
+ switch next {
+
+ case Token.Backslash:
+ stringStorage.append(Token.Backslash)
+
+ case Token.Forwardslash:
+ stringStorage.append(Token.Forwardslash)
+
+ case Token.DoubleQuote:
+ stringStorage.append(Token.DoubleQuote)
+
+ case Token.n:
+ stringStorage.append(Token.Linefeed)
+
+ case Token.b:
+ stringStorage.append(Token.Backspace)
+
+ case Token.f:
+ stringStorage.append(Token.Formfeed)
+
+ case Token.r:
+ stringStorage.append(
+ Token.CarriageReturn
+ )
+
+ case Token.t:
+ stringStorage.append(
+ Token.HorizontalTab
+ )
+
+ case Token.u:
+ let codeunits: [UInt16]
+
+ let codeunit = try parseCodeUnit(
+ generator
+ )
+ if UTF16.isLeadSurrogate(codeunit) {
+ guard
+ let next =
+ generator
+ .next()
+ else {
+ let info = [
+ ErrorKeys
+ .LocalizedDescription:
+ ErrorCode
+ .ParsingError
+ .message,
+ ErrorKeys
+ .LocalizedFailureReason:
+ "Expected to find the second half of the surrogate pair.",
+ ]
+ throw
+ JsonParserError(
+ code:
+ ErrorCode
+ .ParsingError
+ .code,
+ domain:
+ JSValueErrorDomain,
+ userInfo:
+ info
+ )
+ }
+
+ if next != Token.Backslash {
+ let info = [
+ ErrorKeys
+ .LocalizedDescription:
+ ErrorCode
+ .ParsingError
+ .message,
+ ErrorKeys
+ .LocalizedFailureReason:
+ "Invalid unicode scalar, expected \\.",
+ ]
+ throw
+ JsonParserError(
+ code:
+ ErrorCode
+ .ParsingError
+ .code,
+ domain:
+ JSValueErrorDomain,
+ userInfo:
+ info
+ )
+ }
+
+ guard
+ let next2 =
+ generator
+ .next()
+ else {
+ let info = [
+ ErrorKeys
+ .LocalizedDescription:
+ ErrorCode
+ .ParsingError
+ .message,
+ ErrorKeys
+ .LocalizedFailureReason:
+ "Expected to find the second half of the surrogate pair.",
+ ]
+ throw
+ JsonParserError(
+ code:
+ ErrorCode
+ .ParsingError
+ .code,
+ domain:
+ JSValueErrorDomain,
+ userInfo:
+ info
+ )
+ }
+
+ if next2 != Token.u {
+ let info = [
+ ErrorKeys
+ .LocalizedDescription:
+ ErrorCode
+ .ParsingError
+ .message,
+ ErrorKeys
+ .LocalizedFailureReason:
+ "Invalid unicode scalar, expected u.",
+ ]
+ throw
+ JsonParserError(
+ code:
+ ErrorCode
+ .ParsingError
+ .code,
+ domain:
+ JSValueErrorDomain,
+ userInfo:
+ info
+ )
+ }
+
+ let codeunit2 =
+ try parseCodeUnit(
+ generator
+ )
+ codeunits = [
+ codeunit, codeunit2,
+ ]
+ }
+ else {
+ codeunits = [codeunit]
+ }
+
+ let transcodingError = transcode(
+ codeunits.makeIterator(),
+ from: UTF16.self,
+ to: UTF8.self,
+ stoppingOnError: true
+ ) { stringStorage.append($0) }
+
+ if transcodingError {
+ let info = [
+ ErrorKeys
+ .LocalizedDescription:
+ ErrorCode
+ .ParsingError
+ .message,
+ ErrorKeys
+ .LocalizedFailureReason:
+ "Invalid unicode scalar",
+ ]
+ throw JsonParserError(
+ code: ErrorCode
+ .ParsingError
+ .code,
+ domain:
+ JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ default:
+ let info = [
+ ErrorKeys
+ .LocalizedDescription:
+ ErrorCode
+ .ParsingError
+ .message,
+ ErrorKeys
+ .LocalizedFailureReason:
+ "Unexpected token at index: \(idx + 1). Token: \(next). Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError
+ .code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+ }
+ else {
+ let info = [
+ ErrorKeys.LocalizedDescription:
+ ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unexpected token at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ default:
+ stringStorage.append(codeunit)
+ }
+ }
+
let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "An unescaped control character is not allowed in a string."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
-
- case (_, Token.Backslash):
- let next = generator.next()
-
- if let next = next {
- switch next {
-
- case Token.Backslash:
- stringStorage.append(Token.Backslash)
-
- case Token.Forwardslash:
- stringStorage.append(Token.Forwardslash)
-
- case Token.DoubleQuote:
- stringStorage.append(Token.DoubleQuote)
-
- case Token.n:
- stringStorage.append(Token.Linefeed)
-
- case Token.b:
- stringStorage.append(Token.Backspace)
-
- case Token.f:
- stringStorage.append(Token.Formfeed)
-
- case Token.r:
- stringStorage.append(Token.CarriageReturn)
-
- case Token.t:
- stringStorage.append(Token.HorizontalTab)
-
- case Token.u:
- let codeunits: [UInt16]
-
- let codeunit = try parseCodeUnit(generator)
- if UTF16.isLeadSurrogate(codeunit) {
- guard let next = generator.next() else {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Expected to find the second half of the surrogate pair."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- if next != Token.Backslash {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Invalid unicode scalar, expected \\."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- guard let next2 = generator.next() else {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Expected to find the second half of the surrogate pair."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- if next2 != Token.u {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Invalid unicode scalar, expected u."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- let codeunit2 = try parseCodeUnit(generator)
- codeunits = [codeunit, codeunit2]
- }
- else {
- codeunits = [codeunit]
- }
-
- let transcodingError = transcode(codeunits.makeIterator(),
- from: UTF16.self,
- to: UTF8.self,
- stoppingOnError: true) { stringStorage.append($0) }
-
- if transcodingError {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Invalid unicode scalar"]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
+ ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Unable to parse string. Context: '\(contextualString(generator))'.",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ static func parseCodeUnit(_ generator: ReplayableGenerator) throws -> UInt16 {
+ let c1 = generator.next()
+ let c2 = generator.next()
+ let c3 = generator.next()
+ let c4 = generator.next()
+
+ switch (c1, c2, c3, c4) {
+ case let (.some(c1), .some(c2), .some(c3), .some(c4)):
+ let value1 = parseHexDigit(c1)
+ let value2 = parseHexDigit(c2)
+ let value3 = parseHexDigit(c3)
+ let value4 = parseHexDigit(c4)
+
+ if value1 == nil || value2 == nil || value3 == nil || value4 == nil {
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode
+ .ParsingError.message,
+ ErrorKeys.LocalizedFailureReason:
+ "Invalid unicode escape sequence",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
+ }
+
+ return UInt16(
+ (value1! << 12) | (value2! << 8) | (value3! << 4) | value4!
+ )
default:
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unexpected token at index: \(idx + 1). Token: \(next). Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
+ let info = [
+ ErrorKeys.LocalizedDescription: ErrorCode.ParsingError
+ .message,
+ ErrorKeys.LocalizedFailureReason:
+ "Invalid unicode escape sequence",
+ ]
+ throw JsonParserError(
+ code: ErrorCode.ParsingError.code,
+ domain: JSValueErrorDomain,
+ userInfo: info
+ )
}
- }
- else {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unexpected token at index: \(idx). Token: \(codeunit). Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- default:
- stringStorage.append(codeunit)
- }
- }
-
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Unable to parse string. Context: '\(contextualString(generator))'."]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- static func parseCodeUnit(_ generator: ReplayableGenerator) throws -> UInt16 {
- let c1 = generator.next()
- let c2 = generator.next()
- let c3 = generator.next()
- let c4 = generator.next()
-
- switch (c1, c2, c3, c4) {
- case let (.some(c1), .some(c2), .some(c3), .some(c4)):
- let value1 = parseHexDigit(c1)
- let value2 = parseHexDigit(c2)
- let value3 = parseHexDigit(c3)
- let value4 = parseHexDigit(c4)
-
- if value1 == nil || value2 == nil || value3 == nil || value4 == nil {
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Invalid unicode escape sequence"]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
-
- return UInt16((value1! << 12) | (value2! << 8) | (value3! << 4) | value4!)
-
- default:
- let info = [
- ErrorKeys.LocalizedDescription: ErrorCode.ParsingError.message,
- ErrorKeys.LocalizedFailureReason: "Invalid unicode escape sequence"]
- throw JsonParserError(code: ErrorCode.ParsingError.code, domain: JSValueErrorDomain, userInfo: info)
- }
- }
-
- // MARK: Helper functions
-
- static func substring(_ generator: ReplayableGenerator) -> String {
- var string = ""
-
- for codeunit in generator {
- string += String(codeunit)
- }
-
- return string
- }
-
-
- static func contextualString(_ generator: ReplayableGenerator, left: Int = 5, right: Int = 10) -> String {
- var string = ""
-
- for _ in 0.. Double {
- return exp < 0 ?
- (0 ..< abs(exp)).reduce(number, { x, _ in x / 10 }) :
- (0 ..< exp).reduce(number, { x, _ in x * 10 })
- }
+ }
+
+ // MARK: Helper functions
+
+ static func substring(_ generator: ReplayableGenerator) -> String {
+ var string = ""
+
+ for codeunit in generator {
+ string += String(codeunit)
+ }
+
+ return string
+ }
+
+ static func contextualString(
+ _ generator: ReplayableGenerator,
+ left: Int = 5,
+ right: Int = 10
+ ) -> String {
+ var string = ""
+
+ for _ in 0.. Double {
+ return exp < 0
+ ? (0.. Bool {
- switch self {
- case 0x09: return true /* horizontal tab */
- case 0x0A: return true /* line feed or new line */
- case 0x20: return true /* space */
- case 0x0D: return true /* carriage return */
- default: return false
- }
- }
-
- /// Determines if the `UnicodeScalar` represents a numeric digit.
- ///
- /// :return: `true` if the scalar is a Unicode numeric character; `false` otherwise.
- func isDigit() -> Bool {
- return self >= Token.Zero && self <= Token.Nine
- }
-
- /// Determines if the `UnicodeScalar` represents a valid terminating character.
- /// :return: `true` if the scalar is a valid terminator, `false` otherwise.
- func isValidTerminator() -> Bool {
- if self == Token.Comma { return true }
- if self == Token.RightBracket { return true }
- if self == Token.RightCurly { return true }
- if self.isWhitespace() { return true }
-
- return false
- }
-
- // /// Stores the `UInt8` bytes that make up the UTF8 code points for the scalar.
- // ///
- // /// :param: buffer the buffer to write the UTF8 code points into.
- // func utf8(inout buffer: [UInt8]) {
- // /*
- // * This implementation should probably be replaced by the function below. However,
- // * I am not quite sure how to properly use `SinkType` yet...
- // *
- // * UTF8.encode(input: UnicodeScalar, output: &S)
- // */
- //
- // if value <= 0x007F {
- // buffer.append(UInt8(value))
- // }
- // else if 0x0080 <= value && value <= 0x07FF {
- // buffer.append(UInt8(value &/ 64) &+ 192)
- // buffer.append(UInt8(value &% 64) &+ 128)
- // }
- // else if (0x0800 <= value && value <= 0xD7FF) || (0xE000 <= value && value <= 0xFFFF) {
- // buffer.append(UInt8(value &/ 4096) &+ 224)
- // buffer.append(UInt8((value &% 4096) &/ 64) &+ 128)
- // buffer.append(UInt8(value &% 64 &+ 128))
- // }
- // else {
- // buffer.append(UInt8(value &/ 262144) &+ 240)
- // buffer.append(UInt8((value &% 262144) &/ 4096) &+ 128)
- // buffer.append(UInt8((value &% 4096) &/ 64) &+ 128)
- // buffer.append(UInt8(value &% 64) &+ 128)
- // }
- // }
+ /// Determines if the `UnicodeScalar` represents one of the standard Unicode whitespace characters.
+ ///
+ /// :return: `true` if the scalar is a valid JSON whitespace character; `false` otherwise.
+ func isWhitespace() -> Bool {
+ switch self {
+ case 0x09: return true /* horizontal tab */
+ case 0x0A: return true /* line feed or new line */
+ case 0x20: return true /* space */
+ case 0x0D: return true /* carriage return */
+ default: return false
+ }
+ }
+
+ /// Determines if the `UnicodeScalar` represents a numeric digit.
+ ///
+ /// :return: `true` if the scalar is a Unicode numeric character; `false` otherwise.
+ func isDigit() -> Bool {
+ return self >= Token.Zero && self <= Token.Nine
+ }
+
+ /// Determines if the `UnicodeScalar` represents a valid terminating character.
+ /// :return: `true` if the scalar is a valid terminator, `false` otherwise.
+ func isValidTerminator() -> Bool {
+ if self == Token.Comma { return true }
+ if self == Token.RightBracket { return true }
+ if self == Token.RightCurly { return true }
+ if self.isWhitespace() { return true }
+
+ return false
+ }
+
+ // /// Stores the `UInt8` bytes that make up the UTF8 code points for the scalar.
+ // ///
+ // /// :param: buffer the buffer to write the UTF8 code points into.
+ // func utf8(inout buffer: [UInt8]) {
+ // /*
+ // * This implementation should probably be replaced by the function below. However,
+ // * I am not quite sure how to properly use `SinkType` yet...
+ // *
+ // * UTF8.encode(input: UnicodeScalar, output: &S)
+ // */
+ //
+ // if value <= 0x007F {
+ // buffer.append(UInt8(value))
+ // }
+ // else if 0x0080 <= value && value <= 0x07FF {
+ // buffer.append(UInt8(value &/ 64) &+ 192)
+ // buffer.append(UInt8(value &% 64) &+ 128)
+ // }
+ // else if (0x0800 <= value && value <= 0xD7FF) || (0xE000 <= value && value <= 0xFFFF) {
+ // buffer.append(UInt8(value &/ 4096) &+ 224)
+ // buffer.append(UInt8((value &% 4096) &/ 64) &+ 128)
+ // buffer.append(UInt8(value &% 64 &+ 128))
+ // }
+ // else {
+ // buffer.append(UInt8(value &/ 262144) &+ 240)
+ // buffer.append(UInt8((value &% 262144) &/ 4096) &+ 128)
+ // buffer.append(UInt8((value &% 4096) &/ 64) &+ 128)
+ // buffer.append(UInt8(value &% 64) &+ 128)
+ // }
+ // }
}
/// The code unit value for all of the token characters used.
struct Token {
- fileprivate init() {}
-
- // Control Codes
- static let Null = UInt8(0)
- static let Backspace = UInt8(8)
- static let HorizontalTab = UInt8(9)
- static let Linefeed = UInt8(10)
- static let Formfeed = UInt8(12)
- static let CarriageReturn = UInt8(13)
- static let ShiftIn = UInt8(15)
-
- // Tokens for JSON
- static let LeftBracket = UInt8(91)
- static let RightBracket = UInt8(93)
- static let LeftCurly = UInt8(123)
- static let RightCurly = UInt8(125)
- static let Comma = UInt8(44)
- static let SingleQuote = UInt8(39)
- static let DoubleQuote = UInt8(34)
- static let Minus = UInt8(45)
- static let Plus = UInt8(43)
- static let Backslash = UInt8(92)
- static let Forwardslash = UInt8(47)
- static let Colon = UInt8(58)
- static let Period = UInt8(46)
-
- // Numbers
- static let Zero = UInt8(48)
- static let One = UInt8(49)
- static let Two = UInt8(50)
- static let Three = UInt8(51)
- static let Four = UInt8(52)
- static let Five = UInt8(53)
- static let Six = UInt8(54)
- static let Seven = UInt8(55)
- static let Eight = UInt8(56)
- static let Nine = UInt8(57)
-
- // Character tokens for JSON
- static let A = UInt8(65)
- static let E = UInt8(69)
- static let F = UInt8(70)
- static let a = UInt8(97)
- static let b = UInt8(98)
- static let e = UInt8(101)
- static let f = UInt8(102)
- static let l = UInt8(108)
- static let n = UInt8(110)
- static let r = UInt8(114)
- static let s = UInt8(115)
- static let t = UInt8(116)
- static let u = UInt8(117)
+ fileprivate init() {}
+
+ // Control Codes
+ static let Null = UInt8(0)
+ static let Backspace = UInt8(8)
+ static let HorizontalTab = UInt8(9)
+ static let Linefeed = UInt8(10)
+ static let Formfeed = UInt8(12)
+ static let CarriageReturn = UInt8(13)
+ static let ShiftIn = UInt8(15)
+
+ // Tokens for JSON
+ static let LeftBracket = UInt8(91)
+ static let RightBracket = UInt8(93)
+ static let LeftCurly = UInt8(123)
+ static let RightCurly = UInt8(125)
+ static let Comma = UInt8(44)
+ static let SingleQuote = UInt8(39)
+ static let DoubleQuote = UInt8(34)
+ static let Minus = UInt8(45)
+ static let Plus = UInt8(43)
+ static let Backslash = UInt8(92)
+ static let Forwardslash = UInt8(47)
+ static let Colon = UInt8(58)
+ static let Period = UInt8(46)
+
+ // Numbers
+ static let Zero = UInt8(48)
+ static let One = UInt8(49)
+ static let Two = UInt8(50)
+ static let Three = UInt8(51)
+ static let Four = UInt8(52)
+ static let Five = UInt8(53)
+ static let Six = UInt8(54)
+ static let Seven = UInt8(55)
+ static let Eight = UInt8(56)
+ static let Nine = UInt8(57)
+
+ // Character tokens for JSON
+ static let A = UInt8(65)
+ static let E = UInt8(69)
+ static let F = UInt8(70)
+ static let a = UInt8(97)
+ static let b = UInt8(98)
+ static let e = UInt8(101)
+ static let f = UInt8(102)
+ static let l = UInt8(108)
+ static let n = UInt8(110)
+ static let r = UInt8(114)
+ static let s = UInt8(115)
+ static let t = UInt8(116)
+ static let u = UInt8(117)
}
diff --git a/Sources/JSONLib/JSValue.swift b/Sources/JSONLib/JSValue.swift
index da7e802..e5081e0 100644
--- a/Sources/JSONLib/JSValue.swift
+++ b/Sources/JSONLib/JSValue.swift
@@ -7,233 +7,240 @@
public typealias JSON = JSValue
/// The error domain for all `JSValue` related errors.
-public let JSValueErrorDomain = "com.kiadstudios.json.error"
+public let JSValueErrorDomain = "com.kiadstudios.json.error"
/// A representative type for all possible JSON values.
///
/// See http://json.org for a full description.
-public enum JSValue : Equatable {
-
- /// The maximum integer that is safely representable in JavaScript.
- public static let MaximumSafeInt: Int64 = 9007199254740991
-
- /// The minimum integer that is safely representable in JavaScript.
- public static let MinimumSafeInt: Int64 = -9007199254740991
-
- /// Holds an array of JavaScript values that conform to valid `JSValue` types.
- case array([JSValue])
-
- /// Holds an unordered set of key/value pairs conforming to valid `JSValue` types.
- case object([String : JSValue])
-
- /// Holds the value conforming to JavaScript's String object.
- case string(String)
-
- /// Holds the value conforming to JavaScript's Number object.
- case number(Double)
-
- /// Holds the value conforming to JavaScript's Boolean wrapper.
- case bool(Bool)
-
- /// Holds the value that corresponds to `null`.
- case null
-
- /// Initializes a new `JSValue` with a `[JSValue]` value.
- public init(_ value: [JSValue]) {
- self = .array(value)
- }
-
- /// Initializes a new `JSValue` with a `[String:JSValue]` value.
- public init(_ value: [String:JSValue]) {
- self = .object(value)
- }
-
- /// Initializes a new `JSValue` with a `String` value.
- public init(_ value: String) {
- self = .string(value)
- }
-
- /// Initializes a new `JSValue` with a `Double` value.
- public init(_ value: Double) {
- self = .number(value)
- }
-
- /// Initializes a new `JSValue` with a `Bool` value.
- public init(_ value: Bool) {
- self = .bool(value)
- }
+public enum JSValue: Equatable {
+
+ /// The maximum integer that is safely representable in JavaScript.
+ public static let MaximumSafeInt: Int64 = 9_007_199_254_740_991
+
+ /// The minimum integer that is safely representable in JavaScript.
+ public static let MinimumSafeInt: Int64 = -9_007_199_254_740_991
+
+ /// Holds an array of JavaScript values that conform to valid `JSValue` types.
+ case array([JSValue])
+
+ /// Holds an unordered set of key/value pairs conforming to valid `JSValue` types.
+ case object([String: JSValue])
+
+ /// Holds the value conforming to JavaScript's String object.
+ case string(String)
+
+ /// Holds the value conforming to JavaScript's Number object.
+ case number(Double)
+
+ /// Holds the value conforming to JavaScript's Boolean wrapper.
+ case bool(Bool)
+
+ /// Holds the value that corresponds to `null`.
+ case null
+
+ /// Initializes a new `JSValue` with a `[JSValue]` value.
+ public init(_ value: [JSValue]) {
+ self = .array(value)
+ }
+
+ /// Initializes a new `JSValue` with a `[String:JSValue]` value.
+ public init(_ value: [String: JSValue]) {
+ self = .object(value)
+ }
+
+ /// Initializes a new `JSValue` with a `String` value.
+ public init(_ value: String) {
+ self = .string(value)
+ }
+
+ /// Initializes a new `JSValue` with a `Double` value.
+ public init(_ value: Double) {
+ self = .number(value)
+ }
+
+ /// Initializes a new `JSValue` with a `Bool` value.
+ public init(_ value: Bool) {
+ self = .bool(value)
+ }
}
// All of the stupid number-type initializers because of the lack of type conversion.
// Grr... convenience initializers not allowed in this context...
// Also... without the labels, Swift cannot seem to actually get the type inference correct (6.1b3)
extension JSValue {
- /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
- public init(int8 value: Int8) {
- self = .number(Double(value))
- }
-
- /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
- public init(in16 value: Int16) {
- self = .number(Double(value))
- }
-
- /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
- public init(int32 value: Int32) {
- self = .number(Double(value))
- }
-
- /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
- public init(int64 value: Int64) {
- self = .number(Double(value))
- }
-
- /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
- public init(uint8 value: UInt8) {
- self = .number(Double(value))
- }
-
- /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
- public init(uint16 value: UInt16) {
- self = .number(Double(value))
- }
-
- /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
- public init(uint32 value: UInt32) {
- self = .number(Double(value))
- }
-
- /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
- public init(uint64 value: UInt64) {
- self = .number(Double(value))
- }
-
- /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
- public init(int value: Int) {
- self = .number(Double(value))
- }
-
- /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
- public init(uint value: UInt) {
- self = .number(Double(value))
- }
-
- /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
- public init(float value: Float) {
- self = .number(Double(value))
- }
+ /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
+ public init(int8 value: Int8) {
+ self = .number(Double(value))
+ }
+
+ /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
+ public init(in16 value: Int16) {
+ self = .number(Double(value))
+ }
+
+ /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
+ public init(int32 value: Int32) {
+ self = .number(Double(value))
+ }
+
+ /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
+ public init(int64 value: Int64) {
+ self = .number(Double(value))
+ }
+
+ /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
+ public init(uint8 value: UInt8) {
+ self = .number(Double(value))
+ }
+
+ /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
+ public init(uint16 value: UInt16) {
+ self = .number(Double(value))
+ }
+
+ /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
+ public init(uint32 value: UInt32) {
+ self = .number(Double(value))
+ }
+
+ /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
+ public init(uint64 value: UInt64) {
+ self = .number(Double(value))
+ }
+
+ /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
+ public init(int value: Int) {
+ self = .number(Double(value))
+ }
+
+ /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
+ public init(uint value: UInt) {
+ self = .number(Double(value))
+ }
+
+ /// Convenience initializer for a `JSValue` with a non-standard `JSNumberType` value.
+ public init(float value: Float) {
+ self = .number(Double(value))
+ }
}
extension JSValue: CustomStringConvertible {
-
- /// Attempts to convert the `JSValue` into its string representation.
- ///
- /// - parameter indent: the indent string to use; defaults to " ". If `nil` is
- /// given, then the JSON is flattened.
- ///
- /// - returns: A `FailableOf` that will contain the `String` value if successful,
- /// otherwise, the `Error` information for the conversion.
- public func stringify(_ indent: String? = " ") -> String {
- return prettyPrint(indent, 0)
- }
-
- /// Attempts to convert the `JSValue` into its string representation.
- ///
- /// - parameter indent: the number of spaces to include.
- ///
- /// - returns: A `FailableOf` that will contain the `String` value if successful,
- /// otherwise, the `Error` information for the conversion.
- public func stringify(_ indent: Int) -> String {
- let padding = (0..` that will contain the `String` value if successful,
+ /// otherwise, the `Error` information for the conversion.
+ public func stringify(_ indent: String? = " ") -> String {
+ return prettyPrint(indent, 0)
+ }
+
+ /// Attempts to convert the `JSValue` into its string representation.
+ ///
+ /// - parameter indent: the number of spaces to include.
+ ///
+ /// - returns: A `FailableOf` that will contain the `String` value if successful,
+ /// otherwise, the `Error` information for the conversion.
+ public func stringify(_ indent: Int) -> String {
+ let padding = (0.. Bool {
- switch (lhs, rhs) {
- case (.null, .null):
- return true
-
- case let (.bool(lhsValue), .bool(rhsValue)):
- return lhsValue == rhsValue
-
- case let (.string(lhsValue), .string(rhsValue)):
- return lhsValue == rhsValue
-
- case let (.number(lhsValue), .number(rhsValue)):
- return lhsValue == rhsValue
-
- case let (.array(lhsValue), .array(rhsValue)):
- return lhsValue == rhsValue
-
- case let (.object(lhsValue), .object(rhsValue)):
- return lhsValue == rhsValue
-
- default:
- return false
- }
+public func == (lhs: JSValue, rhs: JSValue) -> Bool {
+ switch (lhs, rhs) {
+ case (.null, .null):
+ return true
+
+ case let (.bool(lhsValue), .bool(rhsValue)):
+ return lhsValue == rhsValue
+
+ case let (.string(lhsValue), .string(rhsValue)):
+ return lhsValue == rhsValue
+
+ case let (.number(lhsValue), .number(rhsValue)):
+ return lhsValue == rhsValue
+
+ case let (.array(lhsValue), .array(rhsValue)):
+ return lhsValue == rhsValue
+
+ case let (.object(lhsValue), .object(rhsValue)):
+ return lhsValue == rhsValue
+
+ default:
+ return false
+ }
}
extension JSValue {
- func prettyPrint(_ indent: String?, _ level: Int) -> String {
- func escape(_ value: String) -> String {
- return value
- .replacingOccurrences(of: "\"", with: "\\\"")
- .replacingOccurrences(of: "\t", with: "\\t")
- .replacingOccurrences(of: "\n", with: "\\n")
- .replacingOccurrences(of: "\r", with: "\\r")
- }
-
- var currentIndent = ""
- let nextIndentLevel = level + (indent == nil ? 0 : 1)
-
- for _ in (0.. String {
+ func escape(_ value: String) -> String {
+ return
+ value
+ .replacingOccurrences(of: "\"", with: "\\\"")
+ .replacingOccurrences(of: "\t", with: "\\t")
+ .replacingOccurrences(of: "\n", with: "\\n")
+ .replacingOccurrences(of: "\r", with: "\\r")
+ }
+
+ var currentIndent = ""
+ let nextIndentLevel = level + (indent == nil ? 0 : 1)
+
+ for _ in (0..
- var offset = 0
+ var firstRun = true
+ var usePrevious = false
+ var previousElement: UInt8? = nil
+ var buffer: UnsafeBufferPointer
+ var offset = 0
- /// Initializes a new `ReplayableGenerator` with an underlying `UnsafeBufferPointer`.
- /// - parameter sequence: the sequence that will be used to traverse the content.
- public init(_ buffer: UnsafeBufferPointer) {
- self.buffer = buffer
- }
+ /// Initializes a new `ReplayableGenerator` with an underlying `UnsafeBufferPointer`.
+ /// - parameter sequence: the sequence that will be used to traverse the content.
+ public init(_ buffer: UnsafeBufferPointer) {
+ self.buffer = buffer
+ }
- /// Moves the current element to the next element in the collection, if one exists.
- /// :return: The `current` element or `nil` if the element does not exist.
- public func next() -> Element? {
- if usePrevious {
- usePrevious = false
- }
- else {
- previousElement = offset >= buffer.count ? nil : buffer[offset]
- offset += 1
- }
+ /// Moves the current element to the next element in the collection, if one exists.
+ /// :return: The `current` element or `nil` if the element does not exist.
+ public func next() -> Element? {
+ if usePrevious {
+ usePrevious = false
+ }
+ else {
+ previousElement = offset >= buffer.count ? nil : buffer[offset]
+ offset += 1
+ }
- return previousElement
- }
+ return previousElement
+ }
- /// Ensures that the next call to `next()` will use the current value.
- public func replay() {
- usePrevious = true
- }
+ /// Ensures that the next call to `next()` will use the current value.
+ public func replay() {
+ usePrevious = true
+ }
- /// Creates a generator that can be used to traversed the content. Each call to
- /// `generate` will call `replay()`.
- ///
- /// :return: A iterable collection backing the content.
- public func makeIterator() -> ReplayableGenerator {
- if firstRun {
- firstRun = false
- offset = 0
- usePrevious = false
- previousElement = nil
- }
- else {
- self.replay()
- }
+ /// Creates a generator that can be used to traversed the content. Each call to
+ /// `generate` will call `replay()`.
+ ///
+ /// :return: A iterable collection backing the content.
+ public func makeIterator() -> ReplayableGenerator {
+ if firstRun {
+ firstRun = false
+ offset = 0
+ usePrevious = false
+ previousElement = nil
+ }
+ else {
+ self.replay()
+ }
- return self
- }
+ return self
+ }
- /// Determines if the generator is at the end of the collection's content.
- ///
- /// :return: `true` if there more content in the collection to view, `false` otherwise.
- public func atEnd() -> Bool {
- let element = next()
- replay()
+ /// Determines if the generator is at the end of the collection's content.
+ ///
+ /// :return: `true` if there more content in the collection to view, `false` otherwise.
+ public func atEnd() -> Bool {
+ let element = next()
+ replay()
- return element == nil
- }
+ return element == nil
+ }
}
diff --git a/Sources/ParserPerfTestHarness/main.swift b/Sources/ParserPerfTestHarness/main.swift
index afa9521..b723b2a 100644
--- a/Sources/ParserPerfTestHarness/main.swift
+++ b/Sources/ParserPerfTestHarness/main.swift
@@ -4,105 +4,120 @@
*/
#if os(macOS)
-import Foundation
-import JSONLib
-import QuartzCore
-
-// Must load locally.
-// import Freddy
-
-func printUsage() -> Never {
- print("usage: ParserPerfTestHarness [test file]")
- exit(-1)
-}
-
-struct Results: CustomStringConvertible {
- let minimum: T
- let maximum: T
- let average: T
-
- var description: String {
- return "min: \(minimum), max: \(maximum), avg: \(average)"
- }
-}
-
-func memory(iterations: Int = 10, fn: @escaping () throws -> Void) throws -> Results {
- var min: Int = Int.max
- var max: Int = Int.min
- var counter: Int = 0
-
- for _ in 0.. max { max = used }
- counter += used
- }
-
- return Results(minimum: min, maximum: max, average: Int(counter / iterations))
-}
-
-func performance(iterations: Int = 10, fn: () throws -> Void) throws -> Results {
- var min: CFTimeInterval = 999999999999
- var max: CFTimeInterval = 0
- var counter: CFTimeInterval = 0
-
- for _ in 0.. max { max = used }
- counter += used
- }
-
- return Results(minimum: min, maximum: max, average: counter / Double(iterations))
-}
-
-func diff(_ x: Int, _ y: Int) -> Int {
- return -100 * (x - y) / y
-}
-
-func diff(_ x: Double, _ y: Double) -> Double {
- return -100 * (x - y) / y
-}
-
-
-if CommandLine.arguments.count != 2 { printUsage() }
-let testFile = CommandLine.arguments[1]
-if FileManager.default.fileExists(atPath: testFile) == false {
- print("** file not found: \(testFile)")
- exit(-2)
-}
-
-guard let _contents = try? NSString(contentsOfFile: testFile, encoding: String.Encoding.utf8.rawValue) else {
- print("** unable to load the file at: \(testFile)")
- exit(-3)
-}
-
-let contents = _contents as String
-let data = contents.data(using: .utf8)!
-let bytes = [UInt8](data)
-
-let filename = testFile.components(separatedBy: "/").last!
-let shouldParse = filename.hasPrefix("y_")
-
-
-print("NSJONSerialization:")
-let nsjsonSerializationPerfResults = try performance { let _ = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) }
-print("performance results: \(nsjsonSerializationPerfResults)")
-
-print("\nJSONLib:")
-let jsonlibPerfResults = try performance { let _ = try JSON.parse(data) }
-print("performance results: \(jsonlibPerfResults)")
+ import Foundation
+ import JSONLib
+ import QuartzCore
+
+ // Must load locally.
+ // import Freddy
+
+ func printUsage() -> Never {
+ print("usage: ParserPerfTestHarness [test file]")
+ exit(-1)
+ }
+
+ struct Results: CustomStringConvertible {
+ let minimum: T
+ let maximum: T
+ let average: T
+
+ var description: String {
+ return "min: \(minimum), max: \(maximum), avg: \(average)"
+ }
+ }
+
+ func memory(iterations: Int = 10, fn: @escaping () throws -> Void) throws -> Results
+ {
+ var min: Int = Int.max
+ var max: Int = Int.min
+ var counter: Int = 0
+
+ for _ in 0.. max { max = used }
+ counter += used
+ }
+
+ return Results(minimum: min, maximum: max, average: Int(counter / iterations))
+ }
+
+ func performance(iterations: Int = 10, fn: () throws -> Void) throws -> Results<
+ CFTimeInterval
+ > {
+ var min: CFTimeInterval = 999_999_999_999
+ var max: CFTimeInterval = 0
+ var counter: CFTimeInterval = 0
+
+ for _ in 0.. max { max = used }
+ counter += used
+ }
+
+ return Results(
+ minimum: min,
+ maximum: max,
+ average: counter / Double(iterations)
+ )
+ }
+
+ func diff(_ x: Int, _ y: Int) -> Int {
+ return -100 * (x - y) / y
+ }
+
+ func diff(_ x: Double, _ y: Double) -> Double {
+ return -100 * (x - y) / y
+ }
+
+ if CommandLine.arguments.count != 2 { printUsage() }
+ let testFile = CommandLine.arguments[1]
+ if FileManager.default.fileExists(atPath: testFile) == false {
+ print("** file not found: \(testFile)")
+ exit(-2)
+ }
+
+ guard
+ let _contents = try? NSString(
+ contentsOfFile: testFile,
+ encoding: String.Encoding.utf8.rawValue
+ )
+ else {
+ print("** unable to load the file at: \(testFile)")
+ exit(-3)
+ }
+
+ let contents = _contents as String
+ let data = contents.data(using: .utf8)!
+ let bytes = [UInt8](data)
+
+ let filename = testFile.components(separatedBy: "/").last!
+ let shouldParse = filename.hasPrefix("y_")
+
+ print("NSJONSerialization:")
+ let nsjsonSerializationPerfResults = try performance {
+ let _ = try? JSONSerialization.jsonObject(
+ with: data,
+ options: JSONSerialization.ReadingOptions.allowFragments
+ )
+ }
+ print("performance results: \(nsjsonSerializationPerfResults)")
+
+ print("\nJSONLib:")
+ let jsonlibPerfResults = try performance { let _ = try JSON.parse(data) }
+ print("performance results: \(jsonlibPerfResults)")
// Must enable locally.
// print("\nFreddy Results:")
// let freddyPerfResults = try performance { let _ = try? JSON(data: data) }
// print("performance results: \(freddyPerfResults.description)")
#else
-print("Sadly, only macOS is supported for the perf infrastructure at this time.")
-#endif
\ No newline at end of file
+ print("Sadly, only macOS is supported for the perf infrastructure at this time.")
+#endif
diff --git a/Sources/ParserTestHarness/main.swift b/Sources/ParserTestHarness/main.swift
index 56c3379..abf2ef6 100644
--- a/Sources/ParserTestHarness/main.swift
+++ b/Sources/ParserTestHarness/main.swift
@@ -13,73 +13,73 @@ import JSONLib
// import Freddy
func printUsage() -> Never {
- print("usage: ParserTestHarness [-freddy] -file:[test file]")
- exit(-1)
+ print("usage: ParserTestHarness [-freddy] -file:[test file]")
+ exit(-1)
}
var useFreddy = false
var file: String? = nil
for arg in CommandLine.arguments {
- if arg == "-freddy" {
- useFreddy = true
- }
- else if arg.hasPrefix("-file") {
- file = arg.replacingOccurrences(of: "-file:", with: "")
- }
+ if arg == "-freddy" {
+ useFreddy = true
+ }
+ else if arg.hasPrefix("-file") {
+ file = arg.replacingOccurrences(of: "-file:", with: "")
+ }
}
if let testFile = file {
- if FileManager.default.fileExists(atPath: testFile) == false {
- print("** file not found: \(testFile)")
- exit(-2)
- }
+ if FileManager.default.fileExists(atPath: testFile) == false {
+ print("** file not found: \(testFile)")
+ exit(-2)
+ }
- let filename = testFile.components(separatedBy: "/").last!
- let shouldParse = filename.hasPrefix("y_")
+ let filename = testFile.components(separatedBy: "/").last!
+ let shouldParse = filename.hasPrefix("y_")
- guard let data = try? Data(contentsOf: URL(fileURLWithPath: testFile)) else {
- if shouldParse {
- print("** unable to load the file at: \(testFile)")
- exit(-3)
- }
- else {
- print("expected failing parsing file: \(testFile)")
- exit(0)
- }
- }
+ guard let data = try? Data(contentsOf: URL(fileURLWithPath: testFile)) else {
+ if shouldParse {
+ print("** unable to load the file at: \(testFile)")
+ exit(-3)
+ }
+ else {
+ print("expected failing parsing file: \(testFile)")
+ exit(0)
+ }
+ }
- do {
- if useFreddy {
- // Must do this locally
- // let _ = try JSON(data: data)
- print("Must load Freddy locally")
- exit(-101)
- }
- else {
- let _ = try JSON.parse(data)
- }
- if shouldParse {
- print("success parsing file: \(testFile)")
- }
- else {
- print("** expected error while parsing file: \(testFile)")
- exit(-5)
- }
- }
- catch {
- if shouldParse {
- print("** error parsing file: \(testFile)")
- print("--- ERROR INFO ---")
- print("\(error)")
- print("------------------")
- exit(-4)
- }
- else {
- print("expected failing parsing file: \(testFile)")
- }
- }
+ do {
+ if useFreddy {
+ // Must do this locally
+ // let _ = try JSON(data: data)
+ print("Must load Freddy locally")
+ exit(-101)
+ }
+ else {
+ let _ = try JSON.parse(data)
+ }
+ if shouldParse {
+ print("success parsing file: \(testFile)")
+ }
+ else {
+ print("** expected error while parsing file: \(testFile)")
+ exit(-5)
+ }
+ }
+ catch {
+ if shouldParse {
+ print("** error parsing file: \(testFile)")
+ print("--- ERROR INFO ---")
+ print("\(error)")
+ print("------------------")
+ exit(-4)
+ }
+ else {
+ print("expected failing parsing file: \(testFile)")
+ }
+ }
}
else {
- printUsage()
+ printUsage()
}
diff --git a/Tests/JSONLibTests/JSValueEquatableTests.swift b/Tests/JSONLibTests/JSValueEquatableTests.swift
index 801ebec..7779087 100644
--- a/Tests/JSONLibTests/JSValueEquatableTests.swift
+++ b/Tests/JSONLibTests/JSValueEquatableTests.swift
@@ -4,106 +4,107 @@
* ------------------------------------------------------------------------------------------ */
import XCTest
+
@testable import JSONLib
class JSValueEquatableTests: XCTestCase {
- func testEquatableNullTrue() {
- let lhs: JSON = nil
- let rhs: JSON = nil
-
- XCTAssertEqual(lhs, rhs)
- }
-
- func testEquatableBoolTrue() {
- let lhs: JSValue = true
- let rhs: JSValue = true
-
- XCTAssertEqual(lhs, rhs)
- }
-
- func testEquatableBoolFalse() {
- let lhs: JSValue = true
- let rhs: JSValue = false
-
- XCTAssertNotEqual(lhs, rhs)
- }
-
- func testEquatableStringTrue() {
- let lhs: JSValue = "hello"
- let rhs: JSValue = "hello"
-
- XCTAssertEqual(lhs, rhs)
- }
-
- func testEquatableStringFalse() {
- let lhs: JSValue = "hello"
- let rhs: JSValue = "bob"
-
- XCTAssertNotEqual(lhs, rhs)
- }
-
- func testEquatableNumberTrue() {
- let lhs: JSValue = 1234
- let rhs: JSValue = 1234
-
- XCTAssertEqual(lhs, rhs)
- }
-
- func testEquatableNumberFalse() {
- let lhs: JSValue = 1234
- let rhs: JSValue = 4321
-
- XCTAssertNotEqual(lhs, rhs)
- }
-
- func testEquatableArrayTrue() {
- let lhs: JSValue = [1, 3, 5]
- let rhs: JSValue = [1, 3, 5]
-
- XCTAssertEqual(lhs, rhs)
- }
-
- func testEquatableArrayFalse() {
- let lhs: JSValue = [1, 3, 5]
- let rhs: JSValue = [1, 3, 7]
-
- XCTAssertNotEqual(lhs, rhs)
- }
-
- func testEquatableObjectTrue() {
- let lhs: JSValue = ["key1": 1, "key2": 3, "key3": 5]
- let rhs: JSValue = ["key1": 1, "key2": 3, "key3": 5]
-
- XCTAssertEqual(lhs, rhs)
- }
-
- func testEquatableObjectFalse() {
- let lhs: JSValue = ["key1": 1, "key2": 3, "key3": 5]
- let rhs: JSValue = ["key0": 1, "key1": 3, "key2": 5]
-
- XCTAssertNotEqual(lhs, rhs)
- }
-
- func testEquatableTypeMismatch() {
- let lhs: JSValue = ["key1": 1, "key2": 3, "key3": 5]
- let rhs: JSValue = [1, 3, 5]
-
- XCTAssertNotEqual(lhs, rhs)
- }
-
- static let allTests = [
- ("testEquatableNullTrue", testEquatableNullTrue),
- ("testEquatableBoolTrue", testEquatableBoolTrue),
- ("testEquatableBoolFalse", testEquatableBoolFalse),
- ("testEquatableStringTrue", testEquatableStringTrue),
- ("testEquatableStringFalse", testEquatableStringFalse),
- ("testEquatableNumberTrue", testEquatableNumberTrue),
- ("testEquatableNumberFalse", testEquatableNumberFalse),
- ("testEquatableArrayTrue", testEquatableArrayTrue),
- ("testEquatableArrayFalse", testEquatableArrayFalse),
- ("testEquatableObjectTrue", testEquatableObjectTrue),
- ("testEquatableObjectFalse", testEquatableObjectFalse),
- ("testEquatableTypeMismatch", testEquatableTypeMismatch),
- ]
+ func testEquatableNullTrue() {
+ let lhs: JSON = nil
+ let rhs: JSON = nil
+
+ XCTAssertEqual(lhs, rhs)
+ }
+
+ func testEquatableBoolTrue() {
+ let lhs: JSValue = true
+ let rhs: JSValue = true
+
+ XCTAssertEqual(lhs, rhs)
+ }
+
+ func testEquatableBoolFalse() {
+ let lhs: JSValue = true
+ let rhs: JSValue = false
+
+ XCTAssertNotEqual(lhs, rhs)
+ }
+
+ func testEquatableStringTrue() {
+ let lhs: JSValue = "hello"
+ let rhs: JSValue = "hello"
+
+ XCTAssertEqual(lhs, rhs)
+ }
+
+ func testEquatableStringFalse() {
+ let lhs: JSValue = "hello"
+ let rhs: JSValue = "bob"
+
+ XCTAssertNotEqual(lhs, rhs)
+ }
+
+ func testEquatableNumberTrue() {
+ let lhs: JSValue = 1234
+ let rhs: JSValue = 1234
+
+ XCTAssertEqual(lhs, rhs)
+ }
+
+ func testEquatableNumberFalse() {
+ let lhs: JSValue = 1234
+ let rhs: JSValue = 4321
+
+ XCTAssertNotEqual(lhs, rhs)
+ }
+
+ func testEquatableArrayTrue() {
+ let lhs: JSValue = [1, 3, 5]
+ let rhs: JSValue = [1, 3, 5]
+
+ XCTAssertEqual(lhs, rhs)
+ }
+
+ func testEquatableArrayFalse() {
+ let lhs: JSValue = [1, 3, 5]
+ let rhs: JSValue = [1, 3, 7]
+
+ XCTAssertNotEqual(lhs, rhs)
+ }
+
+ func testEquatableObjectTrue() {
+ let lhs: JSValue = ["key1": 1, "key2": 3, "key3": 5]
+ let rhs: JSValue = ["key1": 1, "key2": 3, "key3": 5]
+
+ XCTAssertEqual(lhs, rhs)
+ }
+
+ func testEquatableObjectFalse() {
+ let lhs: JSValue = ["key1": 1, "key2": 3, "key3": 5]
+ let rhs: JSValue = ["key0": 1, "key1": 3, "key2": 5]
+
+ XCTAssertNotEqual(lhs, rhs)
+ }
+
+ func testEquatableTypeMismatch() {
+ let lhs: JSValue = ["key1": 1, "key2": 3, "key3": 5]
+ let rhs: JSValue = [1, 3, 5]
+
+ XCTAssertNotEqual(lhs, rhs)
+ }
+
+ static let allTests = [
+ ("testEquatableNullTrue", testEquatableNullTrue),
+ ("testEquatableBoolTrue", testEquatableBoolTrue),
+ ("testEquatableBoolFalse", testEquatableBoolFalse),
+ ("testEquatableStringTrue", testEquatableStringTrue),
+ ("testEquatableStringFalse", testEquatableStringFalse),
+ ("testEquatableNumberTrue", testEquatableNumberTrue),
+ ("testEquatableNumberFalse", testEquatableNumberFalse),
+ ("testEquatableArrayTrue", testEquatableArrayTrue),
+ ("testEquatableArrayFalse", testEquatableArrayFalse),
+ ("testEquatableObjectTrue", testEquatableObjectTrue),
+ ("testEquatableObjectFalse", testEquatableObjectFalse),
+ ("testEquatableTypeMismatch", testEquatableTypeMismatch),
+ ]
}
diff --git a/Tests/JSONLibTests/JSValueTests.Indexers.swift b/Tests/JSONLibTests/JSValueTests.Indexers.swift
index 9d55ec0..5eefdc7 100644
--- a/Tests/JSONLibTests/JSValueTests.Indexers.swift
+++ b/Tests/JSONLibTests/JSValueTests.Indexers.swift
@@ -3,77 +3,78 @@
* Licensed under the MIT License. See License in the project root for license information.
* ------------------------------------------------------------------------------------------ */
-import XCTest
import JSONLib
+import XCTest
+
+class JSValueIndexersTests: XCTestCase {
+
+ func testBasicArrayMutability() {
+ var value: JSON = [1, "Dog", 3.412, true]
+ XCTAssertTrue(value[0].number == 1)
+ XCTAssertTrue(value[1].string == "Dog")
+ XCTAssertTrue(value[2].number == 3.412)
+ XCTAssertTrue(value[3].bool == true)
-class JSValueIndexersTests : XCTestCase {
+ let newValue: JSValue = "Cat"
+ value[1] = newValue
+ XCTAssertTrue(value[0].number == 1)
+ XCTAssertTrue(value[1].string == "Cat")
+ XCTAssertTrue(value[2].number == 3.412)
+ XCTAssertTrue(value[3].bool == true)
- func testBasicArrayMutability() {
- var value: JSON = [1, "Dog", 3.412, true]
- XCTAssertTrue(value[0].number == 1)
- XCTAssertTrue(value[1].string == "Dog")
- XCTAssertTrue(value[2].number == 3.412)
- XCTAssertTrue(value[3].bool == true)
-
- let newValue: JSValue = "Cat"
- value[1] = newValue
- XCTAssertTrue(value[0].number == 1)
- XCTAssertTrue(value[1].string == "Cat")
- XCTAssertTrue(value[2].number == 3.412)
- XCTAssertTrue(value[3].bool == true)
+ value[1] = "Mouse"
+ XCTAssertTrue(value[0].number == 1)
+ XCTAssertTrue(value[1].string == "Mouse")
+ XCTAssertTrue(value[2].number == 3.412)
+ XCTAssertTrue(value[3].bool == true)
+ }
- value[1] = "Mouse"
- XCTAssertTrue(value[0].number == 1)
- XCTAssertTrue(value[1].string == "Mouse")
- XCTAssertTrue(value[2].number == 3.412)
- XCTAssertTrue(value[3].bool == true)
- }
-
- func testNestedArray() {
- var value: JSON = [1, "Dog", [3.412, true]]
- XCTAssertTrue(value[0].number == 1)
- XCTAssertTrue(value[1].string == "Dog")
+ func testNestedArray() {
+ var value: JSON = [1, "Dog", [3.412, true]]
+ XCTAssertTrue(value[0].number == 1)
+ XCTAssertTrue(value[1].string == "Dog")
- value[2][0] = 1.123
- XCTAssertTrue(value[2][0].number == 1.123)
- XCTAssertTrue(value[2][1].bool == true)
- }
-
- func testNestedMixedTypes() {
- var json: JSON = [
- "stat": "ok",
- "blogs": [
- "blog": [
- [
- "id": 73,
- "name": "Bloxus test",
- "needspassword": true,
- "url": "http://remote.bloxus.com/"
- ],
- [
- "id": 74,
- "name": "Manila Test",
- "needspassword": false,
- "url": "http://flickrtest1.userland.com/"
+ value[2][0] = 1.123
+ XCTAssertTrue(value[2][0].number == 1.123)
+ XCTAssertTrue(value[2][1].bool == true)
+ }
+
+ func testNestedMixedTypes() {
+ var json: JSON = [
+ "stat": "ok",
+ "blogs": [
+ "blog": [
+ [
+ "id": 73,
+ "name": "Bloxus test",
+ "needspassword": true,
+ "url": "http://remote.bloxus.com/",
+ ],
+ [
+ "id": 74,
+ "name": "Manila Test",
+ "needspassword": false,
+ "url":
+ "http://flickrtest1.userland.com/",
+ ],
+ ]
+ ],
]
- ]
- ]
- ]
-
- XCTAssertTrue(json["stat"].string == "ok")
- XCTAssertTrue(json["blogs"]["blog"][0]["id"].number == 73)
- XCTAssertTrue(json["blogs"]["blog"][0]["needspassword"].bool == true)
-
- json["blogs"]["blog"][0]["needspassword"] = false
- XCTAssertTrue(json["stat"].string == "ok")
- XCTAssertTrue(json["blogs"]["blog"][0]["id"].number == 73)
- XCTAssertTrue(json["blogs"]["blog"][0]["needspassword"].bool == false)
- }
- static let allTests = [
- ("testBasicArrayMutability", testBasicArrayMutability),
- ("testNestedArray", testNestedArray),
- ("testNestedMixedTypes", testNestedMixedTypes),
- ]
+ XCTAssertTrue(json["stat"].string == "ok")
+ XCTAssertTrue(json["blogs"]["blog"][0]["id"].number == 73)
+ XCTAssertTrue(json["blogs"]["blog"][0]["needspassword"].bool == true)
+
+ json["blogs"]["blog"][0]["needspassword"] = false
+ XCTAssertTrue(json["stat"].string == "ok")
+ XCTAssertTrue(json["blogs"]["blog"][0]["id"].number == 73)
+ XCTAssertTrue(json["blogs"]["blog"][0]["needspassword"].bool == false)
+ }
+
+ static let allTests = [
+ ("testBasicArrayMutability", testBasicArrayMutability),
+ ("testNestedArray", testNestedArray),
+ ("testNestedMixedTypes", testNestedMixedTypes),
+ ]
}
diff --git a/Tests/JSONLibTests/JSValueTests.Literals.swift b/Tests/JSONLibTests/JSValueTests.Literals.swift
index e7ee481..678396f 100644
--- a/Tests/JSONLibTests/JSValueTests.Literals.swift
+++ b/Tests/JSONLibTests/JSValueTests.Literals.swift
@@ -3,109 +3,112 @@
* Licensed under the MIT License. See License in the project root for license information.
* ------------------------------------------------------------------------------------------ */
-import XCTest
import JSONLib
+import XCTest
+
+class JSValueLiteralsTests: XCTestCase {
+
+ func testStringValue() {
+ let value: JSValue = "hello world"
+ XCTAssertTrue(
+ value.string?.compare("hello world") == ComparisonResult.orderedSame
+ )
+ }
+
+ func testCompareStringToNonStringValue() {
+ let value: JSValue = 1234
+ XCTAssertFalse(value.string?.compare("1234") == ComparisonResult.orderedSame)
+ }
+
+ func testIntegerValue() {
+ let value: JSValue = 123
+ XCTAssertTrue(value.number == 123)
+ }
+
+ func testDoubleValue() {
+ let value: JSValue = 3.1245123123
+ XCTAssertTrue(value.number == 3.1245123123)
+ }
+
+ func testBoolTrueValue() {
+ let value: JSValue = true
+ XCTAssertTrue(value.bool == true)
+ }
-class JSValueLiteralsTests : XCTestCase {
-
- func testStringValue() {
- let value: JSValue = "hello world"
- XCTAssertTrue(value.string?.compare("hello world") == ComparisonResult.orderedSame)
- }
-
- func testCompareStringToNonStringValue() {
- let value: JSValue = 1234
- XCTAssertFalse(value.string?.compare("1234") == ComparisonResult.orderedSame)
- }
-
- func testIntegerValue() {
- let value: JSValue = 123
- XCTAssertTrue(value.number == 123)
- }
-
- func testDoubleValue() {
- let value: JSValue = 3.1245123123
- XCTAssertTrue(value.number == 3.1245123123)
- }
-
- func testBoolTrueValue() {
- let value: JSValue = true
- XCTAssertTrue(value.bool == true)
- }
-
- func testBoolFalseValue() {
- let value: JSValue = false
- XCTAssertTrue(value.bool == false)
- }
-
- func testNilValue() {
- let value: JSValue = nil
- XCTAssertTrue(value.null)
- }
-
- func testBasicArray() {
- let value: JSON = [1, "Dog", 3.412, true]
- XCTAssertTrue(value[0].number == 1)
- XCTAssertTrue(value[1].string == "Dog")
- XCTAssertTrue(value[2].number == 3.412)
- XCTAssertTrue(value[3].bool == true)
- }
-
- func testNestedArray() {
- let value: JSON = [1, "Dog", [3.412, true]]
- XCTAssertTrue(value[0].number == 1)
- XCTAssertTrue(value[1].string == "Dog")
-
- // Usage #1
- if let array = value[2].array {
- XCTAssertTrue(array[0].number == 3.412)
- XCTAssertTrue(array[1].bool == true)
- }
- else {
- XCTFail()
- }
-
- // Usage #2
- XCTAssertTrue(value[2][0].number == 3.412)
- XCTAssertTrue(value[2][1].bool == true)
- }
-
- func testFlickrResult() {
- var json: JSON = [
- "stat": "ok",
- "blogs": [
- "blog": [
- [
- "id": 73,
- "name": "Bloxus test",
- "needspassword": true,
- "url": "http://remote.bloxus.com/"
- ],
- [
- "id": 74,
- "name": "Manila Test",
- "needspassword": false,
- "url": "http://flickrtest1.userland.com/"
+ func testBoolFalseValue() {
+ let value: JSValue = false
+ XCTAssertTrue(value.bool == false)
+ }
+
+ func testNilValue() {
+ let value: JSValue = nil
+ XCTAssertTrue(value.null)
+ }
+
+ func testBasicArray() {
+ let value: JSON = [1, "Dog", 3.412, true]
+ XCTAssertTrue(value[0].number == 1)
+ XCTAssertTrue(value[1].string == "Dog")
+ XCTAssertTrue(value[2].number == 3.412)
+ XCTAssertTrue(value[3].bool == true)
+ }
+
+ func testNestedArray() {
+ let value: JSON = [1, "Dog", [3.412, true]]
+ XCTAssertTrue(value[0].number == 1)
+ XCTAssertTrue(value[1].string == "Dog")
+
+ // Usage #1
+ if let array = value[2].array {
+ XCTAssertTrue(array[0].number == 3.412)
+ XCTAssertTrue(array[1].bool == true)
+ }
+ else {
+ XCTFail()
+ }
+
+ // Usage #2
+ XCTAssertTrue(value[2][0].number == 3.412)
+ XCTAssertTrue(value[2][1].bool == true)
+ }
+
+ func testFlickrResult() {
+ var json: JSON = [
+ "stat": "ok",
+ "blogs": [
+ "blog": [
+ [
+ "id": 73,
+ "name": "Bloxus test",
+ "needspassword": true,
+ "url": "http://remote.bloxus.com/",
+ ],
+ [
+ "id": 74,
+ "name": "Manila Test",
+ "needspassword": false,
+ "url":
+ "http://flickrtest1.userland.com/",
+ ],
+ ]
+ ],
]
- ]
- ]
- ]
-
- XCTAssertTrue(json["stat"].string == "ok")
- XCTAssertTrue(json["blogs"]["blog"][0]["id"].number == 73)
- XCTAssertTrue(json["blogs"]["blog"][0]["needspassword"].bool == true)
- }
-
- static let allTests = [
- ("testStringValue", testStringValue),
- ("testCompareStringToNonStringValue", testCompareStringToNonStringValue),
- ("testIntegerValue", testIntegerValue),
- ("testDoubleValue", testDoubleValue),
- ("testBoolTrueValue", testBoolTrueValue),
- ("testBoolFalseValue", testBoolFalseValue),
- ("testNilValue", testNilValue),
- ("testBasicArray", testBasicArray),
- ("testNestedArray", testNestedArray),
- ("testFlickrResult", testFlickrResult),
- ]
+
+ XCTAssertTrue(json["stat"].string == "ok")
+ XCTAssertTrue(json["blogs"]["blog"][0]["id"].number == 73)
+ XCTAssertTrue(json["blogs"]["blog"][0]["needspassword"].bool == true)
+ }
+
+ static let allTests = [
+ ("testStringValue", testStringValue),
+ ("testCompareStringToNonStringValue", testCompareStringToNonStringValue),
+ ("testIntegerValue", testIntegerValue),
+ ("testDoubleValue", testDoubleValue),
+ ("testBoolTrueValue", testBoolTrueValue),
+ ("testBoolFalseValue", testBoolFalseValue),
+ ("testNilValue", testNilValue),
+ ("testBasicArray", testBasicArray),
+ ("testNestedArray", testNestedArray),
+ ("testFlickrResult", testFlickrResult),
+ ]
}
diff --git a/Tests/JSONLibTests/JSValueTests.Parsing.swift b/Tests/JSONLibTests/JSValueTests.Parsing.swift
index cac00b2..f8d73e0 100644
--- a/Tests/JSONLibTests/JSValueTests.Parsing.swift
+++ b/Tests/JSONLibTests/JSValueTests.Parsing.swift
@@ -3,711 +3,784 @@
* Licensed under the MIT License. See License in the project root for license information.
* ------------------------------------------------------------------------------------------ */
-import XCTest
import JSONLib
+import XCTest
+
+class JSValueParsingTests: XCTestCase {
+
+ override func setUp() {
+ self.continueAfterFailure = false
+ }
+
+ func testParseNull() {
+ let string = "null"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertTrue(jsvalue.null == true)
+ }
+
+ func testParseNullWithWhitespace() {
+ let string = " \r\n\n\r\t\t\t\tnull\t\t\t\t\t\t\t \n\r\r\n\n\n"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertTrue(jsvalue.null == true)
+ }
+
+ func testParseNullInvalidJSON() {
+ let string = "null,"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue == nil)
+ // TODO: Validate the error information
+ }
+
+ func testParseTrue() {
+ let string = "true"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertTrue(jsvalue.bool == true)
+ }
+
+ func testParseTrueWithWhitespace() {
+ let string = " \r\n\n\r\t\t\t\ttrue\t\t\t\t\t\t\t \n\r\r\n\n\n"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertTrue(jsvalue.bool == true)
+ }
+
+ func testParseTrueInvalidJSON() {
+ let string = "true#"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue == nil)
+ // TODO: Validate the error information
+ }
+
+ func testParseFalse() {
+ let string = "false"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertTrue(jsvalue.bool == false)
+ }
+
+ func testParseFalseWithWhitespace() {
+ let string = " \r\n\n\r\t\t\t\tfalse\t\t\t\t\t\t\t \n\r\r\n\n\n"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertTrue(jsvalue.bool == false)
+ }
+
+ func testParseFalseInvalidJSON() {
+ let string = "false-"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue == nil)
+ // TODO: Validate the error information
+ }
+
+ func testParseStringWithDoubleQuote() {
+ let string = "\"Bob\""
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertEqual(jsvalue.string, "Bob")
+ }
+
+ func testParseStringWithSingleQuote() {
+ let string = "'Bob'"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseStringWithEscapedQuote() {
+ let string = "'Bob \"the man \" Roberts'"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue == nil)
+ }
-class JSValueParsingTests : XCTestCase {
-
- override func setUp() {
- self.continueAfterFailure = false
- }
-
- func testParseNull() {
- let string = "null"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertTrue(jsvalue.null == true)
- }
-
- func testParseNullWithWhitespace() {
- let string = " \r\n\n\r\t\t\t\tnull\t\t\t\t\t\t\t \n\r\r\n\n\n"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertTrue(jsvalue.null == true)
- }
-
- func testParseNullInvalidJSON() {
- let string = "null,"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue == nil)
- // TODO: Validate the error information
- }
-
- func testParseTrue() {
- let string = "true"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertTrue(jsvalue.bool == true)
- }
-
- func testParseTrueWithWhitespace() {
- let string = " \r\n\n\r\t\t\t\ttrue\t\t\t\t\t\t\t \n\r\r\n\n\n"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertTrue(jsvalue.bool == true)
- }
-
- func testParseTrueInvalidJSON() {
- let string = "true#"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue == nil)
- // TODO: Validate the error information
- }
-
- func testParseFalse() {
- let string = "false"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertTrue(jsvalue.bool == false)
- }
-
- func testParseFalseWithWhitespace() {
- let string = " \r\n\n\r\t\t\t\tfalse\t\t\t\t\t\t\t \n\r\r\n\n\n"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertTrue(jsvalue.bool == false)
- }
-
- func testParseFalseInvalidJSON() {
- let string = "false-"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue == nil)
- // TODO: Validate the error information
- }
-
- func testParseStringWithDoubleQuote() {
- let string = "\"Bob\""
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertEqual(jsvalue.string, "Bob")
- }
-
- func testParseStringWithSingleQuote() {
- let string = "'Bob'"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseStringWithEscapedQuote() {
- let string = "'Bob \"the man \" Roberts'"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseStringWithEscapedQuoteMatchingEndQuotes() {
- let string = "\"Bob \\\"the man\\\" Roberts\""
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertEqual(jsvalue.string, "Bob \"the man\" Roberts")
- }
-
- func testParseStringWithMultipleEscapes() {
- let string = "\"e&\\\\첊xz坍崦ݻ鍴\\\"嵥B3\\u000b㢊\\u0015L臯.샥\""
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertEqual(jsvalue.string, "e&\\첊xz坍崦ݻ鍴\"嵥B3\u{000b}㢊\u{0015}L臯.샥")
- }
-
- func testParseStringWithMultipleUnicodeTypes() {
- let string = "\"(\u{20da}g8큽튣>^Y{뤋.袊䂓;_g]S\\u202a꽬L;^'#땏bႌ?C緡<䝲䲝断ꏏ6\\u001asD7IK5Wxo8\\u0006p弊⼂ꯍ扵\\u0003`뵂픋%ꄰ⫙됶l囏尛+䗅E쟇\\\\\""
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertEqual(jsvalue.string, "(\u{20da}g8큽튣>^Y{뤋.袊䂓;_g]S\u{202a}꽬L;^'#땏bႌ?C緡<䝲䲝断ꏏ6\u{001a}sD7IK5Wxo8\u{0006}p弊⼂ꯍ扵\u{0003}`뵂픋%ꄰ⫙됶l囏尛+䗅E쟇\\")
- }
-
- func testParseStringWithTrailingEscapedQuotes() {
- let string = "\"\\\"䬰ỐwD捾V`邀⠕VD㺝sH6[칑.:醥葹*뻵倻aD\\\"\""
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertEqual(jsvalue.string, "\"䬰ỐwD捾V`邀⠕VD㺝sH6[칑.:醥葹*뻵倻aD\"")
- }
-
- func testParseInteger() {
- let string = "101"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertTrue(jsvalue.number == 101)
- }
-
- func testParseNegativeInteger() {
- let string = "-109234"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertTrue(jsvalue.number == -109234)
- }
-
- func testParseDouble() {
- let string = "12.345678"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertEqualWithAccuracy(jsvalue!.number!, 12.345678, accuracy: 0.01)
- }
-
- func testParseNegativeDouble() {
- let string = "-123.949"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertEqualWithAccuracy(jsvalue!.number!, -123.949, accuracy: 0.01)
- }
-
- func testParseExponent() {
- let string = "12.345e2"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertEqualWithAccuracy(jsvalue!.number!, 12.345e2, accuracy: 0.01)
- }
-
- func testParsePositiveExponent() {
- let string = "12.345e+2"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertEqualWithAccuracy(jsvalue!.number!, 12.345e+2, accuracy: 0.01)
- }
-
- func testParseNegativeExponent() {
- let string = "-123.9492e-5"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertEqualWithAccuracy(jsvalue!.number!, -123.9492e-5, accuracy: 0.01)
- }
-
- func testParseEmptyArray() {
- let string = "[]"
- let json = try? JSON.parse(string)
-
- XCTAssertTrue(json != nil)
- if let json = json {
- XCTAssertTrue(json.array != nil)
- }
- }
-
- func testSingleElementArray() {
- let string = "[101]"
- let json = try? JSON.parse(string)
-
- XCTAssertTrue(json != nil)
- if let json = json {
- XCTAssertTrue(json.array != nil)
- XCTAssertTrue(json.array?.count == 1)
- XCTAssertTrue(json[0] == 101)
- }
- }
-
- func testMultipleElementArray() {
- let string = "[101, 202, 303]"
- let json = try? JSON.parse(string)
-
- XCTAssertTrue(json != nil)
- if let json = json {
- XCTAssertTrue(json.array != nil)
- XCTAssertTrue(json.array?.count == 3)
- XCTAssertTrue(json[0] == 101)
- XCTAssertTrue(json[1] == 202)
- XCTAssertTrue(json[2] == 303)
- }
- }
-
- func testParseEmptyDictionary() {
- let string = "{}"
- let json = try? JSON.parse(string)
-
- XCTAssertTrue(json != nil)
- if let json = json {
- XCTAssertTrue(json.object != nil)
- }
- }
-
- func testParseEmptyDictionaryWithExtraWhitespace() {
- let string = " {\r\n\n\n\n \t \t} \t \t"
- let json = try? JSON.parse(string)
-
- XCTAssertTrue(json != nil)
- if let json = json {
- XCTAssertTrue(json.object != nil)
- }
- }
-
- func testParseDictionaryWithSingleKeyValuePair() {
- let string = "{ \"key\": 101 }"
- let json = try? JSON.parse(string)
-
- XCTAssertTrue(json != nil)
- if let json = json {
- XCTAssertTrue(json.object != nil)
- XCTAssertEqual(json["key"].number!, 101)
- }
- }
-
- func testParseDictionaryWithMultipleKeyValuePairs() {
- let string = "{ \"key1\": 101, \"key2\" : 202,\"key3\":303}"
- let json = try? JSON.parse(string)
-
- XCTAssertTrue(json != nil)
- if let json = json {
- XCTAssertTrue(json.object != nil)
- XCTAssertEqual(json["key1"].number!, 101)
- XCTAssertEqual(json["key2"].number!, 202)
- XCTAssertEqual(json["key3"].number!, 303)
- }
- }
-
- func testParseMixedArray() {
- let string = "[1, -12, \"Bob\", true, false, null, -2.11234123]"
- let json = try? JSON.parse(string)
-
- XCTAssertTrue(json != nil)
- if let json = json {
- XCTAssertTrue(json.array != nil)
- XCTAssertEqual(json.array!.count, 7)
- XCTAssertEqual(json[0].number!, 1)
- XCTAssertEqual(json[1].number!, -12)
- XCTAssertEqual(json[2].string!, "Bob")
- XCTAssertEqual(json[3].bool!, true)
- XCTAssertEqual(json[4].bool!, false)
- XCTAssertEqual(json[5].null, true)
- XCTAssertEqualWithAccuracy(json[6].number!, -2.11234123, accuracy: 0.01)
- }
- }
-
- func testParseMixedDictionary() {
- let string = "{\"key1\": 1, \"key2\": -12, \"key3\": \"Bob\", \"key4\": true, \"key5\": false, \"key6\": null, \"key7\": -2.11234123}"
- let json = try? JSON.parse(string)
-
- XCTAssertTrue(json != nil)
- if let json = json {
- XCTAssertTrue(json.object != nil)
- XCTAssertEqual(json.object!.count, 7)
- XCTAssertEqual(json["key1"].number!, 1)
- XCTAssertEqual(json["key2"].number!, -12)
- XCTAssertEqual(json["key3"].string!, "Bob")
- XCTAssertEqual(json["key4"].bool!, true)
- XCTAssertEqual(json["key5"].bool!, false)
- XCTAssertEqual(json["key6"].null, true)
- XCTAssertEqualWithAccuracy(json["key7"].number!, -2.11234123, accuracy: 0.01) }
- }
-
- func testParseNestedMixedTypes() {
- let string = "{\"key1\": 1, \"key2\": [ -12 , 12 ], \"key3\": \"Bob\", \"\\n鱿aK㝡␒㼙2촹f\": { \"foo\": \"bar\" }, \"key5\": false, \"key6\": null, \"key\\\"7\": -2.11234123}"
- let json = try? JSON.parse(string)
-
- XCTAssertTrue(json != nil)
- if let json = json {
- XCTAssertTrue(json.object != nil)
- XCTAssertEqual(json.object!.count, 7)
- XCTAssertEqual(json["key1"].number!, 1)
- XCTAssertTrue(json["key2"].array != nil)
- XCTAssertEqual(json["key2"].array!.count, 2)
- XCTAssertEqual(json["key2"][0].number!, -12)
- XCTAssertEqual(json["key2"][1].number!, 12)
- XCTAssertEqual(json["key3"].string!, "Bob")
- XCTAssertTrue(json["\n鱿aK㝡␒㼙2촹f"].object != nil)
- XCTAssertEqual(json["\n鱿aK㝡␒㼙2촹f"]["foo"].string!, "bar")
- XCTAssertEqual(json["key5"].bool!, false)
- XCTAssertEqual(json["key6"].null, true)
- XCTAssertEqualWithAccuracy(json["key\"7"].number!, -2.11234123, accuracy: 0.01)
- }
- }
-
- func testParsePrettyPrintedNestedMixedTypes() {
- let string = "{\"key1\": 1, \"key2\": [ -12 , 12 ], \"key3\": \"Bob\", \"\\n鱿aK㝡␒㼙2촹f\": { \"foo\": \"bar\" }, \"key5\": false, \"key6\": null, \"key\\\"7\": -2.11234123}"
- let json1 = try? JSON.parse(string)
-
- XCTAssertTrue(json1 != nil)
-
- let prettyPrinted = json1?.stringify() ?? ""
- let json2 = try? JSON.parse(prettyPrinted)
- XCTAssertEqual(json1, json2)
- }
-
- func testPrettyPrintedNestedObjectType() {
- let string = "{\"key\": { \"foo\": \"bar\" }}"
- let json1 = try? JSON.parse(string)
-
- XCTAssertTrue(json1 != nil)
-
- let prettyPrinted = json1?.stringify() ?? ""
- XCTAssertEqual(prettyPrinted, "{\n \"key\": {\n \"foo\": \"bar\"\n }\n}")
- }
-
- func testPrettyPrintedNestedArrayType() {
- let string = "{\"key\": [ \"foo\", \"bar\" ]}"
- let json1 = try? JSON.parse(string)
-
- XCTAssertTrue(json1 != nil)
-
- let prettyPrinted = json1?.stringify() ?? ""
- XCTAssertEqual(prettyPrinted, "{\n \"key\": [\n \"foo\",\n \"bar\"\n ]\n}")
- }
-
- func testMutipleNestedArrayDictionaryTypes() {
- let string = "[[[[{},{},{\"ꫯ\":\"ꫯ\"}]]],[],[],[{}]]"
- let json = try? JSON.parse(string)
-
- XCTAssertTrue(json != nil)
- }
-
- func testParseStringWithSingleEscapedControlCharacters() {
- let string = "\"\\n\""
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertEqual(jsvalue.string, "\n")
-
- let data = string.data(using: String.Encoding.utf8, allowLossyConversion: false)
- let json: Any!
- do {
- json = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.allowFragments)
- } catch _ {
- json = nil
- };
- let jsonString = json as! String
- XCTAssertEqual("\n", jsonString)
- }
-
- func testParseStringWithEscapedControlCharacters() {
- let string = "\"\\\\\\/\\n\\r\\t\"" // "\\\/\n\r\t" => "\/\n\r\t"
- let jsvalue = try? JSValue.parse(string)
-
- XCTAssertTrue(jsvalue != nil)
- XCTAssertEqual(jsvalue.string, "\\/\n\r\t")
-
- let data = string.data(using: String.Encoding.utf8, allowLossyConversion: false)
- let json: Any!
- do {
- json = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.allowFragments)
- } catch _ {
- json = nil
- };
- let jsonString = json as! String
- XCTAssertEqual(jsvalue.string, jsonString)
- }
-
- func testParseStringWithUnicodeEscapes() {
- let string = "\"value=\\u0026\\u03c6\\u00DF\""
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue != nil)
- XCTAssertEqual(jsvalue.string, "value=&\u{03C6}ß")
- }
-
- func testParseStringWithInvalidUnicodeEscapes() {
- let string = "\"value=\\uxyz2\""
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- // TODO: Validate the error information
- }
-
- func testParseStringWithSurrogatePairs() {
- let string = "\"\\uD834\\uDD1E\""
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue != nil)
- }
-
- func testParseInvalidArrayMissingComma() {
- let string = "[1 true]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseInvalidArrayEmptyComma() {
- let string = "[1,,true]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseInvalidArrayTrailingComma() {
- let string = "[1,true,]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseStringUnescapedNewline() {
- let string = "[\"new\nline\"]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseStringEscapedNewline() {
- let string = "[\"new\\nline\"]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue != nil)
- }
-
- func testParseStringUnescapedTab() {
- let string = "[\"new\tline\"]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseStringEscapedTab() {
- let string = "[\"new\\tline\"]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue != nil)
- }
-
- func testParseNumberNetIntStartingWithZero() {
- let string = "[-012]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumber0ePlus1() {
- let string = "[0e+1]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue != nil)
- }
-
- func testParseNumber0e1() {
- let string = "[0e1]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue != nil)
- }
-
- func testParseNumberCloseToZero() {
- let string = "[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue != nil)
- }
-
- func testParseNumberNegativeReal() {
- let string = "-0.1"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue != nil)
- }
-
- func testParseNumberPlusPlus() {
- let string = "[++1234]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberPlus() {
- let string = "[+1]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberTwoDot() {
- let string = "[-2.]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberTwoDotE3() {
- let string = "[2.e3]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberTwoDotEMinus3() {
- let string = "[2.e-3]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberTwoDotEPlus3() {
- let string = "[2.e+3]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberOneDot() {
- let string = "[1.]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseEmpty() {
- let string = ""
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberZeroDotE1() {
- let string = "[0.e1]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberLeadingDot() {
- let string = "[.123]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberMinus() {
- let string = "-"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberZeroDot3EPlus() {
- let string = "0.3e+"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberZero3e() {
- let string = "0.3e"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberZero3eMinus() {
- let string = "0.e3-"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberOneDot0EPlus() {
- let string = "1.0e+"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberOneDot0EMinus() {
- let string = "1.0e-"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberOneDot0e() {
- let string = "1.0e"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberOneDot0E() {
- let string = "1.0E"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberZeroEPlus() {
- let string = "0E+"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberZeroeMinus() {
- let string = "0e-"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberZeroE() {
- let string = "0E"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseObjectTrailingComma() {
- let string = "{\"id\":0,}"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
- func testParseNumberHugeExp() {
- let string = "[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]"
- let jsvalue = try? JSValue.parse(string)
- XCTAssertTrue(jsvalue == nil)
- }
-
-// TODO(owensd): This should be redone to support Linux as well.
-#if os(macOS)
- func testParsingSampleJSON() {
- // SwiftBug(SR-4725) - Support test collateral properly
- let path = NSString.path(withComponents: [Bundle(for: JSValueParsingTests.self).bundlePath, "..", "..", "..", "TestCollateral", "sample.json"])
- XCTAssertNotNil(path)
-
- let string: NSString?
- do {
- string = try NSString(contentsOfFile: path, encoding: String.Encoding.utf8.rawValue)
- } catch _ {
- string = nil
- }
- XCTAssertNotNil(string)
-
- let json = try? JSON.parse(string! as String)
- XCTAssertTrue(json != nil)
- }
-#endif
-
- func testStringifyEscaping() {
- let json: JSON = [
- "url" : "should escape double quotes \""
- ]
-
- let str = json.stringify(0)
- let expected = "{\"url\":\"should escape double quotes \\\"\"}"
- XCTAssertEqual(str, expected)
- }
-
- static let allTests = [
- ("testParseNull", testParseNull),
- ("testParseNullWithWhitespace", testParseNullWithWhitespace),
- ("testParseNullInvalidJSON", testParseNullInvalidJSON),
- ("testParseTrue", testParseTrue),
- ("testParseTrueWithWhitespace", testParseTrueWithWhitespace),
- ("testParseTrueInvalidJSON", testParseTrueInvalidJSON),
- ("testParseFalse", testParseFalse),
- ("testParseFalseWithWhitespace", testParseFalseWithWhitespace),
- ("testParseFalseInvalidJSON", testParseFalseInvalidJSON),
- ("testParseStringWithDoubleQuote", testParseStringWithDoubleQuote),
- ("testParseStringWithSingleQuote", testParseStringWithSingleQuote),
- ("testParseStringWithEscapedQuote", testParseStringWithEscapedQuote),
- ("testParseStringWithEscapedQuoteMatchingEndQuotes", testParseStringWithEscapedQuoteMatchingEndQuotes),
- ("testParseStringWithMultipleEscapes", testParseStringWithMultipleEscapes),
- ("testParseStringWithMultipleUnicodeTypes", testParseStringWithMultipleUnicodeTypes),
- ("testParseStringWithTrailingEscapedQuotes", testParseStringWithTrailingEscapedQuotes),
- ("testParseInteger", testParseInteger),
- ("testParseNegativeInteger", testParseNegativeInteger),
- ("testParseDouble", testParseDouble),
- ("testParseNegativeDouble", testParseNegativeDouble),
- ("testParseExponent", testParseExponent),
- ("testParsePositiveExponent", testParsePositiveExponent),
- ("testParseNegativeExponent", testParseNegativeExponent),
- ("testParseEmptyArray", testParseEmptyArray),
- ("testSingleElementArray", testSingleElementArray),
- ("testMultipleElementArray", testMultipleElementArray),
- ("testParseEmptyDictionary", testParseEmptyDictionary),
- ("testParseEmptyDictionaryWithExtraWhitespace", testParseEmptyDictionaryWithExtraWhitespace),
- ("testParseDictionaryWithSingleKeyValuePair", testParseDictionaryWithSingleKeyValuePair),
- ("testParseDictionaryWithMultipleKeyValuePairs", testParseDictionaryWithMultipleKeyValuePairs),
- ("testParseMixedArray", testParseMixedArray),
- ("testParseMixedDictionary", testParseMixedDictionary),
- ("testParseNestedMixedTypes", testParseNestedMixedTypes),
- ("testParsePrettyPrintedNestedMixedTypes", testParsePrettyPrintedNestedMixedTypes),
- ("testPrettyPrintedNestedObjectType", testPrettyPrintedNestedObjectType),
- ("testPrettyPrintedNestedArrayType", testPrettyPrintedNestedArrayType),
- ("testMutipleNestedArrayDictionaryTypes", testMutipleNestedArrayDictionaryTypes),
- ("testParseStringWithSingleEscapedControlCharacters", testParseStringWithSingleEscapedControlCharacters),
- ("testParseStringWithEscapedControlCharacters", testParseStringWithEscapedControlCharacters),
- ("testParseStringWithUnicodeEscapes", testParseStringWithUnicodeEscapes),
- ("testParseStringWithInvalidUnicodeEscapes", testParseStringWithInvalidUnicodeEscapes),
- ("testStringifyEscaping", testStringifyEscaping),
- ("testParseStringWithSurrogatePairs", testParseStringWithSurrogatePairs)
- ]
+ func testParseStringWithEscapedQuoteMatchingEndQuotes() {
+ let string = "\"Bob \\\"the man\\\" Roberts\""
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertEqual(jsvalue.string, "Bob \"the man\" Roberts")
+ }
+
+ func testParseStringWithMultipleEscapes() {
+ let string = "\"e&\\\\첊xz坍崦ݻ鍴\\\"嵥B3\\u000b㢊\\u0015L臯.샥\""
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertEqual(jsvalue.string, "e&\\첊xz坍崦ݻ鍴\"嵥B3\u{000b}㢊\u{0015}L臯.샥")
+ }
+
+ func testParseStringWithMultipleUnicodeTypes() {
+ let string =
+ "\"(\u{20da}g8큽튣>^Y{뤋.袊䂓;_g]S\\u202a꽬L;^'#땏bႌ?C緡<䝲䲝断ꏏ6\\u001asD7IK5Wxo8\\u0006p弊⼂ꯍ扵\\u0003`뵂픋%ꄰ⫙됶l囏尛+䗅E쟇\\\\\""
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertEqual(
+ jsvalue.string,
+ "(\u{20da}g8큽튣>^Y{뤋.袊䂓;_g]S\u{202a}꽬L;^'#땏bႌ?C緡<䝲䲝断ꏏ6\u{001a}sD7IK5Wxo8\u{0006}p弊⼂ꯍ扵\u{0003}`뵂픋%ꄰ⫙됶l囏尛+䗅E쟇\\"
+ )
+ }
+
+ func testParseStringWithTrailingEscapedQuotes() {
+ let string = "\"\\\"䬰ỐwD捾V`邀⠕VD㺝sH6[칑.:醥葹*뻵倻aD\\\"\""
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertEqual(jsvalue.string, "\"䬰ỐwD捾V`邀⠕VD㺝sH6[칑.:醥葹*뻵倻aD\"")
+ }
+
+ func testParseInteger() {
+ let string = "101"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertTrue(jsvalue.number == 101)
+ }
+
+ func testParseNegativeInteger() {
+ let string = "-109234"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertTrue(jsvalue.number == -109234)
+ }
+
+ func testParseDouble() {
+ let string = "12.345678"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertEqualWithAccuracy(jsvalue!.number!, 12.345678, accuracy: 0.01)
+ }
+
+ func testParseNegativeDouble() {
+ let string = "-123.949"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertEqualWithAccuracy(jsvalue!.number!, -123.949, accuracy: 0.01)
+ }
+
+ func testParseExponent() {
+ let string = "12.345e2"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertEqualWithAccuracy(jsvalue!.number!, 12.345e2, accuracy: 0.01)
+ }
+
+ func testParsePositiveExponent() {
+ let string = "12.345e+2"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertEqualWithAccuracy(jsvalue!.number!, 12.345e+2, accuracy: 0.01)
+ }
+
+ func testParseNegativeExponent() {
+ let string = "-123.9492e-5"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertEqualWithAccuracy(jsvalue!.number!, -123.9492e-5, accuracy: 0.01)
+ }
+
+ func testParseEmptyArray() {
+ let string = "[]"
+ let json = try? JSON.parse(string)
+
+ XCTAssertTrue(json != nil)
+ if let json = json {
+ XCTAssertTrue(json.array != nil)
+ }
+ }
+
+ func testSingleElementArray() {
+ let string = "[101]"
+ let json = try? JSON.parse(string)
+
+ XCTAssertTrue(json != nil)
+ if let json = json {
+ XCTAssertTrue(json.array != nil)
+ XCTAssertTrue(json.array?.count == 1)
+ XCTAssertTrue(json[0] == 101)
+ }
+ }
+
+ func testMultipleElementArray() {
+ let string = "[101, 202, 303]"
+ let json = try? JSON.parse(string)
+
+ XCTAssertTrue(json != nil)
+ if let json = json {
+ XCTAssertTrue(json.array != nil)
+ XCTAssertTrue(json.array?.count == 3)
+ XCTAssertTrue(json[0] == 101)
+ XCTAssertTrue(json[1] == 202)
+ XCTAssertTrue(json[2] == 303)
+ }
+ }
+
+ func testParseEmptyDictionary() {
+ let string = "{}"
+ let json = try? JSON.parse(string)
+
+ XCTAssertTrue(json != nil)
+ if let json = json {
+ XCTAssertTrue(json.object != nil)
+ }
+ }
+
+ func testParseEmptyDictionaryWithExtraWhitespace() {
+ let string = " {\r\n\n\n\n \t \t} \t \t"
+ let json = try? JSON.parse(string)
+
+ XCTAssertTrue(json != nil)
+ if let json = json {
+ XCTAssertTrue(json.object != nil)
+ }
+ }
+
+ func testParseDictionaryWithSingleKeyValuePair() {
+ let string = "{ \"key\": 101 }"
+ let json = try? JSON.parse(string)
+
+ XCTAssertTrue(json != nil)
+ if let json = json {
+ XCTAssertTrue(json.object != nil)
+ XCTAssertEqual(json["key"].number!, 101)
+ }
+ }
+
+ func testParseDictionaryWithMultipleKeyValuePairs() {
+ let string = "{ \"key1\": 101, \"key2\" : 202,\"key3\":303}"
+ let json = try? JSON.parse(string)
+
+ XCTAssertTrue(json != nil)
+ if let json = json {
+ XCTAssertTrue(json.object != nil)
+ XCTAssertEqual(json["key1"].number!, 101)
+ XCTAssertEqual(json["key2"].number!, 202)
+ XCTAssertEqual(json["key3"].number!, 303)
+ }
+ }
+
+ func testParseMixedArray() {
+ let string = "[1, -12, \"Bob\", true, false, null, -2.11234123]"
+ let json = try? JSON.parse(string)
+
+ XCTAssertTrue(json != nil)
+ if let json = json {
+ XCTAssertTrue(json.array != nil)
+ XCTAssertEqual(json.array!.count, 7)
+ XCTAssertEqual(json[0].number!, 1)
+ XCTAssertEqual(json[1].number!, -12)
+ XCTAssertEqual(json[2].string!, "Bob")
+ XCTAssertEqual(json[3].bool!, true)
+ XCTAssertEqual(json[4].bool!, false)
+ XCTAssertEqual(json[5].null, true)
+ XCTAssertEqualWithAccuracy(
+ json[6].number!,
+ -2.11234123,
+ accuracy: 0.01
+ )
+ }
+ }
+
+ func testParseMixedDictionary() {
+ let string =
+ "{\"key1\": 1, \"key2\": -12, \"key3\": \"Bob\", \"key4\": true, \"key5\": false, \"key6\": null, \"key7\": -2.11234123}"
+ let json = try? JSON.parse(string)
+
+ XCTAssertTrue(json != nil)
+ if let json = json {
+ XCTAssertTrue(json.object != nil)
+ XCTAssertEqual(json.object!.count, 7)
+ XCTAssertEqual(json["key1"].number!, 1)
+ XCTAssertEqual(json["key2"].number!, -12)
+ XCTAssertEqual(json["key3"].string!, "Bob")
+ XCTAssertEqual(json["key4"].bool!, true)
+ XCTAssertEqual(json["key5"].bool!, false)
+ XCTAssertEqual(json["key6"].null, true)
+ XCTAssertEqualWithAccuracy(
+ json["key7"].number!,
+ -2.11234123,
+ accuracy: 0.01
+ )
+ }
+ }
+
+ func testParseNestedMixedTypes() {
+ let string =
+ "{\"key1\": 1, \"key2\": [ -12 , 12 ], \"key3\": \"Bob\", \"\\n鱿aK㝡␒㼙2촹f\": { \"foo\": \"bar\" }, \"key5\": false, \"key6\": null, \"key\\\"7\": -2.11234123}"
+ let json = try? JSON.parse(string)
+
+ XCTAssertTrue(json != nil)
+ if let json = json {
+ XCTAssertTrue(json.object != nil)
+ XCTAssertEqual(json.object!.count, 7)
+ XCTAssertEqual(json["key1"].number!, 1)
+ XCTAssertTrue(json["key2"].array != nil)
+ XCTAssertEqual(json["key2"].array!.count, 2)
+ XCTAssertEqual(json["key2"][0].number!, -12)
+ XCTAssertEqual(json["key2"][1].number!, 12)
+ XCTAssertEqual(json["key3"].string!, "Bob")
+ XCTAssertTrue(json["\n鱿aK㝡␒㼙2촹f"].object != nil)
+ XCTAssertEqual(json["\n鱿aK㝡␒㼙2촹f"]["foo"].string!, "bar")
+ XCTAssertEqual(json["key5"].bool!, false)
+ XCTAssertEqual(json["key6"].null, true)
+ XCTAssertEqualWithAccuracy(
+ json["key\"7"].number!,
+ -2.11234123,
+ accuracy: 0.01
+ )
+ }
+ }
+
+ func testParsePrettyPrintedNestedMixedTypes() {
+ let string =
+ "{\"key1\": 1, \"key2\": [ -12 , 12 ], \"key3\": \"Bob\", \"\\n鱿aK㝡␒㼙2촹f\": { \"foo\": \"bar\" }, \"key5\": false, \"key6\": null, \"key\\\"7\": -2.11234123}"
+ let json1 = try? JSON.parse(string)
+
+ XCTAssertTrue(json1 != nil)
+
+ let prettyPrinted = json1?.stringify() ?? ""
+ let json2 = try? JSON.parse(prettyPrinted)
+ XCTAssertEqual(json1, json2)
+ }
+
+ func testPrettyPrintedNestedObjectType() {
+ let string = "{\"key\": { \"foo\": \"bar\" }}"
+ let json1 = try? JSON.parse(string)
+
+ XCTAssertTrue(json1 != nil)
+
+ let prettyPrinted = json1?.stringify() ?? ""
+ XCTAssertEqual(prettyPrinted, "{\n \"key\": {\n \"foo\": \"bar\"\n }\n}")
+ }
+
+ func testPrettyPrintedNestedArrayType() {
+ let string = "{\"key\": [ \"foo\", \"bar\" ]}"
+ let json1 = try? JSON.parse(string)
+
+ XCTAssertTrue(json1 != nil)
+
+ let prettyPrinted = json1?.stringify() ?? ""
+ XCTAssertEqual(
+ prettyPrinted,
+ "{\n \"key\": [\n \"foo\",\n \"bar\"\n ]\n}"
+ )
+ }
+
+ func testMutipleNestedArrayDictionaryTypes() {
+ let string = "[[[[{},{},{\"ꫯ\":\"ꫯ\"}]]],[],[],[{}]]"
+ let json = try? JSON.parse(string)
+
+ XCTAssertTrue(json != nil)
+ }
+
+ func testParseStringWithSingleEscapedControlCharacters() {
+ let string = "\"\\n\""
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertEqual(jsvalue.string, "\n")
+
+ let data = string.data(using: String.Encoding.utf8, allowLossyConversion: false)
+ let json: Any!
+ do {
+ json = try JSONSerialization.jsonObject(
+ with: data!,
+ options: JSONSerialization.ReadingOptions.allowFragments
+ )
+ }
+ catch _ {
+ json = nil
+ }
+ let jsonString = json as! String
+ XCTAssertEqual("\n", jsonString)
+ }
+
+ func testParseStringWithEscapedControlCharacters() {
+ let string = "\"\\\\\\/\\n\\r\\t\"" // "\\\/\n\r\t" => "\/\n\r\t"
+ let jsvalue = try? JSValue.parse(string)
+
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertEqual(jsvalue.string, "\\/\n\r\t")
+
+ let data = string.data(using: String.Encoding.utf8, allowLossyConversion: false)
+ let json: Any!
+ do {
+ json = try JSONSerialization.jsonObject(
+ with: data!,
+ options: JSONSerialization.ReadingOptions.allowFragments
+ )
+ }
+ catch _ {
+ json = nil
+ }
+ let jsonString = json as! String
+ XCTAssertEqual(jsvalue.string, jsonString)
+ }
+
+ func testParseStringWithUnicodeEscapes() {
+ let string = "\"value=\\u0026\\u03c6\\u00DF\""
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue != nil)
+ XCTAssertEqual(jsvalue.string, "value=&\u{03C6}ß")
+ }
+
+ func testParseStringWithInvalidUnicodeEscapes() {
+ let string = "\"value=\\uxyz2\""
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ // TODO: Validate the error information
+ }
+
+ func testParseStringWithSurrogatePairs() {
+ let string = "\"\\uD834\\uDD1E\""
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue != nil)
+ }
+
+ func testParseInvalidArrayMissingComma() {
+ let string = "[1 true]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseInvalidArrayEmptyComma() {
+ let string = "[1,,true]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseInvalidArrayTrailingComma() {
+ let string = "[1,true,]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseStringUnescapedNewline() {
+ let string = "[\"new\nline\"]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseStringEscapedNewline() {
+ let string = "[\"new\\nline\"]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue != nil)
+ }
+
+ func testParseStringUnescapedTab() {
+ let string = "[\"new\tline\"]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseStringEscapedTab() {
+ let string = "[\"new\\tline\"]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue != nil)
+ }
+
+ func testParseNumberNetIntStartingWithZero() {
+ let string = "[-012]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumber0ePlus1() {
+ let string = "[0e+1]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue != nil)
+ }
+
+ func testParseNumber0e1() {
+ let string = "[0e1]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue != nil)
+ }
+
+ func testParseNumberCloseToZero() {
+ let string =
+ "[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue != nil)
+ }
+
+ func testParseNumberNegativeReal() {
+ let string = "-0.1"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue != nil)
+ }
+
+ func testParseNumberPlusPlus() {
+ let string = "[++1234]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberPlus() {
+ let string = "[+1]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberTwoDot() {
+ let string = "[-2.]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberTwoDotE3() {
+ let string = "[2.e3]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberTwoDotEMinus3() {
+ let string = "[2.e-3]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberTwoDotEPlus3() {
+ let string = "[2.e+3]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberOneDot() {
+ let string = "[1.]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseEmpty() {
+ let string = ""
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberZeroDotE1() {
+ let string = "[0.e1]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberLeadingDot() {
+ let string = "[.123]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberMinus() {
+ let string = "-"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberZeroDot3EPlus() {
+ let string = "0.3e+"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberZero3e() {
+ let string = "0.3e"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberZero3eMinus() {
+ let string = "0.e3-"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberOneDot0EPlus() {
+ let string = "1.0e+"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberOneDot0EMinus() {
+ let string = "1.0e-"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberOneDot0e() {
+ let string = "1.0e"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberOneDot0E() {
+ let string = "1.0E"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberZeroEPlus() {
+ let string = "0E+"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberZeroeMinus() {
+ let string = "0e-"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberZeroE() {
+ let string = "0E"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseObjectTrailingComma() {
+ let string = "{\"id\":0,}"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ func testParseNumberHugeExp() {
+ let string =
+ "[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]"
+ let jsvalue = try? JSValue.parse(string)
+ XCTAssertTrue(jsvalue == nil)
+ }
+
+ // TODO(owensd): This should be redone to support Linux as well.
+ #if os(macOS)
+ func testParsingSampleJSON() {
+ // SwiftBug(SR-4725) - Support test collateral properly
+ let path = NSString.path(withComponents: [
+ Bundle(for: JSValueParsingTests.self).bundlePath, "..",
+ "..", "..", "TestCollateral", "sample.json",
+ ])
+ XCTAssertNotNil(path)
+
+ let string: NSString?
+ do {
+ string = try NSString(
+ contentsOfFile: path,
+ encoding: String.Encoding.utf8.rawValue
+ )
+ }
+ catch _ {
+ string = nil
+ }
+ XCTAssertNotNil(string)
+
+ let json = try? JSON.parse(string! as String)
+ XCTAssertTrue(json != nil)
+ }
+ #endif
+
+ func testStringifyEscaping() {
+ let json: JSON = [
+ "url": "should escape double quotes \""
+ ]
+
+ let str = json.stringify(0)
+ let expected = "{\"url\":\"should escape double quotes \\\"\"}"
+ XCTAssertEqual(str, expected)
+ }
+
+ static let allTests = [
+ ("testParseNull", testParseNull),
+ ("testParseNullWithWhitespace", testParseNullWithWhitespace),
+ ("testParseNullInvalidJSON", testParseNullInvalidJSON),
+ ("testParseTrue", testParseTrue),
+ ("testParseTrueWithWhitespace", testParseTrueWithWhitespace),
+ ("testParseTrueInvalidJSON", testParseTrueInvalidJSON),
+ ("testParseFalse", testParseFalse),
+ ("testParseFalseWithWhitespace", testParseFalseWithWhitespace),
+ ("testParseFalseInvalidJSON", testParseFalseInvalidJSON),
+ ("testParseStringWithDoubleQuote", testParseStringWithDoubleQuote),
+ ("testParseStringWithSingleQuote", testParseStringWithSingleQuote),
+ ("testParseStringWithEscapedQuote", testParseStringWithEscapedQuote),
+ (
+ "testParseStringWithEscapedQuoteMatchingEndQuotes",
+ testParseStringWithEscapedQuoteMatchingEndQuotes
+ ),
+ ("testParseStringWithMultipleEscapes", testParseStringWithMultipleEscapes),
+ (
+ "testParseStringWithMultipleUnicodeTypes",
+ testParseStringWithMultipleUnicodeTypes
+ ),
+ (
+ "testParseStringWithTrailingEscapedQuotes",
+ testParseStringWithTrailingEscapedQuotes
+ ),
+ ("testParseInteger", testParseInteger),
+ ("testParseNegativeInteger", testParseNegativeInteger),
+ ("testParseDouble", testParseDouble),
+ ("testParseNegativeDouble", testParseNegativeDouble),
+ ("testParseExponent", testParseExponent),
+ ("testParsePositiveExponent", testParsePositiveExponent),
+ ("testParseNegativeExponent", testParseNegativeExponent),
+ ("testParseEmptyArray", testParseEmptyArray),
+ ("testSingleElementArray", testSingleElementArray),
+ ("testMultipleElementArray", testMultipleElementArray),
+ ("testParseEmptyDictionary", testParseEmptyDictionary),
+ (
+ "testParseEmptyDictionaryWithExtraWhitespace",
+ testParseEmptyDictionaryWithExtraWhitespace
+ ),
+ (
+ "testParseDictionaryWithSingleKeyValuePair",
+ testParseDictionaryWithSingleKeyValuePair
+ ),
+ (
+ "testParseDictionaryWithMultipleKeyValuePairs",
+ testParseDictionaryWithMultipleKeyValuePairs
+ ),
+ ("testParseMixedArray", testParseMixedArray),
+ ("testParseMixedDictionary", testParseMixedDictionary),
+ ("testParseNestedMixedTypes", testParseNestedMixedTypes),
+ (
+ "testParsePrettyPrintedNestedMixedTypes",
+ testParsePrettyPrintedNestedMixedTypes
+ ),
+ ("testPrettyPrintedNestedObjectType", testPrettyPrintedNestedObjectType),
+ ("testPrettyPrintedNestedArrayType", testPrettyPrintedNestedArrayType),
+ (
+ "testMutipleNestedArrayDictionaryTypes",
+ testMutipleNestedArrayDictionaryTypes
+ ),
+ (
+ "testParseStringWithSingleEscapedControlCharacters",
+ testParseStringWithSingleEscapedControlCharacters
+ ),
+ (
+ "testParseStringWithEscapedControlCharacters",
+ testParseStringWithEscapedControlCharacters
+ ),
+ ("testParseStringWithUnicodeEscapes", testParseStringWithUnicodeEscapes),
+ (
+ "testParseStringWithInvalidUnicodeEscapes",
+ testParseStringWithInvalidUnicodeEscapes
+ ),
+ ("testStringifyEscaping", testStringifyEscaping),
+ ("testParseStringWithSurrogatePairs", testParseStringWithSurrogatePairs),
+ ]
}
diff --git a/Tests/JSONLibTests/JSValueTests.Usage.swift b/Tests/JSONLibTests/JSValueTests.Usage.swift
index 4faee8c..6d5f32b 100644
--- a/Tests/JSONLibTests/JSValueTests.Usage.swift
+++ b/Tests/JSONLibTests/JSValueTests.Usage.swift
@@ -3,106 +3,105 @@
* Licensed under the MIT License. See License in the project root for license information.
* ------------------------------------------------------------------------------------------ */
-import XCTest
import JSONLib
+import XCTest
/*
* The purpose of these tests are to ensure the desired usage is maintained throughout all of the
* refactoring that is taking place and will take place in the future.
- *
+ *
* Any breaks in these tests mean that usability of the API has been compromised.
*/
-class JSValueUsageTests : XCTestCase {
+class JSValueUsageTests: XCTestCase {
- func testValidateSingleValueStringUsagePatternIfLet() {
- let json: JSValue = "Hello"
- if let value = json.string {
- XCTAssertEqual(value, "Hello")
- }
- else {
- XCTFail()
- }
- }
-
- func testValidateSingleValueStringUsagePatternOptionalChaining() {
- let json: JSValue = "Hello"
-
- let value = json.string?.uppercased() ?? ""
- XCTAssertEqual(value, "HELLO")
- }
-
- func testValidateSingleValueNumberUsagePatternIfLet() {
- let json: JSValue = 123
- if let value = json.number {
- XCTAssertEqual(value, 123)
- }
- else {
- XCTFail()
- }
- }
-
- func testValidateSingleValueNumberUsagePatternOptionalChaining() {
- let json: JSValue = 123
-
- let value = json.number?.distance(to: 100) ?? 0
- XCTAssertEqual(value, -23)
- }
-
- func testValidateSingleValueBoolUsagePatternIfLet() {
- let json: JSValue = false
- if let value = json.bool {
- XCTAssertEqual(value, false)
- }
- else {
- XCTFail()
- }
- }
-
- func testValidateSingleValueBoolUsagePatternOptionalChaining() {
- let json: JSValue = true
-
- let value = json.bool ?? false
- XCTAssertEqual(value, true)
- }
-
-
- func testValidateSingleLevelAccessInDictionaryUsage() {
- var json: JSValue = ["status": "ok"]
-
- XCTAssertEqual(json["status"].string, "ok")
- }
-
- func testValidateMultipleLevelAccessInDictionaryUsage() {
- var json: JSValue = ["item": ["info": ["name": "Item #1"]]]
-
- XCTAssertEqual(json["item"]["info"]["name"].string, "Item #1")
- }
-
- func testValidateMultipleLevelAccessInDictionaryUsageNonLiterals() {
- let item1 = "Item #1"
-
- var json: JSValue = ["item": ["info": ["name": JSValue(item1) ]]]
-
- XCTAssertEqual(json["item"]["info"]["name"].string, "Item #1")
- }
-
- func testValidateSingleLevelAccessInDictionaryUsageWithMissingKey() {
- var json: JSValue = ["status": "ok"]
-
- XCTAssertTrue(json["stat"].string == nil)
- }
-
- func testValidateArrayUsageNonLiterals() {
- var array = [JSValue]()
- array.append("Item #1")
-
- var json = JSValue(array)
-
- XCTAssertEqual(json[0].string, "Item #1")
- }
-
- /*
+ func testValidateSingleValueStringUsagePatternIfLet() {
+ let json: JSValue = "Hello"
+ if let value = json.string {
+ XCTAssertEqual(value, "Hello")
+ }
+ else {
+ XCTFail()
+ }
+ }
+
+ func testValidateSingleValueStringUsagePatternOptionalChaining() {
+ let json: JSValue = "Hello"
+
+ let value = json.string?.uppercased() ?? ""
+ XCTAssertEqual(value, "HELLO")
+ }
+
+ func testValidateSingleValueNumberUsagePatternIfLet() {
+ let json: JSValue = 123
+ if let value = json.number {
+ XCTAssertEqual(value, 123)
+ }
+ else {
+ XCTFail()
+ }
+ }
+
+ func testValidateSingleValueNumberUsagePatternOptionalChaining() {
+ let json: JSValue = 123
+
+ let value = json.number?.distance(to: 100) ?? 0
+ XCTAssertEqual(value, -23)
+ }
+
+ func testValidateSingleValueBoolUsagePatternIfLet() {
+ let json: JSValue = false
+ if let value = json.bool {
+ XCTAssertEqual(value, false)
+ }
+ else {
+ XCTFail()
+ }
+ }
+
+ func testValidateSingleValueBoolUsagePatternOptionalChaining() {
+ let json: JSValue = true
+
+ let value = json.bool ?? false
+ XCTAssertEqual(value, true)
+ }
+
+ func testValidateSingleLevelAccessInDictionaryUsage() {
+ var json: JSValue = ["status": "ok"]
+
+ XCTAssertEqual(json["status"].string, "ok")
+ }
+
+ func testValidateMultipleLevelAccessInDictionaryUsage() {
+ var json: JSValue = ["item": ["info": ["name": "Item #1"]]]
+
+ XCTAssertEqual(json["item"]["info"]["name"].string, "Item #1")
+ }
+
+ func testValidateMultipleLevelAccessInDictionaryUsageNonLiterals() {
+ let item1 = "Item #1"
+
+ var json: JSValue = ["item": ["info": ["name": JSValue(item1)]]]
+
+ XCTAssertEqual(json["item"]["info"]["name"].string, "Item #1")
+ }
+
+ func testValidateSingleLevelAccessInDictionaryUsageWithMissingKey() {
+ var json: JSValue = ["status": "ok"]
+
+ XCTAssertTrue(json["stat"].string == nil)
+ }
+
+ func testValidateArrayUsageNonLiterals() {
+ var array = [JSValue]()
+ array.append("Item #1")
+
+ var json = JSValue(array)
+
+ XCTAssertEqual(json[0].string, "Item #1")
+ }
+
+ /*
func testFunctionalParsingToStruct() {
var json: JSON = [
"id" : 73,
@@ -110,15 +109,15 @@ class JSValueUsageTests : XCTestCase {
"needspassword" : true,
"url" : "http://remote.bloxus.com/"
]
-
+
let blog = make ⇒
(json["id"].number ⇒ toInt) ⇒
json["name"].string ⇒
json["needspassword"].bool ⇒
(json["url"].string ⇒ toURL)
-
+
XCTAssertTrue(blog != nil)
-
+
if let blog = blog {
XCTAssertEqual(blog.id, 73)
XCTAssertEqual(blog.name, "Bloxus test")
@@ -126,201 +125,211 @@ class JSValueUsageTests : XCTestCase {
XCTAssertEqual(blog.url, URL(string: "http://remote.bloxus.com/")!)
}
}*/
-
- func testFunctionalParsingToStructIncorrectKey() {
- var json: JSON = [
- "id" : 73,
- "name" : "Bloxus test",
- "password" : true,
- "url" : "http://remote.bloxus.com/"
- ]
-
- let id = json["id"] ⇒ toInt
- let name = json["name"] ⇒ toString
- let password = json["needspassword"] ⇒ toBool
- let url = json["url"] ⇒ toURL
-
- _ = makeFailable ⇒ id ⇒ name ⇒ password ⇒ url
-
-// XCTAssertTrue(blog.1 != nil)
-// if let error = blog.1 {
-// XCTAssertEqual(error.code, JSValue.ErrorCode.KeyNotFound.code)
-// }
- }
-
- func testEnhancementRequest18() {
- var object: JSON = [:]
- object["one"] = 1
-
- var array: [JSON] = []
- for index in 1...10 {
- // nope... my stupid bug...
- array.append(JSON(int: index))
- }
-
- object["array"] = JSON(array)
- XCTAssertEqual(array.count, 10)
-
- var root: JSON = []
- root["object"] = object
- }
-
-// Disabling these tests for now as they are not order deterministic.
-//
-// func testStringifyWithDefaultIndent() {
-// var json: JSON = [
-// "id" : 73,
-// "name" : "Bloxus test",
-// "password" : true,
-// "url" : "http://remote.bloxus.com/"
-// ]
-//
-// let str = json.stringify()
-// let expected = "{\n \"id\": 73.0,\n \"password\": true,\n \"name\": \"Bloxus test\",\n \"url\": \"http://remote.bloxus.com/\"\n}"
-// XCTAssertEqual(str, expected)
-// }
-//
-// func testStringifyWithNoIndent() {
-// var json: JSON = [
-// "id" : 73,
-// "name" : "Bloxus test",
-// "password" : true,
-// "url" : "http://remote.bloxus.com/"
-// ]
-//
-// let str = json.stringify(0)
-// let expected = "{\"id\":73.0,\"password\":true,\"name\":\"Bloxus test\",\"url\":\"http://remote.bloxus.com/\"}"
-// XCTAssertEqual(str, expected)
-// }
-
- func testStringifyWithFlatPrintOut() {
- let json: JSON = [
- "id" : [
- "nested": [
- "value": "hi"
- ]
- ]
- ]
-
- let str = json.stringify(nil)
- let expected = "{\"id\": {\"nested\": {\"value\": \"hi\"}}}"
- XCTAssertEqual(str, expected)
- }
-
- func testStringifyWithIntegerValue() {
- let json: JSON = [
- "id": 12
- ]
-
- let str = json.stringify(nil)
- let expected = "{\"id\": 12}"
- XCTAssertEqual(str, expected)
- }
-
- func testStringifyWithDoubleValue() {
- let json: JSON = [
- "id": 12.01
- ]
-
- let str = json.stringify(nil)
- let expected = "{\"id\": 12.01}"
- XCTAssertEqual(str, expected)
- }
+
+ func testFunctionalParsingToStructIncorrectKey() {
+ var json: JSON = [
+ "id": 73,
+ "name": "Bloxus test",
+ "password": true,
+ "url": "http://remote.bloxus.com/",
+ ]
+
+ let id = json["id"] ⇒ toInt
+ let name = json["name"] ⇒ toString
+ let password = json["needspassword"] ⇒ toBool
+ let url = json["url"] ⇒ toURL
+
+ _ = makeFailable ⇒ id ⇒ name ⇒ password ⇒ url
+
+ // XCTAssertTrue(blog.1 != nil)
+ // if let error = blog.1 {
+ // XCTAssertEqual(error.code, JSValue.ErrorCode.KeyNotFound.code)
+ // }
+ }
+
+ func testEnhancementRequest18() {
+ var object: JSON = [:]
+ object["one"] = 1
+
+ var array: [JSON] = []
+ for index in 1...10 {
+ // nope... my stupid bug...
+ array.append(JSON(int: index))
+ }
+
+ object["array"] = JSON(array)
+ XCTAssertEqual(array.count, 10)
+
+ var root: JSON = []
+ root["object"] = object
+ }
+
+ // Disabling these tests for now as they are not order deterministic.
+ //
+ // func testStringifyWithDefaultIndent() {
+ // var json: JSON = [
+ // "id" : 73,
+ // "name" : "Bloxus test",
+ // "password" : true,
+ // "url" : "http://remote.bloxus.com/"
+ // ]
+ //
+ // let str = json.stringify()
+ // let expected = "{\n \"id\": 73.0,\n \"password\": true,\n \"name\": \"Bloxus test\",\n \"url\": \"http://remote.bloxus.com/\"\n}"
+ // XCTAssertEqual(str, expected)
+ // }
+ //
+ // func testStringifyWithNoIndent() {
+ // var json: JSON = [
+ // "id" : 73,
+ // "name" : "Bloxus test",
+ // "password" : true,
+ // "url" : "http://remote.bloxus.com/"
+ // ]
+ //
+ // let str = json.stringify(0)
+ // let expected = "{\"id\":73.0,\"password\":true,\"name\":\"Bloxus test\",\"url\":\"http://remote.bloxus.com/\"}"
+ // XCTAssertEqual(str, expected)
+ // }
+
+ func testStringifyWithFlatPrintOut() {
+ let json: JSON = [
+ "id": [
+ "nested": [
+ "value": "hi"
+ ]
+ ]
+ ]
+
+ let str = json.stringify(nil)
+ let expected = "{\"id\": {\"nested\": {\"value\": \"hi\"}}}"
+ XCTAssertEqual(str, expected)
+ }
+
+ func testStringifyWithIntegerValue() {
+ let json: JSON = [
+ "id": 12
+ ]
+
+ let str = json.stringify(nil)
+ let expected = "{\"id\": 12}"
+ XCTAssertEqual(str, expected)
+ }
+
+ func testStringifyWithDoubleValue() {
+ let json: JSON = [
+ "id": 12.01
+ ]
+
+ let str = json.stringify(nil)
+ let expected = "{\"id\": 12.01}"
+ XCTAssertEqual(str, expected)
+ }
}
// MARK: Test Helpers
struct Blog {
- let id: Int
- let name: String
- let needsPassword : Bool
- let url: URL
+ let id: Int
+ let name: String
+ let needsPassword: Bool
+ let url: URL
}
func toInt(_ number: Double?) -> Int? {
- if let value = number {
- return Int(value)
- }
-
- return nil
+ if let value = number {
+ return Int(value)
+ }
+
+ return nil
}
func toURL(_ string: String?) -> URL? {
- if let url = string {
- return URL(string: url)
- }
-
- return nil
+ if let url = string {
+ return URL(string: url)
+ }
+
+ return nil
}
func make(_ id: Int?)
- -> (String?)
- -> (Bool?)
- -> (URL?)
- -> Blog?
+ -> (String?)
+ -> (Bool?)
+ -> (URL?)
+ -> Blog?
{
- return { name in
- return { needsPassword in
- return { url in
- if id == nil { return nil }
- if name == nil { return nil }
- if needsPassword == nil { return nil }
- if url == nil { return nil }
-
- return Blog(id: id!, name: name!, needsPassword: needsPassword!, url: url!)
- }
- }
- }
+ return { name in
+ return { needsPassword in
+ return { url in
+ if id == nil { return nil }
+ if name == nil { return nil }
+ if needsPassword == nil { return nil }
+ if url == nil { return nil }
+
+ return Blog(
+ id: id!,
+ name: name!,
+ needsPassword: needsPassword!,
+ url: url!
+ )
+ }
+ }
+ }
}
func makeFailable(_ id: Int?)
- -> (String?)
- -> (Bool?)
- -> (URL?)
- -> Blog?
+ -> (String?)
+ -> (Bool?)
+ -> (URL?)
+ -> Blog?
{
- return { name in
- return { needsPassword in
- return { url in
- guard let id = id else { return nil }
- guard let name = name else { return nil }
- guard let needsPassword = needsPassword else { return nil }
- guard let url = url else { return nil }
-
- return Blog(id: id, name: name, needsPassword: needsPassword, url: url)
- }
- }
- }
+ return { name in
+ return { needsPassword in
+ return { url in
+ guard let id = id else { return nil }
+ guard let name = name else { return nil }
+ guard let needsPassword = needsPassword else { return nil }
+ guard let url = url else { return nil }
+
+ return Blog(
+ id: id,
+ name: name,
+ needsPassword: needsPassword,
+ url: url
+ )
+ }
+ }
+ }
}
func toInt(_ value: JSValue) -> Int? {
- if let value = value.number {
- return Int(value)
- }
-
- return nil
+ if let value = value.number {
+ return Int(value)
+ }
+
+ return nil
}
func toURL(_ value: JSValue) -> URL? {
- if let url = value.string {
- return URL(string: url)
- }
-
- return nil
+ if let url = value.string {
+ return URL(string: url)
+ }
+
+ return nil
}
func toBool(_ value: JSValue) -> Bool? {
- if let bool = value.bool {
- return bool
- }
-
- return nil
+ if let bool = value.bool {
+ return bool
+ }
+
+ return nil
}
func toString(_ value: JSValue) -> String? {
- if let string = value.string {
- return string
- }
-
- return nil
+ if let string = value.string {
+ return string
+ }
+
+ return nil
}
diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift
index ccca867..c21ba85 100644
--- a/Tests/LinuxMain.swift
+++ b/Tests/LinuxMain.swift
@@ -4,11 +4,12 @@
* ------------------------------------------------------------------------------------------ */
import XCTest
+
@testable import JSONLibTests
XCTMain([
- testCase(JSValueEquatableTests.allTests),
- testCase(JSValueIndexersTests.allTests),
- testCase(JSValueLiteralsTests.allTests),
- testCase(JSValueParsingTests.allTests),
-])
\ No newline at end of file
+ testCase(JSValueEquatableTests.allTests),
+ testCase(JSValueIndexersTests.allTests),
+ testCase(JSValueLiteralsTests.allTests),
+ testCase(JSValueParsingTests.allTests),
+])