Skip to content

Instantly share code, notes, and snippets.

@TellowKrinkle
Last active March 14, 2019 04:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TellowKrinkle/80a41f82b8652e3b90b39b20d31e2a37 to your computer and use it in GitHub Desktop.
Save TellowKrinkle/80a41f82b8652e3b90b39b20d31e2a37 to your computer and use it in GitHub Desktop.
StringScanner
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