FML emitter
This commit is contained in:
parent
d7bc083014
commit
a54f5dd3f9
|
@ -53,11 +53,10 @@ bool Configs::load(const std::string& fileName)
|
||||||
void Configs::save()
|
void Configs::save()
|
||||||
{
|
{
|
||||||
if(!m_fileName.empty()) {
|
if(!m_fileName.empty()) {
|
||||||
std::stringstream out;
|
FML::Emitter emitter;
|
||||||
foreach(auto pair, m_confsMap) {
|
FML::Node *doc = emitter.createDocument();
|
||||||
out << pair.first << ": " << pair.second << std::endl;
|
doc->write(m_confsMap);
|
||||||
}
|
g_resources.saveFile(m_fileName, emitter.emitDocument());
|
||||||
g_resources.saveFile(m_fileName, out);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ public:
|
||||||
bool loadFile(const std::string& fileName, std::iostream& out);
|
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 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 saveFile(const std::string& fileName, std::istream& in);
|
||||||
|
|
||||||
bool deleteFile(const std::string& fileName);
|
bool deleteFile(const std::string& fileName);
|
||||||
|
|
|
@ -1,217 +1,88 @@
|
||||||
#include "fml.h"
|
#include "fml.h"
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
namespace FML
|
||||||
#include <boost/tokenizer.hpp>
|
|
||||||
|
|
||||||
namespace FML {
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Utilities
|
|
||||||
|
|
||||||
bool fml_convert(const std::string& input, bool& b)
|
|
||||||
{
|
{
|
||||||
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;
|
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;
|
output = input;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fml_int2str(int v)
|
Node* Node::at(const std::string& childTag) const {
|
||||||
{
|
int i=0;
|
||||||
std::stringstream ss;
|
while(i<size() && at(i)->tag()!=childTag)
|
||||||
ss << v;
|
++i;
|
||||||
return ss.str();
|
return at(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
Node* Node::createNode(std::string tag)
|
||||||
// Node
|
|
||||||
|
|
||||||
Node::~Node()
|
|
||||||
{
|
{
|
||||||
for(NodeList::iterator it = m_children.begin(); it != m_children.end(); ++it)
|
Node* node = new Node;
|
||||||
delete (*it);
|
node->setTag(tag);
|
||||||
|
addNode(node);
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Node::what() const
|
void Node::addNode(Node* node) {
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if(node->hasTag() && node->hasValue()) {
|
if(node->hasTag() && node->hasValue()) {
|
||||||
// remove nodes wit the same tag
|
if(Node* other = at(node->tag())) {
|
||||||
for(NodeList::iterator it = m_children.begin(); it != m_children.end(); ++it) {
|
if(removeNode(other))
|
||||||
if((*it)->tag() == node->tag()) {
|
delete node;
|
||||||
delete (*it);
|
|
||||||
m_children.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_children.push_back(node);
|
m_children.push_back(node);
|
||||||
node->setParent(this);
|
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;
|
std::stringstream ss;
|
||||||
ss << "FML error";
|
ss << "FML error";
|
||||||
if(!(what().empty()))
|
if(!what().empty())
|
||||||
ss << " in '" << what() << "'";
|
ss << " in '" << what() << "'";
|
||||||
if(m_line > 0) {
|
if(m_line > 0)
|
||||||
ss << " at line " << m_line;
|
ss << " at line " << m_line;
|
||||||
if(hasTag())
|
if(m_line > 0 && hasTag())
|
||||||
ss << ", in node '" << tag() << "'";
|
ss << ", in node '" << tag() << "'";
|
||||||
}
|
|
||||||
ss << ": " << message;
|
ss << ": " << message;
|
||||||
return ss.str();
|
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<depth;++i)
|
|
||||||
ss << " ";
|
|
||||||
|
|
||||||
if(depth > 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)
|
void Parser::load(std::istream& in)
|
||||||
{
|
{
|
||||||
// initialize root node
|
|
||||||
if(m_rootNode)
|
if(m_rootNode)
|
||||||
delete m_rootNode;
|
delete m_rootNode;
|
||||||
m_rootNode = new Node();
|
m_rootNode = new Node(what());
|
||||||
m_rootNode->setTag("root");
|
m_rootNode->setTag("root");
|
||||||
|
|
||||||
m_currentParent = m_rootNode;
|
m_currentParent = m_rootNode;
|
||||||
m_currentDepth = 0;
|
m_currentDepth = m_currentLine = 0;
|
||||||
m_currentLine = 0;
|
|
||||||
m_multilineMode = DONT_MULTILINE;
|
m_multilineMode = DONT_MULTILINE;
|
||||||
m_multilineData.clear();
|
m_multilineData.clear();
|
||||||
|
|
||||||
|
@ -222,7 +93,6 @@ void Parser::load(std::istream& in)
|
||||||
parseLine(line);
|
parseLine(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop multilining if enabled
|
|
||||||
if(isMultilining())
|
if(isMultilining())
|
||||||
stopMultilining();
|
stopMultilining();
|
||||||
}
|
}
|
||||||
|
@ -239,49 +109,43 @@ void Parser::parseLine(std::string& line)
|
||||||
// trim left whitespaces
|
// trim left whitespaces
|
||||||
boost::trim_left(line);
|
boost::trim_left(line);
|
||||||
|
|
||||||
// skip comment lines
|
// skip comment or empty lines
|
||||||
if(line[0] == '#')
|
if(line[0] == '#' || line.empty())
|
||||||
return;
|
|
||||||
|
|
||||||
// skip empty lines
|
|
||||||
if(line.empty())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// calculate depth
|
||||||
int depth = 0;
|
int depth = 0;
|
||||||
if(numSpaces != std::string::npos) {
|
if(numSpaces != std::string::npos)
|
||||||
depth = numSpaces / 2;
|
depth = numSpaces / 2;
|
||||||
// check for syntax error
|
|
||||||
if(numSpaces % 2 != 0) {
|
// check for syntax error
|
||||||
throwError("file must be idented every 2 whitespaces", m_currentLine);
|
if(numSpaces != std::string::npos && numSpaces % 2 != 0)
|
||||||
return;
|
throwError("file must be idented every 2 whitespaces", m_currentLine);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// a depth above
|
// a depth above
|
||||||
if(depth == m_currentDepth+1) {
|
if(depth == m_currentDepth+1) {
|
||||||
// change current parent to the previous added node
|
// change current parent to the previous added node
|
||||||
m_currentParent = m_previousNode;
|
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) {
|
} else if(depth < m_currentDepth) {
|
||||||
// change current parent to the the new depth parent
|
// change current parent to the the new depth parent
|
||||||
for(int i=0;i<m_currentDepth-depth;++i)
|
for(int i=0;i<m_currentDepth-depth;++i)
|
||||||
m_currentParent = m_currentParent->parent();
|
m_currentParent = m_currentParent->parent();
|
||||||
// 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) {
|
} else if(depth != m_currentDepth) {
|
||||||
throwError("invalid indentation level", m_currentLine);
|
throwError("invalid indentation level", m_currentLine);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update current depth
|
// update current depth
|
||||||
m_currentDepth = depth;
|
m_currentDepth = depth;
|
||||||
|
|
||||||
// add node
|
// add node
|
||||||
Node *node = parseNode(line);
|
Node* node = m_currentParent->createNode();
|
||||||
m_currentParent->addNode(node);
|
parseNode(node, line);
|
||||||
m_previousNode = node;
|
m_previousNode = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *Parser::parseNode(std::string& line)
|
void Parser::parseNode(Node* node, std::string& line)
|
||||||
{
|
{
|
||||||
// determine node tag and value
|
// determine node tag and value
|
||||||
std::string tag;
|
std::string tag;
|
||||||
|
@ -292,61 +156,75 @@ Node *Parser::parseNode(std::string& line)
|
||||||
if(dotsPos != std::string::npos) {
|
if(dotsPos != std::string::npos) {
|
||||||
tag = line.substr(0, dotsPos);
|
tag = line.substr(0, dotsPos);
|
||||||
value = line.substr(dotsPos+1);
|
value = line.substr(dotsPos+1);
|
||||||
}
|
} else if(line[0] == '-') // its a node that has a value but no tag
|
||||||
// its a node that has a value but no tag
|
|
||||||
else if(line[0] == '-') {
|
|
||||||
value = line.substr(1);
|
value = line.substr(1);
|
||||||
}
|
else // its a node that has only a tag
|
||||||
// its a node that has only a tag
|
|
||||||
else {
|
|
||||||
tag = line;
|
tag = line;
|
||||||
}
|
|
||||||
|
|
||||||
// trim the tag and value
|
// set node tag
|
||||||
boost::trim(tag);
|
boost::trim(tag);
|
||||||
boost::trim(value);
|
|
||||||
|
|
||||||
// create the node
|
|
||||||
Node *node = new Node(this);
|
|
||||||
node->setLine(m_currentLine);
|
|
||||||
node->setTag(tag);
|
node->setTag(tag);
|
||||||
|
|
||||||
// process node value
|
// set node line
|
||||||
if(!value.empty()) {
|
node->setLine(m_currentLine);
|
||||||
// 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<boost::escaped_list_separator<char> > tokens(value);
|
|
||||||
for(boost::tokenizer<boost::escaped_list_separator<char> >::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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<boost::escaped_list_separator<char> > tokens(value);
|
||||||
|
for(boost::tokenizer<boost::escaped_list_separator<char> >::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] == '"') {
|
if(value[0] == '"' && value[value.length()-1] == '"') {
|
||||||
value = value.substr(1, value.length()-2);
|
value = value.substr(1, value.length()-2);
|
||||||
|
@ -358,35 +236,12 @@ std::string Parser::parseTextScalar(std::string value)
|
||||||
return 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()
|
void Parser::stopMultilining()
|
||||||
{
|
{
|
||||||
// remove all new lines at the end
|
// remove all new lines at the end
|
||||||
if(m_multilineMode == MULTILINE_DONT_FOLD || m_multilineMode == MULTILINE_FOLD_BLOCK) {
|
if(m_multilineMode == MULTILINE_DONT_FOLD || m_multilineMode == MULTILINE_FOLD_BLOCK) {
|
||||||
while(true) {
|
while(*m_multilineData.rbegin() == '\n')
|
||||||
int lastPos = m_multilineData.length()-1;
|
m_multilineData.erase(m_multilineData.length()-1, 1);
|
||||||
if(m_multilineData[lastPos] != '\n')
|
|
||||||
break;
|
|
||||||
m_multilineData.erase(lastPos, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_multilineMode == MULTILINE_FOLD_BLOCK)
|
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
|
// depth above or equal current depth, add the text to the multiline
|
||||||
if(numSpaces != std::string::npos && (int)numSpaces >= m_currentDepth*2) {
|
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;
|
return true;
|
||||||
// depth below the current depth, check if it is a node
|
// depth below the current depth, check if it is a node
|
||||||
} else if(numSpaces == std::string::npos || (int)numSpaces < m_currentDepth*2) {
|
} 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());
|
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<currentDepth;++i)
|
||||||
|
ss << " ";
|
||||||
|
if(currentDepth > 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;i<node->size();++i)
|
||||||
|
ss << emitNode(node->at(i), currentDepth+1);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,62 +1,53 @@
|
||||||
#ifndef FML_H
|
#ifndef __FML_H
|
||||||
#define FML_H
|
#define __FML_H
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <iostream>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include <boost/tokenizer.hpp>
|
||||||
#include <boost/utility.hpp>
|
#include <boost/utility.hpp>
|
||||||
#include <boost/foreach.hpp>
|
|
||||||
|
|
||||||
namespace FML {
|
namespace FML {
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Utilities
|
|
||||||
|
|
||||||
bool fml_convert(const std::string& input, bool& b);
|
bool fml_convert(const std::string& input, bool& b);
|
||||||
bool fml_convert(const std::string& input, std::string& output);
|
bool fml_convert(const std::string& input, std::string& output);
|
||||||
|
|
||||||
template <typename V, typename T>
|
template <typename V, typename T>
|
||||||
bool fml_convert(const V& in, T& out) {
|
bool fml_convert(const V& in, T& out) { std::stringstream ss; ss << in; ss >> out; return !!ss; }
|
||||||
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"); }
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
template < typename T>
|
||||||
// Exception
|
std::string fml_tostr(T v) { std::stringstream ss; ss << v; return ss.str(); }
|
||||||
|
|
||||||
class Exception : public std::runtime_error {
|
class Exception : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
explicit Exception(const std::string& what) : std::runtime_error(what) {}
|
Exception(const std::string& what) : std::runtime_error(what) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Node
|
|
||||||
|
|
||||||
class Parser;
|
class Parser;
|
||||||
|
|
||||||
class Node : boost::noncopyable
|
class Node : boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef std::vector<Node*> NodeList;
|
typedef std::vector<Node*> 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(std::string what = "") : m_parent(0), m_line(0), m_what(what) { }
|
||||||
~Node();
|
~Node() { for(int i=0;i<size();++i) delete at(i); }
|
||||||
|
|
||||||
bool hasTag() const { return !m_tag.empty(); }
|
bool hasTag() const { return !m_tag.empty(); }
|
||||||
bool hasChildren() const { return size() > 0; }
|
bool hasChildren() const { return size() > 0; }
|
||||||
bool hasValue() const { return !m_value.empty(); }
|
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 setTag(std::string tag) { m_tag = tag; }
|
||||||
void setLine(int line) { m_line = line; }
|
void setLine(int line) { m_line = line; }
|
||||||
|
@ -67,11 +58,7 @@ public:
|
||||||
int line() const { return m_line; }
|
int line() const { return m_line; }
|
||||||
int size() const { return m_children.size(); }
|
int size() const { return m_children.size(); }
|
||||||
Node* parent() { return m_parent; }
|
Node* parent() { return m_parent; }
|
||||||
std::string what() const;
|
std::string what() const { return (m_parent ? m_parent->what() : m_what); }
|
||||||
|
|
||||||
// iterators
|
|
||||||
typedef NodeList::iterator iterator;
|
|
||||||
typedef NodeList::const_iterator const_iterator;
|
|
||||||
|
|
||||||
iterator begin() { return m_children.begin(); }
|
iterator begin() { return m_children.begin(); }
|
||||||
iterator end() { return m_children.end(); }
|
iterator end() { return m_children.end(); }
|
||||||
|
@ -81,38 +68,22 @@ public:
|
||||||
Node* front() const { return at(0); }
|
Node* front() const { return at(0); }
|
||||||
Node* back() const { return at(size()-1); }
|
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)); }
|
void throwError(const std::string& message) const { throw Exception(generateErrorMessage(message)); }
|
||||||
|
|
||||||
// extracting values operator
|
Node* at(const std::string& ctag) const;
|
||||||
template <typename T>
|
Node* at(int pos) const { return ((pos < size() && pos >= 0) ? m_children[pos] : 0); }
|
||||||
friend bool operator >> (const Node& node, T& value);
|
|
||||||
|
|
||||||
// get nodes
|
std::string value(const std::string& def = "") const { return (m_value.empty() ? def : m_value); }
|
||||||
Node* at(const std::string& childTag) const;
|
std::string valueAt(const std::string ctag, const std::string& def = "") const { Node* c = at(ctag); return (c ? c->value() : def); }
|
||||||
Node* at(int pos) const;
|
std::string valueAt(int pos, const std::string& def = "") const { Node* n = at(pos); return (n ? n->value() : def); }
|
||||||
|
|
||||||
// get values
|
Node* createNode(std::string tag = "");
|
||||||
std::string value(const std::string& def = std::string()) const {
|
void addNode(Node* node);
|
||||||
if(!m_value.empty())
|
bool removeNode(Node* node);
|
||||||
return m_value;
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string valueAt(const std::string childTag, const std::string& def = std::string()) const {
|
std::string generateErrorMessage(const std::string& message) const;
|
||||||
if(Node* node = at(childTag))
|
|
||||||
return node->value();
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string valueAt(int pos, const std::string& def = std::string()) const {
|
// read into memory
|
||||||
if(Node* node = at(pos))
|
|
||||||
return node->value();
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read values into memory
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void read(T* v) const {
|
void read(T* v) const {
|
||||||
if(!(*this >> *v))
|
if(!(*this >> *v))
|
||||||
|
@ -120,8 +91,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool readAt(const std::string& childTag, T* v) const {
|
bool readAt(const std::string& ctag, T* v) const {
|
||||||
if(Node* node = at(childTag)) {
|
if(Node* node = at(ctag)) {
|
||||||
node->read<T>(v);
|
node->read<T>(v);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -137,102 +108,114 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read values
|
// read returning the result
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T read() const {
|
T read() const { T v; read<T>(&v); return v;}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T readAt(const std::string& ctag) const {
|
||||||
T v;
|
T v;
|
||||||
read<T>(&v);
|
if(!readAt(ctag, &v))
|
||||||
|
throw Exception(generateErrorMessage("child node " + ctag + " not found"));
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T readAt(const std::string& childTag) const {
|
|
||||||
if(Node* node = at(childTag))
|
|
||||||
return node->read<T>();
|
|
||||||
throw Exception(generateErrorMessage("child node " + childTag + " not found"));
|
|
||||||
return T();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T readAt(int pos) const {
|
T readAt(int pos) const {
|
||||||
if(Node* node = at(pos))
|
T v;
|
||||||
return node->read<T>();
|
if(!readAt(pos, &v))
|
||||||
throw Exception(generateErrorMessage("child node at pos " + fml_int2str(pos) + " not found"));
|
throw Exception(generateErrorMessage("child node at pos " + fml_tostr(pos) + " not found"));
|
||||||
return T();
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read values with defaults
|
// read with default supplied
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T readAt(const std::string& childTag, const T& def) const {
|
T readAt(const std::string& ctag, const T& def) const { Node* c = at(ctag); return (c ? c->read<T>() : def); }
|
||||||
if(Node* node = at(childTag))
|
|
||||||
return node->read<T>();
|
template <typename T>
|
||||||
else
|
T readAt(int pos, const T& def) const { Node* c = at(pos); return (c ? c->read<T>() : def); }
|
||||||
return def;
|
|
||||||
|
// writing
|
||||||
|
template <typename T>
|
||||||
|
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 <typename T>
|
template <typename T>
|
||||||
T readAt(int pos, const T& def) const {
|
void writeIn(int pos, T v) {
|
||||||
if(Node* node = at(pos))
|
Node* c;
|
||||||
return node->read<T>();
|
while(!at(pos))
|
||||||
else
|
c = createNode();
|
||||||
return def;
|
c->write<T>(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addNode(Node* node);
|
template <typename T>
|
||||||
|
void writeIn(const std::string& ctag, T v) {
|
||||||
std::string emitValue();
|
Node* c = createNode(ctag);
|
||||||
std::string emit(int depth = 0);
|
c->write<T>(v);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Parser* m_parser;
|
|
||||||
Node* m_parent;
|
Node* m_parent;
|
||||||
int m_line;
|
int m_line;
|
||||||
|
std::string m_what;
|
||||||
NodeList m_children;
|
NodeList m_children;
|
||||||
std::string m_tag;
|
std::string m_tag;
|
||||||
std::string m_value;
|
std::string m_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// read operators
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
template <typename T>
|
||||||
// Node operators
|
bool operator >> (const Node& node, T& v) { return fml_convert(node.value(), v); }
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool operator >> (const Node& node, T& v)
|
bool operator >> (const Node& node, std::vector<T>& v) {
|
||||||
{
|
v.resize(node.size());
|
||||||
return fml_convert(node.value(), v);
|
for(unsigned i=0;i<node.size();++i)
|
||||||
|
v[i] = node.readAt<T>(i);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool operator >> (const Node& node, std::vector<T>& v)
|
bool operator >> (const Node& node, std::list<T>& v) {
|
||||||
{
|
|
||||||
std::vector<T>& tmp;
|
|
||||||
tmp.resize(node.size());
|
|
||||||
for(unsigned i=0;i<node.size();++i)
|
for(unsigned i=0;i<node.size();++i)
|
||||||
v[i] = node.readAt<T>(i);
|
v.push_back(node.readAt<T>(i));
|
||||||
v = tmp;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename K, typename T>
|
template <typename K, typename T>
|
||||||
bool operator >> (const Node& node, std::map<K, T>& m)
|
bool operator >> (const Node& node, std::map<K, T>& m) {
|
||||||
{
|
for(int i=0;i<node.size();++i)
|
||||||
std::map<K, T> tmp;
|
m[node.at(i)->read<K>()] = node.at(i)->read<T>();
|
||||||
for(Node::const_iterator it = node.begin(); it != node.end(); ++it) {
|
|
||||||
K k;
|
|
||||||
T v;
|
|
||||||
if(fml_convert<std::string, K>((*it)->tag(), k)) {
|
|
||||||
(*it)->read(&v);
|
|
||||||
tmp[k] = v;
|
|
||||||
} else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m = tmp;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write operators
|
||||||
|
template <typename T>
|
||||||
|
bool operator << (Node& node, const T& v) { node.setValue(fml_tostr(v)); return true; }
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
template <typename T>
|
||||||
// Parser
|
bool operator << (Node& node, const std::vector<T>& v) {
|
||||||
|
for(unsigned i=0;i<v.size();++i)
|
||||||
|
node.createNode()->write(v[i]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool operator << (Node& node, const std::list<T>& v) {
|
||||||
|
for(unsigned i=0;i<v.size();++i)
|
||||||
|
node.createNode()->write(v[i]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K, typename T>
|
||||||
|
bool operator << (Node& node, const std::map<K, T>& m) {
|
||||||
|
typename std::map<K, T>::const_iterator it;
|
||||||
|
for(it = m.begin(); it != m.end(); ++it)
|
||||||
|
node.createNode(fml_tostr(it->first))->write(it->second);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
class Parser
|
class Parser
|
||||||
{
|
{
|
||||||
|
@ -246,24 +229,24 @@ class Parser
|
||||||
public:
|
public:
|
||||||
Parser(std::string what = std::string()) : m_rootNode(0), m_what(what) { }
|
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(std::istream& in, std::string what = std::string()) : m_rootNode(0), m_what(what) { load(in); }
|
||||||
~Parser();
|
~Parser() { if(m_rootNode) delete m_rootNode; }
|
||||||
|
|
||||||
void load(std::istream& in);
|
|
||||||
|
|
||||||
Node* getDocument() const { return m_rootNode; }
|
Node* getDocument() const { return m_rootNode; }
|
||||||
std::string what() { return m_what; }
|
std::string what() { return m_what; }
|
||||||
|
|
||||||
|
void load(std::istream& in);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void parseLine(std::string& line);
|
void parseLine(std::string& line);
|
||||||
Node* parseNode(std::string& line);
|
void parseNode(Node* node, std::string& line);
|
||||||
std::string parseTextScalar(std::string value);
|
void parseNodeValue(Node* node, std::string& value);
|
||||||
|
std::string parseTextValue(std::string value);
|
||||||
|
|
||||||
void startMultilining(const std::string& param);
|
|
||||||
void stopMultilining();
|
void stopMultilining();
|
||||||
bool parseMultiline(std::string line);
|
bool parseMultiline(std::string line);
|
||||||
bool isMultilining() { return m_multilineMode != DONT_MULTILINE; }
|
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:
|
private:
|
||||||
int m_currentDepth;
|
int m_currentDepth;
|
||||||
|
@ -276,6 +259,22 @@ private:
|
||||||
std::string m_what;
|
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 {
|
} // namespace FML {
|
||||||
|
|
||||||
#endif // FML_H
|
#endif // __FML_H
|
||||||
|
|
Loading…
Reference in New Issue