aboutsummaryrefslogtreecommitdiff
path: root/Sources/WmapParser/ComponentParser.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Sources/WmapParser/ComponentParser.swift')
-rw-r--r--Sources/WmapParser/ComponentParser.swift78
1 files changed, 78 insertions, 0 deletions
diff --git a/Sources/WmapParser/ComponentParser.swift b/Sources/WmapParser/ComponentParser.swift
new file mode 100644
index 0000000..c899637
--- /dev/null
+++ b/Sources/WmapParser/ComponentParser.swift
@@ -0,0 +1,78 @@
+/// 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
+ }
+
+ guard
+ let (coordinates, endIndex) = parseCoordinates(
+ bytes, parenthesisIndex + 1, end)
+ else {
+ return nil
+ }
+
+ let shape = extractComponentShape(bytes, endIndex, end)
+
+ return Component(label: label, coordinates: coordinates, shape: shape)
+}
+
+/// Looks for a component label by taking the string before the first open
+/// parenthesis.
+@inline(__always)
+private 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
+}
+
+/// 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)
+
+ guard index < end && bytes[index] == kOpenSquareBracket else { return .circle }
+
+ index += 1
+ let shapeStart = index
+ while index < end && bytes[index] != kCloseSquareBracket { index += 1 }
+
+ guard index > shapeStart && index < end else { return .circle }
+
+ let shapeName = bytes[shapeStart..<index]
+ return parseShapeFromBytes(shapeName)
+}
+
+/// 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
+
+ trimStart = skipWhitespace(shapeName, trimEnd, trimStart)
+ trimEnd = shaveWhitespace(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 }
+
+ return .circle
+}