Skip to content

Commit 1da5f5f

Browse files
committed
Add parser entry functions for more nodes
1 parent 8105452 commit 1da5f5f

File tree

12 files changed

+562
-241
lines changed

12 files changed

+562
-241
lines changed

CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public let DECL_NODES: [Node] = [
4646
kind: .accessorBlock,
4747
base: .syntax,
4848
nameForDiagnostics: nil,
49+
parserFunction: "parseGetSet",
4950
traits: [
5051
"Braced"
5152
],

CodeGeneration/Sources/generate-swiftsyntax/GenerateSwiftSyntax.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ struct GenerateSwiftSyntax: ParsableCommand {
8282
[
8383
// SwiftParser
8484
GeneratedFileSpec(swiftParserGeneratedDir + ["IsLexerClassified.swift"], isLexerClassifiedFile),
85-
GeneratedFileSpec(swiftParserGeneratedDir + ["Parser+Entry.swift"], parserEntryFile),
85+
GeneratedFileSpec(swiftParserGeneratedDir + ["LayoutNodes+Parsable.swift"], parserEntryFile),
8686
GeneratedFileSpec(swiftParserGeneratedDir + ["Parser+TokenSpecSet.swift"], parserTokenSpecSetFile),
8787
GeneratedFileSpec(swiftParserGeneratedDir + ["TokenSpecStaticMembers.swift"], tokenSpecStaticMembersFile),
8888

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

Lines changed: 0 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -18,84 +18,6 @@ import Utils
1818
let parserEntryFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
1919
DeclSyntax("@_spi(RawSyntax) import SwiftSyntax")
2020

21-
try! ExtensionDeclSyntax("extension Parser") {
22-
DeclSyntax(
23-
"""
24-
/// Parse the source code in the given string as Swift source file. See
25-
/// `Parser.init` for more details.
26-
public static func parse(
27-
source: String
28-
) -> SourceFileSyntax {
29-
var parser = Parser(source)
30-
return SourceFileSyntax.parse(from: &parser)
31-
}
32-
"""
33-
)
34-
35-
DeclSyntax(
36-
"""
37-
/// Parse the source code in the given buffer as Swift source file. See
38-
/// `Parser.init` for more details.
39-
public static func parse(
40-
source: UnsafeBufferPointer<UInt8>,
41-
maximumNestingLevel: Int? = nil
42-
) -> SourceFileSyntax {
43-
var parser = Parser(source, maximumNestingLevel: maximumNestingLevel)
44-
return SourceFileSyntax.parse(from: &parser)
45-
}
46-
"""
47-
)
48-
49-
DeclSyntax(
50-
"""
51-
/// Parse the source code in the given string as Swift source file with support
52-
/// for incremental parsing.
53-
///
54-
/// When parsing a source file for the first time, invoke `parseIncrementally`
55-
/// with `parseTransition: nil`. This returns the initial tree as well as
56-
/// ``LookaheadRanges``. If an edit is made to the source file, an
57-
/// ``IncrementalParseTransition`` can be constructed from the initial tree
58-
/// and its ``LookaheadRanges``. When invoking `parseIncrementally` again with
59-
/// the post-edit source and that parse transition, the parser will re-use
60-
/// nodes that haven’t changed.
61-
///
62-
/// - Parameters:
63-
/// - source: The source code to parse
64-
/// - parseTransition: If a similar source file has already been parsed, the
65-
/// ``IncrementalParseTransition`` that contains the previous tree as well
66-
/// as the edits that were performed to it.
67-
/// - Returns: The parsed tree as well as the ``LookaheadRanges`` that describe
68-
/// how far the parser looked ahead while parsing a node, which is
69-
/// necessary to construct an ``IncrementalParseTransition`` for a
70-
/// subsequent incremental parse
71-
public static func parseIncrementally(
72-
source: String,
73-
parseTransition: IncrementalParseTransition?
74-
) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) {
75-
var parser = Parser(source, parseTransition: parseTransition)
76-
return (SourceFileSyntax.parse(from: &parser), parser.lookaheadRanges)
77-
}
78-
"""
79-
)
80-
81-
DeclSyntax(
82-
"""
83-
/// Parse the source code in the given buffer as Swift source file with support
84-
/// for incremental parsing.
85-
///
86-
/// See doc comments in ``Parser/parseIncrementally(source:parseTransition:)``
87-
public static func parseIncrementally(
88-
source: UnsafeBufferPointer<UInt8>,
89-
maximumNestingLevel: Int? = nil,
90-
parseTransition: IncrementalParseTransition?
91-
) -> (tree: SourceFileSyntax, lookaheadRanges: LookaheadRanges) {
92-
var parser = Parser(source, maximumNestingLevel: maximumNestingLevel, parseTransition: parseTransition)
93-
return (SourceFileSyntax.parse(from: &parser), parser.lookaheadRanges)
94-
}
95-
"""
96-
)
97-
}
98-
9921
DeclSyntax(
10022
"""
10123
public protocol SyntaxParseable: SyntaxProtocol {
@@ -128,33 +50,6 @@ let parserEntryFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
12850
}
12951

13052
try! ExtensionDeclSyntax("fileprivate extension Parser") {
131-
DeclSyntax(
132-
"""
133-
mutating func parseRemainder<R: RawSyntaxNodeProtocol>(into: R) -> R {
134-
guard !into.raw.kind.isSyntaxCollection, let layout = into.raw.layoutView else {
135-
preconditionFailure("Only support parsing of non-collection layout nodes")
136-
}
137-
138-
let remainingTokens = self.consumeRemainingTokens()
139-
if remainingTokens.isEmpty {
140-
return into
141-
}
142-
143-
let existingUnexpected: [RawSyntax]
144-
if let unexpectedNode = layout.children[layout.children.count - 1] {
145-
precondition(unexpectedNode.is(RawUnexpectedNodesSyntax.self))
146-
existingUnexpected = unexpectedNode.as(RawUnexpectedNodesSyntax.self).elements
147-
} else {
148-
existingUnexpected = []
149-
}
150-
let unexpected = RawUnexpectedNodesSyntax(elements: existingUnexpected + remainingTokens, arena: self.arena)
151-
152-
let withUnexpected = layout.replacingChild(at: layout.children.count - 1, with: unexpected.raw, arena: self.arena)
153-
return R.init(withUnexpected)!
154-
}
155-
"""
156-
)
157-
15853
DeclSyntax(
15954
"""
16055
mutating func parseNonOptionalCodeBlockItem() -> RawCodeBlockItemSyntax {

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/ResultBuildersFile.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ let resultBuildersFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
9090
DeclSyntax(
9191
"""
9292
/// Add all the elements of `expression` to this result builder, effectively flattening them.
93+
///
94+
/// - Note: This overload is disfavored to resolve an ambiguity when both the final result and
95+
/// the elements are expressible by string interpolation. In that case we favor creating a
96+
/// single element from the string literal.
97+
@_disfavoredOverload
9398
public static func buildExpression(_ expression: Self.FinalResult) -> Self.Component {
9499
return expression.map { $0 }
95100
}

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/SyntaxExpressibleByStringInterpolationConformancesFile.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,11 @@ let syntaxExpressibleByStringInterpolationConformancesFile = SourceFileSyntax(le
2121
for node in SYNTAX_NODES where node.parserFunction != nil {
2222
DeclSyntax("extension \(node.kind.syntaxType): SyntaxExpressibleByStringInterpolation {}")
2323
}
24+
25+
// `SyntaxParsable` conformance for collection nodes is hand-written.
26+
// We also need to hand-write the corresponding `SyntaxExpressibleByStringInterpolation` conformances.
27+
DeclSyntax("extension AccessorDeclListSyntax: SyntaxExpressibleByStringInterpolation {}")
28+
DeclSyntax("extension AttributeListSyntax: SyntaxExpressibleByStringInterpolation {}")
29+
DeclSyntax("extension CodeBlockItemListSyntax: SyntaxExpressibleByStringInterpolation {}")
30+
DeclSyntax("extension MemberBlockItemListSyntax: SyntaxExpressibleByStringInterpolation {}")
2431
}

Sources/SwiftParser/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_swift_host_library(SwiftParser
1010
Attributes.swift
1111
Availability.swift
1212
CharacterInfo.swift
13+
CollectionNodes+Parsable.swift
1314
Declarations.swift
1415
Directives.swift
1516
Expressions.swift
@@ -21,6 +22,7 @@ add_swift_host_library(SwiftParser
2122
Nominals.swift
2223
Parameters.swift
2324
Parser.swift
25+
ParseSourceFile.swift
2426
Patterns.swift
2527
TokenSpec.swift
2628
TokenSpecSet.swift
@@ -37,7 +39,7 @@ add_swift_host_library(SwiftParser
3739
Types.swift
3840

3941
generated/IsLexerClassified.swift
40-
generated/Parser+Entry.swift
42+
generated/LayoutNodes+Parsable.swift
4143
generated/Parser+TokenSpecSet.swift
4244
generated/TokenSpecStaticMembers.swift
4345

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
@_spi(RawSyntax) import SwiftSyntax
14+
15+
fileprivate extension SyntaxCollection {
16+
static func parse(
17+
from parser: inout Parser,
18+
parse: (_ parser: inout Parser) -> some RawSyntaxNodeProtocol,
19+
makeMissing: (_ remainingTokens: [RawSyntax], _ arena: SyntaxArena) -> some RawSyntaxNodeProtocol
20+
) -> Self {
21+
// Keep the parser alive so that the arena in which `raw` is allocated
22+
// doesn’t get deallocated before we have a chance to create a syntax node
23+
// from it. We can’t use `parser.arena` as the parameter to
24+
// `Syntax(raw:arena:)` because the node might have been re-used during an
25+
// incremental parse and would then live in a different arena than
26+
// `parser.arena`.
27+
defer {
28+
withExtendedLifetime(parser) {
29+
}
30+
}
31+
let node = parse(&parser)
32+
33+
if parser.at(.endOfFile) {
34+
return Syntax(raw: node.raw, rawNodeArena: parser.arena).cast(Self.self)
35+
}
36+
37+
let layoutView = node.raw.layoutView!
38+
39+
if layoutView.children.isEmpty {
40+
let remainingTokens = parser.consumeRemainingTokens()
41+
assert(!remainingTokens.isEmpty)
42+
let missing = makeMissing(remainingTokens, parser.arena)
43+
let raw = layoutView.insertingChild(missing.raw, at: node.raw.layoutView!.children.count, arena: parser.arena)
44+
return Syntax(raw: raw, rawNodeArena: parser.arena).cast(Self.self)
45+
} else {
46+
// First unwrap: We know that children.last exists because children is not empty
47+
// Second unwrap: This is a collection and collections never have optional children. Thus the last child can’t be nil.
48+
let lastWithRemainder = parser.parseRemainder(into: layoutView.children.last!!)
49+
let raw = layoutView.replacingChild(at: layoutView.children.count - 1, with: lastWithRemainder, arena: parser.arena)
50+
return Syntax(raw: raw, rawNodeArena: parser.arena).cast(Self.self)
51+
}
52+
}
53+
}
54+
55+
extension AccessorDeclListSyntax: SyntaxParseable {
56+
public static func parse(from parser: inout Parser) -> Self {
57+
return parse(from: &parser) { parser in
58+
return parser.parseAccessorList() ?? RawAccessorDeclListSyntax(elements: [], arena: parser.arena)
59+
} makeMissing: { remainingTokens, arena in
60+
return RawAccessorDeclSyntax(
61+
attributes: nil,
62+
modifier: nil,
63+
accessorSpecifier: RawTokenSyntax(missing: .keyword, text: "get", arena: arena),
64+
parameters: nil,
65+
effectSpecifiers: nil,
66+
initEffects: nil,
67+
body: nil,
68+
arena: arena
69+
)
70+
}
71+
}
72+
}
73+
74+
extension AttributeListSyntax: SyntaxParseable {
75+
public static func parse(from parser: inout Parser) -> Self {
76+
return parse(from: &parser) { parser in
77+
let node = parser.parseAttributeList() ?? RawAttributeListSyntax(elements: [], arena: parser.arena)
78+
return RawSyntax(node)
79+
} makeMissing: { remainingTokens, arena in
80+
return RawAttributeSyntax(
81+
atSign: RawTokenSyntax(missing: .atSign, arena: arena),
82+
attributeName: RawTypeSyntax(RawMissingTypeSyntax(arena: arena)),
83+
leftParen: nil,
84+
arguments: nil,
85+
rightParen: nil,
86+
arena: arena
87+
)
88+
}
89+
}
90+
}
91+
92+
extension CodeBlockItemListSyntax: SyntaxParseable {
93+
public static func parse(from parser: inout Parser) -> Self {
94+
return parse(from: &parser) { parser in
95+
let node = parser.parseCodeBlockItemList(until: { _ in false })
96+
return RawSyntax(node)
97+
} makeMissing: { remainingTokens, arena in
98+
let missingExpr = RawMissingExprSyntax(arena: arena)
99+
return RawCodeBlockItemSyntax(item: .expr(RawExprSyntax(missingExpr)), semicolon: nil, arena: arena)
100+
}
101+
}
102+
}
103+
104+
extension MemberBlockItemListSyntax: SyntaxParseable {
105+
public static func parse(from parser: inout Parser) -> Self {
106+
return parse(from: &parser) { parser in
107+
return RawSyntax(parser.parseMemberDeclList())
108+
} makeMissing: { remainingTokens, arena in
109+
let missingDecl = RawMissingDeclSyntax(
110+
attributes: nil,
111+
modifiers: nil,
112+
placeholder: RawTokenSyntax(missing: .identifier, text: "<#declaration#>", arena: arena),
113+
RawUnexpectedNodesSyntax(remainingTokens, arena: arena),
114+
arena: arena
115+
)
116+
return RawMemberBlockItemSyntax(decl: RawDeclSyntax(missingDecl), semicolon: nil, arena: arena)
117+
}
118+
}
119+
}
120+
121+
//==========================================================================//
122+
// IMPORTANT: When adding a new conformance, also add a corrsponding //
123+
// conformance to SyntaxExpressibleByStringInterpolation. //
124+
//==========================================================================//
125+
126+
//==========================================================================//
127+
// IMPORTANT: If you are tempted to add a new conformances here please //
128+
// insert in in alphabetical order above //
129+
//==========================================================================//

0 commit comments

Comments
 (0)