aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--Sources/WmapParser/ComponentParser.swift122
-rw-r--r--Sources/WmapParser/Constants.swift62
-rw-r--r--Sources/WmapParser/DataStructures/Component.swift12
-rw-r--r--Sources/WmapParser/DataStructures/Dependency.swift12
-rw-r--r--Sources/WmapParser/DataStructures/Evolution.swift10
-rw-r--r--Sources/WmapParser/DataStructures/Group.swift8
-rw-r--r--Sources/WmapParser/DataStructures/Inertia.swift8
-rw-r--r--Sources/WmapParser/DataStructures/Map.swift20
-rw-r--r--Sources/WmapParser/DataStructures/MapPoint.swift10
-rw-r--r--Sources/WmapParser/DataStructures/Note.swift10
-rw-r--r--Sources/WmapParser/DataStructures/Shape.swift16
-rw-r--r--Sources/WmapParser/DataStructures/Stage.swift10
-rw-r--r--Sources/WmapParser/DataStructures/StageNumber.swift16
-rw-r--r--Sources/WmapParser/DependencyParser.swift89
-rw-r--r--Sources/WmapParser/KeywordParser.swift306
-rw-r--r--Sources/WmapParser/ParserContext/KeywordParserContext.swift14
-rw-r--r--Sources/WmapParser/ParserContext/LineParserContext.swift10
-rw-r--r--Sources/WmapParser/Utils.swift226
-rw-r--r--Sources/WmapParser/WmapParser.swift125
-rw-r--r--Tests/WmapParserTests/ComponentParserTests.swift21
-rw-r--r--Tests/WmapParserTests/DependenciesParserTests.swift3
-rw-r--r--Tests/WmapParserTests/NotesParserTests.swift16
-rw-r--r--Tests/WmapParserTests/WmapParserTests.swift39
24 files changed, 604 insertions, 562 deletions
diff --git a/Makefile b/Makefile
index 14a2e2c..f962113 100644
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,7 @@ benchmark:
swift package benchmark --target ParseBenchmarkTarget
format:
+ swift-format --in-place --recursive Sources Tests
swiftlint --fix Sources Tests
lint:
diff --git a/Sources/WmapParser/ComponentParser.swift b/Sources/WmapParser/ComponentParser.swift
index 66979eb..79f5493 100644
--- a/Sources/WmapParser/ComponentParser.swift
+++ b/Sources/WmapParser/ComponentParser.swift
@@ -1,81 +1,83 @@
extension WmapParser {
- /// Parses a component with its coordinates.
- static func parseComponent<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Component?
- where T.Element == UInt8, T.Index == Int {
- guard let (label, parenthesisIndex) = extractComponentLabel(bytes, start, end) else {
- return nil
- }
+ /// Parses a component with its coordinates.
+ static func parseComponent<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Component?
+ where T.Element == UInt8, T.Index == Int {
+ guard let (label, parenthesisIndex) = extractComponentLabel(bytes, start, end) else {
+ return nil
+ }
- guard
- let (coordinates, endIndex) = parseCoordinates(
- bytes, parenthesisIndex + 1, end)
- else {
- return nil
- }
+ guard
+ let (coordinates, endIndex) = parseCoordinates(
+ bytes, parenthesisIndex + 1, end)
+ else {
+ return nil
+ }
- let shape = extractComponentShape(bytes, endIndex, end)
+ let shape = extractComponentShape(bytes, endIndex, end)
- return Component(label: label, coordinates: coordinates, shape: shape)
- }
+ return Component(label: label, coordinates: coordinates, shape: shape)
+ }
- /// Looks for a component label by taking the string before the first open
- /// parenthesis.
- @inline(__always)
- private static func extractComponentLabel<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> (
+ /// Looks for a component label by taking the string before the first open
+ /// parenthesis.
+ @inline(__always)
+ private static func extractComponentLabel<T: Collection>(_ bytes: T, _ start: Int, _ end: Int)
+ -> (
String, Int
)?
- where T.Element == UInt8, T.Index == Int {
- var index = start
- while index < end {
- if bytes[index] == kOpenParenthesis {
- guard index > start else { return nil }
- guard let label = extractTrimmedString(bytes, start, index) else { return nil }
- return (label, index)
- }
- index += 1
+ where T.Element == UInt8, T.Index == Int {
+ var index = start
+ while index < end {
+ if bytes[index] == kOpenParenthesis {
+ guard index > start else { return nil }
+ guard let label = extractTrimmedString(bytes, start, index) else { return nil }
+ return (label, index)
}
- return nil
+ index += 1
}
+ return nil
+ }
- /// Extracts a shape by looking for a shape label in square brackets.
- /// If missing, incomplete or invalid, it returns a circle
- @inline(__always)
- private static func extractComponentShape<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Shape
- where T.Element == UInt8, T.Index == Int {
- var index = skipWhitespace(bytes, end, start)
+ /// Extracts a shape by looking for a shape label in square brackets.
+ /// If missing, incomplete or invalid, it returns a circle
+ @inline(__always)
+ private static func extractComponentShape<T: Collection>(_ bytes: T, _ start: Int, _ end: Int)
+ -> Shape
+ where T.Element == UInt8, T.Index == Int {
+ var index = skipWhitespace(bytes, end, start)
- guard index < end && bytes[index] == kOpenSquareBracket else { return .circle }
+ guard index < end && bytes[index] == kOpenSquareBracket else { return .circle }
- index += 1
- let shapeStart = index
- while index < end && bytes[index] != kCloseSquareBracket { index += 1 }
+ index += 1
+ let shapeStart = index
+ while index < end && bytes[index] != kCloseSquareBracket { index += 1 }
- guard index > shapeStart && index < end else { return .circle }
+ guard index > shapeStart && index < end else { return .circle }
- let shapeName = bytes[shapeStart..<index]
- return parseShapeFromBytes(shapeName)
- }
+ let shapeName = bytes[shapeStart..<index]
+ return parseShapeFromBytes(shapeName)
+ }
- /// Given the contents of a shape label, return the right type.
- @inline(__always)
- private static func parseShapeFromBytes<T: Collection>(_ shapeName: T) -> Shape
- where T.Element == UInt8, T.Index == Int {
- var trimStart = shapeName.startIndex
- var trimEnd = shapeName.endIndex
+ /// Given the contents of a shape label, return the right type.
+ @inline(__always)
+ private static func parseShapeFromBytes<T: Collection>(_ shapeName: T) -> Shape
+ where T.Element == UInt8, T.Index == Int {
+ var trimStart = shapeName.startIndex
+ var trimEnd = shapeName.endIndex
- trimStart = skipWhitespace(shapeName, trimEnd, trimStart)
- trimEnd = shaveWhitespace(shapeName, trimStart, trimEnd)
+ trimStart = skipWhitespace(shapeName, trimEnd, trimStart)
+ trimEnd = shaveWhitespace(shapeName, trimStart, trimEnd)
- let trimmedName = shapeName[trimStart..<trimEnd]
+ let trimmedName = shapeName[trimStart..<trimEnd]
- // "x"
- if matchesKeyword(trimmedName, [120]) { return .xMark }
- // "square"
- if matchesKeyword(trimmedName, [115, 113, 117, 97, 114, 101]) { return .square }
- // "triangle"
- if matchesKeyword(trimmedName, [116, 114, 105, 97, 110, 103, 108, 101]) { return .triangle }
+ // "x"
+ if matchesKeyword(trimmedName, [120]) { return .xMark }
+ // "square"
+ if matchesKeyword(trimmedName, [115, 113, 117, 97, 114, 101]) { return .square }
+ // "triangle"
+ if matchesKeyword(trimmedName, [116, 114, 105, 97, 110, 103, 108, 101]) { return .triangle }
- return .circle
- }
+ return .circle
+ }
}
diff --git a/Sources/WmapParser/Constants.swift b/Sources/WmapParser/Constants.swift
index e659c03..8646243 100644
--- a/Sources/WmapParser/Constants.swift
+++ b/Sources/WmapParser/Constants.swift
@@ -1,37 +1,37 @@
extension WmapParser {
- // Whitespace
- static let kNewline = 10
- static let kCarriageReturn = 13
- static let kTab = 9
- static let kSpace = 32
+ // Whitespace
+ static let kNewline = 10
+ static let kCarriageReturn = 13
+ static let kTab = 9
+ static let kSpace = 32
- // Punctuation
- static let kOpenParenthesis = 40
- static let kCloseParenthesis = 41
- static let kPlus = 43
- static let kComma = 44
- static let kDash = 45
- static let kPeriod = 46
- static let kGreaterThan = 62
- static let kOpenSquareBracket = 91
- static let kCloseSquareBracket = 93
+ // Punctuation
+ static let kOpenParenthesis = 40
+ static let kCloseParenthesis = 41
+ static let kPlus = 43
+ static let kComma = 44
+ static let kDash = 45
+ static let kPeriod = 46
+ static let kGreaterThan = 62
+ static let kOpenSquareBracket = 91
+ static let kCloseSquareBracket = 93
- // Letters
- static let kCapitalA = 65
- static let kCapitalZ = 90
- static let kCapsToLowercaseDistance: UInt8 = 32
+ // Letters
+ static let kCapitalA = 65
+ static let kCapitalZ = 90
+ static let kCapsToLowercaseDistance: UInt8 = 32
- // Numbers
- static let kZero = 48
- static let kNine = 57
+ // Numbers
+ static let kZero = 48
+ static let kNine = 57
- // Keywords. ASCII representations of the keywords.
- static let kInertiaKeyword: [UInt8] = [105, 110, 101, 114, 116, 105, 97]
- static let kEvolutionKeyword: [UInt8] = [101, 118, 111, 108, 117, 116, 105, 111, 110]
- static let kNoteKeyword: [UInt8] = [110, 111, 116, 101]
- static let kGroupKeyword: [UInt8] = [103, 114, 111, 117, 112]
- static let kIKeyword: [UInt8] = [105]
- static let kIIKeyword: [UInt8] = [105, 105]
- static let kIIIKeyword: [UInt8] = [105, 105, 105]
- static let kIVKeyword: [UInt8] = [105, 118]
+ // Keywords. ASCII representations of the keywords.
+ static let kInertiaKeyword: [UInt8] = [105, 110, 101, 114, 116, 105, 97]
+ static let kEvolutionKeyword: [UInt8] = [101, 118, 111, 108, 117, 116, 105, 111, 110]
+ static let kNoteKeyword: [UInt8] = [110, 111, 116, 101]
+ static let kGroupKeyword: [UInt8] = [103, 114, 111, 117, 112]
+ static let kIKeyword: [UInt8] = [105]
+ static let kIIKeyword: [UInt8] = [105, 105]
+ static let kIIIKeyword: [UInt8] = [105, 105, 105]
+ static let kIVKeyword: [UInt8] = [105, 118]
}
diff --git a/Sources/WmapParser/DataStructures/Component.swift b/Sources/WmapParser/DataStructures/Component.swift
index 5ef2716..48dac78 100644
--- a/Sources/WmapParser/DataStructures/Component.swift
+++ b/Sources/WmapParser/DataStructures/Component.swift
@@ -1,8 +1,8 @@
extension WmapParser {
- /// A component in the map.
- public struct Component: Sendable, Equatable {
- public let label: String
- public let coordinates: MapPoint
- public let shape: Shape
- }
+ /// A component in the map.
+ public struct Component: Sendable, Equatable {
+ public let label: String
+ public let coordinates: MapPoint
+ public let shape: Shape
+ }
}
diff --git a/Sources/WmapParser/DataStructures/Dependency.swift b/Sources/WmapParser/DataStructures/Dependency.swift
index 5f0089c..c2f4163 100644
--- a/Sources/WmapParser/DataStructures/Dependency.swift
+++ b/Sources/WmapParser/DataStructures/Dependency.swift
@@ -1,8 +1,8 @@
extension WmapParser {
-/// A dependency between two components.
-public struct Dependency: Sendable, Equatable {
- public let fromComponent: String
- public let toComponent: String
- public let isDirected: Bool
-}
+ /// A dependency between two components.
+ public struct Dependency: Sendable, Equatable {
+ public let fromComponent: String
+ public let toComponent: String
+ public let isDirected: Bool
+ }
}
diff --git a/Sources/WmapParser/DataStructures/Evolution.swift b/Sources/WmapParser/DataStructures/Evolution.swift
index 07fbf2a..d5b0ee3 100644
--- a/Sources/WmapParser/DataStructures/Evolution.swift
+++ b/Sources/WmapParser/DataStructures/Evolution.swift
@@ -1,7 +1,7 @@
extension WmapParser {
-/// Evolution associated with a component.
-public struct Evolution: Sendable, Equatable {
- public let component: String
- public let value: Double
-}
+ /// Evolution associated with a component.
+ public struct Evolution: Sendable, Equatable {
+ public let component: String
+ public let value: Double
+ }
}
diff --git a/Sources/WmapParser/DataStructures/Group.swift b/Sources/WmapParser/DataStructures/Group.swift
index 5dabc11..9a18d92 100644
--- a/Sources/WmapParser/DataStructures/Group.swift
+++ b/Sources/WmapParser/DataStructures/Group.swift
@@ -1,6 +1,6 @@
extension WmapParser {
- /// A group of components.
- public struct Group: Sendable, Equatable {
- public let components: [String]
- }
+ /// A group of components.
+ public struct Group: Sendable, Equatable {
+ public let components: [String]
+ }
}
diff --git a/Sources/WmapParser/DataStructures/Inertia.swift b/Sources/WmapParser/DataStructures/Inertia.swift
index 146ad70..30cace2 100644
--- a/Sources/WmapParser/DataStructures/Inertia.swift
+++ b/Sources/WmapParser/DataStructures/Inertia.swift
@@ -1,6 +1,6 @@
extension WmapParser {
- /// Inertia associated with a component.
- public struct Inertia: Sendable, Equatable {
- public let component: String
- }
+ /// Inertia associated with a component.
+ public struct Inertia: Sendable, Equatable {
+ public let component: String
+ }
}
diff --git a/Sources/WmapParser/DataStructures/Map.swift b/Sources/WmapParser/DataStructures/Map.swift
index dbb3c7d..776ba18 100644
--- a/Sources/WmapParser/DataStructures/Map.swift
+++ b/Sources/WmapParser/DataStructures/Map.swift
@@ -1,12 +1,12 @@
extension WmapParser {
-/// A parsed wardley map.
-public struct Map: Sendable {
- public let components: [Component]
- public let dependencies: [Dependency]
- public let notes: [Note]
- public let stages: [Stage]
- public let groups: [Group]
- public let inertias: [Inertia]
- public let evolutions: [Evolution]
-}
+ /// A parsed wardley map.
+ public struct Map: Sendable, Equatable {
+ public let components: [Component]
+ public let dependencies: [Dependency]
+ public let notes: [Note]
+ public let stages: [Stage]
+ public let groups: [Group]
+ public let inertias: [Inertia]
+ public let evolutions: [Evolution]
+ }
}
diff --git a/Sources/WmapParser/DataStructures/MapPoint.swift b/Sources/WmapParser/DataStructures/MapPoint.swift
index c0eb971..7fedc00 100644
--- a/Sources/WmapParser/DataStructures/MapPoint.swift
+++ b/Sources/WmapParser/DataStructures/MapPoint.swift
@@ -1,7 +1,7 @@
extension WmapParser {
- /// A point in the map
- public struct MapPoint: Sendable, Equatable {
- let xCoordinate: Double
- let yCoordinate: Double
- }
+ /// A point in the map
+ public struct MapPoint: Sendable, Equatable {
+ let xCoordinate: Double
+ let yCoordinate: Double
+ }
}
diff --git a/Sources/WmapParser/DataStructures/Note.swift b/Sources/WmapParser/DataStructures/Note.swift
index de87979..b6184a9 100644
--- a/Sources/WmapParser/DataStructures/Note.swift
+++ b/Sources/WmapParser/DataStructures/Note.swift
@@ -1,7 +1,7 @@
extension WmapParser {
-/// A note.
-public struct Note: Sendable, Equatable {
- public let text: String
- public let coordinates: MapPoint
-}
+ /// A note.
+ public struct Note: Sendable, Equatable {
+ public let text: String
+ public let coordinates: MapPoint
+ }
}
diff --git a/Sources/WmapParser/DataStructures/Shape.swift b/Sources/WmapParser/DataStructures/Shape.swift
index 146b0e6..4870670 100644
--- a/Sources/WmapParser/DataStructures/Shape.swift
+++ b/Sources/WmapParser/DataStructures/Shape.swift
@@ -1,10 +1,10 @@
extension WmapParser {
-/// Any of the potential shapes in a component.
-@frozen
-public enum Shape: Sendable, Equatable {
- case circle
- case xMark
- case square
- case triangle
-}
+ /// Any of the potential shapes in a component.
+ @frozen
+ public enum Shape: Sendable, Equatable {
+ case circle
+ case xMark
+ case square
+ case triangle
+ }
}
diff --git a/Sources/WmapParser/DataStructures/Stage.swift b/Sources/WmapParser/DataStructures/Stage.swift
index e0a99f1..87efdc1 100644
--- a/Sources/WmapParser/DataStructures/Stage.swift
+++ b/Sources/WmapParser/DataStructures/Stage.swift
@@ -1,7 +1,7 @@
extension WmapParser {
-/// An override for the width of an evolution stage.
-public struct Stage: Sendable, Equatable {
- public let stage: StageNumber
- public let value: Double
-}
+ /// An override for the width of an evolution stage.
+ public struct Stage: Sendable, Equatable {
+ public let stage: StageNumber
+ public let value: Double
+ }
}
diff --git a/Sources/WmapParser/DataStructures/StageNumber.swift b/Sources/WmapParser/DataStructures/StageNumber.swift
index 603e06b..b308742 100644
--- a/Sources/WmapParser/DataStructures/StageNumber.swift
+++ b/Sources/WmapParser/DataStructures/StageNumber.swift
@@ -1,10 +1,10 @@
extension WmapParser {
-/// The potential stages of evolution.
-@frozen
-public enum StageNumber: Sendable, Equatable {
- case stageI
- case stageII
- case stageIII
- case stageIV
-}
+ /// The potential stages of evolution.
+ @frozen
+ public enum StageNumber: Sendable, Equatable {
+ case stageI
+ case stageII
+ case stageIII
+ case stageIV
+ }
}
diff --git a/Sources/WmapParser/DependencyParser.swift b/Sources/WmapParser/DependencyParser.swift
index 891be01..2567bfe 100644
--- a/Sources/WmapParser/DependencyParser.swift
+++ b/Sources/WmapParser/DependencyParser.swift
@@ -1,52 +1,57 @@
extension WmapParser {
- /// Looks for -- or ->. If found, returns where this arrow starts.
- @inline(__always)
- static func hasDependency<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Int?
- where T.Element == UInt8, T.Index == Int {
- var index = start
- while index < end {
- if bytes[index] == kOpenParenthesis { return nil }
- if bytes[index] == kDash && index + 1 < end {
- let next = bytes[index + 1]
- if next == kGreaterThan || next == kDash { return index }
- }
- index += 1
+ /// Looks for -- or ->. If found, returns where this arrow starts.
+ @inline(__always)
+ static func hasDependency<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Int?
+ where T.Element == UInt8, T.Index == Int {
+ var index = start
+ while index < end {
+ if bytes[index] == kOpenParenthesis { return nil }
+ if bytes[index] == kDash && index + 1 < end {
+ let next = bytes[index + 1]
+ if next == kGreaterThan || next == kDash { return index }
}
- return nil
+ index += 1
}
+ return nil
+ }
- /// Finds a dependency and whether it's directed. It assumes we previously
- /// checked for the location of our separator to speed things up.
- static func parseDependency<T: Collection>(_ bytes: T, _ start: Int, _ end: Int, _ separatorIndex: Int)
- -> Dependency?
- where T.Element == UInt8, T.Index == Int {
- guard let isDirected = extractIsDirected(bytes, separatorIndex, end) else {
- return nil
- }
-
- guard let fromComponent = extractTrimmedString(bytes, start, separatorIndex) else {
- return nil
- }
+ /// Finds a dependency and whether it's directed. It assumes we previously
+ /// checked for the location of our separator to speed things up.
+ static func parseDependency<T: Collection>(
+ _ bytes: T, _ start: Int, _ end: Int, _ separatorIndex: Int
+ )
+ -> Dependency?
+ where T.Element == UInt8, T.Index == Int {
+ guard let isDirected = extractIsDirected(bytes, separatorIndex, end) else {
+ return nil
+ }
- guard let toComponent = extractTrimmedString(bytes, separatorIndex + 2, end) else {
- return nil
- }
+ guard let fromComponent = extractTrimmedString(bytes, start, separatorIndex) else {
+ return nil
+ }
- return Dependency(fromComponent: fromComponent, toComponent: toComponent, isDirected: isDirected)
+ guard let toComponent = extractTrimmedString(bytes, separatorIndex + 2, end) else {
+ return nil
}
- /// Checks the second character of the separator to see if it's directed.
- /// It assumes we already checked for -- or ->, so it only checks for
- /// Whether it's greaterThan or not.
- @inline(__always)
- private static func extractIsDirected<T: Collection>(_ bytes: T, _ separatorIndex: Int, _ end: Int)
- -> Bool?
- where T.Element == UInt8, T.Index == Int {
- let followingCharacter = separatorIndex + 1
- guard followingCharacter < end else { return nil }
- if bytes[followingCharacter] == kGreaterThan {
- return true
- }
- return false
+ return Dependency(
+ fromComponent: fromComponent, toComponent: toComponent, isDirected: isDirected)
+ }
+
+ /// Checks the second character of the separator to see if it's directed.
+ /// It assumes we already checked for -- or ->, so it only checks for
+ /// Whether it's greaterThan or not.
+ @inline(__always)
+ private static func extractIsDirected<T: Collection>(
+ _ bytes: T, _ separatorIndex: Int, _ end: Int
+ )
+ -> Bool?
+ where T.Element == UInt8, T.Index == Int {
+ let followingCharacter = separatorIndex + 1
+ guard followingCharacter < end else { return nil }
+ if bytes[followingCharacter] == kGreaterThan {
+ return true
}
+ return false
+ }
}
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)
+ }
}
diff --git a/Sources/WmapParser/ParserContext/KeywordParserContext.swift b/Sources/WmapParser/ParserContext/KeywordParserContext.swift
index c3a77a7..bfafad2 100644
--- a/Sources/WmapParser/ParserContext/KeywordParserContext.swift
+++ b/Sources/WmapParser/ParserContext/KeywordParserContext.swift
@@ -1,9 +1,9 @@
extension WmapParser {
- struct KeywordParserContext {
- var stages: [Stage]
- var notes: [Note]
- var groups: [Group]
- var inertias: [Inertia]
- var evolutions: [Evolution]
- }
+ struct KeywordParserContext {
+ var stages: [Stage]
+ var notes: [Note]
+ var groups: [Group]
+ var inertias: [Inertia]
+ var evolutions: [Evolution]
+ }
}
diff --git a/Sources/WmapParser/ParserContext/LineParserContext.swift b/Sources/WmapParser/ParserContext/LineParserContext.swift
index c823383..67b156c 100644
--- a/Sources/WmapParser/ParserContext/LineParserContext.swift
+++ b/Sources/WmapParser/ParserContext/LineParserContext.swift
@@ -1,7 +1,7 @@
extension WmapParser {
- struct LineParserContext {
- var components: [Component]
- var dependencies: [Dependency]
- var keywordContext: KeywordParserContext
- }
+ struct LineParserContext {
+ var components: [Component]
+ var dependencies: [Dependency]
+ var keywordContext: KeywordParserContext
+ }
}
diff --git a/Sources/WmapParser/Utils.swift b/Sources/WmapParser/Utils.swift
index d79578b..4886eb3 100644
--- a/Sources/WmapParser/Utils.swift
+++ b/Sources/WmapParser/Utils.swift
@@ -1,138 +1,142 @@
extension WmapParser {
- /// Check if a byte is a digit
- @inline(__always)
- static func isDigit(_ byte: UInt8) -> Bool {
- byte >= kZero && byte <= kNine
- }
+ /// Check if a byte is a digit
+ @inline(__always)
+ static func isDigit(_ byte: UInt8) -> Bool {
+ byte >= kZero && byte <= kNine
+ }
- /// Looks for x and y separated by a comma.
- static func parseCoordinates<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> (
- MapPoint, Int
- )?
- where T.Element == UInt8, T.Index == Int {
- var index = skipWhitespace(bytes, end, start)
+ /// Looks for x and y separated by a comma.
+ static func parseCoordinates<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> (
+ MapPoint, Int
+ )?
+ where T.Element == UInt8, T.Index == Int {
+ var index = skipWhitespace(bytes, end, start)
- guard let (xCoordinate, nextIndex) = parseSingleCoordinate(bytes, index, end) else {
- return nil
- }
- index = nextIndex
+ guard let (xCoordinate, nextIndex) = parseSingleCoordinate(bytes, index, end) else {
+ return nil
+ }
+ index = nextIndex
- guard expectComma(bytes, &index, end) else { return nil }
+ guard expectComma(bytes, &index, end) else { return nil }
- guard let (yCoordinate, nextIndex) = parseSingleCoordinate(bytes, index, end) else {
- return nil
- }
- index = nextIndex
+ guard let (yCoordinate, nextIndex) = parseSingleCoordinate(bytes, index, end) else {
+ return nil
+ }
+ index = nextIndex
- guard expectClosingParen(bytes, &index, end) else { return nil }
+ guard expectClosingParen(bytes, &index, end) else { return nil }
- return (MapPoint(xCoordinate: xCoordinate, yCoordinate: yCoordinate), index)
- }
+ return (MapPoint(xCoordinate: xCoordinate, yCoordinate: yCoordinate), index)
+ }
- /// Checks if a candidate for a coordinate is a digit, and parses it.
- @inline(__always)
- static func parseSingleCoordinate<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> (Double, Int)?
- where T.Element == UInt8, T.Index == Int {
- var index = start
- while index < end && (isDigit(bytes[index]) || bytes[index] == 46) { index += 1 }
- guard index > start else { return nil }
- guard let value = parseDouble(bytes, start, index) else { return nil }
- return (value, index)
- }
+ /// Checks if a candidate for a coordinate is a digit, and parses it.
+ @inline(__always)
+ static func parseSingleCoordinate<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> (
+ Double, Int
+ )?
+ where T.Element == UInt8, T.Index == Int {
+ var index = start
+ while index < end && (isDigit(bytes[index]) || bytes[index] == 46) { index += 1 }
+ guard index > start else { return nil }
+ guard let value = parseDouble(bytes, start, index) else { return nil }
+ return (value, index)
+ }
- /// Advances the index until we find a comma.
- @inline(__always)
- static func expectComma<T: Collection>(_ bytes: T, _ index: inout Int, _ end: Int) -> Bool
- where T.Element == UInt8, T.Index == Int {
- index = skipWhitespace(bytes, end, index)
- guard index < end && bytes[index] == kComma else { return false }
- index += 1
- index = skipWhitespace(bytes, end, index)
- return true
- }
+ /// Advances the index until we find a comma.
+ @inline(__always)
+ static func expectComma<T: Collection>(_ bytes: T, _ index: inout Int, _ end: Int) -> Bool
+ where T.Element == UInt8, T.Index == Int {
+ index = skipWhitespace(bytes, end, index)
+ guard index < end && bytes[index] == kComma else { return false }
+ index += 1
+ index = skipWhitespace(bytes, end, index)
+ return true
+ }
- /// Advances the index until we find a closing parenthesis
- @inline(__always)
- static func expectClosingParen<T: Collection>(_ bytes: T, _ index: inout Int, _ end: Int) -> Bool
- where T.Element == UInt8, T.Index == Int {
- index = skipWhitespace(bytes, end, index)
- guard index < end && bytes[index] == kCloseParenthesis else { return false }
- index += 1
- return true
- }
+ /// Advances the index until we find a closing parenthesis
+ @inline(__always)
+ static func expectClosingParen<T: Collection>(_ bytes: T, _ index: inout Int, _ end: Int) -> Bool
+ where T.Element == UInt8, T.Index == Int {
+ index = skipWhitespace(bytes, end, index)
+ guard index < end && bytes[index] == kCloseParenthesis else { return false }
+ index += 1
+ return true
+ }
- /// Parses a double
- @inline(__always)
- static func parseDouble<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Double?
- where T.Element == UInt8, T.Index == Int {
- var index = skipWhitespace(bytes, end, start)
+ /// Parses a double
+ @inline(__always)
+ static func parseDouble<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Double?
+ where T.Element == UInt8, T.Index == Int {
+ var index = skipWhitespace(bytes, end, start)
- let numStart = index
- while index < end && (isDigit(bytes[index]) || bytes[index] == kPeriod) { index += 1 }
+ let numStart = index
+ while index < end && (isDigit(bytes[index]) || bytes[index] == kPeriod) { index += 1 }
- guard index > numStart else { return nil }
+ guard index > numStart else { return nil }
- // Why not String(data:encoding:)? This is much faster.
- // swiftlint:disable:next optional_data_string_conversion
- let str = String(decoding: bytes[numStart..<index], as: UTF8.self)
- return Double(str)
- }
+ // Why not String(data:encoding:)? This is much faster.
+ // swiftlint:disable:next optional_data_string_conversion
+ let str = String(decoding: bytes[numStart..<index], as: UTF8.self)
+ return Double(str)
+ }
- /// Advances an index by looking for whitespace left-to-right
- static func skipWhitespace<T: Collection>(_ bytes: T, _ end: Int, _ start: Int) -> Int
- where T.Element == UInt8, T.Index == Int {
- var index = start
- while index < end && (bytes[index] == kSpace || bytes[index] == kTab) { index += 1 }
- return index
- }
+ /// Advances an index by looking for whitespace left-to-right
+ static func skipWhitespace<T: Collection>(_ bytes: T, _ end: Int, _ start: Int) -> Int
+ where T.Element == UInt8, T.Index == Int {
+ var index = start
+ while index < end && (bytes[index] == kSpace || bytes[index] == kTab) { index += 1 }
+ return index
+ }
- /// Decreases an index by looking for whitespace right-to-left
- static func shaveWhitespace<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Int
- where T.Element == UInt8, T.Index == Int {
- var index = end
- while index > start && (bytes[index - 1] == kSpace || bytes[index - 1] == kTab) { index -= 1 }
- return index
- }
+ /// Decreases an index by looking for whitespace right-to-left
+ static func shaveWhitespace<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Int
+ where T.Element == UInt8, T.Index == Int {
+ var index = end
+ while index > start && (bytes[index - 1] == kSpace || bytes[index - 1] == kTab) { index -= 1 }
+ return index
+ }
- /// Given the bytes buffer, finds the next non-empty line.
- @inline(__always)
- static func extractLine<T: Collection>(_ bytes: T, _ index: inout Int, _ length: Int) -> (
- Int, Int
- )?
- where T.Element == UInt8, T.Index == Int {
- while index < length && (bytes[index] == kNewline || bytes[index] == kCarriageReturn) {
- index += 1
- }
- let lineStart = index
+ /// Given the bytes buffer, finds the next non-empty line.
+ @inline(__always)
+ static func extractLine<T: Collection>(_ bytes: T, _ index: inout Int, _ length: Int) -> (
+ Int, Int
+ )?
+ where T.Element == UInt8, T.Index == Int {
+ while index < length && (bytes[index] == kNewline || bytes[index] == kCarriageReturn) {
+ index += 1
+ }
+ let lineStart = index
- while index < length && bytes[index] != kNewline && bytes[index] != kCarriageReturn { index += 1 }
- if lineStart == index { return nil }
+ while index < length && bytes[index] != kNewline && bytes[index] != kCarriageReturn {
+ index += 1
+ }
+ if lineStart == index { return nil }
- var start = lineStart
- var end = index
+ var start = lineStart
+ var end = index
- start = skipWhitespace(bytes, end, start)
- end = shaveWhitespace(bytes, start, end)
+ start = skipWhitespace(bytes, end, start)
+ end = shaveWhitespace(bytes, start, end)
- if start == end { return nil }
+ if start == end { return nil }
- return (start, end)
- }
+ return (start, end)
+ }
- /// Trims a byte collection and returns a string
- @inline(__always)
- static func extractTrimmedString<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> String?
- where T.Element == UInt8, T.Index == Int {
- var trimStart = start
- var trimEnd = end
+ /// Trims a byte collection and returns a string
+ @inline(__always)
+ static func extractTrimmedString<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> String?
+ where T.Element == UInt8, T.Index == Int {
+ var trimStart = start
+ var trimEnd = end
- trimStart = skipWhitespace(bytes, trimEnd, trimStart)
- trimEnd = shaveWhitespace(bytes, trimStart, trimEnd)
+ trimStart = skipWhitespace(bytes, trimEnd, trimStart)
+ trimEnd = shaveWhitespace(bytes, trimStart, trimEnd)
- 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
- return String(decoding: bytes[trimStart..<trimEnd], as: UTF8.self)
- }
+ // Why not String(data:encoding:)? This is much faster.
+ // swiftlint:disable:next optional_data_string_conversion
+ return String(decoding: bytes[trimStart..<trimEnd], as: UTF8.self)
+ }
}
diff --git a/Sources/WmapParser/WmapParser.swift b/Sources/WmapParser/WmapParser.swift
index 41843b3..ef203ec 100644
--- a/Sources/WmapParser/WmapParser.swift
+++ b/Sources/WmapParser/WmapParser.swift
@@ -2,75 +2,76 @@ import Foundation
public struct WmapParser {
- // Public API //////////////////////////////////////////////////////////////////
+ // Public API //////////////////////////////////////////////////////////////////
- /// Parses a wmap source string.
- public static func parse(_ source: String) -> Map {
- source.utf8.withContiguousStorageIfAvailable { bytes in
- parseBytes(bytes)
- } ?? parseBytes(Array(source.utf8))
- }
-
- // Parsers /////////////////////////////////////////////////////////////////////
+ /// Parses a wmap source string.
+ public static func parse(_ source: String) -> Map {
+ source.utf8.withContiguousStorageIfAvailable { bytes in
+ parseBytes(bytes)
+ } ?? parseBytes(Array(source.utf8))
+ }
- /// Main parsing function, we work on bytes as it's faster.
- private static func parseBytes<T: Collection>(_ bytes: T) -> Map where T.Element == UInt8, T.Index == Int {
- let estimatedLines = bytes.count / 30
+ // Parsers /////////////////////////////////////////////////////////////////////
- var context = LineParserContext(
- components: [],
- dependencies: [],
- keywordContext: KeywordParserContext(
- stages: [],
- notes: [],
- groups: [],
- inertias: [],
- evolutions: []
- )
- )
+ /// Main parsing function, we work on bytes as it's faster.
+ private static func parseBytes<T: Collection>(_ bytes: T) -> Map
+ where T.Element == UInt8, T.Index == Int {
+ let estimatedLines = bytes.count / 30
- context.components.reserveCapacity(estimatedLines)
- context.dependencies.reserveCapacity(estimatedLines)
- context.keywordContext.notes.reserveCapacity(estimatedLines / 10)
- context.keywordContext.stages.reserveCapacity(4)
- context.keywordContext.groups.reserveCapacity(estimatedLines / 20)
- context.keywordContext.inertias.reserveCapacity(estimatedLines / 20)
- context.keywordContext.evolutions.reserveCapacity(estimatedLines / 20)
+ var context = LineParserContext(
+ components: [],
+ dependencies: [],
+ keywordContext: KeywordParserContext(
+ stages: [],
+ notes: [],
+ groups: [],
+ inertias: [],
+ evolutions: []
+ )
+ )
- var index = 0
- let length = bytes.count
+ context.components.reserveCapacity(estimatedLines)
+ context.dependencies.reserveCapacity(estimatedLines)
+ context.keywordContext.notes.reserveCapacity(estimatedLines / 10)
+ context.keywordContext.stages.reserveCapacity(4)
+ context.keywordContext.groups.reserveCapacity(estimatedLines / 20)
+ context.keywordContext.inertias.reserveCapacity(estimatedLines / 20)
+ context.keywordContext.evolutions.reserveCapacity(estimatedLines / 20)
- while index < length {
- guard let (start, end) = extractLine(bytes, &index, length) else { continue }
- parseLine(bytes, start, end, &context)
- }
+ var index = 0
+ let length = bytes.count
- return Map(
- components: context.components,
- dependencies: context.dependencies,
- notes: context.keywordContext.notes,
- stages: context.keywordContext.stages,
- groups: context.keywordContext.groups,
- inertias: context.keywordContext.inertias,
- evolutions: context.keywordContext.evolutions
- )
+ while index < length {
+ guard let (start, end) = extractLine(bytes, &index, length) else { continue }
+ parseLine(bytes, start, end, &context)
}
- /// Given a trimmed line, checks if it's a keyword, dependency or component
- /// and parses it.
- @inline(__always)
- private static func parseLine<T: Collection>(
- _ bytes: T, _ start: Int, _ end: Int,
- _ context: inout LineParserContext
- ) where T.Element == UInt8, T.Index == Int {
- if bytes[start] == kOpenSquareBracket {
- parseKeywordLine(bytes, start, end, &context.keywordContext)
- } else if let separatorIndex = hasDependency(bytes, start, end) {
- if let dependency = parseDependency(bytes, start, end, separatorIndex) {
- context.dependencies.append(dependency)
- }
- } else if let component = parseComponent(bytes, start, end) {
- context.components.append(component)
- }
- }
+ return Map(
+ components: context.components,
+ dependencies: context.dependencies,
+ notes: context.keywordContext.notes,
+ stages: context.keywordContext.stages,
+ groups: context.keywordContext.groups,
+ inertias: context.keywordContext.inertias,
+ evolutions: context.keywordContext.evolutions
+ )
+ }
+
+ /// Given a trimmed line, checks if it's a keyword, dependency or component
+ /// and parses it.
+ @inline(__always)
+ private static func parseLine<T: Collection>(
+ _ bytes: T, _ start: Int, _ end: Int,
+ _ context: inout LineParserContext
+ ) where T.Element == UInt8, T.Index == Int {
+ if bytes[start] == kOpenSquareBracket {
+ parseKeywordLine(bytes, start, end, &context.keywordContext)
+ } else if let separatorIndex = hasDependency(bytes, start, end) {
+ if let dependency = parseDependency(bytes, start, end, separatorIndex) {
+ context.dependencies.append(dependency)
+ }
+ } else if let component = parseComponent(bytes, start, end) {
+ context.components.append(component)
+ }
+ }
}
diff --git a/Tests/WmapParserTests/ComponentParserTests.swift b/Tests/WmapParserTests/ComponentParserTests.swift
index 1f3a451..624f433 100644
--- a/Tests/WmapParserTests/ComponentParserTests.swift
+++ b/Tests/WmapParserTests/ComponentParserTests.swift
@@ -9,7 +9,8 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "Dominoes", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle
+ label: "Dominoes", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5),
+ shape: .circle
)
)
}
@@ -33,7 +34,8 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "API", coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.6), shape: .xMark)
+ label: "API", coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.6),
+ shape: .xMark)
)
}
@@ -56,7 +58,8 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "Process", coordinates: WmapParser.MapPoint(xCoordinate: 0.2, yCoordinate: 0.9), shape: .circle)
+ label: "Process", coordinates: WmapParser.MapPoint(xCoordinate: 0.2, yCoordinate: 0.9),
+ shape: .circle)
)
}
@@ -79,7 +82,8 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "Item", coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.123), shape: .circle)
+ label: "Item", coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.123),
+ shape: .circle)
)
}
@@ -90,7 +94,8 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "Item", coordinates: WmapParser.MapPoint(xCoordinate: 1.0, yCoordinate: 0.0), shape: .circle)
+ label: "Item", coordinates: WmapParser.MapPoint(xCoordinate: 1.0, yCoordinate: 0.0),
+ shape: .circle)
)
}
@@ -101,7 +106,8 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "RubberGaskets", coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.5),
+ label: "RubberGaskets",
+ coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.5),
shape: .circle)
)
}
@@ -113,7 +119,8 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "Big Lawnmowers", coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.5),
+ label: "Big Lawnmowers",
+ coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.5),
shape: .circle)
)
}
diff --git a/Tests/WmapParserTests/DependenciesParserTests.swift b/Tests/WmapParserTests/DependenciesParserTests.swift
index 356d5d3..4911ce0 100644
--- a/Tests/WmapParserTests/DependenciesParserTests.swift
+++ b/Tests/WmapParserTests/DependenciesParserTests.swift
@@ -29,7 +29,8 @@ import Testing
#expect(result.dependencies.count == 1)
#expect(
result.dependencies.first
- == WmapParser.Dependency(fromComponent: "SaRcAsM", toComponent: "Seriousness.", isDirected: true)
+ == WmapParser.Dependency(
+ fromComponent: "SaRcAsM", toComponent: "Seriousness.", isDirected: true)
)
}
diff --git a/Tests/WmapParserTests/NotesParserTests.swift b/Tests/WmapParserTests/NotesParserTests.swift
index ae99c75..9e906dc 100644
--- a/Tests/WmapParserTests/NotesParserTests.swift
+++ b/Tests/WmapParserTests/NotesParserTests.swift
@@ -8,7 +8,9 @@ import Testing
#expect(result.notes.count == 1)
#expect(
result.notes.first
- == WmapParser.Note(text: "This is a note", coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.5))
+ == WmapParser.Note(
+ text: "This is a note", coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.5)
+ )
)
}
@@ -18,7 +20,8 @@ import Testing
#expect(result.notes.count == 1)
#expect(
result.notes.first
- == WmapParser.Note(text: "Text", coordinates: WmapParser.MapPoint(xCoordinate: 0.1, yCoordinate: 0.2))
+ == WmapParser.Note(
+ text: "Text", coordinates: WmapParser.MapPoint(xCoordinate: 0.1, yCoordinate: 0.2))
)
}
@@ -28,7 +31,9 @@ import Testing
#expect(result.notes.count == 1)
#expect(
result.notes.first
- == WmapParser.Note(text: "Important NOTE", coordinates: WmapParser.MapPoint(xCoordinate: 0.77, yCoordinate: 0.19))
+ == WmapParser.Note(
+ text: "Important NOTE",
+ coordinates: WmapParser.MapPoint(xCoordinate: 0.77, yCoordinate: 0.19))
)
}
@@ -37,9 +42,10 @@ import Testing
let result = WmapParser.parse(source)
#expect(result.notes.count == 1)
#expect(
- result.notes.first == WmapParser.Note(
+ result.notes.first
+ == WmapParser.Note(
text: "",
coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.5)
- )
+ )
)
}
diff --git a/Tests/WmapParserTests/WmapParserTests.swift b/Tests/WmapParserTests/WmapParserTests.swift
index f5dfe38..baf114f 100644
--- a/Tests/WmapParserTests/WmapParserTests.swift
+++ b/Tests/WmapParserTests/WmapParserTests.swift
@@ -13,12 +13,14 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "Water", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
+ label: "Water", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5),
+ shape: .circle)
)
#expect(
result.components[1]
== WmapParser.Component(
- label: "Bucket", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4), shape: .xMark)
+ label: "Bucket", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4),
+ shape: .xMark)
)
#expect(result.dependencies.count == 1)
#expect(
@@ -34,13 +36,15 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "Penguins", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle
+ label: "Penguins", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5),
+ shape: .circle
)
)
#expect(
result.components[1]
== WmapParser.Component(
- label: "I Use Arch BTW", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4),
+ label: "I Use Arch BTW",
+ coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4),
shape: .xMark)
)
}
@@ -52,7 +56,8 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "Clippy", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
+ label: "Clippy", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5),
+ shape: .circle)
)
#expect(
result.components[1]
@@ -69,12 +74,14 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "SE/30", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
+ label: "SE/30", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5),
+ shape: .circle)
)
#expect(
result.components[1]
== WmapParser.Component(
- label: "Performa", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4), shape: .xMark)
+ label: "Performa", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4),
+ shape: .xMark)
)
}
@@ -89,7 +96,8 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "The Void", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle
+ label: "The Void", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5),
+ shape: .circle
)
)
#expect(
@@ -171,12 +179,14 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "Hello", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
+ label: "Hello", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5),
+ shape: .circle)
)
#expect(
result.components[1]
== WmapParser.Component(
- label: "It's OK", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4), shape: .circle)
+ label: "It's OK", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4),
+ shape: .circle)
)
#expect(result.dependencies.count == 1)
#expect(
@@ -267,17 +277,20 @@ import Testing
#expect(
result.components.first
== WmapParser.Component(
- label: "Tea", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
+ label: "Tea", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5),
+ shape: .circle)
)
#expect(
result.components[1]
== WmapParser.Component(
- label: "Cup", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4), shape: .circle)
+ label: "Cup", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4),
+ shape: .circle)
)
#expect(
result.components[2]
== WmapParser.Component(
- label: "Kettle", coordinates: WmapParser.MapPoint(xCoordinate: 0.7, yCoordinate: 0.3), shape: .square)
+ label: "Kettle", coordinates: WmapParser.MapPoint(xCoordinate: 0.7, yCoordinate: 0.3),
+ shape: .square)
)
#expect(
result.components[3]