Skip to content

Commit c725aec

Browse files
authored
Merge pull request swiftlang#74 from akyrtzi/opt3-rawsyntax
Optimize creation of RawSyntax objects for parsing
2 parents 581cf8f + ee51cda commit c725aec

13 files changed

+1017
-251
lines changed

Sources/SwiftSyntax/RawSyntax.swift

Lines changed: 943 additions & 114 deletions
Large diffs are not rendered by default.

Sources/SwiftSyntax/Syntax.swift

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -191,14 +191,14 @@ extension _SyntaxBase {
191191
/// the first token syntax contained by this node. Without such token, this
192192
/// property will return nil.
193193
var leadingTrivia: Trivia? {
194-
return raw.leadingTrivia
194+
return raw.formLeadingTrivia()
195195
}
196196

197197
/// The trailing trivia of this syntax node. Trailing trivia is attached to
198198
/// the last token syntax contained by this node. Without such token, this
199199
/// property will return nil.
200200
var trailingTrivia: Trivia? {
201-
return raw.trailingTrivia
201+
return raw.formTrailingTrivia()
202202
}
203203

204204
/// The length this node's leading trivia takes up spelled out in source.
@@ -497,7 +497,7 @@ public struct TokenSyntax: _SyntaxBase, Hashable {
497497
fatalError("TokenSyntax must have token as its raw")
498498
}
499499
let newRaw = RawSyntax.createAndCalcLength(kind: tokenKind,
500-
leadingTrivia: raw.leadingTrivia!, trailingTrivia: raw.trailingTrivia!,
500+
leadingTrivia: raw.formLeadingTrivia()!, trailingTrivia: raw.formTrailingTrivia()!,
501501
presence: raw.presence)
502502
let newData = data.replacingSelf(newRaw)
503503
return TokenSyntax(newData)
@@ -509,8 +509,8 @@ public struct TokenSyntax: _SyntaxBase, Hashable {
509509
guard raw.kind == .token else {
510510
fatalError("TokenSyntax must have token as its raw")
511511
}
512-
let newRaw = RawSyntax.createAndCalcLength(kind: raw.tokenKind!,
513-
leadingTrivia: leadingTrivia, trailingTrivia: raw.trailingTrivia!,
512+
let newRaw = RawSyntax.createAndCalcLength(kind: raw.formTokenKind()!,
513+
leadingTrivia: leadingTrivia, trailingTrivia: raw.formTrailingTrivia()!,
514514
presence: raw.presence)
515515
let newData = data.replacingSelf(newRaw)
516516
return TokenSyntax(newData)
@@ -522,8 +522,8 @@ public struct TokenSyntax: _SyntaxBase, Hashable {
522522
guard raw.kind == .token else {
523523
fatalError("TokenSyntax must have token as its raw")
524524
}
525-
let newRaw = RawSyntax.createAndCalcLength(kind: raw.tokenKind!,
526-
leadingTrivia: raw.leadingTrivia!,
525+
let newRaw = RawSyntax.createAndCalcLength(kind: raw.formTokenKind()!,
526+
leadingTrivia: raw.formLeadingTrivia()!,
527527
trailingTrivia: trailingTrivia,
528528
presence: raw.presence)
529529
let newData = data.replacingSelf(newRaw)
@@ -547,26 +547,17 @@ public struct TokenSyntax: _SyntaxBase, Hashable {
547547

548548
/// The leading trivia (spaces, newlines, etc.) associated with this token.
549549
public var leadingTrivia: Trivia {
550-
guard raw.kind == .token else {
551-
fatalError("TokenSyntax must have token as its raw")
552-
}
553-
return raw.leadingTrivia!
550+
return raw.formLeadingTrivia()!
554551
}
555552

556553
/// The trailing trivia (spaces, newlines, etc.) associated with this token.
557554
public var trailingTrivia: Trivia {
558-
guard raw.kind == .token else {
559-
fatalError("TokenSyntax must have token as its raw")
560-
}
561-
return raw.trailingTrivia!
555+
return raw.formTrailingTrivia()!
562556
}
563557

564558
/// The kind of token this node represents.
565559
public var tokenKind: TokenKind {
566-
guard raw.kind == .token else {
567-
fatalError("TokenSyntax must have token as its raw")
568-
}
569-
return raw.tokenKind!
560+
return raw.formTokenKind()!
570561
}
571562

572563
/// The length this node takes up spelled out in the source, excluding its

Sources/SwiftSyntax/SyntaxBuilders.swift.gyb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ public struct ${Builder} {
4545
if let list = layout[idx] {
4646
layout[idx] = list.appending(elt.raw)
4747
} else {
48-
layout[idx] = RawSyntax(kind: SyntaxKind.${child.swift_syntax_kind},
49-
layout: [elt.raw], length: elt.raw.totalLength,
50-
presence: SourcePresence.present)
48+
layout[idx] = RawSyntax.create(kind: SyntaxKind.${child.swift_syntax_kind},
49+
layout: [elt.raw], length: elt.raw.totalLength,
50+
presence: SourcePresence.present)
5151
}
5252
}
5353
% else:

Sources/SwiftSyntax/SyntaxChildren.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ struct RawSyntaxChildren: Sequence {
2929

3030
mutating func next() -> (RawSyntax?, AbsoluteSyntaxInfo)? {
3131
let idx = Int(nextChildInfo.indexInParent)
32-
guard idx < parent.layout.endIndex else {
32+
guard idx < parent.numberOfChildren else {
3333
return nil
3434
}
35-
let child = parent.layout[idx]
35+
let child = parent.child(at: idx)
3636
let thisItem = (child, nextChildInfo)
3737
nextChildInfo = nextChildInfo.advancedBySibling(child)
3838
return thisItem
@@ -115,10 +115,10 @@ struct ReversedPresentRawSyntaxChildren: Sequence {
115115
mutating func next() -> AbsoluteRawSyntax? {
116116
while true {
117117
let prevIdx = Int(previousChildInfo.indexInParent)
118-
guard prevIdx > parent.layout.startIndex else {
118+
guard prevIdx > 0 else {
119119
return nil
120120
}
121-
let child = parent.layout[prevIdx-1]
121+
let child = parent.child(at: prevIdx-1)
122122
previousChildInfo = previousChildInfo.reversedBySibling(child)
123123
if let n = child, n.isPresent {
124124
return AbsoluteRawSyntax(raw: n, info: previousChildInfo)

Sources/SwiftSyntax/SyntaxCollections.swift.gyb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public struct ${node.name}: _SyntaxBase, Hashable, SyntaxCollection {
6262
/// - Returns: A new `${node.name}` with that element appended to the end.
6363
public func appending(
6464
_ syntax: ${node.collection_element_type}) -> ${node.name} {
65-
var newLayout = data.raw.layout
65+
var newLayout = data.raw.formLayoutArray()
6666
newLayout.append(syntax.raw)
6767
return replacingLayout(newLayout)
6868
}
@@ -88,7 +88,7 @@ public struct ${node.name}: _SyntaxBase, Hashable, SyntaxCollection {
8888
/// - Returns: A new `${node.name}` with that element appended to the end.
8989
public func inserting(_ syntax: ${node.collection_element_type},
9090
at index: Int) -> ${node.name} {
91-
var newLayout = data.raw.layout
91+
var newLayout = data.raw.formLayoutArray()
9292
/// Make sure the index is a valid insertion index (0 to 1 past the end)
9393
precondition((newLayout.startIndex...newLayout.endIndex).contains(index),
9494
"inserting node at invalid index \(index)")
@@ -106,7 +106,7 @@ public struct ${node.name}: _SyntaxBase, Hashable, SyntaxCollection {
106106
/// - Returns: A new `${node.name}` with the new element at the provided index.
107107
public func replacing(childAt index: Int,
108108
with syntax: ${node.collection_element_type}) -> ${node.name} {
109-
var newLayout = data.raw.layout
109+
var newLayout = data.raw.formLayoutArray()
110110
/// Make sure the index is a valid index for replacing
111111
precondition((newLayout.startIndex..<newLayout.endIndex).contains(index),
112112
"replacing node at invalid index \(index)")
@@ -121,7 +121,7 @@ public struct ${node.name}: _SyntaxBase, Hashable, SyntaxCollection {
121121
/// - Returns: A new `${node.name}` with the element at the provided index
122122
/// removed.
123123
public func removing(childAt index: Int) -> ${node.name} {
124-
var newLayout = data.raw.layout
124+
var newLayout = data.raw.formLayoutArray()
125125
newLayout.remove(at: index)
126126
return replacingLayout(newLayout)
127127
}
@@ -130,7 +130,7 @@ public struct ${node.name}: _SyntaxBase, Hashable, SyntaxCollection {
130130
///
131131
/// - Returns: A new `${node.name}` with the first element removed.
132132
public func removingFirst() -> ${node.name} {
133-
var newLayout = data.raw.layout
133+
var newLayout = data.raw.formLayoutArray()
134134
newLayout.removeFirst()
135135
return replacingLayout(newLayout)
136136
}
@@ -139,7 +139,7 @@ public struct ${node.name}: _SyntaxBase, Hashable, SyntaxCollection {
139139
///
140140
/// - Returns: A new `${node.name}` with the last element removed.
141141
public func removingLast() -> ${node.name} {
142-
var newLayout = data.raw.layout
142+
var newLayout = data.raw.formLayoutArray()
143143
newLayout.removeLast()
144144
return replacingLayout(newLayout)
145145
}

Sources/SwiftSyntax/SyntaxData.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ struct SyntaxData {
152152
/// normally the Syntax node that this `SyntaxData` belongs to.
153153
/// - Returns: The child's data at the provided index.
154154
func child(at index: Int, parent: _SyntaxBase) -> SyntaxData? {
155-
if raw.layout[index] == nil { return nil }
155+
if !raw.hasChild(at: index) { return nil }
156156
var iter = RawSyntaxChildren(absoluteRaw).makeIterator()
157157
for _ in 0..<index { _ = iter.next() }
158158
let (raw, info) = iter.next()!

Sources/SwiftSyntax/SyntaxFactory.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public enum SyntaxFactory {
8484

8585
% if not node.is_base():
8686
public static func makeBlank${node.syntax_kind}() -> ${node.name} {
87-
let data = SyntaxData.forRoot(RawSyntax(kind: .${node.swift_syntax_kind},
87+
let data = SyntaxData.forRoot(RawSyntax.create(kind: .${node.swift_syntax_kind},
8888
layout: [
8989
% for child in node.children:
9090
% if child.is_optional:

Sources/SwiftSyntax/SyntaxNodes.swift.gyb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,8 @@ public struct ${node.name}: ${base_type}, _SyntaxBase, Hashable {
130130
if let col = raw[Cursor.${child.swift_name}] {
131131
collection = col.appending(element.raw)
132132
} else {
133-
collection = RawSyntax(kind: SyntaxKind.${child_node.swift_syntax_kind},
134-
layout: [element.raw],
135-
length: element.raw.totalLength,
136-
presence: .present)
133+
collection = RawSyntax.create(kind: SyntaxKind.${child_node.swift_syntax_kind},
134+
layout: [element.raw], length: element.raw.totalLength, presence: .present)
137135
}
138136
let newData = data.replacingChild(collection,
139137
at: Cursor.${child.swift_name})

Sources/SwiftSyntax/SyntaxParser.swift

Lines changed: 5 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ typealias CTriviaPiecePtr = UnsafePointer<CTriviaPiece>
2424
typealias CSyntaxKind = swiftparse_syntax_kind_t
2525
typealias CTokenKind = swiftparse_token_kind_t
2626
typealias CTriviaKind = swiftparse_trivia_kind_t
27+
typealias CTokenData = swiftparse_token_data_t
28+
typealias CLayoutData = swiftparse_layout_data_t
2729
typealias CParseLookupResult = swiftparse_lookup_result_t
30+
typealias CClientNode = swiftparse_client_node_t
2831

2932
/// A list of possible errors that could be encountered while parsing a
3033
/// Syntax tree.
@@ -128,7 +131,7 @@ public struct SyntaxParser {
128131
}
129132

130133
let nodeHandler = { (cnode: CSyntaxNodePtr!) -> UnsafeMutableRawPointer in
131-
let node = makeRawNode(cnode, source: source)
134+
let node = RawSyntax.create(from: cnode, source: source)
132135
// Transfer ownership of the object to the C parser. We get ownership back
133136
// via `moveFromCRawNode()`.
134137
let bits = Unmanaged.passRetained(node)
@@ -152,98 +155,8 @@ public struct SyntaxParser {
152155
}
153156

154157
let c_top = swiftparse_parse_string(c_parser, source)
155-
return moveFromCRawNode(c_top)!
156-
}
157-
}
158-
159-
fileprivate
160-
func makeRawToken(_ c_node: CSyntaxNode, source: String) -> RawSyntax {
161-
let tokdat = c_node.token_data
162-
let kind = tokdat.kind
163-
var leadingTriviaLen = 0
164-
for i in 0..<Int(tokdat.leading_trivia_count) {
165-
leadingTriviaLen += Int(tokdat.leading_trivia![i].length)
166-
}
167-
var trailingTriviaLen = 0
168-
for i in 0..<Int(tokdat.trailing_trivia_count) {
169-
trailingTriviaLen += Int(tokdat.trailing_trivia![i].length)
170-
}
171-
172-
let offset = Int(c_node.range.offset)
173-
let totalLen = Int(c_node.range.length)
174-
let tokOffset = offset + leadingTriviaLen
175-
let tokLen = totalLen - (leadingTriviaLen + trailingTriviaLen)
176-
177-
let text = source.utf8Slice(offset: tokOffset, length: tokLen)
178-
let tokKind = TokenKind.fromRawValue(kind: kind, text: text)
179-
let leadingTrivia = toTrivia(tokdat.leading_trivia,
180-
count: Int(tokdat.leading_trivia_count),
181-
offset: offset, source: source)
182-
let trailingTrivia = toTrivia(tokdat.trailing_trivia,
183-
count: Int(tokdat.trailing_trivia_count),
184-
offset: tokOffset+tokLen, source: source)
185-
let presence: SourcePresence = c_node.present ? .present : .missing
186-
return RawSyntax(kind: tokKind, leadingTrivia: leadingTrivia,
187-
trailingTrivia: trailingTrivia,
188-
length: SourceLength(utf8Length: totalLen),
189-
presence: presence)
190-
}
191-
192-
fileprivate
193-
func makeRawNode(_ cnodeptr: CSyntaxNodePtr, source: String) -> RawSyntax {
194-
let cnode = cnodeptr.pointee
195-
let kind = SyntaxKind.fromRawValue(cnode.kind)
196-
if kind == .token {
197-
return makeRawToken(cnode, source: source)
198-
} else {
199-
var layout = [RawSyntax?]()
200-
layout.reserveCapacity(Int(cnode.layout_data.nodes_count))
201-
for i in 0..<Int(cnode.layout_data.nodes_count) {
202-
// The parser guarantees that the `RawSyntax` pointers we passed via the
203-
// node handler, we'll get them back in a depth-first fashion.
204-
// From the memory management perspective we gave ownership of the
205-
// `RawSyntax` object to the C parser via the node handler and now we get
206-
// ownership back.
207-
let subnode = moveFromCRawNode(cnode.layout_data.nodes![i])
208-
layout.append(subnode)
209-
}
210-
let totalLen = Int(cnode.range.length)
211-
let presence: SourcePresence = cnode.present ? .present : .missing
212-
return RawSyntax(kind: kind, layout: layout,
213-
length: SourceLength(utf8Length: totalLen), presence: presence)
214-
}
215-
}
216158

217-
fileprivate
218-
func moveFromCRawNode(_ ptr: UnsafeMutableRawPointer?) -> RawSyntax? {
219-
guard let ptr = ptr else {
220-
return nil
221-
}
222159
// Get ownership back from the C parser.
223-
let node: RawSyntax = Unmanaged.fromOpaque(ptr).takeRetainedValue()
224-
return node
225-
}
226-
227-
fileprivate
228-
func toTrivia(_ cptr: CTriviaPiecePtr?, count: Int, offset: Int,
229-
source: String) -> Trivia {
230-
var pieces = [TriviaPiece]()
231-
pieces.reserveCapacity(count)
232-
var offs = offset
233-
for i in 0..<count {
234-
let c_piece = cptr![i]
235-
let piece = toTriviaPiece(c_piece, offset: offs, source: source)
236-
pieces.append(piece)
237-
offs += Int(c_piece.length)
160+
return RawSyntax.moveFromOpaque(c_top)!
238161
}
239-
return Trivia(pieces: pieces)
240-
}
241-
242-
fileprivate
243-
func toTriviaPiece(_ c_piece: CTriviaPiece, offset: Int,
244-
source: String) -> TriviaPiece {
245-
let kind = c_piece.kind
246-
let text = source.utf8Slice(offset: offset, length: Int(c_piece.length))
247-
return TriviaPiece.fromRawValue(kind: kind, length: Int(c_piece.length),
248-
text: text)
249162
}

Sources/SwiftSyntax/SyntaxRewriter.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ open class SyntaxRewriter {
9292
}
9393

9494
// Sanity check, ensure the new children are the same length.
95-
assert(newLayout.count == node.raw.layout.count)
95+
assert(newLayout.count == node.raw.numberOfChildren)
9696

9797
let newRaw = node.raw.replacingLayout(newLayout)
9898
let newData = node.base.data.replacingSelf(newRaw)

Sources/SwiftSyntax/TokenKind.swift.gyb

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,27 +105,43 @@ extension TokenKind: Equatable {
105105

106106
extension TokenKind {
107107
static func fromRawValue(kind: CTokenKind,
108-
text: Substring) -> TokenKind {
108+
textBuffer: UnsafeBufferPointer<UInt8>) -> TokenKind {
109109
switch kind {
110110
case 0: return .eof
111111
% for token in SYNTAX_TOKENS:
112112
case ${token.serialization_code}:
113113
% if token.text: # The token does not have text associated with it
114114
return .${token.swift_kind()}
115115
% else:
116-
return .${token.swift_kind()}(String(text))
116+
return .${token.swift_kind()}(.fromBuffer(textBuffer))
117117
% end
118118
% end
119119
default:
120-
if !text.isEmpty {
120+
if !textBuffer.isEmpty {
121121
// Default to an unknown token with the passed text if we don't know
122122
// its kind.
123-
return .unknown(String(text))
123+
return .unknown(.fromBuffer(textBuffer))
124124
} else {
125125
// If we were not passed the token's text, we cannot recover since we
126126
// would lose roundtripness.
127127
fatalError("unexpected token kind \(kind)")
128128
}
129129
}
130130
}
131+
132+
static func hasText(kind: CTokenKind) -> Bool {
133+
switch kind {
134+
case 0: return false
135+
% for token in SYNTAX_TOKENS:
136+
case ${token.serialization_code}:
137+
% if token.text: # The token does not have text associated with it
138+
return false
139+
% else:
140+
return true
141+
% end
142+
% end
143+
default:
144+
fatalError("unexpected token kind \(kind)")
145+
}
146+
}
131147
}

0 commit comments

Comments
 (0)