Skip to content

Commit 03f748b

Browse files
authored
Merge pull request #1289 from ahoppen/ahoppen/assert-token-choices-preparation
Improve modeling of `Child` in CodeGeneration
2 parents 343f75a + a515a28 commit 03f748b

34 files changed

+1354
-2912
lines changed

CodeGeneration/Sources/SyntaxSupport/Child.swift

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,79 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
/// The kind of token a node can contain. Either a token of a specific kind or a
14+
/// keyword with the given text.
15+
public enum TokenChoice {
16+
case keyword(text: String)
17+
case token(tokenKind: String)
18+
19+
public var isKeyword: Bool {
20+
switch self {
21+
case .keyword: return true
22+
case .token: return false
23+
}
24+
}
25+
}
26+
27+
public enum ChildKind {
28+
/// The child always contains a node of the given `kind`.
29+
case node(kind: String)
30+
/// The child always contains a node that matches one of the `choices`.
31+
case nodeChoices(choices: [Child])
32+
/// The child is a collection of `kind`.
33+
case collection(kind: String, collectionElementName: String)
34+
/// The child is a token that matches one of the given `choices`.
35+
case token(choices: [TokenChoice])
36+
37+
public var isNodeChoices: Bool {
38+
if case .nodeChoices = self {
39+
return true
40+
} else {
41+
return false
42+
}
43+
}
44+
}
45+
1346
/// A child of a node, that may be declared optional or a token with a
1447
/// restricted subset of acceptable kinds or texts.
1548
public class Child {
1649
public let name: String
17-
public let syntaxKind: String
50+
public let kind: ChildKind
1851
public let description: String?
19-
public let collectionElementName: String?
2052
public let forceClassification: Bool
2153
public let isIndented: Bool
2254
public let requiresLeadingNewline: Bool
2355
public let isOptional: Bool
24-
public let textChoices: [String]
25-
public let nodeChoices: [Child]
2656
public let classification: SyntaxClassification?
27-
/// A restricted set of token kinds that will be accepted for this child.
28-
public let tokenChoices: [TokenSpec]
29-
public let tokenCanContainArbitraryText: Bool
3057

3158
public var swiftName: String {
3259
return lowercaseFirstWord(name: name)
3360
}
3461

62+
public var syntaxKind: String {
63+
switch kind {
64+
case .node(kind: let kind):
65+
return kind
66+
case .nodeChoices:
67+
return "syntax"
68+
case .collection(kind: let kind, collectionElementName: _):
69+
return kind
70+
case .token(choices: let choices):
71+
if choices.count == 1 {
72+
switch choices.first! {
73+
case .keyword: return "KeywordToken"
74+
case .token(tokenKind: let tokenKind): return tokenKind
75+
}
76+
} else {
77+
if choices.allSatisfy({ $0.isKeyword }) {
78+
return "KeywordToken"
79+
} else {
80+
return "Token"
81+
}
82+
}
83+
}
84+
}
85+
3586
public var swiftSyntaxKind: String {
3687
return lowercaseFirstWord(name: syntaxKind)
3788
}
@@ -59,11 +110,6 @@ public class Child {
59110
return SYNTAX_TOKEN_MAP[tokenKind]
60111
}
61112

62-
/// Returns the first choice from the `tokenChoices` if there are any, otherwise returns `nil`.
63-
public var mainToken: TokenSpec? {
64-
return tokenChoices.first
65-
}
66-
67113
/// Whether this child has syntax kind `UnexpectedNodes`.
68114
public var isUnexpectedNodes: Bool {
69115
syntaxKind == "UnexpectedNodes"
@@ -75,47 +121,20 @@ public class Child {
75121
/// If force_classification is also set to true, all child nodes (not only
76122
/// identifiers) inherit the syntax classification.
77123
init(name: String,
78-
kind: String,
124+
kind: ChildKind,
79125
description: String? = nil,
80126
isOptional: Bool = false,
81-
tokenChoices: [String] = [],
82-
textChoices: [String] = [],
83-
nodeChoices: [Child] = [],
84-
collectionElementName: String? = nil,
85127
classification: String? = nil,
86128
forceClassification: Bool = false,
87129
isIndented: Bool = false,
88130
requiresLeadingNewline: Bool = false) {
89131
self.name = name
90-
self.syntaxKind = kind
132+
self.kind = kind
91133
self.description = description
92-
self.collectionElementName = collectionElementName
93134
self.classification = classificationByName(classification)
94135
self.forceClassification = forceClassification
95136
self.isIndented = isIndented
96137
self.requiresLeadingNewline = requiresLeadingNewline
97138
self.isOptional = isOptional
98-
99-
self.tokenChoices = tokenChoices.compactMap { SYNTAX_TOKEN_MAP["\($0)Token"] }
100-
101-
// If mappedTokenChoices contains `nil`, the token can contain arbitrary text
102-
self.tokenCanContainArbitraryText = self.tokenChoices.contains { $0.text == nil }
103-
104-
// A list of valid text for tokens, if specified.
105-
// This will force validation logic to check the text passed into the
106-
// token against the choices.
107-
self.textChoices = textChoices
108-
109-
// A list of valid choices for a child
110-
self.nodeChoices = nodeChoices
111-
112-
// Check the choices are either empty or multiple
113-
assert(nodeChoices.count != 1)
114-
115-
// Check node choices are well-formed
116-
for choice in self.nodeChoices {
117-
assert(!choice.isOptional, "node choice \(choice.name) cannot be optional")
118-
assert(choice.nodeChoices.isEmpty, "node choice \(choice.name) cannot have further choices")
119-
}
120139
}
121140
}

CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,15 @@ public let KEYWORDS: [KeywordSpec] = [
9898
KeywordSpec("available"),
9999
KeywordSpec("await"),
100100
KeywordSpec("before"),
101+
KeywordSpec("block"),
101102
KeywordSpec("break", isLexerClassified: true, requiresTrailingSpace: true),
102103
KeywordSpec("case", isLexerClassified: true, requiresTrailingSpace: true),
103104
KeywordSpec("catch", isLexerClassified: true, requiresLeadingSpace: true),
104105
KeywordSpec("class", isLexerClassified: true, requiresTrailingSpace: true),
105106
KeywordSpec("continue", isLexerClassified: true, requiresTrailingSpace: true),
106107
KeywordSpec("convenience"),
107108
KeywordSpec("convention"),
109+
KeywordSpec("cType"),
108110
KeywordSpec("default", isLexerClassified: true),
109111
KeywordSpec("defer", isLexerClassified: true, requiresTrailingSpace: true),
110112
KeywordSpec("deinit", isLexerClassified: true, requiresTrailingSpace: true),
@@ -124,6 +126,7 @@ public let KEYWORDS: [KeywordSpec] = [
124126
KeywordSpec("extension", isLexerClassified: true, requiresTrailingSpace: true),
125127
KeywordSpec("fallthrough", isLexerClassified: true, requiresTrailingSpace: true),
126128
KeywordSpec("false", isLexerClassified: true),
129+
KeywordSpec("file"),
127130
KeywordSpec("fileprivate", isLexerClassified: true, requiresTrailingSpace: true),
128131
KeywordSpec("final"),
129132
KeywordSpec("for", isLexerClassified: true, requiresTrailingSpace: true),
@@ -145,7 +148,9 @@ public let KEYWORDS: [KeywordSpec] = [
145148
KeywordSpec("isolated"),
146149
KeywordSpec("kind"),
147150
KeywordSpec("lazy"),
151+
KeywordSpec("left"),
148152
KeywordSpec("let", isLexerClassified: true, requiresTrailingSpace: true),
153+
KeywordSpec("line"),
149154
KeywordSpec("lowerThan"),
150155
KeywordSpec("macro"),
151156
KeywordSpec("message"),

CodeGeneration/Sources/SyntaxSupport/Node.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,17 +113,15 @@ public class Node {
113113
return [
114114
Child(
115115
name: unexpectedName,
116-
kind: "UnexpectedNodes",
117-
isOptional: true,
118-
collectionElementName: unexpectedName
116+
kind: .collection(kind: "UnexpectedNodes", collectionElementName: unexpectedName),
117+
isOptional: true
119118
),
120119
child
121120
]
122-
} + (children.last != nil ? [Child(
121+
} + (!children.isEmpty ? [Child(
123122
name: "UnexpectedAfter\(children.last!.name)",
124-
kind: "UnexpectedNodes",
125-
isOptional: true,
126-
collectionElementName: "UnexpectedAfter\(children.last!.name)"
123+
kind: .collection(kind: "UnexpectedNodes", collectionElementName: "UnexpectedAfter\(children.last!.name)"),
124+
isOptional: true
127125
)] : [])
128126
}
129127

CodeGeneration/Sources/SyntaxSupport/Traits.swift.gyb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ public let TRAITS: [Trait] = [
3737
% for child in trait.children:
3838
% is_optional = ", isOptional: true" if child.is_optional else ""
3939
% description = ", description: %s" % child.description if child.description else ""
40-
Child(name: "${child.name}", kind: "${child.syntax_kind}"${is_optional}${description}),
40+
% if child.syntax_kind.endswith('Token'):
41+
% kind = f'.token(choices: [.token(tokenKind: "{child.syntax_kind}")])'
42+
% else:
43+
% kind = f'.node(kind: "{child.syntax_kind}")'
44+
% end
45+
Child(name: "${child.name}", kind: ${kind}${is_optional}${description}),
4146
% end
4247
${"],\n description: %s" % trait.description if trait.description else " ]"}
4348
),

0 commit comments

Comments
 (0)