[clang] a90c78a - [SyntaxTree] Implement the List construct.
Eduardo Caldas via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 10 03:32:43 PDT 2020
Author: Eduardo Caldas
Date: 2020-08-10T10:32:28Z
New Revision: a90c78ac52615d256142ecd64fbedabb612dc73f
URL: https://github.com/llvm/llvm-project/commit/a90c78ac52615d256142ecd64fbedabb612dc73f
DIFF: https://github.com/llvm/llvm-project/commit/a90c78ac52615d256142ecd64fbedabb612dc73f.diff
LOG: [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
Added:
Modified:
clang/include/clang/Tooling/Syntax/Nodes.h
clang/include/clang/Tooling/Syntax/Tree.h
clang/lib/Tooling/Syntax/Nodes.cpp
clang/lib/Tooling/Syntax/Tree.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Tooling/Syntax/Nodes.h b/clang/include/clang/Tooling/Syntax/Nodes.h
index 2273cb70307a..fc44076621d6 100644
--- a/clang/include/clang/Tooling/Syntax/Nodes.h
+++ b/clang/include/clang/Tooling/Syntax/Nodes.h
@@ -147,6 +147,8 @@ enum class NodeRole : uint8_t {
/// statement, e.g. loop body for while, for, etc; inner statement for case,
/// default, etc.
BodyStatement,
+ List_element,
+ List_delimiter,
// Roles specific to particular node kinds.
OperatorExpression_operatorToken,
diff --git a/clang/include/clang/Tooling/Syntax/Tree.h b/clang/include/clang/Tooling/Syntax/Tree.h
index d35bc6467bcc..fcd169cad3ec 100644
--- a/clang/include/clang/Tooling/Syntax/Tree.h
+++ b/clang/include/clang/Tooling/Syntax/Tree.h
@@ -191,6 +191,59 @@ class Tree : public Node {
Node *FirstChild = nullptr;
};
+/// A list of Elements separated or terminated by a fixed token.
+///
+/// This type models the following grammar construct:
+/// delimited-list(element, delimiter, termination, canBeEmpty)
+class List : public Tree {
+public:
+ template <typename Element> struct ElementAndDelimiter {
+ Element *element;
+ Leaf *delimiter;
+ };
+
+ enum class TerminationKind {
+ Terminated,
+ MaybeTerminated,
+ Separated,
+ };
+
+ using Tree::Tree;
+ /// Returns the elements and corresponding delimiters. Missing elements
+ /// and delimiters are represented as null pointers.
+ ///
+ /// For example, in a separated list:
+ /// "a, b, c" <=> [("a", ","), ("b", ","), ("c", null)]
+ /// "a, , c" <=> [("a", ","), (null, ","), ("c", ",)]
+ /// "a, b," <=> [("a", ","), ("b", ","), (null, null)]
+ ///
+ /// In a terminated or maybe-terminated list:
+ /// "a, b," <=> [("a", ","), ("b", ",")]
+ std::vector<ElementAndDelimiter<Node>> getElementsAsNodesAndDelimiters();
+
+ /// Returns the elements of the list. Missing elements are represented
+ /// as null pointers in the same way as in the return value of
+ /// `getElementsAsNodesAndDelimiters()`.
+ std::vector<Node *> getElementsAsNodes();
+
+ // These can't be implemented with the information we have!
+
+ /// Returns the appropriate delimiter for this list.
+ ///
+ /// Useful for discovering the correct delimiter to use when adding
+ /// elements to empty or one-element lists.
+ clang::tok::TokenKind getDelimiterTokenKind();
+
+ TerminationKind getTerminationKind();
+
+ /// Whether this list can be empty in syntactically and semantically correct
+ /// code.
+ ///
+ /// This list may be empty when the source code has errors even if
+ /// canBeEmpty() returns false.
+ bool canBeEmpty();
+};
+
} // namespace syntax
} // namespace clang
diff --git a/clang/lib/Tooling/Syntax/Nodes.cpp b/clang/lib/Tooling/Syntax/Nodes.cpp
index b5a4c50b2875..5e8deb668352 100644
--- a/clang/lib/Tooling/Syntax/Nodes.cpp
+++ b/clang/lib/Tooling/Syntax/Nodes.cpp
@@ -152,6 +152,10 @@ raw_ostream &syntax::operator<<(raw_ostream &OS, NodeRole R) {
return OS << "TemplateKeyword";
case syntax::NodeRole::BodyStatement:
return OS << "BodyStatement";
+ case syntax::NodeRole::List_element:
+ return OS << "List_element";
+ case syntax::NodeRole::List_delimiter:
+ return OS << "List_delimiter";
case syntax::NodeRole::CaseStatement_value:
return OS << "CaseStatement_value";
case syntax::NodeRole::IfStatement_thenStatement:
diff --git a/clang/lib/Tooling/Syntax/Tree.cpp b/clang/lib/Tooling/Syntax/Tree.cpp
index 6f9ee40a89f2..6070d1603eb8 100644
--- a/clang/lib/Tooling/Syntax/Tree.cpp
+++ b/clang/lib/Tooling/Syntax/Tree.cpp
@@ -268,3 +268,110 @@ syntax::Node *syntax::Tree::findChild(NodeRole R) {
}
return nullptr;
}
+
+std::vector<syntax::List::ElementAndDelimiter<syntax::Node>>
+syntax::List::getElementsAsNodesAndDelimiters() {
+ if (!firstChild())
+ return {};
+
+ auto children = std::vector<syntax::List::ElementAndDelimiter<Node>>();
+ syntax::Node *elementWithoutDelimiter = nullptr;
+ for (auto *C = firstChild(); C; C = C->nextSibling()) {
+ switch (C->role()) {
+ case syntax::NodeRole::List_element: {
+ if (elementWithoutDelimiter) {
+ children.push_back({elementWithoutDelimiter, nullptr});
+ }
+ elementWithoutDelimiter = C;
+ break;
+ }
+ case syntax::NodeRole::List_delimiter: {
+ children.push_back({elementWithoutDelimiter, cast<syntax::Leaf>(C)});
+ elementWithoutDelimiter = nullptr;
+ break;
+ }
+ default:
+ llvm_unreachable(
+ "A list can have only elements and delimiters as children.");
+ }
+ }
+
+ switch (getTerminationKind()) {
+ case syntax::List::TerminationKind::Separated: {
+ children.push_back({elementWithoutDelimiter, nullptr});
+ break;
+ }
+ case syntax::List::TerminationKind::Terminated:
+ case syntax::List::TerminationKind::MaybeTerminated: {
+ if (elementWithoutDelimiter) {
+ children.push_back({elementWithoutDelimiter, nullptr});
+ }
+ break;
+ }
+ }
+
+ return children;
+}
+
+// Almost the same implementation of `getElementsAsNodesAndDelimiters` but
+// ignoring delimiters
+std::vector<syntax::Node *> syntax::List::getElementsAsNodes() {
+ if (!firstChild())
+ return {};
+
+ auto children = std::vector<syntax::Node *>();
+ syntax::Node *elementWithoutDelimiter = nullptr;
+ for (auto *C = firstChild(); C; C = C->nextSibling()) {
+ switch (C->role()) {
+ case syntax::NodeRole::List_element: {
+ if (elementWithoutDelimiter) {
+ children.push_back(elementWithoutDelimiter);
+ }
+ elementWithoutDelimiter = C;
+ break;
+ }
+ case syntax::NodeRole::List_delimiter: {
+ children.push_back(elementWithoutDelimiter);
+ elementWithoutDelimiter = nullptr;
+ break;
+ }
+ default:
+ llvm_unreachable("A list has only elements or delimiters.");
+ }
+ }
+
+ switch (getTerminationKind()) {
+ case syntax::List::TerminationKind::Separated: {
+ children.push_back(elementWithoutDelimiter);
+ break;
+ }
+ case syntax::List::TerminationKind::Terminated:
+ case syntax::List::TerminationKind::MaybeTerminated: {
+ if (elementWithoutDelimiter) {
+ children.push_back(elementWithoutDelimiter);
+ }
+ break;
+ }
+ }
+
+ return children;
+}
+
+// The methods below can't be implemented without information about the derived
+// list. These methods will be implemented by switching on the derived list's
+// `NodeKind`
+
+clang::tok::TokenKind syntax::List::getDelimiterTokenKind() {
+ llvm_unreachable("There are no subclasses of List, thus "
+ "getDelimiterTokenKind() cannot be called");
+}
+
+syntax::List::TerminationKind syntax::List::getTerminationKind() {
+ llvm_unreachable("There are no subclasses of List, thus getTerminationKind() "
+ "cannot be called");
+}
+
+bool syntax::List::canBeEmpty() {
+ llvm_unreachable(
+ "There are no subclasses of List, thus canBeEmpty() cannot be called");
+}
More information about the cfe-commits
mailing list