diff --git a/Sources/Yams/Constructor.swift b/Sources/Yams/Constructor.swift index 4120202c..41633d69 100644 --- a/Sources/Yams/Constructor.swift +++ b/Sources/Yams/Constructor.swift @@ -98,6 +98,19 @@ extension Constructor { .omap: [Any].construct_omap, .pairs: [Any].construct_pairs ] + + ///`Tag.Name` to `Node.Mapping` map that support dynamic collection. + public static let dynamicMappingMap: MappingMap = [ + .map: NSMutableDictionary.construct_mapping, + .set: NSMutableSet.construct_set + ] + + ///`Tag.Name` to `Node.Sequence` map that support dynamic collection. + public static let dynamicSequenceMap: SequenceMap = [ + .seq: NSMutableArray.construct_seq, + .omap: NSMutableArray.construct_omap, + .pairs: NSMutableArray.construct_pairs + ] } // MARK: - ScalarConstructible @@ -252,12 +265,12 @@ extension ScalarConstructible where Self: FloatingPoint & SexagesimalConvertible return nil } - switch scalar.string { - case ".inf", ".Inf", ".INF", "+.inf", "+.Inf", "+.INF": + switch scalar.string.lowercased() { + case ".inf", "+.inf": return .infinity - case "-.inf", "-.Inf", "-.INF": + case "-.inf": return -.infinity - case ".nan", ".NaN", ".NAN": + case ".nan": return .nan default: let string = scalar.string.replacingOccurrences(of: "_", with: "") @@ -433,6 +446,31 @@ private extension Dictionary { } } +extension NSMutableDictionary { + /// Construct an `NSMutableDictionary`, if possible, from the specified mapping. + /// + /// - parameter mapping: The `Node.Mapping` from which to extract an `NSMutableDictionary`, if possible. + /// + /// - returns: An instance of `NSMutableDictionary`, if one was successfully extracted from the mapping. + public static func construct_mapping(from mapping: Node.Mapping) -> NSMutableDictionary? { + _construct_mapping(from: mapping) + } +} + +private extension NSMutableDictionary { + static func _construct_mapping(from mapping: Node.Mapping) -> NSMutableDictionary { + let result = NSMutableDictionary() + let mapping = mapping.flatten() + + mapping.forEach { key, value in + if let keyString = String.construct(from: key) { + result[keyString] = mapping.tag.constructor.any(from: value) + } + } + return result + } +} + extension Set { /// Construct a `Set`, if possible, from the specified mapping. /// @@ -447,6 +485,23 @@ extension Set { } } +extension NSMutableSet { + /// Construct an `NSMutableSet`, if possible, from the specified mapping. + /// + /// - parameter mapping: The `Node.Mapping` from which to extract an `NSMutableSet`, if possible. + /// + /// - returns: An instance of `NSMutableSet`, if one was successfully extracted from the mapping. + public static func construct_set(from mapping: Node.Mapping) -> NSMutableSet? { + let result = NSMutableSet() + mapping.forEach { key, _ in + if let keyString = String.construct(from: key) { + result.add(keyString as AnyHashable) + } + } + return result + } +} + // MARK: Sequence extension Array { @@ -488,6 +543,52 @@ extension Array { } } +extension NSMutableArray { + /// Construct an NSMutableArray of `Any` from the specified `sequence`. + /// + /// - parameter sequence: Sequence to convert to `NSMutableArray`. + /// + /// - returns: NSMutableArray of `Any`. + public static func construct_seq(from sequence: Node.Sequence) -> NSMutableArray { + let result = NSMutableArray() + sequence.forEach { subnode in + result.add(sequence.tag.constructor.any(from: subnode)) + } + return result + } + + /// Construct an "O-map" (NSMutableArray of `(Any, Any)` tuples) from the specified `sequence`. + /// + /// - parameter sequence: Sequence to convert to `NSMutableArray`. + /// + /// - returns: NSMutableArray of `(Any, Any)` tuples. + public static func construct_omap(from sequence: Node.Sequence) -> NSMutableArray { + let result = NSMutableArray() + sequence.forEach { subnode in + // TODO: Should raise error if subnode is not mapping or mapping.count != 1 + if let (key, value) = subnode.mapping?.first { + result.add((sequence.tag.constructor.any(from: key), sequence.tag.constructor.any(from: value))) + } + } + return result + } + + /// Construct an NSMutableArray of `(Any, Any)` tuples from the specified `sequence`. + /// + /// - parameter sequence: Sequence to convert to `NSMutableArray`. + /// + /// - returns: NSMutableArray of `(Any, Any)` tuples. + public static func construct_pairs(from sequence: Node.Sequence) -> NSMutableArray { + let result = NSMutableArray() + sequence.forEach { subnode in + if let (key, value) = subnode.mapping?.first { + result.add((sequence.tag.constructor.any(from: key), sequence.tag.constructor.any(from: value))) + } + } + return result + } +} + private extension String { func substring(with range: NSRange) -> Substring? { guard range.location != NSNotFound else { return nil } diff --git a/Sources/Yams/Representer.swift b/Sources/Yams/Representer.swift index 70310ff8..41e7296d 100644 --- a/Sources/Yams/Representer.swift +++ b/Sources/Yams/Representer.swift @@ -57,6 +57,14 @@ extension Array: NodeRepresentable { } } +extension NSArray: NodeRepresentable { + /// This value's `Node` representation. + public func represented() throws -> Node { + let nodes = try map(represent) + return Node(nodes, Tag(.seq)) + } +} + extension Dictionary: NodeRepresentable { /// This value's `Node` representation. public func represented() throws -> Node { @@ -65,6 +73,14 @@ extension Dictionary: NodeRepresentable { } } +extension NSDictionary: NodeRepresentable { + /// This value's `Node` representation. + public func represented() throws -> Node { + let pairs = try map { (key: try represent($0.0), value: try represent($0.1)) } + return Node(pairs.sorted { $0.key < $1.key }, Tag(.map)) + } +} + private func represent(_ value: Any) throws -> Node { if let representable = value as? NodeRepresentable { return try representable.represented() @@ -187,6 +203,13 @@ extension Float: ScalarRepresentable { } } +extension NSValue: ScalarRepresentable { + /// This value's `Node.scalar` representation. + public func represented() -> Node.Scalar { + return .init(floatFormatter.string(for: self)!.replacingOccurrences(of: "+-", with: "-"), Tag(.float)) + } +} + private func numberFormatter(with significantDigits: Int) -> NumberFormatter { let formatter = NumberFormatter() formatter.locale = Locale(identifier: "en_US") @@ -258,6 +281,14 @@ extension String: ScalarRepresentable { } } +extension NSString: ScalarRepresentable { + /// This value's `Node.scalar` representation. + public func represented() -> Node.Scalar { + let scalar = Node.Scalar(String(self)) + return scalar.resolvedTag.name == .str ? scalar : .init(String(self), Tag(.str), .singleQuoted) + } +} + extension UUID: ScalarRepresentable { /// This value's `Node.scalar` representation. public func represented() -> Node.Scalar {