aboutsummaryrefslogtreecommitdiff
path: root/Sources/WmapParser
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 /Sources/WmapParser
parent088a941a72c2f3e9b3382eb9c5d2f21203dd401c (diff)
Use WmapParser namespace
Diffstat (limited to 'Sources/WmapParser')
-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
24 files changed, 529 insertions, 490 deletions
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)
+ }
+ }
}