llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-tools-extra Author: Neil Nair (Neil-N4) <details> <summary>Changes</summary> Node type definitions for the Markdown parsing library under clang-doc/support. Separate Block and Inline node hierarchies using llvm::ilist_node and llvm::simple_ilist. Each node type has private fields with getters, print() for recursive output, and LLVM_DUMP_METHOD dump(). ASTContext owns the arena. Unit tests included. Assisted-by: Claude cc @<!-- -->ilovepi @<!-- -->petrhosek @<!-- -->evelez7 --- Full diff: https://github.com/llvm/llvm-project/pull/205609.diff 5 Files Affected: - (modified) clang-tools-extra/clang-doc/support/CMakeLists.txt (+1) - (added) clang-tools-extra/clang-doc/support/Markdown.cpp (+177) - (added) clang-tools-extra/clang-doc/support/Markdown.h (+258) - (modified) clang-tools-extra/unittests/clang-doc/CMakeLists.txt (+2) - (added) clang-tools-extra/unittests/clang-doc/MarkdownParserTest.cpp (+71) ``````````diff diff --git a/clang-tools-extra/clang-doc/support/CMakeLists.txt b/clang-tools-extra/clang-doc/support/CMakeLists.txt index 8ac913ffbe998..7dc11f07ff8b3 100644 --- a/clang-tools-extra/clang-doc/support/CMakeLists.txt +++ b/clang-tools-extra/clang-doc/support/CMakeLists.txt @@ -6,5 +6,6 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangDocSupport STATIC File.cpp + Markdown.cpp Utils.cpp ) diff --git a/clang-tools-extra/clang-doc/support/Markdown.cpp b/clang-tools-extra/clang-doc/support/Markdown.cpp new file mode 100644 index 0000000000000..ad29ba4789ffb --- /dev/null +++ b/clang-tools-extra/clang-doc/support/Markdown.cpp @@ -0,0 +1,177 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Markdown.h" +#include "llvm/Support/Casting.h" + +namespace clang::doc::markdown { + +//===----------------------------------------------------------------------===// +// Inline node print/dump +//===----------------------------------------------------------------------===// + +void InlineNode::print(llvm::raw_ostream &OS) const { + switch (Kind) { + case NodeKind::NK_Text: + llvm::cast<TextNode>(this)->print(OS); + break; + case NodeKind::NK_InlineCode: + llvm::cast<InlineCodeNode>(this)->print(OS); + break; + case NodeKind::NK_Emphasis: + llvm::cast<EmphasisNode>(this)->print(OS); + break; + case NodeKind::NK_Strong: + llvm::cast<StrongNode>(this)->print(OS); + break; + default: + OS << "UnknownInlineNode\n"; + break; + } +} + +LLVM_DUMP_METHOD void InlineNode::dump() const { print(llvm::errs()); } + +void TextNode::print(llvm::raw_ostream &OS) const { + OS << "TextNode: " << getText() << "\n"; +} + +LLVM_DUMP_METHOD void TextNode::dump() const { print(llvm::errs()); } + +void InlineCodeNode::print(llvm::raw_ostream &OS) const { + OS << "InlineCodeNode: " << getCode() << "\n"; +} + +LLVM_DUMP_METHOD void InlineCodeNode::dump() const { print(llvm::errs()); } + +void EmphasisNode::print(llvm::raw_ostream &OS) const { + OS << "EmphasisNode\n"; + for (const auto &Child : Children) + Child.print(OS); +} + +LLVM_DUMP_METHOD void EmphasisNode::dump() const { print(llvm::errs()); } + +void StrongNode::print(llvm::raw_ostream &OS) const { + OS << "StrongNode\n"; + for (const auto &Child : Children) + Child.print(OS); +} + +LLVM_DUMP_METHOD void StrongNode::dump() const { print(llvm::errs()); } + +//===----------------------------------------------------------------------===// +// Block node print/dump +//===----------------------------------------------------------------------===// + +void BlockNode::print(llvm::raw_ostream &OS) const { + switch (Kind) { + case NodeKind::NK_Paragraph: + llvm::cast<ParagraphNode>(this)->print(OS); + break; + case NodeKind::NK_Heading: + llvm::cast<HeadingNode>(this)->print(OS); + break; + case NodeKind::NK_FencedCode: + llvm::cast<FencedCodeNode>(this)->print(OS); + break; + case NodeKind::NK_UnorderedList: + llvm::cast<UnorderedListNode>(this)->print(OS); + break; + case NodeKind::NK_OrderedList: + llvm::cast<OrderedListNode>(this)->print(OS); + break; + case NodeKind::NK_ListItem: + llvm::cast<ListItemNode>(this)->print(OS); + break; + case NodeKind::NK_BlockQuote: + llvm::cast<BlockQuoteNode>(this)->print(OS); + break; + case NodeKind::NK_ThematicBreak: + llvm::cast<ThematicBreakNode>(this)->print(OS); + break; + case NodeKind::NK_Document: + llvm::cast<DocumentNode>(this)->print(OS); + break; + default: + OS << "UnknownBlockNode\n"; + break; + } +} + +LLVM_DUMP_METHOD void BlockNode::dump() const { print(llvm::errs()); } + +void ParagraphNode::print(llvm::raw_ostream &OS) const { + OS << "ParagraphNode\n"; + for (const auto &Child : Children) + Child.print(OS); +} + +LLVM_DUMP_METHOD void ParagraphNode::dump() const { print(llvm::errs()); } + +void HeadingNode::print(llvm::raw_ostream &OS) const { + OS << "HeadingNode: level=" << getLevel() << "\n"; + for (const auto &Child : Children) + Child.print(OS); +} + +LLVM_DUMP_METHOD void HeadingNode::dump() const { print(llvm::errs()); } + +void FencedCodeNode::print(llvm::raw_ostream &OS) const { + OS << "FencedCodeNode: lang=" << getLang() << "\n" << getCode() << "\n"; +} + +LLVM_DUMP_METHOD void FencedCodeNode::dump() const { print(llvm::errs()); } + +void ListItemNode::print(llvm::raw_ostream &OS) const { + OS << "ListItemNode\n"; + for (const auto &Child : Children) + Child.print(OS); +} + +LLVM_DUMP_METHOD void ListItemNode::dump() const { print(llvm::errs()); } + +void UnorderedListNode::print(llvm::raw_ostream &OS) const { + OS << "UnorderedListNode\n"; + for (const auto &Item : Items) + Item.print(OS); +} + +LLVM_DUMP_METHOD void UnorderedListNode::dump() const { print(llvm::errs()); } + +void OrderedListNode::print(llvm::raw_ostream &OS) const { + OS << "OrderedListNode: start=" << getStart() << "\n"; + for (const auto &Item : Items) + Item.print(OS); +} + +LLVM_DUMP_METHOD void OrderedListNode::dump() const { print(llvm::errs()); } + +void BlockQuoteNode::print(llvm::raw_ostream &OS) const { + OS << "BlockQuoteNode\n"; + for (const auto &Child : Children) + Child.print(OS); +} + +LLVM_DUMP_METHOD void BlockQuoteNode::dump() const { print(llvm::errs()); } + +void ThematicBreakNode::print(llvm::raw_ostream &OS) const { + OS << "ThematicBreakNode\n"; +} + +LLVM_DUMP_METHOD void ThematicBreakNode::dump() const { print(llvm::errs()); } + +void DocumentNode::print(llvm::raw_ostream &OS) const { + OS << "DocumentNode\n"; + for (const auto &Child : Children) + Child.print(OS); +} + +LLVM_DUMP_METHOD void DocumentNode::dump() const { print(llvm::errs()); } + +} // namespace clang::doc::markdown diff --git a/clang-tools-extra/clang-doc/support/Markdown.h b/clang-tools-extra/clang-doc/support/Markdown.h new file mode 100644 index 0000000000000..410f133b0e74d --- /dev/null +++ b/clang-tools-extra/clang-doc/support/Markdown.h @@ -0,0 +1,258 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_SUPPORT_MARKDOWN_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_SUPPORT_MARKDOWN_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/simple_ilist.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/raw_ostream.h" +#include <type_traits> + +namespace clang::doc::markdown { + +enum class NodeKind { + // Inline nodes + NK_Text, + NK_InlineCode, + NK_Emphasis, + NK_Strong, + // Block nodes + NK_Paragraph, + NK_Heading, + NK_FencedCode, + NK_Table, + NK_UnorderedList, + NK_OrderedList, + NK_ListItem, + NK_BlockQuote, + NK_ThematicBreak, + NK_Document, +}; + +struct InlineNode; +struct BlockNode; + +//===----------------------------------------------------------------------===// +// Inline nodes +//===----------------------------------------------------------------------===// + +struct InlineNode : llvm::ilist_node<InlineNode> { + NodeKind Kind; + explicit InlineNode(NodeKind K) : Kind(K) {} + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; +}; + +using InlineList = llvm::simple_ilist<InlineNode>; + +struct TextNode : InlineNode { +private: + llvm::StringRef Text; + +public: + explicit TextNode(llvm::StringRef T) + : InlineNode(NodeKind::NK_Text), Text(T) {} + llvm::StringRef getText() const { return Text; } + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; + static bool classof(const InlineNode *N) { + return N->Kind == NodeKind::NK_Text; + } +}; +static_assert(std::is_trivially_destructible_v<TextNode>); + +struct InlineCodeNode : InlineNode { +private: + llvm::StringRef Code; + +public: + explicit InlineCodeNode(llvm::StringRef C) + : InlineNode(NodeKind::NK_InlineCode), Code(C) {} + llvm::StringRef getCode() const { return Code; } + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; + static bool classof(const InlineNode *N) { + return N->Kind == NodeKind::NK_InlineCode; + } +}; +static_assert(std::is_trivially_destructible_v<InlineCodeNode>); + +struct EmphasisNode : InlineNode { + InlineList Children; + EmphasisNode() : InlineNode(NodeKind::NK_Emphasis) {} + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; + static bool classof(const InlineNode *N) { + return N->Kind == NodeKind::NK_Emphasis; + } +}; + +struct StrongNode : InlineNode { + InlineList Children; + StrongNode() : InlineNode(NodeKind::NK_Strong) {} + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; + static bool classof(const InlineNode *N) { + return N->Kind == NodeKind::NK_Strong; + } +}; + +//===----------------------------------------------------------------------===// +// Block nodes +//===----------------------------------------------------------------------===// + +struct BlockNode : llvm::ilist_node<BlockNode> { + NodeKind Kind; + explicit BlockNode(NodeKind K) : Kind(K) {} + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; +}; + +using BlockList = llvm::simple_ilist<BlockNode>; + +struct ParagraphNode : BlockNode { + InlineList Children; + ParagraphNode() : BlockNode(NodeKind::NK_Paragraph) {} + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; + static bool classof(const BlockNode *N) { + return N->Kind == NodeKind::NK_Paragraph; + } +}; + +struct HeadingNode : BlockNode { +private: + unsigned Level; + +public: + InlineList Children; + explicit HeadingNode(unsigned L) + : BlockNode(NodeKind::NK_Heading), Level(L) {} + unsigned getLevel() const { return Level; } + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; + static bool classof(const BlockNode *N) { + return N->Kind == NodeKind::NK_Heading; + } +}; + +struct FencedCodeNode : BlockNode { +private: + llvm::StringRef Lang; + llvm::StringRef Code; + +public: + FencedCodeNode(llvm::StringRef L, llvm::StringRef C) + : BlockNode(NodeKind::NK_FencedCode), Lang(L), Code(C) {} + llvm::StringRef getLang() const { return Lang; } + llvm::StringRef getCode() const { return Code; } + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; + static bool classof(const BlockNode *N) { + return N->Kind == NodeKind::NK_FencedCode; + } +}; +static_assert(std::is_trivially_destructible_v<FencedCodeNode>); + +struct ListItemNode : BlockNode, llvm::ilist_node<ListItemNode> { + InlineList Children; + ListItemNode() : BlockNode(NodeKind::NK_ListItem) {} + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; + static bool classof(const BlockNode *N) { + return N->Kind == NodeKind::NK_ListItem; + } +}; + +struct UnorderedListNode : BlockNode { + llvm::simple_ilist<ListItemNode> Items; + UnorderedListNode() : BlockNode(NodeKind::NK_UnorderedList) {} + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; + static bool classof(const BlockNode *N) { + return N->Kind == NodeKind::NK_UnorderedList; + } +}; + +struct OrderedListNode : BlockNode { +private: + unsigned Start; + +public: + llvm::simple_ilist<ListItemNode> Items; + explicit OrderedListNode(unsigned S = 1) + : BlockNode(NodeKind::NK_OrderedList), Start(S) {} + unsigned getStart() const { return Start; } + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; + static bool classof(const BlockNode *N) { + return N->Kind == NodeKind::NK_OrderedList; + } +}; + +struct BlockQuoteNode : BlockNode { + BlockList Children; + BlockQuoteNode() : BlockNode(NodeKind::NK_BlockQuote) {} + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; + static bool classof(const BlockNode *N) { + return N->Kind == NodeKind::NK_BlockQuote; + } +}; + +struct ThematicBreakNode : BlockNode { + ThematicBreakNode() : BlockNode(NodeKind::NK_ThematicBreak) {} + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; + static bool classof(const BlockNode *N) { + return N->Kind == NodeKind::NK_ThematicBreak; + } +}; + +struct DocumentNode : BlockNode { + // FIXME: add constructor that accepts children once parser is in place + BlockList Children; + DocumentNode() : BlockNode(NodeKind::NK_Document) {} + void print(llvm::raw_ostream &OS) const; + LLVM_DUMP_METHOD void dump() const; + static bool classof(const BlockNode *N) { + return N->Kind == NodeKind::NK_Document; + } +}; + +//===----------------------------------------------------------------------===// +// ASTContext - owns the arena +//===----------------------------------------------------------------------===// + +template <typename T> +using IsMarkdownNode = std::enable_if_t<std::is_base_of_v<InlineNode, T> || + std::is_base_of_v<BlockNode, T>>; + +class ASTContext { + llvm::BumpPtrAllocator Arena; + DocumentNode *Root = nullptr; + +public: + ASTContext() = default; + + template <typename T, typename... Args, typename = IsMarkdownNode<T>> + T *allocate(Args &&...args) { + return new (Arena.Allocate<T>()) T(std::forward<Args>(args)...); + } + + DocumentNode *getRoot() { return Root; } + void setRoot(DocumentNode *R) { Root = R; } +}; + +} // namespace clang::doc::markdown + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_SUPPORT_MARKDOWN_H diff --git a/clang-tools-extra/unittests/clang-doc/CMakeLists.txt b/clang-tools-extra/unittests/clang-doc/CMakeLists.txt index 01b34ec9a791e..935df6da8ac78 100644 --- a/clang-tools-extra/unittests/clang-doc/CMakeLists.txt +++ b/clang-tools-extra/unittests/clang-doc/CMakeLists.txt @@ -31,6 +31,7 @@ add_extra_unittest(ClangDocTests SerializeTest.cpp YAMLGeneratorTest.cpp JSONGeneratorTest.cpp + MarkdownParserTest.cpp ) clang_target_link_libraries(ClangDocTests @@ -49,5 +50,6 @@ clang_target_link_libraries(ClangDocTests target_link_libraries(ClangDocTests PRIVATE clangDoc + clangDocSupport LLVMTestingSupport ) diff --git a/clang-tools-extra/unittests/clang-doc/MarkdownParserTest.cpp b/clang-tools-extra/unittests/clang-doc/MarkdownParserTest.cpp new file mode 100644 index 0000000000000..1b99776e7b6eb --- /dev/null +++ b/clang-tools-extra/unittests/clang-doc/MarkdownParserTest.cpp @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "support/Markdown.h" +#include "gtest/gtest.h" + +using namespace clang::doc::markdown; +using namespace llvm; + +namespace { + +TEST(MarkdownNodeTest, TextNode) { + TextNode N("hello"); + EXPECT_EQ(N.Kind, NodeKind::NK_Text); + EXPECT_EQ(N.getText(), "hello"); +} + +TEST(MarkdownNodeTest, FencedCodeNode) { + FencedCodeNode N("cpp", R"(int x = 0; +int y = 1; +return x + y;)"); + EXPECT_EQ(N.Kind, NodeKind::NK_FencedCode); + EXPECT_EQ(N.getLang(), "cpp"); + EXPECT_TRUE(N.getCode().contains("int x = 0;")); + EXPECT_TRUE(N.getCode().contains("int y = 1;")); +} + +TEST(MarkdownNodeTest, HeadingNode) { + HeadingNode N(2); + EXPECT_EQ(N.Kind, NodeKind::NK_Heading); + EXPECT_EQ(N.getLevel(), 2u); +} + +TEST(MarkdownNodeTest, ThematicBreakNode) { + ThematicBreakNode N; + EXPECT_EQ(N.Kind, NodeKind::NK_ThematicBreak); +} + +TEST(MarkdownNodeTest, InlineCodeNode) { + InlineCodeNode N("foo()"); + EXPECT_EQ(N.Kind, NodeKind::NK_InlineCode); + EXPECT_EQ(N.getCode(), "foo()"); +} + +TEST(MarkdownNodeTest, EmphasisNode) { + EmphasisNode N; + TextNode Child("emphasized"); + N.Children.push_back(Child); + EXPECT_EQ(N.Kind, NodeKind::NK_Emphasis); + EXPECT_FALSE(N.Children.empty()); + EXPECT_EQ(llvm::cast<TextNode>(N.Children.front()).getText(), "emphasized"); +} + +TEST(MarkdownNodeTest, UnorderedListNode) { + UnorderedListNode N; + EXPECT_EQ(N.Kind, NodeKind::NK_UnorderedList); + EXPECT_TRUE(N.Items.empty()); +} + +TEST(MarkdownNodeTest, ParagraphNode) { + ParagraphNode N; + EXPECT_EQ(N.Kind, NodeKind::NK_Paragraph); + EXPECT_TRUE(N.Children.empty()); +} + +} // namespace `````````` </details> https://github.com/llvm/llvm-project/pull/205609 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
