forked from OSchip/llvm-project
[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:
parent
dbcfbffc7a
commit
a90c78ac52
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue