diff options
Diffstat (limited to 'Sources/WmapParser/KeywordParser.swift')
| -rw-r--r-- | Sources/WmapParser/KeywordParser.swift | 306 |
1 files changed, 154 insertions, 152 deletions
diff --git a/Sources/WmapParser/KeywordParser.swift b/Sources/WmapParser/KeywordParser.swift index b560d6a..25f0ecd 100644 --- a/Sources/WmapParser/KeywordParser.swift +++ b/Sources/WmapParser/KeywordParser.swift @@ -1,186 +1,188 @@ extension WmapParser { - /// Checks which keyword we're dealing with and routes it to the right parser. - static func parseKeywordLine<T: Collection>( - _ bytes: T, _ start: Int, _ end: Int, - _ context: inout KeywordParserContext - ) where T.Element == UInt8, T.Index == Int { - guard let (keyword, rest) = extractKeyword(bytes, start, end) else { return } + /// Checks which keyword we're dealing with and routes it to the right parser. + static func parseKeywordLine<T: Collection>( + _ bytes: T, _ start: Int, _ end: Int, + _ context: inout KeywordParserContext + ) where T.Element == UInt8, T.Index == Int { + guard let (keyword, rest) = extractKeyword(bytes, start, end) else { return } - if matchesKeyword(keyword, kInertiaKeyword), let inertia = parseInertia(bytes, rest, end) { - context.inertias.append(inertia) - } else if matchesKeyword(keyword, kEvolutionKeyword), - let evolution = parseEvolution(bytes, rest, end) { - context.evolutions.append(evolution) - } else if matchesKeyword(keyword, kNoteKeyword), let note = parseNote(bytes, rest, end) { - context.notes.append(note) - } else if matchesKeyword(keyword, kGroupKeyword), let group = parseGroup(bytes, rest, end) { - context.groups.append(group) - } else if let stageNumber = isStageKeyword(keyword), - let stage = parseStage(bytes, stageNumber, rest, end) { - context.stages.append(stage) - } + if matchesKeyword(keyword, kInertiaKeyword), let inertia = parseInertia(bytes, rest, end) { + context.inertias.append(inertia) + } else if matchesKeyword(keyword, kEvolutionKeyword), + let evolution = parseEvolution(bytes, rest, end) { + context.evolutions.append(evolution) + } else if matchesKeyword(keyword, kNoteKeyword), let note = parseNote(bytes, rest, end) { + context.notes.append(note) + } else if matchesKeyword(keyword, kGroupKeyword), let group = parseGroup(bytes, rest, end) { + context.groups.append(group) + } else if let stageNumber = isStageKeyword(keyword), + let stage = parseStage(bytes, stageNumber, rest, end) { + context.stages.append(stage) } + } - /// Extracts a keyword from inside brackets. + the index of the rest of the - /// line. - @inline(__always) - static func extractKeyword<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> (T.SubSequence, Int)? - where T.Element == UInt8, T.Index == Int { - var index = start + 1 // Skip '[' - let keywordStart = index + /// Extracts a keyword from inside brackets. + the index of the rest of the + /// line. + @inline(__always) + static func extractKeyword<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> ( + T.SubSequence, Int + )? + where T.Element == UInt8, T.Index == Int { + var index = start + 1 // Skip '[' + let keywordStart = index - while index < end && bytes[index] != kCloseSquareBracket { index += 1 } // Find ']' - if index >= end { return nil } + while index < end && bytes[index] != kCloseSquareBracket { index += 1 } // Find ']' + if index >= end { return nil } - let keywordEnd = index - index += 1 + let keywordEnd = index + index += 1 - // Skip whitespace after ']' - index = skipWhitespace(bytes, end, index) + // Skip whitespace after ']' + index = skipWhitespace(bytes, end, index) - return (bytes[keywordStart..<keywordEnd], index) - } + return (bytes[keywordStart..<keywordEnd], index) + } - /// Checks the four potential values for the stage keyword. - @inline(__always) - static func isStageKeyword<T: Collection>(_ keyword: T) -> StageNumber? - where T.Element == UInt8, T.Index == Int { - if matchesKeyword(keyword, kIKeyword) { - return .stageI - } - if matchesKeyword(keyword, kIIKeyword) { - return .stageII - } - if matchesKeyword(keyword, kIIIKeyword) { - return .stageIII - } - if matchesKeyword(keyword, kIVKeyword) { - return .stageIV - } - return nil - } + /// Checks the four potential values for the stage keyword. + @inline(__always) + static func isStageKeyword<T: Collection>(_ keyword: T) -> StageNumber? + where T.Element == UInt8, T.Index == Int { + if matchesKeyword(keyword, kIKeyword) { + return .stageI + } + if matchesKeyword(keyword, kIIKeyword) { + return .stageII + } + if matchesKeyword(keyword, kIIIKeyword) { + return .stageIII + } + if matchesKeyword(keyword, kIVKeyword) { + return .stageIV + } + return nil + } - /// Compares a collection of bytes against a list of bytes. - /// Short-Circuits by length, only two have the same length (ii and iv) - @inline(__always) - static func matchesKeyword<T: Collection>(_ keyword: T, _ expected: [UInt8]) -> Bool - where T.Element == UInt8, T.Index == Int { - guard keyword.count == expected.count else { return false } - var keywordIndex = keyword.startIndex - for index in 0..<expected.count { - let currentKeyword = keyword[keywordIndex] - let expectedKeyword = expected[index] + /// Compares a collection of bytes against a list of bytes. + /// Short-Circuits by length, only two have the same length (ii and iv) + @inline(__always) + static func matchesKeyword<T: Collection>(_ keyword: T, _ expected: [UInt8]) -> Bool + where T.Element == UInt8, T.Index == Int { + guard keyword.count == expected.count else { return false } + var keywordIndex = keyword.startIndex + for index in 0..<expected.count { + let currentKeyword = keyword[keywordIndex] + let expectedKeyword = expected[index] - let lowercasedKeyword = - (currentKeyword >= kCapitalA && currentKeyword <= kCapitalZ) - ? currentKeyword + kCapsToLowercaseDistance : currentKeyword - if lowercasedKeyword != expectedKeyword { return false } - keywordIndex += 1 - } - return true - } + let lowercasedKeyword = + (currentKeyword >= kCapitalA && currentKeyword <= kCapitalZ) + ? currentKeyword + kCapsToLowercaseDistance : currentKeyword + if lowercasedKeyword != expectedKeyword { return false } + keywordIndex += 1 + } + return true + } - /// Parses the stage. Assumes we got the stage number in the keyword check - /// to avoid checking twice. - static func parseStage<T: Collection>( - _ bytes: T, _ stageNumber: StageNumber, _ start: Int, _ end: Int - ) -> Stage? - where T.Element == UInt8, T.Index == Int { - guard let value = parseDouble(bytes, start, end) else { return nil } + /// Parses the stage. Assumes we got the stage number in the keyword check + /// to avoid checking twice. + static func parseStage<T: Collection>( + _ bytes: T, _ stageNumber: StageNumber, _ start: Int, _ end: Int + ) -> Stage? + where T.Element == UInt8, T.Index == Int { + guard let value = parseDouble(bytes, start, end) else { return nil } - return Stage(stage: stageNumber, value: value) - } + return Stage(stage: stageNumber, value: value) + } - /// Parses the note coordinates and text. - static func parseNote<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Note? - where T.Element == UInt8, T.Index == Int { - var index = skipWhitespace(bytes, end, start) + /// Parses the note coordinates and text. + static func parseNote<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Note? + where T.Element == UInt8, T.Index == Int { + var index = skipWhitespace(bytes, end, start) - guard index < end && bytes[index] == kOpenParenthesis else { return nil } - index += 1 + guard index < end && bytes[index] == kOpenParenthesis else { return nil } + index += 1 - guard let (coordinates, endIndex) = parseCoordinates(bytes, index, end) else { - return nil - } - index = skipWhitespace(bytes, end, endIndex) + guard let (coordinates, endIndex) = parseCoordinates(bytes, index, end) else { + return nil + } + index = skipWhitespace(bytes, end, endIndex) - let textEnd = shaveWhitespace(bytes, index, end) + let textEnd = shaveWhitespace(bytes, index, end) - // Why not String(data:encoding:)? This is much faster. - // swiftlint:disable:next optional_data_string_conversion - let text = String(decoding: bytes[index..<textEnd], as: UTF8.self) - return Note(text: text, coordinates: coordinates) - } + // Why not String(data:encoding:)? This is much faster. + // swiftlint:disable:next optional_data_string_conversion + let text = String(decoding: bytes[index..<textEnd], as: UTF8.self) + return Note(text: text, coordinates: coordinates) + } - /// Parses the comma separated labels for a group - static func parseGroup<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Group? - where T.Element == UInt8, T.Index == Int { - var components = [String]() - var index = start - var componentStart = start + /// Parses the comma separated labels for a group + static func parseGroup<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Group? + where T.Element == UInt8, T.Index == Int { + var components = [String]() + var index = start + var componentStart = start - while index <= end { - if index == end || bytes[index] == kComma { - let trimStart = skipWhitespace(bytes, index, componentStart) - let trimEnd = shaveWhitespace(bytes, trimStart, index) + while index <= end { + if index == end || bytes[index] == kComma { + let trimStart = skipWhitespace(bytes, index, componentStart) + let trimEnd = shaveWhitespace(bytes, trimStart, index) - if trimStart < trimEnd { - // Why not String(data:encoding:)? This is much faster. - // swiftlint:disable:next optional_data_string_conversion - let component = String(decoding: bytes[trimStart..<trimEnd], as: UTF8.self) - components.append(component) - } + if trimStart < trimEnd { + // Why not String(data:encoding:)? This is much faster. + // swiftlint:disable:next optional_data_string_conversion + let component = String(decoding: bytes[trimStart..<trimEnd], as: UTF8.self) + components.append(component) + } - componentStart = index + 1 - } - index += 1 - } + componentStart = index + 1 + } + index += 1 + } - return components.isEmpty ? nil : Group(components: components) - } + return components.isEmpty ? nil : Group(components: components) + } - /// Parses the component label for inertia - static func parseInertia<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Inertia? - where T.Element == UInt8, T.Index == Int { - let trimStart = skipWhitespace(bytes, end, start) - let trimEnd = shaveWhitespace(bytes, trimStart, end) + /// Parses the component label for inertia + static func parseInertia<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Inertia? + where T.Element == UInt8, T.Index == Int { + let trimStart = skipWhitespace(bytes, end, start) + let trimEnd = shaveWhitespace(bytes, trimStart, end) - guard trimStart < trimEnd else { return nil } + guard trimStart < trimEnd else { return nil } - // Why not String(data:encoding:)? This is much faster. - // swiftlint:disable:next optional_data_string_conversion - let component = String(decoding: bytes[trimStart..<trimEnd], as: UTF8.self) - return Inertia(component: component) - } + // Why not String(data:encoding:)? This is much faster. + // swiftlint:disable:next optional_data_string_conversion + let component = String(decoding: bytes[trimStart..<trimEnd], as: UTF8.self) + return Inertia(component: component) + } - /// Parses the component label and value for inertia - static func parseEvolution<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Evolution? - where T.Element == UInt8, T.Index == Int { - var signIndex: Int? - var sign = 1.0 + /// Parses the component label and value for inertia + static func parseEvolution<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Evolution? + where T.Element == UInt8, T.Index == Int { + var signIndex: Int? + var sign = 1.0 - var index = start - while index < end { - if bytes[index] == kPlus || bytes[index] == kDash { - signIndex = index - sign = bytes[index] == 45 ? -1.0 : 1.0 - break - } - index += 1 - } + var index = start + while index < end { + if bytes[index] == kPlus || bytes[index] == kDash { + signIndex = index + sign = bytes[index] == 45 ? -1.0 : 1.0 + break + } + index += 1 + } - guard let signIndex, signIndex > start else { return nil } + guard let signIndex, signIndex > start else { return nil } - let trimStart = skipWhitespace(bytes, signIndex, start) - let trimEnd = shaveWhitespace(bytes, trimStart, signIndex) + let trimStart = skipWhitespace(bytes, signIndex, start) + let trimEnd = shaveWhitespace(bytes, trimStart, signIndex) - guard trimStart < trimEnd else { return nil } + guard trimStart < trimEnd else { return nil } - // Why not String(data:encoding:)? This is much faster. - // swiftlint:disable:next optional_data_string_conversion - let component = String(decoding: bytes[trimStart..<trimEnd], as: UTF8.self) - guard let value = parseDouble(bytes, signIndex + 1, end) else { return nil } + // Why not String(data:encoding:)? This is much faster. + // swiftlint:disable:next optional_data_string_conversion + let component = String(decoding: bytes[trimStart..<trimEnd], as: UTF8.self) + guard let value = parseDouble(bytes, signIndex + 1, end) else { return nil } - return Evolution(component: component, value: sign * value) - } + return Evolution(component: component, value: sign * value) + } } |