aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2022-01-01 01:28:21 +0800
committercrupest <crupest@outlook.com>2022-01-01 01:28:21 +0800
commit96a93e17baaff2c2050eba2afada639e93001232 (patch)
tree4e1625c0004b711c0bcf5c774ad37b54e3975de1
parent4e92e8709b30c385e1a88d7d4f76c50ee4a3d736 (diff)
downloadcru-96a93e17baaff2c2050eba2afada639e93001232.tar.gz
cru-96a93e17baaff2c2050eba2afada639e93001232.tar.bz2
cru-96a93e17baaff2c2050eba2afada639e93001232.zip
...
-rw-r--r--include/cru/common/String.hpp4
-rw-r--r--include/cru/xml/XmlNode.hpp94
-rw-r--r--include/cru/xml/XmlParser.hpp22
-rw-r--r--src/xml/XmlNode.cpp23
-rw-r--r--src/xml/XmlParser.cpp114
5 files changed, 201 insertions, 56 deletions
diff --git a/include/cru/common/String.hpp b/include/cru/common/String.hpp
index 4dab5b5a..ade2d84b 100644
--- a/include/cru/common/String.hpp
+++ b/include/cru/common/String.hpp
@@ -166,6 +166,10 @@ class CRU_BASE_API String {
inline void append(StringView str);
public:
+ String& operator+=(value_type value) {
+ this->append(value);
+ return *this;
+ }
String& operator+=(StringView other);
operator std::u16string_view() const {
diff --git a/include/cru/xml/XmlNode.hpp b/include/cru/xml/XmlNode.hpp
index 1787d6a0..0cbb6756 100644
--- a/include/cru/xml/XmlNode.hpp
+++ b/include/cru/xml/XmlNode.hpp
@@ -7,61 +7,87 @@
#include <vector>
namespace cru::xml {
+class XmlElementNode;
+
class XmlNode {
+ friend XmlElementNode;
+
public:
enum class Type { Text, Element };
- static XmlNode CreateText(String text) {
- XmlNode node(Type::Text);
- node.SetText(std::move(text));
- return node;
- }
-
- static XmlNode CreateElement(
- String tag, std::unordered_map<String, String> attributes = {},
- std::vector<XmlNode> children = {}) {
- XmlNode node(Type::Element);
- node.SetTag(std::move(tag));
- node.SetAttributes(std::move(attributes));
- node.SetChildren(std::move(children));
- return node;
- }
-
+ protected:
explicit XmlNode(Type type) : type_(type) {}
- CRU_DEFAULT_COPY(XmlNode)
- CRU_DEFAULT_MOVE(XmlNode)
+ public:
+ CRU_DELETE_COPY(XmlNode)
+ CRU_DELETE_MOVE(XmlNode)
- ~XmlNode() = default;
+ virtual ~XmlNode() = default;
Type GetType() const { return type_; }
+ XmlElementNode* GetParent() const { return parent_; }
+
+ virtual XmlNode* Clone() const = 0;
+
+ private:
+ const Type type_;
+ XmlElementNode* parent_ = nullptr;
+};
+
+class XmlTextNode : public XmlNode {
+ public:
+ XmlTextNode() : XmlNode(Type::Text) {}
+ explicit XmlTextNode(String text)
+ : XmlNode(Type::Text), text_(std::move(text)) {}
+
+ CRU_DELETE_COPY(XmlTextNode)
+ CRU_DELETE_MOVE(XmlTextNode)
+
+ ~XmlTextNode() override = default;
+
+ public:
String GetText() const { return text_; }
+ void SetText(String text) { text_ = std::move(text); }
+
+ XmlNode* Clone() const override { return new XmlTextNode(text_); }
+
+ private:
+ String text_;
+};
+
+class XmlElementNode : public XmlNode {
+ public:
+ XmlElementNode() : XmlNode(Type::Element) {}
+ explicit XmlElementNode(String tag,
+ std::unordered_map<String, String> attributes = {})
+ : XmlNode(Type::Element),
+ tag_(std::move(tag)),
+ attributes_(std::move(attributes)) {}
+
+ CRU_DELETE_COPY(XmlElementNode)
+ CRU_DELETE_MOVE(XmlElementNode)
+
+ ~XmlElementNode() override = default;
+
+ public:
String GetTag() const { return tag_; }
- std::unordered_map<String, String> GetAttributes() { return attributes_; }
+ void SetTag(String tag) { tag_ = std::move(tag); }
const std::unordered_map<String, String>& GetAttributes() const {
return attributes_;
}
- std::vector<XmlNode>& GetChildren() { return children_; }
- const std::vector<XmlNode>& GetChildren() const { return children_; }
-
- void SetType(Type type) { type_ = type; }
- void SetText(String text) { text_ = std::move(text); }
- void SetTag(String tag) { tag_ = std::move(tag); }
void SetAttributes(std::unordered_map<String, String> attributes) {
attributes_ = std::move(attributes);
}
- void SetChildren(std::vector<XmlNode> children) {
- children_ = std::move(children);
- }
+
+ void AddAttribute(String key, String value);
+ void AddChild(XmlNode* child);
+
+ XmlNode* Clone() const override;
private:
- Type type_;
- String text_;
String tag_;
std::unordered_map<String, String> attributes_;
- std::vector<XmlNode> children_;
+ std::vector<XmlNode*> children_;
};
-bool operator==(const XmlNode& lhs, const XmlNode& rhs);
-bool operator!=(const XmlNode& lhs, const XmlNode& rhs);
} // namespace cru::xml
diff --git a/include/cru/xml/XmlParser.hpp b/include/cru/xml/XmlParser.hpp
index 19f569d1..1d44c46f 100644
--- a/include/cru/xml/XmlParser.hpp
+++ b/include/cru/xml/XmlParser.hpp
@@ -2,11 +2,17 @@
#include "XmlNode.hpp"
+#include "cru/common/Exception.hpp"
#include "cru/common/String.hpp"
#include <optional>
namespace cru::xml {
+class XmlParsingException : public Exception {
+ public:
+ using Exception::Exception;
+};
+
class XmlParser {
public:
explicit XmlParser(String xml);
@@ -16,14 +22,24 @@ class XmlParser {
~XmlParser();
- XmlNode Parse();
+ XmlElementNode* Parse();
private:
- XmlNode DoParse();
+ XmlElementNode* DoParse();
+
+ char16_t Read1();
+ void ReadSpacesAndDiscard();
+ String ReadSpaces();
+ String ReadIdenitifier();
+ String ReadAttributeString();
private:
String xml_;
- std::optional<XmlNode> root_node_;
+ XmlElementNode* cache_;
+
+ XmlElementNode* pseudo_root_node_ = new XmlElementNode(u"$root");
+ XmlElementNode* current_ = pseudo_root_node_;
+ int current_position_ = 0;
};
} // namespace cru::xml
diff --git a/src/xml/XmlNode.cpp b/src/xml/XmlNode.cpp
index cfeb5cf9..f4b43ea6 100644
--- a/src/xml/XmlNode.cpp
+++ b/src/xml/XmlNode.cpp
@@ -1,14 +1,23 @@
#include "cru/xml/XmlNode.hpp"
namespace cru::xml {
-bool operator==(const XmlNode& lhs, const XmlNode& rhs) {
- return lhs.GetType() == rhs.GetType() && lhs.GetText() == rhs.GetText() &&
- lhs.GetTag() == rhs.GetTag() &&
- lhs.GetAttributes() == rhs.GetAttributes() &&
- lhs.GetChildren() == rhs.GetChildren();
+void XmlElementNode::AddAttribute(String key, String value) {
+ attributes_[std::move(key)] = std::move(value);
}
-bool operator!=(const XmlNode& lhs, const XmlNode& rhs) {
- return !(lhs == rhs);
+void XmlElementNode::AddChild(XmlNode* child) {
+ assert(child->GetParent() == nullptr);
+ children_.push_back(child);
+ child->parent_ = this;
+}
+
+XmlNode* XmlElementNode::Clone() const {
+ XmlElementNode* node = new XmlElementNode(tag_, attributes_);
+
+ for (auto child : children_) {
+ node->AddChild(child->Clone());
+ }
+
+ return node;
}
} // namespace cru::xml
diff --git a/src/xml/XmlParser.cpp b/src/xml/XmlParser.cpp
index 23407d11..f24a7f68 100644
--- a/src/xml/XmlParser.cpp
+++ b/src/xml/XmlParser.cpp
@@ -1,27 +1,117 @@
#include "cru/xml/XmlParser.hpp"
+#include "cru/xml/XmlNode.hpp"
namespace cru::xml {
-XmlNode XmlParser::Parse() {
- if (!root_node_) {
- root_node_ = DoParse();
+XmlElementNode* XmlParser::Parse() {
+ if (!cache_) {
+ cache_ = DoParse();
}
- return *root_node_;
+ return static_cast<XmlElementNode*>(cache_->Clone());
}
-XmlNode XmlParser::DoParse() {
- XmlNode root(XmlNode::Type::Element);
- XmlNode* current = &root;
- int current_position = 0;
+char16_t XmlParser::Read1() {
+ if (current_position_ >= xml_.size()) {
+ throw XmlParsingException(u"Unexpected end of xml");
+ }
+ return xml_[current_position_++];
+}
+
+void XmlParser::ReadSpacesAndDiscard() {
+ while (current_position_ < xml_.size() &&
+ (xml_[current_position_] == ' ' || xml_[current_position_] == '\t' ||
+ xml_[current_position_] == '\n' || xml_[current_position_] == '\r')) {
+ ++current_position_;
+ }
+}
+
+String XmlParser::ReadSpaces() {
+ String spaces;
+ while (current_position_ < xml_.size() &&
+ (xml_[current_position_] == ' ' || xml_[current_position_] == '\t' ||
+ xml_[current_position_] == '\n' || xml_[current_position_] == '\r')) {
+ spaces += xml_[current_position_];
+ ++current_position_;
+ }
+ return spaces;
+}
+
+String XmlParser::ReadIdenitifier() {
+ String identifier;
+ while (current_position_ < xml_.size() &&
+ (xml_[current_position_] >= 'a' && xml_[current_position_] <= 'z' ||
+ xml_[current_position_] >= 'A' && xml_[current_position_] <= 'Z' ||
+ xml_[current_position_] >= '0' && xml_[current_position_] <= '9' ||
+ xml_[current_position_] == '_')) {
+ identifier += xml_[current_position_];
+ ++current_position_;
+ }
+ return identifier;
+}
+
+String XmlParser::ReadAttributeString() {
+ if (Read1() != '"') {
+ throw XmlParsingException(u"Expected \".");
+ }
+
+ String string;
+
+ while (true) {
+ char16_t c = Read1();
+
+ if (c == '"') {
+ break;
+ }
+
+ string += c;
+ }
+
+ return string;
+}
- // TODO: Implement this.
- while (current_position < xml_.size()) {
- switch (xml_[current_position]) {
+XmlElementNode* XmlParser::DoParse() {
+ while (current_position_ < xml_.size()) {
+ switch (xml_[current_position_]) {
case '<': {
+ ++current_position_;
+
+ if (Read1() == '/') {
+ } else {
+ ReadSpacesAndDiscard();
+
+ String tag = ReadIdenitifier();
+
+ XmlElementNode* node = new XmlElementNode(tag);
+
+ while (true) {
+ ReadSpacesAndDiscard();
+ if (Read1() == '>') {
+ break;
+ } else {
+ String attribute_name = ReadIdenitifier();
+
+ ReadSpacesAndDiscard();
+
+ if (Read1() != '=') {
+ throw XmlParsingException(u"Expected '='");
+ }
+
+ ReadSpacesAndDiscard();
+
+ String attribute_value = ReadAttributeString();
+
+ node->AddAttribute(attribute_name, attribute_value);
+ }
+ }
+
+ current_->AddChild(node);
+ current_ = node;
+ }
+
break;
}
}
}
- return root;
+ return pseudo_root_node_;
}
} // namespace cru::xml