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"); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits