Merge pull request #18 from jpsim/nn-further-improve-node
Further improve Node
This commit is contained in:
commit
2fd2f9c558
|
@ -59,7 +59,7 @@ extension Node {
|
|||
}
|
||||
|
||||
public struct Mapping {
|
||||
public var pairs: [Pair<Node>]
|
||||
internal var pairs: [Pair<Node>]
|
||||
public var tag: Tag
|
||||
public var style: Style
|
||||
|
||||
|
@ -88,7 +88,7 @@ extension Node {
|
|||
}
|
||||
|
||||
public struct Sequence {
|
||||
public var nodes: [Node]
|
||||
internal var nodes: [Node]
|
||||
public var tag: Tag
|
||||
public var style: Style
|
||||
|
||||
|
@ -248,6 +248,15 @@ extension Node {
|
|||
self[node] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
public subscript(string: String) -> Node? {
|
||||
get {
|
||||
return self[Node(string)]
|
||||
}
|
||||
set {
|
||||
self[Node(string)] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Hashable
|
||||
|
@ -344,6 +353,185 @@ extension Node: ExpressibleByStringLiteral {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Node.Mapping
|
||||
|
||||
extension Node.Mapping: Equatable {
|
||||
public static func == (lhs: Node.Mapping, rhs: Node.Mapping) -> Bool {
|
||||
return lhs.pairs == rhs.pairs
|
||||
}
|
||||
}
|
||||
|
||||
extension Node.Mapping: ExpressibleByDictionaryLiteral {
|
||||
public init(dictionaryLiteral elements: (Node, Node)...) {
|
||||
self.init(pairs: elements.map(Pair.init), tag: .implicit, style: .any)
|
||||
}
|
||||
}
|
||||
|
||||
extension Node.Mapping: MutableCollection {
|
||||
public typealias Element = (key: Node, value: Node)
|
||||
|
||||
// Sequence
|
||||
public func makeIterator() -> Array<Element>.Iterator {
|
||||
let iterator = pairs.map({ (key: $0.key, value: $0.value) }).makeIterator()
|
||||
return iterator
|
||||
}
|
||||
|
||||
// Collection
|
||||
public typealias Index = Array<Element>.Index
|
||||
|
||||
public var startIndex: Int {
|
||||
return pairs.startIndex
|
||||
}
|
||||
|
||||
public var endIndex: Int {
|
||||
return pairs.endIndex
|
||||
}
|
||||
|
||||
public func index(after index: Int) -> Int {
|
||||
return pairs.index(after:index)
|
||||
}
|
||||
|
||||
public subscript(index: Int) -> Element {
|
||||
get {
|
||||
return (key: pairs[index].key, value: pairs[index].value)
|
||||
}
|
||||
// MutableCollection
|
||||
set {
|
||||
pairs[index] = Pair(newValue.key, newValue.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Node.Mapping {
|
||||
public var keys: LazyMapCollection<Node.Mapping, Node> {
|
||||
return lazy.map { $0.key }
|
||||
}
|
||||
|
||||
public var values: LazyMapCollection<Node.Mapping, Node> {
|
||||
return lazy.map { $0.value }
|
||||
}
|
||||
|
||||
public subscript(string: String) -> Node? {
|
||||
get {
|
||||
return self[Node(string)]
|
||||
}
|
||||
set {
|
||||
self[Node(string)] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
public subscript(node: Node) -> Node? {
|
||||
get {
|
||||
let v = pairs.reversed().first(where: { $0.key == node })
|
||||
return v?.value
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
if let index = pairs.reversed().index(where: { $0.key == node }) {
|
||||
let actualIndex = pairs.index(before: index.base)
|
||||
pairs[actualIndex] = Pair(pairs[actualIndex].key, newValue)
|
||||
} else {
|
||||
pairs.append(Pair(node, newValue))
|
||||
}
|
||||
} else {
|
||||
if let index = pairs.reversed().index(where: { $0.key == node }) {
|
||||
let actualIndex = pairs.index(before: index.base)
|
||||
pairs.remove(at: actualIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Node.Sequence
|
||||
|
||||
extension Node.Sequence: Equatable {
|
||||
public static func == (lhs: Node.Sequence, rhs: Node.Sequence) -> Bool {
|
||||
return lhs.nodes == rhs.nodes
|
||||
}
|
||||
}
|
||||
|
||||
extension Node.Sequence: ExpressibleByArrayLiteral {
|
||||
public init(arrayLiteral elements: Node...) {
|
||||
self.init(nodes: elements, tag: .implicit, style: .any)
|
||||
}
|
||||
}
|
||||
|
||||
extension Node.Sequence: MutableCollection {
|
||||
// Sequence
|
||||
public func makeIterator() -> Array<Node>.Iterator {
|
||||
return nodes.makeIterator()
|
||||
}
|
||||
|
||||
// Collection
|
||||
public typealias Index = Array<Node>.Index
|
||||
|
||||
public var startIndex: Index {
|
||||
return nodes.startIndex
|
||||
}
|
||||
|
||||
public var endIndex: Index {
|
||||
return nodes.endIndex
|
||||
}
|
||||
|
||||
public func index(after index: Index) -> Index {
|
||||
return nodes.index(after: index)
|
||||
}
|
||||
|
||||
public subscript(index: Index) -> Node {
|
||||
get {
|
||||
return nodes[index]
|
||||
}
|
||||
// MutableCollection
|
||||
set {
|
||||
nodes[index] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
public subscript(bounds: Range<Index>) -> Array<Node>.SubSequence {
|
||||
get {
|
||||
return nodes[bounds]
|
||||
}
|
||||
// MutableCollection
|
||||
set {
|
||||
nodes[bounds] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
public var indices: Array<Node>.Indices {
|
||||
return nodes.indices
|
||||
}
|
||||
}
|
||||
|
||||
extension Node.Sequence: RandomAccessCollection {
|
||||
// BidirectionalCollection
|
||||
public func index(before index: Index) -> Index {
|
||||
return nodes.index(before: index)
|
||||
}
|
||||
|
||||
// RandomAccessCollection
|
||||
public func index(_ index: Index, offsetBy num: Int) -> Index {
|
||||
return nodes.index(index, offsetBy: num)
|
||||
}
|
||||
|
||||
public func distance(from start: Index, to end: Int) -> Index {
|
||||
return nodes.distance(from: start, to: end)
|
||||
}
|
||||
}
|
||||
|
||||
extension Node.Sequence: RangeReplaceableCollection {
|
||||
public init() {
|
||||
self.init(nodes: [], tag: .implicit, style: .any)
|
||||
}
|
||||
|
||||
public mutating func replaceSubrange<C>(_ subrange: Range<Int>, with newElements: C)
|
||||
where C : Collection, C.Iterator.Element == Node {
|
||||
nodes.replaceSubrange(subrange, with: newElements)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - internal
|
||||
|
||||
extension Node {
|
||||
// MARK: Internal convenience accessors
|
||||
var isScalar: Bool {
|
||||
|
@ -367,3 +555,5 @@ extension Node {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// swiftlint:disable:this file_length
|
||||
|
|
|
@ -110,15 +110,81 @@ class NodeTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testSubscriptMapping() {
|
||||
let mapping: Node = ["key1": "value1", "key2": "value2"]
|
||||
let valueForKey1 = mapping["key1"]?.string
|
||||
XCTAssertEqual(valueForKey1, "value1")
|
||||
let mapping: Node = ["key1": "value1", "key2": "value2", "key3": "value3"]
|
||||
XCTAssertEqual(mapping["key1" as Node]?.string, "value1") // Node resolvable as String
|
||||
XCTAssertEqual(mapping["key2"]?.string, "value2") // String Literal
|
||||
XCTAssertEqual(mapping[String("key3")]?.string, "value3") // String
|
||||
}
|
||||
|
||||
func testSubscriptSequence() {
|
||||
let mapping: Node = ["value1", "value2", "value3"]
|
||||
let valueAtSecond = mapping[1]?.string
|
||||
XCTAssertEqual(valueAtSecond, "value2")
|
||||
XCTAssertEqual(mapping["0" as Node]?.string, "value1") // Node resolvable as Integer
|
||||
XCTAssertEqual(mapping[1]?.string, "value2") // Integer Literal
|
||||
XCTAssertEqual(mapping[Int(2)]?.string, "value3") // Int
|
||||
}
|
||||
|
||||
func testMappingBehavesLikeADictionary() {
|
||||
let node: Node = ["key1": "value1", "key2": "value2"]
|
||||
let mapping = node.mapping!
|
||||
XCTAssertEqual(mapping.count, 2)
|
||||
XCTAssertEqual(mapping.endIndex, 2)
|
||||
XCTAssertTrue(mapping.first! == ("key1", "value1"))
|
||||
XCTAssertFalse(mapping.isEmpty)
|
||||
XCTAssertEqual(Set(mapping.keys), ["key1", "key2"])
|
||||
XCTAssertEqual(mapping.lazy.count, 2)
|
||||
XCTAssertEqual(mapping.startIndex, 0)
|
||||
XCTAssertEqual(mapping.underestimatedCount, 2)
|
||||
XCTAssertEqual(Set(mapping.values), ["value1", "value2"])
|
||||
|
||||
// subscript
|
||||
var mutableMapping = mapping
|
||||
mutableMapping["key3"] = "value3"
|
||||
XCTAssertEqual(mutableMapping["key3"], "value3")
|
||||
mutableMapping["key3"] = "value3changed"
|
||||
XCTAssertEqual(mutableMapping["key3"], "value3changed")
|
||||
mutableMapping["key3"] = nil
|
||||
XCTAssertNil(mutableMapping["key3"])
|
||||
|
||||
// iterator
|
||||
var iterator = mapping.makeIterator()
|
||||
XCTAssertTrue(iterator.next()! == ("key1", "value1"))
|
||||
XCTAssertTrue(iterator.next()! == ("key2", "value2"))
|
||||
XCTAssertNil(iterator.next())
|
||||
|
||||
// ExpressibleByDictionaryLiteral
|
||||
XCTAssertEqual(mapping, ["key1": "value1", "key2": "value2"])
|
||||
}
|
||||
|
||||
func testSequenceBehavesLikeAnArray() {
|
||||
let node: Node = ["value1", "value2", "value3"]
|
||||
let sequence = node.sequence!
|
||||
XCTAssertEqual(sequence.count, 3)
|
||||
XCTAssertEqual(sequence.endIndex, 3)
|
||||
XCTAssertEqual(sequence.first, "value1")
|
||||
XCTAssertFalse(sequence.isEmpty)
|
||||
XCTAssertEqual(sequence.last, "value3")
|
||||
XCTAssertEqual(sequence.lazy.count, 3)
|
||||
XCTAssertEqual(sequence.startIndex, 0)
|
||||
XCTAssertEqual(sequence.underestimatedCount, 3)
|
||||
|
||||
// subscript
|
||||
var mutableSequence = sequence
|
||||
mutableSequence.append("value4")
|
||||
XCTAssertEqual(mutableSequence.count, 4)
|
||||
XCTAssertEqual(mutableSequence[3], "value4")
|
||||
mutableSequence.remove(at: 2)
|
||||
XCTAssertEqual(mutableSequence.count, 3)
|
||||
XCTAssertEqual(Array(mutableSequence), ["value1", "value2", "value4"])
|
||||
|
||||
// iterator
|
||||
var iterator = sequence.makeIterator()
|
||||
XCTAssertEqual(iterator.next(), "value1")
|
||||
XCTAssertEqual(iterator.next(), "value2")
|
||||
XCTAssertEqual(iterator.next(), "value3")
|
||||
XCTAssertNil(iterator.next())
|
||||
|
||||
// ExpressibleByArrayLiteral
|
||||
XCTAssertEqual(sequence, ["value1", "value2", "value3"])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,9 +196,12 @@ extension NodeTests {
|
|||
("testExpressibleByFloatLiteral", testExpressibleByFloatLiteral),
|
||||
("testExpressibleByIntegerLiteral", testExpressibleByIntegerLiteral),
|
||||
("testExpressibleByStringLiteral", testExpressibleByStringLiteral),
|
||||
("testTypedAccessorProperties", testTypedAccessorProperties),
|
||||
("testArray", testArray),
|
||||
("testSubscriptMapping", testSubscriptMapping),
|
||||
("testSubscriptSequence", testSubscriptSequence),
|
||||
("testArray", testArray)
|
||||
("testMappingBehavesLikeADictionary", testMappingBehavesLikeADictionary),
|
||||
("testSequenceBehavesLikeAnArray", testSequenceBehavesLikeAnArray)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,11 +62,12 @@ class PerformanceTests: XCTestCase {
|
|||
XCTFail("Can't load \(filename)")
|
||||
return
|
||||
}
|
||||
let spmName = "SourceKittenFramework"
|
||||
self.measure {
|
||||
do {
|
||||
guard let yaml = try Yams.load(yaml: yamlString) as? [String:Any],
|
||||
let commands = yaml["commands"] as? [String:Any],
|
||||
let moduleCommand = commands["<SourceKittenFramework.module>"] as? [String:Any],
|
||||
let commands = (yaml["commands"] as? [String:[String:Any]])?.values,
|
||||
let moduleCommand = commands.first(where: { ($0["module-name"] as? String ?? "") == spmName }),
|
||||
let imports = moduleCommand["import-paths"] as? [String],
|
||||
let otherArguments = moduleCommand["other-args"] as? [String],
|
||||
let sources = moduleCommand["sources"] as? [String] else {
|
||||
|
@ -87,10 +88,12 @@ class PerformanceTests: XCTestCase {
|
|||
XCTFail("Can't load \(filename)")
|
||||
return
|
||||
}
|
||||
let spmName = "SourceKittenFramework"
|
||||
self.measure {
|
||||
do {
|
||||
guard let yaml = try Yams.compose(yaml: yamlString),
|
||||
let moduleCommand = yaml["commands"]?["<SourceKittenFramework.module>"],
|
||||
let commands = yaml["commands"]?.mapping?.values,
|
||||
let moduleCommand = commands.first(where: { $0["module-name"]?.string == spmName }),
|
||||
let imports = moduleCommand["import-paths"]?.array(of: String.self),
|
||||
let otherArguments = moduleCommand["other-args"]?.array(of: String.self),
|
||||
let sources = moduleCommand["sources"]?.array(of: String.self) else {
|
||||
|
|
Loading…
Reference in New Issue