Skip to content

Commit 5edfc14

Browse files
committed
Refactor incremental parse API
Make `nodeAffectRangeCollector` a non-optional member of `parser` and make it a return value when we want to do incremental parse
1 parent 9ca6adf commit 5edfc14

File tree

6 files changed

+82
-45
lines changed

6 files changed

+82
-45
lines changed

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftparser/ParserEntryFile.swift

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ let parserEntryFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
2424
/// Parse the source code in the given string as Swift source file. See
2525
/// `Parser.init` for more details.
2626
public static func parse(
27-
source: String,
28-
parseTransition: IncrementalParseTransition? = nil
27+
source: String
2928
) -> SourceFileSyntax {
3029
var parser = Parser(source)
3130
return SourceFileSyntax.parse(from: &parser)
@@ -39,14 +38,40 @@ let parserEntryFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
3938
/// `Parser.init` for more details.
4039
public static func parse(
4140
source: UnsafeBufferPointer<UInt8>,
42-
maximumNestingLevel: Int? = nil,
43-
parseTransition: IncrementalParseTransition? = nil
41+
maximumNestingLevel: Int? = nil
4442
) -> SourceFileSyntax {
4543
var parser = Parser(source, maximumNestingLevel: maximumNestingLevel)
4644
return SourceFileSyntax.parse(from: &parser)
4745
}
4846
"""
4947
)
48+
49+
DeclSyntax(
50+
"""
51+
/// Parse the source code in the given string as Swift source file while return `nodeAffectRangeCollector` to enable incremental parse.
52+
public static func parse(
53+
source: String,
54+
parseTransition: IncrementalParseTransition? = nil
55+
) -> (tree: SourceFileSyntax, nodeAffectRangeCollector: IncrementalParseNodeAffectRangeCollector) {
56+
var parser = Parser(source, parseTransition: parseTransition)
57+
return (SourceFileSyntax.parse(from: &parser), parser.parseNodeAffectRange)
58+
}
59+
"""
60+
)
61+
62+
DeclSyntax(
63+
"""
64+
/// parse the source code in the given buffer as Swift source file while return `nodeAffectRangeCollector` to enable incremental parse.
65+
public static func parse(
66+
source: UnsafeBufferPointer<UInt8>,
67+
maximumNestingLevel: Int? = nil,
68+
parseTransition: IncrementalParseTransition? = nil
69+
) -> (tree: SourceFileSyntax, nodeAffectRangeCollector: IncrementalParseNodeAffectRangeCollector) {
70+
var parser = Parser(source, maximumNestingLevel: maximumNestingLevel, parseTransition: parseTransition)
71+
return (SourceFileSyntax.parse(from: &parser), parser.parseNodeAffectRange)
72+
}
73+
"""
74+
)
5075
}
5176

5277
DeclSyntax(

Sources/SwiftParser/IncrementalParseTransition.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,15 @@ public final class IncrementalParseReusedNodeCollector:
4545
/// Record the affect range for potential re-used nodes. When edits intersect the affect range, the node is not able to be re-used.
4646
///
4747
/// This is also a trigger to enable parser to parse incrementally.
48-
public final class IncrementalParseNodeAffectRangeCollector {
48+
public struct IncrementalParseNodeAffectRangeCollector {
4949
/// A dict to record the utf8 length in source that might affect the parse of a node.
5050
/// This information is used to determine whether a node can be reused
5151
fileprivate var nodeAffectRange: [RawSyntax.ID: Int] = [:]
5252

5353
public init() {}
5454

5555
@_spi(RawSyntax)
56-
public func registerNodeForIncrementalParse(node: RawSyntax, length: Int) {
56+
public mutating func registerNodeForIncrementalParse(node: RawSyntax, length: Int) {
5757
self.nodeAffectRange[node.id] = length
5858
}
5959
}
@@ -63,6 +63,7 @@ public final class IncrementalParseNodeAffectRangeCollector {
6363
public final class IncrementalParseTransition {
6464
fileprivate let previousTree: SourceFileSyntax
6565
fileprivate let edits: ConcurrentEdits
66+
fileprivate let nodeAffectRangeCollector: IncrementalParseNodeAffectRangeCollector
6667
fileprivate let reusedDelegate: IncrementalParseReusedNodeDelegate?
6768

6869
/// - Parameters:
@@ -74,10 +75,12 @@ public final class IncrementalParseTransition {
7475
public init(
7576
previousTree: SourceFileSyntax,
7677
edits: ConcurrentEdits,
78+
nodeAffectRangeCollector: IncrementalParseNodeAffectRangeCollector,
7779
reusedNodeDelegate: IncrementalParseReusedNodeDelegate? = nil
7880
) {
7981
self.previousTree = previousTree
8082
self.edits = edits
83+
self.nodeAffectRangeCollector = nodeAffectRangeCollector
8184
self.reusedDelegate = reusedNodeDelegate
8285
}
8386
}
@@ -89,14 +92,15 @@ public struct IncrementalParseLookup {
8992

9093
fileprivate var cursor: SyntaxCursor
9194

92-
fileprivate let nodeAffectRangeCollector: IncrementalParseNodeAffectRangeCollector
95+
fileprivate var nodeAffectRangeCollector: IncrementalParseNodeAffectRangeCollector {
96+
return transition.nodeAffectRangeCollector
97+
}
9398

9499
/// Create a new ``IncrementalParseLookup`` that can look nodes up based on the
95100
/// given ``IncrementalParseTransition``.
96-
public init(transition: IncrementalParseTransition, nodeAffectRangeCollector: IncrementalParseNodeAffectRangeCollector) {
101+
public init(transition: IncrementalParseTransition) {
97102
self.transition = transition
98103
self.cursor = .init(root: Syntax(transition.previousTree))
99-
self.nodeAffectRangeCollector = nodeAffectRangeCollector
100104
}
101105

102106
fileprivate var edits: ConcurrentEdits {

Sources/SwiftParser/Parser.swift

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public struct Parser {
103103

104104
let parseLookup: IncrementalParseLookup?
105105

106-
let parseNodeAffectRange: IncrementalParseNodeAffectRangeCollector?
106+
let parseNodeAffectRange = IncrementalParseNodeAffectRangeCollector()
107107

108108
/// A default maximum nesting level that is used if the client didn't
109109
/// explicitly specify one. Debug builds of the parser comume a lot more stack
@@ -118,7 +118,6 @@ public struct Parser {
118118
public init(
119119
_ input: String,
120120
maximumNestingLevel: Int? = nil,
121-
parseNodeAffectRange: IncrementalParseNodeAffectRangeCollector? = nil,
122121
parseTransition: IncrementalParseTransition? = nil
123122
) {
124123
self.maximumNestingLevel = maximumNestingLevel ?? Self.defaultMaximumNestingLevel
@@ -135,11 +134,8 @@ public struct Parser {
135134

136135
self.lexemes = Lexer.tokenize(interned)
137136
self.currentToken = self.lexemes.advance()
138-
self.parseNodeAffectRange = parseNodeAffectRange
139-
if let parseTransition,
140-
let parseNodeAffectRange
141-
{
142-
self.parseLookup = IncrementalParseLookup(transition: parseTransition, nodeAffectRangeCollector: parseNodeAffectRange)
137+
if let parseTransition {
138+
self.parseLookup = IncrementalParseLookup(transition: parseTransition)
143139
} else {
144140
self.parseLookup = nil
145141
}
@@ -162,7 +158,6 @@ public struct Parser {
162158
public init(
163159
_ input: UnsafeBufferPointer<UInt8>,
164160
maximumNestingLevel: Int? = nil,
165-
parseNodeAffectRange: IncrementalParseNodeAffectRangeCollector? = nil,
166161
parseTransition: IncrementalParseTransition? = nil,
167162
arena: ParsingSyntaxArena? = nil
168163
) {
@@ -182,11 +177,8 @@ public struct Parser {
182177

183178
self.lexemes = Lexer.tokenize(sourceBuffer)
184179
self.currentToken = self.lexemes.advance()
185-
self.parseNodeAffectRange = parseNodeAffectRange
186-
if let parseTransition,
187-
let parseNodeAffectRange
188-
{
189-
self.parseLookup = IncrementalParseLookup(transition: parseTransition, nodeAffectRangeCollector: parseNodeAffectRange)
180+
if let parseTransition {
181+
self.parseLookup = IncrementalParseLookup(transition: parseTransition)
190182
} else {
191183
self.parseLookup = nil
192184
}
@@ -679,9 +671,6 @@ extension Parser {
679671
}
680672

681673
mutating func registerNodeForIncrementalParse(node: RawSyntax, startToken: Lexer.Lexeme) {
682-
guard let parseNodeAffectRange else {
683-
return
684-
}
685674
parseNodeAffectRange.registerNodeForIncrementalParse(
686675
node: node,
687676
length: max(lookaheadFurthestOffset - self.lexemes.getOffsetToStart(startToken), node.byteLength + currentToken.byteLength)

Sources/SwiftParser/generated/Parser+Entry.swift

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,40 @@ extension Parser {
1818
/// Parse the source code in the given string as Swift source file. See
1919
/// `Parser.init` for more details.
2020
public static func parse(
21-
source: String,
22-
parseNodeAffectRange: IncrementalParseNodeAffectRangeCollector? = nil,
23-
parseTransition: IncrementalParseTransition? = nil
21+
source: String
2422
) -> SourceFileSyntax {
25-
var parser = Parser(source, parseNodeAffectRange: parseNodeAffectRange, parseTransition: parseTransition)
23+
var parser = Parser(source)
2624
return SourceFileSyntax.parse(from: &parser)
2725
}
2826

2927
/// Parse the source code in the given string as Swift source file. See
3028
/// `Parser.init` for more details.
3129
public static func parse(
3230
source: UnsafeBufferPointer<UInt8>,
33-
maximumNestingLevel: Int? = nil,
34-
parseNodeAffectRange: IncrementalParseNodeAffectRangeCollector? = nil,
35-
parseTransition: IncrementalParseTransition? = nil
31+
maximumNestingLevel: Int? = nil
3632
) -> SourceFileSyntax {
37-
var parser = Parser(source, maximumNestingLevel: maximumNestingLevel, parseNodeAffectRange: parseNodeAffectRange, parseTransition: parseTransition)
33+
var parser = Parser(source, maximumNestingLevel: maximumNestingLevel)
3834
return SourceFileSyntax.parse(from: &parser)
3935
}
36+
37+
/// Parse the source code in the given string as Swift source file while return `nodeAffectRangeCollector` to enable incremental parse.
38+
public static func parse(
39+
source: String,
40+
parseTransition: IncrementalParseTransition? = nil
41+
) -> (tree: SourceFileSyntax, nodeAffectRangeCollector: IncrementalParseNodeAffectRangeCollector) {
42+
var parser = Parser(source, parseTransition: parseTransition)
43+
return (SourceFileSyntax.parse(from: &parser), parser.parseNodeAffectRange)
44+
}
45+
46+
/// parse the source code in the given buffer as Swift source file while return `nodeAffectRangeCollector` to enable incremental parse.
47+
public static func parse(
48+
source: UnsafeBufferPointer<UInt8>,
49+
maximumNestingLevel: Int? = nil,
50+
parseTransition: IncrementalParseTransition? = nil
51+
) -> (tree: SourceFileSyntax, nodeAffectRangeCollector: IncrementalParseNodeAffectRangeCollector) {
52+
var parser = Parser(source, maximumNestingLevel: maximumNestingLevel, parseTransition: parseTransition)
53+
return (SourceFileSyntax.parse(from: &parser), parser.parseNodeAffectRange)
54+
}
4055
}
4156

4257
public protocol SyntaxParseable: SyntaxProtocol {

Sources/_SwiftSyntaxTestSupport/IncrementalParseTestUtils.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,18 @@ public func assertIncrementalParse(
3030
let originalString = String(originalSource)
3131
let editedString = String(editedSource)
3232

33-
let nodeAffectRangeCollector = IncrementalParseNodeAffectRangeCollector()
34-
let originalTree = Parser.parse(source: originalString, parseNodeAffectRange: nodeAffectRangeCollector)
33+
let (originalTree, nodeAffectRangeCollector) = Parser.parse(source: originalString, parseTransition: nil)
3534

3635
let reusedNodesCollector = IncrementalParseReusedNodeCollector()
37-
let transition = IncrementalParseTransition(previousTree: originalTree, edits: concurrentEdits, reusedNodeDelegate: reusedNodesCollector)
36+
let transition = IncrementalParseTransition(
37+
previousTree: originalTree,
38+
edits: concurrentEdits,
39+
nodeAffectRangeCollector: nodeAffectRangeCollector,
40+
reusedNodeDelegate: reusedNodesCollector
41+
)
3842

3943
let newTree = Parser.parse(source: editedString)
40-
let incrementallyParsedNewTree = Parser.parse(source: editedString, parseNodeAffectRange: nodeAffectRangeCollector, parseTransition: transition)
44+
let (incrementallyParsedNewTree, _) = Parser.parse(source: editedString, parseTransition: transition)
4145

4246
// Round-trip
4347
assertStringsEqualWithDiff(

Sources/swift-parser-cli/Commands/PerformanceTest.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,21 @@ struct PerformanceTest: ParsableCommand {
4141
.filter { $0.pathExtension == "swift" }
4242
.map { try Data(contentsOf: $0) }
4343

44-
var fileTransition: [Data: (parseTransition: IncrementalParseTransition, nodeAffectRangeCollector: IncrementalParseNodeAffectRangeCollector)] = [:]
44+
var fileTransition: [Data: IncrementalParseTransition] = [:]
4545

4646
if self.incrementalParse {
4747
/// The initial parse for incremental parse
4848
for file in files {
49-
let incrementalParseAffectRangeCollector = IncrementalParseNodeAffectRangeCollector()
5049
file.withUnsafeBytes { buf in
51-
let tree = Parser.parse(
50+
let (tree, nodeAffectRangeCollector) = Parser.parse(
5251
source: buf.bindMemory(to: UInt8.self),
53-
parseNodeAffectRange: incrementalParseAffectRangeCollector
52+
parseTransition: nil
5453
)
5554

56-
fileTransition[file] = (
57-
IncrementalParseTransition(previousTree: tree, edits: ConcurrentEdits(fromSequential: [])), incrementalParseAffectRangeCollector
55+
fileTransition[file] = IncrementalParseTransition(
56+
previousTree: tree,
57+
edits: ConcurrentEdits(fromSequential: []),
58+
nodeAffectRangeCollector: nodeAffectRangeCollector
5859
)
5960
}
6061
}
@@ -67,8 +68,7 @@ struct PerformanceTest: ParsableCommand {
6768
file.withUnsafeBytes { buf in
6869
_ = Parser.parse(
6970
source: buf.bindMemory(to: UInt8.self),
70-
parseNodeAffectRange: fileTransition[file]?.nodeAffectRangeCollector,
71-
parseTransition: fileTransition[file]?.parseTransition
71+
parseTransition: fileTransition[file]
7272
)
7373
}
7474
}

0 commit comments

Comments
 (0)