Porting String APIs to Subtring and fixing some tests

This commit is contained in:
Max Moiseev
2017-02-16 16:10:22 -08:00
parent 9a40996253
commit b1898ab768
6 changed files with 230 additions and 99 deletions

View File

@@ -158,6 +158,18 @@ public func expectEqual<T : Equatable, U : Equatable, V : Equatable, W : Equatab
expectEqualTest(expected.3, actual.3, ${trace}, showFrame: false) {$0 == $1} expectEqualTest(expected.3, actual.3, ${trace}, showFrame: false) {$0 == $1}
} }
% for (Lhs, Rhs) in [('String', 'Substring'), ('Substring', 'String')]:
public func expectEqual(_ expected: ${Lhs}, _ actual: ${Rhs}, ${TRACE}) {
if !(expected == actual) {
expectationFailure(
"expected: \(String(reflecting: expected)) (of type \(String(reflecting: type(of: expected))))\n"
+ "actual: \(String(reflecting: actual)) (of type \(String(reflecting: type(of: actual))))",
trace: ${trace}
)
}
}
% end
public func expectEqualReference(_ expected: AnyObject?, _ actual: AnyObject?, ${TRACE}) { public func expectEqualReference(_ expected: AnyObject?, _ actual: AnyObject?, ${TRACE}) {
expectEqualTest(expected, actual, ${trace}, showFrame: false) {$0 === $1} expectEqualTest(expected, actual, ${trace}, showFrame: false) {$0 === $1}
} }

View File

@@ -127,6 +127,7 @@ set(SWIFTLIB_ESSENTIAL
StringUnicodeScalarView.swift StringUnicodeScalarView.swift
StringUTF16.swift StringUTF16.swift
StringUTF8.swift StringUTF8.swift
Substring.swift.gyb
SwiftNativeNSArray.swift SwiftNativeNSArray.swift
UnavailableStringAPIs.swift.gyb UnavailableStringAPIs.swift.gyb
Unicode.swift Unicode.swift

View File

@@ -21,6 +21,7 @@
"StringUTF16.swift", "StringUTF16.swift",
"StringUTF8.swift", "StringUTF8.swift",
"StringUnicodeScalarView.swift", "StringUnicodeScalarView.swift",
"Substring.swift",
"Unicode.swift", "Unicode.swift",
"UnicodeScalar.swift", "UnicodeScalar.swift",
"UnicodeTrie.swift", "UnicodeTrie.swift",

View File

@@ -10,98 +10,6 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
public struct Substring : RangeReplaceableCollection {
public typealias Index = String.CharacterView.Index
public typealias IndexDistance = String.CharacterView.IndexDistance
internal let base: String
internal var bounds: Range<Index>
public init() {
let s = ""
self.init(s, s.startIndex..<s.endIndex)
}
internal init(_ base: String, _ bounds: Range<Index>) {
self.base = base
self.bounds = bounds
}
internal init(_ base: String, _ bounds: ClosedRange<Index>) {
self.init(base, base._makeHalfOpen(bounds))
}
public var startIndex: Index { return bounds.lowerBound }
public var endIndex: Index { return bounds.upperBound }
public func index(after i: Index) -> Index {
_precondition(i < bounds.upperBound, "Cannot increment beyond endIndex")
_precondition(i >= bounds.lowerBound, "Cannot increment beyond startIndex")
return base.characters.index(after: i)
}
// TODO: swift-3-indexing-model - add docs
public func index(before i: Index) -> Index {
_precondition(i <= bounds.upperBound, "Cannot decrement an invalid index")
_precondition(i > bounds.lowerBound, "Cannot decrement beyond startIndex")
return base.characters.index(before: i)
}
public func index(_ i: Index, offsetBy n: IndexDistance) -> Index {
let result = base.characters.index(i, offsetBy: n)
_precondition(bounds.contains(result),
"Operation results in an invalid index")
return result
}
public func index(
_ i: Index, offsetBy n: IndexDistance, limitedBy limit: Index
) -> Index? {
let result = base.characters.index(i, offsetBy: n, limitedBy: limit)
_precondition(result.map { bounds.contains($0) } ?? true,
"Operation results in an invalid index")
return result
}
public func distance(from start: Index, to end: Index) -> IndexDistance {
return base.characters.distance(from: start, to: end)
}
public subscript(i: Index) -> Character {
_precondition(bounds.contains(i), "Invalid index")
return base.characters[i]
}
public subscript(bounds: Range<Index>) -> Substring {
// FIXME(strings): add checks
return Substring(base, bounds)
}
public subscript(bounds: ClosedRange<Index>) -> Substring {
// FIXME(strings): add checks
return Substring(base, bounds)
}
% for Range in ['Range', 'ClosedRange']:
public mutating func replaceSubrange<C>(
_ bounds: ${Range}<Index>,
with newElements: C
) where C : Collection, C.Iterator.Element == Character {
fatalError()
}
public mutating func replaceSubrange(
_ bounds: ${Range}<Index>, with newElements: Substring
) {
replaceSubrange(bounds, with: newElements.base.characters)
}
% end
}
extension String : RangeReplaceableCollection, BidirectionalCollection { extension String : RangeReplaceableCollection, BidirectionalCollection {
/// The index type for subscripting a string. /// The index type for subscripting a string.
public typealias Index = CharacterView.Index public typealias Index = CharacterView.Index

View File

@@ -0,0 +1,207 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
public struct Substring : RangeReplaceableCollection {
public typealias Index = String.CharacterView.Index
public typealias IndexDistance = String.CharacterView.IndexDistance
public let base: String
public internal(set) var bounds: Range<Index>
public init() {
let s = ""
self.init(s, s.startIndex..<s.endIndex)
}
internal init(_ base: String, _ bounds: Range<Index>) {
self.base = base
self.bounds = bounds
}
internal init(_ base: String, _ bounds: ClosedRange<Index>) {
self.init(base, base._makeHalfOpen(bounds))
}
public var startIndex: Index { return bounds.lowerBound }
public var endIndex: Index { return bounds.upperBound }
public func index(after i: Index) -> Index {
_precondition(i < bounds.upperBound, "Cannot increment beyond endIndex")
_precondition(i >= bounds.lowerBound, "Cannot increment beyond startIndex")
return base.characters.index(after: i)
}
// TODO: swift-3-indexing-model - add docs
public func index(before i: Index) -> Index {
_precondition(i <= bounds.upperBound, "Cannot decrement an invalid index")
_precondition(i > bounds.lowerBound, "Cannot decrement beyond startIndex")
return base.characters.index(before: i)
}
public func index(_ i: Index, offsetBy n: IndexDistance) -> Index {
let result = base.characters.index(i, offsetBy: n)
_precondition(bounds.contains(result),
"Operation results in an invalid index")
return result
}
public func index(
_ i: Index, offsetBy n: IndexDistance, limitedBy limit: Index
) -> Index? {
let result = base.characters.index(i, offsetBy: n, limitedBy: limit)
_precondition(result.map { bounds.contains($0) } ?? true,
"Operation results in an invalid index")
return result
}
public func distance(from start: Index, to end: Index) -> IndexDistance {
return base.characters.distance(from: start, to: end)
}
public subscript(i: Index) -> Character {
_precondition(bounds.contains(i), "Invalid index")
return base.characters[i]
}
public subscript(bounds: Range<Index>) -> Substring {
// FIXME(strings): add checks
return Substring(base, bounds)
}
public subscript(bounds: ClosedRange<Index>) -> Substring {
// FIXME(strings): add checks
return Substring(base, bounds)
}
public mutating func replaceSubrange<C>(
_ bounds: Range<Index>,
with newElements: C
) where C : Collection, C.Iterator.Element == Iterator.Element {
fatalError()
}
% for Range in ['Range', 'ClosedRange']:
public mutating func replaceSubrange(
_ bounds: ${Range}<Index>, with newElements: Substring
) {
replaceSubrange(bounds, with: newElements.base.characters)
}
% end
}
extension Substring : CustomReflectable {
public var customMirror: Mirror {
return String(self).customMirror
}
}
extension Substring : CustomPlaygroundQuickLookable {
public var customPlaygroundQuickLook: PlaygroundQuickLook {
return String(self).customPlaygroundQuickLook
}
}
extension Substring : CustomStringConvertible {
public var description: String {
return String(self)
}
}
extension Substring : LosslessStringConvertible {
public init?(_ description: String) {
self.init(description, description.startIndex ..< description.endIndex)
}
}
extension Substring : Equatable {
public static func ==(lhs: Substring, rhs: Substring) -> Bool {
return String(lhs) == String(rhs)
}
// These are not Equatable requirements, but sufficiently similar to be in
// this extension.
// FIXME(strings): should be gone if/when an implicit conversion from/to
// String is available.
// FIXME(ABI):
public static func ==(lhs: String, rhs: Substring) -> Bool {
return lhs == String(rhs)
}
public static func ==(lhs: Substring, rhs: String) -> Bool {
return String(lhs) == rhs
}
public static func !=(lhs: String, rhs: Substring) -> Bool {
return lhs != String(rhs)
}
public static func !=(lhs: Substring, rhs: String) -> Bool {
return String(lhs) != rhs
}
}
extension Substring : Comparable {
public static func <(lhs: Substring, rhs: Substring) -> Bool {
return String(lhs) < String(rhs)
}
}
extension Substring : Hashable {
public var hashValue : Int {
return String(self).hashValue
}
}
extension Substring {
% for (property, ViewPrefix) in [
% ('utf8', 'UTF8'),
% ('utf16', 'UTF16'),
% ('unicodeScalars', 'UnicodeScalar')]:
public typealias ${ViewPrefix}Index = String.${ViewPrefix}View.Index
public var ${property}: String.${ViewPrefix}View {
get {
return String(self).${property}
}
set {
let base = String(describing: newValue)
self = Substring(base, base.startIndex ..< base.endIndex)
}
}
% end
}
extension Substring {
public func hasPrefix(_ prefix: String) -> Bool {
return String(self).hasPrefix(prefix)
}
public func hasSuffix(_ suffix: String) -> Bool {
return String(self).hasSuffix(suffix)
}
}
extension Substring {
public func lowercased() -> String {
return String(self).lowercased()
}
public func uppercased() -> String {
return String(self).uppercased()
}
}

View File

@@ -530,8 +530,8 @@ NSStringAPIs.test("enumerateLinguisticTagsIn(_:scheme:options:orthography:_:") {
(tag: String, tokenRange: Range<String.Index>, sentenceRange: Range<String.Index>, stop: inout Bool) (tag: String, tokenRange: Range<String.Index>, sentenceRange: Range<String.Index>, stop: inout Bool)
in in
tags.append(tag) tags.append(tag)
tokens.append(s[tokenRange]) tokens.append(String(s[tokenRange]))
sentences.append(s[sentenceRange]) sentences.append(String(s[sentenceRange]))
if tags.count == 3 { if tags.count == 3 {
stop = true stop = true
} }
@@ -540,7 +540,7 @@ NSStringAPIs.test("enumerateLinguisticTagsIn(_:scheme:options:orthography:_:") {
[NSLinguisticTagWord, NSLinguisticTagWhitespace, NSLinguisticTagWord], [NSLinguisticTagWord, NSLinguisticTagWhitespace, NSLinguisticTagWord],
tags) tags)
expectEqual(["Глокая", " ", "куздра"], tokens) expectEqual(["Глокая", " ", "куздра"], tokens)
let sentence = s[startIndex..<endIndex] let sentence = String(s[startIndex..<endIndex])
expectEqual([sentence, sentence, sentence], sentences) expectEqual([sentence, sentence, sentence], sentences)
} }
@@ -550,14 +550,16 @@ NSStringAPIs.test("enumerateSubstringsIn(_:options:_:)") {
let endIndex = s.index(s.startIndex, offsetBy: 5) let endIndex = s.index(s.startIndex, offsetBy: 5)
do { do {
var substrings: [String] = [] var substrings: [String] = []
// FIXME(strings): this API should probably change to accept a Substring?
// instead of a String? and a range.
s.enumerateSubstrings(in: startIndex..<endIndex, s.enumerateSubstrings(in: startIndex..<endIndex,
options: String.EnumerationOptions.byComposedCharacterSequences) { options: String.EnumerationOptions.byComposedCharacterSequences) {
(substring: String?, substringRange: Range<String.Index>, (substring: String?, substringRange: Range<String.Index>,
enclosingRange: Range<String.Index>, stop: inout Bool) enclosingRange: Range<String.Index>, stop: inout Bool)
in in
substrings.append(substring!) substrings.append(substring!)
expectEqual(substring, s[substringRange]) expectEqual(substring, String(s[substringRange]))
expectEqual(substring, s[enclosingRange]) expectEqual(substring, String(s[enclosingRange]))
} }
expectEqual(["\u{304b}\u{3099}", "", "☺️", "😀"], substrings) expectEqual(["\u{304b}\u{3099}", "", "☺️", "😀"], substrings)
} }
@@ -569,7 +571,7 @@ NSStringAPIs.test("enumerateSubstringsIn(_:options:_:)") {
enclosingRange: Range<String.Index>, stop: inout Bool) enclosingRange: Range<String.Index>, stop: inout Bool)
in in
expectNil(substring_) expectNil(substring_)
let substring = s[substringRange] let substring = String(s[substringRange])
substrings.append(substring) substrings.append(substring)
expectEqual(substring, s[enclosingRange]) expectEqual(substring, s[enclosingRange])
} }
@@ -873,7 +875,7 @@ NSStringAPIs.test("linguisticTagsIn(_:scheme:options:orthography:tokenRanges:)")
[NSLinguisticTagWord, NSLinguisticTagWhitespace, NSLinguisticTagWord], [NSLinguisticTagWord, NSLinguisticTagWhitespace, NSLinguisticTagWord],
tags) tags)
expectEqual(["Глокая", " ", "куздра"], expectEqual(["Глокая", " ", "куздра"],
tokenRanges.map { s[$0] } ) tokenRanges.map { String(s[$0]) } )
} }
NSStringAPIs.test("localizedCaseInsensitiveCompare(_:)") { NSStringAPIs.test("localizedCaseInsensitiveCompare(_:)") {