1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
import Foundation
public struct WmapParser {
// Public API //////////////////////////////////////////////////////////////////
/// Parses a wmap source string.
public static func parse(_ source: String) -> Map {
source.utf8.withContiguousStorageIfAvailable { bytes in
parseBytes(bytes)
} ?? parseBytes(Array(source.utf8))
}
// Parsers /////////////////////////////////////////////////////////////////////
/// 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
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 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)
}
}
}
|