-
-
Save TellowKrinkle/80a41f82b8652e3b90b39b20d31e2a37 to your computer and use it in GitHub Desktop.
StringScanner
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct StringScanner<T: StringProtocol> where T.SubSequence: StringProtocol { | |
let str: T | |
var pos: T.Index | |
init(_ string: T) { | |
self.str = string | |
self.pos = self.str.startIndex | |
} | |
func peek(toNext delimeter: Character) -> T.SubSequence? { | |
guard pos != str.endIndex else { return nil } | |
let end = str[pos...].index(of: delimeter) ?? str.endIndex | |
if end != pos { | |
return str[pos..<end] | |
} | |
return "" | |
} | |
mutating func read(toNext delimeter: Character, clearDelimeter: Bool = true) -> T.SubSequence? { | |
guard pos != str.endIndex else { return nil } | |
let end = str[pos...].index(of: delimeter) ?? str.endIndex | |
if end != pos { | |
defer { | |
if clearDelimeter && end != str.endIndex { | |
pos = str.index(after: end) | |
} | |
else { | |
pos = end | |
} | |
} | |
return str[pos..<end] | |
} | |
if clearDelimeter { | |
pos = str.index(after: pos) | |
} | |
return "" | |
} | |
func peek(while condition: (Character) -> Bool) -> T.SubSequence? { | |
guard pos != str.endIndex else { return nil } | |
let end = str[pos...].index(where: { !condition($0) }) ?? str.endIndex | |
return str[pos..<end] | |
} | |
mutating func read(while condition: (Character) -> Bool) -> T.SubSequence? { | |
guard pos != str.endIndex else { return nil } | |
let end = str[pos...].index(where: { !condition($0) }) ?? str.endIndex | |
defer { pos = end } | |
return str[pos..<end] | |
} | |
private static func setContainsChar(set: CharacterSet, char: Character) -> Bool { | |
for scalar in char.unicodeScalars { | |
if set.contains(scalar) { | |
return true | |
} | |
} | |
return false | |
} | |
func peek(untilInSet set: CharacterSet) -> T.SubSequence? { | |
return peek { !StringScanner.setContainsChar(set: set, char: $0) } | |
} | |
mutating func read(untilInSet set: CharacterSet, clearDelimeter: Bool = true) -> T.SubSequence? { | |
let string = read { !StringScanner.setContainsChar(set: set, char: $0) } | |
if clearDelimeter { | |
_ = read(whileInSet: set) | |
} | |
return string | |
} | |
func peek(whileInSet set: CharacterSet) -> T.SubSequence? { | |
return peek { StringScanner.setContainsChar(set: set, char: $0) } | |
} | |
mutating func read(whileInSet set: CharacterSet) -> T.SubSequence? { | |
return read { StringScanner.setContainsChar(set: set, char: $0) } | |
} | |
mutating func read(characterCount count: T.IndexDistance, shouldAdvancePos: Bool = true) -> T.SubSequence? { | |
guard str.count >= count else { return nil } | |
let newPos: T.Index = str.index(pos, offsetBy: count) | |
defer { if shouldAdvancePos { pos = newPos } } | |
return str[pos..<newPos] | |
} | |
mutating func remove(prefix: String, matchingCase: Bool = false) -> Bool { | |
guard str[pos...].count >= prefix.count else { return false } | |
let end = str.index(pos, offsetBy: T.IndexDistance(prefix.count)) | |
let strprefix = str[pos..<end] | |
if matchingCase { | |
guard prefix == strprefix else { return false } | |
} | |
else { | |
guard prefix.caseInsensitiveCompare(String(strprefix)) == .orderedSame else { return false } | |
} | |
pos = end | |
return true | |
} | |
var rest: T.SubSequence? { | |
guard pos != str.endIndex else { return nil } | |
return str[pos..<str.endIndex] | |
} | |
var isEmpty: Bool { | |
return pos == str.endIndex | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment