Skip to content

Commit e8edeca

Browse files
authored
Merge pull request #485 from fwcd/syntax-buildable-wrappers-in-swift
Port `SyntaxBuildableWrappers` to Swift
2 parents 950dba9 + 4575970 commit e8edeca

16 files changed

+591
-12
lines changed

Package.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,18 @@ let package = Package(
104104
"gyb_helpers",
105105
"AttributeNodes.swift.gyb",
106106
"AvailabilityNodes.swift.gyb",
107+
"BuilderInitializableTypes.swift.gyb",
107108
"Classification.swift.gyb",
108109
"CommonNodes.swift.gyb",
109110
"DeclNodes.swift.gyb",
111+
"ExpressibleAsConformances.swift.gyb",
110112
"ExprNodes.swift.gyb",
111113
"GenericNodes.swift.gyb",
112114
"Kinds.swift.gyb",
113115
"NodeSerializationCodes.swift.gyb",
114116
"PatternNodes.swift.gyb",
115117
"StmtNodes.swift.gyb",
118+
"SyntaxBaseKinds.swift.gyb",
116119
"Tokens.swift.gyb",
117120
"Traits.swift.gyb",
118121
"Trivia.swift.gyb",
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
%{
2+
from gyb_syntax_support import *
3+
from gyb_helpers import BUILDER_INITIALIZABLE_TYPES
4+
# -*- mode: Swift -*-
5+
# Ignore the following admonition; it applies to the resulting .swift file only
6+
}%
7+
//// Automatically Generated From BuilderInitializableTypes.swift.gyb.
8+
//// Do Not Edit Directly!
9+
//===----------------------------------------------------------------------===//
10+
//
11+
// This source file is part of the Swift.org open source project
12+
//
13+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
14+
// Licensed under Apache License v2.0 with Runtime Library Exception
15+
//
16+
// See https://swift.org/LICENSE.txt for license information
17+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
18+
//
19+
//===----------------------------------------------------------------------===//
20+
21+
let BUILDER_INITIALIZABLE_TYPES: [String: String?] = [
22+
% for type, resolved_type in BUILDER_INITIALIZABLE_TYPES.items():
23+
"${type}": ${'"' + resolved_type + '"' if resolved_type is not None else 'nil'},
24+
% end
25+
]
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
%{
2+
from gyb_syntax_support import *
3+
from gyb_helpers import SYNTAX_BUILDABLE_EXPRESSIBLE_AS_CONFORMANCES
4+
# -*- mode: Swift -*-
5+
# Ignore the following admonition; it applies to the resulting .swift file only
6+
}%
7+
//// Automatically Generated From ExpressibleAsConformances.swift.gyb.
8+
//// Do Not Edit Directly!
9+
//===----------------------------------------------------------------------===//
10+
//
11+
// This source file is part of the Swift.org open source project
12+
//
13+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
14+
// Licensed under Apache License v2.0 with Runtime Library Exception
15+
//
16+
// See https://swift.org/LICENSE.txt for license information
17+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
18+
//
19+
//===----------------------------------------------------------------------===//
20+
21+
let SYNTAX_BUILDABLE_EXPRESSIBLE_AS_CONFORMANCES: [String: [String]] = [
22+
% for buildable, conformances in SYNTAX_BUILDABLE_EXPRESSIBLE_AS_CONFORMANCES.items():
23+
"${buildable}": [
24+
% for conformance in conformances:
25+
"${conformance}",
26+
% end
27+
],
28+
% end
29+
]

Sources/SwiftSyntaxBuilderGeneration/Node.swift

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,6 @@ class Node {
2828
let elementsSeparatedByNewline: Bool
2929
let collectionElement: String
3030

31-
var baseType: String {
32-
if self.baseKind == "SyntaxCollection" {
33-
return "Syntax"
34-
} else {
35-
return kindToType(kind: baseKind)
36-
}
37-
}
38-
39-
var collectionElementType: String {
40-
return kindToType(kind: collectionElement)
41-
}
42-
4331
/// Returns `True` if this node declares one of the base syntax kinds.
4432
var isBase: Bool {
4533
return SyntaxBaseKinds.contains(syntaxKind)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
%{
2+
from gyb_syntax_support import *
3+
from gyb_syntax_support.kinds import SYNTAX_BASE_KINDS
4+
# -*- mode: Swift -*-
5+
# Ignore the following admonition; it applies to the resulting .swift file only
6+
}%
7+
//// Automatically Generated From SyntaxBaseKinds.swift.gyb.
8+
//// Do Not Edit Directly!
9+
//===----------------------------------------------------------------------===//
10+
//
11+
// This source file is part of the Swift.org open source project
12+
//
13+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
14+
// Licensed under Apache License v2.0 with Runtime Library Exception
15+
//
16+
// See https://swift.org/LICENSE.txt for license information
17+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
18+
//
19+
//===----------------------------------------------------------------------===//
20+
21+
let SYNTAX_BASE_KINDS: Set<String> = [
22+
% for name in SYNTAX_BASE_KINDS:
23+
"${name}",
24+
% end
25+
]
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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+
/// Extension to the `Child` type to provide functionality specific to
14+
/// SwiftSyntaxBuilder.
15+
extension Child {
16+
/// The type of this child, represented by a `SyntaxBuildableType`, which can
17+
/// be used to create the corresponding `Buildable` and `ExpressibleAs` types.
18+
var type: SyntaxBuildableType {
19+
SyntaxBuildableType(
20+
syntaxKind: syntaxKind,
21+
isOptional: isOptional
22+
)
23+
}
24+
25+
/// If the child node has documentation associated with it, return it as single
26+
/// line string. Otherwise return an empty string.
27+
var documentation: String {
28+
flattened(indentedDocumentation: description ?? "")
29+
}
30+
31+
/// Generate a Swift expression that creates a proper SwiftSyntax node of type
32+
/// `type.syntax` from a variable named `varName` of type `type.buildable` that
33+
/// represents this child node.
34+
func generateExprBuildSyntaxNode(varName: String, formatName: String) -> String {
35+
if type.isToken {
36+
if requiresLeadingNewline {
37+
return "\(varName).withLeadingTrivia(.newline + \(formatName)._makeIndent() + (\(varName).leadingTrivia ?? []))"
38+
} else {
39+
return varName
40+
}
41+
} else {
42+
let format = formatName + (isIndented ? "._indented()" : "")
43+
let expr = "\(varName)\(type.optionalQuestionMark)"
44+
return "\(expr).build\(type.baseName)(format: \(format), leadingTrivia: nil)"
45+
}
46+
}
47+
48+
/// If this node is a token that can't contain arbitrary text, generate a Swift
49+
/// `assert` statement that verifies the variable with name var_name and of type
50+
/// `TokenSyntax` contains one of the supported text options. Otherwise return `nil`.
51+
func generateAssertStmtTextChoices(varName: String) -> String? {
52+
guard type.isToken else {
53+
return nil
54+
}
55+
56+
let choices: [String]
57+
if !textChoices.isEmpty {
58+
choices = textChoices
59+
} else if !tokenChoices.isEmpty {
60+
let optionalChoices = tokenChoices.map { SYNTAX_TOKEN_MAP["\($0.name)Token"]?.text }
61+
choices = optionalChoices.compactMap { $0 }
62+
guard choices.count == optionalChoices.count else {
63+
// If `nil` is in the text choices, one of the token options can contain arbitrary
64+
// text. Don't generate an assert statement.
65+
return nil
66+
}
67+
} else {
68+
return nil
69+
}
70+
71+
var assertChoices: [String] = []
72+
if type.isOptional {
73+
assertChoices.append("\(varName) == nil")
74+
}
75+
let unwrap = type.isOptional ? "!" : ""
76+
for textChoice in choices {
77+
assertChoices.append("\(varName)\(unwrap) == \"\(textChoice)\"")
78+
}
79+
return "assert(\(assertChoices.joined(separator: " || ")))"
80+
}
81+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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+
/// Extension to the `Node` type to provide functionality specific to
14+
/// SwiftSyntaxBuilder.
15+
extension Node {
16+
/// The node's syntax kind as `SyntaxBuildableType`.
17+
var type: SyntaxBuildableType {
18+
SyntaxBuildableType(syntaxKind: syntaxKind)
19+
}
20+
21+
/// The node's syntax kind as `SyntaxBuildableType`.
22+
var baseType: SyntaxBuildableType {
23+
SyntaxBuildableType(syntaxKind: baseKind)
24+
}
25+
26+
/// If documentation exists for this node, return it as a single-line string.
27+
/// Otherwise return an empty string.
28+
var documentation: String {
29+
guard let description = description,
30+
!description.isEmpty else {
31+
return ""
32+
}
33+
if isSyntaxCollection {
34+
return "`\(syntaxKind)` represents a collection of `\(description)`"
35+
} else {
36+
return flattened(indentedDocumentation: description)
37+
}
38+
}
39+
40+
/// Assuming this node is a syntax collection, the type of its elements.
41+
var collectionElementType: SyntaxBuildableType {
42+
assert(isSyntaxCollection)
43+
return SyntaxBuildableType(syntaxKind: collectionElement)
44+
}
45+
46+
/// Assuming this node has a single child without a default value, that child.
47+
var singleNonDefaultedChild: Child {
48+
let nonDefaultedParams = children.filter(\.type.defaultInitialization.isEmpty)
49+
assert(nonDefaultedParams.count == 1)
50+
return nonDefaultedParams[0]
51+
}
52+
53+
static func from(type: SyntaxBuildableType) -> Node {
54+
let baseName = type.baseName
55+
guard let node = SYNTAX_NODE_MAP[baseName] else {
56+
fatalError("Base name \(baseName) does not have a syntax node")
57+
}
58+
return node
59+
}
60+
}

0 commit comments

Comments
 (0)