Skip to content

Commit 0fbe758

Browse files
authored
Merge pull request #1771 from ahoppen/ahoppen/generate-grammar
Generate grammar doc comments for layout nodes
2 parents ea70e0e + 9b128ec commit 0fbe758

File tree

9 files changed

+1523
-0
lines changed

9 files changed

+1523
-0
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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+
import SwiftSyntax
14+
15+
/// Generates grammar doc comments for syntax nodes.
16+
struct GrammarGenerator {
17+
private func grammar(for tokenChoice: TokenChoice) -> String {
18+
switch tokenChoice {
19+
case .keyword(text: let text):
20+
return "`'\(text)'`"
21+
case .token(tokenKind: let tokenKind):
22+
let token = SYNTAX_TOKEN_MAP[tokenKind]!
23+
if let tokenText = token.text {
24+
return "`'\(tokenText)'`"
25+
} else {
26+
return "`<\(token.swiftKind)>`"
27+
}
28+
}
29+
}
30+
31+
private func grammar(for child: Child) -> String {
32+
let optionality = child.isOptional ? "?" : ""
33+
switch child.kind {
34+
case .node(let kind):
35+
return "``\(kind.syntaxType)``\(optionality)"
36+
case .nodeChoices(let choices):
37+
let choicesDescriptions = choices.map { grammar(for: $0) }
38+
return "(\(choicesDescriptions.joined(separator: " | ")))\(optionality)"
39+
case .collection(let kind, _, _):
40+
return "``\(kind.syntaxType)``"
41+
case .token(let choices, _, _):
42+
if choices.count == 1 {
43+
return "\(grammar(for: choices.first!))\(optionality)"
44+
} else {
45+
let choicesDescriptions = choices.map { grammar(for: $0) }
46+
return "(\(choicesDescriptions.joined(separator: " | ")))\(optionality)"
47+
}
48+
}
49+
}
50+
51+
/// Generates a markdown list containing the children’s names and their
52+
/// grammar.
53+
///
54+
/// - Parameter children: The children to show in the list
55+
static func childrenList(for children: [Child]) -> String {
56+
let generator = GrammarGenerator()
57+
return
58+
children
59+
.filter { !$0.isUnexpectedNodes }
60+
.map { " - `\($0.varName)`: \(generator.grammar(for: $0))" }
61+
.joined(separator: "\n")
62+
}
63+
}

CodeGeneration/Sources/SyntaxSupport/Node.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,20 @@ public struct LayoutNode {
220220
preconditionFailure("NodeLayoutView must wrap a Node with data `.layout`")
221221
}
222222
}
223+
224+
public var grammar: SwiftSyntax.Trivia {
225+
guard !children.isEmpty else {
226+
return []
227+
}
228+
229+
return docCommentTrivia(
230+
from: """
231+
### Children
232+
233+
\(GrammarGenerator.childrenList(for: children))
234+
"""
235+
)
236+
}
223237
}
224238

225239
/// Provides a view into a collection node that offers access to the

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxNodesFile.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ func syntaxNode(emitKind: SyntaxNodeKind) -> SourceFileSyntax {
4040
// MARK: - \(raw: node.kind.syntaxType)
4141
4242
\(raw: node.documentation)
43+
\(raw: node.documentation.isEmpty ? "" : "///")
44+
\(raw: node.grammar)
4345
public struct \(raw: node.kind.syntaxType): \(raw: node.baseType.syntaxBaseName)Protocol, SyntaxHashable
4446
"""
4547
) {

0 commit comments

Comments
 (0)