[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,
|
/// statement, e.g. loop body for while, for, etc; inner statement for case,
|
||||||
/// default, etc.
|
/// default, etc.
|
||||||
BodyStatement,
|
BodyStatement,
|
||||||
|
List_element,
|
||||||
|
List_delimiter,
|
||||||
|
|
||||||
// Roles specific to particular node kinds.
|
// Roles specific to particular node kinds.
|
||||||
OperatorExpression_operatorToken,
|
OperatorExpression_operatorToken,
|
||||||
|
|
|
@ -191,6 +191,59 @@ private:
|
||||||
Node *FirstChild = nullptr;
|
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 syntax
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,10 @@ raw_ostream &syntax::operator<<(raw_ostream &OS, NodeRole R) {
|
||||||
return OS << "TemplateKeyword";
|
return OS << "TemplateKeyword";
|
||||||
case syntax::NodeRole::BodyStatement:
|
case syntax::NodeRole::BodyStatement:
|
||||||
return OS << "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:
|
case syntax::NodeRole::CaseStatement_value:
|
||||||
return OS << "CaseStatement_value";
|
return OS << "CaseStatement_value";
|
||||||
case syntax::NodeRole::IfStatement_thenStatement:
|
case syntax::NodeRole::IfStatement_thenStatement:
|
||||||
|
|
|
@ -268,3 +268,110 @@ syntax::Node *syntax::Tree::findChild(NodeRole R) {
|
||||||
}
|
}
|
||||||
return nullptr;
|
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