Skip to content

Commit 5879aa3

Browse files
authored
Merge pull request #1836 from ahoppen/ahoppen/5.9/generate-grammar
[5.9] Generate grammar doc comments for layout nodes
2 parents 858aafd + 476b42b commit 5879aa3

File tree

9 files changed

+1503
-0
lines changed

9 files changed

+1503
-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)Syntax``\(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)Syntax``"
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.swiftName)`: \(generator.grammar(for: $0))" }
61+
.joined(separator: "\n")
62+
}
63+
}

CodeGeneration/Sources/SyntaxSupport/Node.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,21 @@ public class Node {
8787
}
8888
}
8989

90+
public var grammar: String {
91+
guard !children.isEmpty else {
92+
return ""
93+
}
94+
95+
return """
96+
### Children
97+
98+
\(GrammarGenerator.childrenList(for: children))
99+
"""
100+
.split(separator: "\n", omittingEmptySubsequences: false)
101+
.map { "/// \($0)" }
102+
.joined(separator: "\n")
103+
}
104+
90105
init(
91106
name: String,
92107
nameForDiagnostics: String?,

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ func syntaxNode(emitKind: String) -> SourceFileSyntax {
4141
// MARK: - \(raw: node.name)
4242
4343
\(raw: nodeDoc ?? "")
44+
\(raw: node.documentation.isEmpty ? "" : "///")
45+
\(raw: node.grammar)
4446
public struct \(raw: node.name): \(raw: node.baseType.syntaxBaseName)Protocol, SyntaxHashable
4547
"""
4648
) {

0 commit comments

Comments
 (0)