[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
This commit is contained in:
Eduardo Caldas 2020-08-04 17:33:36 +00:00
parent dbcfbffc7a
commit a90c78ac52
4 changed files with 166 additions and 0 deletions

View File

@ -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,

View File

@ -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

View File

@ -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:

View File

@ -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");
}