mirror of
https://github.com/Relintai/rcpp_framework.git
synced 2025-02-20 15:14:26 +01: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 = env_base.Clone()
|
||||
|
||||
Export("env")
|
||||
|
||||
SConscript("libs/bbcpp/SCsub")
|
||||
|
||||
SConscript("core/SCsub")
|
||||
|
||||
for d in database_list:
|
||||
|
@ -1,3 +1,4 @@
|
||||
RapidJSON 0ccdbf364c577803e2a751f5aededce935314313
|
||||
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