aboutsummaryrefslogtreecommitdiff
path: root/Sources/WmapParser/WmapParser.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Sources/WmapParser/WmapParser.swift')
-rw-r--r--Sources/WmapParser/WmapParser.swift79
1 files changed, 79 insertions, 0 deletions
diff --git a/Sources/WmapParser/WmapParser.swift b/Sources/WmapParser/WmapParser.swift
new file mode 100644
index 0000000..f2bef8f
--- /dev/null
+++ b/Sources/WmapParser/WmapParser.swift
@@ -0,0 +1,79 @@
+import Foundation
+
+// Public API //////////////////////////////////////////////////////////////////
+
+/// Parses a wmap source string.
+public func parse(_ source: String) -> Map {
+ source.utf8.withContiguousStorageIfAvailable { bytes in
+ parseBytes(bytes)
+ } ?? parseBytes(Array(source.utf8))
+}
+
+// Parsers /////////////////////////////////////////////////////////////////////
+
+struct LineParserContext {
+ var components: [Component]
+ var dependencies: [Dependency]
+ var keywordContext: KeywordParserContext
+}
+
+/// 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
+
+ var context = LineParserContext(
+ components: [],
+ dependencies: [],
+ keywordContext: KeywordParserContext(
+ stages: [],
+ notes: [],
+ groups: [],
+ inertias: [],
+ evolutions: []
+ )
+ )
+
+ 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 index = 0
+ let length = bytes.count
+
+ while index < length {
+ guard let (start, end) = extractLine(bytes, &index, length) else { continue }
+ parseLine(bytes, start, end, &context)
+ }
+
+ return Map(
+ components: context.components,
+ dependencies: context.dependencies,
+ notes: context.keywordContext.notes,
+ stages: context.keywordContext.stages,
+ groups: context.keywordContext.groups,
+ inertias: context.keywordContext.inertias,
+ evolutions: context.keywordContext.evolutions
+ )
+}
+
+/// Given a trimmed line, checks if it's a keyword, dependency or component
+/// and parses it.
+@inline(__always)
+private 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)
+ }
+}