From a54f5dd3f9e1bd1227e36da438d8cd5432d441a3 Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Sun, 22 May 2011 00:56:58 -0300 Subject: [PATCH] FML emitter --- src/framework/core/configs.cpp | 9 +- src/framework/core/resources.h | 1 + src/framework/util/fml.cpp | 422 ++++++++++++--------------------- src/framework/util/fml.h | 245 ++++++++++--------- 4 files changed, 281 insertions(+), 396 deletions(-) diff --git a/src/framework/core/configs.cpp b/src/framework/core/configs.cpp index f5bff931..77ffb6c5 100644 --- a/src/framework/core/configs.cpp +++ b/src/framework/core/configs.cpp @@ -53,11 +53,10 @@ bool Configs::load(const std::string& fileName) void Configs::save() { if(!m_fileName.empty()) { - std::stringstream out; - foreach(auto pair, m_confsMap) { - out << pair.first << ": " << pair.second << std::endl; - } - g_resources.saveFile(m_fileName, out); + FML::Emitter emitter; + FML::Node *doc = emitter.createDocument(); + doc->write(m_confsMap); + g_resources.saveFile(m_fileName, emitter.emitDocument()); } } diff --git a/src/framework/core/resources.h b/src/framework/core/resources.h index d690d447..3a9904c3 100644 --- a/src/framework/core/resources.h +++ b/src/framework/core/resources.h @@ -49,6 +49,7 @@ public: bool loadFile(const std::string& fileName, std::iostream& out); bool saveFile(const std::string& fileName, const uchar *data, uint size); + bool saveFile(const std::string& fileName, const std::string& data) { return saveFile(fileName, (const uchar*)data.c_str(), data.size()); } bool saveFile(const std::string& fileName, std::istream& in); bool deleteFile(const std::string& fileName); diff --git a/src/framework/util/fml.cpp b/src/framework/util/fml.cpp index bdc31076..b2ad3510 100644 --- a/src/framework/util/fml.cpp +++ b/src/framework/util/fml.cpp @@ -1,217 +1,88 @@ #include "fml.h" -#include -#include - -namespace FML { - - -/////////////////////////////////////////////////////////////////////////////// -// Utilities - -bool fml_convert(const std::string& input, bool& b) +namespace FML { - std::string names[5][2] = { { "1", "0" }, - { "y", "n" }, - { "yes", "no" }, - { "true", "false" }, - { "on", "off" } }; - std::string processedInput = input; - boost::trim(processedInput); - boost::to_lower(processedInput); - for(int i=0;i<5;i++) { - if(names[i][0] == processedInput) { - b = true; - return true; - } - if(names[i][1] == processedInput) { +bool fml_convert(const std::string& input, bool& b) { + static std::string validNames[2][4] = {{"true","yes","on","1"}, {"false","no","off","0"}}; + bool ret = false; + for(int i=0;i<4;++i) { + if(input == validNames[0][i]) { + b = true; + ret = true; + break; + } else if(input == validNames[1][i]) { b = false; - return true; + ret = true; + break; } } - return false; + return ret; } -bool fml_convert(const std::string& input, std::string& output) { +bool fml_convert(const std::string& input, std::string& output) +{ output = input; return true; } -std::string fml_int2str(int v) -{ - std::stringstream ss; - ss << v; - return ss.str(); +Node* Node::at(const std::string& childTag) const { + int i=0; + while(itag()!=childTag) + ++i; + return at(i); } -/////////////////////////////////////////////////////////////////////////////// -// Node - -Node::~Node() +Node* Node::createNode(std::string tag) { - for(NodeList::iterator it = m_children.begin(); it != m_children.end(); ++it) - delete (*it); + Node* node = new Node; + node->setTag(tag); + addNode(node); + return node; } -std::string Node::what() const -{ - if(m_parser) - return m_parser->what(); - return std::string(); -} - -Node* Node::at(const std::string& childTag) const -{ - for(NodeList::const_iterator it = m_children.begin(); it != m_children.end(); ++it) { - if((*it)->tag() == childTag) - return (*it); - } - return NULL; -} - -Node* Node::at(int pos) const -{ - if(pos < 0 || pos >= size()) - return NULL; - return m_children[pos]; -} - -void Node::addNode(Node *node) -{ +void Node::addNode(Node* node) { if(node->hasTag() && node->hasValue()) { - // remove nodes wit the same tag - for(NodeList::iterator it = m_children.begin(); it != m_children.end(); ++it) { - if((*it)->tag() == node->tag()) { - delete (*it); - m_children.erase(it); - break; - } + if(Node* other = at(node->tag())) { + if(removeNode(other)) + delete node; } } m_children.push_back(node); node->setParent(this); } -std::string Node::generateErrorMessage(const std::string& message) const -{ +bool Node::removeNode(Node* node) { + for(NodeList::iterator it = m_children.begin(); it != m_children.end(); ++it) { + if((*it) == node) { + m_children.erase(it); + return true; + } + } + return false; +} + +std::string Node::generateErrorMessage(const std::string& message) const { std::stringstream ss; ss << "FML error"; - if(!(what().empty())) + if(!what().empty()) ss << " in '" << what() << "'"; - if(m_line > 0) { + if(m_line > 0) ss << " at line " << m_line; - if(hasTag()) - ss << ", in node '" << tag() << "'"; - } + if(m_line > 0 && hasTag()) + ss << ", in node '" << tag() << "'"; ss << ": " << message; return ss.str(); } -std::string Node::emitValue() -{ - std::string tmpValue = value(); - - bool shouldQuote = false; - if(tmpValue.find_first_of("\\") != std::string::npos) { - boost::replace_all(tmpValue, "\\", "\\\\"); - shouldQuote = true; - } - if(tmpValue.find_first_of("\"") != std::string::npos) { - boost::replace_all(tmpValue, "\"", "\\\""); - shouldQuote = true; - } - if(tmpValue.find_first_of("\n") != std::string::npos) { - boost::replace_all(tmpValue, "\n", "\\n"); - shouldQuote = true; - } - - if(shouldQuote) { - tmpValue.append("\""); - tmpValue.insert(0, "\""); - } - - return tmpValue; -} - -std::string Node::emit(int depth) -{ - std::stringstream ss; - std::stringstream inlinestr; - bool shouldInline = false; - - for(int i=1;i 0) { - shouldInline = true; - - if(hasTag()) - ss << tag(); - - if(hasValue()) { - if(hasTag()) - ss << ": "; - else - ss << "- "; - ss << emitValue(); - ss << std::endl; - shouldInline = false; - } - - if(size() > 8 || size() == 0) - shouldInline = false; - } - - if(shouldInline) { - inlinestr << "["; - for(NodeList::const_iterator it = m_children.begin(); it != m_children.end(); ++it) { - Node* child = (*it); - if(child->hasTag() || ss.str().length() > 31) { - shouldInline = false; - break; - } else { - if(it != m_children.begin()) - inlinestr << ", "; - inlinestr << child->emitValue(); - } - } - inlinestr << "]"; - } - - if(shouldInline) { - ss << ": " << inlinestr.str() << std::endl; - } else { - if(!hasValue()) - ss << std::endl; - - for(NodeList::const_iterator it = m_children.begin(); it != m_children.end(); ++it) - ss << (*it)->emit(depth+1); - } - - return ss.str(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Parser - -Parser::~Parser() -{ - if(m_rootNode) - delete m_rootNode; -} - void Parser::load(std::istream& in) { - // initialize root node if(m_rootNode) delete m_rootNode; - m_rootNode = new Node(); + m_rootNode = new Node(what()); m_rootNode->setTag("root"); - m_currentParent = m_rootNode; - m_currentDepth = 0; - m_currentLine = 0; + m_currentDepth = m_currentLine = 0; m_multilineMode = DONT_MULTILINE; m_multilineData.clear(); @@ -222,7 +93,6 @@ void Parser::load(std::istream& in) parseLine(line); } - // stop multilining if enabled if(isMultilining()) stopMultilining(); } @@ -239,49 +109,43 @@ void Parser::parseLine(std::string& line) // trim left whitespaces boost::trim_left(line); - // skip comment lines - if(line[0] == '#') - return; - - // skip empty lines - if(line.empty()) + // skip comment or empty lines + if(line[0] == '#' || line.empty()) return; + // calculate depth int depth = 0; - if(numSpaces != std::string::npos) { + if(numSpaces != std::string::npos) depth = numSpaces / 2; - // check for syntax error - if(numSpaces % 2 != 0) { - throwError("file must be idented every 2 whitespaces", m_currentLine); - return; - } - } + + // check for syntax error + if(numSpaces != std::string::npos && numSpaces % 2 != 0) + throwError("file must be idented every 2 whitespaces", m_currentLine); // a depth above if(depth == m_currentDepth+1) { // change current parent to the previous added node m_currentParent = m_previousNode; - // a depth below, change parent to previus parent and add new node inside previuos parent + // a depth below, change parent to previus parent and add new node inside previuos parent } else if(depth < m_currentDepth) { // change current parent to the the new depth parent for(int i=0;iparent(); - // else if nots the same depth it's a syntax error + // else if nots the same depth it's a syntax error } else if(depth != m_currentDepth) { throwError("invalid indentation level", m_currentLine); - return; } // update current depth m_currentDepth = depth; // add node - Node *node = parseNode(line); - m_currentParent->addNode(node); + Node* node = m_currentParent->createNode(); + parseNode(node, line); m_previousNode = node; } -Node *Parser::parseNode(std::string& line) +void Parser::parseNode(Node* node, std::string& line) { // determine node tag and value std::string tag; @@ -292,61 +156,75 @@ Node *Parser::parseNode(std::string& line) if(dotsPos != std::string::npos) { tag = line.substr(0, dotsPos); value = line.substr(dotsPos+1); - } - // its a node that has a value but no tag - else if(line[0] == '-') { + } else if(line[0] == '-') // its a node that has a value but no tag value = line.substr(1); - } - // its a node that has only a tag - else { + else // its a node that has only a tag tag = line; - } - // trim the tag and value + // set node tag boost::trim(tag); - boost::trim(value); - - // create the node - Node *node = new Node(this); - node->setLine(m_currentLine); node->setTag(tag); - // process node value - if(!value.empty()) { - // multiline text scalar - if(value[0] == '|') { - startMultilining(value); - } - // sequence - else if(value[0] == '[') { - if(boost::ends_with(value, "]")) { - value.erase(value.length()-1, 1); - value.erase(0, 1); - boost::trim(value); - boost::tokenizer > tokens(value); - for(boost::tokenizer >::iterator it = tokens.begin(); it != tokens.end(); ++it) { - std::string tmp = (*it); - boost::trim(tmp); - if(!tmp.empty()) { - Node *child = new Node(this); - child->setLine(m_currentLine); - child->setValue(tmp); - node->addNode(child); - } - } - } else - throwError("missing ']' in sequence", m_currentLine); - } - // text scalar - else { - node->setValue(parseTextScalar(value)); - } - } + // set node line + node->setLine(m_currentLine); - return node; + // process node value + parseNodeValue(node, value); } -std::string Parser::parseTextScalar(std::string value) +void Parser::parseNodeValue(Node* node, std::string& value) +{ + boost::trim(value); + if(value.empty()) + return; + + // multiline text scalar + if(value[0] == '|') { + // determine multiline mode + m_multilineMode = MULTILINE_DONT_FOLD; + if(value.length() == 2) { + switch(value[1]) { + case '-': + m_multilineMode = MULTILINE_FOLD_BLOCK; + break; + case '+': + m_multilineMode = MULTILINE_FOLD_FLOW; + break; + default: + throwError("invalid multiline identifier", m_currentLine); + break; + } + } + m_currentDepth++; + } + // sequence + else if(value[0] == '[') { + if(!boost::ends_with(value, "]")) + throwError("missing ']' in sequence", m_currentLine); + + // erase '[' and ']' + value.erase(value.length()-1, 1); + value.erase(0, 1); + + // split commas + boost::tokenizer > tokens(value); + for(boost::tokenizer >::iterator it = tokens.begin(); it != tokens.end(); ++it) { + std::string tmp = (*it); + boost::trim(tmp); + if(!tmp.empty()) { + Node* child = node->createNode(); + child->setLine(m_currentLine); + child->setValue(tmp); + } + } + } + // text scalar + else { + node->setValue(parseTextValue(value)); + } +} + +std::string Parser::parseTextValue(std::string value) { if(value[0] == '"' && value[value.length()-1] == '"') { value = value.substr(1, value.length()-2); @@ -358,35 +236,12 @@ std::string Parser::parseTextScalar(std::string value) return value; } -void Parser::startMultilining(const std::string& param) -{ - m_multilineMode = MULTILINE_DONT_FOLD; - m_currentDepth++; - if(param.length() == 2) { - switch(param[1]) { - case '-': - m_multilineMode = MULTILINE_FOLD_BLOCK; - break; - case '+': - m_multilineMode = MULTILINE_FOLD_FLOW; - break; - default: - throwError("invalid multiline identifier", m_currentLine); - break; - } - } -} - void Parser::stopMultilining() { // remove all new lines at the end if(m_multilineMode == MULTILINE_DONT_FOLD || m_multilineMode == MULTILINE_FOLD_BLOCK) { - while(true) { - int lastPos = m_multilineData.length()-1; - if(m_multilineData[lastPos] != '\n') - break; - m_multilineData.erase(lastPos, 1); - } + while(*m_multilineData.rbegin() == '\n') + m_multilineData.erase(m_multilineData.length()-1, 1); } if(m_multilineMode == MULTILINE_FOLD_BLOCK) @@ -405,7 +260,7 @@ bool Parser::parseMultiline(std::string line) // depth above or equal current depth, add the text to the multiline if(numSpaces != std::string::npos && (int)numSpaces >= m_currentDepth*2) { - m_multilineData += parseTextScalar(line.substr(m_currentDepth*2)) + "\n"; + m_multilineData += parseTextValue(line.substr(m_currentDepth*2)) + "\n"; return true; // depth below the current depth, check if it is a node } else if(numSpaces == std::string::npos || (int)numSpaces < m_currentDepth*2) { @@ -435,4 +290,35 @@ void Parser::throwError(const std::string& message, int line) throw Exception(ss.str()); } -} // namespace FML { +std::string Emitter::emitNodeValue(Node* node) +{ + std::string value = node->value(); + if(value[0] == '"' || *value.rbegin() == '"' || value.find("\n") != std::string::npos) { + boost::replace_all(value, "\\", "\\\\"); + boost::replace_all(value, "\"", "\\\""); + boost::replace_all(value, "\n", "\\n"); + value.append("\""); + value.insert(0, "\""); + } + return value; +} + +std::string Emitter::emitNode(Node* node, int currentDepth) +{ + std::stringstream ss; + for(int i=1;i 0) { + if(node->hasTag()) + ss << node->tag(); + if(node->hasValue()) + ss << (node->hasTag() ? ": " : "- ") << emitNodeValue(node) << std::endl; + else + ss << std::endl; + } + for(int i=0;isize();++i) + ss << emitNode(node->at(i), currentDepth+1); + return ss.str(); +} + +} diff --git a/src/framework/util/fml.h b/src/framework/util/fml.h index 86222783..2b9f2980 100644 --- a/src/framework/util/fml.h +++ b/src/framework/util/fml.h @@ -1,62 +1,53 @@ -#ifndef FML_H -#define FML_H +#ifndef __FML_H +#define __FML_H #include #include #include +#include #include #include -#include #include #include +#include +#include #include -#include namespace FML { -/////////////////////////////////////////////////////////////////////////////// -// Utilities - bool fml_convert(const std::string& input, bool& b); bool fml_convert(const std::string& input, std::string& output); template -bool fml_convert(const V& in, T& out) { - std::stringstream ss; - ss << in; - ss >> out; - return !!ss; -} +bool fml_convert(const V& in, T& out) { std::stringstream ss; ss << in; ss >> out; return !!ss; } -std::string fml_int2str(int v); +inline std::string fml_tostr(bool b) { return (b ? "true" : "false"); } -/////////////////////////////////////////////////////////////////////////////// -// Exception +template < typename T> +std::string fml_tostr(T v) { std::stringstream ss; ss << v; return ss.str(); } class Exception : public std::runtime_error { public: - explicit Exception(const std::string& what) : std::runtime_error(what) {} + Exception(const std::string& what) : std::runtime_error(what) {} }; - -/////////////////////////////////////////////////////////////////////////////// -// Node - class Parser; class Node : boost::noncopyable { public: typedef std::vector NodeList; + typedef NodeList::iterator iterator; + typedef NodeList::const_iterator const_iterator; - Node(Parser* parser = 0) : m_parser(parser), m_parent(0), m_line(0) { } - ~Node(); + Node(std::string what = "") : m_parent(0), m_line(0), m_what(what) { } + ~Node() { for(int i=0;i 0; } bool hasValue() const { return !m_value.empty(); } - bool hasNode(const std::string childTag) const { return at(childTag) != 0; } + bool hasNode(const std::string ctag) const { return at(ctag) != 0; } void setTag(std::string tag) { m_tag = tag; } void setLine(int line) { m_line = line; } @@ -67,11 +58,7 @@ public: int line() const { return m_line; } int size() const { return m_children.size(); } Node* parent() { return m_parent; } - std::string what() const; - - // iterators - typedef NodeList::iterator iterator; - typedef NodeList::const_iterator const_iterator; + std::string what() const { return (m_parent ? m_parent->what() : m_what); } iterator begin() { return m_children.begin(); } iterator end() { return m_children.end(); } @@ -81,38 +68,22 @@ public: Node* front() const { return at(0); } Node* back() const { return at(size()-1); } - // util for generating error message - std::string generateErrorMessage(const std::string& message) const; void throwError(const std::string& message) const { throw Exception(generateErrorMessage(message)); } - // extracting values operator - template - friend bool operator >> (const Node& node, T& value); + Node* at(const std::string& ctag) const; + Node* at(int pos) const { return ((pos < size() && pos >= 0) ? m_children[pos] : 0); } - // get nodes - Node* at(const std::string& childTag) const; - Node* at(int pos) const; + std::string value(const std::string& def = "") const { return (m_value.empty() ? def : m_value); } + std::string valueAt(const std::string ctag, const std::string& def = "") const { Node* c = at(ctag); return (c ? c->value() : def); } + std::string valueAt(int pos, const std::string& def = "") const { Node* n = at(pos); return (n ? n->value() : def); } - // get values - std::string value(const std::string& def = std::string()) const { - if(!m_value.empty()) - return m_value; - return def; - } + Node* createNode(std::string tag = ""); + void addNode(Node* node); + bool removeNode(Node* node); - std::string valueAt(const std::string childTag, const std::string& def = std::string()) const { - if(Node* node = at(childTag)) - return node->value(); - return def; - } + std::string generateErrorMessage(const std::string& message) const; - std::string valueAt(int pos, const std::string& def = std::string()) const { - if(Node* node = at(pos)) - return node->value(); - return def; - } - - // read values into memory + // read into memory template void read(T* v) const { if(!(*this >> *v)) @@ -120,8 +91,8 @@ public: } template - bool readAt(const std::string& childTag, T* v) const { - if(Node* node = at(childTag)) { + bool readAt(const std::string& ctag, T* v) const { + if(Node* node = at(ctag)) { node->read(v); return true; } @@ -137,102 +108,114 @@ public: return false; } - // read values + // read returning the result template - T read() const { + T read() const { T v; read(&v); return v;} + + template + T readAt(const std::string& ctag) const { T v; - read(&v); + if(!readAt(ctag, &v)) + throw Exception(generateErrorMessage("child node " + ctag + " not found")); return v; } - template - T readAt(const std::string& childTag) const { - if(Node* node = at(childTag)) - return node->read(); - throw Exception(generateErrorMessage("child node " + childTag + " not found")); - return T(); - } - template T readAt(int pos) const { - if(Node* node = at(pos)) - return node->read(); - throw Exception(generateErrorMessage("child node at pos " + fml_int2str(pos) + " not found")); - return T(); + T v; + if(!readAt(pos, &v)) + throw Exception(generateErrorMessage("child node at pos " + fml_tostr(pos) + " not found")); + return v; } - // read values with defaults + // read with default supplied template - T readAt(const std::string& childTag, const T& def) const { - if(Node* node = at(childTag)) - return node->read(); - else - return def; + T readAt(const std::string& ctag, const T& def) const { Node* c = at(ctag); return (c ? c->read() : def); } + + template + T readAt(int pos, const T& def) const { Node* c = at(pos); return (c ? c->read() : def); } + + // writing + template + void write(T v) { + if(!(*this << v)) + throw Exception(generateErrorMessage("failed to cast to string node value of type " + std::string(typeid(T).name()))); } template - T readAt(int pos, const T& def) const { - if(Node* node = at(pos)) - return node->read(); - else - return def; + void writeIn(int pos, T v) { + Node* c; + while(!at(pos)) + c = createNode(); + c->write(v); } - void addNode(Node* node); - - std::string emitValue(); - std::string emit(int depth = 0); + template + void writeIn(const std::string& ctag, T v) { + Node* c = createNode(ctag); + c->write(v); + } private: - Parser* m_parser; Node* m_parent; int m_line; + std::string m_what; NodeList m_children; std::string m_tag; std::string m_value; }; - -/////////////////////////////////////////////////////////////////////////////// -// Node operators +// read operators +template +bool operator >> (const Node& node, T& v) { return fml_convert(node.value(), v); } template -bool operator >> (const Node& node, T& v) -{ - return fml_convert(node.value(), v); +bool operator >> (const Node& node, std::vector& v) { + v.resize(node.size()); + for(unsigned i=0;i(i); + return true; } template -bool operator >> (const Node& node, std::vector& v) -{ - std::vector& tmp; - tmp.resize(node.size()); +bool operator >> (const Node& node, std::list& v) { for(unsigned i=0;i(i); - v = tmp; + v.push_back(node.readAt(i)); return true; } template -bool operator >> (const Node& node, std::map& m) -{ - std::map tmp; - for(Node::const_iterator it = node.begin(); it != node.end(); ++it) { - K k; - T v; - if(fml_convert((*it)->tag(), k)) { - (*it)->read(&v); - tmp[k] = v; - } else - return false; - } - m = tmp; +bool operator >> (const Node& node, std::map& m) { + for(int i=0;iread()] = node.at(i)->read(); return true; } +// write operators +template +bool operator << (Node& node, const T& v) { node.setValue(fml_tostr(v)); return true; } -/////////////////////////////////////////////////////////////////////////////// -// Parser +template +bool operator << (Node& node, const std::vector& v) { + for(unsigned i=0;iwrite(v[i]); + return true; +} + +template +bool operator << (Node& node, const std::list& v) { + for(unsigned i=0;iwrite(v[i]); + return true; +} + +template +bool operator << (Node& node, const std::map& m) { + typename std::map::const_iterator it; + for(it = m.begin(); it != m.end(); ++it) + node.createNode(fml_tostr(it->first))->write(it->second); + return true; +} class Parser { @@ -246,24 +229,24 @@ class Parser public: Parser(std::string what = std::string()) : m_rootNode(0), m_what(what) { } Parser(std::istream& in, std::string what = std::string()) : m_rootNode(0), m_what(what) { load(in); } - ~Parser(); - - void load(std::istream& in); + ~Parser() { if(m_rootNode) delete m_rootNode; } Node* getDocument() const { return m_rootNode; } std::string what() { return m_what; } + void load(std::istream& in); + protected: void parseLine(std::string& line); - Node* parseNode(std::string& line); - std::string parseTextScalar(std::string value); + void parseNode(Node* node, std::string& line); + void parseNodeValue(Node* node, std::string& value); + std::string parseTextValue(std::string value); - void startMultilining(const std::string& param); void stopMultilining(); bool parseMultiline(std::string line); bool isMultilining() { return m_multilineMode != DONT_MULTILINE; } - void throwError(const std::string& message, int line = 0); + void throwError(const std::string& message, int line); private: int m_currentDepth; @@ -276,6 +259,22 @@ private: std::string m_what; }; +class Emitter +{ +public: + Emitter() : m_rootNode(0) { } + ~Emitter() { if(m_rootNode) delete m_rootNode; } + + Node* createDocument() { m_rootNode = new Node; return m_rootNode; } + std::string emitDocument() { if(m_rootNode) return emitNode(m_rootNode, 0); return std::string(); } + + static std::string emitNodeValue(Node* node); + static std::string emitNode(Node* node, int currentDepth = 0); + +private: + Node* m_rootNode; +}; + } // namespace FML { -#endif // FML_H +#endif // __FML_H