diff options
| author | Eduardo Caldas <ecaldas@google.com> | 2020-08-04 17:33:36 +0000 | 
|---|---|---|
| committer | Eduardo Caldas <ecaldas@google.com> | 2020-08-10 10:32:28 +0000 | 
| commit | a90c78ac52615d256142ecd64fbedabb612dc73f (patch) | |
| tree | c1044fffd5dfea8e86add3eb3f9c0db051b1108b | |
| parent | dbcfbffc7ae46cc7b84257787681676144a1bd5f (diff) | |
| download | llvm-a90c78ac52615d256142ecd64fbedabb612dc73f.zip llvm-a90c78ac52615d256142ecd64fbedabb612dc73f.tar.gz llvm-a90c78ac52615d256142ecd64fbedabb612dc73f.tar.bz2 | |
[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
| -rw-r--r-- | clang/include/clang/Tooling/Syntax/Nodes.h | 2 | ||||
| -rw-r--r-- | clang/include/clang/Tooling/Syntax/Tree.h | 53 | ||||
| -rw-r--r-- | clang/lib/Tooling/Syntax/Nodes.cpp | 4 | ||||
| -rw-r--r-- | clang/lib/Tooling/Syntax/Tree.cpp | 107 | 
4 files changed, 166 insertions, 0 deletions
| diff --git a/clang/include/clang/Tooling/Syntax/Nodes.h b/clang/include/clang/Tooling/Syntax/Nodes.h index 2273cb7..fc44076 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 d35bc64..fcd169c 100644 --- a/clang/include/clang/Tooling/Syntax/Tree.h +++ b/clang/include/clang/Tooling/Syntax/Tree.h @@ -191,6 +191,59 @@ private:    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 b5a4c50b..5e8deb6 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 6f9ee40..6070d16 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"); +} | 
