};
CountVisits(this);
+ // The box-drawing characters that should be added as a child is rendered.
+ struct LineDecoration {
+ std::string Prefix; // Prepended to every line.
+ llvm::StringRef First; // added to the child's line.
+ llvm::StringRef Subsequent; // added to descendants' lines.
+ };
+
// We print a "#<id>" for nonterminal forest nodes that are being dumped
// multiple times.
llvm::DenseMap<const ForestNode *, size_t> ReferenceIds;
std::string Result;
constexpr Token::Index KEnd = std::numeric_limits<Token::Index>::max();
- std::function<void(const ForestNode *, unsigned, Token::Index,
- llvm::Optional<SymbolID>)>
- Dump = [&](const ForestNode *P, unsigned Level, Token::Index End,
- llvm::Optional<SymbolID> ElidedParent) {
+ std::function<void(const ForestNode *, Token::Index, llvm::Optional<SymbolID>,
+ LineDecoration &LineDec)>
+ Dump = [&](const ForestNode *P, Token::Index End,
+ llvm::Optional<SymbolID> ElidedParent,
+ LineDecoration LineDec) {
llvm::ArrayRef<const ForestNode *> Children;
auto EndOfElement = [&](size_t ChildIndex) {
return ChildIndex + 1 == Children.size()
if (Children[I]->startTokenIndex() == P->startTokenIndex() &&
EndOfElement(I) == End) {
return Dump(
- Children[I], Level, End,
- /*ElidedParent=*/ElidedParent.getValueOr(P->symbol()));
+ Children[I], End,
+ /*ElidedParent=*/ElidedParent.getValueOr(P->symbol()),
+ LineDec);
}
}
}
- // FIXME: pretty ascii trees
if (End == KEnd)
Result += llvm::formatv("[{0,3}, end) ", P->startTokenIndex());
else
Result += llvm::formatv("[{0,3}, {1,3}) ", P->startTokenIndex(), End);
- Result.append(2 * Level, ' ');
+ Result += LineDec.Prefix;
+ Result += LineDec.First;
if (ElidedParent.hasValue()) {
Result += G.symbolName(*ElidedParent);
Result += "~";
}
Result.push_back('\n');
- ++Level;
- for (size_t I = 0; I < Children.size(); ++I)
- Dump(Children[I], Level,
- P->kind() == Sequence ? EndOfElement(I) : End, llvm::None);
+ auto OldPrefixSize = LineDec.Prefix.size();
+ LineDec.Prefix += LineDec.Subsequent;
+ for (size_t I = 0; I < Children.size(); ++I) {
+ if (I == Children.size() - 1) {
+ LineDec.First = "└─";
+ LineDec.Subsequent = " ";
+ } else {
+ LineDec.First = "├─";
+ LineDec.Subsequent = "│ ";
+ }
+ Dump(Children[I], P->kind() == Sequence ? EndOfElement(I) : End,
+ llvm::None, LineDec);
+ }
+ LineDec.Prefix.resize(OldPrefixSize);
};
- Dump(this, 0, KEnd, llvm::None);
+ LineDecoration LineDec;
+ Dump(this, KEnd, llvm::None, LineDec);
return Result;
}
ruleFor("id-expression"), {&T[2]});
const auto *Add =
- &Arena.createSequence(symbol("add-expression"), ruleFor("add-expression"), {Left, &T[1], Right});
+ &Arena.createSequence(symbol("add-expression"), ruleFor("add-expression"),
+ {Left, &T[1], Right});
EXPECT_EQ(Add->dumpRecursive(*G, true),
"[ 0, end) add-expression := id-expression + id-expression\n"
- "[ 0, 1) id-expression~IDENTIFIER := tok[0]\n"
- "[ 1, 2) + := tok[1]\n"
- "[ 2, end) id-expression~IDENTIFIER := tok[2]\n");
+ "[ 0, 1) ├─id-expression~IDENTIFIER := tok[0]\n"
+ "[ 1, 2) ├─+ := tok[1]\n"
+ "[ 2, end) └─id-expression~IDENTIFIER := tok[2]\n");
EXPECT_EQ(Add->dumpRecursive(*G, false),
"[ 0, end) add-expression := id-expression + id-expression\n"
- "[ 0, 1) id-expression := IDENTIFIER\n"
- "[ 0, 1) IDENTIFIER := tok[0]\n"
- "[ 1, 2) + := tok[1]\n"
- "[ 2, end) id-expression := IDENTIFIER\n"
- "[ 2, end) IDENTIFIER := tok[2]\n");
+ "[ 0, 1) ├─id-expression := IDENTIFIER\n"
+ "[ 0, 1) │ └─IDENTIFIER := tok[0]\n"
+ "[ 1, 2) ├─+ := tok[1]\n"
+ "[ 2, end) └─id-expression := IDENTIFIER\n"
+ "[ 2, end) └─IDENTIFIER := tok[2]\n");
}
TEST_F(ForestTest, DumpAmbiguousAndRefs) {
&Arena.createAmbiguous(symbol("type"), {Alternative1, Alternative2});
EXPECT_EQ(Type->dumpRecursive(*G),
"[ 0, end) type := <ambiguous>\n"
- "[ 0, end) class-type := shared-type\n"
- "[ 0, end) class-type := shared-type\n"
- "[ 0, end) shared-type := IDENTIFIER #1\n"
- "[ 0, end) IDENTIFIER := tok[0]\n"
- "[ 0, end) enum-type := shared-type\n"
- "[ 0, end) enum-type := shared-type\n"
- "[ 0, end) shared-type := IDENTIFIER =#1\n"
- "[ 0, end) IDENTIFIER := tok[0]\n");
+ "[ 0, end) ├─class-type := shared-type\n"
+ "[ 0, end) │ └─class-type := shared-type\n"
+ "[ 0, end) │ └─shared-type := IDENTIFIER #1\n"
+ "[ 0, end) │ └─IDENTIFIER := tok[0]\n"
+ "[ 0, end) └─enum-type := shared-type\n"
+ "[ 0, end) └─enum-type := shared-type\n"
+ "[ 0, end) └─shared-type := IDENTIFIER =#1\n"
+ "[ 0, end) └─IDENTIFIER := tok[0]\n");
}
} // namespace