-
Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy pathRawSyntax.swift
260 lines (235 loc) · 8.92 KB
/
RawSyntax.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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
//===------------------ RawSyntax.swift - Raw Syntax nodes ----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
import Foundation
/// Represents the raw tree structure underlying the syntax tree. These nodes
/// have no notion of identity and only provide structure to the tree. They
/// are immutable and can be freely shared between syntax nodes.
indirect enum RawSyntax: Codable {
/// A tree node with a kind, an array of children, and a source presence.
case node(SyntaxKind, [RawSyntax?], SourcePresence)
/// A token with a token kind, leading trivia, trailing trivia, and a source
/// presence.
case token(TokenKind, Trivia, Trivia, SourcePresence)
/// The syntax kind of this raw syntax.
var kind: SyntaxKind {
switch self {
case .node(let kind, _, _): return kind
case .token(_, _, _, _): return .token
}
}
var tokenKind: TokenKind? {
switch self {
case .node(_, _, _): return nil
case .token(let kind, _, _, _): return kind
}
}
/// The layout of the children of this Raw syntax node.
var layout: [RawSyntax?] {
switch self {
case .node(_, let layout, _): return layout
case .token(_, _, _, _): return []
}
}
/// The source presence of this node.
var presence: SourcePresence {
switch self {
case .node(_, _, let presence): return presence
case .token(_, _, _, let presence): return presence
}
}
/// Whether this node is present in the original source.
var isPresent: Bool {
return presence == .present
}
/// Whether this node is missing from the original source.
var isMissing: Bool {
return presence == .missing
}
/// Keys for serializing RawSyntax nodes.
enum CodingKeys: String, CodingKey {
// Keys for the `node` case
case kind, layout, presence
// Keys for the `token` case
case tokenKind, leadingTrivia, trailingTrivia
}
/// Creates a RawSyntax from the provided Foundation Decoder.
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let presence = try container.decode(SourcePresence.self, forKey: .presence)
if let kind = try container.decodeIfPresent(SyntaxKind.self, forKey: .kind) {
let layout = try container.decode([RawSyntax?].self, forKey: .layout)
self = .node(kind, layout, presence)
} else {
let kind = try container.decode(TokenKind.self, forKey: .tokenKind)
let leadingTrivia = try container.decode(Trivia.self, forKey: .leadingTrivia)
let trailingTrivia = try container.decode(Trivia.self, forKey: .trailingTrivia)
self = .token(kind, leadingTrivia, trailingTrivia, presence)
}
}
/// Encodes the RawSyntax to the provided Foundation Encoder.
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .node(kind, layout, presence):
try container.encode(kind, forKey: .kind)
try container.encode(layout, forKey: .layout)
try container.encode(presence, forKey: .presence)
case let .token(kind, leadingTrivia, trailingTrivia, presence):
try container.encode(kind, forKey: .tokenKind)
try container.encode(leadingTrivia, forKey: .leadingTrivia)
try container.encode(trailingTrivia, forKey: .trailingTrivia)
try container.encode(presence, forKey: .presence)
}
}
/// Creates a RawSyntax node that's marked missing in the source with the
/// provided kind and layout.
/// - Parameters:
/// - kind: The syntax kind underlying this node.
/// - layout: The children of this node.
/// - Returns: A new RawSyntax `.node` with the provided kind and layout, with
/// `.missing` source presence.
static func missing(_ kind: SyntaxKind) -> RawSyntax {
return .node(kind, [], .missing)
}
/// Creates a RawSyntax token that's marked missing in the source with the
/// provided kind and no leading/trailing trivia.
/// - Parameter kind: The token kind.
/// - Returns: A new RawSyntax `.token` with the provided kind, no
/// leading/trailing trivia, and `.missing` source presence.
static func missingToken(_ kind: TokenKind) -> RawSyntax {
return .token(kind, [], [], .missing)
}
/// Returns a new RawSyntax node with the provided layout instead of the
/// existing layout.
/// - Note: This function does nothing with `.token` nodes --- the same token
/// is returned.
/// - Parameter newLayout: The children of the new node you're creating.
func replacingLayout(_ newLayout: [RawSyntax?]) -> RawSyntax {
switch self {
case let .node(kind, _, presence): return .node(kind, newLayout, presence)
case .token(_, _, _, _): return self
}
}
/// Creates a new RawSyntax with the provided child appended to its layout.
/// - Parameter child: The child to append
/// - Note: This function does nothing with `.token` nodes --- the same token
/// is returned.
/// - Return: A new RawSyntax node with the provided child at the end.
func appending(_ child: RawSyntax) -> RawSyntax {
var newLayout = layout
newLayout.append(child)
return replacingLayout(newLayout)
}
/// Returns the child at the provided cursor in the layout.
/// - Parameter index: The index of the child you're accessing.
/// - Returns: The child at the provided index.
subscript<CursorType: RawRepresentable>(_ index: CursorType) -> RawSyntax?
where CursorType.RawValue == Int {
return layout[index.rawValue]
}
/// Replaces the child at the provided index in this node with the provided
/// child.
/// - Parameters:
/// - index: The index of the child to replace.
/// - newChild: The new child that should occupy that index in the node.
func replacingChild(_ index: Int,
with newChild: RawSyntax) -> RawSyntax {
precondition(index < layout.count, "Cursor \(index) reached past layout")
var newLayout = layout
newLayout[index] = newChild
return replacingLayout(newLayout)
}
}
extension RawSyntax: TextOutputStreamable {
/// Prints the RawSyntax node, and all of its children, to the provided
/// stream. This implementation must be source-accurate.
/// - Parameter stream: The stream on which to output this node.
func write<Target>(to target: inout Target)
where Target: TextOutputStream {
switch self {
case .node(_, let layout, _):
for child in layout {
guard let child = child else { continue }
child.write(to: &target)
}
case let .token(kind, leadingTrivia, trailingTrivia, presence):
guard case .present = presence else { return }
for piece in leadingTrivia {
piece.write(to: &target)
}
target.write(kind.text)
for piece in trailingTrivia {
piece.write(to: &target)
}
}
}
}
extension RawSyntax {
func accumulateAbsolutePosition(_ pos: AbsolutePosition) {
switch self {
case .node(_, let layout, _):
for child in layout {
guard let child = child else { continue }
child.accumulateAbsolutePosition(pos)
}
case let .token(kind, leadingTrivia, trailingTrivia, presence):
guard case .present = presence else { return }
for piece in leadingTrivia {
piece.accumulateAbsolutePosition(pos)
}
pos.add(text: kind.text)
for piece in trailingTrivia {
piece.accumulateAbsolutePosition(pos)
}
}
}
var leadingTrivia: Trivia? {
switch self {
case .node(_, let layout, _):
for child in layout {
guard let child = child else { continue }
guard let result = child.leadingTrivia else { continue }
return result
}
return nil
case let .token(_, leadingTrivia, _, presence):
guard case .present = presence else { return nil }
return leadingTrivia
}
}
var trailingTrivia: Trivia? {
switch self {
case .node(_, let layout, _):
for child in layout.reversed() {
guard let child = child else { continue }
guard let result = child.trailingTrivia else { continue }
return result
}
return nil
case let .token(_, _, trailingTrivia, presence):
guard case .present = presence else { return nil }
return trailingTrivia
}
}
func accumulateLeadingTrivia(_ pos: AbsolutePosition) {
guard let trivia = leadingTrivia else { return }
for piece in trivia {
piece.accumulateAbsolutePosition(pos)
}
}
func accumulateTrailingTrivia(_ pos: AbsolutePosition) {
guard let trivia = trailingTrivia else { return }
for piece in trivia {
piece.accumulateAbsolutePosition(pos)
}
}
}