-
Notifications
You must be signed in to change notification settings - Fork 441
/
Copy pathParseSourceFile.swift
209 lines (190 loc) · 8.85 KB
/
ParseSourceFile.swift
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#if compiler(>=6)
@_spi(RawSyntax) public import SwiftSyntax
#else
@_spi(RawSyntax) import SwiftSyntax
#endif
extension Parser {
/// Parse the source code in the given string as Swift source file. See
/// `Parser.init` for more details.
public static func parse(
source: String
) -> SourceFileSyntax {
var parser = Parser(source)
return SourceFileSyntax.parse(from: &parser)
}
/// A compiler interface that allows the enabling of experimental features.
@_spi(ExperimentalLanguageFeatures)
public static func parse(
source: UnsafeBufferPointer<UInt8>,
swiftVersion: SwiftVersion? = nil,
experimentalFeatures: ExperimentalFeatures
) -> SourceFileSyntax {
var parser = Parser(source, swiftVersion: swiftVersion, experimentalFeatures: experimentalFeatures)
return SourceFileSyntax.parse(from: &parser)
}
/// Parse the source code in the given buffer as Swift source file. See
/// `Parser.init` for more details.
public static func parse(
source: UnsafeBufferPointer<UInt8>,
maximumNestingLevel: Int? = nil,
swiftVersion: SwiftVersion? = nil
) -> SourceFileSyntax {
var parser = Parser(source, maximumNestingLevel: maximumNestingLevel, swiftVersion: swiftVersion)
return SourceFileSyntax.parse(from: &parser)
}
/// Parse the source code in the given string as Swift source file with support
/// for incremental parsing.
///
/// When parsing a source file for the first time, invoke `parseIncrementally`
/// with `parseTransition: nil`. This returns the initial tree as well as
/// ``LookaheadRanges``. If an edit is made to the source file, an
/// ``IncrementalParseTransition`` can be constructed from the initial tree
/// and its ``LookaheadRanges``. When invoking `parseIncrementally` again with
/// the post-edit source and that parse transition, the parser will re-use
/// nodes that haven’t changed.
///
/// - Parameters:
/// - source: The source code to parse
/// - parseTransition: If a similar source file has already been parsed, the
/// ``IncrementalParseTransition`` that contains the previous tree as well
/// as the edits that were performed to it.
/// - Returns: The parsed tree as well as the ``LookaheadRanges`` that describe
/// how far the parser looked ahead while parsing a node, which is
/// necessary to construct an ``IncrementalParseTransition`` for a
/// subsequent incremental parse
@available(*, deprecated, message: "Use parseIncrementally with `IncrementalParseResult` return instead")
@_disfavoredOverload
public static func parseIncrementally(
source: String,
parseTransition: IncrementalParseTransition?
) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) {
let parseResult = parseIncrementally(source: source, parseTransition: parseTransition)
return (parseResult.tree, parseResult.lookaheadRanges)
}
/// Parse the source code in the given buffer as Swift source file with support
/// for incremental parsing.
///
/// See doc comments in ``Parser/parseIncrementally(source:parseTransition:)-2gtt2``
@available(*, deprecated, message: "Use parseIncrementally with `IncrementalParseResult` return instead")
@_disfavoredOverload
public static func parseIncrementally(
source: UnsafeBufferPointer<UInt8>,
maximumNestingLevel: Int? = nil,
parseTransition: IncrementalParseTransition?
) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) {
let parseResult = parseIncrementally(
source: source,
maximumNestingLevel: maximumNestingLevel,
parseTransition: parseTransition
)
return (parseResult.tree, parseResult.lookaheadRanges)
}
/// Parse the source code in the given string as Swift source file with support
/// for incremental parsing.
///
/// When parsing a source file for the first time, invoke `parseIncrementally`
/// with `parseTransition: nil`. This returns the ``IncrementalParseResult``
/// If an edit is made to the source file, an ``IncrementalParseTransition``
/// can be constructed from the ``IncrementalParseResult``.
/// When invoking `parseIncrementally` again with
/// the post-edit source and that parse transition, the parser will re-use
/// nodes that haven’t changed.
///
/// - Parameters:
/// - source: The source code to parse
/// - parseTransition: If a similar source file has already been parsed, the
/// ``IncrementalParseTransition`` that contains the previous tree as well
/// as the edits that were performed to it.
/// - Returns: The ``IncrementalParseResult``, which is
/// necessary to construct an ``IncrementalParseTransition`` for a
/// subsequent incremental parse
public static func parseIncrementally(
source: String,
parseTransition: IncrementalParseTransition?
) -> IncrementalParseResult {
var parser = Parser(source, parseTransition: parseTransition)
return IncrementalParseResult(tree: SourceFileSyntax.parse(from: &parser), lookaheadRanges: parser.lookaheadRanges)
}
/// Parse the source code in the given buffer as Swift source file with support
/// for incremental parsing.
///
/// See doc comments in ``Parser/parseIncrementally(source:parseTransition:)-dj0z``
public static func parseIncrementally(
source: UnsafeBufferPointer<UInt8>,
maximumNestingLevel: Int? = nil,
parseTransition: IncrementalParseTransition?
) -> IncrementalParseResult {
var parser = Parser(source, maximumNestingLevel: maximumNestingLevel, parseTransition: parseTransition)
return IncrementalParseResult(tree: SourceFileSyntax.parse(from: &parser), lookaheadRanges: parser.lookaheadRanges)
}
}
/// The result of incrementally parsing a file.
///
/// This contains the parsed syntax tree and additional information on how far the parser looked ahead to parse each node.
/// This information is required to perform an incremental parse of the tree after applying edits to it.
public struct IncrementalParseResult: Sendable {
/// The syntax tree from parsing source
public let tree: SourceFileSyntax
/// The lookahead ranges for syntax nodes describe
/// how far the parser looked ahead while parsing a node.
public let lookaheadRanges: LookaheadRanges
public init(tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) {
self.tree = tree
self.lookaheadRanges = lookaheadRanges
}
}
extension Parser {
mutating func parseRemainder<R: RawSyntaxNodeProtocol>(into: R) -> R {
guard !into.raw.kind.isSyntaxCollection, let layout = into.raw.layoutView else {
preconditionFailure("Only support parsing of non-collection layout nodes")
}
let remainingTokens = self.consumeRemainingTokens()
if remainingTokens.isEmpty {
return R.init(transferTrailingTrivaFromEndOfFileIfPresent(raw: into.raw))!
}
let existingUnexpected: [RawSyntax]
if let unexpectedNode = layout.children[layout.children.count - 1] {
precondition(unexpectedNode.is(RawUnexpectedNodesSyntax.self))
existingUnexpected = unexpectedNode.as(RawUnexpectedNodesSyntax.self).elements
} else {
existingUnexpected = []
}
let unexpected = RawUnexpectedNodesSyntax(elements: existingUnexpected + remainingTokens, arena: self.arena)
let withUnexpected = layout.replacingChild(at: layout.children.count - 1, with: unexpected.raw, arena: self.arena)
return R.init(transferTrailingTrivaFromEndOfFileIfPresent(raw: withUnexpected))!
}
/// Parses the end-of-file token and appends its leading trivia to the provided `RawSyntax`.
/// - Parameter raw: The raw syntax node to which the leading trivia of the end-of-file token will be appended.
/// - Returns: A new `RawSyntax` instance with trailing trivia transferred from the end-of-file token if present, otherwise it will return the raw parameter..
private mutating func transferTrailingTrivaFromEndOfFileIfPresent(raw: RawSyntax) -> RawSyntax {
guard let endOfFileToken = self.consume(if: .endOfFile),
!endOfFileToken.leadingTriviaPieces.isEmpty,
let raw = raw.withTrailingTrivia(
Trivia(
rawPieces: (raw.trailingTriviaPieces ?? []) + endOfFileToken.leadingTriviaPieces
),
arena: self.arena
)
else {
return raw
}
return raw
}
}
private extension Trivia {
init(rawPieces: [RawTriviaPiece]) {
let pieces = rawPieces.map(TriviaPiece.init(raw:))
self.init(pieces: pieces)
}
}