mirror of
https://github.com/Relintai/rcpp_framework.git
synced 2025-05-06 17:51:36 +02:00
Added bbcpp.
This commit is contained in:
parent
ad31e648ed
commit
67a3ae801a
@ -138,7 +138,11 @@ env_base.Append(CXX=["-o3"])
|
|||||||
#env_base.Append(CXX=["-g2"])
|
#env_base.Append(CXX=["-g2"])
|
||||||
|
|
||||||
env = env_base.Clone()
|
env = env_base.Clone()
|
||||||
|
|
||||||
Export("env")
|
Export("env")
|
||||||
|
|
||||||
|
SConscript("libs/bbcpp/SCsub")
|
||||||
|
|
||||||
SConscript("core/SCsub")
|
SConscript("core/SCsub")
|
||||||
|
|
||||||
for d in database_list:
|
for d in database_list:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
RapidJSON 0ccdbf364c577803e2a751f5aededce935314313
|
RapidJSON 0ccdbf364c577803e2a751f5aededce935314313
|
||||||
brynet b0d13e7419628d0f7051a2bb310daaf8a506e08b
|
brynet b0d13e7419628d0f7051a2bb310daaf8a506e08b
|
||||||
rapidxml 1.13
|
rapidxml 1.13
|
||||||
|
bbcpp a035c4942ed9e5277833fe80e444406f959c3d88
|
114
libs/bbcpp/BBDocument.cpp
Executable file
114
libs/bbcpp/BBDocument.cpp
Executable file
@ -0,0 +1,114 @@
|
|||||||
|
#include <cstring>
|
||||||
|
#include <cctype>
|
||||||
|
#include "BBDocument.h"
|
||||||
|
|
||||||
|
namespace bbcpp
|
||||||
|
{
|
||||||
|
|
||||||
|
BBNode::BBNode(NodeType nodeType, const std::string& name)
|
||||||
|
: _name(name), _nodeType(nodeType)
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
BBText &BBDocument::newText(const std::string &text)
|
||||||
|
{
|
||||||
|
// first try to append this text to the item on top of the stack
|
||||||
|
// if that is a BBText object, if not, then see if the last element
|
||||||
|
// pushed to BBDocument is a text item, and if so append this to that
|
||||||
|
// text
|
||||||
|
if (_stack.size() > 0 && _stack.top()->getChildren().size() > 0)
|
||||||
|
{
|
||||||
|
auto totalChildCnt = _stack.top()->getChildren().size();
|
||||||
|
auto textnode = _stack.top()->getChildren().at(totalChildCnt - 1)->downCast<BBTextPtr>(false);
|
||||||
|
if (textnode)
|
||||||
|
{
|
||||||
|
textnode->append(text);
|
||||||
|
return *textnode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_children.size() > 0)
|
||||||
|
{
|
||||||
|
auto textnode = _children.back()->downCast<BBTextPtr>(false);
|
||||||
|
if (textnode)
|
||||||
|
{
|
||||||
|
textnode->append(text);
|
||||||
|
return *textnode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ok, there was no previous text element so we wil either add this text
|
||||||
|
// element as a child of the top item OR we'll add it to the BBDocucment
|
||||||
|
// object
|
||||||
|
auto textNode = std::make_shared<BBText>(text);
|
||||||
|
if (_stack.size() > 0)
|
||||||
|
{
|
||||||
|
_stack.top()->appendChild(textNode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// add this node to the document-node if needed
|
||||||
|
appendChild(textNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *textNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
BBElement& BBDocument::newElement(const std::string &name)
|
||||||
|
{
|
||||||
|
auto newNode = std::make_shared<BBElement>(name);
|
||||||
|
if (_stack.size() > 0)
|
||||||
|
{
|
||||||
|
_stack.top()->appendChild(newNode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// add this node to the document-node if needed
|
||||||
|
appendChild(newNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
_stack.push(newNode);
|
||||||
|
return *newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
BBElement& BBDocument::newClosingElement(const std::string& name)
|
||||||
|
{
|
||||||
|
auto newNode = std::make_shared<BBElement>(name, BBElement::CLOSING);
|
||||||
|
if (_stack.size() > 0)
|
||||||
|
{
|
||||||
|
_stack.top()->appendChild(newNode);
|
||||||
|
_stack.pop();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendChild(newNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
BBElement& BBDocument::newKeyValueElement(const std::string& name, const ParameterMap& pairs)
|
||||||
|
{
|
||||||
|
auto newNode = std::make_shared<BBElement>(name, BBElement::PARAMETER);
|
||||||
|
if (_stack.size() > 0)
|
||||||
|
{
|
||||||
|
_stack.top()->appendChild(newNode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// add this node to the document-node if needed
|
||||||
|
appendChild(newNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& kv : pairs)
|
||||||
|
{
|
||||||
|
newNode->setOrAddParameter(kv.first, kv.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
_stack.push(newNode);
|
||||||
|
return *newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace
|
605
libs/bbcpp/BBDocument.h
Executable file
605
libs/bbcpp/BBDocument.h
Executable file
@ -0,0 +1,605 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <stack>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <iterator>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace bbcpp
|
||||||
|
{
|
||||||
|
|
||||||
|
inline bool IsDigit(char c)
|
||||||
|
{
|
||||||
|
return ('0' <= c && c <= '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsAlpha(char c)
|
||||||
|
{
|
||||||
|
static const char alpha[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
return (std::strchr(alpha, c) != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsAlNum(char c)
|
||||||
|
{
|
||||||
|
return IsAlpha(c) || IsDigit(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsSpace(char c)
|
||||||
|
{
|
||||||
|
return std::isspace(static_cast<unsigned char>(c)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BBNode;
|
||||||
|
class BBText;
|
||||||
|
class BBElement;
|
||||||
|
class BBDocument;
|
||||||
|
|
||||||
|
using BBNodePtr = std::shared_ptr<BBNode>;
|
||||||
|
using BBTextPtr = std::shared_ptr<BBText>;
|
||||||
|
using BBElementPtr = std::shared_ptr<BBElement>;
|
||||||
|
|
||||||
|
using BBNodeWeakPtr = std::weak_ptr<BBNode>;
|
||||||
|
using BBNodeList = std::vector<BBNodePtr>;
|
||||||
|
using BBNodeStack = std::stack<BBNodePtr>;
|
||||||
|
using BBDocumentPtr = std::shared_ptr<BBDocument>;
|
||||||
|
|
||||||
|
using ParameterMap = std::map<std::string, std::string>;
|
||||||
|
|
||||||
|
class BBNode : public std::enable_shared_from_this<BBNode>
|
||||||
|
{
|
||||||
|
template<typename NewTypePtrT>
|
||||||
|
NewTypePtrT cast(BBNodePtr node, bool bThrowOnFail)
|
||||||
|
{
|
||||||
|
if (node == nullptr && !bThrowOnFail)
|
||||||
|
{
|
||||||
|
return NewTypePtrT();
|
||||||
|
}
|
||||||
|
else if (node == nullptr)
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("Cannot downcast BBNode, object is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
NewTypePtrT newobj = std::dynamic_pointer_cast<typename NewTypePtrT::element_type, BBNode>(node);
|
||||||
|
|
||||||
|
if (newobj == nullptr && bThrowOnFail)
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("Cannot downcast, object is not correct type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return newobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename NewTypePtrT>
|
||||||
|
NewTypePtrT cast(BBNodePtr node, bool bThrowOnFail) const
|
||||||
|
{
|
||||||
|
if (node == nullptr && !bThrowOnFail)
|
||||||
|
{
|
||||||
|
return NewTypePtrT();
|
||||||
|
}
|
||||||
|
else if (node == nullptr)
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("Cannot downcast, BBNode object is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
NewTypePtrT newobj = std::dynamic_pointer_cast<typename NewTypePtrT::element_type, BBNode>(node);
|
||||||
|
|
||||||
|
if (newobj == nullptr && bThrowOnFail)
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("Cannot downcast, object is not correct type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return newobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class NodeType
|
||||||
|
{
|
||||||
|
DOCUMENT,
|
||||||
|
ELEMENT, // [b]bold[/b], [QUOTE], [QUOTE=Username;1234], [QUOTE user=Bob]
|
||||||
|
TEXT, // plain text
|
||||||
|
ATTRIBUTE
|
||||||
|
};
|
||||||
|
|
||||||
|
BBNode(NodeType nodeType, const std::string& name);
|
||||||
|
virtual ~BBNode() = default;
|
||||||
|
|
||||||
|
const std::string& getNodeName() const { return _name; }
|
||||||
|
NodeType getNodeType() const { return _nodeType; }
|
||||||
|
BBNodePtr getParent() const { return BBNodePtr(_parent); }
|
||||||
|
|
||||||
|
const BBNodeList& getChildren() const { return _children; }
|
||||||
|
|
||||||
|
virtual void appendChild(BBNodePtr node)
|
||||||
|
{
|
||||||
|
_children.push_back(node);
|
||||||
|
node->_parent = shared_from_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename NewTypePtrT>
|
||||||
|
NewTypePtrT downCast(bool bThrowOnFail = true)
|
||||||
|
{
|
||||||
|
return cast<NewTypePtrT>(shared_from_this(), bThrowOnFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename NewTypePtrT>
|
||||||
|
NewTypePtrT downCast(bool bThrowOnFail = true) const
|
||||||
|
{
|
||||||
|
return cast<NewTypePtrT>(shared_from_this(), bThrowOnFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string _name;
|
||||||
|
NodeType _nodeType;
|
||||||
|
BBNodeWeakPtr _parent;
|
||||||
|
BBNodeList _children;
|
||||||
|
|
||||||
|
friend class BBText;
|
||||||
|
friend class BBDocument;
|
||||||
|
friend class BBElement;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BBText : public BBNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BBText(const std::string& value)
|
||||||
|
: BBNode(BBNode::NodeType::TEXT, value)
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~BBText() = default;
|
||||||
|
|
||||||
|
virtual const std::string getText() const { return _name; }
|
||||||
|
|
||||||
|
void append(const std::string& text)
|
||||||
|
{
|
||||||
|
_name.append(text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BBElement : public BBNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ElementType
|
||||||
|
{
|
||||||
|
SIMPLE, // [b]bold[/b], [code]print("hello")[/code]
|
||||||
|
VALUE, // [QUOTE=Username;12345]This is a quote[/QUOTE] (mostly used by vBulletin)
|
||||||
|
PARAMETER, // [QUOTE user=Bob userid=1234]This is a quote[/QUOTE]
|
||||||
|
CLOSING // [/b], [/code]
|
||||||
|
};
|
||||||
|
|
||||||
|
BBElement(const std::string& name, ElementType et = BBElement::SIMPLE)
|
||||||
|
: BBNode(BBNode::NodeType::ELEMENT, name),
|
||||||
|
_elementType(et)
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~BBElement() = default;
|
||||||
|
|
||||||
|
const ElementType getElementType() const { return _elementType; }
|
||||||
|
|
||||||
|
void setOrAddParameter(const std::string& key, const std::string& value, bool addIfNotExists = true)
|
||||||
|
{
|
||||||
|
_parameters.insert({key,value});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getParameter(const std::string& key, bool bDoThrow = true)
|
||||||
|
{
|
||||||
|
if (_parameters.find(key) == _parameters.end() && bDoThrow)
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("Undefine attribute '" + key + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _parameters.at(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ParameterMap& getParameters() const { return _parameters; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ElementType _elementType = BBElement::SIMPLE;
|
||||||
|
ParameterMap _parameters;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BBDocument : public BBNode
|
||||||
|
{
|
||||||
|
BBDocument()
|
||||||
|
: BBNode(BBNode::NodeType::DOCUMENT, "#document")
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename citerator>
|
||||||
|
citerator parseText(citerator begin, citerator end)
|
||||||
|
{
|
||||||
|
auto endingChar = begin;
|
||||||
|
|
||||||
|
for (auto it = begin; it != end; it++)
|
||||||
|
{
|
||||||
|
if (*it == '[')
|
||||||
|
{
|
||||||
|
endingChar = it;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endingChar == begin)
|
||||||
|
{
|
||||||
|
endingChar = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
newText(std::string(begin, endingChar));
|
||||||
|
|
||||||
|
return endingChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename citerator>
|
||||||
|
citerator parseElementName(citerator begin, citerator end, std::string& buf)
|
||||||
|
{
|
||||||
|
auto start = begin;
|
||||||
|
std::stringstream str;
|
||||||
|
|
||||||
|
for (auto it = start; it != end; it++)
|
||||||
|
{
|
||||||
|
// TODO: alphanumeric names only?
|
||||||
|
if (bbcpp::IsAlNum((char)*it))
|
||||||
|
{
|
||||||
|
str << *it;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buf.assign(str.str());
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename citerator>
|
||||||
|
citerator parseValue(citerator begin, citerator end, std::string& value)
|
||||||
|
{
|
||||||
|
auto start = begin;
|
||||||
|
while (bbcpp::IsSpace(*start) && start != end)
|
||||||
|
{
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start == end)
|
||||||
|
{
|
||||||
|
// we got to the end and there was nothing but spaces
|
||||||
|
// so return our starting point so the caller can create
|
||||||
|
// a text node with those spaces
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream temp;
|
||||||
|
|
||||||
|
for (auto it = start; it != end; it++)
|
||||||
|
{
|
||||||
|
if (bbcpp::IsAlNum(*it))
|
||||||
|
{
|
||||||
|
temp << *it;
|
||||||
|
}
|
||||||
|
else if (*it == ']')
|
||||||
|
{
|
||||||
|
value.assign(temp.str());
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
else if(*it == '#')
|
||||||
|
{
|
||||||
|
//is color
|
||||||
|
temp << *it;
|
||||||
|
}
|
||||||
|
else if (*it == ':' || *it == '/' || *it == '.' || *it == '&'
|
||||||
|
|| *it == '?' || *it == '$' || *it == '-' || *it == '+'
|
||||||
|
|| *it == '*' || *it == '(' || *it == ')' || *it == ',')
|
||||||
|
{
|
||||||
|
//is url
|
||||||
|
temp << *it;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// some invalid character, so return the point where
|
||||||
|
// we stopped parsing
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we get here then we're at the end, so we return the starting
|
||||||
|
// point so the callerd can create a text node
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename citerator>
|
||||||
|
citerator parseKey(citerator begin, citerator end, std::string& keyname)
|
||||||
|
{
|
||||||
|
auto start = begin;
|
||||||
|
while (bbcpp::IsSpace(*start) && start != end)
|
||||||
|
{
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start == end)
|
||||||
|
{
|
||||||
|
// we got to the end and there was nothing but spaces
|
||||||
|
// so return our end point so the caller can create
|
||||||
|
// a text node with those spaces
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream temp;
|
||||||
|
|
||||||
|
// TODO: need to handle spaces after the key name and before
|
||||||
|
// the equal sign (ie. "[style color =red]")
|
||||||
|
for (auto it = start; it != end; it++)
|
||||||
|
{
|
||||||
|
if (bbcpp::IsAlNum(*it))
|
||||||
|
{
|
||||||
|
temp << *it;
|
||||||
|
}
|
||||||
|
else if (*it == '=')
|
||||||
|
{
|
||||||
|
keyname.assign(temp.str());
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// some invalid character, so return the point where
|
||||||
|
// we stopped parsing
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we get here then we're at the end, so we return the starting
|
||||||
|
// point so the callerd can create a text node
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename citerator>
|
||||||
|
citerator parseKeyValuePairs(citerator begin, citerator end, ParameterMap& pairs)
|
||||||
|
{
|
||||||
|
auto current = begin;
|
||||||
|
std::string tempKey;
|
||||||
|
std::string tempVal;
|
||||||
|
|
||||||
|
while (current != end)
|
||||||
|
{
|
||||||
|
current = parseKey(current, end, tempKey);
|
||||||
|
if (tempKey.empty())
|
||||||
|
{
|
||||||
|
pairs.clear();
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*current != '=')
|
||||||
|
{
|
||||||
|
pairs.clear();
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = std::next(current);
|
||||||
|
current = parseValue(current, end, tempVal);
|
||||||
|
|
||||||
|
if (tempKey.empty() || tempVal.empty())
|
||||||
|
{
|
||||||
|
pairs.clear();
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs.insert(std::make_pair(tempKey, tempVal));
|
||||||
|
if (*current == ']')
|
||||||
|
{
|
||||||
|
// this is the only valid condition for key/value pairs so we do
|
||||||
|
// not want to clear `pairs` like in the other cases
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename citerator>
|
||||||
|
citerator parseElement(citerator begin, citerator end)
|
||||||
|
{
|
||||||
|
bool closingTag = false;
|
||||||
|
|
||||||
|
// the first non-[ and non-/ character
|
||||||
|
auto nameStart = std::next(begin);
|
||||||
|
|
||||||
|
std::string elementName;
|
||||||
|
|
||||||
|
// this might be a closing tag so mark it
|
||||||
|
if (*nameStart == '/')
|
||||||
|
{
|
||||||
|
closingTag = true;
|
||||||
|
nameStart = std::next(nameStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto nameEnd = parseElementName(nameStart, end, elementName);
|
||||||
|
|
||||||
|
// no valid name was found, so bail out
|
||||||
|
if (elementName.empty())
|
||||||
|
{
|
||||||
|
newText(std::string{*begin});
|
||||||
|
return nameEnd;
|
||||||
|
}
|
||||||
|
else if (nameEnd == end)
|
||||||
|
{
|
||||||
|
newText(std::string(begin,end));
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*nameEnd == ']')
|
||||||
|
{
|
||||||
|
// end of element
|
||||||
|
}
|
||||||
|
else if (*nameEnd == '=')
|
||||||
|
{
|
||||||
|
// possibly a QUOTE value element
|
||||||
|
// possibly key-value pairs of a QUOTE
|
||||||
|
ParameterMap pairs;
|
||||||
|
|
||||||
|
auto kvEnd = parseKeyValuePairs(nameStart, end, pairs);
|
||||||
|
if (pairs.size() == 0)
|
||||||
|
{
|
||||||
|
newText(std::string(begin, kvEnd));
|
||||||
|
return kvEnd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newKeyValueElement(elementName, pairs);
|
||||||
|
// TODO: add 'pairs'
|
||||||
|
return std::next(kvEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*nameEnd == ' ')
|
||||||
|
{
|
||||||
|
// possibly key-value pairs of a QUOTE
|
||||||
|
ParameterMap pairs;
|
||||||
|
|
||||||
|
auto kvEnd = parseKeyValuePairs(nameEnd, end, pairs);
|
||||||
|
if (pairs.size() == 0)
|
||||||
|
{
|
||||||
|
newText(std::string(begin, kvEnd));
|
||||||
|
return kvEnd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newKeyValueElement(elementName, pairs);
|
||||||
|
// TODO: add 'pairs'
|
||||||
|
return std::next(kvEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// some invalid char proceeded the element name, so it's not actually a
|
||||||
|
// valid element, so create it as text and move on
|
||||||
|
newText(std::string(begin,nameEnd));
|
||||||
|
return nameEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closingTag)
|
||||||
|
{
|
||||||
|
newClosingElement(elementName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newElement(elementName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::next(nameEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static BBDocumentPtr create()
|
||||||
|
{
|
||||||
|
BBDocumentPtr doc = BBDocumentPtr(new BBDocument());
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load(const std::string& bbcode)
|
||||||
|
{
|
||||||
|
load(bbcode.begin(), bbcode.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Iterator>
|
||||||
|
void load(Iterator begin, Iterator end)
|
||||||
|
{
|
||||||
|
std::string buffer;
|
||||||
|
auto bUnknownNodeType = true;
|
||||||
|
auto current = begin;
|
||||||
|
auto nodeType = BBNode::NodeType::TEXT;
|
||||||
|
|
||||||
|
Iterator temp;
|
||||||
|
|
||||||
|
while (current != end)
|
||||||
|
{
|
||||||
|
if (bUnknownNodeType)
|
||||||
|
{
|
||||||
|
if (*current == '[')
|
||||||
|
{
|
||||||
|
nodeType = BBNode::NodeType::ELEMENT;
|
||||||
|
bUnknownNodeType = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodeType = BBNode::NodeType::TEXT;
|
||||||
|
bUnknownNodeType = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bUnknownNodeType)
|
||||||
|
{
|
||||||
|
switch (nodeType)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unknown node type in BBDocument::load()");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BBNode::NodeType::TEXT:
|
||||||
|
{
|
||||||
|
current = parseText(current, end);
|
||||||
|
bUnknownNodeType = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BBNode::NodeType::ELEMENT:
|
||||||
|
{
|
||||||
|
temp = parseElement(current, end);
|
||||||
|
if (temp == current)
|
||||||
|
{
|
||||||
|
// nothing was parsed, treat as text
|
||||||
|
nodeType = BBNode::NodeType::TEXT;
|
||||||
|
bUnknownNodeType = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current = temp;
|
||||||
|
bUnknownNodeType = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BBNodeStack _stack;
|
||||||
|
|
||||||
|
BBText& newText(const std::string& text = std::string());
|
||||||
|
BBElement& newElement(const std::string& name);
|
||||||
|
BBElement& newClosingElement(const std::string& name);
|
||||||
|
BBElement& newKeyValueElement(const std::string& name, const ParameterMap& pairs);
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const ParameterMap& params)
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
os << "{ ";
|
||||||
|
for (auto& p : params)
|
||||||
|
{
|
||||||
|
os << (first ? "" : ", ") << "{" << p.first << "=" << p.second << "}";
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (os << " }");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace
|
21
libs/bbcpp/LICENSE
Normal file
21
libs/bbcpp/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
105
libs/bbcpp/README.md
Normal file
105
libs/bbcpp/README.md
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# bbcpp
|
||||||
|
|
||||||
|
[![Build Status][travis-img]][travis]
|
||||||
|
[![Build Status][appveyor-img]][appveyor]
|
||||||
|
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
bbcpp is a C++ library for parsing BBCode, or Bulletin Board Code, a markup language used to format posts in many message boards.
|
||||||
|
|
||||||
|
This library parses BBCode into a tree data structure that can be used to format output. However, this library does not include any output classes, though a basic HTML output class will likely be included.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto doc = BBDocument::create();
|
||||||
|
doc->load("This is [b]an example[/b] of some text.");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Element Types
|
||||||
|
|
||||||
|
#### Examples:
|
||||||
|
[B] - Bold text
|
||||||
|
[I] - Italicized text
|
||||||
|
[QUOTE] - Blockquote text (without specifiers as discussed below)
|
||||||
|
|
||||||
|
## Value Elements
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
[COLOR="green"]
|
||||||
|
[FONT="Arial Narrow"]
|
||||||
|
[SIZE="5"]
|
||||||
|
[EMAIL="billgates@microsoft.com"]
|
||||||
|
|
||||||
|
## The `QUOTE` Element
|
||||||
|
|
||||||
|
The **bbcpp** parser will accept three different formats for the `QUOTE` tag:
|
||||||
|
|
||||||
|
1. `[QUOTE user=Username postid=1234]`: A key-value pair of values. In theory they are space delimited unless quoted. (Used with phpBB)
|
||||||
|
1. `[QUOTE="username, post: 1799684, member: 11733"]`: Another key-value pair format except the first argument is assumed to be the username. (Used with XenForo)
|
||||||
|
1. `[QUOTE=Username;1234]`: `Username` is the name of the user being quoted and `1234` is the postid. (Used with vBulletin)
|
||||||
|
|
||||||
|
### `FONT`
|
||||||
|
|
||||||
|
### `COLOR`
|
||||||
|
|
||||||
|
### `LIST`/`[*]`
|
||||||
|
|
||||||
|
### `IMG`
|
||||||
|
|
||||||
|
### `URL`
|
||||||
|
|
||||||
|
## BBNode Tree
|
||||||
|
|
||||||
|
The following are examples of the node tree built during parsing.
|
||||||
|
|
||||||
|
#### Example 1
|
||||||
|
|
||||||
|
> `This is [b]an example[/b] of some text`
|
||||||
|
|
||||||
|
```
|
||||||
|
#document
|
||||||
|
│-- @"This is"
|
||||||
|
│-- [b]
|
||||||
|
│ │-- @"an example"
|
||||||
|
│ │-- [/b]
|
||||||
|
│-- @"of some text"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 2
|
||||||
|
|
||||||
|
> `[QUOTE]This is [b]important[/b] news![/QUOTE]` <br/><br/>
|
||||||
|
> `Indeed it is!`
|
||||||
|
|
||||||
|
```
|
||||||
|
#document
|
||||||
|
│-- [QUOTE]
|
||||||
|
│ │-- @"This is "
|
||||||
|
│ │-- [b]
|
||||||
|
| | |-- @"important"
|
||||||
|
| | |-- [/b]
|
||||||
|
│ │-- @"news!"
|
||||||
|
│ │-- [/QUOTE]
|
||||||
|
│-- @"\n\nIndeed it is!"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 3
|
||||||
|
> `[QUOTE user=Joe userid=1 postid=1234]This is another quote![/QUOTE]`<br/><br/>
|
||||||
|
> `I'm quoting you!`
|
||||||
|
|
||||||
|
```
|
||||||
|
#document
|
||||||
|
│-- [QUOTE]
|
||||||
|
| |-- {user=Joe}
|
||||||
|
| |-- {userid=1}
|
||||||
|
| |-- {postid=1234}
|
||||||
|
│ │-- @"This is another quote!"
|
||||||
|
│ │-- [/QUOTE]
|
||||||
|
│-- @"\n\nI'm quoting you!"
|
||||||
|
```
|
||||||
|
[travis-img]: https://travis-ci.org/zethon/bbcpp.svg?branch=master
|
||||||
|
[travis]: https://travis-ci.org/zethon/bbcpp
|
||||||
|
|
||||||
|
[appveyor-img]: https://ci.appveyor.com/api/projects/status/i7p4q2d0vvoyv8aq?svg=true
|
||||||
|
[appveyor]: https://ci.appveyor.com/project/zethon/bbcpp
|
11
libs/bbcpp/SCsub
Normal file
11
libs/bbcpp/SCsub
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
core_sources = []
|
||||||
|
|
||||||
|
env.add_source_files(core_sources, "*.cpp")
|
||||||
|
|
||||||
|
# Build it all as a library
|
||||||
|
lib = env.add_library("lib_bbcpp", core_sources)
|
||||||
|
env.Prepend(LIBS=[lib])
|
131
libs/bbcpp/bbcpputils.cpp
Normal file
131
libs/bbcpp/bbcpputils.cpp
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#include "BBDocument.h"
|
||||||
|
#include "bbcpputils.h"
|
||||||
|
|
||||||
|
namespace bbcpp
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string nodeTypeToString(BBNode::NodeType type)
|
||||||
|
{
|
||||||
|
std::string retval = "Unknown";
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case BBNode::NodeType::DOCUMENT:
|
||||||
|
retval = "Document";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BBNode::NodeType::ELEMENT:
|
||||||
|
retval = "Element";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BBNode::NodeType::TEXT:
|
||||||
|
retval = "Text";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper Functions
|
||||||
|
std::string getIndentString(const unsigned int indent)
|
||||||
|
{
|
||||||
|
std::stringstream output;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < indent; i++)
|
||||||
|
{
|
||||||
|
output << "| ";
|
||||||
|
}
|
||||||
|
|
||||||
|
output << "|-- ";
|
||||||
|
return output.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void printChildren(const BBNode& parent, unsigned int indent)
|
||||||
|
{
|
||||||
|
for (const auto node : parent.getChildren())
|
||||||
|
{
|
||||||
|
switch (node->getNodeType())
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BBNode::NodeType::ELEMENT:
|
||||||
|
{
|
||||||
|
const auto element = node->downCast<BBElementPtr>();
|
||||||
|
std::cout
|
||||||
|
<< getIndentString(indent)
|
||||||
|
<< "["
|
||||||
|
<< (element->getElementType() == BBElement::CLOSING ? "/" : "")
|
||||||
|
<< element->getNodeName() << "]"
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
if (element->getElementType() == BBElement::PARAMETER)
|
||||||
|
{
|
||||||
|
std::cout
|
||||||
|
<< getIndentString(indent + 1)
|
||||||
|
<< element->getParameters()
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BBNode::NodeType::TEXT:
|
||||||
|
{
|
||||||
|
const auto textnode = node->downCast<BBTextPtr>();
|
||||||
|
std::cout << getIndentString(indent)
|
||||||
|
<< "@\"" << textnode->getText() << "\""
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
printChildren(*node, indent+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void printDocument(const BBDocument& doc)
|
||||||
|
{
|
||||||
|
std::cout << "#document" << std::endl;
|
||||||
|
|
||||||
|
auto indent = 0u;
|
||||||
|
printChildren(doc, indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getRawString(const BBNode& parent)
|
||||||
|
{
|
||||||
|
std::string root = "";
|
||||||
|
for (const auto node : parent.getChildren())
|
||||||
|
{
|
||||||
|
switch (node->getNodeType())
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BBNode::NodeType::ELEMENT:
|
||||||
|
{
|
||||||
|
const auto element = node->downCast<BBElementPtr>();
|
||||||
|
|
||||||
|
if (element->getElementType() == BBElement::PARAMETER)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BBNode::NodeType::TEXT:
|
||||||
|
{
|
||||||
|
const auto textnode = node->downCast<BBTextPtr>();
|
||||||
|
root += textnode->getText();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
root += getRawString(*node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
15
libs/bbcpp/bbcpputils.h
Normal file
15
libs/bbcpp/bbcpputils.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BBDocument.h"
|
||||||
|
|
||||||
|
namespace bbcpp
|
||||||
|
{
|
||||||
|
|
||||||
|
// Helper Functions
|
||||||
|
std::string nodeTypeToString(BBNode::NodeType type);
|
||||||
|
std::string getIndentString(const unsigned int indent);
|
||||||
|
void printChildren(const BBNode& parent, unsigned int indent);
|
||||||
|
void printDocument(const BBDocument& doc);
|
||||||
|
std::string getRawString(const BBNode& node);
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user