Skip to content

Commit 4f09798

Browse files
Eduardo Caldasmemfrob
Eduardo Caldas
authored and
memfrob
committed
[SyntaxTree] Implement the List construct.
We defined a List construct to help with the implementation of list-like grammar rules. This is a first implementation of this API. Differential Revision: https://reviews.llvm.org/D85295
1 parent 142c131 commit 4f09798

File tree

4 files changed

+166
-0
lines changed

4 files changed

+166
-0
lines changed

clang/include/clang/Tooling/Syntax/Nodes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ enum class NodeRole : uint8_t {
147147
/// statement, e.g. loop body for while, for, etc; inner statement for case,
148148
/// default, etc.
149149
BodyStatement,
150+
List_element,
151+
List_delimiter,
150152

151153
// Roles specific to particular node kinds.
152154
OperatorExpression_operatorToken,

clang/include/clang/Tooling/Syntax/Tree.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,59 @@ class Tree : public Node {
191191
Node *FirstChild = nullptr;
192192
};
193193

194+
/// A list of Elements separated or terminated by a fixed token.
195+
///
196+
/// This type models the following grammar construct:
197+
/// delimited-list(element, delimiter, termination, canBeEmpty)
198+
class List : public Tree {
199+
public:
200+
template <typename Element> struct ElementAndDelimiter {
201+
Element *element;
202+
Leaf *delimiter;
203+
};
204+
205+
enum class TerminationKind {
206+
Terminated,
207+
MaybeTerminated,
208+
Separated,
209+
};
210+
211+
using Tree::Tree;
212+
/// Returns the elements and corresponding delimiters. Missing elements
213+
/// and delimiters are represented as null pointers.
214+
///
215+
/// For example, in a separated list:
216+
/// "a, b, c" <=> [("a", ","), ("b", ","), ("c", null)]
217+
/// "a, , c" <=> [("a", ","), (null, ","), ("c", ",)]
218+
/// "a, b," <=> [("a", ","), ("b", ","), (null, null)]
219+
///
220+
/// In a terminated or maybe-terminated list:
221+
/// "a, b," <=> [("a", ","), ("b", ",")]
222+
std::vector<ElementAndDelimiter<Node>> getElementsAsNodesAndDelimiters();
223+
224+
/// Returns the elements of the list. Missing elements are represented
225+
/// as null pointers in the same way as in the return value of
226+
/// `getElementsAsNodesAndDelimiters()`.
227+
std::vector<Node *> getElementsAsNodes();
228+
229+
// These can't be implemented with the information we have!
230+
231+
/// Returns the appropriate delimiter for this list.
232+
///
233+
/// Useful for discovering the correct delimiter to use when adding
234+
/// elements to empty or one-element lists.
235+
clang::tok::TokenKind getDelimiterTokenKind();
236+
237+
TerminationKind getTerminationKind();
238+
239+
/// Whether this list can be empty in syntactically and semantically correct
240+
/// code.
241+
///
242+
/// This list may be empty when the source code has errors even if
243+
/// canBeEmpty() returns false.
244+
bool canBeEmpty();
245+
};
246+
194247
} // namespace syntax
195248
} // namespace clang
196249

clang/lib/Tooling/Syntax/Nodes.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ raw_ostream &syntax::operator<<(raw_ostream &OS, NodeRole R) {
152152
return OS << "TemplateKeyword";
153153
case syntax::NodeRole::BodyStatement:
154154
return OS << "BodyStatement";
155+
case syntax::NodeRole::List_element:
156+
return OS << "List_element";
157+
case syntax::NodeRole::List_delimiter:
158+
return OS << "List_delimiter";
155159
case syntax::NodeRole::CaseStatement_value:
156160
return OS << "CaseStatement_value";
157161
case syntax::NodeRole::IfStatement_thenStatement:

clang/lib/Tooling/Syntax/Tree.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,110 @@ syntax::Node *syntax::Tree::findChild(NodeRole R) {
268268
}
269269
return nullptr;
270270
}
271+
272+
std::vector<syntax::List::ElementAndDelimiter<syntax::Node>>
273+
syntax::List::getElementsAsNodesAndDelimiters() {
274+
if (!firstChild())
275+
return {};
276+
277+
auto children = std::vector<syntax::List::ElementAndDelimiter<Node>>();
278+
syntax::Node *elementWithoutDelimiter = nullptr;
279+
for (auto *C = firstChild(); C; C = C->nextSibling()) {
280+
switch (C->role()) {
281+
case syntax::NodeRole::List_element: {
282+
if (elementWithoutDelimiter) {
283+
children.push_back({elementWithoutDelimiter, nullptr});
284+
}
285+
elementWithoutDelimiter = C;
286+
break;
287+
}
288+
case syntax::NodeRole::List_delimiter: {
289+
children.push_back({elementWithoutDelimiter, cast<syntax::Leaf>(C)});
290+
elementWithoutDelimiter = nullptr;
291+
break;
292+
}
293+
default:
294+
llvm_unreachable(
295+
"A list can have only elements and delimiters as children.");
296+
}
297+
}
298+
299+
switch (getTerminationKind()) {
300+
case syntax::List::TerminationKind::Separated: {
301+
children.push_back({elementWithoutDelimiter, nullptr});
302+
break;
303+
}
304+
case syntax::List::TerminationKind::Terminated:
305+
case syntax::List::TerminationKind::MaybeTerminated: {
306+
if (elementWithoutDelimiter) {
307+
children.push_back({elementWithoutDelimiter, nullptr});
308+
}
309+
break;
310+
}
311+
}
312+
313+
return children;
314+
}
315+
316+
// Almost the same implementation of `getElementsAsNodesAndDelimiters` but
317+
// ignoring delimiters
318+
std::vector<syntax::Node *> syntax::List::getElementsAsNodes() {
319+
if (!firstChild())
320+
return {};
321+
322+
auto children = std::vector<syntax::Node *>();
323+
syntax::Node *elementWithoutDelimiter = nullptr;
324+
for (auto *C = firstChild(); C; C = C->nextSibling()) {
325+
switch (C->role()) {
326+
case syntax::NodeRole::List_element: {
327+
if (elementWithoutDelimiter) {
328+
children.push_back(elementWithoutDelimiter);
329+
}
330+
elementWithoutDelimiter = C;
331+
break;
332+
}
333+
case syntax::NodeRole::List_delimiter: {
334+
children.push_back(elementWithoutDelimiter);
335+
elementWithoutDelimiter = nullptr;
336+
break;
337+
}
338+
default:
339+
llvm_unreachable("A list has only elements or delimiters.");
340+
}
341+
}
342+
343+
switch (getTerminationKind()) {
344+
case syntax::List::TerminationKind::Separated: {
345+
children.push_back(elementWithoutDelimiter);
346+
break;
347+
}
348+
case syntax::List::TerminationKind::Terminated:
349+
case syntax::List::TerminationKind::MaybeTerminated: {
350+
if (elementWithoutDelimiter) {
351+
children.push_back(elementWithoutDelimiter);
352+
}
353+
break;
354+
}
355+
}
356+
357+
return children;
358+
}
359+
360+
// The methods below can't be implemented without information about the derived
361+
// list. These methods will be implemented by switching on the derived list's
362+
// `NodeKind`
363+
364+
clang::tok::TokenKind syntax::List::getDelimiterTokenKind() {
365+
llvm_unreachable("There are no subclasses of List, thus "
366+
"getDelimiterTokenKind() cannot be called");
367+
}
368+
369+
syntax::List::TerminationKind syntax::List::getTerminationKind() {
370+
llvm_unreachable("There are no subclasses of List, thus getTerminationKind() "
371+
"cannot be called");
372+
}
373+
374+
bool syntax::List::canBeEmpty() {
375+
llvm_unreachable(
376+
"There are no subclasses of List, thus canBeEmpty() cannot be called");
377+
}

0 commit comments

Comments
 (0)