From 4e8ed8e99bd4ff457251d750d0d4c046ca23e937 Mon Sep 17 00:00:00 2001 From: adamska <2639980868@qq.com> Date: Wed, 4 Sep 2024 20:08:08 +0800 Subject: [PATCH 1/9] feat: set some rules for refactor in examples --- docs/examples.md | 62 ++++++++++++++++++++++++++++++++++++++++++++ src/CMakeLists.txt | 2 +- tests/CMakeLists.txt | 2 -- 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 docs/examples.md diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000..bd157a1 --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,62 @@ +# Examples + +## Document + +### Simple + +```C++ +using myxml; +// `std::string xml` +document doc = document::parse(xml); +// or +document doc = document::load(path); +// get root elem +element root = doc.root(); +// or just query elem +optional elem = doc.first_elem(); +// or query by name +optional elem = doc.first_elem("elem"); +``` + +### Element + +#### Attributes + +```C++ +// create an element with name root +element elem("root"); +// or +element elem = element::parse(xml); +// query attributes, it returns an optional string reference +fmt::println(elem["hello"]); +// modify attribute, will create new attribute if not found one +elem["hello"] = "world!"; +// if key not exist, query it will create an empty attribute +elem["hello"]; // == elem["hello"] = ""; +``` + +#### Children + +```C++ +// element root; +element child = root.first_elem(); +// or first element with name "child" +element child = root.first_elem("child"); +// elements == vector +std::vector children = root.elems(); +// or elements with same name +std::vector children = root.elems("child"); +``` + +### Text + +```C++ +// Simple Query +text txt = root.first_text(); +// print raw +fmt::println(txt); +// print trimmed text, it returns std::string and will not modify it +fmt::println(txt.trimmed()); +// yet it will modify. return a new text +fmt::println(txt.trim()); +``` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 58e0e18..9447d87 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,4 +4,4 @@ add_library(myxml ${sources}) target_include_directories(myxml PUBLIC ${PROJECT_SOURCE_DIR}/include/) -target_link_libraries(myxml fmt::fmt) \ No newline at end of file +target_link_libraries(myxml PRIVATE fmt::fmt) \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 36c49ee..a88b1ab 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,5 @@ include(CTest) - - add_executable(element_test element_test.cpp) add_executable(parser_test parser_test.cpp) add_executable(exportable_test exportable_test.cpp) From 59d6127a81d7d57a957359ddfe5e9c5054843389 Mon Sep 17 00:00:00 2001 From: adamska <2639980868@qq.com> Date: Wed, 4 Sep 2024 20:44:55 +0800 Subject: [PATCH 2/9] refactor(document.hpp): naming --- docs/examples.md | 17 ++++++--- include/myxml/cdata.hpp | 2 +- include/myxml/document.hpp | 42 +++++++++++----------- include/myxml/exportable.hpp | 8 ++--- include/myxml/node.hpp | 10 +++--- include/myxml/parser.hpp | 4 +-- include/myxml/text.hpp | 2 +- src/cdata.cpp | 2 +- src/document.cpp | 68 ++++++++++++++++++------------------ src/node.cpp | 12 +++---- src/parser.cpp | 12 +++---- tests/document_test.cpp | 16 ++++----- tests/parser_test.cpp | 6 ++-- 13 files changed, 104 insertions(+), 97 deletions(-) diff --git a/docs/examples.md b/docs/examples.md index bd157a1..f100fbf 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -11,8 +11,8 @@ document doc = document::parse(xml); // or document doc = document::load(path); // get root elem -element root = doc.root(); -// or just query elem +optional elem = doc.root().first_elem(); +// or directly query elem in root optional elem = doc.first_elem(); // or query by name optional elem = doc.first_elem("elem"); @@ -23,16 +23,23 @@ optional elem = doc.first_elem("elem"); #### Attributes ```C++ -// create an element with name root +// create an element with name 'root' element elem("root"); -// or +// or get an element from parsing element elem = element::parse(xml); -// query attributes, it returns an optional string reference +// or do it by custom string literal +element elem = ""_elem; +// query attributes, it returns an string reference fmt::println(elem["hello"]); // modify attribute, will create new attribute if not found one elem["hello"] = "world!"; // if key not exist, query it will create an empty attribute elem["hello"]; // == elem["hello"] = ""; +// use attribute to get an optional reference +if (auto value = elem.attribute("hello"); value) +{ + fmt::println("element has attribute \"hello\" with value {}", *value); +} ``` #### Children diff --git a/include/myxml/cdata.hpp b/include/myxml/cdata.hpp index a8368f3..97d2390 100644 --- a/include/myxml/cdata.hpp +++ b/include/myxml/cdata.hpp @@ -14,6 +14,6 @@ namespace myxml virtual ~CData() = default; virtual std::string ExportRaw() const override; virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const override; - virtual void SetEntityEncoding(bool) override; + virtual void entity_encoding(bool) override; }; } \ No newline at end of file diff --git a/include/myxml/document.hpp b/include/myxml/document.hpp index 8033b8c..9dfa1fb 100644 --- a/include/myxml/document.hpp +++ b/include/myxml/document.hpp @@ -6,7 +6,7 @@ // Declaration and Documant are both NOT Node namespace myxml { - struct Declaration : public Exportable + struct declaration : public exportable { std::string version; std::optional encoding; @@ -14,53 +14,53 @@ namespace myxml // return `std::nullopt` if declartion is in bad format // TODO: use exception to distinguish each of bad format - static std::optional BuildFromAttrs(std::map attrs); + static std::optional from_attrs(std::map attrs); /* Exportable */ virtual std::string ExportRaw() const; virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const; }; - class Document : public Exportable + class document : public exportable { private: - Declaration declaration; + declaration decl; std::shared_ptr root; public: /* Manipulate */ - void SetDeclaration(const Declaration &); - void SetRoot(std::shared_ptr root); + void set_declaration(const declaration &); + void set_root(std::shared_ptr root); /* Query */ - const Declaration &GetDeclartion() const; - Declaration &GetDeclartion(); - const std::shared_ptr &GetRoot() const; - std::shared_ptr GetRoot(); - std::shared_ptr Elem(std::string_view); - std::shared_ptr FirstElem(); - std::shared_ptr FirstText(); + const declaration &get_declaration() const; + declaration &get_declaration(); + const std::shared_ptr &get_root() const; + std::shared_ptr get_root(); + std::shared_ptr first_elem(std::string_view); + std::shared_ptr first_elem(); + std::shared_ptr first_text(); /** Load */ - static Document Parse(std::string_view); - static Document ParseFile(std::string fileName); + static document parse(std::string_view); + static document load(std::string fileName); /* Exportable */ virtual std::string ExportRaw() const; virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const; - virtual void SetEntityEncoding(bool); - virtual void SetPlatformSpecificNewline(bool); + virtual void entity_encoding(bool); + virtual void platform_specific_newline(bool); }; namespace util { - bool isValidXmlVersion(std::string_view); - bool isValidXmlEncoding(std::string_view); - bool isValidXmlStandalone(std::string_view); + bool is_valid_xml_version(std::string_view); + bool is_valid_xml_encoding(std::string_view); + bool is_valid_xml_standalone(std::string_view); } namespace literals { - Document operator""_doc(const char *, std::size_t); + document operator""_doc(const char *, std::size_t); } } diff --git a/include/myxml/exportable.hpp b/include/myxml/exportable.hpp index 717264e..ac15353 100644 --- a/include/myxml/exportable.hpp +++ b/include/myxml/exportable.hpp @@ -12,16 +12,16 @@ namespace myxml ExportConfig(); }; - class Exportable + class exportable { protected: ExportConfig config; public: - virtual ~Exportable() = default; + virtual ~exportable() = default; virtual std::string ExportRaw() const = 0; virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const = 0; - virtual void SetEntityEncoding(bool) {}; - virtual void SetPlatformSpecificNewline(bool) {}; + virtual void entity_encoding(bool) {}; + virtual void platform_specific_newline(bool) {}; }; } \ No newline at end of file diff --git a/include/myxml/node.hpp b/include/myxml/node.hpp index 120a1b3..5cab298 100644 --- a/include/myxml/node.hpp +++ b/include/myxml/node.hpp @@ -15,7 +15,7 @@ namespace myxml class CompositeNode; // Element, Text are Node. - class Node : public std::enable_shared_from_this, public Exportable + class Node : public std::enable_shared_from_this, public exportable { private: template >> @@ -65,8 +65,8 @@ namespace myxml std::shared_ptr PrevText(); /** Implement Export */ - virtual void SetEntityEncoding(bool) override; - virtual void SetPlatformSpecificNewline(bool) override; + virtual void entity_encoding(bool) override; + virtual void platform_specific_newline(bool) override; }; // Element are Composite Node. @@ -108,7 +108,7 @@ namespace myxml void Unlink(const std::shared_ptr &); /** Implement Export */ - virtual void SetEntityEncoding(bool) override; - virtual void SetPlatformSpecificNewline(bool) override; + virtual void entity_encoding(bool) override; + virtual void platform_specific_newline(bool) override; }; } diff --git a/include/myxml/parser.hpp b/include/myxml/parser.hpp index 0c8ad1c..83bd099 100644 --- a/include/myxml/parser.hpp +++ b/include/myxml/parser.hpp @@ -79,7 +79,7 @@ namespace myxml * @throws `SyntaxError` * @throws `SemanticError` */ - std::optional parseDeclaration(); + std::optional parseDeclaration(); public: std::shared_ptr ParseElement(); @@ -94,7 +94,7 @@ namespace myxml * @throws `SyntaxError` * @throws `SemanticError` */ - Document ParseDocument(); + document ParseDocument(); Parser() = delete; explicit Parser(std::string_view); explicit Parser(std::string &&); diff --git a/include/myxml/text.hpp b/include/myxml/text.hpp index fed8f38..e918776 100644 --- a/include/myxml/text.hpp +++ b/include/myxml/text.hpp @@ -12,7 +12,7 @@ namespace myxml public: explicit Text(std::string_view str); - virtual ~Text() = default; + virtual ~Text() = default; // may used in Export bool IsAllSpace() const; diff --git a/src/cdata.cpp b/src/cdata.cpp index c3f390f..996482a 100644 --- a/src/cdata.cpp +++ b/src/cdata.cpp @@ -18,7 +18,7 @@ namespace myxml return std::string(indentLevel * indentSize, ' ') + this->ExportRaw(); } - void CData::SetEntityEncoding(bool) + void CData::entity_encoding(bool) { // do nothing } } diff --git a/src/document.cpp b/src/document.cpp index 88692e1..6c0edbb 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -6,93 +6,93 @@ namespace myxml { - void Document::SetDeclaration(const Declaration &declaration) + void document::set_declaration(const declaration &decl) { - this->declaration = declaration; + this->decl = decl; } - void Document::SetRoot(std::shared_ptr root) + void document::set_root(std::shared_ptr root) { this->root = root; } - const Declaration &Document::GetDeclartion() const + const declaration &document::get_declaration() const { - return this->declaration; + return this->decl; } - Declaration &Document::GetDeclartion() + declaration &document::get_declaration() { - return this->declaration; + return this->decl; } - const std::shared_ptr &Document::GetRoot() const + const std::shared_ptr &document::get_root() const { return this->root; } - std::shared_ptr Document::GetRoot() + std::shared_ptr document::get_root() { return this->root; } - std::shared_ptr Document::Elem(std::string_view name) + std::shared_ptr document::first_elem(std::string_view name) { return this->root->Elem(name); } - std::shared_ptr Document::FirstElem() + std::shared_ptr document::first_elem() { return this->root->FirstElem(); } - std::shared_ptr Document::FirstText() + std::shared_ptr document::first_text() { return this->root->FirstText(); } - Document Document::Parse(std::string_view input) + document document::parse(std::string_view input) { return Parser(input).ParseDocument(); } - Document Document::ParseFile(std::string fileName) + document document::load(std::string fileName) { auto f = XMLFile::Open(fileName); return Parser(f).ParseDocument(); } - std::string Document::ExportRaw() const + std::string document::ExportRaw() const { - return this->declaration.ExportRaw() + this->root->ExportRaw(); + return this->decl.ExportRaw() + this->root->ExportRaw(); } - std::string Document::ExportFormatted(int indentLevel, int indentSize) const + std::string document::ExportFormatted(int indentLevel, int indentSize) const { - return this->declaration.ExportFormatted(indentLevel + 1, indentSize) + this->root->ExportFormatted(indentLevel + 1, indentSize); + return this->decl.ExportFormatted(indentLevel + 1, indentSize) + this->root->ExportFormatted(indentLevel + 1, indentSize); } - void Document::SetEntityEncoding(bool flag) + void document::entity_encoding(bool flag) { - this->root->SetEntityEncoding(flag); + this->root->entity_encoding(flag); } - void Document::SetPlatformSpecificNewline(bool flag) + void document::platform_specific_newline(bool flag) { - this->root->SetPlatformSpecificNewline(flag); + this->root->platform_specific_newline(flag); } - std::optional Declaration::BuildFromAttrs(std::map attrs) + std::optional declaration::from_attrs(std::map attrs) { - if (!attrs.count("version") || !util::isValidXmlVersion(attrs["version"])) + if (!attrs.count("version") || !util::is_valid_xml_version(attrs["version"])) { return std::nullopt; } - Declaration declaration; + declaration declaration; declaration.version = attrs["version"]; if (attrs.count("encoding")) { auto encoding = attrs["encoding"]; - if (!util::isValidXmlEncoding(encoding)) + if (!util::is_valid_xml_encoding(encoding)) { return std::nullopt; } @@ -101,7 +101,7 @@ namespace myxml if (attrs.count("standalone")) { auto standalone = attrs["standalone"]; - if (!util::isValidXmlStandalone(standalone)) + if (!util::is_valid_xml_standalone(standalone)) { return std::nullopt; } @@ -110,7 +110,7 @@ namespace myxml return declaration; } - std::string Declaration::ExportRaw() const + std::string declaration::ExportRaw() const { std::string builder = fmt::format("version); if (this->encoding) @@ -124,19 +124,19 @@ namespace myxml return builder + "?>\n"; } - std::string Declaration::ExportFormatted(int indentLevel, int indentSize) const + std::string declaration::ExportFormatted(int indentLevel, int indentSize) const { return std::string(' ', indentLevel * indentSize) + this->ExportRaw(); } namespace util { - bool isValidXmlVersion(std::string_view version) + bool is_valid_xml_version(std::string_view version) { return version == "1.0" || version == "1.1"; } - bool isValidXmlEncoding(std::string_view encoding) + bool is_valid_xml_encoding(std::string_view encoding) { // FIXME: not cover all valid encoding static std::set> valid{ @@ -148,14 +148,14 @@ namespace myxml return valid.count(encoding); } - bool isValidXmlStandalone(std::string_view standalone) + bool is_valid_xml_standalone(std::string_view standalone) { return standalone == "yes" || standalone == "no"; } } - Document literals::operator""_doc(const char *literal, std::size_t len) + document literals::operator""_doc(const char *literal, std::size_t len) { - return Document::Parse(std::string_view(literal, len)); + return document::parse(std::string_view(literal, len)); } } diff --git a/src/node.cpp b/src/node.cpp index 024d21e..e688984 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -4,12 +4,12 @@ namespace myxml { - void Node::SetEntityEncoding(bool flag) + void Node::entity_encoding(bool flag) { this->config.EntityEncoding = flag; } - void Node::SetPlatformSpecificNewline(bool flag) + void Node::platform_specific_newline(bool flag) { this->config.PlatformSpecificNewline = flag; } @@ -168,21 +168,21 @@ namespace myxml elem->parent = nullptr; } - void CompositeNode::SetEntityEncoding(bool flag) + void CompositeNode::entity_encoding(bool flag) { this->config.EntityEncoding = flag; for (auto it = this->FirstChild(); it != nullptr; it = it->next) { - it->SetEntityEncoding(flag); + it->entity_encoding(flag); } } - void CompositeNode::SetPlatformSpecificNewline(bool flag) + void CompositeNode::platform_specific_newline(bool flag) { this->config.PlatformSpecificNewline = flag; for (auto it = this->FirstChild(); it != nullptr; it = it->next) { - it->SetPlatformSpecificNewline(flag); + it->platform_specific_newline(flag); } } } diff --git a/src/parser.cpp b/src/parser.cpp index cce6fef..2630e3b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -299,7 +299,7 @@ namespace myxml } } - std::optional Parser::parseDeclaration() + std::optional Parser::parseDeclaration() { if (this->peekN(5) != "currentLoc(); throw SyntaxError(fmt::format("expected \"?>\" at end of xml declaration"), line, col); } - if (auto decl = Declaration::BuildFromAttrs(attrs); decl) + if (auto decl = declaration::from_attrs(attrs); decl) { return decl; } @@ -328,16 +328,16 @@ namespace myxml } } - Document Parser::ParseDocument() + document Parser::ParseDocument() { - Document document; + document document; if (auto decl = this->parseDeclaration(); decl) { - document.SetDeclaration(*decl); + document.set_declaration(*decl); } if (auto root = this->ParseElement(); root) { - document.SetRoot(root); + document.set_root(root); } else { diff --git a/tests/document_test.cpp b/tests/document_test.cpp index b5d7481..0f29074 100644 --- a/tests/document_test.cpp +++ b/tests/document_test.cpp @@ -8,10 +8,10 @@ TEST_CASE("Simple document", "[document]") std::string input = R"( Value )"; - auto doc = myxml::Document::Parse(input); - REQUIRE(doc.GetRoot()->GetName() == "root"); - REQUIRE(doc.Elem("child")->GetName() == "child"); - REQUIRE(doc.Elem("child")->FirstText()->ExportRaw() == "Value"); + auto doc = myxml::document::parse(input); + REQUIRE(doc.get_root()->GetName() == "root"); + REQUIRE(doc.first_elem("child")->GetName() == "child"); + REQUIRE(doc.first_elem("child")->FirstText()->ExportRaw() == "Value"); } SECTION("With decl") @@ -21,9 +21,9 @@ TEST_CASE("Simple document", "[document]") Value )"; - auto doc = myxml::Document::Parse(input); - REQUIRE(doc.GetDeclartion().version == "1.0"); - REQUIRE(doc.GetDeclartion().encoding == "UTF-8"); + auto doc = myxml::document::parse(input); + REQUIRE(doc.get_declaration().version == "1.0"); + REQUIRE(doc.get_declaration().encoding == "UTF-8"); } } @@ -31,5 +31,5 @@ TEST_CASE("Custom String Literal", "[document]") { using namespace myxml::literals; auto doc = ""_doc; - REQUIRE(doc.GetRoot()->GetName() == "root"); + REQUIRE(doc.get_root()->GetName() == "root"); } \ No newline at end of file diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index 391fc14..929ffd1 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -187,11 +187,11 @@ TEST_CASE("Parsing simple xml elements", "[parser]") )"; auto elem = myxml::Element::Parse(root); - elem->SetEntityEncoding(false); + elem->entity_encoding(false); REQUIRE(elem->GetName() == "root"); REQUIRE(elem->FirstText()->ExportRaw() == "\n <>\n"); - elem->SetEntityEncoding(true); + elem->entity_encoding(true); REQUIRE(elem->FirstText()->ExportRaw() == "\n <>\n"); } @@ -218,6 +218,6 @@ TEST_CASE("Parsing simple xml elements", "[parser]") SECTION("Simple File Buffer") { std::cout << std::filesystem::current_path() << std::endl; - auto doc = myxml::Document::ParseFile("tests/data/example.xml"); + auto doc = myxml::document::load("tests/data/example.xml"); } } \ No newline at end of file From a4154efa00b274698b70729d7102a1a16e06feb8 Mon Sep 17 00:00:00 2001 From: adamska <2639980868@qq.com> Date: Thu, 5 Sep 2024 00:36:07 +0800 Subject: [PATCH 3/9] refactor(printable.hpp): use printable instead of Exportable --- docs/examples.md | 20 ++-- include/myxml/cdata.hpp | 6 +- include/myxml/document.hpp | 30 +++--- include/myxml/element.hpp | 85 +++++++++++------ include/myxml/exportable.hpp | 27 ------ include/myxml/node.hpp | 42 ++++---- include/myxml/parser.hpp | 6 +- include/myxml/printable.hpp | 40 ++++++++ include/myxml/text.hpp | 34 +++++-- src/cdata.cpp | 22 +++-- src/document.cpp | 39 ++++---- src/element.cpp | 180 ++++++++++++++++++++++++----------- src/exportable.cpp | 10 -- src/node.cpp | 38 ++++---- src/parser.cpp | 28 +++--- src/printable.cpp | 35 +++++++ src/text.cpp | 67 ++++++++----- tests/CMakeLists.txt | 8 +- tests/document_test.cpp | 8 +- tests/element_test.cpp | 30 +++--- tests/exportable_test.cpp | 33 ------- tests/parser_test.cpp | 130 ++++++++++++------------- tests/printable_test.cpp | 27 ++++++ 23 files changed, 564 insertions(+), 381 deletions(-) delete mode 100644 include/myxml/exportable.hpp create mode 100644 include/myxml/printable.hpp delete mode 100644 src/exportable.cpp create mode 100644 src/printable.cpp delete mode 100644 tests/exportable_test.cpp create mode 100644 tests/printable_test.cpp diff --git a/docs/examples.md b/docs/examples.md index f100fbf..70571c3 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -5,7 +5,7 @@ ### Simple ```C++ -using myxml; +using namespace myxml; // `std::string xml` document doc = document::parse(xml); // or @@ -20,6 +20,15 @@ optional elem = doc.first_elem("elem"); ### Element +#### Basic + +```C++ +using namespace myxml; +element elem("root"); +fmt::println(root->name()); +root->set_name("far"); +``` + #### Attributes ```C++ @@ -35,11 +44,6 @@ fmt::println(elem["hello"]); elem["hello"] = "world!"; // if key not exist, query it will create an empty attribute elem["hello"]; // == elem["hello"] = ""; -// use attribute to get an optional reference -if (auto value = elem.attribute("hello"); value) -{ - fmt::println("element has attribute \"hello\" with value {}", *value); -} ``` #### Children @@ -58,7 +62,9 @@ std::vector children = root.elems("child"); ### Text ```C++ -// Simple Query +// create from string +text txt = "Hello"; +// or from query text txt = root.first_text(); // print raw fmt::println(txt); diff --git a/include/myxml/cdata.hpp b/include/myxml/cdata.hpp index 97d2390..eed6394 100644 --- a/include/myxml/cdata.hpp +++ b/include/myxml/cdata.hpp @@ -12,8 +12,10 @@ namespace myxml explicit CData(std::string); virtual ~CData() = default; - virtual std::string ExportRaw() const override; - virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const override; + // virtual std::string ExportRaw() const override; + // virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const override; virtual void entity_encoding(bool) override; + virtual void platform_specific_newline(bool) override {} + virtual void print(std::ostream &) const override; }; } \ No newline at end of file diff --git a/include/myxml/document.hpp b/include/myxml/document.hpp index 9dfa1fb..3c0d667 100644 --- a/include/myxml/document.hpp +++ b/include/myxml/document.hpp @@ -6,7 +6,7 @@ // Declaration and Documant are both NOT Node namespace myxml { - struct declaration : public exportable + struct declaration : public printable { std::string version; std::optional encoding; @@ -19,37 +19,39 @@ namespace myxml /* Exportable */ virtual std::string ExportRaw() const; virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const; + virtual void entity_encoding(bool) {}; + virtual void platform_specific_newline(bool) {}; + virtual void print(std::ostream &os) const {} }; - class document : public exportable + class document : public printable { private: declaration decl; - std::shared_ptr root; + element root; public: /* Manipulate */ void set_declaration(const declaration &); - void set_root(std::shared_ptr root); + void set_root(std::shared_ptr root); /* Query */ const declaration &get_declaration() const; declaration &get_declaration(); - const std::shared_ptr &get_root() const; - std::shared_ptr get_root(); - std::shared_ptr first_elem(std::string_view); - std::shared_ptr first_elem(); - std::shared_ptr first_text(); + const element &get_root() const; + element &get_root(); + element first_elem(std::string_view); + element first_elem(); + text first_text(); /** Load */ static document parse(std::string_view); static document load(std::string fileName); - /* Exportable */ - virtual std::string ExportRaw() const; - virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const; - virtual void entity_encoding(bool); - virtual void platform_specific_newline(bool); + /* Implement printable */ + virtual void print(std::ostream &os) const override; + virtual void entity_encoding(bool) override; + virtual void platform_specific_newline(bool) override; }; namespace util diff --git a/include/myxml/element.hpp b/include/myxml/element.hpp index 9d1015a..9f2a3e6 100644 --- a/include/myxml/element.hpp +++ b/include/myxml/element.hpp @@ -5,54 +5,77 @@ #include #include "myxml/text.hpp" -#include "myxml/exportable.hpp" +#include "myxml/printable.hpp" namespace myxml { - class Element : public CompositeNode // public std::enable_shared_from_this, public Node + + enum class ClosingType { - public: - enum class ClosingType - { - Closed, - Closing, - }; + Closed, + Closing, + }; + + class element; + + namespace literals + { + // Custom String Literal for Element + element operator""_elem(const char *, std::size_t); + } + + class element : public printable + { + + friend element literals::operator""_elem(const char *, std::size_t); + friend class document; private: + std::shared_ptr _impl; + element(std::shared_ptr); + + public: + element() : _impl(nullptr) {} + explicit element(std::string_view); + + static element parse(std::string_view); + static element load(std::string_view); + std::string &operator[](const std::string &); + std::string_view name(); + + element first_elem(); + element first_elem(std::string_view); + text first_text(); + + /* Implement printable */ + virtual void print(std::ostream &) const override; + virtual void entity_encoding(bool) override; + virtual void platform_specific_newline(bool) override; + }; + + struct element_impl : public CompositeNode // public std::enable_shared_from_this, public Node + { + public: std::string name; std::map> attributes; /* Set initializer as private to avoid using Element without share_ptr*/ - explicit Element(std::string_view name); - Element() = default; - - public: - virtual ~Element() = default; + virtual ~element_impl() = default; + explicit element_impl(std::string_view name); + element_impl() = default; /* Builder */ // Wraps creating shared_ptr - static std::shared_ptr New(std::string_view name); - static std::shared_ptr New(); - static std::shared_ptr Parse(std::string_view buf); - - /* Query */ - std::optional GetAttribute(std::string_view name); - std::string_view GetName() const; + static std::shared_ptr _new(std::string_view name); + static std::shared_ptr _new(); + static std::shared_ptr parse(std::string_view buf); + static std::shared_ptr load(std::string_view path); /* Manipulate */ - void SetName(std::string_view); - void SetAttribute(std::string key, std::string value); - void ExtendAttributes(std::map); + void extend_attributes(std::map); std::string &operator[](const std::string &); /* Implement Exportable */ - virtual std::string ExportRaw() const override; - virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const override; + virtual void print(std::ostream &) const override; }; - - namespace literals - { - // Custom String Literal for Element - std::shared_ptr operator""_elem(const char *, std::size_t); - } } diff --git a/include/myxml/exportable.hpp b/include/myxml/exportable.hpp deleted file mode 100644 index ac15353..0000000 --- a/include/myxml/exportable.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include - -namespace myxml -{ - class ExportConfig - { - public: - bool EntityEncoding; - bool PlatformSpecificNewline; - - ExportConfig(); - }; - - class exportable - { - protected: - ExportConfig config; - - public: - virtual ~exportable() = default; - virtual std::string ExportRaw() const = 0; - virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const = 0; - virtual void entity_encoding(bool) {}; - virtual void platform_specific_newline(bool) {}; - }; -} \ No newline at end of file diff --git a/include/myxml/node.hpp b/include/myxml/node.hpp index 5cab298..ecbe847 100644 --- a/include/myxml/node.hpp +++ b/include/myxml/node.hpp @@ -3,19 +3,19 @@ #include #include -#include "myxml/exportable.hpp" +#include "myxml/printable.hpp" namespace myxml { // defined in element.hpp - class Element; + class element_impl; // defined in text.hpp - class Text; + class text_impl; // defined below class CompositeNode; // Element, Text are Node. - class Node : public std::enable_shared_from_this, public exportable + class Node : public std::enable_shared_from_this, public printable { private: template >> @@ -59,10 +59,10 @@ namespace myxml /* Query */ std::shared_ptr NextSibiling(); std::shared_ptr PrevSibiling(); - std::shared_ptr NextElem(); - std::shared_ptr PrevElem(); - std::shared_ptr NextText(); - std::shared_ptr PrevText(); + std::shared_ptr NextElem(); + std::shared_ptr PrevElem(); + std::shared_ptr NextText(); + std::shared_ptr PrevText(); /** Implement Export */ virtual void entity_encoding(bool) override; @@ -75,7 +75,19 @@ namespace myxml private: std::shared_ptr firstChild; std::shared_ptr lastChild; - std::map, std::less<>> nameToElemBuffer; + std::map, std::less<>> nameToElemBuffer; + + public: + virtual ~CompositeNode() = default; + + /* Query */ + std::shared_ptr FirstChild(); + const std::shared_ptr &FirstChild() const; + std::shared_ptr LastChild(); + const std::shared_ptr &LastChild() const; + std::shared_ptr Elem(std::string_view name); + std::shared_ptr FirstElem(); + std::shared_ptr FirstText(); template >> std::shared_ptr First() @@ -90,18 +102,6 @@ namespace myxml return nullptr; } - public: - virtual ~CompositeNode() = default; - - /* Query */ - std::shared_ptr FirstChild(); - const std::shared_ptr &FirstChild() const; - std::shared_ptr LastChild(); - const std::shared_ptr &LastChild() const; - std::shared_ptr Elem(std::string_view name); - std::shared_ptr FirstElem(); - std::shared_ptr FirstText(); - /* Manipulate */ std::shared_ptr InsertAtFront(const std::shared_ptr &); std::shared_ptr InsertAtEnd(const std::shared_ptr &); diff --git a/include/myxml/parser.hpp b/include/myxml/parser.hpp index 83bd099..6d9bc5b 100644 --- a/include/myxml/parser.hpp +++ b/include/myxml/parser.hpp @@ -62,7 +62,7 @@ namespace myxml /** * @throws `SyntaxError` if faild to find `<` */ - std::shared_ptr parseText(); + std::shared_ptr parseText(); /** * @returns `std::nullopt` if not start with ` parseElementWithHeader(ElementTag header); + std::shared_ptr parseElementWithHeader(ElementTag header); /** * @returns std::nullopt if not starts with ` parseDeclaration(); public: - std::shared_ptr ParseElement(); + std::shared_ptr ParseElement(); /** * @returns std::nullopt if no heading `<` * @throws `SyntaxError` if the heading character is `<` and the trailing characters are in incorrect format diff --git a/include/myxml/printable.hpp b/include/myxml/printable.hpp new file mode 100644 index 0000000..72b9579 --- /dev/null +++ b/include/myxml/printable.hpp @@ -0,0 +1,40 @@ +#pragma once +#include +#include + +namespace myxml +{ + struct format_config + { + std::size_t indent_level; + std::size_t indent_size; + + format_config deeper() { return {indent_level + 1, indent_size}; } + }; + + struct print_config + { + public: + bool entity_encoding; + bool platform_specific_newline; + std::optional fconfig; + print_config(); + }; + + class printable + { + protected: + print_config config; + + public: + virtual ~printable() = default; + virtual void print(std::ostream &os) const = 0; + // virtual std::string ExportRaw() const = 0; + // virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const = 0; + virtual void entity_encoding(bool); + virtual void platform_specific_newline(bool); + + friend std::ostream &operator<<(std::ostream &os, const printable &obj); + std::string str(); + }; +} \ No newline at end of file diff --git a/include/myxml/text.hpp b/include/myxml/text.hpp index e918776..ea7e3b4 100644 --- a/include/myxml/text.hpp +++ b/include/myxml/text.hpp @@ -4,26 +4,46 @@ namespace myxml { - class Text : public Node + + class text : public printable { + friend class element; + private: - std::string inner; + std::shared_ptr _impl; + + text(std::shared_ptr); public: - explicit Text(std::string_view str); + text(std::string_view); + text(std::string &&); + + /* Implement printable */ + virtual void print(std::ostream &) const override; + virtual void entity_encoding(bool) override; + virtual void platform_specific_newline(bool) override; + }; + + struct text_impl : public Node + { + std::string inner; + + text_impl() = default; + explicit text_impl(std::string_view str); - virtual ~Text() = default; + virtual ~text_impl() = default; // may used in Export bool IsAllSpace() const; /* Implment Exportable*/ - virtual std::string ExportRaw() const override; - virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const override; + // virtual std::string ExportRaw() const override; + // virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const override; + virtual void print(std::ostream &) const override; }; namespace util { - const char *const platformSpecificNewline(); + const std::string_view platform_specific_newline(); }; } \ No newline at end of file diff --git a/src/cdata.cpp b/src/cdata.cpp index 996482a..d4c239b 100644 --- a/src/cdata.cpp +++ b/src/cdata.cpp @@ -1,3 +1,4 @@ +#include #include #include "myxml/cdata.hpp" @@ -8,17 +9,22 @@ namespace myxml { } - std::string CData::ExportRaw() const - { - return fmt::format("\n", this->inner); - } + // std::string CData::ExportRaw() const + // { + // return fmt::format("\n", this->inner); + // } - std::string CData::ExportFormatted(int indentLevel, int indentSize) const - { - return std::string(indentLevel * indentSize, ' ') + this->ExportRaw(); - } + // std::string CData::ExportFormatted(int indentLevel, int indentSize) const + // { + // return std::string(indentLevel * indentSize, ' ') + this->ExportRaw(); + // } void CData::entity_encoding(bool) { // do nothing } + + void CData::print(std::ostream &os) const + { + os << "inner << "]]>\n"; + } } diff --git a/src/document.cpp b/src/document.cpp index 6c0edbb..e3f2aa4 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "myxml/document.hpp" #include "myxml/parser.hpp" @@ -11,7 +12,7 @@ namespace myxml this->decl = decl; } - void document::set_root(std::shared_ptr root) + void document::set_root(std::shared_ptr root) { this->root = root; } @@ -26,29 +27,29 @@ namespace myxml return this->decl; } - const std::shared_ptr &document::get_root() const + const element &document::get_root() const { return this->root; } - std::shared_ptr document::get_root() + element &document::get_root() { return this->root; } - std::shared_ptr document::first_elem(std::string_view name) + element document::first_elem(std::string_view name) { - return this->root->Elem(name); + return this->root.first_elem(name); } - std::shared_ptr document::first_elem() + element document::first_elem() { - return this->root->FirstElem(); + return this->root.first_elem(); } - std::shared_ptr document::first_text() + text document::first_text() { - return this->root->FirstText(); + return this->root.first_text(); } document document::parse(std::string_view input) @@ -61,24 +62,28 @@ namespace myxml auto f = XMLFile::Open(fileName); return Parser(f).ParseDocument(); } - std::string document::ExportRaw() const - { - return this->decl.ExportRaw() + this->root->ExportRaw(); - } + // std::string document::ExportRaw() const + // { + // return this->decl.ExportRaw() + this->root->ExportRaw(); + // } - std::string document::ExportFormatted(int indentLevel, int indentSize) const + void document::print(std::ostream &os) const { - return this->decl.ExportFormatted(indentLevel + 1, indentSize) + this->root->ExportFormatted(indentLevel + 1, indentSize); + os << this->decl << this->root; } + // std::string document::ExportFormatted(int indentLevel, int indentSize) const + // { + // return this->decl.ExportFormatted(indentLevel + 1, indentSize) + this->root->ExportFormatted(indentLevel + 1, indentSize); + // } void document::entity_encoding(bool flag) { - this->root->entity_encoding(flag); + this->root.entity_encoding(flag); } void document::platform_specific_newline(bool flag) { - this->root->platform_specific_newline(flag); + this->root.platform_specific_newline(flag); } std::optional declaration::from_attrs(std::map attrs) diff --git a/src/element.cpp b/src/element.cpp index bf2cc52..4551e15 100644 --- a/src/element.cpp +++ b/src/element.cpp @@ -1,109 +1,173 @@ +#include #include "myxml/element.hpp" #include "myxml/parser.hpp" +#include "myxml/xmlfile.hpp" namespace myxml { - Element::Element(std::string_view name) - : name(name) {} + element::element(std::shared_ptr impl) + : _impl(impl) + { + } - std::shared_ptr Element::New(std::string_view name) + element::element(std::string_view name) + : element(element_impl::_new(name)) { - return std::shared_ptr(new Element(name)); } - std::shared_ptr Element::New() + std::string_view element::name() { - return std::shared_ptr(new Element()); + return this->_impl->name; } - std::shared_ptr Element::Parse(std::string_view buf) + element element::parse(std::string_view xml) { - return Parser(buf).ParseElement(); + return element_impl::parse(xml); } - std::optional Element::GetAttribute(std::string_view name) + element element::load(std::string_view path) { - if (auto attr = this->attributes.find(name); attr != this->attributes.end()) - { - return attr->second; - } - else - { - return std::nullopt; - } + return element_impl::load(path); } - std::string_view Element::GetName() const + std::string &element::operator[](const std::string &attr) { - return this->name; + return (*_impl)[attr]; } - void Element::SetName(std::string_view name) + element element::first_elem() { - this->name = name; + return _impl->First(); } - void Element::SetAttribute(std::string key, std::string value) + element element::first_elem(std::string_view name) { - this->attributes.emplace(key, value); + return _impl->Elem(name); } - void Element::ExtendAttributes(std::map attris) + text element::first_text() { - this->attributes.insert(attris.begin(), attris.end()); + return _impl->First(); } - std::string &Element::operator[](const std::string &key) + void element::print(std::ostream &os) const { - return this->attributes[key]; + _impl->print(os); } - std::string Element::ExportRaw() const + void element::entity_encoding(bool flag) { + return _impl->entity_encoding(flag); + } - std::string builder = "<" + std::string(this->GetName()); - for (const auto &[key, value] : this->attributes) - { - builder += "" + key + "=\"" + value + "\""; - } - if (this->FirstChild() == nullptr) - { - builder += " />"; - return builder; - } - builder += ">"; - for (auto node = this->FirstChild(); node != nullptr; node = node->NextSibiling()) - { - builder += node->ExportRaw(); - } - builder += "GetName()) + ">"; - return builder; + void element::platform_specific_newline(bool flag) + { + return _impl->platform_specific_newline(flag); } - std::string Element::ExportFormatted(int indentLevel, int indentSize) const + element_impl::element_impl(std::string_view name) + : name(name) {} + + std::shared_ptr element_impl::_new(std::string_view name) { - std::string indent(indentLevel * indentSize, ' '); - std::string builder = indent + "<" + std::string(this->GetName()); + return std::shared_ptr(new element_impl(name)); + } + + std::shared_ptr element_impl::_new() + { + return std::shared_ptr(new element_impl()); + } + + std::shared_ptr element_impl::parse(std::string_view buf) + { + return Parser(buf).ParseElement(); + } + + std::shared_ptr element_impl::load(std::string_view path) + { + auto f = XMLFile::Open(path); + return Parser(f).ParseElement(); + } + + void element_impl::extend_attributes(std::map attris) + { + this->attributes.insert(attris.begin(), attris.end()); + } + + std::string &element_impl::operator[](const std::string &key) + { + return this->attributes[key]; + } + + // std::string element_impl::ExportRaw() const + // { + + // std::string builder = "<" + std::string(this->name); + // for (const auto &[key, value] : this->attributes) + // { + // builder += "" + key + "=\"" + value + "\""; + // } + // if (this->FirstChild() == nullptr) + // { + // builder += " />"; + // return builder; + // } + // builder += ">"; + // for (auto node = this->FirstChild(); node != nullptr; node = node->NextSibiling()) + // { + // builder += node->ExportRaw(); + // } + // builder += "name) + ">"; + // return builder; + // } + + void element_impl::print(std::ostream &os) const + { + os << "<" << this->name; for (const auto &[key, value] : this->attributes) { - builder += "" + key + "=\"" + value + "\""; + os << "" << key << "=\"" << value << "\""; } if (this->FirstChild() == nullptr) { - builder += " />\n"; - return builder; + os << " />"; + return; } - builder += ">\n"; + os << ">"; for (auto node = this->FirstChild(); node != nullptr; node = node->NextSibiling()) { - builder += node->ExportFormatted(indentLevel + 1, indentSize); + node->print(os); } - builder += indent + "GetName()) + ">\n"; - return builder; + os << "name << ">"; } - std::shared_ptr literals::operator""_elem(const char *literal, std::size_t len) + // std::string element_impl::ExportFormatted(int indentLevel, int indentSize) const + // { + // std::string indent(indentLevel * indentSize, ' '); + // std::string builder = indent + "<" + std::string(this->name); + // for (const auto &[key, value] : this->attributes) + // { + // builder += "" + key + "=\"" + value + "\""; + // } + // if (this->FirstChild() == nullptr) + // { + // builder += " />\n"; + // return builder; + // } + // builder += ">\n"; + // for (auto node = this->FirstChild(); node != nullptr; node = node->NextSibiling()) + // { + // builder += node->ExportFormatted(indentLevel + 1, indentSize); + // } + // builder += indent + "name) + ">\n"; + // return builder; + // } + + namespace literals { - return Element::Parse(std::string_view(literal, len)); + element operator""_elem(const char *literal, std::size_t len) + { + return element_impl::parse(std::string_view(literal, len)); + } } -} \ No newline at end of file +} diff --git a/src/exportable.cpp b/src/exportable.cpp deleted file mode 100644 index 3474be3..0000000 --- a/src/exportable.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "myxml/exportable.hpp" - -namespace myxml -{ - ExportConfig::ExportConfig() - : EntityEncoding(true), - PlatformSpecificNewline(false) - { - } -} diff --git a/src/node.cpp b/src/node.cpp index e688984..5ed56df 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -6,12 +6,12 @@ namespace myxml { void Node::entity_encoding(bool flag) { - this->config.EntityEncoding = flag; + this->config.entity_encoding = flag; } void Node::platform_specific_newline(bool flag) { - this->config.PlatformSpecificNewline = flag; + this->config.platform_specific_newline = flag; } std::shared_ptr Node::NextSibiling() @@ -24,24 +24,24 @@ namespace myxml return this->prev; } - std::shared_ptr Node::NextElem() + std::shared_ptr Node::NextElem() { - return this->Next(); + return this->Next(); } - std::shared_ptr Node::PrevElem() + std::shared_ptr Node::PrevElem() { - return this->Prev(); + return this->Prev(); } - std::shared_ptr Node::NextText() + std::shared_ptr Node::NextText() { - return this->Next(); + return this->Next(); } - std::shared_ptr Node::PrevText() + std::shared_ptr Node::PrevText() { - return this->Prev(); + return this->Prev(); } std::shared_ptr CompositeNode::LastChild() @@ -64,21 +64,21 @@ namespace myxml return this->firstChild; } - std::shared_ptr CompositeNode::FirstElem() + std::shared_ptr CompositeNode::FirstElem() { - return this->First(); + return this->First(); } - std::shared_ptr CompositeNode::FirstText() + std::shared_ptr CompositeNode::FirstText() { - return this->First(); + return this->First(); } - std::shared_ptr CompositeNode::Elem(std::string_view name) + std::shared_ptr CompositeNode::Elem(std::string_view name) { if (auto buf = this->nameToElemBuffer.find(name); buf != this->nameToElemBuffer.end()) { - std::weak_ptr ptr = buf->second; + std::weak_ptr ptr = buf->second; if (auto child = ptr.lock(); child != nullptr) { return child; @@ -90,7 +90,7 @@ namespace myxml } for (auto child = this->firstChild; child != nullptr; child = child->next) { - if (auto elem = child->As(); elem && elem->GetName() == name) + if (auto elem = child->As(); elem && elem->name == name) { this->nameToElemBuffer.emplace(name, elem); return elem; @@ -170,7 +170,7 @@ namespace myxml void CompositeNode::entity_encoding(bool flag) { - this->config.EntityEncoding = flag; + this->config.entity_encoding = flag; for (auto it = this->FirstChild(); it != nullptr; it = it->next) { it->entity_encoding(flag); @@ -179,7 +179,7 @@ namespace myxml void CompositeNode::platform_specific_newline(bool flag) { - this->config.PlatformSpecificNewline = flag; + this->config.platform_specific_newline = flag; for (auto it = this->FirstChild(); it != nullptr; it = it->next) { it->platform_specific_newline(flag); diff --git a/src/parser.cpp b/src/parser.cpp index 2630e3b..08e31e0 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -125,7 +125,7 @@ namespace myxml return attr; } - std::shared_ptr Parser::parseText() + std::shared_ptr Parser::parseText() { if (!this->peek()) { @@ -142,7 +142,7 @@ namespace myxml auto [line, col] = this->currentLoc(); throw SyntaxError(fmt::format("expected '<' after text"), line, col); } - return std::shared_ptr(new Text(*this->takeN(len))); + return std::shared_ptr(new text_impl(*this->takeN(len))); } std::shared_ptr Parser::parseCData() @@ -206,10 +206,10 @@ namespace myxml return tag; } - std::shared_ptr Parser::parseElementWithHeader(ElementTag header) + std::shared_ptr Parser::parseElementWithHeader(ElementTag header) { - auto elem = Element::New(); - elem->SetName(header.name); + auto elem = element_impl::_new(); + elem->name = header.name; while (auto ch = this->peek()) { switch (*ch) @@ -233,24 +233,24 @@ namespace myxml } case ElementTag::ClosingType::Closed: { - auto child = Element::New(); - child->SetName(tag->name); + auto child = element_impl::_new(); + child->name = tag->name; if (!tag->attris.empty()) { - child->ExtendAttributes(tag->attris); + child->extend_attributes(tag->attris); } elem->InsertAtEnd(child); break; } case ElementTag::ClosingType::Closing: - if (tag->name != elem->GetName()) + if (tag->name != elem->name) { auto [line, col] = this->currentLoc(); throw SyntaxError(fmt::format("elem name in closing tag is mismatched with the header"), line, col); } if (!header.attris.empty()) { - elem->ExtendAttributes(header.attris); + elem->extend_attributes(header.attris); } return elem; default: @@ -268,18 +268,18 @@ namespace myxml throw UnexpectedEndOfInput(line, col); } - std::shared_ptr Parser::ParseElement() + std::shared_ptr Parser::ParseElement() { this->skipWhiteSpaces(); if (auto tag = this->ParseElementTag(); tag) { if (tag->type == ElementTag::ClosingType::Closed) { - auto elem = Element::New(); - elem->SetName(tag->name); + auto elem = element_impl::_new(); + elem->name = tag->name; if (!tag->attris.empty()) { - elem->ExtendAttributes(tag->attris); + elem->extend_attributes(tag->attris); } return elem; } diff --git a/src/printable.cpp b/src/printable.cpp new file mode 100644 index 0000000..23350a6 --- /dev/null +++ b/src/printable.cpp @@ -0,0 +1,35 @@ +#include +#include "myxml/printable.hpp" + +namespace myxml +{ + print_config::print_config() + : entity_encoding(true), + platform_specific_newline(false), + fconfig(std::nullopt) + { + } + + std::ostream &operator<<(std::ostream &os, const printable &obj) + { + obj.print(os); + return os; + } + + void printable::entity_encoding(bool flag) + { + this->config.entity_encoding = flag; + } + + void printable::platform_specific_newline(bool flag) + { + this->config.platform_specific_newline = flag; + } + + std::string printable::str() + { + std::stringstream sstream; + sstream << (*this); + return sstream.str(); + } +} diff --git a/src/text.cpp b/src/text.cpp index f478bf1..37b5a39 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -4,10 +4,41 @@ namespace myxml { + text::text(std::shared_ptr impl) + : _impl(impl) + { + } + + text::text(std::string &&str) + { + _impl = std::make_shared(); + _impl->inner = str; + } + + text::text(std::string_view str) + { + _impl = std::make_shared(); + _impl->inner = str; + } + + void text::print(std::ostream &os) const + { + this->_impl->print(os); + } + + void text::entity_encoding(bool flag) + { + this->_impl->entity_encoding(flag); + } + + void text::platform_specific_newline(bool flag) + { + this->_impl->platform_specific_newline(flag); + } - Text::Text(std::string_view input) + text_impl::text_impl(std::string_view input) { - if (config.EntityEncoding) + if (config.entity_encoding) { // entity encoding static std::map> entityMap = { @@ -57,16 +88,16 @@ namespace myxml } } - bool Text::IsAllSpace() const + bool text_impl::IsAllSpace() const { return std::all_of(this->inner.begin(), this->inner.end(), isspace); } - std::string Text::ExportRaw() const + void text_impl::print(std::ostream &os) const { - if (!this->config.EntityEncoding && !this->config.PlatformSpecificNewline) + if (!this->config.entity_encoding && !this->config.platform_specific_newline) { - return this->inner; + os << this->inner; } else { @@ -79,42 +110,34 @@ namespace myxml }; std::size_t start = 0; // start of current segement std::size_t len = this->inner.length(); - std::string builder; for (std::size_t i = 0; i < len; i++) { - if (this->config.EntityEncoding) + if (this->config.entity_encoding) { if (auto it = entityMap.find(this->inner[i]); it != entityMap.end()) { - builder += this->inner.substr(start, i - start); - builder += it->second; + os << this->inner.substr(start, i - start); + os << it->second; start = i + 1; } } - if (this->config.PlatformSpecificNewline) + if (this->config.platform_specific_newline) { if (this->inner[i] == '\n') { - builder += this->inner.substr(start, i - start); - builder += util::platformSpecificNewline(); + os << this->inner.substr(start, i - start); + os << util::platform_specific_newline(); start = i + 1; } } } - builder += this->inner.substr(start, len - start); - return builder; + os << this->inner.substr(start, len - start); } } - std::string Text::ExportFormatted(int indentLevel, int indentSize) const - { - // TODO: better implementation - return std::string(indentLevel * indentSize, ' ') + this->inner + '\n'; - } - namespace util { - const char *const platformSpecificNewline() + const std::string_view platform_specific_newline() { #ifdef _WIN32 return "\r\n"; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a88b1ab..39e76f6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,24 +2,24 @@ include(CTest) add_executable(element_test element_test.cpp) add_executable(parser_test parser_test.cpp) -add_executable(exportable_test exportable_test.cpp) +add_executable(printable_test printable_test.cpp) add_executable(document_test document_test.cpp) add_executable(buffer_test buffer_test.cpp) target_link_libraries(element_test Catch2::Catch2WithMain myxml) target_link_libraries(parser_test Catch2::Catch2WithMain myxml) -target_link_libraries(exportable_test Catch2::Catch2WithMain myxml) +target_link_libraries(printable_test Catch2::Catch2WithMain myxml) target_link_libraries(document_test Catch2::Catch2WithMain myxml) target_link_libraries(buffer_test Catch2::Catch2WithMain myxml) target_compile_features(element_test PRIVATE cxx_std_17) target_compile_features(parser_test PRIVATE cxx_std_17) -target_compile_features(exportable_test PRIVATE cxx_std_17) +target_compile_features(printable_test PRIVATE cxx_std_17) target_compile_features(document_test PRIVATE cxx_std_17) target_compile_features(buffer_test PRIVATE cxx_std_17) add_test(NAME element_test COMMAND element_test) add_test(NAME parser_test COMMAND parser_test WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) -add_test(NAME exportable_test COMMAND exportable_test) +add_test(NAME printable_test COMMAND printable_test) add_test(NAME document_test COMMAND document_test) add_test(NAME buffer_test COMMAND buffer_test) diff --git a/tests/document_test.cpp b/tests/document_test.cpp index 0f29074..88b4838 100644 --- a/tests/document_test.cpp +++ b/tests/document_test.cpp @@ -9,9 +9,9 @@ TEST_CASE("Simple document", "[document]") Value )"; auto doc = myxml::document::parse(input); - REQUIRE(doc.get_root()->GetName() == "root"); - REQUIRE(doc.first_elem("child")->GetName() == "child"); - REQUIRE(doc.first_elem("child")->FirstText()->ExportRaw() == "Value"); + REQUIRE(doc.get_root().name() == "root"); + REQUIRE(doc.first_elem("child").name() == "child"); + REQUIRE(doc.first_elem("child").first_text().str() == "Value"); } SECTION("With decl") @@ -31,5 +31,5 @@ TEST_CASE("Custom String Literal", "[document]") { using namespace myxml::literals; auto doc = ""_doc; - REQUIRE(doc.get_root()->GetName() == "root"); + REQUIRE(doc.get_root().name() == "root"); } \ No newline at end of file diff --git a/tests/element_test.cpp b/tests/element_test.cpp index cbe6ad0..19ce7c6 100644 --- a/tests/element_test.cpp +++ b/tests/element_test.cpp @@ -1,47 +1,47 @@ #include #include "myxml/element.hpp" -TEST_CASE("Element Functionality", "[element]") +TEST_CASE("Element Impl", "[element]") { - auto root = myxml::Element::New("root"); - auto child = myxml::Element::New("child"); - auto sibiling = myxml::Element::New("sibiling"); + auto root = myxml::element_impl::_new("root"); + auto child = myxml::element_impl::_new("child"); + auto sibiling = myxml::element_impl::_new("sibiling"); SECTION("GetName") { - REQUIRE(root->GetName() == "root"); + REQUIRE(root->name == "root"); } SECTION("Basic Insertion") { root->InsertAtFront(child); - REQUIRE(root->FirstElem()->GetName() == "child"); - REQUIRE(root->LastChild()->As()->GetName() == "child"); + REQUIRE(root->FirstElem()->name == "child"); + REQUIRE(root->LastChild()->As()->name == "child"); } SECTION("Get child by name after insert it") { root->InsertAtFront(child); // Unbuffered - REQUIRE(root->Elem("child")->GetName() == "child"); + REQUIRE(root->Elem("child")->name == "child"); // Buffered - REQUIRE(root->Elem("child")->GetName() == "child"); + REQUIRE(root->Elem("child")->name == "child"); } SECTION("Multi child") { root->InsertAtEnd(child); root->InsertAtEnd(sibiling); - REQUIRE(root->Elem("child")->GetName() == "child"); - REQUIRE(root->Elem("child")->NextElem()->GetName() == "sibiling"); - REQUIRE(root->Elem("sibiling")->PrevElem()->GetName() == "child"); + REQUIRE(root->Elem("child")->name == "child"); + REQUIRE(root->Elem("child")->NextElem()->name == "sibiling"); + REQUIRE(root->Elem("sibiling")->PrevElem()->name == "child"); } SECTION("Overload []") { - root->SetAttribute("hello", "world"); + root->attributes["hello"] = "world"; REQUIRE((*root)["hello"] == "world"); - REQUIRE(root->GetAttribute("hello") == "world"); + REQUIRE(root->attributes["hello"] == "world"); (*root)["hello"] = "bar"; REQUIRE((*root)["hello"] == "bar"); } @@ -51,5 +51,5 @@ TEST_CASE("Custom String Literal", "[Element]") { using namespace myxml::literals; auto elem = ""_elem; - REQUIRE(elem->GetName() == "root"); + REQUIRE(elem.name() == "root"); } \ No newline at end of file diff --git a/tests/exportable_test.cpp b/tests/exportable_test.cpp deleted file mode 100644 index 9587b98..0000000 --- a/tests/exportable_test.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include "myxml/element.hpp" - -TEST_CASE("Formatted Export", "[exportable]") -{ - SECTION("Single") - { - auto root = myxml::Element::New("root"); - REQUIRE(root->ExportFormatted() == "\n"); - } - - SECTION("Text") - { - auto root = myxml::Element::Parse("Hello, world!"); - REQUIRE(root->ExportFormatted() == "\n Hello, world!\n\n"); - } -} - -TEST_CASE("Raw Export", "[exportable]") -{ - SECTION("Single") - { - auto root = myxml::Element::Parse(""); - REQUIRE(root->ExportRaw() == ""); - } - - SECTION("Text") - { - auto root = myxml::Element::Parse("Hello, world!"); - REQUIRE(root->ExportRaw() == "Hello, world!"); - } -} \ No newline at end of file diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index 929ffd1..a80338f 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -28,8 +28,8 @@ TEST_CASE("Parsing simple xml elements", "[parser]") { std::string tooEasy = R"( )"; - auto elem = myxml::Element::Parse(tooEasy); - REQUIRE(elem->GetName() == "root"); + auto elem = myxml::element_impl::parse(tooEasy); + REQUIRE(elem->name == "root"); } SECTION("Text") @@ -37,11 +37,11 @@ TEST_CASE("Parsing simple xml elements", "[parser]") std::string withText = R"( Hello, world! )"; - auto elem = myxml::Element::Parse(withText); - REQUIRE(elem->GetName() == "root"); - REQUIRE(elem->FirstText()->ExportRaw() == "\n "); - REQUIRE(elem->FirstElem()->GetName() == "child"); - REQUIRE(elem->FirstElem()->FirstText()->ExportRaw() == "Hello, world!"); + auto elem = myxml::element_impl::parse(withText); + REQUIRE(elem->name == "root"); + REQUIRE(elem->FirstText()->str() == "\n "); + REQUIRE(elem->FirstElem()->name == "child"); + REQUIRE(elem->FirstElem()->FirstText()->str() == "Hello, world!"); } SECTION("Nested") @@ -51,14 +51,14 @@ TEST_CASE("Parsing simple xml elements", "[parser]") )"; - auto elem = myxml::Element::Parse(nested); - REQUIRE(elem->GetName() == "root"); - REQUIRE(elem->FirstText()->ExportRaw() == "\n "); + auto elem = myxml::element_impl::parse(nested); + REQUIRE(elem->name == "root"); + REQUIRE(elem->FirstText()->str() == "\n "); auto parent = elem->FirstElem(); - REQUIRE(parent->GetName() == "parent"); - REQUIRE(parent->FirstText()->ExportRaw() == "\n "); + REQUIRE(parent->name == "parent"); + REQUIRE(parent->FirstText()->str() == "\n "); auto child = parent->FirstElem(); - REQUIRE(child->GetName() == "child"); + REQUIRE(child->name == "child"); REQUIRE(child->FirstChild() == nullptr); } @@ -69,26 +69,26 @@ TEST_CASE("Parsing simple xml elements", "[parser]") Second Third )"; - auto elem = myxml::Element::Parse(multiLevel); - REQUIRE(elem->GetName() == "root"); - REQUIRE(elem->FirstText()->ExportRaw() == "\n "); + auto elem = myxml::element_impl::parse(multiLevel); + REQUIRE(elem->name == "root"); + REQUIRE(elem->FirstText()->str() == "\n "); - auto item1 = elem->FirstElem()->As(); - REQUIRE(item1->GetName() == "item"); - REQUIRE(item1->FirstText()->ExportRaw() == "First"); + auto item1 = elem->FirstElem()->As(); + REQUIRE(item1->name == "item"); + REQUIRE(item1->FirstText()->str() == "First"); // 验证第二个 节点 auto item2 = item1->NextElem(); - REQUIRE(item2->GetName() == "item"); - REQUIRE(item2->FirstText()->ExportRaw() == "Second"); + REQUIRE(item2->name == "item"); + REQUIRE(item2->FirstText()->str() == "Second"); // 验证第三个 节点 auto item3 = item2->NextElem(); - REQUIRE(item3->GetName() == "item"); - REQUIRE(item3->FirstText()->ExportRaw() == "Third"); + REQUIRE(item3->name == "item"); + REQUIRE(item3->FirstText()->str() == "Third"); // 验证 root 节点的最后文本 - REQUIRE(item3->NextText()->ExportRaw() == "\n"); + REQUIRE(item3->NextText()->str() == "\n"); } SECTION("Closed Element") @@ -96,17 +96,17 @@ TEST_CASE("Parsing simple xml elements", "[parser]") std::string closed = R"( )"; - auto elem = myxml::Element::Parse(closed); - REQUIRE(elem->GetName() == "root"); - REQUIRE(elem->FirstText()->ExportRaw() == "\n "); + auto elem = myxml::element_impl::parse(closed); + REQUIRE(elem->name == "root"); + REQUIRE(elem->FirstText()->str() == "\n "); // 验证 节点 - auto emptyElement = elem->FirstChild()->next->As(); - REQUIRE(emptyElement->GetName() == "empty"); + auto emptyElement = elem->FirstChild()->next->As(); + REQUIRE(emptyElement->name == "empty"); REQUIRE(emptyElement->FirstChild() == nullptr); // 自闭合标签没有子节点 // 验证 root 节点的最后文本 - REQUIRE(emptyElement->NextText()->ExportRaw() == "\n"); + REQUIRE(emptyElement->NextText()->str() == "\n"); } SECTION("Mixed") @@ -115,69 +115,69 @@ TEST_CASE("Parsing simple xml elements", "[parser]") hello )"; - auto elem = myxml::Element::Parse(mixed); - REQUIRE(elem->GetName() == "root"); - REQUIRE(elem->FirstText()->ExportRaw() == "\n hello\n "); + auto elem = myxml::element_impl::parse(mixed); + REQUIRE(elem->name == "root"); + REQUIRE(elem->FirstText()->str() == "\n hello\n "); // 验证 节点 - auto child = elem->FirstChild()->next->As(); - REQUIRE(child->GetName() == "child"); + auto child = elem->FirstChild()->next->As(); + REQUIRE(child->name == "child"); REQUIRE(child->FirstChild() == nullptr); // 是空的,没有文本子节点 // 验证 root 节点的最后文本 - REQUIRE(child->NextText()->ExportRaw() == "\n"); + REQUIRE(child->NextText()->str() == "\n"); } SECTION("With attributes") { std::string attri = R"()"; - auto elem = myxml::Element::Parse(attri); - REQUIRE(elem->GetName() == "root"); - REQUIRE(elem->GetAttribute("hello") == "world"); + auto elem = myxml::element_impl::parse(attri); + REQUIRE(elem->name == "root"); + REQUIRE(elem->attributes["hello"] == "world"); } SECTION("Empty With Attributes") { std::string attri = R"()"; - auto elem = myxml::Element::Parse(attri); - REQUIRE(elem->GetName() == "root"); - REQUIRE(elem->GetAttribute("hello") == "world"); + auto elem = myxml::element_impl::parse(attri); + REQUIRE(elem->name == "root"); + REQUIRE(elem->attributes["hello"] == "world"); } SECTION("Multiple Attributes") { std::string multipleAttributes = R"()"; - auto elem = myxml::Element::Parse(multipleAttributes); + auto elem = myxml::element_impl::parse(multipleAttributes); - REQUIRE(elem->GetName() == "root"); - REQUIRE(elem->GetAttribute("attr1") == "value1"); - REQUIRE(elem->GetAttribute("attr2") == "value2"); - REQUIRE(elem->GetAttribute("attr3") == "value3"); + REQUIRE(elem->name == "root"); + REQUIRE(elem->attributes["attr1"] == "value1"); + REQUIRE(elem->attributes["attr2"] == "value2"); + REQUIRE(elem->attributes["attr3"] == "value3"); } SECTION("Empty Attributes") { std::string emptyAttribute = R"()"; - auto elem = myxml::Element::Parse(emptyAttribute); - REQUIRE(elem->GetName() == "root"); - REQUIRE(elem->GetAttribute("attr") == ""); + auto elem = myxml::element_impl::parse(emptyAttribute); + REQUIRE(elem->name == "root"); + REQUIRE(elem->attributes["attr"] == ""); } SECTION("Attributes And Children") { std::string attributesAndChildren = R"(Text)"; - auto elem = myxml::Element::Parse(attributesAndChildren); + auto elem = myxml::element_impl::parse(attributesAndChildren); // 检查根元素的名称和属性 - REQUIRE(elem->GetName() == "root"); - REQUIRE(elem->GetAttribute("attr") == "value"); + REQUIRE(elem->name == "root"); + REQUIRE(elem->attributes["attr"] == "value"); // 检查根元素的第一个子元素 auto child = elem->FirstElem(); - REQUIRE(child->GetName() == "child"); + REQUIRE(child->name == "child"); // 检查子元素的文本内容 - REQUIRE(child->FirstText()->ExportRaw() == "Text"); + REQUIRE(child->FirstText()->str() == "Text"); } SECTION("Decoding entity") @@ -185,34 +185,34 @@ TEST_CASE("Parsing simple xml elements", "[parser]") std::string root = R"( <> )"; - auto elem = myxml::Element::Parse(root); + auto elem = myxml::element_impl::parse(root); elem->entity_encoding(false); - REQUIRE(elem->GetName() == "root"); - REQUIRE(elem->FirstText()->ExportRaw() == "\n <>\n"); + REQUIRE(elem->name == "root"); + REQUIRE(elem->FirstText()->str() == "\n <>\n"); elem->entity_encoding(true); - REQUIRE(elem->FirstText()->ExportRaw() == "\n <>\n"); + REQUIRE(elem->FirstText()->str() == "\n <>\n"); } SECTION("CDATA") { std::string cdata = ""; - auto elem = myxml::Element::Parse(cdata); + auto elem = myxml::element_impl::parse(cdata); - REQUIRE(elem->FirstChild()->As()->ExportRaw() == "\n"); + REQUIRE(elem->FirstChild()->As()->str() == "\n"); } SECTION("Newline Normalization") { std::string nl = "hello\r\n"; - auto elem = myxml::Element::Parse(nl); + auto elem = myxml::element_impl::parse(nl); - REQUIRE(elem->FirstText()->ExportRaw() == "hello\n"); + REQUIRE(elem->FirstText()->str() == "hello\n"); nl = "hello\r"; - elem = myxml::Element::Parse(nl); - REQUIRE(elem->FirstText()->ExportRaw() == "hello\n"); + elem = myxml::element_impl::parse(nl); + REQUIRE(elem->FirstText()->str() == "hello\n"); } SECTION("Simple File Buffer") diff --git a/tests/printable_test.cpp b/tests/printable_test.cpp new file mode 100644 index 0000000..b24ab5c --- /dev/null +++ b/tests/printable_test.cpp @@ -0,0 +1,27 @@ +#include +#include +#include "myxml/element.hpp" + +// TEST_CASE("Formatted Export", "[exportable]") +// { +// SECTION("Single") +// { +// auto root = myxml::element_impl::_new("root"); +// REQUIRE(root->str() == "\n"); +// } + +// SECTION("Text") +// { +// auto root = myxml::element_impl::parse("Hello, world!"); +// REQUIRE(root->str() == "\n Hello, world!\n\n"); +// } +// } + +TEST_CASE("Raw Export", "[exportable]") +{ + auto root = myxml::element_impl::parse(""); + REQUIRE(root->str() == ""); + + root = myxml::element_impl::parse("Hello, world!"); + REQUIRE(root->str() == "Hello, world!"); +} \ No newline at end of file From f7b745abb02293d93914f5651b3fe1bc899032ef Mon Sep 17 00:00:00 2001 From: adamska <2639980868@qq.com> Date: Thu, 5 Sep 2024 14:28:15 +0800 Subject: [PATCH 4/9] refactor: naming parser, buffer and cdata --- docs/examples.md | 3 + include/myxml/buffer.hpp | 34 +++--- include/myxml/cdata.hpp | 32 +++-- include/myxml/error.hpp | 20 ++-- include/myxml/parser.hpp | 60 +++++----- include/myxml/xmlfile.hpp | 4 +- src/buffer.cpp | 38 +++--- src/cdata.cpp | 37 +++--- src/document.cpp | 4 +- src/element.cpp | 4 +- src/error.cpp | 26 ++--- src/parser.cpp | 238 +++++++++++++++++++------------------- src/xmlfile.cpp | 6 +- tests/buffer_test.cpp | 30 ++--- tests/parser_test.cpp | 14 +-- 15 files changed, 291 insertions(+), 259 deletions(-) diff --git a/docs/examples.md b/docs/examples.md index 70571c3..e26d8af 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -32,6 +32,7 @@ root->set_name("far"); #### Attributes ```C++ +using namespace myxml; // create an element with name 'root' element elem("root"); // or get an element from parsing @@ -49,6 +50,7 @@ elem["hello"]; // == elem["hello"] = ""; #### Children ```C++ +using namespace myxml; // element root; element child = root.first_elem(); // or first element with name "child" @@ -62,6 +64,7 @@ std::vector children = root.elems("child"); ### Text ```C++ +using namespace myxml; // create from string text txt = "Hello"; // or from query diff --git a/include/myxml/buffer.hpp b/include/myxml/buffer.hpp index 6d11035..9631799 100644 --- a/include/myxml/buffer.hpp +++ b/include/myxml/buffer.hpp @@ -8,10 +8,10 @@ namespace myxml { /** - * ADT. Used by Parser. + * ADT. Used by parser. * Implement by StringBuffer and XMLFile */ - class Buffer + class buffer { private: std::size_t offset = 0; @@ -21,34 +21,34 @@ namespace myxml // @returns {pointer to data, data length} virtual std::tuple base() const = 0; // update line and column - void updateLocation(char); + void update_loc(char); // update line and column - void updateLocation(std::string_view); + void update_loc(std::string_view); public: - virtual ~Buffer() = default; - std::optional Peek() const; - std::optional PeekN(int) const; - std::optional AfterN(int) const; - std::optional AfterNM(int, int) const; - std::optional Take(); - std::optional TakeN(int); + virtual ~buffer() = default; + std::optional peek() const; + std::optional peek_n(int) const; + std::optional after_n(int) const; + std::optional after_n_m(int, int) const; + std::optional take(); + std::optional take_n(int); // @returns {line, column} - std::tuple CurrentLocation(); + std::tuple cur_loc(); }; - class StringBuffer : public Buffer + class string_buffer : public buffer { private: std::variant inner; - std::string_view getView() const; + std::string_view view() const; /** Implement Buffer */ virtual std::tuple base() const override; public: - StringBuffer(std::string_view); - StringBuffer(std::string &&); - StringBuffer(const char *); + string_buffer(std::string_view); + string_buffer(std::string &&); + string_buffer(const char *); }; } \ No newline at end of file diff --git a/include/myxml/cdata.hpp b/include/myxml/cdata.hpp index eed6394..7c94e2e 100644 --- a/include/myxml/cdata.hpp +++ b/include/myxml/cdata.hpp @@ -1,20 +1,38 @@ #pragma once #include "myxml/node.hpp" +#include "myxml/printable.hpp" namespace myxml { - class CData : public Node + class cdata_impl; + + class cdata : public printable { private: - std::string inner; + std::shared_ptr _impl; + + cdata(std::shared_ptr); + + public: + cdata(std::string &&); + cdata(std::string_view); + + virtual void entity_encoding(bool) override {} + virtual void platform_specific_newline(bool) override {} + virtual void print(std::ostream &) const override; + }; + class cdata_impl : public Node + { public: - explicit CData(std::string); + std::string inner; + + cdata_impl() {}; + explicit cdata_impl(std::string_view); + explicit cdata_impl(std::string &&); + virtual ~cdata_impl() = default; - virtual ~CData() = default; - // virtual std::string ExportRaw() const override; - // virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const override; - virtual void entity_encoding(bool) override; + virtual void entity_encoding(bool) override {} virtual void platform_specific_newline(bool) override {} virtual void print(std::ostream &) const override; }; diff --git a/include/myxml/error.hpp b/include/myxml/error.hpp index f46fb19..ea7ae3a 100644 --- a/include/myxml/error.hpp +++ b/include/myxml/error.hpp @@ -3,7 +3,7 @@ namespace myxml { - class ParseError : public std::exception + class parse_error : public std::exception { private: virtual const char *prefix() const = 0; @@ -16,7 +16,7 @@ namespace myxml std::size_t column; public: - ParseError(std::string message, std::size_t line, std::size_t column); + parse_error(std::string message, std::size_t line, std::size_t column); virtual const char *what() const noexcept override; }; @@ -27,46 +27,46 @@ namespace myxml * 2. Unexpected token. Encounter a token that is not expected in the context. For example: extra semicolon. * ... */ - class SyntaxError : public ParseError + class syntax_error : public parse_error { private: virtual const char *prefix() const; public: - SyntaxError(std::string, std::size_t line, std::size_t column); + syntax_error(std::string, std::size_t line, std::size_t column); }; /** * */ - class SemanticError : public ParseError + class semantic_error : public parse_error { private: virtual const char *prefix() const; public: - SemanticError(std::string, std::size_t line, std::size_t column); + semantic_error(std::string, std::size_t line, std::size_t column); }; /** * e.g. EOF */ - class UnexpectedEndOfInput : public ParseError + class unexpected_eof : public parse_error { private: virtual const char *prefix() const; public: - UnexpectedEndOfInput(std::size_t line, std::size_t column); + unexpected_eof(std::size_t line, std::size_t column); }; - class IOError : public std::exception + class io_error : public std::exception { private: std::string message; public: - IOError(std::string); + io_error(std::string); virtual const char *what() const noexcept override; }; diff --git a/include/myxml/parser.hpp b/include/myxml/parser.hpp index 6d9bc5b..d79f00a 100644 --- a/include/myxml/parser.hpp +++ b/include/myxml/parser.hpp @@ -6,9 +6,9 @@ namespace myxml { - struct ElementTag + struct element_tag { - enum class ClosingType + enum class closing_type { Open, // Closed, // @@ -16,25 +16,26 @@ namespace myxml }; std::string name; - ElementTag::ClosingType type = ClosingType::Open; - std::map attris; + element_tag::closing_type type = closing_type::Open; + std::map attrs; }; - class Parser + class parser { private: - std::shared_ptr buffer; + std::shared_ptr _buffer; std::optional peek(); - std::optional peekN(int); - std::optional afterN(int); + std::optional peek_n(int); + std::optional after_n(int); // m characters after n characters - std::optional afterNM(int, int); + std::optional after_n_m(int, int); std::optional take(); - std::optional takeN(int); - void skipWhiteSpaces(); - std::tuple currentLoc(); + std::optional take_n(int); + void skip_whitespaces(); + std::tuple cur_loc(); + public: /** * For all parsing method, * return std::nullopt means `not this one` and will not consume buffer. @@ -46,66 +47,65 @@ namespace myxml * @throws `UnexpectedEndOfInput` * @throws `SyntaxError` if an invalid character occurs. */ - std::string parseIdent(); + std::string parse_ident(); /** * Parse a string literal * @throws `UnexpectedEndOfInput` * @throws `SyntaxError` if missing any of `"` */ - std::string parseStringLiteral(); + std::string parse_str_literal(); /** * @returns std::nullopt if find no attribute * @throws `UnexpectedEndOfInput` * @throws `SyntaxError` if the following chars do not confront to `key="value"` format */ - std::optional> parseAttribute(); + std::optional> parse_attribute(); /** * @throws `SyntaxError` if faild to find `<` */ - std::shared_ptr parseText(); + std::shared_ptr parse_text(); /** * @returns `std::nullopt` if not start with ` parseCData(); + std::shared_ptr parse_cdata(); /** * @throws `UnexpectedEndOfInput` * @throws `SyntaxError` * @throws `SemanticError` */ - std::shared_ptr parseElementWithHeader(ElementTag header); + std::shared_ptr parse_element_with_header(element_tag header); /** * @returns std::nullopt if not starts with ` parseDeclaration(); + std::optional parse_declaration(); - public: - std::shared_ptr ParseElement(); + std::shared_ptr parse_element(); /** * @returns std::nullopt if no heading `<` * @throws `SyntaxError` if the heading character is `<` and the trailing characters are in incorrect format * @throws `UnexpectedEndOfInput` if missing name */ - std::optional ParseElementTag(); + std::optional parse_element_tag(); /** * @throws `UnexpectedEndOfInput` * @throws `SyntaxError` * @throws `SemanticError` */ - document ParseDocument(); - Parser() = delete; - explicit Parser(std::string_view); - explicit Parser(std::string &&); + document parse_document(); + parser() = delete; + explicit parser(std::string_view); + explicit parser(std::string &&); - template >> - explicit Parser(std::shared_ptr buffer) - : buffer(buffer) {} + template >> + explicit parser(std::shared_ptr buffer) + : _buffer(buffer) {} }; namespace util { - bool isValidXmlChar(char ch); + bool is_valid_xml_char(char ch); } } diff --git a/include/myxml/xmlfile.hpp b/include/myxml/xmlfile.hpp index bbd25dc..31dca07 100644 --- a/include/myxml/xmlfile.hpp +++ b/include/myxml/xmlfile.hpp @@ -6,7 +6,7 @@ namespace myxml { - class XMLFile : public Buffer + class XMLFile : public buffer { private: XMLFile(); @@ -18,7 +18,7 @@ namespace myxml std::size_t offset; - /* Implement Buffer*/ + /* Implement buffer*/ virtual std::tuple base() const; public: diff --git a/src/buffer.cpp b/src/buffer.cpp index dc16df5..3a51084 100644 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -2,7 +2,7 @@ namespace myxml { - void Buffer::updateLocation(char ch) + void buffer::update_loc(char ch) { if (ch == '\n') { @@ -15,15 +15,15 @@ namespace myxml } } - void Buffer::updateLocation(std::string_view strv) + void buffer::update_loc(std::string_view strv) { for (auto ch : strv) { - this->updateLocation(ch); + this->update_loc(ch); } } - std::optional Buffer::Peek() const + std::optional buffer::peek() const { auto [ptr, len] = this->base(); if (this->offset >= len) @@ -33,7 +33,7 @@ namespace myxml return ptr[this->offset]; } - std::optional Buffer::PeekN(int n) const + std::optional buffer::peek_n(int n) const { auto [ptr, len] = this->base(); if (this->offset >= len) @@ -43,7 +43,7 @@ namespace myxml return std::string_view(ptr + this->offset, n); } - std::optional Buffer::AfterN(int n) const + std::optional buffer::after_n(int n) const { auto [ptr, len] = this->base(); if (this->offset + n > len) @@ -53,7 +53,7 @@ namespace myxml return ptr[this->offset + n]; } - std::optional Buffer::AfterNM(int n, int m) const + std::optional buffer::after_n_m(int n, int m) const { auto [ptr, len] = this->base(); if (this->offset + n + m > len) @@ -63,7 +63,7 @@ namespace myxml return std::string_view(ptr + this->offset + n, m); } - std::optional Buffer::Take() + std::optional buffer::take() { auto [ptr, len] = this->base(); if (this->offset >= len) @@ -71,11 +71,11 @@ namespace myxml return std::nullopt; } auto ch = ptr[this->offset++]; - this->updateLocation(ch); + this->update_loc(ch); return ch; } - std::optional Buffer::TakeN(int n) + std::optional buffer::take_n(int n) { auto [ptr, len] = this->base(); if (offset + n >= len) @@ -83,38 +83,38 @@ namespace myxml return std::nullopt; } std::string_view strv(ptr + this->offset, n); - this->updateLocation(strv); + this->update_loc(strv); offset += n; return strv; } - std::tuple Buffer::CurrentLocation() + std::tuple buffer::cur_loc() { return {this->line, this->column}; } - StringBuffer::StringBuffer(std::string_view inner) + string_buffer::string_buffer(std::string_view inner) : inner(inner) { } - StringBuffer::StringBuffer(std::string &&inner) + string_buffer::string_buffer(std::string &&inner) : inner(inner) { } - StringBuffer::StringBuffer(const char *ptr) - : StringBuffer(std::string_view(ptr)) + string_buffer::string_buffer(const char *ptr) + : string_buffer(std::string_view(ptr)) { } - std::tuple StringBuffer::base() const + std::tuple string_buffer::base() const { - auto view = this->getView(); + auto view = this->view(); return {view.data(), view.length()}; } - std::string_view StringBuffer::getView() const + std::string_view string_buffer::view() const { if (std::holds_alternative(this->inner)) { diff --git a/src/cdata.cpp b/src/cdata.cpp index d4c239b..f79542c 100644 --- a/src/cdata.cpp +++ b/src/cdata.cpp @@ -4,26 +4,37 @@ namespace myxml { - CData::CData(std::string str) - : inner(str) + cdata::cdata(std::shared_ptr impl) + : _impl(impl) + { + } + + cdata::cdata(std::string_view str) + : _impl(std::make_shared(str)) + { + } + + cdata::cdata(std::string &&str) + : _impl(std::make_shared(str)) { } - // std::string CData::ExportRaw() const - // { - // return fmt::format("\n", this->inner); - // } + void cdata::print(std::ostream &os) const + { + _impl->print(os); + } - // std::string CData::ExportFormatted(int indentLevel, int indentSize) const - // { - // return std::string(indentLevel * indentSize, ' ') + this->ExportRaw(); - // } + cdata_impl::cdata_impl(std::string_view str) + : inner(str) + { + } - void CData::entity_encoding(bool) - { // do nothing + cdata_impl::cdata_impl(std::string &&str) + : inner(str) + { } - void CData::print(std::ostream &os) const + void cdata_impl::print(std::ostream &os) const { os << "inner << "]]>\n"; } diff --git a/src/document.cpp b/src/document.cpp index e3f2aa4..519e04d 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -54,13 +54,13 @@ namespace myxml document document::parse(std::string_view input) { - return Parser(input).ParseDocument(); + return parser(input).parse_document(); } document document::load(std::string fileName) { auto f = XMLFile::Open(fileName); - return Parser(f).ParseDocument(); + return parser(f).parse_document(); } // std::string document::ExportRaw() const // { diff --git a/src/element.cpp b/src/element.cpp index 4551e15..52c38b9 100644 --- a/src/element.cpp +++ b/src/element.cpp @@ -80,13 +80,13 @@ namespace myxml std::shared_ptr element_impl::parse(std::string_view buf) { - return Parser(buf).ParseElement(); + return parser(buf).parse_element(); } std::shared_ptr element_impl::load(std::string_view path) { auto f = XMLFile::Open(path); - return Parser(f).ParseElement(); + return parser(f).parse_element(); } void element_impl::extend_attributes(std::map attris) diff --git a/src/error.cpp b/src/error.cpp index 8dc02ee..aef71b7 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -3,54 +3,54 @@ namespace myxml { - ParseError::ParseError(std::string message, std::size_t line, std::size_t column) + parse_error::parse_error(std::string message, std::size_t line, std::size_t column) : message(message), line(line), column(column) { } - const char *ParseError::what() const noexcept + const char *parse_error::what() const noexcept { this->fullMessage = fmt::format("{}{}\nin line: {} column: {}", this->prefix(), this->message, this->line, this->column); return this->fullMessage.c_str(); } - const char *SyntaxError::prefix() const + const char *syntax_error::prefix() const { return "Syntax Error: "; } - SyntaxError::SyntaxError(std::string, std::size_t line, std::size_t column) - : ParseError(message, line, column) + syntax_error::syntax_error(std::string, std::size_t line, std::size_t column) + : parse_error(message, line, column) { } - const char *SemanticError::prefix() const + const char *semantic_error::prefix() const { return "Sematic Error: "; } - SemanticError::SemanticError(std::string, std::size_t line, std::size_t column) - : ParseError(message, line, column) + semantic_error::semantic_error(std::string, std::size_t line, std::size_t column) + : parse_error(message, line, column) { } - const char *UnexpectedEndOfInput::prefix() const + const char *unexpected_eof::prefix() const { return "Unexpected End of Input: "; } - UnexpectedEndOfInput::UnexpectedEndOfInput(std::size_t line, std::size_t column) - : ParseError("End of input", line, column) + unexpected_eof::unexpected_eof(std::size_t line, std::size_t column) + : parse_error("End of input", line, column) { } - IOError::IOError(std::string message) + io_error::io_error(std::string message) : message(message) { } - const char *IOError::what() const noexcept + const char *io_error::what() const noexcept { return message.c_str(); } diff --git a/src/parser.cpp b/src/parser.cpp index 08e31e0..23a67c5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5,7 +5,7 @@ namespace myxml { - void Parser::skipWhiteSpaces() + void parser::skip_whitespaces() { while (this->peek() && std::isspace(*this->peek())) { @@ -13,200 +13,200 @@ namespace myxml } } - std::tuple Parser::currentLoc() + std::tuple parser::cur_loc() { - return this->buffer->CurrentLocation(); + return this->_buffer->cur_loc(); } - std::optional Parser::peek() + std::optional parser::peek() { - return this->buffer->Peek(); + return this->_buffer->peek(); } - std::optional Parser::peekN(int n) + std::optional parser::peek_n(int n) { - return this->buffer->PeekN(n); + return this->_buffer->peek_n(n); } - std::optional Parser::afterN(int n) + std::optional parser::after_n(int n) { - return this->buffer->AfterN(n); + return this->_buffer->after_n(n); } - std::optional Parser::afterNM(int n, int m) + std::optional parser::after_n_m(int n, int m) { - return this->buffer->AfterNM(n, m); + return this->_buffer->after_n_m(n, m); } - std::optional Parser::take() + std::optional parser::take() { - return this->buffer->Take(); + return this->_buffer->take(); } - std::optional Parser::takeN(int n) + std::optional parser::take_n(int n) { - return this->buffer->TakeN(n); + return this->_buffer->take_n(n); } - std::string Parser::parseIdent() + std::string parser::parse_ident() { if (this->peek() == std::nullopt) { - auto [line, col] = this->currentLoc(); - throw UnexpectedEndOfInput(line, col); + auto [line, col] = this->cur_loc(); + throw unexpected_eof(line, col); } // validate heading character if (auto head = this->peek(); !head || (!std::isalpha(*head) && head != '_')) { - auto [line, col] = this->currentLoc(); - throw SyntaxError(fmt::format("element name which starts with {} is invalid.", *head), line, col); + auto [line, col] = this->cur_loc(); + throw syntax_error(fmt::format("element name which starts with {} is invalid.", *head), line, col); } std::size_t len = 0; - while (this->afterN(len) && util::isValidXmlChar(*this->afterN(len))) + while (this->after_n(len) && util::is_valid_xml_char(*this->after_n(len))) { len++; } - return std::string(*this->takeN(len)); + return std::string(*this->take_n(len)); } - std::string Parser::parseStringLiteral() + std::string parser::parse_str_literal() { if (!this->peek()) { - auto [line, col] = this->currentLoc(); - throw UnexpectedEndOfInput(line, col); + auto [line, col] = this->cur_loc(); + throw unexpected_eof(line, col); } if (this->peek() != '"') { - auto [line, col] = this->currentLoc(); - throw SyntaxError(fmt::format("expected '\"' at the beginning of string literal, find {}", *this->peek()), line, col); + auto [line, col] = this->cur_loc(); + throw syntax_error(fmt::format("expected '\"' at the beginning of string literal, find {}", *this->peek()), line, col); } this->take(); std::size_t len = 0; - while (this->afterN(len) != '"') + while (this->after_n(len) != '"') { len++; } - if (!this->afterN(len)) + if (!this->after_n(len)) { // if jump out due to length limit - auto [line, col] = this->currentLoc(); - throw SyntaxError(fmt::format("missing closing double quote for string literal"), line, col); + auto [line, col] = this->cur_loc(); + throw syntax_error(fmt::format("missing closing double quote for string literal"), line, col); } - auto it = this->takeN(len); + auto it = this->take_n(len); this->take(); // skip " return std::string(*it); } - std::optional> Parser::parseAttribute() + std::optional> parser::parse_attribute() { - this->skipWhiteSpaces(); + this->skip_whitespaces(); std::pair attr; std::string key; try { - key = this->parseIdent(); + key = this->parse_ident(); } - catch (SyntaxError e) + catch (syntax_error e) { // Only SyntaxError in parseIdent is incorrect heading character return std::nullopt; } - catch (UnexpectedEndOfInput e) + catch (unexpected_eof e) { // There must be `>` or else after all attributes throw e; } if (this->take() != '=') { - auto [line, col] = this->currentLoc(); - throw SyntaxError(fmt::format("expected '=' after attribute name"), line, col); + auto [line, col] = this->cur_loc(); + throw syntax_error(fmt::format("expected '=' after attribute name"), line, col); } attr.first = key; - auto value = this->parseStringLiteral(); + auto value = this->parse_str_literal(); attr.second = value; return attr; } - std::shared_ptr Parser::parseText() + std::shared_ptr parser::parse_text() { if (!this->peek()) { - auto [line, col] = this->currentLoc(); - throw UnexpectedEndOfInput(line, col); + auto [line, col] = this->cur_loc(); + throw unexpected_eof(line, col); } std::size_t len = 0; - while (this->afterN(len) != '<') + while (this->after_n(len) != '<') { len++; } - if (!this->afterN(len)) + if (!this->after_n(len)) { // if jump out of while loop due to length limit - auto [line, col] = this->currentLoc(); - throw SyntaxError(fmt::format("expected '<' after text"), line, col); + auto [line, col] = this->cur_loc(); + throw syntax_error(fmt::format("expected '<' after text"), line, col); } - return std::shared_ptr(new text_impl(*this->takeN(len))); + return std::shared_ptr(new text_impl(*this->take_n(len))); } - std::shared_ptr Parser::parseCData() + std::shared_ptr parser::parse_cdata() { - if (this->peekN(9) != "peek_n(9) != "takeN(9); + this->take_n(9); std::size_t len = 0; - while (this->afterNM(len, 3) != "]]>") + while (this->after_n_m(len, 3) != "]]>") { len++; } - if (!this->afterN(len + 2)) + if (!this->after_n(len + 2)) { - auto [line, col] = this->currentLoc(); - throw SyntaxError(fmt::format("expected \"]]>\" after CDATA"), line, col); + auto [line, col] = this->cur_loc(); + throw syntax_error(fmt::format("expected \"]]>\" after CDATA"), line, col); } - auto it = std::string(*this->takeN(len)); - this->takeN(2); - return std::make_shared(it); + auto it = std::string(*this->take_n(len)); + this->take_n(2); + return std::make_shared(it); } - std::optional Parser::ParseElementTag() + std::optional parser::parse_element_tag() { if (this->take() != '<') { return std::nullopt; } - ElementTag tag; + element_tag tag; if (this->peek() == '/') { - tag.type = ElementTag::ClosingType::Closing; + tag.type = element_tag::closing_type::Closing; this->take(); } - this->skipWhiteSpaces(); - auto name = this->parseIdent(); + this->skip_whitespaces(); + auto name = this->parse_ident(); tag.name = name; - this->skipWhiteSpaces(); - while (auto attr = this->parseAttribute()) + this->skip_whitespaces(); + while (auto attr = this->parse_attribute()) { - tag.attris.insert(*attr); + tag.attrs.insert(*attr); } - this->skipWhiteSpaces(); + this->skip_whitespaces(); if (this->peek() == '/') { - if (tag.type != ElementTag::ClosingType::Open) + if (tag.type != element_tag::closing_type::Open) { - auto [line, col] = this->currentLoc(); - throw SyntaxError(fmt::format("unexpected ending '/' found in closing tag"), line, col); + auto [line, col] = this->cur_loc(); + throw syntax_error(fmt::format("unexpected ending '/' found in closing tag"), line, col); } - tag.type = ElementTag::ClosingType::Closed; + tag.type = element_tag::closing_type::Closed; this->take(); } if (this->take() != '>') { - auto [line, col] = this->currentLoc(); - throw SyntaxError(fmt::format("expected '>' at the end of the tag"), line, col); + auto [line, col] = this->cur_loc(); + throw syntax_error(fmt::format("expected '>' at the end of the tag"), line, col); } return tag; } - std::shared_ptr Parser::parseElementWithHeader(ElementTag header) + std::shared_ptr parser::parse_element_with_header(element_tag header) { auto elem = element_impl::_new(); elem->name = header.name; @@ -216,41 +216,41 @@ namespace myxml { case '<': { - if (auto cdata = this->parseCData(); cdata) + if (auto cdata = this->parse_cdata(); cdata) { elem->InsertAtEnd(cdata); continue; } - auto tag = this->ParseElementTag(); // impossible to be std::nullopt + auto tag = this->parse_element_tag(); // impossible to be std::nullopt assert(tag); switch (tag->type) { - case ElementTag::ClosingType::Open: + case element_tag::closing_type::Open: { - auto child = this->parseElementWithHeader(*tag); + auto child = this->parse_element_with_header(*tag); elem->InsertAtEnd(child); break; } - case ElementTag::ClosingType::Closed: + case element_tag::closing_type::Closed: { auto child = element_impl::_new(); child->name = tag->name; - if (!tag->attris.empty()) + if (!tag->attrs.empty()) { - child->extend_attributes(tag->attris); + child->extend_attributes(tag->attrs); } elem->InsertAtEnd(child); break; } - case ElementTag::ClosingType::Closing: + case element_tag::closing_type::Closing: if (tag->name != elem->name) { - auto [line, col] = this->currentLoc(); - throw SyntaxError(fmt::format("elem name in closing tag is mismatched with the header"), line, col); + auto [line, col] = this->cur_loc(); + throw syntax_error(fmt::format("elem name in closing tag is mismatched with the header"), line, col); } - if (!header.attris.empty()) + if (!header.attrs.empty()) { - elem->extend_attributes(header.attris); + elem->extend_attributes(header.attrs); } return elem; default: @@ -259,38 +259,38 @@ namespace myxml break; } default: - auto text = this->parseText(); + auto text = this->parse_text(); elem->InsertAtEnd(text); break; } } - auto [line, col] = this->currentLoc(); - throw UnexpectedEndOfInput(line, col); + auto [line, col] = this->cur_loc(); + throw unexpected_eof(line, col); } - std::shared_ptr Parser::ParseElement() + std::shared_ptr parser::parse_element() { - this->skipWhiteSpaces(); - if (auto tag = this->ParseElementTag(); tag) + this->skip_whitespaces(); + if (auto tag = this->parse_element_tag(); tag) { - if (tag->type == ElementTag::ClosingType::Closed) + if (tag->type == element_tag::closing_type::Closed) { auto elem = element_impl::_new(); elem->name = tag->name; - if (!tag->attris.empty()) + if (!tag->attrs.empty()) { - elem->extend_attributes(tag->attris); + elem->extend_attributes(tag->attrs); } return elem; } - else if (tag->type == ElementTag::ClosingType::Open) + else if (tag->type == element_tag::closing_type::Open) { - return this->parseElementWithHeader(*tag); + return this->parse_element_with_header(*tag); } else // Closing { - auto [line, col] = this->currentLoc(); - throw SyntaxError(fmt::format("unexpected closing tag"), line, col); + auto [line, col] = this->cur_loc(); + throw syntax_error(fmt::format("unexpected closing tag"), line, col); } } else @@ -299,23 +299,23 @@ namespace myxml } } - std::optional Parser::parseDeclaration() + std::optional parser::parse_declaration() { - if (this->peekN(5) != "peek_n(5) != "takeN(5); + this->take_n(5); std::map attrs; - while (auto attr = this->parseAttribute()) + while (auto attr = this->parse_attribute()) { attrs.insert(*attr); } - this->skipWhiteSpaces(); - if (this->takeN(2) != "?>") + this->skip_whitespaces(); + if (this->take_n(2) != "?>") { - auto [line, col] = this->currentLoc(); - throw SyntaxError(fmt::format("expected \"?>\" at end of xml declaration"), line, col); + auto [line, col] = this->cur_loc(); + throw syntax_error(fmt::format("expected \"?>\" at end of xml declaration"), line, col); } if (auto decl = declaration::from_attrs(attrs); decl) { @@ -323,41 +323,41 @@ namespace myxml } else { - auto [line, col] = this->currentLoc(); - throw SemanticError(fmt::format("declaration has incorrect attributes"), line, col); + auto [line, col] = this->cur_loc(); + throw semantic_error(fmt::format("declaration has incorrect attributes"), line, col); } } - document Parser::ParseDocument() + document parser::parse_document() { document document; - if (auto decl = this->parseDeclaration(); decl) + if (auto decl = this->parse_declaration(); decl) { document.set_declaration(*decl); } - if (auto root = this->ParseElement(); root) + if (auto root = this->parse_element(); root) { document.set_root(root); } else { - auto [line, col] = this->currentLoc(); - throw SemanticError(fmt::format("missing root element in xml document"), line, col); + auto [line, col] = this->cur_loc(); + throw semantic_error(fmt::format("missing root element in xml document"), line, col); } return document; } - Parser::Parser(std::string_view buffer) - : buffer(std::make_shared(buffer)) + parser::parser(std::string_view buffer) + : _buffer(std::make_shared(buffer)) { } - Parser::Parser(std::string &&buffer) - : buffer(std::make_shared(buffer)) + parser::parser(std::string &&buffer) + : _buffer(std::make_shared(buffer)) { } - bool util::isValidXmlChar(char ch) + bool util::is_valid_xml_char(char ch) { return std::isalnum(ch) || ch == '_' || ch == '-' || ch == '.' || ch == ':'; } diff --git a/src/xmlfile.cpp b/src/xmlfile.cpp index 66d3941..207635e 100644 --- a/src/xmlfile.cpp +++ b/src/xmlfile.cpp @@ -22,18 +22,18 @@ namespace myxml xfile->fd = open(fpath.data(), O_RDONLY); if (xfile->fd == -1) { - throw IOError(fmt::format("failed to open file: {}", fpath)); + throw io_error(fmt::format("failed to open file: {}", fpath)); } struct stat fileInfo; if (fstat(xfile->fd, &fileInfo) == -1) { - throw IOError(fmt::format("failed to get info of file: {}", fpath)); + throw io_error(fmt::format("failed to get info of file: {}", fpath)); } xfile->fileSize = fileInfo.st_size; void *mappedRegion = mmap(nullptr, xfile->fileSize, PROT_READ, MAP_PRIVATE, xfile->fd, 0); if (mappedRegion == MAP_FAILED) { - throw IOError(fmt::format("failed to map memory for file: {}", fpath)); + throw io_error(fmt::format("failed to map memory for file: {}", fpath)); } xfile->inner = static_cast(mappedRegion); return xfile; diff --git a/tests/buffer_test.cpp b/tests/buffer_test.cpp index 8131164..26428d4 100644 --- a/tests/buffer_test.cpp +++ b/tests/buffer_test.cpp @@ -6,25 +6,25 @@ TEST_CASE("String Buffer", "[buffer]") { SECTION("Simple String Buffer") { - myxml::StringBuffer sb = "Hello, world!"; - REQUIRE(sb.Peek() == 'H'); - REQUIRE(sb.PeekN(3) == "Hel"); - REQUIRE(sb.TakeN(3) == "Hel"); - REQUIRE(sb.AfterN(1) == 'o'); - REQUIRE(sb.AfterNM(1, 2) == "o,"); + myxml::string_buffer sb = "Hello, world!"; + REQUIRE(sb.peek() == 'H'); + REQUIRE(sb.peek_n(3) == "Hel"); + REQUIRE(sb.take_n(3) == "Hel"); + REQUIRE(sb.after_n(1) == 'o'); + REQUIRE(sb.after_n_m(1, 2) == "o,"); } SECTION("Location", "[buffer]") { - myxml::StringBuffer sb = "Hello, world!\nLine2"; + myxml::string_buffer sb = "Hello, world!\nLine2"; - REQUIRE(sb.TakeN(3) == "Hel"); - REQUIRE(sb.CurrentLocation() == std::make_tuple(0, 3)); - REQUIRE(sb.TakeN(4) == "lo, "); - REQUIRE(sb.CurrentLocation() == std::make_tuple(0, 7)); - REQUIRE(sb.TakeN(7) == "world!\n"); - REQUIRE(sb.CurrentLocation() == std::make_tuple(1, 0)); - REQUIRE(sb.TakeN(3) == "Lin"); - REQUIRE(sb.CurrentLocation() == std::make_tuple(1, 3)); + REQUIRE(sb.take_n(3) == "Hel"); + REQUIRE(sb.cur_loc() == std::make_tuple(0, 3)); + REQUIRE(sb.take_n(4) == "lo, "); + REQUIRE(sb.cur_loc() == std::make_tuple(0, 7)); + REQUIRE(sb.take_n(7) == "world!\n"); + REQUIRE(sb.cur_loc() == std::make_tuple(1, 0)); + REQUIRE(sb.take_n(3) == "Lin"); + REQUIRE(sb.cur_loc() == std::make_tuple(1, 3)); } } \ No newline at end of file diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index a80338f..80bbab8 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -7,19 +7,19 @@ TEST_CASE("Parsing tag", "parser") { std::string open = ""; - auto tag = myxml::Parser(open).ParseElementTag(); + auto tag = myxml::parser(open).parse_element_tag(); REQUIRE(tag->name == "tag"); - REQUIRE(tag->type == myxml::ElementTag::ClosingType::Open); + REQUIRE(tag->type == myxml::element_tag::closing_type::Open); std::string closed = ""; - tag = myxml::Parser(closed).ParseElementTag(); + tag = myxml::parser(closed).parse_element_tag(); REQUIRE(tag->name == "tag"); - REQUIRE(tag->type == myxml::ElementTag::ClosingType::Closed); + REQUIRE(tag->type == myxml::element_tag::closing_type::Closed); std::string closing = ""; - tag = myxml::Parser(closing).ParseElementTag(); + tag = myxml::parser(closing).parse_element_tag(); REQUIRE(tag->name == "tag"); - REQUIRE(tag->type == myxml::ElementTag::ClosingType::Closing); + REQUIRE(tag->type == myxml::element_tag::closing_type::Closing); } TEST_CASE("Parsing simple xml elements", "[parser]") @@ -200,7 +200,7 @@ TEST_CASE("Parsing simple xml elements", "[parser]") std::string cdata = ""; auto elem = myxml::element_impl::parse(cdata); - REQUIRE(elem->FirstChild()->As()->str() == "\n"); + REQUIRE(elem->FirstChild()->As()->str() == "\n"); } SECTION("Newline Normalization") From 4555a008194753a525f8db671b46c481bf50c39f Mon Sep 17 00:00:00 2001 From: adamska <2639980868@qq.com> Date: Thu, 5 Sep 2024 14:45:21 +0800 Subject: [PATCH 5/9] refactor: naming two node classes --- include/myxml/cdata.hpp | 2 +- include/myxml/element.hpp | 2 +- include/myxml/node.hpp | 77 ++++++++++++-------------- include/myxml/text.hpp | 2 +- src/element.cpp | 32 ++--------- src/node.cpp | 110 ++++++++++++++------------------------ src/parser.cpp | 8 +-- tests/element_test.cpp | 30 ++++++----- tests/parser_test.cpp | 95 ++++++++++++++++---------------- 9 files changed, 151 insertions(+), 207 deletions(-) diff --git a/include/myxml/cdata.hpp b/include/myxml/cdata.hpp index 7c94e2e..4e635e4 100644 --- a/include/myxml/cdata.hpp +++ b/include/myxml/cdata.hpp @@ -22,7 +22,7 @@ namespace myxml virtual void print(std::ostream &) const override; }; - class cdata_impl : public Node + class cdata_impl : public node { public: std::string inner; diff --git a/include/myxml/element.hpp b/include/myxml/element.hpp index 9f2a3e6..936d751 100644 --- a/include/myxml/element.hpp +++ b/include/myxml/element.hpp @@ -53,7 +53,7 @@ namespace myxml virtual void platform_specific_newline(bool) override; }; - struct element_impl : public CompositeNode // public std::enable_shared_from_this, public Node + struct element_impl : public composite_node // public std::enable_shared_from_this, public Node { public: std::string name; diff --git a/include/myxml/node.hpp b/include/myxml/node.hpp index ecbe847..ea24b65 100644 --- a/include/myxml/node.hpp +++ b/include/myxml/node.hpp @@ -12,18 +12,18 @@ namespace myxml // defined in text.hpp class text_impl; // defined below - class CompositeNode; + class composite_node; // Element, Text are Node. - class Node : public std::enable_shared_from_this, public printable + class node : public std::enable_shared_from_this, public printable { - private: - template >> - std::shared_ptr Next() + public: + template >> + std::shared_ptr next() { - for (auto it = this->NextSibiling(); it != nullptr; it = it->NextSibiling()) + for (auto it = this->next_sibiling(); it != nullptr; it = it->next_sibiling()) { - if (auto cast = it->As(); cast) + if (auto cast = it->as(); cast) { return cast; } @@ -31,12 +31,12 @@ namespace myxml return nullptr; } - template >> - std::shared_ptr Prev() + template >> + std::shared_ptr prev() { - for (auto it = this->PrevSibiling(); it != nullptr; it = it->PrevSibiling()) + for (auto it = this->prev_sibiling(); it != nullptr; it = it->prev_sibiling()) { - if (auto cast = it->As(); cast) + if (auto cast = it->as(); cast) { return cast; } @@ -44,25 +44,20 @@ namespace myxml return nullptr; } - public: - virtual ~Node() = default; - std::shared_ptr parent; - std::shared_ptr prev; - std::shared_ptr next; + virtual ~node() = default; + std::shared_ptr _parent; + std::shared_ptr _prev; + std::shared_ptr _next; - template >> - std::shared_ptr As() + template >> + std::shared_ptr as() { return std::dynamic_pointer_cast(this->shared_from_this()); } /* Query */ - std::shared_ptr NextSibiling(); - std::shared_ptr PrevSibiling(); - std::shared_ptr NextElem(); - std::shared_ptr PrevElem(); - std::shared_ptr NextText(); - std::shared_ptr PrevText(); + std::shared_ptr next_sibiling(); + std::shared_ptr prev_sibiling(); /** Implement Export */ virtual void entity_encoding(bool) override; @@ -70,31 +65,29 @@ namespace myxml }; // Element are Composite Node. - class CompositeNode : public Node + class composite_node : public node { private: - std::shared_ptr firstChild; - std::shared_ptr lastChild; + std::shared_ptr firstChild; + std::shared_ptr lastChild; std::map, std::less<>> nameToElemBuffer; public: - virtual ~CompositeNode() = default; + virtual ~composite_node() = default; /* Query */ - std::shared_ptr FirstChild(); - const std::shared_ptr &FirstChild() const; - std::shared_ptr LastChild(); - const std::shared_ptr &LastChild() const; - std::shared_ptr Elem(std::string_view name); - std::shared_ptr FirstElem(); - std::shared_ptr FirstText(); + std::shared_ptr first_child(); + const std::shared_ptr &first_child() const; + std::shared_ptr last_child(); + const std::shared_ptr &last_child() const; + std::shared_ptr first_elem(std::string_view name); - template >> - std::shared_ptr First() + template >> + std::shared_ptr first() { - for (auto it = this->FirstChild(); it != nullptr; it = it->NextSibiling()) + for (auto it = this->first_child(); it != nullptr; it = it->next_sibiling()) { - if (auto cast = it->As(); cast) + if (auto cast = it->as(); cast) { return cast; } @@ -103,9 +96,9 @@ namespace myxml } /* Manipulate */ - std::shared_ptr InsertAtFront(const std::shared_ptr &); - std::shared_ptr InsertAtEnd(const std::shared_ptr &); - void Unlink(const std::shared_ptr &); + std::shared_ptr push_front(const std::shared_ptr &); + std::shared_ptr push_back(const std::shared_ptr &); + void unlink(const std::shared_ptr &); /** Implement Export */ virtual void entity_encoding(bool) override; diff --git a/include/myxml/text.hpp b/include/myxml/text.hpp index ea7e3b4..a5f20f2 100644 --- a/include/myxml/text.hpp +++ b/include/myxml/text.hpp @@ -24,7 +24,7 @@ namespace myxml virtual void platform_specific_newline(bool) override; }; - struct text_impl : public Node + struct text_impl : public node { std::string inner; diff --git a/src/element.cpp b/src/element.cpp index 52c38b9..b33e45c 100644 --- a/src/element.cpp +++ b/src/element.cpp @@ -37,17 +37,17 @@ namespace myxml element element::first_elem() { - return _impl->First(); + return _impl->first(); } element element::first_elem(std::string_view name) { - return _impl->Elem(name); + return _impl->first_elem(name); } text element::first_text() { - return _impl->First(); + return _impl->first(); } void element::print(std::ostream &os) const @@ -128,41 +128,19 @@ namespace myxml { os << "" << key << "=\"" << value << "\""; } - if (this->FirstChild() == nullptr) + if (this->first_child() == nullptr) { os << " />"; return; } os << ">"; - for (auto node = this->FirstChild(); node != nullptr; node = node->NextSibiling()) + for (auto node = this->first_child(); node != nullptr; node = node->next_sibiling()) { node->print(os); } os << "name << ">"; } - // std::string element_impl::ExportFormatted(int indentLevel, int indentSize) const - // { - // std::string indent(indentLevel * indentSize, ' '); - // std::string builder = indent + "<" + std::string(this->name); - // for (const auto &[key, value] : this->attributes) - // { - // builder += "" + key + "=\"" + value + "\""; - // } - // if (this->FirstChild() == nullptr) - // { - // builder += " />\n"; - // return builder; - // } - // builder += ">\n"; - // for (auto node = this->FirstChild(); node != nullptr; node = node->NextSibiling()) - // { - // builder += node->ExportFormatted(indentLevel + 1, indentSize); - // } - // builder += indent + "name) + ">\n"; - // return builder; - // } - namespace literals { element operator""_elem(const char *literal, std::size_t len) diff --git a/src/node.cpp b/src/node.cpp index 5ed56df..c3d69ba 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -4,77 +4,47 @@ namespace myxml { - void Node::entity_encoding(bool flag) + void node::entity_encoding(bool flag) { this->config.entity_encoding = flag; } - void Node::platform_specific_newline(bool flag) + void node::platform_specific_newline(bool flag) { this->config.platform_specific_newline = flag; } - std::shared_ptr Node::NextSibiling() + std::shared_ptr node::next_sibiling() { - return this->next; + return this->_next; } - std::shared_ptr Node::PrevSibiling() + std::shared_ptr node::prev_sibiling() { - return this->prev; + return this->_prev; } - std::shared_ptr Node::NextElem() - { - return this->Next(); - } - - std::shared_ptr Node::PrevElem() - { - return this->Prev(); - } - - std::shared_ptr Node::NextText() - { - return this->Next(); - } - - std::shared_ptr Node::PrevText() - { - return this->Prev(); - } - - std::shared_ptr CompositeNode::LastChild() + std::shared_ptr composite_node::last_child() { return this->lastChild; } - const std::shared_ptr &CompositeNode::LastChild() const + const std::shared_ptr &composite_node::last_child() const { return this->lastChild; } - std::shared_ptr CompositeNode::FirstChild() + std::shared_ptr composite_node::first_child() { return this->firstChild; } - const std::shared_ptr &CompositeNode::FirstChild() const + const std::shared_ptr &composite_node::first_child() const { return this->firstChild; } - std::shared_ptr CompositeNode::FirstElem() - { - return this->First(); - } - - std::shared_ptr CompositeNode::FirstText() - { - return this->First(); - } - - std::shared_ptr CompositeNode::Elem(std::string_view name) + std::shared_ptr composite_node::first_elem(std::string_view name) { if (auto buf = this->nameToElemBuffer.find(name); buf != this->nameToElemBuffer.end()) { @@ -88,9 +58,9 @@ namespace myxml this->nameToElemBuffer.erase(buf); } } - for (auto child = this->firstChild; child != nullptr; child = child->next) + for (auto child = this->firstChild; child != nullptr; child = child->_next) { - if (auto elem = child->As(); elem && elem->name == name) + if (auto elem = child->as(); elem && elem->name == name) { this->nameToElemBuffer.emplace(name, elem); return elem; @@ -99,13 +69,13 @@ namespace myxml return nullptr; } - std::shared_ptr CompositeNode::InsertAtFront(const std::shared_ptr &elem) + std::shared_ptr composite_node::push_front(const std::shared_ptr &elem) { - if (elem->parent != nullptr) + if (elem->_parent != nullptr) { - elem->parent->Unlink(elem); + elem->_parent->unlink(elem); } - elem->parent = this->shared_from_this()->As(); + elem->_parent = this->shared_from_this()->as(); if (this->firstChild == nullptr) { this->firstChild = elem; @@ -113,20 +83,20 @@ namespace myxml } else { - this->firstChild->prev = elem; - elem->next = this->firstChild; + this->firstChild->_prev = elem; + elem->_next = this->firstChild; this->firstChild = elem; } return elem; } - std::shared_ptr CompositeNode::InsertAtEnd(const std::shared_ptr &elem) + std::shared_ptr composite_node::push_back(const std::shared_ptr &elem) { - if (elem->parent != nullptr) + if (elem->_parent != nullptr) { - elem->parent->Unlink(elem); + elem->_parent->unlink(elem); } - elem->parent = this->shared_from_this()->As(); + elem->_parent = this->shared_from_this()->as(); if (this->firstChild == nullptr) { this->firstChild = elem; @@ -134,53 +104,53 @@ namespace myxml } else { - this->lastChild->next = elem; - elem->prev = this->lastChild; + this->lastChild->_next = elem; + elem->_prev = this->lastChild; this->lastChild = elem; } return elem; } - void CompositeNode::Unlink(const std::shared_ptr &elem) + void composite_node::unlink(const std::shared_ptr &elem) { - if (elem->parent.get() != this) + if (elem->_parent.get() != this) { return; } if (elem == this->firstChild) { - this->firstChild = this->firstChild->next; + this->firstChild = this->firstChild->_next; } if (elem == this->lastChild) { - this->lastChild = this->lastChild->prev; + this->lastChild = this->lastChild->_prev; } - if (elem->prev != nullptr) + if (elem->_prev != nullptr) { - elem->prev->next = elem->next; + elem->_prev->_next = elem->_next; } - if (elem->next != nullptr) + if (elem->_next != nullptr) { - elem->next->prev = elem->prev; + elem->_next->_prev = elem->_prev; } - elem->next = nullptr; - elem->prev = nullptr; - elem->parent = nullptr; + elem->_next = nullptr; + elem->_prev = nullptr; + elem->_parent = nullptr; } - void CompositeNode::entity_encoding(bool flag) + void composite_node::entity_encoding(bool flag) { this->config.entity_encoding = flag; - for (auto it = this->FirstChild(); it != nullptr; it = it->next) + for (auto it = this->first_child(); it != nullptr; it = it->_next) { it->entity_encoding(flag); } } - void CompositeNode::platform_specific_newline(bool flag) + void composite_node::platform_specific_newline(bool flag) { this->config.platform_specific_newline = flag; - for (auto it = this->FirstChild(); it != nullptr; it = it->next) + for (auto it = this->first_child(); it != nullptr; it = it->_next) { it->platform_specific_newline(flag); } diff --git a/src/parser.cpp b/src/parser.cpp index 23a67c5..af8d028 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -218,7 +218,7 @@ namespace myxml { if (auto cdata = this->parse_cdata(); cdata) { - elem->InsertAtEnd(cdata); + elem->push_back(cdata); continue; } auto tag = this->parse_element_tag(); // impossible to be std::nullopt @@ -228,7 +228,7 @@ namespace myxml case element_tag::closing_type::Open: { auto child = this->parse_element_with_header(*tag); - elem->InsertAtEnd(child); + elem->push_back(child); break; } case element_tag::closing_type::Closed: @@ -239,7 +239,7 @@ namespace myxml { child->extend_attributes(tag->attrs); } - elem->InsertAtEnd(child); + elem->push_back(child); break; } case element_tag::closing_type::Closing: @@ -260,7 +260,7 @@ namespace myxml } default: auto text = this->parse_text(); - elem->InsertAtEnd(text); + elem->push_back(text); break; } } diff --git a/tests/element_test.cpp b/tests/element_test.cpp index 19ce7c6..f5686d7 100644 --- a/tests/element_test.cpp +++ b/tests/element_test.cpp @@ -3,9 +3,11 @@ TEST_CASE("Element Impl", "[element]") { - auto root = myxml::element_impl::_new("root"); - auto child = myxml::element_impl::_new("child"); - auto sibiling = myxml::element_impl::_new("sibiling"); + using namespace myxml; + + auto root = element_impl::_new("root"); + auto child = element_impl::_new("child"); + auto sibiling = element_impl::_new("sibiling"); SECTION("GetName") { @@ -14,27 +16,27 @@ TEST_CASE("Element Impl", "[element]") SECTION("Basic Insertion") { - root->InsertAtFront(child); - REQUIRE(root->FirstElem()->name == "child"); - REQUIRE(root->LastChild()->As()->name == "child"); + root->push_front(child); + REQUIRE(root->first()->name == "child"); + REQUIRE(root->last_child()->as()->name == "child"); } SECTION("Get child by name after insert it") { - root->InsertAtFront(child); + root->push_front(child); // Unbuffered - REQUIRE(root->Elem("child")->name == "child"); + REQUIRE(root->first_elem("child")->name == "child"); // Buffered - REQUIRE(root->Elem("child")->name == "child"); + REQUIRE(root->first_elem("child")->name == "child"); } SECTION("Multi child") { - root->InsertAtEnd(child); - root->InsertAtEnd(sibiling); - REQUIRE(root->Elem("child")->name == "child"); - REQUIRE(root->Elem("child")->NextElem()->name == "sibiling"); - REQUIRE(root->Elem("sibiling")->PrevElem()->name == "child"); + root->push_back(child); + root->push_back(sibiling); + REQUIRE(root->first_elem("child")->name == "child"); + REQUIRE(root->first_elem("child")->next()->name == "sibiling"); + REQUIRE(root->first_elem("sibiling")->prev()->name == "child"); } SECTION("Overload []") diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index 80bbab8..53004bd 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -24,11 +24,12 @@ TEST_CASE("Parsing tag", "parser") TEST_CASE("Parsing simple xml elements", "[parser]") { + using namespace myxml; SECTION("Basic") { std::string tooEasy = R"( )"; - auto elem = myxml::element_impl::parse(tooEasy); + auto elem = element_impl::parse(tooEasy); REQUIRE(elem->name == "root"); } @@ -37,11 +38,11 @@ TEST_CASE("Parsing simple xml elements", "[parser]") std::string withText = R"( Hello, world! )"; - auto elem = myxml::element_impl::parse(withText); + auto elem = element_impl::parse(withText); REQUIRE(elem->name == "root"); - REQUIRE(elem->FirstText()->str() == "\n "); - REQUIRE(elem->FirstElem()->name == "child"); - REQUIRE(elem->FirstElem()->FirstText()->str() == "Hello, world!"); + REQUIRE(elem->first()->str() == "\n "); + REQUIRE(elem->first()->name == "child"); + REQUIRE(elem->first()->first()->str() == "Hello, world!"); } SECTION("Nested") @@ -51,15 +52,15 @@ TEST_CASE("Parsing simple xml elements", "[parser]") )"; - auto elem = myxml::element_impl::parse(nested); + auto elem = element_impl::parse(nested); REQUIRE(elem->name == "root"); - REQUIRE(elem->FirstText()->str() == "\n "); - auto parent = elem->FirstElem(); + REQUIRE(elem->first()->str() == "\n "); + auto parent = elem->first(); REQUIRE(parent->name == "parent"); - REQUIRE(parent->FirstText()->str() == "\n "); - auto child = parent->FirstElem(); + REQUIRE(parent->first()->str() == "\n "); + auto child = parent->first(); REQUIRE(child->name == "child"); - REQUIRE(child->FirstChild() == nullptr); + REQUIRE(child->first_child() == nullptr); } SECTION("Mutli-Level") @@ -69,26 +70,26 @@ TEST_CASE("Parsing simple xml elements", "[parser]") Second Third )"; - auto elem = myxml::element_impl::parse(multiLevel); + auto elem = element_impl::parse(multiLevel); REQUIRE(elem->name == "root"); - REQUIRE(elem->FirstText()->str() == "\n "); + REQUIRE(elem->first()->str() == "\n "); - auto item1 = elem->FirstElem()->As(); + auto item1 = elem->first()->as(); REQUIRE(item1->name == "item"); - REQUIRE(item1->FirstText()->str() == "First"); + REQUIRE(item1->first()->str() == "First"); // 验证第二个 节点 - auto item2 = item1->NextElem(); + auto item2 = item1->next(); REQUIRE(item2->name == "item"); - REQUIRE(item2->FirstText()->str() == "Second"); + REQUIRE(item2->first()->str() == "Second"); // 验证第三个 节点 - auto item3 = item2->NextElem(); + auto item3 = item2->next(); REQUIRE(item3->name == "item"); - REQUIRE(item3->FirstText()->str() == "Third"); + REQUIRE(item3->first()->str() == "Third"); // 验证 root 节点的最后文本 - REQUIRE(item3->NextText()->str() == "\n"); + REQUIRE(item3->next()->str() == "\n"); } SECTION("Closed Element") @@ -96,17 +97,17 @@ TEST_CASE("Parsing simple xml elements", "[parser]") std::string closed = R"( )"; - auto elem = myxml::element_impl::parse(closed); + auto elem = element_impl::parse(closed); REQUIRE(elem->name == "root"); - REQUIRE(elem->FirstText()->str() == "\n "); + REQUIRE(elem->first()->str() == "\n "); // 验证 节点 - auto emptyElement = elem->FirstChild()->next->As(); + auto emptyElement = elem->first_child()->next(); REQUIRE(emptyElement->name == "empty"); - REQUIRE(emptyElement->FirstChild() == nullptr); // 自闭合标签没有子节点 + REQUIRE(emptyElement->first_child() == nullptr); // 自闭合标签没有子节点 // 验证 root 节点的最后文本 - REQUIRE(emptyElement->NextText()->str() == "\n"); + REQUIRE(emptyElement->next()->str() == "\n"); } SECTION("Mixed") @@ -115,23 +116,23 @@ TEST_CASE("Parsing simple xml elements", "[parser]") hello )"; - auto elem = myxml::element_impl::parse(mixed); + auto elem = element_impl::parse(mixed); REQUIRE(elem->name == "root"); - REQUIRE(elem->FirstText()->str() == "\n hello\n "); + REQUIRE(elem->first()->str() == "\n hello\n "); // 验证 节点 - auto child = elem->FirstChild()->next->As(); + auto child = elem->first_child()->next(); REQUIRE(child->name == "child"); - REQUIRE(child->FirstChild() == nullptr); // 是空的,没有文本子节点 + REQUIRE(child->first_child() == nullptr); // 是空的,没有文本子节点 // 验证 root 节点的最后文本 - REQUIRE(child->NextText()->str() == "\n"); + REQUIRE(child->next()->str() == "\n"); } SECTION("With attributes") { std::string attri = R"()"; - auto elem = myxml::element_impl::parse(attri); + auto elem = element_impl::parse(attri); REQUIRE(elem->name == "root"); REQUIRE(elem->attributes["hello"] == "world"); } @@ -139,7 +140,7 @@ TEST_CASE("Parsing simple xml elements", "[parser]") SECTION("Empty With Attributes") { std::string attri = R"()"; - auto elem = myxml::element_impl::parse(attri); + auto elem = element_impl::parse(attri); REQUIRE(elem->name == "root"); REQUIRE(elem->attributes["hello"] == "world"); } @@ -147,7 +148,7 @@ TEST_CASE("Parsing simple xml elements", "[parser]") SECTION("Multiple Attributes") { std::string multipleAttributes = R"()"; - auto elem = myxml::element_impl::parse(multipleAttributes); + auto elem = element_impl::parse(multipleAttributes); REQUIRE(elem->name == "root"); REQUIRE(elem->attributes["attr1"] == "value1"); @@ -158,7 +159,7 @@ TEST_CASE("Parsing simple xml elements", "[parser]") SECTION("Empty Attributes") { std::string emptyAttribute = R"()"; - auto elem = myxml::element_impl::parse(emptyAttribute); + auto elem = element_impl::parse(emptyAttribute); REQUIRE(elem->name == "root"); REQUIRE(elem->attributes["attr"] == ""); } @@ -166,18 +167,18 @@ TEST_CASE("Parsing simple xml elements", "[parser]") SECTION("Attributes And Children") { std::string attributesAndChildren = R"(Text)"; - auto elem = myxml::element_impl::parse(attributesAndChildren); + auto elem = element_impl::parse(attributesAndChildren); // 检查根元素的名称和属性 REQUIRE(elem->name == "root"); REQUIRE(elem->attributes["attr"] == "value"); // 检查根元素的第一个子元素 - auto child = elem->FirstElem(); + auto child = elem->first(); REQUIRE(child->name == "child"); // 检查子元素的文本内容 - REQUIRE(child->FirstText()->str() == "Text"); + REQUIRE(child->first()->str() == "Text"); } SECTION("Decoding entity") @@ -185,39 +186,39 @@ TEST_CASE("Parsing simple xml elements", "[parser]") std::string root = R"( <> )"; - auto elem = myxml::element_impl::parse(root); + auto elem = element_impl::parse(root); elem->entity_encoding(false); REQUIRE(elem->name == "root"); - REQUIRE(elem->FirstText()->str() == "\n <>\n"); + REQUIRE(elem->first()->str() == "\n <>\n"); elem->entity_encoding(true); - REQUIRE(elem->FirstText()->str() == "\n <>\n"); + REQUIRE(elem->first()->str() == "\n <>\n"); } SECTION("CDATA") { std::string cdata = ""; - auto elem = myxml::element_impl::parse(cdata); + auto elem = element_impl::parse(cdata); - REQUIRE(elem->FirstChild()->As()->str() == "\n"); + REQUIRE(elem->first_child()->as()->str() == "\n"); } SECTION("Newline Normalization") { std::string nl = "hello\r\n"; - auto elem = myxml::element_impl::parse(nl); + auto elem = element_impl::parse(nl); - REQUIRE(elem->FirstText()->str() == "hello\n"); + REQUIRE(elem->first()->str() == "hello\n"); nl = "hello\r"; - elem = myxml::element_impl::parse(nl); - REQUIRE(elem->FirstText()->str() == "hello\n"); + elem = element_impl::parse(nl); + REQUIRE(elem->first()->str() == "hello\n"); } SECTION("Simple File Buffer") { std::cout << std::filesystem::current_path() << std::endl; - auto doc = myxml::document::load("tests/data/example.xml"); + auto doc = document::load("tests/data/example.xml"); } } \ No newline at end of file From 0d0deb3dab009f65acad4d8789deafc4be9e2328 Mon Sep 17 00:00:00 2001 From: adamska <2639980868@qq.com> Date: Thu, 5 Sep 2024 15:00:57 +0800 Subject: [PATCH 6/9] feat(element.hpp): add first_cdata method --- docs/examples.md | 13 +++++++++ include/myxml/buffer.hpp | 8 +++--- include/myxml/cdata.hpp | 3 +- include/myxml/document.hpp | 4 +-- include/myxml/element.hpp | 6 ++-- include/myxml/node.hpp | 10 +++---- include/myxml/printable.hpp | 2 +- include/myxml/text.hpp | 5 ---- include/myxml/xmlfile.hpp | 19 ++++++------- src/buffer.cpp | 44 ++++++++++++++--------------- src/cdata.cpp | 6 ++-- src/document.cpp | 26 ++++++++--------- src/element.cpp | 21 ++++++++------ src/node.cpp | 10 +++---- src/parser.cpp | 8 +++--- src/printable.cpp | 4 +-- src/text.cpp | 8 +++--- src/xmlfile.cpp | 30 ++++++++++---------- tests/element_test.cpp | 20 ++++++------- tests/parser_test.cpp | 56 ++++++++++++++++++------------------- 20 files changed, 159 insertions(+), 144 deletions(-) diff --git a/docs/examples.md b/docs/examples.md index e26d8af..88785a5 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -76,3 +76,16 @@ fmt::println(txt.trimmed()); // yet it will modify. return a new text fmt::println(txt.trim()); ``` + +### CData + +```C++ +// similar to text +using namespace myxml; +// create from string +cdata txt = "Hello"; +// or from query +cdata txt = root.first_cdata(); +// print raw +fmt::println(txt); +``` diff --git a/include/myxml/buffer.hpp b/include/myxml/buffer.hpp index 9631799..20eeb05 100644 --- a/include/myxml/buffer.hpp +++ b/include/myxml/buffer.hpp @@ -14,9 +14,9 @@ namespace myxml class buffer { private: - std::size_t offset = 0; - std::size_t line = 0; - std::size_t column = 0; + std::size_t _offset = 0; + std::size_t _line = 0; + std::size_t _column = 0; // @returns {pointer to data, data length} virtual std::tuple base() const = 0; @@ -40,7 +40,7 @@ namespace myxml class string_buffer : public buffer { private: - std::variant inner; + std::variant _inner; std::string_view view() const; /** Implement Buffer */ diff --git a/include/myxml/cdata.hpp b/include/myxml/cdata.hpp index 4e635e4..a2fcab4 100644 --- a/include/myxml/cdata.hpp +++ b/include/myxml/cdata.hpp @@ -8,6 +8,7 @@ namespace myxml class cdata : public printable { + friend class element; private: std::shared_ptr _impl; @@ -25,7 +26,7 @@ namespace myxml class cdata_impl : public node { public: - std::string inner; + std::string _inner; cdata_impl() {}; explicit cdata_impl(std::string_view); diff --git a/include/myxml/document.hpp b/include/myxml/document.hpp index 3c0d667..f991c5c 100644 --- a/include/myxml/document.hpp +++ b/include/myxml/document.hpp @@ -27,8 +27,8 @@ namespace myxml class document : public printable { private: - declaration decl; - element root; + declaration _decl; + element _root; public: /* Manipulate */ diff --git a/include/myxml/element.hpp b/include/myxml/element.hpp index 936d751..c49a72a 100644 --- a/include/myxml/element.hpp +++ b/include/myxml/element.hpp @@ -5,6 +5,7 @@ #include #include "myxml/text.hpp" +#include "myxml/cdata.hpp" #include "myxml/printable.hpp" namespace myxml @@ -46,6 +47,7 @@ namespace myxml element first_elem(); element first_elem(std::string_view); text first_text(); + cdata first_cdata(); /* Implement printable */ virtual void print(std::ostream &) const override; @@ -56,8 +58,8 @@ namespace myxml struct element_impl : public composite_node // public std::enable_shared_from_this, public Node { public: - std::string name; - std::map> attributes; + std::string _name; + std::map> _attributes; /* Set initializer as private to avoid using Element without share_ptr*/ diff --git a/include/myxml/node.hpp b/include/myxml/node.hpp index ea24b65..36a48ca 100644 --- a/include/myxml/node.hpp +++ b/include/myxml/node.hpp @@ -18,6 +18,11 @@ namespace myxml class node : public std::enable_shared_from_this, public printable { public: + virtual ~node() = default; + std::shared_ptr _parent; + std::shared_ptr _prev; + std::shared_ptr _next; + template >> std::shared_ptr next() { @@ -44,11 +49,6 @@ namespace myxml return nullptr; } - virtual ~node() = default; - std::shared_ptr _parent; - std::shared_ptr _prev; - std::shared_ptr _next; - template >> std::shared_ptr as() { diff --git a/include/myxml/printable.hpp b/include/myxml/printable.hpp index 72b9579..4c3354a 100644 --- a/include/myxml/printable.hpp +++ b/include/myxml/printable.hpp @@ -24,7 +24,7 @@ namespace myxml class printable { protected: - print_config config; + print_config _config; public: virtual ~printable() = default; diff --git a/include/myxml/text.hpp b/include/myxml/text.hpp index a5f20f2..0c79ef2 100644 --- a/include/myxml/text.hpp +++ b/include/myxml/text.hpp @@ -33,12 +33,7 @@ namespace myxml virtual ~text_impl() = default; - // may used in Export - bool IsAllSpace() const; - /* Implment Exportable*/ - // virtual std::string ExportRaw() const override; - // virtual std::string ExportFormatted(int indentLevel = 0, int indentSize = 4) const override; virtual void print(std::ostream &) const override; }; diff --git a/include/myxml/xmlfile.hpp b/include/myxml/xmlfile.hpp index 31dca07..e6fdc9d 100644 --- a/include/myxml/xmlfile.hpp +++ b/include/myxml/xmlfile.hpp @@ -6,26 +6,25 @@ namespace myxml { - class XMLFile : public buffer + class xml_file : public buffer { private: - XMLFile(); + xml_file(); /* mmap related*/ - int fd; - std::size_t fileSize; - char *inner; - - std::size_t offset; + int _fd; + std::size_t _size; + char *_mapped; + std::size_t _offset; /* Implement buffer*/ virtual std::tuple base() const; public: - static std::shared_ptr Open(std::string_view fpath); + static std::shared_ptr open(std::string_view fpath); // RAII - ~XMLFile(); + ~xml_file(); // always copy shared_ptr instead of XMLFile - XMLFile(const XMLFile &) = delete; + xml_file(const xml_file &) = delete; }; } \ No newline at end of file diff --git a/src/buffer.cpp b/src/buffer.cpp index 3a51084..9768b60 100644 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -6,12 +6,12 @@ namespace myxml { if (ch == '\n') { - this->column = 0; - this->line++; + this->_column = 0; + this->_line++; } else { - this->column++; + this->_column++; } } @@ -26,51 +26,51 @@ namespace myxml std::optional buffer::peek() const { auto [ptr, len] = this->base(); - if (this->offset >= len) + if (this->_offset >= len) { return std::nullopt; } - return ptr[this->offset]; + return ptr[this->_offset]; } std::optional buffer::peek_n(int n) const { auto [ptr, len] = this->base(); - if (this->offset >= len) + if (this->_offset >= len) { return std::nullopt; } - return std::string_view(ptr + this->offset, n); + return std::string_view(ptr + this->_offset, n); } std::optional buffer::after_n(int n) const { auto [ptr, len] = this->base(); - if (this->offset + n > len) + if (this->_offset + n > len) { return std::nullopt; } - return ptr[this->offset + n]; + return ptr[this->_offset + n]; } std::optional buffer::after_n_m(int n, int m) const { auto [ptr, len] = this->base(); - if (this->offset + n + m > len) + if (this->_offset + n + m > len) { return std::nullopt; } - return std::string_view(ptr + this->offset + n, m); + return std::string_view(ptr + this->_offset + n, m); } std::optional buffer::take() { auto [ptr, len] = this->base(); - if (this->offset >= len) + if (this->_offset >= len) { return std::nullopt; } - auto ch = ptr[this->offset++]; + auto ch = ptr[this->_offset++]; this->update_loc(ch); return ch; } @@ -78,28 +78,28 @@ namespace myxml std::optional buffer::take_n(int n) { auto [ptr, len] = this->base(); - if (offset + n >= len) + if (_offset + n >= len) { return std::nullopt; } - std::string_view strv(ptr + this->offset, n); + std::string_view strv(ptr + this->_offset, n); this->update_loc(strv); - offset += n; + _offset += n; return strv; } std::tuple buffer::cur_loc() { - return {this->line, this->column}; + return {this->_line, this->_column}; } string_buffer::string_buffer(std::string_view inner) - : inner(inner) + : _inner(inner) { } string_buffer::string_buffer(std::string &&inner) - : inner(inner) + : _inner(inner) { } @@ -116,13 +116,13 @@ namespace myxml std::string_view string_buffer::view() const { - if (std::holds_alternative(this->inner)) + if (std::holds_alternative(this->_inner)) { - return std::string_view(std::get(this->inner)); + return std::string_view(std::get(this->_inner)); } else { - return std::get(this->inner); + return std::get(this->_inner); } } } diff --git a/src/cdata.cpp b/src/cdata.cpp index f79542c..f8d417a 100644 --- a/src/cdata.cpp +++ b/src/cdata.cpp @@ -25,17 +25,17 @@ namespace myxml } cdata_impl::cdata_impl(std::string_view str) - : inner(str) + : _inner(str) { } cdata_impl::cdata_impl(std::string &&str) - : inner(str) + : _inner(str) { } void cdata_impl::print(std::ostream &os) const { - os << "inner << "]]>\n"; + os << "_inner << "]]>\n"; } } diff --git a/src/document.cpp b/src/document.cpp index 519e04d..1bfc4a2 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -9,47 +9,47 @@ namespace myxml { void document::set_declaration(const declaration &decl) { - this->decl = decl; + this->_decl = decl; } void document::set_root(std::shared_ptr root) { - this->root = root; + this->_root = root; } const declaration &document::get_declaration() const { - return this->decl; + return this->_decl; } declaration &document::get_declaration() { - return this->decl; + return this->_decl; } const element &document::get_root() const { - return this->root; + return this->_root; } element &document::get_root() { - return this->root; + return this->_root; } element document::first_elem(std::string_view name) { - return this->root.first_elem(name); + return this->_root.first_elem(name); } element document::first_elem() { - return this->root.first_elem(); + return this->_root.first_elem(); } text document::first_text() { - return this->root.first_text(); + return this->_root.first_text(); } document document::parse(std::string_view input) @@ -59,7 +59,7 @@ namespace myxml document document::load(std::string fileName) { - auto f = XMLFile::Open(fileName); + auto f = xml_file::open(fileName); return parser(f).parse_document(); } // std::string document::ExportRaw() const @@ -69,7 +69,7 @@ namespace myxml void document::print(std::ostream &os) const { - os << this->decl << this->root; + os << this->_decl << this->_root; } // std::string document::ExportFormatted(int indentLevel, int indentSize) const // { @@ -78,12 +78,12 @@ namespace myxml void document::entity_encoding(bool flag) { - this->root.entity_encoding(flag); + this->_root.entity_encoding(flag); } void document::platform_specific_newline(bool flag) { - this->root.platform_specific_newline(flag); + this->_root.platform_specific_newline(flag); } std::optional declaration::from_attrs(std::map attrs) diff --git a/src/element.cpp b/src/element.cpp index b33e45c..c573fdb 100644 --- a/src/element.cpp +++ b/src/element.cpp @@ -17,7 +17,7 @@ namespace myxml std::string_view element::name() { - return this->_impl->name; + return this->_impl->_name; } element element::parse(std::string_view xml) @@ -50,6 +50,11 @@ namespace myxml return _impl->first(); } + cdata element::first_cdata() + { + return _impl->first(); + } + void element::print(std::ostream &os) const { _impl->print(os); @@ -66,7 +71,7 @@ namespace myxml } element_impl::element_impl(std::string_view name) - : name(name) {} + : _name(name) {} std::shared_ptr element_impl::_new(std::string_view name) { @@ -85,18 +90,18 @@ namespace myxml std::shared_ptr element_impl::load(std::string_view path) { - auto f = XMLFile::Open(path); + auto f = xml_file::open(path); return parser(f).parse_element(); } void element_impl::extend_attributes(std::map attris) { - this->attributes.insert(attris.begin(), attris.end()); + this->_attributes.insert(attris.begin(), attris.end()); } std::string &element_impl::operator[](const std::string &key) { - return this->attributes[key]; + return this->_attributes[key]; } // std::string element_impl::ExportRaw() const @@ -123,8 +128,8 @@ namespace myxml void element_impl::print(std::ostream &os) const { - os << "<" << this->name; - for (const auto &[key, value] : this->attributes) + os << "<" << this->_name; + for (const auto &[key, value] : this->_attributes) { os << "" << key << "=\"" << value << "\""; } @@ -138,7 +143,7 @@ namespace myxml { node->print(os); } - os << "name << ">"; + os << "_name << ">"; } namespace literals diff --git a/src/node.cpp b/src/node.cpp index c3d69ba..5aa1903 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -6,12 +6,12 @@ namespace myxml { void node::entity_encoding(bool flag) { - this->config.entity_encoding = flag; + this->_config.entity_encoding = flag; } void node::platform_specific_newline(bool flag) { - this->config.platform_specific_newline = flag; + this->_config.platform_specific_newline = flag; } std::shared_ptr node::next_sibiling() @@ -60,7 +60,7 @@ namespace myxml } for (auto child = this->firstChild; child != nullptr; child = child->_next) { - if (auto elem = child->as(); elem && elem->name == name) + if (auto elem = child->as(); elem && elem->_name == name) { this->nameToElemBuffer.emplace(name, elem); return elem; @@ -140,7 +140,7 @@ namespace myxml void composite_node::entity_encoding(bool flag) { - this->config.entity_encoding = flag; + this->_config.entity_encoding = flag; for (auto it = this->first_child(); it != nullptr; it = it->_next) { it->entity_encoding(flag); @@ -149,7 +149,7 @@ namespace myxml void composite_node::platform_specific_newline(bool flag) { - this->config.platform_specific_newline = flag; + this->_config.platform_specific_newline = flag; for (auto it = this->first_child(); it != nullptr; it = it->_next) { it->platform_specific_newline(flag); diff --git a/src/parser.cpp b/src/parser.cpp index af8d028..4d765ec 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -209,7 +209,7 @@ namespace myxml std::shared_ptr parser::parse_element_with_header(element_tag header) { auto elem = element_impl::_new(); - elem->name = header.name; + elem->_name = header.name; while (auto ch = this->peek()) { switch (*ch) @@ -234,7 +234,7 @@ namespace myxml case element_tag::closing_type::Closed: { auto child = element_impl::_new(); - child->name = tag->name; + child->_name = tag->name; if (!tag->attrs.empty()) { child->extend_attributes(tag->attrs); @@ -243,7 +243,7 @@ namespace myxml break; } case element_tag::closing_type::Closing: - if (tag->name != elem->name) + if (tag->name != elem->_name) { auto [line, col] = this->cur_loc(); throw syntax_error(fmt::format("elem name in closing tag is mismatched with the header"), line, col); @@ -276,7 +276,7 @@ namespace myxml if (tag->type == element_tag::closing_type::Closed) { auto elem = element_impl::_new(); - elem->name = tag->name; + elem->_name = tag->name; if (!tag->attrs.empty()) { elem->extend_attributes(tag->attrs); diff --git a/src/printable.cpp b/src/printable.cpp index 23350a6..d4c58d7 100644 --- a/src/printable.cpp +++ b/src/printable.cpp @@ -18,12 +18,12 @@ namespace myxml void printable::entity_encoding(bool flag) { - this->config.entity_encoding = flag; + this->_config.entity_encoding = flag; } void printable::platform_specific_newline(bool flag) { - this->config.platform_specific_newline = flag; + this->_config.platform_specific_newline = flag; } std::string printable::str() diff --git a/src/text.cpp b/src/text.cpp index 37b5a39..8bdf709 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -38,7 +38,7 @@ namespace myxml text_impl::text_impl(std::string_view input) { - if (config.entity_encoding) + if (_config.entity_encoding) { // entity encoding static std::map> entityMap = { @@ -95,7 +95,7 @@ namespace myxml void text_impl::print(std::ostream &os) const { - if (!this->config.entity_encoding && !this->config.platform_specific_newline) + if (!this->_config.entity_encoding && !this->_config.platform_specific_newline) { os << this->inner; } @@ -112,7 +112,7 @@ namespace myxml std::size_t len = this->inner.length(); for (std::size_t i = 0; i < len; i++) { - if (this->config.entity_encoding) + if (this->_config.entity_encoding) { if (auto it = entityMap.find(this->inner[i]); it != entityMap.end()) { @@ -121,7 +121,7 @@ namespace myxml start = i + 1; } } - if (this->config.platform_specific_newline) + if (this->_config.platform_specific_newline) { if (this->inner[i] == '\n') { diff --git a/src/xmlfile.cpp b/src/xmlfile.cpp index 207635e..9467414 100644 --- a/src/xmlfile.cpp +++ b/src/xmlfile.cpp @@ -10,43 +10,43 @@ namespace myxml { - XMLFile::XMLFile() - : offset(0) + xml_file::xml_file() + : _offset(0) { } - std::shared_ptr XMLFile::Open(std::string_view fpath) + std::shared_ptr xml_file::open(std::string_view fpath) { // can't use make_shared because XMLFile() is private - auto xfile = std::shared_ptr(new XMLFile()); - xfile->fd = open(fpath.data(), O_RDONLY); - if (xfile->fd == -1) + auto xfile = std::shared_ptr(new xml_file()); + xfile->_fd = open(fpath.data(), O_RDONLY); + if (xfile->_fd == -1) { throw io_error(fmt::format("failed to open file: {}", fpath)); } struct stat fileInfo; - if (fstat(xfile->fd, &fileInfo) == -1) + if (fstat(xfile->_fd, &fileInfo) == -1) { throw io_error(fmt::format("failed to get info of file: {}", fpath)); } - xfile->fileSize = fileInfo.st_size; - void *mappedRegion = mmap(nullptr, xfile->fileSize, PROT_READ, MAP_PRIVATE, xfile->fd, 0); + xfile->_size = fileInfo.st_size; + void *mappedRegion = mmap(nullptr, xfile->_size, PROT_READ, MAP_PRIVATE, xfile->_fd, 0); if (mappedRegion == MAP_FAILED) { throw io_error(fmt::format("failed to map memory for file: {}", fpath)); } - xfile->inner = static_cast(mappedRegion); + xfile->_mapped = static_cast(mappedRegion); return xfile; } - XMLFile::~XMLFile() + xml_file::~xml_file() { - close(this->fd); - munmap(static_cast(this->inner), this->fileSize); + close(this->_fd); + munmap(static_cast(this->_mapped), this->_size); } - std::tuple XMLFile::base() const + std::tuple xml_file::base() const { - return {this->inner, this->fileSize}; + return {this->_mapped, this->_size}; } } \ No newline at end of file diff --git a/tests/element_test.cpp b/tests/element_test.cpp index f5686d7..b0c7e2d 100644 --- a/tests/element_test.cpp +++ b/tests/element_test.cpp @@ -11,39 +11,39 @@ TEST_CASE("Element Impl", "[element]") SECTION("GetName") { - REQUIRE(root->name == "root"); + REQUIRE(root->_name == "root"); } SECTION("Basic Insertion") { root->push_front(child); - REQUIRE(root->first()->name == "child"); - REQUIRE(root->last_child()->as()->name == "child"); + REQUIRE(root->first()->_name == "child"); + REQUIRE(root->last_child()->as()->_name == "child"); } SECTION("Get child by name after insert it") { root->push_front(child); // Unbuffered - REQUIRE(root->first_elem("child")->name == "child"); + REQUIRE(root->first_elem("child")->_name == "child"); // Buffered - REQUIRE(root->first_elem("child")->name == "child"); + REQUIRE(root->first_elem("child")->_name == "child"); } SECTION("Multi child") { root->push_back(child); root->push_back(sibiling); - REQUIRE(root->first_elem("child")->name == "child"); - REQUIRE(root->first_elem("child")->next()->name == "sibiling"); - REQUIRE(root->first_elem("sibiling")->prev()->name == "child"); + REQUIRE(root->first_elem("child")->_name == "child"); + REQUIRE(root->first_elem("child")->next()->_name == "sibiling"); + REQUIRE(root->first_elem("sibiling")->prev()->_name == "child"); } SECTION("Overload []") { - root->attributes["hello"] = "world"; + root->_attributes["hello"] = "world"; REQUIRE((*root)["hello"] == "world"); - REQUIRE(root->attributes["hello"] == "world"); + REQUIRE(root->_attributes["hello"] == "world"); (*root)["hello"] = "bar"; REQUIRE((*root)["hello"] == "bar"); } diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index 53004bd..92b324c 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -30,7 +30,7 @@ TEST_CASE("Parsing simple xml elements", "[parser]") std::string tooEasy = R"( )"; auto elem = element_impl::parse(tooEasy); - REQUIRE(elem->name == "root"); + REQUIRE(elem->_name == "root"); } SECTION("Text") @@ -39,9 +39,9 @@ TEST_CASE("Parsing simple xml elements", "[parser]") Hello, world! )"; auto elem = element_impl::parse(withText); - REQUIRE(elem->name == "root"); + REQUIRE(elem->_name == "root"); REQUIRE(elem->first()->str() == "\n "); - REQUIRE(elem->first()->name == "child"); + REQUIRE(elem->first()->_name == "child"); REQUIRE(elem->first()->first()->str() == "Hello, world!"); } @@ -53,13 +53,13 @@ TEST_CASE("Parsing simple xml elements", "[parser]") )"; auto elem = element_impl::parse(nested); - REQUIRE(elem->name == "root"); + REQUIRE(elem->_name == "root"); REQUIRE(elem->first()->str() == "\n "); auto parent = elem->first(); - REQUIRE(parent->name == "parent"); + REQUIRE(parent->_name == "parent"); REQUIRE(parent->first()->str() == "\n "); auto child = parent->first(); - REQUIRE(child->name == "child"); + REQUIRE(child->_name == "child"); REQUIRE(child->first_child() == nullptr); } @@ -71,21 +71,21 @@ TEST_CASE("Parsing simple xml elements", "[parser]") Third )"; auto elem = element_impl::parse(multiLevel); - REQUIRE(elem->name == "root"); + REQUIRE(elem->_name == "root"); REQUIRE(elem->first()->str() == "\n "); auto item1 = elem->first()->as(); - REQUIRE(item1->name == "item"); + REQUIRE(item1->_name == "item"); REQUIRE(item1->first()->str() == "First"); // 验证第二个 节点 auto item2 = item1->next(); - REQUIRE(item2->name == "item"); + REQUIRE(item2->_name == "item"); REQUIRE(item2->first()->str() == "Second"); // 验证第三个 节点 auto item3 = item2->next(); - REQUIRE(item3->name == "item"); + REQUIRE(item3->_name == "item"); REQUIRE(item3->first()->str() == "Third"); // 验证 root 节点的最后文本 @@ -98,12 +98,12 @@ TEST_CASE("Parsing simple xml elements", "[parser]") )"; auto elem = element_impl::parse(closed); - REQUIRE(elem->name == "root"); + REQUIRE(elem->_name == "root"); REQUIRE(elem->first()->str() == "\n "); // 验证 节点 auto emptyElement = elem->first_child()->next(); - REQUIRE(emptyElement->name == "empty"); + REQUIRE(emptyElement->_name == "empty"); REQUIRE(emptyElement->first_child() == nullptr); // 自闭合标签没有子节点 // 验证 root 节点的最后文本 @@ -117,12 +117,12 @@ TEST_CASE("Parsing simple xml elements", "[parser]") )"; auto elem = element_impl::parse(mixed); - REQUIRE(elem->name == "root"); + REQUIRE(elem->_name == "root"); REQUIRE(elem->first()->str() == "\n hello\n "); // 验证 节点 auto child = elem->first_child()->next(); - REQUIRE(child->name == "child"); + REQUIRE(child->_name == "child"); REQUIRE(child->first_child() == nullptr); // 是空的,没有文本子节点 // 验证 root 节点的最后文本 @@ -133,16 +133,16 @@ TEST_CASE("Parsing simple xml elements", "[parser]") { std::string attri = R"()"; auto elem = element_impl::parse(attri); - REQUIRE(elem->name == "root"); - REQUIRE(elem->attributes["hello"] == "world"); + REQUIRE(elem->_name == "root"); + REQUIRE(elem->_attributes["hello"] == "world"); } SECTION("Empty With Attributes") { std::string attri = R"()"; auto elem = element_impl::parse(attri); - REQUIRE(elem->name == "root"); - REQUIRE(elem->attributes["hello"] == "world"); + REQUIRE(elem->_name == "root"); + REQUIRE(elem->_attributes["hello"] == "world"); } SECTION("Multiple Attributes") @@ -150,18 +150,18 @@ TEST_CASE("Parsing simple xml elements", "[parser]") std::string multipleAttributes = R"()"; auto elem = element_impl::parse(multipleAttributes); - REQUIRE(elem->name == "root"); - REQUIRE(elem->attributes["attr1"] == "value1"); - REQUIRE(elem->attributes["attr2"] == "value2"); - REQUIRE(elem->attributes["attr3"] == "value3"); + REQUIRE(elem->_name == "root"); + REQUIRE(elem->_attributes["attr1"] == "value1"); + REQUIRE(elem->_attributes["attr2"] == "value2"); + REQUIRE(elem->_attributes["attr3"] == "value3"); } SECTION("Empty Attributes") { std::string emptyAttribute = R"()"; auto elem = element_impl::parse(emptyAttribute); - REQUIRE(elem->name == "root"); - REQUIRE(elem->attributes["attr"] == ""); + REQUIRE(elem->_name == "root"); + REQUIRE(elem->_attributes["attr"] == ""); } SECTION("Attributes And Children") @@ -170,12 +170,12 @@ TEST_CASE("Parsing simple xml elements", "[parser]") auto elem = element_impl::parse(attributesAndChildren); // 检查根元素的名称和属性 - REQUIRE(elem->name == "root"); - REQUIRE(elem->attributes["attr"] == "value"); + REQUIRE(elem->_name == "root"); + REQUIRE(elem->_attributes["attr"] == "value"); // 检查根元素的第一个子元素 auto child = elem->first(); - REQUIRE(child->name == "child"); + REQUIRE(child->_name == "child"); // 检查子元素的文本内容 REQUIRE(child->first()->str() == "Text"); @@ -189,7 +189,7 @@ TEST_CASE("Parsing simple xml elements", "[parser]") auto elem = element_impl::parse(root); elem->entity_encoding(false); - REQUIRE(elem->name == "root"); + REQUIRE(elem->_name == "root"); REQUIRE(elem->first()->str() == "\n <>\n"); elem->entity_encoding(true); From d386cce14d6bebd7c2099aaa7fd369bb858f87ff Mon Sep 17 00:00:00 2001 From: adamska <2639980868@qq.com> Date: Thu, 5 Sep 2024 15:05:34 +0800 Subject: [PATCH 7/9] fix: --- include/myxml/xmlfile.hpp | 2 +- src/buffer.cpp | 36 ++++++++++++++++++------------------ src/cdata.cpp | 2 +- src/document.cpp | 2 +- src/element.cpp | 2 +- src/text.cpp | 5 ----- src/xmlfile.cpp | 2 +- 7 files changed, 23 insertions(+), 28 deletions(-) diff --git a/include/myxml/xmlfile.hpp b/include/myxml/xmlfile.hpp index e6fdc9d..adec415 100644 --- a/include/myxml/xmlfile.hpp +++ b/include/myxml/xmlfile.hpp @@ -21,7 +21,7 @@ namespace myxml virtual std::tuple base() const; public: - static std::shared_ptr open(std::string_view fpath); + static std::shared_ptr open_file(std::string_view fpath); // RAII ~xml_file(); // always copy shared_ptr instead of XMLFile diff --git a/src/buffer.cpp b/src/buffer.cpp index 9768b60..02c146c 100644 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -6,12 +6,12 @@ namespace myxml { if (ch == '\n') { - this->_column = 0; - this->_line++; + _column = 0; + _line++; } else { - this->_column++; + _column++; } } @@ -26,51 +26,51 @@ namespace myxml std::optional buffer::peek() const { auto [ptr, len] = this->base(); - if (this->_offset >= len) + if (_offset >= len) { return std::nullopt; } - return ptr[this->_offset]; + return ptr[_offset]; } std::optional buffer::peek_n(int n) const { auto [ptr, len] = this->base(); - if (this->_offset >= len) + if (_offset >= len) { return std::nullopt; } - return std::string_view(ptr + this->_offset, n); + return std::string_view(ptr + _offset, n); } std::optional buffer::after_n(int n) const { auto [ptr, len] = this->base(); - if (this->_offset + n > len) + if (_offset + n > len) { return std::nullopt; } - return ptr[this->_offset + n]; + return ptr[_offset + n]; } std::optional buffer::after_n_m(int n, int m) const { auto [ptr, len] = this->base(); - if (this->_offset + n + m > len) + if (_offset + n + m > len) { return std::nullopt; } - return std::string_view(ptr + this->_offset + n, m); + return std::string_view(ptr + _offset + n, m); } std::optional buffer::take() { auto [ptr, len] = this->base(); - if (this->_offset >= len) + if (_offset >= len) { return std::nullopt; } - auto ch = ptr[this->_offset++]; + auto ch = ptr[_offset++]; this->update_loc(ch); return ch; } @@ -82,7 +82,7 @@ namespace myxml { return std::nullopt; } - std::string_view strv(ptr + this->_offset, n); + std::string_view strv(ptr + _offset, n); this->update_loc(strv); _offset += n; return strv; @@ -90,7 +90,7 @@ namespace myxml std::tuple buffer::cur_loc() { - return {this->_line, this->_column}; + return {_line, _column}; } string_buffer::string_buffer(std::string_view inner) @@ -116,13 +116,13 @@ namespace myxml std::string_view string_buffer::view() const { - if (std::holds_alternative(this->_inner)) + if (std::holds_alternative(_inner)) { - return std::string_view(std::get(this->_inner)); + return std::string_view(std::get(_inner)); } else { - return std::get(this->_inner); + return std::get(_inner); } } } diff --git a/src/cdata.cpp b/src/cdata.cpp index f8d417a..896e686 100644 --- a/src/cdata.cpp +++ b/src/cdata.cpp @@ -36,6 +36,6 @@ namespace myxml void cdata_impl::print(std::ostream &os) const { - os << "_inner << "]]>\n"; + os << "\n"; } } diff --git a/src/document.cpp b/src/document.cpp index 1bfc4a2..1bbbd49 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -59,7 +59,7 @@ namespace myxml document document::load(std::string fileName) { - auto f = xml_file::open(fileName); + auto f = xml_file::open_file(fileName); return parser(f).parse_document(); } // std::string document::ExportRaw() const diff --git a/src/element.cpp b/src/element.cpp index c573fdb..5bfacbc 100644 --- a/src/element.cpp +++ b/src/element.cpp @@ -90,7 +90,7 @@ namespace myxml std::shared_ptr element_impl::load(std::string_view path) { - auto f = xml_file::open(path); + auto f = xml_file::open_file(path); return parser(f).parse_element(); } diff --git a/src/text.cpp b/src/text.cpp index 8bdf709..f053e26 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -88,11 +88,6 @@ namespace myxml } } - bool text_impl::IsAllSpace() const - { - return std::all_of(this->inner.begin(), this->inner.end(), isspace); - } - void text_impl::print(std::ostream &os) const { if (!this->_config.entity_encoding && !this->_config.platform_specific_newline) diff --git a/src/xmlfile.cpp b/src/xmlfile.cpp index 9467414..27f18c0 100644 --- a/src/xmlfile.cpp +++ b/src/xmlfile.cpp @@ -15,7 +15,7 @@ namespace myxml { } - std::shared_ptr xml_file::open(std::string_view fpath) + std::shared_ptr xml_file::open_file(std::string_view fpath) { // can't use make_shared because XMLFile() is private auto xfile = std::shared_ptr(new xml_file()); From e02354205488cd53b0afa40d6c336d3651a507f2 Mon Sep 17 00:00:00 2001 From: adamska <2639980868@qq.com> Date: Thu, 5 Sep 2024 15:31:37 +0800 Subject: [PATCH 8/9] test: doc_test --- docs/examples.md | 8 ++++---- include/myxml/document.hpp | 4 +--- src/document.cpp | 12 +----------- tests/CMakeLists.txt | 35 +++++++++++++---------------------- tests/doc_test.cpp | 23 +++++++++++++++++++++++ tests/document_test.cpp | 9 +++++---- tests/parser_test.cpp | 1 - 7 files changed, 47 insertions(+), 45 deletions(-) create mode 100644 tests/doc_test.cpp diff --git a/docs/examples.md b/docs/examples.md index 88785a5..ffb0d0a 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -9,13 +9,13 @@ using namespace myxml; // `std::string xml` document doc = document::parse(xml); // or -document doc = document::load(path); +doc = document::load(path); // get root elem -optional elem = doc.root().first_elem(); +std::optional elem = doc.root().first_elem(); // or directly query elem in root -optional elem = doc.first_elem(); +elem = doc.first_elem(); // or query by name -optional elem = doc.first_elem("elem"); +elem = doc.first_elem("elem"); ``` ### Element diff --git a/include/myxml/document.hpp b/include/myxml/document.hpp index f991c5c..a39961e 100644 --- a/include/myxml/document.hpp +++ b/include/myxml/document.hpp @@ -36,10 +36,8 @@ namespace myxml void set_root(std::shared_ptr root); /* Query */ - const declaration &get_declaration() const; declaration &get_declaration(); - const element &get_root() const; - element &get_root(); + element &root(); element first_elem(std::string_view); element first_elem(); text first_text(); diff --git a/src/document.cpp b/src/document.cpp index 1bbbd49..7608e63 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -17,22 +17,12 @@ namespace myxml this->_root = root; } - const declaration &document::get_declaration() const - { - return this->_decl; - } - declaration &document::get_declaration() { return this->_decl; } - const element &document::get_root() const - { - return this->_root; - } - - element &document::get_root() + element &document::root() { return this->_root; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 39e76f6..92a0883 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,25 +1,16 @@ include(CTest) -add_executable(element_test element_test.cpp) -add_executable(parser_test parser_test.cpp) -add_executable(printable_test printable_test.cpp) -add_executable(document_test document_test.cpp) -add_executable(buffer_test buffer_test.cpp) +function(add_custom_test test_name) + add_executable(${test_name} ${test_name}.cpp) + target_link_libraries(${test_name} Catch2::Catch2WithMain myxml) + target_compile_features(${test_name} PRIVATE cxx_std_17) + target_compile_features(${test_name} PRIVATE cxx_std_17) + add_test(NAME ${test_name} COMMAND ${test_name} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) +endfunction() -target_link_libraries(element_test Catch2::Catch2WithMain myxml) -target_link_libraries(parser_test Catch2::Catch2WithMain myxml) -target_link_libraries(printable_test Catch2::Catch2WithMain myxml) -target_link_libraries(document_test Catch2::Catch2WithMain myxml) -target_link_libraries(buffer_test Catch2::Catch2WithMain myxml) - -target_compile_features(element_test PRIVATE cxx_std_17) -target_compile_features(parser_test PRIVATE cxx_std_17) -target_compile_features(printable_test PRIVATE cxx_std_17) -target_compile_features(document_test PRIVATE cxx_std_17) -target_compile_features(buffer_test PRIVATE cxx_std_17) - -add_test(NAME element_test COMMAND element_test) -add_test(NAME parser_test COMMAND parser_test WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) -add_test(NAME printable_test COMMAND printable_test) -add_test(NAME document_test COMMAND document_test) -add_test(NAME buffer_test COMMAND buffer_test) +add_custom_test(element_test) +add_custom_test(parser_test) +add_custom_test(printable_test) +add_custom_test(document_test) +add_custom_test(buffer_test) +add_custom_test(doc_test) diff --git a/tests/doc_test.cpp b/tests/doc_test.cpp new file mode 100644 index 0000000..f9ace25 --- /dev/null +++ b/tests/doc_test.cpp @@ -0,0 +1,23 @@ +// All example codes in document +#include +#include +#include "myxml/document.hpp" +#include "myxml/element.hpp" + +TEST_CASE("docs/examples", "[doc]") +{ + SECTION("1") + { + using namespace myxml; + std::string xml = ""; + document doc = document::parse(xml); + std::string path = "tests/data/example.xml"; + doc = document::load(path); + // get root elem + std::optional elem = doc.root().first_elem(); + // or directly query elem in root + elem = doc.first_elem(); + // or query by name + elem = doc.first_elem("elem"); + } +} \ No newline at end of file diff --git a/tests/document_test.cpp b/tests/document_test.cpp index 88b4838..e1316fb 100644 --- a/tests/document_test.cpp +++ b/tests/document_test.cpp @@ -3,13 +3,14 @@ TEST_CASE("Simple document", "[document]") { + using namespace myxml; SECTION("No decl") { std::string input = R"( Value )"; - auto doc = myxml::document::parse(input); - REQUIRE(doc.get_root().name() == "root"); + auto doc = document::parse(input); + REQUIRE(doc.root().name() == "root"); REQUIRE(doc.first_elem("child").name() == "child"); REQUIRE(doc.first_elem("child").first_text().str() == "Value"); } @@ -21,7 +22,7 @@ TEST_CASE("Simple document", "[document]") Value )"; - auto doc = myxml::document::parse(input); + auto doc = document::parse(input); REQUIRE(doc.get_declaration().version == "1.0"); REQUIRE(doc.get_declaration().encoding == "UTF-8"); } @@ -31,5 +32,5 @@ TEST_CASE("Custom String Literal", "[document]") { using namespace myxml::literals; auto doc = ""_doc; - REQUIRE(doc.get_root().name() == "root"); + REQUIRE(doc.root().name() == "root"); } \ No newline at end of file diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index 92b324c..320ae73 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -218,7 +218,6 @@ TEST_CASE("Parsing simple xml elements", "[parser]") SECTION("Simple File Buffer") { - std::cout << std::filesystem::current_path() << std::endl; auto doc = document::load("tests/data/example.xml"); } } \ No newline at end of file From a5fbd533e12f8c816581094f72725d4fb2a554b2 Mon Sep 17 00:00:00 2001 From: adamska <2639980868@qq.com> Date: Thu, 5 Sep 2024 16:00:27 +0800 Subject: [PATCH 9/9] fix: --- Testing/Temporary/CTestCostData.txt | 1 + Testing/Temporary/LastTest.log | 3 +++ tests/CMakeLists.txt | 1 - tests/data/{example.xml => example1.xml} | 0 tests/doc_test.cpp | 23 ----------------------- tests/parser_test.cpp | 3 ++- tests/printable_test.cpp | 15 --------------- 7 files changed, 6 insertions(+), 40 deletions(-) create mode 100644 Testing/Temporary/CTestCostData.txt create mode 100644 Testing/Temporary/LastTest.log rename tests/data/{example.xml => example1.xml} (100%) delete mode 100644 tests/doc_test.cpp diff --git a/Testing/Temporary/CTestCostData.txt b/Testing/Temporary/CTestCostData.txt new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/Testing/Temporary/CTestCostData.txt @@ -0,0 +1 @@ +--- diff --git a/Testing/Temporary/LastTest.log b/Testing/Temporary/LastTest.log new file mode 100644 index 0000000..7a4e83d --- /dev/null +++ b/Testing/Temporary/LastTest.log @@ -0,0 +1,3 @@ +Start testing: Sep 05 15:55 CST +---------------------------------------------------------- +End testing: Sep 05 15:55 CST diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 92a0883..c894429 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,4 +13,3 @@ add_custom_test(parser_test) add_custom_test(printable_test) add_custom_test(document_test) add_custom_test(buffer_test) -add_custom_test(doc_test) diff --git a/tests/data/example.xml b/tests/data/example1.xml similarity index 100% rename from tests/data/example.xml rename to tests/data/example1.xml diff --git a/tests/doc_test.cpp b/tests/doc_test.cpp deleted file mode 100644 index f9ace25..0000000 --- a/tests/doc_test.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// All example codes in document -#include -#include -#include "myxml/document.hpp" -#include "myxml/element.hpp" - -TEST_CASE("docs/examples", "[doc]") -{ - SECTION("1") - { - using namespace myxml; - std::string xml = ""; - document doc = document::parse(xml); - std::string path = "tests/data/example.xml"; - doc = document::load(path); - // get root elem - std::optional elem = doc.root().first_elem(); - // or directly query elem in root - elem = doc.first_elem(); - // or query by name - elem = doc.first_elem("elem"); - } -} \ No newline at end of file diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index 320ae73..c4b210c 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -218,6 +218,7 @@ TEST_CASE("Parsing simple xml elements", "[parser]") SECTION("Simple File Buffer") { - auto doc = document::load("tests/data/example.xml"); + auto doc = document::load("tests/data/example1.xml"); + auto root = doc.root(); } } \ No newline at end of file diff --git a/tests/printable_test.cpp b/tests/printable_test.cpp index b24ab5c..d4ada4a 100644 --- a/tests/printable_test.cpp +++ b/tests/printable_test.cpp @@ -2,21 +2,6 @@ #include #include "myxml/element.hpp" -// TEST_CASE("Formatted Export", "[exportable]") -// { -// SECTION("Single") -// { -// auto root = myxml::element_impl::_new("root"); -// REQUIRE(root->str() == "\n"); -// } - -// SECTION("Text") -// { -// auto root = myxml::element_impl::parse("Hello, world!"); -// REQUIRE(root->str() == "\n Hello, world!\n\n"); -// } -// } - TEST_CASE("Raw Export", "[exportable]") { auto root = myxml::element_impl::parse("");