diff --git a/src/coders/json.cpp b/src/coders/json.cpp index 017f7d26..0990ff47 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -11,15 +11,17 @@ using namespace json; -class Parser : BasicParser { - dv::value parseList(); - dv::value parseObject(); - dv::value parseValue(); -public: - Parser(std::string_view filename, std::string_view source); +namespace { + class Parser : BasicParser { + dv::value parseList(); + dv::value parseObject(); + dv::value parseValue(); + public: + Parser(std::string_view filename, std::string_view source); - dv::value parse(); -}; + dv::value parse(); + }; +} inline void newline( std::stringstream& ss, bool nice, uint indent, const std::string& indentstr diff --git a/src/coders/xml.cpp b/src/coders/xml.cpp index faf84f15..b2cb8809 100644 --- a/src/coders/xml.cpp +++ b/src/coders/xml.cpp @@ -107,8 +107,8 @@ glm::vec4 Attribute::asColor() const { Node::Node(std::string tag) : tag(std::move(tag)) { } -void Node::add(const xmlelement& element) { - elements.push_back(element); +void Node::add(std::unique_ptr element) { + elements.push_back(std::move(element)); } void Node::set(const std::string& name, const std::string& text) { @@ -119,7 +119,7 @@ const std::string& Node::getTag() const { return tag; } -const xmlattribute& Node::attr(const std::string& name) const { +const Attribute& Node::attr(const std::string& name) const { auto found = attrs.find(name); if (found == attrs.end()) { throw std::runtime_error( @@ -129,7 +129,7 @@ const xmlattribute& Node::attr(const std::string& name) const { return found->second; } -xmlattribute Node::attr(const std::string& name, const std::string& def) const { +Attribute Node::attr(const std::string& name, const std::string& def) const { auto found = attrs.find(name); if (found == attrs.end()) { return Attribute(name, def); @@ -142,19 +142,23 @@ bool Node::has(const std::string& name) const { return found != attrs.end(); } -xmlelement Node::sub(size_t index) { - return elements.at(index); +Node& Node::sub(size_t index) { + return *elements.at(index); +} + +const Node& Node::sub(size_t index) const { + return *elements.at(index); } size_t Node::size() const { return elements.size(); } -const std::vector& Node::getElements() const { +const std::vector>& Node::getElements() const { return elements; } -const xmlelements_map& Node::getAttributes() const { +const std::unordered_map& Node::getAttributes() const { return attrs; } @@ -162,12 +166,12 @@ Document::Document(std::string version, std::string encoding) : version(std::move(version)), encoding(std::move(encoding)) { } -void Document::setRoot(const xmlelement& element) { - this->root = element; +void Document::setRoot(std::unique_ptr element) { + root = std::move(element); } -xmlelement Document::getRoot() const { - return root; +const Node* Document::getRoot() const { + return root.get(); } const std::string& Document::getVersion() const { @@ -178,82 +182,6 @@ const std::string& Document::getEncoding() const { return encoding; } -Parser::Parser(std::string_view filename, std::string_view source) - : BasicParser(filename, source) { -} - -xmlelement Parser::parseOpenTag() { - std::string tag = parseXMLName(); - auto node = std::make_shared(tag); - - char c; - while (true) { - skipWhitespace(); - c = peek(); - if (c == '/' || c == '>' || c == '?') break; - std::string attrname = parseXMLName(); - std::string attrtext = ""; - skipWhitespace(); - if (peek() == '=') { - nextChar(); - skipWhitespace(); - - char quote = peek(); - if (quote != '\'' && quote != '"') { - throw error("string literal expected"); - } - skip(1); - attrtext = parseString(quote); - } - node->set(attrname, attrtext); - } - return node; -} - -void Parser::parseDeclaration() { - std::string version = "1.0"; - std::string encoding = "UTF-8"; - expect('<'); - if (peek() == '?') { - nextChar(); - xmlelement node = parseOpenTag(); - expect("?>"); - if (node->getTag() != "xml") { - throw error("invalid declaration"); - } - version = node->attr("version", version).getText(); - encoding = node->attr("encoding", encoding).getText(); - if (encoding != "utf-8" && encoding != "UTF-8") { - throw error("UTF-8 encoding is only supported"); - } - } else { - goBack(); - } - document = std::make_shared(version, encoding); -} - -void Parser::parseComment() { - expect("!--"); - if (skipTo("-->")) { - skip(3); - } else { - throw error("comment close missing"); - } -} - -std::string Parser::parseText() { - size_t start = pos; - while (hasNext()) { - char c = peek(); - if (c == '<') { - break; - } - nextChar(); - } - return Parser("[string]", std::string(source.substr(start, pos - start))) - .parseString('\0', false); -} - inline bool is_xml_identifier_start(char c) { return is_identifier_start(c) || c == ':'; } @@ -262,82 +190,166 @@ inline bool is_xml_identifier_part(char c) { return is_identifier_part(c) || c == '-' || c == '.' || c == ':'; } -std::string Parser::parseXMLName() { - char c = peek(); - if (!is_xml_identifier_start(c)) { - throw error("identifier expected"); - } - int start = pos; - while (hasNext() && is_xml_identifier_part(source[pos])) { - pos++; - } - return std::string(source.substr(start, pos - start)); -} +namespace { +class Parser : BasicParser { + std::unique_ptr document; -xmlelement Parser::parseElement() { - // text element - if (peek() != '<') { - auto element = std::make_shared("#"); - auto text = parseText(); - util::replaceAll(text, """, "\""); - util::replaceAll(text, "'", "'"); - util::replaceAll(text, "<", "<"); - util::replaceAll(text, ">", ">"); - util::replaceAll(text, "&", "&"); - element->set("#", text); + std::unique_ptr parseOpenTag() { + std::string tag = parseXMLName(); + auto node = std::make_unique(tag); + + char c; + while (true) { + skipWhitespace(); + c = peek(); + if (c == '/' || c == '>' || c == '?') break; + std::string attrname = parseXMLName(); + std::string attrtext = ""; + skipWhitespace(); + if (peek() == '=') { + nextChar(); + skipWhitespace(); + + char quote = peek(); + if (quote != '\'' && quote != '"') { + throw error("string literal expected"); + } + skip(1); + attrtext = parseString(quote); + } + node->set(attrname, attrtext); + } + return node; + } + + std::unique_ptr parseElement() { + // text element + if (peek() != '<') { + auto element = std::make_unique("#"); + auto text = parseText(); + util::replaceAll(text, """, "\""); + util::replaceAll(text, "'", "'"); + util::replaceAll(text, "<", "<"); + util::replaceAll(text, ">", ">"); + util::replaceAll(text, "&", "&"); + element->set("#", text); + return element; + } + nextChar(); + + // + if (peek() == '!') { + if (isNext("!DOCTYPE ")) { + throw error("XML DTD is not supported yet"); + } + parseComment(); + return nullptr; + } + + auto element = parseOpenTag(); + char c = nextChar(); + + // + if (c == '/') { + expect('>'); + } + // ... + else if (c == '>') { + skipWhitespace(); + while (!isNext("add(std::move(sub)); + } + skipWhitespace(); + } + skip(2); + expect(element->getTag()); + expect('>'); + } + // + else { + throw error("invalid syntax"); + } return element; } - nextChar(); - // - if (peek() == '!') { - if (isNext("!DOCTYPE ")) { - throw error("XML DTD is not supported yet"); - } - parseComment(); - return nullptr; - } - - auto element = parseOpenTag(); - char c = nextChar(); - - // - if (c == '/') { - expect('>'); - } - // ... - else if (c == '>') { - skipWhitespace(); - while (!isNext("add(sub); + void parseDeclaration() { + std::string version = "1.0"; + std::string encoding = "UTF-8"; + expect('<'); + if (peek() == '?') { + nextChar(); + auto node = parseOpenTag(); + expect("?>"); + if (node->getTag() != "xml") { + throw error("invalid declaration"); } - skipWhitespace(); + version = node->attr("version", version).getText(); + encoding = node->attr("encoding", encoding).getText(); + if (encoding != "utf-8" && encoding != "UTF-8") { + throw error("UTF-8 encoding is only supported"); + } + } else { + goBack(); } - skip(2); - expect(element->getTag()); - expect('>'); + document = std::make_unique(version, encoding); } - // - else { - throw error("invalid syntax"); + + void parseComment() { + expect("!--"); + if (skipTo("-->")) { + skip(3); + } else { + throw error("comment close missing"); + } } - return element; + + std::string parseText() { + size_t start = pos; + while (hasNext()) { + char c = peek(); + if (c == '<') { + break; + } + nextChar(); + } + return Parser("[string]", std::string(source.substr(start, pos - start))) + .parseString('\0', false); + } + + std::string parseXMLName() { + char c = peek(); + if (!is_xml_identifier_start(c)) { + throw error("identifier expected"); + } + int start = pos; + while (hasNext() && is_xml_identifier_part(source[pos])) { + pos++; + } + return std::string(source.substr(start, pos - start)); + } +public: + Parser(std::string_view filename, std::string_view source) + : BasicParser(filename, source) { + } + + std::unique_ptr parse() { + parseDeclaration(); + + std::unique_ptr root; + while (root == nullptr) { + root = parseElement(); + } + document->setRoot(std::move(root)); + return std::move(document); + } +}; } -xmldocument Parser::parse() { - parseDeclaration(); - - xmlelement root = nullptr; - while (root == nullptr) { - root = parseElement(); - } - document->setRoot(root); - return document; -} - -xmldocument xml::parse(std::string_view filename, std::string_view source) { +std::unique_ptr xml::parse( + std::string_view filename, std::string_view source +) { Parser parser(filename, source); return parser.parse(); } @@ -354,13 +366,13 @@ inline void newline( static void stringifyElement( std::stringstream& ss, - const xmlelement& element, + const Node& element, bool nice, const std::string& indentStr, int indent ) { - if (element->isText()) { - std::string text = element->attr("#").getText(); + if (element.isText()) { + std::string text = element.attr("#").getText(); util::replaceAll(text, "&", "&"); util::replaceAll(text, "\"", """); util::replaceAll(text, "'", "'"); @@ -369,10 +381,10 @@ static void stringifyElement( ss << text; return; } - const std::string& tag = element->getTag(); + const std::string& tag = element.getTag(); ss << '<' << tag; - auto& attrs = element->getAttributes(); + auto& attrs = element.getAttributes(); if (!attrs.empty()) { ss << ' '; int count = 0; @@ -388,10 +400,10 @@ static void stringifyElement( count++; } } - auto& elements = element->getElements(); + auto& elements = element.getElements(); if (elements.size() == 1 && elements[0]->isText()) { ss << ">"; - stringifyElement(ss, elements[0], nice, indentStr, indent + 1); + stringifyElement(ss, *elements[0], nice, indentStr, indent + 1); ss << ""; return; } @@ -399,7 +411,7 @@ static void stringifyElement( ss << '>'; for (auto& sub : elements) { newline(ss, nice, indentStr, indent + 1); - stringifyElement(ss, sub, nice, indentStr, indent + 1); + stringifyElement(ss, *sub, nice, indentStr, indent + 1); } newline(ss, nice, indentStr, indent); ss << ""; @@ -410,16 +422,16 @@ static void stringifyElement( } std::string xml::stringify( - const xmldocument& document, bool nice, const std::string& indentStr + const Document& document, bool nice, const std::string& indentStr ) { std::stringstream ss; // XML declaration - ss << "getVersion(); + ss << ""; newline(ss, nice, indentStr, 0); - stringifyElement(ss, document->getRoot(), nice, indentStr, 0); + stringifyElement(ss, *document.getRoot(), nice, indentStr, 0); return ss.str(); } diff --git a/src/coders/xml.hpp b/src/coders/xml.hpp index 54a2b589..5831c29a 100644 --- a/src/coders/xml.hpp +++ b/src/coders/xml.hpp @@ -13,11 +13,6 @@ namespace xml { class Attribute; class Document; - using xmlattribute = Attribute; - using xmlelement = std::shared_ptr; - using xmldocument = std::shared_ptr; - using xmlelements_map = std::unordered_map; - class Attribute { std::string name; std::string text; @@ -40,13 +35,15 @@ namespace xml { /// 'text' class Node { std::string tag; - std::unordered_map attrs; - std::vector elements; + std::unordered_map attrs; + std::vector> elements; public: Node(std::string tag); + Node(const Node&) = delete; + /// @brief Add sub-element - void add(const xmlelement& element); + void add(std::unique_ptr element); /// @brief Set attribute value. Creates attribute if does not exists /// @param name attribute name @@ -67,15 +64,15 @@ namespace xml { /// @brief Get attribute by name /// @param name attribute name /// @throws std::runtime_error if element has no attribute - /// @return xmlattribute - {name, value} - const xmlattribute& attr(const std::string& name) const; + /// @return xml attribute - {name, value} + const Attribute& attr(const std::string& name) const; /// @brief Get attribute by name /// @param name attribute name /// @param def default value will be returned wrapped in xmlattribute /// if element has no attribute - /// @return xmlattribute - {name, value} or {name, def} if not found*/ - xmlattribute attr(const std::string& name, const std::string& def) + /// @return xml attribute - {name, value} or {name, def} if not found + Attribute attr(const std::string& name, const std::string& def) const; /// @brief Check if element has attribute @@ -86,51 +83,37 @@ namespace xml { /// @param index sub-element index /// @throws std::out_of_range if an invalid index given /// @return sub-element - xmlelement sub(size_t index); + Node& sub(size_t index); + const Node& sub(size_t index) const; /// @brief Get number of sub-elements size_t size() const; - const std::vector& getElements() const; - const xmlelements_map& getAttributes() const; + const std::vector>& getElements() const; + const std::unordered_map& getAttributes() const; }; class Document { - xmlelement root = nullptr; + std::unique_ptr root = nullptr; std::string version; std::string encoding; public: Document(std::string version, std::string encoding); - void setRoot(const xmlelement& element); - xmlelement getRoot() const; + void setRoot(std::unique_ptr element); + const Node* getRoot() const; const std::string& getVersion() const; const std::string& getEncoding() const; }; - class Parser : BasicParser { - xmldocument document; - - xmlelement parseOpenTag(); - xmlelement parseElement(); - void parseDeclaration(); - void parseComment(); - std::string parseText(); - std::string parseXMLName(); - public: - Parser(std::string_view filename, std::string_view source); - - xmldocument parse(); - }; - /// @brief Serialize XML Document to string /// @param document serializing document /// @param nice use human readable format (with indents and line-separators) /// @param indentStr indentation characters sequence (default - 4 spaces) /// @return XML string - extern std::string stringify( - const xmldocument& document, + std::string stringify( + const Document& document, bool nice = true, const std::string& indentStr = " " ); @@ -139,7 +122,9 @@ namespace xml { /// @param filename file name will be shown in error messages /// @param source xml source code string /// @return xml document - extern xmldocument parse( + std::unique_ptr parse( std::string_view filename, std::string_view source ); + + using xmlelement = Node; } diff --git a/src/frontend/UiDocument.cpp b/src/frontend/UiDocument.cpp index fff98f3f..2108b46e 100644 --- a/src/frontend/UiDocument.cpp +++ b/src/frontend/UiDocument.cpp @@ -67,9 +67,7 @@ std::unique_ptr UiDocument::read( : scripting::create_doc_environment(penv, name); gui::UiXmlReader reader(env); - auto view = reader.readXML( - file.u8string(), xmldoc->getRoot() - ); + auto view = reader.readXML(file.u8string(), *xmldoc->getRoot()); view->setId("root"); uidocscript script {}; auto scriptFile = fs::path(file.u8string()+".lua"); diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index 0c721524..93fc6b29 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -56,8 +56,8 @@ static runnable create_runnable( const xml::xmlelement& element, const std::string& name ) { - if (element->has(name)) { - std::string text = element->attr(name).getText(); + if (element.has(name)) { + std::string text = element.attr(name).getText(); if (!text.empty()) { return scripting::create_runnable( reader.getEnvironment(), text, reader.getFilename() @@ -83,78 +83,78 @@ static onaction create_action( static void _readUINode( const UiXmlReader& reader, const xml::xmlelement& element, UINode& node ) { - if (element->has("id")) { - node.setId(element->attr("id").getText()); + if (element.has("id")) { + node.setId(element.attr("id").getText()); } - if (element->has("pos")) { - node.setPos(element->attr("pos").asVec2()); + if (element.has("pos")) { + node.setPos(element.attr("pos").asVec2()); } - if (element->has("size")) { - node.setSize(element->attr("size").asVec2()); + if (element.has("size")) { + node.setSize(element.attr("size").asVec2()); } - if (element->has("color")) { - glm::vec4 color = element->attr("color").asColor(); + if (element.has("color")) { + glm::vec4 color = element.attr("color").asColor(); glm::vec4 hoverColor = color; glm::vec4 pressedColor = color; - if (element->has("hover-color")) { + if (element.has("hover-color")) { hoverColor = node.getHoverColor(); } - if (element->has("pressed-color")) { + if (element.has("pressed-color")) { pressedColor = node.getPressedColor(); } node.setColor(color); node.setHoverColor(hoverColor); node.setPressedColor(pressedColor); } - if (element->has("margin")) { - node.setMargin(element->attr("margin").asVec4()); + if (element.has("margin")) { + node.setMargin(element.attr("margin").asVec4()); } - if (element->has("z-index")) { - node.setZIndex(element->attr("z-index").asInt()); + if (element.has("z-index")) { + node.setZIndex(element.attr("z-index").asInt()); } - if (element->has("interactive")) { - node.setInteractive(element->attr("interactive").asBool()); + if (element.has("interactive")) { + node.setInteractive(element.attr("interactive").asBool()); } - if (element->has("visible")) { - node.setVisible(element->attr("visible").asBool()); + if (element.has("visible")) { + node.setVisible(element.attr("visible").asBool()); } - if (element->has("enabled")) { - node.setEnabled(element->attr("enabled").asBool()); + if (element.has("enabled")) { + node.setEnabled(element.attr("enabled").asBool()); } - if (element->has("position-func")) { + if (element.has("position-func")) { node.setPositionFunc(scripting::create_vec2_supplier( reader.getEnvironment(), - element->attr("position-func").getText(), + element.attr("position-func").getText(), reader.getFilename() )); } - if (element->has("size-func")) { + if (element.has("size-func")) { node.setSizeFunc(scripting::create_vec2_supplier( reader.getEnvironment(), - element->attr("size-func").getText(), + element.attr("size-func").getText(), reader.getFilename() )); } - if (element->has("hover-color")) { - node.setHoverColor(element->attr("hover-color").asColor()); + if (element.has("hover-color")) { + node.setHoverColor(element.attr("hover-color").asColor()); } - if (element->has("pressed-color")) { - node.setPressedColor(element->attr("pressed-color").asColor()); + if (element.has("pressed-color")) { + node.setPressedColor(element.attr("pressed-color").asColor()); } - std::string alignName = element->attr("align", "").getText(); + std::string alignName = element.attr("align", "").getText(); node.setAlign(align_from_string(alignName, node.getAlign())); - if (element->has("gravity")) { + if (element.has("gravity")) { node.setGravity(gravity_from_string( - element->attr("gravity").getText() + element.attr("gravity").getText() )); } - if (element->has("tooltip")) { - node.setTooltip(util::str2wstr_utf8(element->attr("tooltip").getText())); + if (element.has("tooltip")) { + node.setTooltip(util::str2wstr_utf8(element.attr("tooltip").getText())); } - if (element->has("tooltip-delay")) { - node.setTooltipDelay(element->attr("tooltip-delay").asFloat()); + if (element.has("tooltip-delay")) { + node.setTooltipDelay(element.attr("tooltip-delay").asFloat()); } if (auto onclick = create_action(reader, element, "onclick")) { @@ -169,16 +169,16 @@ static void _readUINode( static void _readContainer(UiXmlReader& reader, const xml::xmlelement& element, Container& container) { _readUINode(reader, element, container); - if (element->has("scrollable")) { - container.setScrollable(element->attr("scrollable").asBool()); + if (element.has("scrollable")) { + container.setScrollable(element.attr("scrollable").asBool()); } - if (element->has("scroll-step")) { - container.setScrollStep(element->attr("scroll-step").asInt()); + if (element.has("scroll-step")) { + container.setScrollStep(element.attr("scroll-step").asInt()); } - for (auto& sub : element->getElements()) { + for (auto& sub : element.getElements()) { if (sub->isText()) continue; - auto subnode = reader.readUINode(sub); + auto subnode = reader.readUINode(*sub); if (subnode) { container.add(subnode); } @@ -196,8 +196,8 @@ void UiXmlReader::readUINode(UiXmlReader& reader, const xml::xmlelement& element static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Panel& panel, bool subnodes=true) { _readUINode(reader, element, panel); - if (element->has("padding")) { - glm::vec4 padding = element->attr("padding").asVec4(); + if (element.has("padding")) { + glm::vec4 padding = element.attr("padding").asVec4(); panel.setPadding(padding); glm::vec2 size = panel.getSize(); panel.setSize(glm::vec2( @@ -205,23 +205,23 @@ static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Pane size.y + padding.y + padding.w )); } - if (element->has("size")) { + if (element.has("size")) { panel.setResizing(false); } - if (element->has("max-length")) { - panel.setMaxLength(element->attr("max-length").asInt()); + if (element.has("max-length")) { + panel.setMaxLength(element.attr("max-length").asInt()); } - if (element->has("orientation")) { - auto &oname = element->attr("orientation").getText(); + if (element.has("orientation")) { + auto &oname = element.attr("orientation").getText(); if (oname == "horizontal") { panel.setOrientation(Orientation::horizontal); } } if (subnodes) { - for (auto& sub : element->getElements()) { + for (auto& sub : element.getElements()) { if (sub->isText()) continue; - auto subnode = reader.readUINode(sub); + auto subnode = reader.readUINode(*sub); if (subnode) { panel.add(subnode); } @@ -231,8 +231,8 @@ static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Pane static std::wstring readAndProcessInnerText(const xml::xmlelement& element, const std::string& context) { std::wstring text = L""; - if (element->size() == 1) { - std::string source = element->sub(0)->attr("#").getText(); + if (element.size() == 1) { + std::string source = element.sub(0).attr("#").getText(); util::trim(source); text = util::str2wstr_utf8(source); if (text[0] == '@') { @@ -250,29 +250,29 @@ static std::shared_ptr readLabel(UiXmlReader& reader, const xml::xmlelem std::wstring text = readAndProcessInnerText(element, reader.getContext()); auto label = std::make_shared