Make the tree structure more explicit in parse dumps (#7591)

It's hard to track 2-space indent levels across large vertical gaps, and
it can be hard to suppress the instinct to read the dump as if it were
preorder. This change introduces a new dump mode that addresses both
problems by using box-drawing characters to explicitly represent the
parent-child edges of the tree. This new mode is the default, but the
old behavior remains available with
`--parse-dump-format=yaml-postorder`.
This commit is contained in:
Geoff Romer
2026-07-31 16:44:18 +00:00
committed by GitHub
parent 2c91e5f5de
commit 6429c1655c
343 changed files with 11839 additions and 12185 deletions
+68 -10
View File
@@ -140,8 +140,8 @@ auto TreeAndSubtrees::roots() const -> llvm::iterator_range<SiblingIterator> {
SiblingIterator(*this, NodeId(-1)));
}
auto TreeAndSubtrees::PrintNode(llvm::raw_ostream& output, NodeId n, int depth,
bool preorder) const -> bool {
auto TreeAndSubtrees::YamlPrintNode(llvm::raw_ostream& output, NodeId n,
int depth, bool preorder) const -> bool {
output.indent(2 * (depth + 2));
output << "{";
// If children are being added, include node_index in order to disambiguate
@@ -167,9 +167,56 @@ auto TreeAndSubtrees::PrintNode(llvm::raw_ostream& output, NodeId n, int depth,
return false;
}
auto TreeAndSubtrees::Print(llvm::raw_ostream& output) const -> void {
output << "- filename: " << tokens_->source().filename() << "\n"
<< " parse_tree: [\n";
auto TreeAndSubtrees::PrettyPrintNode(llvm::raw_ostream& output, NodeId n,
int depth,
llvm::BitVector& pending_parents) const
-> void {
output.indent(2);
// In this mode we treat all nodes as nested under the parse tree root.
++depth;
CARBON_CHECK(pending_parents.find_last() <= depth,
"Node interleaved between a non-ancestor and its child");
pending_parents.resize(depth);
for (int i = 0; i < depth; ++i) {
if (i == depth - 1) {
if (pending_parents.test(i)) {
output << "├─";
} else {
output << "╭─";
pending_parents.set(i);
}
} else {
if (pending_parents.test(i)) {
output << "│ ";
} else {
output << " ";
}
}
}
output << tree_->node_kind(n) << " '"
<< tokens_->GetTokenText(tree_->node_token(n)) << "'";
if (tree_->node_has_error(n)) {
output << " has_error";
}
}
auto TreeAndSubtrees::PrintYamlPostorder(llvm::raw_ostream& output) const
-> void {
PrintPostorder(output, /*yaml=*/true);
}
auto TreeAndSubtrees::PrettyPrint(llvm::raw_ostream& output) const -> void {
PrintPostorder(output, /*yaml=*/false);
}
auto TreeAndSubtrees::PrintPostorder(llvm::raw_ostream& output, bool yaml) const
-> void {
output << "- filename: " << tokens_->source().filename() << "\n";
if (yaml) {
output << " parse_tree: [\n";
}
// Walk the tree in reverse, just to calculate depths for each node.
llvm::SmallVector<int> depths(tree_->size(), 0);
@@ -180,14 +227,25 @@ auto TreeAndSubtrees::Print(llvm::raw_ostream& output) const -> void {
}
}
llvm::BitVector pending_parents;
for (auto [n, depth] : llvm::zip_equal(tree_->postorder(), depths)) {
PrintNode(output, n, depth, /*preorder=*/false);
output << ",\n";
if (yaml) {
YamlPrintNode(output, n, depth, /*preorder=*/false);
output << ",\n";
} else {
PrettyPrintNode(output, n, depth, pending_parents);
output << "\n";
}
}
if (yaml) {
output << " ]\n";
} else {
output << " (root)\n";
}
output << " ]\n";
}
auto TreeAndSubtrees::PrintPreorder(llvm::raw_ostream& output) const -> void {
auto TreeAndSubtrees::PrintYamlPreorder(llvm::raw_ostream& output) const
-> void {
output << "- filename: " << tokens_->source().filename() << "\n"
<< " parse_tree: [\n";
@@ -208,7 +266,7 @@ auto TreeAndSubtrees::PrintPreorder(llvm::raw_ostream& output) const -> void {
int depth;
std::tie(n, depth) = node_stack.pop_back_val();
if (PrintNode(output, n, depth, /*preorder=*/true)) {
if (YamlPrintNode(output, n, depth, /*preorder=*/true)) {
// Has children, so we descend. We append the children in order here as
// well because they will get reversed when popped off the stack.
for (NodeId sibling_n : children(n)) {