aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Beltran del Rio <jj@r.bdr.sh>2025-12-14 16:16:22 +0100
committerRuben Beltran del Rio <jj@r.bdr.sh>2025-12-14 16:40:52 +0100
commit0b90075f76b6f2df77c01c303322323b1395641a (patch)
tree4e13d3ce0b1f4792eab8d04caefda2218565f5a9
parent088a941a72c2f3e9b3382eb9c5d2f21203dd401c (diff)
Use WmapParser namespace
-rw-r--r--Benchmarks/ParseBenchmarkTarget/ParseBenchmarkTarget.swift4
-rw-r--r--Sources/WmapParser/Component.swift6
-rw-r--r--Sources/WmapParser/ComponentParser.swift127
-rw-r--r--Sources/WmapParser/Constants.swift64
-rw-r--r--Sources/WmapParser/DataStructures/Component.swift8
-rw-r--r--Sources/WmapParser/DataStructures/Dependency.swift (renamed from Sources/WmapParser/Dependency.swift)2
-rw-r--r--Sources/WmapParser/DataStructures/Evolution.swift (renamed from Sources/WmapParser/Evolution.swift)2
-rw-r--r--Sources/WmapParser/DataStructures/Group.swift6
-rw-r--r--Sources/WmapParser/DataStructures/Inertia.swift6
-rw-r--r--Sources/WmapParser/DataStructures/Map.swift (renamed from Sources/WmapParser/Map.swift)2
-rw-r--r--Sources/WmapParser/DataStructures/MapPoint.swift7
-rw-r--r--Sources/WmapParser/DataStructures/Note.swift (renamed from Sources/WmapParser/Note.swift)2
-rw-r--r--Sources/WmapParser/DataStructures/Shape.swift (renamed from Sources/WmapParser/Shape.swift)2
-rw-r--r--Sources/WmapParser/DataStructures/Stage.swift (renamed from Sources/WmapParser/Stage.swift)2
-rw-r--r--Sources/WmapParser/DataStructures/StageNumber.swift (renamed from Sources/WmapParser/StageNumber.swift)2
-rw-r--r--Sources/WmapParser/DependencyParser.swift88
-rw-r--r--Sources/WmapParser/Group.swift4
-rw-r--r--Sources/WmapParser/Inertia.swift4
-rw-r--r--Sources/WmapParser/KeywordParser.swift306
-rw-r--r--Sources/WmapParser/KeywordParserContext.swift7
-rw-r--r--Sources/WmapParser/MapPoint.swift5
-rw-r--r--Sources/WmapParser/ParserContext/KeywordParserContext.swift9
-rw-r--r--Sources/WmapParser/ParserContext/LineParserContext.swift7
-rw-r--r--Sources/WmapParser/Utils.swift222
-rw-r--r--Sources/WmapParser/WmapParser.swift129
-rw-r--r--Tests/WmapParserTests/ComponentParserTests.swift66
-rw-r--r--Tests/WmapParserTests/DependenciesParserTests.swift16
-rw-r--r--Tests/WmapParserTests/EvolutionParserTests.swift20
-rw-r--r--Tests/WmapParserTests/GroupsParserTests.swift10
-rw-r--r--Tests/WmapParserTests/InertiaParserTests.swift6
-rw-r--r--Tests/WmapParserTests/NotesParserTests.swift19
-rw-r--r--Tests/WmapParserTests/StagesParserTests.swift20
-rw-r--r--Tests/WmapParserTests/WmapParserTests.swift134
33 files changed, 678 insertions, 636 deletions
diff --git a/Benchmarks/ParseBenchmarkTarget/ParseBenchmarkTarget.swift b/Benchmarks/ParseBenchmarkTarget/ParseBenchmarkTarget.swift
index c99814f..5dc438c 100644
--- a/Benchmarks/ParseBenchmarkTarget/ParseBenchmarkTarget.swift
+++ b/Benchmarks/ParseBenchmarkTarget/ParseBenchmarkTarget.swift
@@ -36,13 +36,13 @@ let benchmarks: @Sendable () -> Benchmark? = {
Benchmark("parse example") { benchmark in
for _ in benchmark.scaledIterations {
- blackHole(parse(exampleSource))
+ blackHole(WmapParser.parse(exampleSource))
}
}
return Benchmark("parse huge") { benchmark in
for _ in benchmark.scaledIterations {
- blackHole(parse(hugeSource))
+ blackHole(WmapParser.parse(hugeSource))
}
}
}
diff --git a/Sources/WmapParser/Component.swift b/Sources/WmapParser/Component.swift
deleted file mode 100644
index 027ac59..0000000
--- a/Sources/WmapParser/Component.swift
+++ /dev/null
@@ -1,6 +0,0 @@
-/// 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/ComponentParser.swift b/Sources/WmapParser/ComponentParser.swift
index c899637..66979eb 100644
--- a/Sources/WmapParser/ComponentParser.swift
+++ b/Sources/WmapParser/ComponentParser.swift
@@ -1,78 +1,81 @@
-/// Parses a component with its coordinates.
-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
- }
+extension WmapParser {
- guard
- let (coordinates, endIndex) = parseCoordinates(
- bytes, parenthesisIndex + 1, 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
+ }
- let shape = extractComponentShape(bytes, endIndex, end)
+ guard
+ let (coordinates, endIndex) = parseCoordinates(
+ bytes, parenthesisIndex + 1, end)
+ else {
+ return nil
+ }
- return Component(label: label, coordinates: coordinates, shape: shape)
-}
+ let shape = extractComponentShape(bytes, endIndex, end)
-/// Looks for a component label by taking the string before the first open
-/// parenthesis.
-@inline(__always)
-private 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)
+ return Component(label: label, coordinates: coordinates, shape: shape)
}
- 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 func extractComponentShape<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Shape
-where T.Element == UInt8, T.Index == Int {
- var index = skipWhitespace(bytes, end, start)
+ /// 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
+ }
+ return nil
+ }
- guard index < end && bytes[index] == kOpenSquareBracket else { return .circle }
+ /// 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)
- index += 1
- let shapeStart = index
- while index < end && bytes[index] != kCloseSquareBracket { index += 1 }
+ guard index < end && bytes[index] == kOpenSquareBracket else { return .circle }
- guard index > shapeStart && index < end else { return .circle }
+ index += 1
+ let shapeStart = index
+ while index < end && bytes[index] != kCloseSquareBracket { index += 1 }
- let shapeName = bytes[shapeStart..<index]
- return parseShapeFromBytes(shapeName)
-}
+ guard index > shapeStart && index < end else { return .circle }
-/// Given the contents of a shape label, return the right type.
-@inline(__always)
-private func parseShapeFromBytes<T: Collection>(_ shapeName: T) -> Shape
-where T.Element == UInt8, T.Index == Int {
- var trimStart = shapeName.startIndex
- var trimEnd = shapeName.endIndex
+ 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
- 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 73340b6..e659c03 100644
--- a/Sources/WmapParser/Constants.swift
+++ b/Sources/WmapParser/Constants.swift
@@ -1,35 +1,37 @@
-// Whitespace
-let kNewline = 10
-let kCarriageReturn = 13
-let kTab = 9
-let kSpace = 32
+extension WmapParser {
+ // Whitespace
+ static let kNewline = 10
+ static let kCarriageReturn = 13
+ static let kTab = 9
+ static let kSpace = 32
-// Punctuation
-let kOpenParenthesis = 40
-let kCloseParenthesis = 41
-let kPlus = 43
-let kComma = 44
-let kDash = 45
-let kPeriod = 46
-let kGreaterThan = 62
-let kOpenSquareBracket = 91
-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
-let kCapitalA = 65
-let kCapitalZ = 90
-let kCapsToLowercaseDistance: UInt8 = 32
+ // Letters
+ static let kCapitalA = 65
+ static let kCapitalZ = 90
+ static let kCapsToLowercaseDistance: UInt8 = 32
-// Numbers
-let kZero = 48
-let kNine = 57
+ // Numbers
+ static let kZero = 48
+ static let kNine = 57
-// Keywords. ASCII representations of the keywords.
-let kInertiaKeyword: [UInt8] = [105, 110, 101, 114, 116, 105, 97]
-let kEvolutionKeyword: [UInt8] = [101, 118, 111, 108, 117, 116, 105, 111, 110]
-let kNoteKeyword: [UInt8] = [110, 111, 116, 101]
-let kGroupKeyword: [UInt8] = [103, 114, 111, 117, 112]
-let kIKeyword: [UInt8] = [105]
-let kIIKeyword: [UInt8] = [105, 105]
-let kIIIKeyword: [UInt8] = [105, 105, 105]
-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
new file mode 100644
index 0000000..5ef2716
--- /dev/null
+++ b/Sources/WmapParser/DataStructures/Component.swift
@@ -0,0 +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
+ }
+}
diff --git a/Sources/WmapParser/Dependency.swift b/Sources/WmapParser/DataStructures/Dependency.swift
index 38f03be..5f0089c 100644
--- a/Sources/WmapParser/Dependency.swift
+++ b/Sources/WmapParser/DataStructures/Dependency.swift
@@ -1,6 +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
}
+}
diff --git a/Sources/WmapParser/Evolution.swift b/Sources/WmapParser/DataStructures/Evolution.swift
index df6dd33..07fbf2a 100644
--- a/Sources/WmapParser/Evolution.swift
+++ b/Sources/WmapParser/DataStructures/Evolution.swift
@@ -1,5 +1,7 @@
+extension WmapParser {
/// 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
new file mode 100644
index 0000000..5dabc11
--- /dev/null
+++ b/Sources/WmapParser/DataStructures/Group.swift
@@ -0,0 +1,6 @@
+extension WmapParser {
+ /// 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
new file mode 100644
index 0000000..146ad70
--- /dev/null
+++ b/Sources/WmapParser/DataStructures/Inertia.swift
@@ -0,0 +1,6 @@
+extension WmapParser {
+ /// Inertia associated with a component.
+ public struct Inertia: Sendable, Equatable {
+ public let component: String
+ }
+}
diff --git a/Sources/WmapParser/Map.swift b/Sources/WmapParser/DataStructures/Map.swift
index 7a25686..dbb3c7d 100644
--- a/Sources/WmapParser/Map.swift
+++ b/Sources/WmapParser/DataStructures/Map.swift
@@ -1,3 +1,4 @@
+extension WmapParser {
/// A parsed wardley map.
public struct Map: Sendable {
public let components: [Component]
@@ -8,3 +9,4 @@ public struct Map: Sendable {
public let inertias: [Inertia]
public let evolutions: [Evolution]
}
+}
diff --git a/Sources/WmapParser/DataStructures/MapPoint.swift b/Sources/WmapParser/DataStructures/MapPoint.swift
new file mode 100644
index 0000000..c0eb971
--- /dev/null
+++ b/Sources/WmapParser/DataStructures/MapPoint.swift
@@ -0,0 +1,7 @@
+extension WmapParser {
+ /// A point in the map
+ public struct MapPoint: Sendable, Equatable {
+ let xCoordinate: Double
+ let yCoordinate: Double
+ }
+}
diff --git a/Sources/WmapParser/Note.swift b/Sources/WmapParser/DataStructures/Note.swift
index 0db3739..de87979 100644
--- a/Sources/WmapParser/Note.swift
+++ b/Sources/WmapParser/DataStructures/Note.swift
@@ -1,5 +1,7 @@
+extension WmapParser {
/// A note.
public struct Note: Sendable, Equatable {
public let text: String
public let coordinates: MapPoint
}
+}
diff --git a/Sources/WmapParser/Shape.swift b/Sources/WmapParser/DataStructures/Shape.swift
index ea1844e..146b0e6 100644
--- a/Sources/WmapParser/Shape.swift
+++ b/Sources/WmapParser/DataStructures/Shape.swift
@@ -1,3 +1,4 @@
+extension WmapParser {
/// Any of the potential shapes in a component.
@frozen
public enum Shape: Sendable, Equatable {
@@ -6,3 +7,4 @@ public enum Shape: Sendable, Equatable {
case square
case triangle
}
+}
diff --git a/Sources/WmapParser/Stage.swift b/Sources/WmapParser/DataStructures/Stage.swift
index 78429e0..e0a99f1 100644
--- a/Sources/WmapParser/Stage.swift
+++ b/Sources/WmapParser/DataStructures/Stage.swift
@@ -1,5 +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
}
+}
diff --git a/Sources/WmapParser/StageNumber.swift b/Sources/WmapParser/DataStructures/StageNumber.swift
index 2e8a2da..603e06b 100644
--- a/Sources/WmapParser/StageNumber.swift
+++ b/Sources/WmapParser/DataStructures/StageNumber.swift
@@ -1,3 +1,4 @@
+extension WmapParser {
/// The potential stages of evolution.
@frozen
public enum StageNumber: Sendable, Equatable {
@@ -6,3 +7,4 @@ public enum StageNumber: Sendable, Equatable {
case stageIII
case stageIV
}
+}
diff --git a/Sources/WmapParser/DependencyParser.swift b/Sources/WmapParser/DependencyParser.swift
index 9964d5a..891be01 100644
--- a/Sources/WmapParser/DependencyParser.swift
+++ b/Sources/WmapParser/DependencyParser.swift
@@ -1,50 +1,52 @@
-/// Looks for -- or ->. If found, returns where this arrow starts.
-@inline(__always)
-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 }
+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
+ }
+ 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.
-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
- }
+ /// 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
- }
+ guard let fromComponent = extractTrimmedString(bytes, start, separatorIndex) else {
+ return nil
+ }
- guard let toComponent = extractTrimmedString(bytes, separatorIndex + 2, end) else {
- return nil
- }
+ guard let toComponent = extractTrimmedString(bytes, separatorIndex + 2, end) else {
+ return nil
+ }
- return Dependency(fromComponent: fromComponent, toComponent: toComponent, isDirected: isDirected)
-}
+ 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 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
+ /// 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/Group.swift b/Sources/WmapParser/Group.swift
deleted file mode 100644
index 5204d45..0000000
--- a/Sources/WmapParser/Group.swift
+++ /dev/null
@@ -1,4 +0,0 @@
-/// A group of components.
-public struct Group: Sendable, Equatable {
- public let components: [String]
-}
diff --git a/Sources/WmapParser/Inertia.swift b/Sources/WmapParser/Inertia.swift
deleted file mode 100644
index e9be9c0..0000000
--- a/Sources/WmapParser/Inertia.swift
+++ /dev/null
@@ -1,4 +0,0 @@
-/// Inertia associated with a component.
-public struct Inertia: Sendable, Equatable {
- public let component: String
-}
diff --git a/Sources/WmapParser/KeywordParser.swift b/Sources/WmapParser/KeywordParser.swift
index 8db1164..b560d6a 100644
--- a/Sources/WmapParser/KeywordParser.swift
+++ b/Sources/WmapParser/KeywordParser.swift
@@ -1,184 +1,186 @@
-/// Checks which keyword we're dealing with and routes it to the right parser.
-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 }
+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 }
- 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)
-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)
-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)
-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.
-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.
-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
-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
-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
-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/KeywordParserContext.swift b/Sources/WmapParser/KeywordParserContext.swift
deleted file mode 100644
index 1b889b3..0000000
--- a/Sources/WmapParser/KeywordParserContext.swift
+++ /dev/null
@@ -1,7 +0,0 @@
-struct KeywordParserContext {
- var stages: [Stage]
- var notes: [Note]
- var groups: [Group]
- var inertias: [Inertia]
- var evolutions: [Evolution]
-}
diff --git a/Sources/WmapParser/MapPoint.swift b/Sources/WmapParser/MapPoint.swift
deleted file mode 100644
index 718ecab..0000000
--- a/Sources/WmapParser/MapPoint.swift
+++ /dev/null
@@ -1,5 +0,0 @@
-/// A point in the map
-public struct MapPoint: Sendable, Equatable {
- let xCoordinate: Double
- let yCoordinate: Double
-}
diff --git a/Sources/WmapParser/ParserContext/KeywordParserContext.swift b/Sources/WmapParser/ParserContext/KeywordParserContext.swift
new file mode 100644
index 0000000..c3a77a7
--- /dev/null
+++ b/Sources/WmapParser/ParserContext/KeywordParserContext.swift
@@ -0,0 +1,9 @@
+extension WmapParser {
+ 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
new file mode 100644
index 0000000..c823383
--- /dev/null
+++ b/Sources/WmapParser/ParserContext/LineParserContext.swift
@@ -0,0 +1,7 @@
+extension WmapParser {
+ struct LineParserContext {
+ var components: [Component]
+ var dependencies: [Dependency]
+ var keywordContext: KeywordParserContext
+ }
+}
diff --git a/Sources/WmapParser/Utils.swift b/Sources/WmapParser/Utils.swift
index 8bea13a..d79578b 100644
--- a/Sources/WmapParser/Utils.swift
+++ b/Sources/WmapParser/Utils.swift
@@ -1,136 +1,138 @@
-/// Check if a byte is a digit
-@inline(__always)
-func isDigit(_ byte: UInt8) -> Bool {
- byte >= kZero && byte <= kNine
-}
+extension WmapParser {
+ /// 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.
-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)
-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)
-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)
-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)
-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
-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
-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)
-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)
-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 f2bef8f..41843b3 100644
--- a/Sources/WmapParser/WmapParser.swift
+++ b/Sources/WmapParser/WmapParser.swift
@@ -1,79 +1,76 @@
import Foundation
-// Public API //////////////////////////////////////////////////////////////////
+public struct WmapParser {
-/// Parses a wmap source string.
-public func parse(_ source: String) -> Map {
- source.utf8.withContiguousStorageIfAvailable { bytes in
- parseBytes(bytes)
- } ?? parseBytes(Array(source.utf8))
-}
-
-// Parsers /////////////////////////////////////////////////////////////////////
+ // Public API //////////////////////////////////////////////////////////////////
-struct LineParserContext {
- var components: [Component]
- var dependencies: [Dependency]
- var keywordContext: KeywordParserContext
-}
+ /// 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 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 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)
+ 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
+ )
}
- } else if let component = parseComponent(bytes, start, end) {
- context.components.append(component)
- }
+
+ /// 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 52e347a..1f3a451 100644
--- a/Tests/WmapParserTests/ComponentParserTests.swift
+++ b/Tests/WmapParserTests/ComponentParserTests.swift
@@ -4,128 +4,128 @@ import Testing
@Test func shouldParseABasicComponent() {
let source = "Dominoes (0.9, 0.5)"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 1)
#expect(
result.components.first
- == Component(
- label: "Dominoes", coordinates: MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle
+ == WmapParser.Component(
+ label: "Dominoes", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle
)
)
}
@Test func shouldParseAComponentWithAShape() {
let source = "Database (0.33, 0.81) [Square]"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 1)
#expect(
result.components.first
- == Component(
- label: "Database", coordinates: MapPoint(xCoordinate: 0.33, yCoordinate: 0.81),
+ == WmapParser.Component(
+ label: "Database", coordinates: WmapParser.MapPoint(xCoordinate: 0.33, yCoordinate: 0.81),
shape: .square)
)
}
@Test func shouldParseAComponentWithXShape() {
let source = "API (0.5, 0.6) [x]"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 1)
#expect(
result.components.first
- == Component(
- label: "API", coordinates: MapPoint(xCoordinate: 0.5, yCoordinate: 0.6), shape: .xMark)
+ == WmapParser.Component(
+ label: "API", coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.6), shape: .xMark)
)
}
@Test func shouldParseAComponentWithTriangleShape() {
let source = "Service (0.4, 0.7) [Triangle]"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 1)
#expect(
result.components.first
- == Component(
- label: "Service", coordinates: MapPoint(xCoordinate: 0.4, yCoordinate: 0.7),
+ == WmapParser.Component(
+ label: "Service", coordinates: WmapParser.MapPoint(xCoordinate: 0.4, yCoordinate: 0.7),
shape: .triangle)
)
}
@Test func shouldParseAComponentWithCircleShape() {
let source = "Process (0.2, 0.9) [CIRCLE]"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 1)
#expect(
result.components.first
- == Component(
- label: "Process", coordinates: MapPoint(xCoordinate: 0.2, yCoordinate: 0.9), shape: .circle)
+ == WmapParser.Component(
+ label: "Process", coordinates: WmapParser.MapPoint(xCoordinate: 0.2, yCoordinate: 0.9), shape: .circle)
)
}
@Test func shouldDefaultShapeToCircleIfInvalid() {
let source = "Figures (0.12, 0.711) ["
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 1)
#expect(
result.components.first
- == Component(
- label: "Figures", coordinates: MapPoint(xCoordinate: 0.12, yCoordinate: 0.711),
+ == WmapParser.Component(
+ label: "Figures", coordinates: WmapParser.MapPoint(xCoordinate: 0.12, yCoordinate: 0.711),
shape: .circle)
)
}
@Test func shouldHandleDecimalNumbers() {
let source = "Item (.5, .123)"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 1)
#expect(
result.components.first
- == Component(
- label: "Item", coordinates: MapPoint(xCoordinate: 0.5, yCoordinate: 0.123), shape: .circle)
+ == WmapParser.Component(
+ label: "Item", coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.123), shape: .circle)
)
}
@Test func shouldHandleIntegersAsCoordinates() {
let source = "Item (1, 0)"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 1)
#expect(
result.components.first
- == Component(
- label: "Item", coordinates: MapPoint(xCoordinate: 1.0, yCoordinate: 0.0), shape: .circle)
+ == WmapParser.Component(
+ label: "Item", coordinates: WmapParser.MapPoint(xCoordinate: 1.0, yCoordinate: 0.0), shape: .circle)
)
}
@Test func shouldPreserveCaseInComponentLabels() {
let source = "RubberGaskets (0.5, 0.5)"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 1)
#expect(
result.components.first
- == Component(
- label: "RubberGaskets", coordinates: MapPoint(xCoordinate: 0.5, yCoordinate: 0.5),
+ == WmapParser.Component(
+ label: "RubberGaskets", coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.5),
shape: .circle)
)
}
@Test func shouldHandleComponentLabelsWithSpaces() {
let source = "Big Lawnmowers (0.5, 0.5)"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 1)
#expect(
result.components.first
- == Component(
- label: "Big Lawnmowers", coordinates: MapPoint(xCoordinate: 0.5, yCoordinate: 0.5),
+ == WmapParser.Component(
+ label: "Big Lawnmowers", coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.5),
shape: .circle)
)
}
@Test func shouldHandleWhitespaceAroundTokens() {
let source = " Outer Space ( 0.9 , 0.5 ) [ Square ] "
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 1)
#expect(
result.components.first
- == Component(
- label: "Outer Space", coordinates: MapPoint(xCoordinate: 0.9, yCoordinate: 0.5),
+ == WmapParser.Component(
+ label: "Outer Space", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5),
shape: .square)
)
}
diff --git a/Tests/WmapParserTests/DependenciesParserTests.swift b/Tests/WmapParserTests/DependenciesParserTests.swift
index 88e6e99..356d5d3 100644
--- a/Tests/WmapParserTests/DependenciesParserTests.swift
+++ b/Tests/WmapParserTests/DependenciesParserTests.swift
@@ -4,41 +4,41 @@ import Testing
@Test func shouldParseUndirectedDependency() {
let source = "Smoke -- Fire"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.dependencies.count == 1)
#expect(
result.dependencies.first
- == Dependency(fromComponent: "Smoke", toComponent: "Fire", isDirected: false)
+ == WmapParser.Dependency(fromComponent: "Smoke", toComponent: "Fire", isDirected: false)
)
}
@Test func shouldParseDirectedDependency() {
let source = "Cool Thing -> Different Cool Thing"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.dependencies.count == 1)
#expect(
result.dependencies.first
- == Dependency(
+ == WmapParser.Dependency(
fromComponent: "Cool Thing", toComponent: "Different Cool Thing", isDirected: true)
)
}
@Test func shouldPreserveCaseInDependencyLabels() {
let source = "SaRcAsM -> Seriousness."
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.dependencies.count == 1)
#expect(
result.dependencies.first
- == Dependency(fromComponent: "SaRcAsM", toComponent: "Seriousness.", isDirected: true)
+ == WmapParser.Dependency(fromComponent: "SaRcAsM", toComponent: "Seriousness.", isDirected: true)
)
}
@Test func shouldIgnoreWhitespaceInDependencies() {
let source = " loose --TIGHT"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.dependencies.count == 1)
#expect(
result.dependencies.first
- == Dependency(fromComponent: "loose", toComponent: "TIGHT", isDirected: false)
+ == WmapParser.Dependency(fromComponent: "loose", toComponent: "TIGHT", isDirected: false)
)
}
diff --git a/Tests/WmapParserTests/EvolutionParserTests.swift b/Tests/WmapParserTests/EvolutionParserTests.swift
index e87bf9e..66c946a 100644
--- a/Tests/WmapParserTests/EvolutionParserTests.swift
+++ b/Tests/WmapParserTests/EvolutionParserTests.swift
@@ -4,45 +4,45 @@ import Testing
@Test func shouldParsePositiveEvolution() {
let source = "[Evolution] Nanomachines + 0.5"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.evolutions.count == 1)
#expect(
- result.evolutions.first == Evolution(component: "Nanomachines", value: 0.5)
+ result.evolutions.first == WmapParser.Evolution(component: "Nanomachines", value: 0.5)
)
}
@Test func shouldParseNegativeEvolution() {
let source = "[Evolution] FOXDIE - 0.3"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.evolutions.count == 1)
#expect(
- result.evolutions.first == Evolution(component: "FOXDIE", value: -0.3)
+ result.evolutions.first == WmapParser.Evolution(component: "FOXDIE", value: -0.3)
)
}
@Test func shouldHandleCaseInsensitiveEvolutionKeyword() {
let source = "[Evolution] tamales + 0.73"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.evolutions.count == 1)
#expect(
- result.evolutions.first == Evolution(component: "tamales", value: 0.73)
+ result.evolutions.first == WmapParser.Evolution(component: "tamales", value: 0.73)
)
}
@Test func shouldPreserveCaseInEvolutionComponentLabel() {
let source = "[EVOLUTION] iMac - 0.2"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.evolutions.count == 1)
#expect(
- result.evolutions.first == Evolution(component: "iMac", value: -0.2)
+ result.evolutions.first == WmapParser.Evolution(component: "iMac", value: -0.2)
)
}
@Test func shouldHandleDecimalEvolutionValues() {
let source = "[Evolution] Numerals + .75"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.evolutions.count == 1)
#expect(
- result.evolutions.first == Evolution(component: "Numerals", value: 0.75)
+ result.evolutions.first == WmapParser.Evolution(component: "Numerals", value: 0.75)
)
}
diff --git a/Tests/WmapParserTests/GroupsParserTests.swift b/Tests/WmapParserTests/GroupsParserTests.swift
index dced486..a441a67 100644
--- a/Tests/WmapParserTests/GroupsParserTests.swift
+++ b/Tests/WmapParserTests/GroupsParserTests.swift
@@ -4,35 +4,35 @@ import Testing
@Test func shouldParseAGroupWithASingleComponent() {
let source = "[Group] Lonely"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.groups.count == 1)
#expect(result.groups.first?.components == ["Lonely"])
}
@Test func shouldParseAGroupWithMultipleComponents() {
let source = "[GrouP] Huey, Dewey, Louie"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.groups.count == 1)
#expect(result.groups.first?.components == ["Huey", "Dewey", "Louie"])
}
@Test func shouldHandleCaseInsensitiveGroupKeyword() {
let source = "[group] Salt, Pepper"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.groups.count == 1)
#expect(result.groups.first?.components == ["Salt", "Pepper"])
}
@Test func shouldPreserveCaseInGroupComponentLabel() {
let source = "[GROUP] XML, LaTeX"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.groups.count == 1)
#expect(result.groups.first?.components == ["XML", "LaTeX"])
}
@Test func shouldHandleWhitespaceAroundCommas() {
let source = "[Group] sky, is large, emperor"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.groups.count == 1)
#expect(result.groups.first?.components == ["sky", "is large", "emperor"])
}
diff --git a/Tests/WmapParserTests/InertiaParserTests.swift b/Tests/WmapParserTests/InertiaParserTests.swift
index 92d9868..bcee55f 100644
--- a/Tests/WmapParserTests/InertiaParserTests.swift
+++ b/Tests/WmapParserTests/InertiaParserTests.swift
@@ -4,21 +4,21 @@ import Testing
@Test func shouldParseInertia() {
let source = "[Inertia] Print Media"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.inertias.count == 1)
#expect(result.inertias.first?.component == "Print Media")
}
@Test func shouldHandleCaseInsensitiveInertiaKeyword() {
let source = "[inertia] Yes"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.inertias.count == 1)
#expect(result.inertias.first?.component == "Yes")
}
@Test func shouldPreserveCaseInInertiaComponentLabel() {
let source = "[INERTIA] SpOnGeBoB"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.inertias.count == 1)
#expect(result.inertias.first?.component == "SpOnGeBoB")
}
diff --git a/Tests/WmapParserTests/NotesParserTests.swift b/Tests/WmapParserTests/NotesParserTests.swift
index 655bef6..ae99c75 100644
--- a/Tests/WmapParserTests/NotesParserTests.swift
+++ b/Tests/WmapParserTests/NotesParserTests.swift
@@ -4,39 +4,42 @@ import Testing
@Test func shouldParseANote() {
let source = "[Note] (0.5, 0.5) This is a note"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.notes.count == 1)
#expect(
result.notes.first
- == Note(text: "This is a note", coordinates: MapPoint(xCoordinate: 0.5, yCoordinate: 0.5))
+ == WmapParser.Note(text: "This is a note", coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.5))
)
}
@Test func shouldHandleCaseInsensitiveNoteKeyword() {
let source = "[note] (0.1, 0.2) Text"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.notes.count == 1)
#expect(
result.notes.first
- == Note(text: "Text", coordinates: MapPoint(xCoordinate: 0.1, yCoordinate: 0.2))
+ == WmapParser.Note(text: "Text", coordinates: WmapParser.MapPoint(xCoordinate: 0.1, yCoordinate: 0.2))
)
}
@Test func shouldPreserveCaseInNoteText() {
let source = "[NOTE] (0.77, 0.19) Important NOTE"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.notes.count == 1)
#expect(
result.notes.first
- == Note(text: "Important NOTE", coordinates: MapPoint(xCoordinate: 0.77, yCoordinate: 0.19))
+ == WmapParser.Note(text: "Important NOTE", coordinates: WmapParser.MapPoint(xCoordinate: 0.77, yCoordinate: 0.19))
)
}
@Test func shouldHandleEmptyNoteText() {
let source = "[Note] (0.5, 0.5) "
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.notes.count == 1)
#expect(
- result.notes.first == Note(text: "", coordinates: MapPoint(xCoordinate: 0.5, yCoordinate: 0.5))
+ result.notes.first == WmapParser.Note(
+ text: "",
+ coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.5)
+ )
)
}
diff --git a/Tests/WmapParserTests/StagesParserTests.swift b/Tests/WmapParserTests/StagesParserTests.swift
index 50bab4b..2dd30fa 100644
--- a/Tests/WmapParserTests/StagesParserTests.swift
+++ b/Tests/WmapParserTests/StagesParserTests.swift
@@ -4,45 +4,45 @@ import Testing
@Test func shouldParseStageI() {
let source = "[I] 0.25"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.stages.count == 1)
#expect(
- result.stages.first == Stage(stage: .stageI, value: 0.25)
+ result.stages.first == WmapParser.Stage(stage: .stageI, value: 0.25)
)
}
@Test func shouldParseStageII() {
let source = "[ii] 0.5"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.stages.count == 1)
#expect(
- result.stages.first == Stage(stage: .stageII, value: 0.5)
+ result.stages.first == WmapParser.Stage(stage: .stageII, value: 0.5)
)
}
@Test func shouldParseStageIII() {
let source = "[III] 0.75"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.stages.count == 1)
#expect(
- result.stages.first == Stage(stage: .stageIII, value: 0.75)
+ result.stages.first == WmapParser.Stage(stage: .stageIII, value: 0.75)
)
}
@Test func shouldParseStageIV() {
let source = "[Iv] 1.0"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.stages.count == 1)
#expect(
- result.stages.first == Stage(stage: .stageIV, value: 1.0)
+ result.stages.first == WmapParser.Stage(stage: .stageIV, value: 1.0)
)
}
@Test func shouldHandleDecimalValues() {
let source = "[i] .333"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.stages.count == 1)
#expect(
- result.stages.first == Stage(stage: .stageI, value: 0.333)
+ result.stages.first == WmapParser.Stage(stage: .stageI, value: 0.333)
)
}
diff --git a/Tests/WmapParserTests/WmapParserTests.swift b/Tests/WmapParserTests/WmapParserTests.swift
index b141dc9..f5dfe38 100644
--- a/Tests/WmapParserTests/WmapParserTests.swift
+++ b/Tests/WmapParserTests/WmapParserTests.swift
@@ -8,73 +8,73 @@ import Testing
Bucket (0.8, 0.4) [x]
Water -> Bucket
"""
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 2)
#expect(
result.components.first
- == Component(
- label: "Water", coordinates: MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
+ == WmapParser.Component(
+ label: "Water", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
)
#expect(
result.components[1]
- == Component(
- label: "Bucket", coordinates: MapPoint(xCoordinate: 0.8, yCoordinate: 0.4), shape: .xMark)
+ == WmapParser.Component(
+ label: "Bucket", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4), shape: .xMark)
)
#expect(result.dependencies.count == 1)
#expect(
result.dependencies.first
- == Dependency(fromComponent: "Water", toComponent: "Bucket", isDirected: true)
+ == WmapParser.Dependency(fromComponent: "Water", toComponent: "Bucket", isDirected: true)
)
}
@Test func shouldHandleUnixLineEndings() {
let source = "Penguins (0.9, 0.5)\nI Use Arch BTW (0.8, 0.4) [x]"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 2)
#expect(
result.components.first
- == Component(
- label: "Penguins", coordinates: MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle
+ == WmapParser.Component(
+ label: "Penguins", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle
)
)
#expect(
result.components[1]
- == Component(
- label: "I Use Arch BTW", coordinates: MapPoint(xCoordinate: 0.8, yCoordinate: 0.4),
+ == WmapParser.Component(
+ label: "I Use Arch BTW", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4),
shape: .xMark)
)
}
@Test func shouldHandleWindowsLineEndings() {
let source = "Clippy (0.9, 0.5)\r\nSpace Cadet (0.8, 0.4) [x]"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 2)
#expect(
result.components.first
- == Component(
- label: "Clippy", coordinates: MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
+ == WmapParser.Component(
+ label: "Clippy", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
)
#expect(
result.components[1]
- == Component(
- label: "Space Cadet", coordinates: MapPoint(xCoordinate: 0.8, yCoordinate: 0.4),
+ == WmapParser.Component(
+ label: "Space Cadet", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4),
shape: .xMark)
)
}
@Test func shouldHandleMacLineEndings() {
let source = "SE/30 (0.9, 0.5)\rPerforma (0.8, 0.4) [x]"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 2)
#expect(
result.components.first
- == Component(
- label: "SE/30", coordinates: MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
+ == WmapParser.Component(
+ label: "SE/30", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
)
#expect(
result.components[1]
- == Component(
- label: "Performa", coordinates: MapPoint(xCoordinate: 0.8, yCoordinate: 0.4), shape: .xMark)
+ == WmapParser.Component(
+ label: "Performa", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4), shape: .xMark)
)
}
@@ -84,73 +84,73 @@ import Testing
The Abyss (0.8, 0.4)
"""
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 2)
#expect(
result.components.first
- == Component(
- label: "The Void", coordinates: MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle
+ == WmapParser.Component(
+ label: "The Void", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle
)
)
#expect(
result.components[1]
- == Component(
- label: "The Abyss", coordinates: MapPoint(xCoordinate: 0.8, yCoordinate: 0.4),
+ == WmapParser.Component(
+ label: "The Abyss", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4),
shape: .circle)
)
}
@Test func shouldHandleIncompleteNotes() {
let source = "[Note]"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 0)
}
@Test func shouldHandleIncompleteGroups() {
let source = "[Group]"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 0)
}
@Test func shouldHandleIncompleteNodesWithNoCoordinates() {
let source = "I am a node? ("
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 0)
}
@Test func shouldHandleIncompleteNodesWithIncompleteCoordinates() {
let source = "I am a node? (12"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 0)
}
@Test func shouldHandleIncompleteNodesWithMissingYCoordinates() {
let source = "I am a node? (12,"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 0)
}
@Test func shouldHandleIncompleteNodesWithUnclosedCoordinates() {
let source = "I am a node? (12,21"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 0)
}
@Test func shouldHandleIncompleteEvolutionWithoutValue() {
let source = "[Evolution] I accidentally a +"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 0)
}
@Test func shouldHandleIncompleteEvolutionWithoutComponent() {
let source = "[Evolution]+"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 0)
}
@Test func shouldHandleJustAParenthesis() {
let source = " ("
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 0)
}
@@ -166,22 +166,22 @@ import Testing
Hello -> It's OK
[
"""
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 2)
#expect(
result.components.first
- == Component(
- label: "Hello", coordinates: MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
+ == WmapParser.Component(
+ label: "Hello", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
)
#expect(
result.components[1]
- == Component(
- label: "It's OK", coordinates: MapPoint(xCoordinate: 0.8, yCoordinate: 0.4), shape: .circle)
+ == WmapParser.Component(
+ label: "It's OK", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4), shape: .circle)
)
#expect(result.dependencies.count == 1)
#expect(
result.dependencies.first
- == Dependency(fromComponent: "Hello", toComponent: "It's OK", isDirected: true)
+ == WmapParser.Dependency(fromComponent: "Hello", toComponent: "It's OK", isDirected: true)
)
}
@@ -189,37 +189,37 @@ import Testing
@Test func shouldHandleEmptyInput() {
let source = ""
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 0)
}
@Test func shouldHandleOnlyWhitespace() {
let source = " \n \n "
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 0)
}
@Test func onlyLineBreaks() {
let source = "\n"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 0)
}
@Test func shouldHandleOnlyInvalidLines() {
let source = "invalid\nstill invalid"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 0)
}
@Test func shouldHandleComplexComponentLabels() {
let source = "My Sü'per Complex Label 123 (0.5, 0.5)"
- let result = parse(source)
+ let result = WmapParser.parse(source)
#expect(result.components.count == 1)
#expect(
result.components.first
- == Component(
+ == WmapParser.Component(
label: "My Sü'per Complex Label 123",
- coordinates: MapPoint(xCoordinate: 0.5, yCoordinate: 0.5), shape: .circle)
+ coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.5), shape: .circle)
)
}
@@ -248,7 +248,7 @@ import Testing
[Evolution] Tea + 0.1
"""
- let result = parse(source)
+ let result = WmapParser.parse(source)
// 4 stages + 4 components + 3 dependencies + 1 note + 1 group + 1 inertia + 1 evolution
#expect(result.stages.count == 4)
@@ -259,54 +259,54 @@ import Testing
#expect(result.inertias.count == 1)
#expect(result.evolutions.count == 1)
- #expect(result.stages.first == Stage(stage: .stageI, value: 0.25))
- #expect(result.stages[1] == Stage(stage: .stageII, value: 0.5))
- #expect(result.stages[2] == Stage(stage: .stageIII, value: 0.75))
- #expect(result.stages[3] == Stage(stage: .stageIV, value: 1.0))
+ #expect(result.stages.first == WmapParser.Stage(stage: .stageI, value: 0.25))
+ #expect(result.stages[1] == WmapParser.Stage(stage: .stageII, value: 0.5))
+ #expect(result.stages[2] == WmapParser.Stage(stage: .stageIII, value: 0.75))
+ #expect(result.stages[3] == WmapParser.Stage(stage: .stageIV, value: 1.0))
#expect(
result.components.first
- == Component(
- label: "Tea", coordinates: MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
+ == WmapParser.Component(
+ label: "Tea", coordinates: WmapParser.MapPoint(xCoordinate: 0.9, yCoordinate: 0.5), shape: .circle)
)
#expect(
result.components[1]
- == Component(
- label: "Cup", coordinates: MapPoint(xCoordinate: 0.8, yCoordinate: 0.4), shape: .circle)
+ == WmapParser.Component(
+ label: "Cup", coordinates: WmapParser.MapPoint(xCoordinate: 0.8, yCoordinate: 0.4), shape: .circle)
)
#expect(
result.components[2]
- == Component(
- label: "Kettle", coordinates: MapPoint(xCoordinate: 0.7, yCoordinate: 0.3), shape: .square)
+ == WmapParser.Component(
+ label: "Kettle", coordinates: WmapParser.MapPoint(xCoordinate: 0.7, yCoordinate: 0.3), shape: .square)
)
#expect(
result.components[3]
- == Component(
- label: "Hot Water", coordinates: MapPoint(xCoordinate: 0.6, yCoordinate: 0.2),
+ == WmapParser.Component(
+ label: "Hot Water", coordinates: WmapParser.MapPoint(xCoordinate: 0.6, yCoordinate: 0.2),
shape: .circle)
)
#expect(
result.dependencies.first
- == Dependency(fromComponent: "Tea", toComponent: "Cup", isDirected: true)
+ == WmapParser.Dependency(fromComponent: "Tea", toComponent: "Cup", isDirected: true)
)
#expect(
result.dependencies[1]
- == Dependency(fromComponent: "Cup", toComponent: "Kettle", isDirected: true)
+ == WmapParser.Dependency(fromComponent: "Cup", toComponent: "Kettle", isDirected: true)
)
#expect(
result.dependencies[2]
- == Dependency(fromComponent: "Kettle", toComponent: "Hot Water", isDirected: true)
+ == WmapParser.Dependency(fromComponent: "Kettle", toComponent: "Hot Water", isDirected: true)
)
#expect(
result.notes.first
- == Note(
+ == WmapParser.Note(
text: "This is our tea supply chain",
- coordinates: MapPoint(xCoordinate: 0.5, yCoordinate: 0.5))
+ coordinates: WmapParser.MapPoint(xCoordinate: 0.5, yCoordinate: 0.5))
)
#expect(result.groups.first?.components == ["Tea", "Cup", "Kettle"])
#expect(result.inertias.first?.component == "Kettle")
- #expect(result.evolutions.first == Evolution(component: "Tea", value: 0.1))
+ #expect(result.evolutions.first == WmapParser.Evolution(component: "Tea", value: 0.1))
}