using maddy for markdown parsing
This commit is contained in:
192
maddy/blockparser.h
Normal file
192
maddy/blockparser.h
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
// windows compatibility includes
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* BlockParser
|
||||
*
|
||||
* The code expects every child to have the following static function to be
|
||||
* implemented:
|
||||
* `static bool IsStartingLine(const std::string& line)`
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class BlockParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* ctor
|
||||
*
|
||||
* @method
|
||||
* @param {std::function<void(std::string&)>} parseLineCallback
|
||||
* @param {std::function<std::shared_ptr<BlockParser>(const std::string&
|
||||
* line)>} getBlockParserForLineCallback
|
||||
*/
|
||||
BlockParser(
|
||||
std::function<void(std::string&)> parseLineCallback,
|
||||
std::function<std::shared_ptr<BlockParser>(const std::string& line)>
|
||||
getBlockParserForLineCallback
|
||||
)
|
||||
: result("", std::ios_base::ate | std::ios_base::in | std::ios_base::out)
|
||||
, childParser(nullptr)
|
||||
, parseLineCallback(parseLineCallback)
|
||||
, getBlockParserForLineCallback(getBlockParserForLineCallback)
|
||||
{}
|
||||
|
||||
/**
|
||||
* dtor
|
||||
*
|
||||
* @method
|
||||
*/
|
||||
virtual ~BlockParser() {}
|
||||
|
||||
/**
|
||||
* AddLine
|
||||
*
|
||||
* Adding a line which has to be parsed.
|
||||
*
|
||||
* @method
|
||||
* @param {std::string&} line
|
||||
* @return {void}
|
||||
*/
|
||||
virtual void AddLine(std::string& line)
|
||||
{
|
||||
this->parseBlock(line);
|
||||
|
||||
if (this->isInlineBlockAllowed() && !this->childParser)
|
||||
{
|
||||
this->childParser = this->getBlockParserForLine(line);
|
||||
}
|
||||
|
||||
if (this->childParser)
|
||||
{
|
||||
this->childParser->AddLine(line);
|
||||
|
||||
if (this->childParser->IsFinished())
|
||||
{
|
||||
this->result << this->childParser->GetResult().str();
|
||||
this->childParser = nullptr;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->isLineParserAllowed())
|
||||
{
|
||||
this->parseLine(line);
|
||||
}
|
||||
|
||||
this->result << line;
|
||||
}
|
||||
|
||||
/**
|
||||
* IsFinished
|
||||
*
|
||||
* Check if the BlockParser is done
|
||||
*
|
||||
* @method
|
||||
* @return {bool}
|
||||
*/
|
||||
virtual bool IsFinished() const = 0;
|
||||
|
||||
/**
|
||||
* GetResult
|
||||
*
|
||||
* Get the parsed HTML output.
|
||||
*
|
||||
* @method
|
||||
* @return {std::stringstream}
|
||||
*/
|
||||
std::stringstream& GetResult() { return this->result; }
|
||||
|
||||
/**
|
||||
* Clear
|
||||
*
|
||||
* Clear the result to reuse the parser object.
|
||||
*
|
||||
* It is only used by one test for now.
|
||||
*
|
||||
* @method
|
||||
* @return {void}
|
||||
*/
|
||||
void Clear() { this->result.str(""); }
|
||||
|
||||
protected:
|
||||
std::stringstream result;
|
||||
std::shared_ptr<BlockParser> childParser;
|
||||
|
||||
virtual bool isInlineBlockAllowed() const = 0;
|
||||
virtual bool isLineParserAllowed() const = 0;
|
||||
virtual void parseBlock(std::string& line) = 0;
|
||||
|
||||
void parseLine(std::string& line)
|
||||
{
|
||||
if (parseLineCallback)
|
||||
{
|
||||
parseLineCallback(line);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t getIndentationWidth(const std::string& line) const
|
||||
{
|
||||
bool hasMetNonSpace = false;
|
||||
|
||||
uint32_t indentation = static_cast<uint32_t>(std::count_if(
|
||||
line.begin(),
|
||||
line.end(),
|
||||
[&hasMetNonSpace](unsigned char c)
|
||||
{
|
||||
if (hasMetNonSpace)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::isspace(c))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
hasMetNonSpace = true;
|
||||
return false;
|
||||
}
|
||||
));
|
||||
|
||||
return indentation;
|
||||
}
|
||||
|
||||
std::shared_ptr<BlockParser> getBlockParserForLine(const std::string& line)
|
||||
{
|
||||
if (getBlockParserForLineCallback)
|
||||
{
|
||||
return getBlockParserForLineCallback(line);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(std::string&)> parseLineCallback;
|
||||
std::function<std::shared_ptr<BlockParser>(const std::string& line)>
|
||||
getBlockParserForLineCallback;
|
||||
}; // class BlockParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
50
maddy/breaklineparser.h
Normal file
50
maddy/breaklineparser.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/lineparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* BreakLineParser
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class BreakLineParser : public LineParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* From Markdown: `text\r\n text`
|
||||
*
|
||||
* To HTML: `text<br> text`
|
||||
*
|
||||
* @method
|
||||
* @param {std::string&} line The line to interpret
|
||||
* @return {void}
|
||||
*/
|
||||
void Parse(std::string& line) override
|
||||
{
|
||||
static std::regex re(R"((\r\n|\r))");
|
||||
static std::string replacement = "<br>";
|
||||
|
||||
line = std::regex_replace(line, re, replacement);
|
||||
}
|
||||
}; // class BreakLineParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
127
maddy/checklistparser.h
Normal file
127
maddy/checklistparser.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/blockparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ChecklistParser
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class ChecklistParser : public BlockParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* ctor
|
||||
*
|
||||
* @method
|
||||
* @param {std::function<void(std::string&)>} parseLineCallback
|
||||
* @param {std::function<std::shared_ptr<BlockParser>(const std::string&
|
||||
* line)>} getBlockParserForLineCallback
|
||||
*/
|
||||
ChecklistParser(
|
||||
std::function<void(std::string&)> parseLineCallback,
|
||||
std::function<std::shared_ptr<BlockParser>(const std::string& line)>
|
||||
getBlockParserForLineCallback
|
||||
)
|
||||
: BlockParser(parseLineCallback, getBlockParserForLineCallback)
|
||||
, isStarted(false)
|
||||
, isFinished(false)
|
||||
{}
|
||||
|
||||
/**
|
||||
* IsStartingLine
|
||||
*
|
||||
* An unordered list starts with `* `.
|
||||
*
|
||||
* @method
|
||||
* @param {const std::string&} line
|
||||
* @return {bool}
|
||||
*/
|
||||
static bool IsStartingLine(const std::string& line)
|
||||
{
|
||||
static std::regex re(R"(^- \[[x| ]\] .*)");
|
||||
return std::regex_match(line, re);
|
||||
}
|
||||
|
||||
/**
|
||||
* IsFinished
|
||||
*
|
||||
* @method
|
||||
* @return {bool}
|
||||
*/
|
||||
bool IsFinished() const override { return this->isFinished; }
|
||||
|
||||
protected:
|
||||
bool isInlineBlockAllowed() const override { return true; }
|
||||
|
||||
bool isLineParserAllowed() const override { return true; }
|
||||
|
||||
void parseBlock(std::string& line) override
|
||||
{
|
||||
bool isStartOfNewListItem = IsStartingLine(line);
|
||||
uint32_t indentation = getIndentationWidth(line);
|
||||
|
||||
static std::regex lineRegex("^(- )");
|
||||
line = std::regex_replace(line, lineRegex, "");
|
||||
|
||||
static std::regex emptyBoxRegex(R"(^\[ \])");
|
||||
static std::string emptyBoxReplacement = "<input type=\"checkbox\"/>";
|
||||
line = std::regex_replace(line, emptyBoxRegex, emptyBoxReplacement);
|
||||
|
||||
static std::regex boxRegex(R"(^\[x\])");
|
||||
static std::string boxReplacement =
|
||||
"<input type=\"checkbox\" checked=\"checked\"/>";
|
||||
line = std::regex_replace(line, boxRegex, boxReplacement);
|
||||
|
||||
if (!this->isStarted)
|
||||
{
|
||||
line = "<ul class=\"checklist\"><li><label>" + line;
|
||||
this->isStarted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (indentation >= 2)
|
||||
{
|
||||
line = line.substr(2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.empty() ||
|
||||
line.find("</label></li><li><label>") != std::string::npos ||
|
||||
line.find("</label></li></ul>") != std::string::npos)
|
||||
{
|
||||
line = "</label></li></ul>" + line;
|
||||
this->isFinished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isStartOfNewListItem)
|
||||
{
|
||||
line = "</label></li><li><label>" + line;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool isStarted;
|
||||
bool isFinished;
|
||||
}; // class ChecklistParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
132
maddy/codeblockparser.h
Normal file
132
maddy/codeblockparser.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/blockparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* CodeBlockParser
|
||||
*
|
||||
* From Markdown: 3 times surrounded code (without space in the beginning)
|
||||
*
|
||||
* ```
|
||||
* ```
|
||||
* some code
|
||||
* ```
|
||||
* ```
|
||||
*
|
||||
* To HTML:
|
||||
*
|
||||
* ```
|
||||
* <pre><code>
|
||||
* some code
|
||||
* </code></pre>
|
||||
* ```
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class CodeBlockParser : public BlockParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* ctor
|
||||
*
|
||||
* @method
|
||||
* @param {std::function<void(std::string&)>} parseLineCallback
|
||||
* @param {std::function<std::shared_ptr<BlockParser>(const std::string&
|
||||
* line)>} getBlockParserForLineCallback
|
||||
*/
|
||||
CodeBlockParser(
|
||||
std::function<void(std::string&)> parseLineCallback,
|
||||
std::function<std::shared_ptr<BlockParser>(const std::string& line)>
|
||||
getBlockParserForLineCallback
|
||||
)
|
||||
: BlockParser(parseLineCallback, getBlockParserForLineCallback)
|
||||
, isStarted(false)
|
||||
, isFinished(false)
|
||||
{}
|
||||
|
||||
/**
|
||||
* IsStartingLine
|
||||
*
|
||||
* If the line starts with three code signs, then it is a code block.
|
||||
*
|
||||
* ```
|
||||
* ```
|
||||
* ```
|
||||
*
|
||||
* @method
|
||||
* @param {const std::string&} line
|
||||
* @return {bool}
|
||||
*/
|
||||
static bool IsStartingLine(const std::string& line)
|
||||
{
|
||||
static std::regex re("^(?:`){3}(.*)$");
|
||||
return std::regex_match(line, re);
|
||||
}
|
||||
|
||||
/**
|
||||
* IsFinished
|
||||
*
|
||||
* @method
|
||||
* @return {bool}
|
||||
*/
|
||||
bool IsFinished() const override { return this->isFinished; }
|
||||
|
||||
protected:
|
||||
bool isInlineBlockAllowed() const override { return false; }
|
||||
|
||||
bool isLineParserAllowed() const override { return false; }
|
||||
|
||||
void parseBlock(std::string& line) override
|
||||
{
|
||||
if (line == "```")
|
||||
{
|
||||
if (!this->isStarted)
|
||||
{
|
||||
line = "<pre><code>\n";
|
||||
this->isStarted = true;
|
||||
this->isFinished = false;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
line = "</code></pre>";
|
||||
this->isFinished = true;
|
||||
this->isStarted = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (!this->isStarted && line.substr(0, 3) == "```")
|
||||
{
|
||||
line = "<pre class=\"" + line.substr(3) + "\"><code>\n";
|
||||
this->isStarted = true;
|
||||
this->isFinished = false;
|
||||
return;
|
||||
}
|
||||
|
||||
line += "\n";
|
||||
}
|
||||
|
||||
private:
|
||||
bool isStarted;
|
||||
bool isFinished;
|
||||
}; // class CodeBlockParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
54
maddy/emphasizedparser.h
Normal file
54
maddy/emphasizedparser.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/lineparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* EmphasizedParser
|
||||
*
|
||||
* Has to be used after the `StrongParser`.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class EmphasizedParser : public LineParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* From Markdown: `text _text_`
|
||||
*
|
||||
* To HTML: `text <em>text</em>`
|
||||
*
|
||||
* @method
|
||||
* @param {std::string&} line The line to interpret
|
||||
* @return {void}
|
||||
*/
|
||||
void Parse(std::string& line) override
|
||||
{
|
||||
static std::regex re(
|
||||
R"((?!.*`.*|.*<code>.*)_(?!.*`.*|.*<\/code>.*)([^_]*)_(?!.*`.*|.*<\/code>.*))"
|
||||
);
|
||||
static std::string replacement = "<em>$1</em>";
|
||||
|
||||
line = std::regex_replace(line, re, replacement);
|
||||
}
|
||||
}; // class EmphasizedParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
134
maddy/headlineparser.h
Normal file
134
maddy/headlineparser.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/blockparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* HeadlineParser
|
||||
*
|
||||
* From Markdown:
|
||||
*
|
||||
* ```
|
||||
* # Headline 1
|
||||
* ## Headline 2
|
||||
* ### Headline 3
|
||||
* #### Headline 4
|
||||
* ##### Headline 5
|
||||
* ###### Headline 6
|
||||
* ```
|
||||
*
|
||||
* To HTML:
|
||||
*
|
||||
* ```
|
||||
* <h1>Headline 1</h1>
|
||||
* <h2>Headline 2</h2>
|
||||
* <h3>Headline 3</h3>
|
||||
* <h4>Headline 4</h4>
|
||||
* <h5>Headline 5</h5>
|
||||
* <h6>Headline 6</h6>
|
||||
* ```
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class HeadlineParser : public BlockParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* ctor
|
||||
*
|
||||
* @method
|
||||
* @param {std::function<void(std::string&)>} parseLineCallback
|
||||
* @param {std::function<std::shared_ptr<BlockParser>(const std::string&
|
||||
* line)>} getBlockParserForLineCallback
|
||||
*/
|
||||
HeadlineParser(
|
||||
std::function<void(std::string&)> parseLineCallback,
|
||||
std::function<std::shared_ptr<BlockParser>(const std::string& line)>
|
||||
getBlockParserForLineCallback,
|
||||
bool isInlineParserAllowed = true
|
||||
)
|
||||
: BlockParser(parseLineCallback, getBlockParserForLineCallback)
|
||||
, isInlineParserAllowed(isInlineParserAllowed)
|
||||
{}
|
||||
|
||||
/**
|
||||
* IsStartingLine
|
||||
*
|
||||
* If the line starts with 1 - 6 `#`, then it is a headline.
|
||||
*
|
||||
* @method
|
||||
* @param {const std::string&} line
|
||||
* @return {bool}
|
||||
*/
|
||||
static bool IsStartingLine(const std::string& line)
|
||||
{
|
||||
static std::regex re("^(?:#){1,6} (.*)");
|
||||
return std::regex_match(line, re);
|
||||
}
|
||||
|
||||
/**
|
||||
* IsFinished
|
||||
*
|
||||
* The headline is always only one line long, so this method always returns
|
||||
* true.
|
||||
*
|
||||
* @method
|
||||
* @return {bool}
|
||||
*/
|
||||
bool IsFinished() const override { return true; }
|
||||
|
||||
protected:
|
||||
bool isInlineBlockAllowed() const override { return false; }
|
||||
|
||||
bool isLineParserAllowed() const override
|
||||
{
|
||||
return this->isInlineParserAllowed;
|
||||
}
|
||||
|
||||
void parseBlock(std::string& line) override
|
||||
{
|
||||
static std::vector<std::regex> hlRegex = {
|
||||
std::regex("^# (.*)"),
|
||||
std::regex("^(?:#){2} (.*)"),
|
||||
std::regex("^(?:#){3} (.*)"),
|
||||
std::regex("^(?:#){4} (.*)"),
|
||||
std::regex("^(?:#){5} (.*)"),
|
||||
std::regex("^(?:#){6} (.*)")
|
||||
};
|
||||
static std::vector<std::string> hlReplacement = {
|
||||
"<h1>$1</h1>",
|
||||
"<h2>$1</h2>",
|
||||
"<h3>$1</h3>",
|
||||
"<h4>$1</h4>",
|
||||
"<h5>$1</h5>",
|
||||
"<h6>$1</h6>"
|
||||
};
|
||||
|
||||
for (uint8_t i = 0; i < 6; ++i)
|
||||
{
|
||||
line = std::regex_replace(line, hlRegex[i], hlReplacement[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool isInlineParserAllowed;
|
||||
}; // class HeadlineParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
94
maddy/horizontallineparser.h
Normal file
94
maddy/horizontallineparser.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/blockparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* HorizontalLineParser
|
||||
*
|
||||
* From Markdown: `---`
|
||||
*
|
||||
* To HTML: `<hr/>`
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class HorizontalLineParser : public BlockParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* ctor
|
||||
*
|
||||
* @method
|
||||
* @param {std::function<void(std::string&)>} parseLineCallback
|
||||
* @param {std::function<std::shared_ptr<BlockParser>(const std::string&
|
||||
* line)>} getBlockParserForLineCallback
|
||||
*/
|
||||
HorizontalLineParser(
|
||||
std::function<void(std::string&)> parseLineCallback,
|
||||
std::function<std::shared_ptr<BlockParser>(const std::string& line)>
|
||||
getBlockParserForLineCallback
|
||||
)
|
||||
: BlockParser(parseLineCallback, getBlockParserForLineCallback)
|
||||
, lineRegex("^---$")
|
||||
{}
|
||||
|
||||
/**
|
||||
* IsStartingLine
|
||||
*
|
||||
* If the line has exact three dashes `---`, then it is a horizontal line.
|
||||
*
|
||||
* @method
|
||||
* @param {const std::string&} line
|
||||
* @return {bool}
|
||||
*/
|
||||
static bool IsStartingLine(const std::string& line)
|
||||
{
|
||||
static std::regex re("^---$");
|
||||
return std::regex_match(line, re);
|
||||
}
|
||||
|
||||
/**
|
||||
* IsFinished
|
||||
*
|
||||
* The horizontal line is always only one line long, so this method always
|
||||
* returns true.
|
||||
*
|
||||
* @method
|
||||
* @return {bool}
|
||||
*/
|
||||
bool IsFinished() const override { return true; }
|
||||
|
||||
protected:
|
||||
bool isInlineBlockAllowed() const override { return false; }
|
||||
|
||||
bool isLineParserAllowed() const override { return false; }
|
||||
|
||||
void parseBlock(std::string& line) override
|
||||
{
|
||||
static std::string replacement = "<hr/>";
|
||||
|
||||
line = std::regex_replace(line, lineRegex, replacement);
|
||||
}
|
||||
|
||||
private:
|
||||
std::regex lineRegex;
|
||||
}; // class HorizontalLineParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
112
maddy/htmlparser.h
Normal file
112
maddy/htmlparser.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/blockparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* HtmlParser
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class HtmlParser : public BlockParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* ctor
|
||||
*
|
||||
* @method
|
||||
* @param {std::function<void(std::string&)>} parseLineCallback
|
||||
* @param {std::function<std::shared_ptr<BlockParser>(const std::string&
|
||||
* line)>} getBlockParserForLineCallback
|
||||
*/
|
||||
HtmlParser(
|
||||
std::function<void(std::string&)> parseLineCallback,
|
||||
std::function<std::shared_ptr<BlockParser>(const std::string& line)>
|
||||
getBlockParserForLineCallback
|
||||
)
|
||||
: BlockParser(parseLineCallback, getBlockParserForLineCallback)
|
||||
, isStarted(false)
|
||||
, isFinished(false)
|
||||
, isGreaterThanFound(false)
|
||||
{}
|
||||
|
||||
/**
|
||||
* IsStartingLine
|
||||
*
|
||||
* If the line is starting with `<`, HTML is expected to follow.
|
||||
* Nothing after that will be parsed, it only is copied.
|
||||
*
|
||||
* @method
|
||||
* @param {const std::string&} line
|
||||
* @return {bool}
|
||||
*/
|
||||
static bool IsStartingLine(const std::string& line) { return line[0] == '<'; }
|
||||
|
||||
/**
|
||||
* IsFinished
|
||||
*
|
||||
* `>` followed by an empty line will end the HTML block.
|
||||
*
|
||||
* @method
|
||||
* @return {bool}
|
||||
*/
|
||||
bool IsFinished() const override { return this->isFinished; }
|
||||
|
||||
protected:
|
||||
bool isInlineBlockAllowed() const override { return false; }
|
||||
|
||||
bool isLineParserAllowed() const override { return false; }
|
||||
|
||||
void parseBlock(std::string& line) override
|
||||
{
|
||||
if (!this->isStarted)
|
||||
{
|
||||
this->isStarted = true;
|
||||
}
|
||||
|
||||
if (!line.empty() && line[line.size() - 1] == '>')
|
||||
{
|
||||
this->isGreaterThanFound = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.empty() && this->isGreaterThanFound)
|
||||
{
|
||||
this->isFinished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!line.empty() && this->isGreaterThanFound)
|
||||
{
|
||||
this->isGreaterThanFound = false;
|
||||
}
|
||||
|
||||
if (!line.empty())
|
||||
{
|
||||
line += " ";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool isStarted;
|
||||
bool isFinished;
|
||||
bool isGreaterThanFound;
|
||||
}; // class HtmlParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
52
maddy/imageparser.h
Normal file
52
maddy/imageparser.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/lineparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ImageParser
|
||||
*
|
||||
* Has to be used before the `LinkParser`.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class ImageParser : public LineParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* From Markdown: ``
|
||||
*
|
||||
* To HTML: `<img src="http://example.com/a.png" alt="text"/>`
|
||||
*
|
||||
* @method
|
||||
* @param {std::string&} line The line to interpret
|
||||
* @return {void}
|
||||
*/
|
||||
void Parse(std::string& line) override
|
||||
{
|
||||
static std::regex re(R"(\!\[([^\]]*)\]\(([^\]]*)\))");
|
||||
static std::string replacement = "<img src=\"$2\" alt=\"$1\"/>";
|
||||
|
||||
line = std::regex_replace(line, re, replacement);
|
||||
}
|
||||
}; // class ImageParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
50
maddy/inlinecodeparser.h
Normal file
50
maddy/inlinecodeparser.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/lineparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* InlineCodeParser
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class InlineCodeParser : public LineParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* From Markdown: `text `some code``
|
||||
*
|
||||
* To HTML: `text <code>some code</code>`
|
||||
*
|
||||
* @method
|
||||
* @param {std::string&} line The line to interpret
|
||||
* @return {void}
|
||||
*/
|
||||
void Parse(std::string& line) override
|
||||
{
|
||||
static std::regex re("`([^`]*)`");
|
||||
static std::string replacement = "<code>$1</code>";
|
||||
|
||||
line = std::regex_replace(line, re, replacement);
|
||||
}
|
||||
}; // class InlineCodeParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
51
maddy/italicparser.h
Normal file
51
maddy/italicparser.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/lineparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ItalicParser
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class ItalicParser : public LineParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* From Markdown: `text *text*`
|
||||
*
|
||||
* To HTML: `text <i>text</i>`
|
||||
*
|
||||
* @method
|
||||
* @param {std::string&} line The line to interpret
|
||||
* @return {void}
|
||||
*/
|
||||
void Parse(std::string& line) override
|
||||
{
|
||||
static std::regex re(
|
||||
R"((?!.*`.*|.*<code>.*)\*(?!.*`.*|.*<\/code>.*)([^\*]*)\*(?!.*`.*|.*<\/code>.*))"
|
||||
);
|
||||
static std::string replacement = "<i>$1</i>";
|
||||
line = std::regex_replace(line, re, replacement);
|
||||
}
|
||||
}; // class ItalicParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
124
maddy/latexblockparser.h
Normal file
124
maddy/latexblockparser.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/blockparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* LatexBlockParser
|
||||
*
|
||||
* Support for https://www.mathjax.org/
|
||||
* Be aware, that if you want to make MathJax work, you need also their
|
||||
* JavaScript library added to your HTML code.
|
||||
* maddy does not itself add that code to be more flexible in how you write your
|
||||
* head and full body.
|
||||
*
|
||||
* From Markdown: `$$` surrounded text
|
||||
*
|
||||
* ```
|
||||
* $$some formula
|
||||
* $$
|
||||
* ```
|
||||
*
|
||||
* To HTML:
|
||||
*
|
||||
* ```
|
||||
* $$some formula
|
||||
* $$
|
||||
* ```
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class LatexBlockParser : public BlockParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* ctor
|
||||
*
|
||||
* @method
|
||||
* @param {std::function<void(std::string&)>} parseLineCallback
|
||||
* @param {std::function<std::shared_ptr<BlockParser>(const std::string&
|
||||
* line)>} getBlockParserForLineCallback
|
||||
*/
|
||||
LatexBlockParser(
|
||||
std::function<void(std::string&)> parseLineCallback,
|
||||
std::function<std::shared_ptr<BlockParser>(const std::string& line)>
|
||||
getBlockParserForLineCallback
|
||||
)
|
||||
: BlockParser(parseLineCallback, getBlockParserForLineCallback)
|
||||
, isStarted(false)
|
||||
, isFinished(false)
|
||||
{}
|
||||
|
||||
/**
|
||||
* IsStartingLine
|
||||
*
|
||||
* If the line starts with two dollars, then it is a latex block.
|
||||
*
|
||||
* ```
|
||||
* $$
|
||||
* ```
|
||||
*
|
||||
* @method
|
||||
* @param {const std::string&} line
|
||||
* @return {bool}
|
||||
*/
|
||||
static bool IsStartingLine(const std::string& line)
|
||||
{
|
||||
static std::regex re(R"(^(?:\$){2}(.*)$)");
|
||||
return std::regex_match(line, re);
|
||||
}
|
||||
|
||||
/**
|
||||
* IsFinished
|
||||
*
|
||||
* @method
|
||||
* @return {bool}
|
||||
*/
|
||||
bool IsFinished() const override { return this->isFinished; }
|
||||
|
||||
protected:
|
||||
bool isInlineBlockAllowed() const override { return false; }
|
||||
|
||||
bool isLineParserAllowed() const override { return false; }
|
||||
|
||||
void parseBlock(std::string& line) override
|
||||
{
|
||||
if (!this->isStarted && line.substr(0, 2) == "$$")
|
||||
{
|
||||
this->isStarted = true;
|
||||
this->isFinished = false;
|
||||
}
|
||||
|
||||
if (this->isStarted && !this->isFinished && line.size() > 1 &&
|
||||
line.substr(line.size() - 2, 2) == "$$")
|
||||
{
|
||||
this->isFinished = true;
|
||||
this->isStarted = false;
|
||||
}
|
||||
|
||||
line += "\n";
|
||||
}
|
||||
|
||||
private:
|
||||
bool isStarted;
|
||||
bool isFinished;
|
||||
}; // class LatexBlockParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
46
maddy/lineparser.h
Normal file
46
maddy/lineparser.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <string>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* LineParser
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class LineParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* dtor
|
||||
*
|
||||
* @method
|
||||
*/
|
||||
virtual ~LineParser() {}
|
||||
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* From Markdown to HTML
|
||||
*
|
||||
* @method
|
||||
* @param {std::string&} line The line to interpret
|
||||
* @return {void}
|
||||
*/
|
||||
virtual void Parse(std::string& line) = 0;
|
||||
}; // class LineParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
52
maddy/linkparser.h
Normal file
52
maddy/linkparser.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/lineparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* LinkParser
|
||||
*
|
||||
* Has to be used after the `ImageParser`.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class LinkParser : public LineParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* From Markdown: `[text](http://example.com)`
|
||||
*
|
||||
* To HTML: `<a href="http://example.com">text</a>`
|
||||
*
|
||||
* @method
|
||||
* @param {std::string&} line The line to interpret
|
||||
* @return {void}
|
||||
*/
|
||||
void Parse(std::string& line) override
|
||||
{
|
||||
static std::regex re(R"(\[([^\]]*)\]\(([^\]]*)\))");
|
||||
static std::string replacement = "<a href=\"$2\">$1</a>";
|
||||
|
||||
line = std::regex_replace(line, re, replacement);
|
||||
}
|
||||
}; // class LinkParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
126
maddy/orderedlistparser.h
Normal file
126
maddy/orderedlistparser.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/blockparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* OrderedListParser
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class OrderedListParser : public BlockParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* ctor
|
||||
*
|
||||
* @method
|
||||
* @param {std::function<void(std::string&)>} parseLineCallback
|
||||
* @param {std::function<std::shared_ptr<BlockParser>(const std::string&
|
||||
* line)>} getBlockParserForLineCallback
|
||||
*/
|
||||
OrderedListParser(
|
||||
std::function<void(std::string&)> parseLineCallback,
|
||||
std::function<std::shared_ptr<BlockParser>(const std::string& line)>
|
||||
getBlockParserForLineCallback
|
||||
)
|
||||
: BlockParser(parseLineCallback, getBlockParserForLineCallback)
|
||||
, isStarted(false)
|
||||
, isFinished(false)
|
||||
{}
|
||||
|
||||
/**
|
||||
* IsStartingLine
|
||||
*
|
||||
* An ordered list starts with `1. `.
|
||||
*
|
||||
* @method
|
||||
* @param {const std::string&} line
|
||||
* @return {bool}
|
||||
*/
|
||||
static bool IsStartingLine(const std::string& line)
|
||||
{
|
||||
static std::regex re("^1\\. .*");
|
||||
return std::regex_match(line, re);
|
||||
}
|
||||
|
||||
/**
|
||||
* IsFinished
|
||||
*
|
||||
* @method
|
||||
* @return {bool}
|
||||
*/
|
||||
bool IsFinished() const override { return this->isFinished; }
|
||||
|
||||
protected:
|
||||
bool isInlineBlockAllowed() const override { return true; }
|
||||
|
||||
bool isLineParserAllowed() const override { return true; }
|
||||
|
||||
void parseBlock(std::string& line) override
|
||||
{
|
||||
bool isStartOfNewListItem = this->isStartOfNewListItem(line);
|
||||
uint32_t indentation = getIndentationWidth(line);
|
||||
|
||||
static std::regex orderedlineRegex(R"(^[1-9]+[0-9]*\. )");
|
||||
line = std::regex_replace(line, orderedlineRegex, "");
|
||||
static std::regex unorderedlineRegex(R"(^\* )");
|
||||
line = std::regex_replace(line, unorderedlineRegex, "");
|
||||
|
||||
if (!this->isStarted)
|
||||
{
|
||||
line = "<ol><li>" + line;
|
||||
this->isStarted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (indentation >= 2)
|
||||
{
|
||||
line = line.substr(2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.empty() || line.find("</li><li>") != std::string::npos ||
|
||||
line.find("</li></ol>") != std::string::npos ||
|
||||
line.find("</li></ul>") != std::string::npos)
|
||||
{
|
||||
line = "</li></ol>" + line;
|
||||
this->isFinished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isStartOfNewListItem)
|
||||
{
|
||||
line = "</li><li>" + line;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool isStarted;
|
||||
bool isFinished;
|
||||
|
||||
bool isStartOfNewListItem(const std::string& line) const
|
||||
{
|
||||
static std::regex re(R"(^(?:[1-9]+[0-9]*\. |\* ).*)");
|
||||
return std::regex_match(line, re);
|
||||
}
|
||||
}; // class OrderedListParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
115
maddy/paragraphparser.h
Normal file
115
maddy/paragraphparser.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/blockparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ParagraphParser
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class ParagraphParser : public BlockParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* ctor
|
||||
*
|
||||
* @method
|
||||
* @param {std::function<void(std::string&)>} parseLineCallback
|
||||
* @param {std::function<std::shared_ptr<BlockParser>(const std::string&
|
||||
* line)>} getBlockParserForLineCallback
|
||||
*/
|
||||
ParagraphParser(
|
||||
std::function<void(std::string&)> parseLineCallback,
|
||||
std::function<std::shared_ptr<BlockParser>(const std::string& line)>
|
||||
getBlockParserForLineCallback,
|
||||
bool isEnabled
|
||||
)
|
||||
: BlockParser(parseLineCallback, getBlockParserForLineCallback)
|
||||
, isStarted(false)
|
||||
, isFinished(false)
|
||||
, isEnabled(isEnabled)
|
||||
{}
|
||||
|
||||
/**
|
||||
* IsStartingLine
|
||||
*
|
||||
* If the line is not empty, it will be a paragraph.
|
||||
*
|
||||
* This block parser has to always run as the last one!
|
||||
*
|
||||
* @method
|
||||
* @param {const std::string&} line
|
||||
* @return {bool}
|
||||
*/
|
||||
static bool IsStartingLine(const std::string& line) { return !line.empty(); }
|
||||
|
||||
/**
|
||||
* IsFinished
|
||||
*
|
||||
* An empty line will end the paragraph.
|
||||
*
|
||||
* @method
|
||||
* @return {bool}
|
||||
*/
|
||||
bool IsFinished() const override { return this->isFinished; }
|
||||
|
||||
protected:
|
||||
bool isInlineBlockAllowed() const override { return false; }
|
||||
|
||||
bool isLineParserAllowed() const override { return true; }
|
||||
|
||||
void parseBlock(std::string& line) override
|
||||
{
|
||||
if (this->isEnabled && !this->isStarted)
|
||||
{
|
||||
line = "<p>" + line + " ";
|
||||
this->isStarted = true;
|
||||
return;
|
||||
}
|
||||
else if (!this->isEnabled && !this->isStarted)
|
||||
{
|
||||
line += " ";
|
||||
this->isStarted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->isEnabled && line.empty())
|
||||
{
|
||||
line += "</p>";
|
||||
this->isFinished = true;
|
||||
return;
|
||||
}
|
||||
else if (!this->isEnabled && line.empty())
|
||||
{
|
||||
line += "<br/>";
|
||||
this->isFinished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
line += " ";
|
||||
}
|
||||
|
||||
private:
|
||||
bool isStarted;
|
||||
bool isFinished;
|
||||
bool isEnabled;
|
||||
}; // class ParagraphParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
412
maddy/parser.h
Normal file
412
maddy/parser.h
Normal file
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/parserconfig.h"
|
||||
|
||||
// BlockParser
|
||||
#include "maddy/checklistparser.h"
|
||||
#include "maddy/codeblockparser.h"
|
||||
#include "maddy/headlineparser.h"
|
||||
#include "maddy/horizontallineparser.h"
|
||||
#include "maddy/htmlparser.h"
|
||||
#include "maddy/latexblockparser.h"
|
||||
#include "maddy/orderedlistparser.h"
|
||||
#include "maddy/paragraphparser.h"
|
||||
#include "maddy/quoteparser.h"
|
||||
#include "maddy/tableparser.h"
|
||||
#include "maddy/unorderedlistparser.h"
|
||||
|
||||
// LineParser
|
||||
#include "maddy/breaklineparser.h"
|
||||
#include "maddy/emphasizedparser.h"
|
||||
#include "maddy/imageparser.h"
|
||||
#include "maddy/inlinecodeparser.h"
|
||||
#include "maddy/italicparser.h"
|
||||
#include "maddy/linkparser.h"
|
||||
#include "maddy/strikethroughparser.h"
|
||||
#include "maddy/strongparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parser
|
||||
*
|
||||
* Transforms Markdown to HTML
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Version info
|
||||
*
|
||||
* Check https://github.com/progsource/maddy/blob/master/CHANGELOG.md
|
||||
* for the changelog.
|
||||
*/
|
||||
static const std::string& version()
|
||||
{
|
||||
static const std::string v = "1.3.0";
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* ctor
|
||||
*
|
||||
* Initializes all `LineParser`
|
||||
*
|
||||
* @method
|
||||
*/
|
||||
Parser(std::shared_ptr<ParserConfig> config = nullptr) : config(config)
|
||||
{
|
||||
// deprecated backward compatibility
|
||||
// will be removed in 1.4.0 latest including the booleans
|
||||
if (this->config && !this->config->isEmphasizedParserEnabled)
|
||||
{
|
||||
this->config->enabledParsers &= ~maddy::types::EMPHASIZED_PARSER;
|
||||
}
|
||||
if (this->config && !this->config->isHTMLWrappedInParagraph)
|
||||
{
|
||||
this->config->enabledParsers |= maddy::types::HTML_PARSER;
|
||||
}
|
||||
|
||||
if (!this->config ||
|
||||
(this->config->enabledParsers & maddy::types::BREAKLINE_PARSER) != 0)
|
||||
{
|
||||
this->breakLineParser = std::make_shared<BreakLineParser>();
|
||||
}
|
||||
|
||||
if (!this->config ||
|
||||
(this->config->enabledParsers & maddy::types::EMPHASIZED_PARSER) != 0)
|
||||
{
|
||||
this->emphasizedParser = std::make_shared<EmphasizedParser>();
|
||||
}
|
||||
|
||||
if (!this->config ||
|
||||
(this->config->enabledParsers & maddy::types::IMAGE_PARSER) != 0)
|
||||
{
|
||||
this->imageParser = std::make_shared<ImageParser>();
|
||||
}
|
||||
|
||||
if (!this->config ||
|
||||
(this->config->enabledParsers & maddy::types::INLINE_CODE_PARSER) != 0)
|
||||
{
|
||||
this->inlineCodeParser = std::make_shared<InlineCodeParser>();
|
||||
}
|
||||
|
||||
if (!this->config ||
|
||||
(this->config->enabledParsers & maddy::types::ITALIC_PARSER) != 0)
|
||||
{
|
||||
this->italicParser = std::make_shared<ItalicParser>();
|
||||
}
|
||||
|
||||
if (!this->config ||
|
||||
(this->config->enabledParsers & maddy::types::LINK_PARSER) != 0)
|
||||
{
|
||||
this->linkParser = std::make_shared<LinkParser>();
|
||||
}
|
||||
|
||||
if (!this->config || (this->config->enabledParsers &
|
||||
maddy::types::STRIKETHROUGH_PARSER) != 0)
|
||||
{
|
||||
this->strikeThroughParser = std::make_shared<StrikeThroughParser>();
|
||||
}
|
||||
|
||||
if (!this->config ||
|
||||
(this->config->enabledParsers & maddy::types::STRONG_PARSER) != 0)
|
||||
{
|
||||
this->strongParser = std::make_shared<StrongParser>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* @method
|
||||
* @param {const std::istream&} markdown
|
||||
* @return {std::string} HTML
|
||||
*/
|
||||
std::string Parse(std::istream& markdown) const
|
||||
{
|
||||
std::string result = "";
|
||||
std::shared_ptr<BlockParser> currentBlockParser = nullptr;
|
||||
|
||||
for (std::string line; std::getline(markdown, line);)
|
||||
{
|
||||
if (!currentBlockParser)
|
||||
{
|
||||
currentBlockParser = getBlockParserForLine(line);
|
||||
}
|
||||
|
||||
if (currentBlockParser)
|
||||
{
|
||||
currentBlockParser->AddLine(line);
|
||||
|
||||
if (currentBlockParser->IsFinished())
|
||||
{
|
||||
result += currentBlockParser->GetResult().str();
|
||||
currentBlockParser = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure, that all parsers are finished
|
||||
if (currentBlockParser)
|
||||
{
|
||||
std::string emptyLine = "";
|
||||
currentBlockParser->AddLine(emptyLine);
|
||||
if (currentBlockParser->IsFinished())
|
||||
{
|
||||
result += currentBlockParser->GetResult().str();
|
||||
currentBlockParser = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<ParserConfig> config;
|
||||
std::shared_ptr<BreakLineParser> breakLineParser;
|
||||
std::shared_ptr<EmphasizedParser> emphasizedParser;
|
||||
std::shared_ptr<ImageParser> imageParser;
|
||||
std::shared_ptr<InlineCodeParser> inlineCodeParser;
|
||||
std::shared_ptr<ItalicParser> italicParser;
|
||||
std::shared_ptr<LinkParser> linkParser;
|
||||
std::shared_ptr<StrikeThroughParser> strikeThroughParser;
|
||||
std::shared_ptr<StrongParser> strongParser;
|
||||
|
||||
// block parser have to run before
|
||||
void runLineParser(std::string& line) const
|
||||
{
|
||||
// Attention! ImageParser has to be before LinkParser
|
||||
if (this->imageParser)
|
||||
{
|
||||
this->imageParser->Parse(line);
|
||||
}
|
||||
|
||||
if (this->linkParser)
|
||||
{
|
||||
this->linkParser->Parse(line);
|
||||
}
|
||||
|
||||
// Attention! StrongParser has to be before EmphasizedParser
|
||||
if (this->strongParser)
|
||||
{
|
||||
this->strongParser->Parse(line);
|
||||
}
|
||||
|
||||
if (this->emphasizedParser)
|
||||
{
|
||||
this->emphasizedParser->Parse(line);
|
||||
}
|
||||
|
||||
if (this->strikeThroughParser)
|
||||
{
|
||||
this->strikeThroughParser->Parse(line);
|
||||
}
|
||||
|
||||
if (this->inlineCodeParser)
|
||||
{
|
||||
this->inlineCodeParser->Parse(line);
|
||||
}
|
||||
|
||||
if (this->italicParser)
|
||||
{
|
||||
this->italicParser->Parse(line);
|
||||
}
|
||||
|
||||
if (this->breakLineParser)
|
||||
{
|
||||
this->breakLineParser->Parse(line);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<BlockParser> getBlockParserForLine(const std::string& line
|
||||
) const
|
||||
{
|
||||
std::shared_ptr<BlockParser> parser;
|
||||
|
||||
if ((!this->config || (this->config->enabledParsers &
|
||||
maddy::types::CODE_BLOCK_PARSER) != 0) &&
|
||||
maddy::CodeBlockParser::IsStartingLine(line))
|
||||
{
|
||||
parser = std::make_shared<maddy::CodeBlockParser>(nullptr, nullptr);
|
||||
}
|
||||
else if (this->config &&
|
||||
(this->config->enabledParsers & maddy::types::LATEX_BLOCK_PARSER
|
||||
) != 0 &&
|
||||
maddy::LatexBlockParser::IsStartingLine(line))
|
||||
{
|
||||
parser = std::make_shared<LatexBlockParser>(nullptr, nullptr);
|
||||
}
|
||||
else if ((!this->config || (this->config->enabledParsers &
|
||||
maddy::types::HEADLINE_PARSER) != 0) &&
|
||||
maddy::HeadlineParser::IsStartingLine(line))
|
||||
{
|
||||
if (!this->config || this->config->isHeadlineInlineParsingEnabled)
|
||||
{
|
||||
parser = std::make_shared<maddy::HeadlineParser>(
|
||||
[this](std::string& line) { this->runLineParser(line); },
|
||||
nullptr,
|
||||
true
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
parser =
|
||||
std::make_shared<maddy::HeadlineParser>(nullptr, nullptr, false);
|
||||
}
|
||||
}
|
||||
else if ((!this->config || (this->config->enabledParsers &
|
||||
maddy::types::HORIZONTAL_LINE_PARSER) != 0) &&
|
||||
maddy::HorizontalLineParser::IsStartingLine(line))
|
||||
{
|
||||
parser = std::make_shared<maddy::HorizontalLineParser>(nullptr, nullptr);
|
||||
}
|
||||
else if ((!this->config || (this->config->enabledParsers &
|
||||
maddy::types::QUOTE_PARSER) != 0) &&
|
||||
maddy::QuoteParser::IsStartingLine(line))
|
||||
{
|
||||
parser = std::make_shared<maddy::QuoteParser>(
|
||||
[this](std::string& line) { this->runLineParser(line); },
|
||||
[this](const std::string& line)
|
||||
{ return this->getBlockParserForLine(line); }
|
||||
);
|
||||
}
|
||||
else if ((!this->config || (this->config->enabledParsers &
|
||||
maddy::types::TABLE_PARSER) != 0) &&
|
||||
maddy::TableParser::IsStartingLine(line))
|
||||
{
|
||||
parser = std::make_shared<maddy::TableParser>(
|
||||
[this](std::string& line) { this->runLineParser(line); }, nullptr
|
||||
);
|
||||
}
|
||||
else if ((!this->config || (this->config->enabledParsers &
|
||||
maddy::types::CHECKLIST_PARSER) != 0) &&
|
||||
maddy::ChecklistParser::IsStartingLine(line))
|
||||
{
|
||||
parser = this->createChecklistParser();
|
||||
}
|
||||
else if ((!this->config || (this->config->enabledParsers &
|
||||
maddy::types::ORDERED_LIST_PARSER) != 0) &&
|
||||
maddy::OrderedListParser::IsStartingLine(line))
|
||||
{
|
||||
parser = this->createOrderedListParser();
|
||||
}
|
||||
else if ((!this->config || (this->config->enabledParsers &
|
||||
maddy::types::UNORDERED_LIST_PARSER) != 0) &&
|
||||
maddy::UnorderedListParser::IsStartingLine(line))
|
||||
{
|
||||
parser = this->createUnorderedListParser();
|
||||
}
|
||||
else if (this->config &&
|
||||
(this->config->enabledParsers & maddy::types::HTML_PARSER) != 0 &&
|
||||
maddy::HtmlParser::IsStartingLine(line))
|
||||
{
|
||||
parser = std::make_shared<maddy::HtmlParser>(nullptr, nullptr);
|
||||
}
|
||||
else if (maddy::ParagraphParser::IsStartingLine(line))
|
||||
{
|
||||
parser = std::make_shared<maddy::ParagraphParser>(
|
||||
[this](std::string& line) { this->runLineParser(line); },
|
||||
nullptr,
|
||||
(!this->config ||
|
||||
(this->config->enabledParsers & maddy::types::PARAGRAPH_PARSER) != 0)
|
||||
);
|
||||
}
|
||||
|
||||
return parser;
|
||||
}
|
||||
|
||||
std::shared_ptr<BlockParser> createChecklistParser() const
|
||||
{
|
||||
return std::make_shared<maddy::ChecklistParser>(
|
||||
[this](std::string& line) { this->runLineParser(line); },
|
||||
[this](const std::string& line)
|
||||
{
|
||||
std::shared_ptr<BlockParser> parser;
|
||||
|
||||
if ((!this->config || (this->config->enabledParsers &
|
||||
maddy::types::CHECKLIST_PARSER) != 0) &&
|
||||
maddy::ChecklistParser::IsStartingLine(line))
|
||||
{
|
||||
parser = this->createChecklistParser();
|
||||
}
|
||||
|
||||
return parser;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
std::shared_ptr<BlockParser> createOrderedListParser() const
|
||||
{
|
||||
return std::make_shared<maddy::OrderedListParser>(
|
||||
[this](std::string& line) { this->runLineParser(line); },
|
||||
[this](const std::string& line)
|
||||
{
|
||||
std::shared_ptr<BlockParser> parser;
|
||||
|
||||
if ((!this->config || (this->config->enabledParsers &
|
||||
maddy::types::ORDERED_LIST_PARSER) != 0) &&
|
||||
maddy::OrderedListParser::IsStartingLine(line))
|
||||
{
|
||||
parser = this->createOrderedListParser();
|
||||
}
|
||||
else if ((!this->config || (this->config->enabledParsers &
|
||||
maddy::types::UNORDERED_LIST_PARSER) != 0
|
||||
) &&
|
||||
maddy::UnorderedListParser::IsStartingLine(line))
|
||||
{
|
||||
parser = this->createUnorderedListParser();
|
||||
}
|
||||
|
||||
return parser;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
std::shared_ptr<BlockParser> createUnorderedListParser() const
|
||||
{
|
||||
return std::make_shared<maddy::UnorderedListParser>(
|
||||
[this](std::string& line) { this->runLineParser(line); },
|
||||
[this](const std::string& line)
|
||||
{
|
||||
std::shared_ptr<BlockParser> parser;
|
||||
|
||||
if ((!this->config || (this->config->enabledParsers &
|
||||
maddy::types::ORDERED_LIST_PARSER) != 0) &&
|
||||
maddy::OrderedListParser::IsStartingLine(line))
|
||||
{
|
||||
parser = this->createOrderedListParser();
|
||||
}
|
||||
else if ((!this->config || (this->config->enabledParsers &
|
||||
maddy::types::UNORDERED_LIST_PARSER) != 0
|
||||
) &&
|
||||
maddy::UnorderedListParser::IsStartingLine(line))
|
||||
{
|
||||
parser = this->createUnorderedListParser();
|
||||
}
|
||||
|
||||
return parser;
|
||||
}
|
||||
);
|
||||
}
|
||||
}; // class Parser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
97
maddy/parserconfig.h
Normal file
97
maddy/parserconfig.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace types {
|
||||
|
||||
// clang-format off
|
||||
/**
|
||||
* PARSER_TYPE
|
||||
*
|
||||
* Bitwise flags to turn on/off each parser
|
||||
*/
|
||||
enum PARSER_TYPE : uint32_t
|
||||
{
|
||||
NONE = 0,
|
||||
|
||||
BREAKLINE_PARSER = 0b1,
|
||||
CHECKLIST_PARSER = 0b10,
|
||||
CODE_BLOCK_PARSER = 0b100,
|
||||
EMPHASIZED_PARSER = 0b1000,
|
||||
HEADLINE_PARSER = 0b10000,
|
||||
HORIZONTAL_LINE_PARSER = 0b100000,
|
||||
HTML_PARSER = 0b1000000,
|
||||
IMAGE_PARSER = 0b10000000,
|
||||
INLINE_CODE_PARSER = 0b100000000,
|
||||
ITALIC_PARSER = 0b1000000000,
|
||||
LINK_PARSER = 0b10000000000,
|
||||
ORDERED_LIST_PARSER = 0b100000000000,
|
||||
PARAGRAPH_PARSER = 0b1000000000000,
|
||||
QUOTE_PARSER = 0b10000000000000,
|
||||
STRIKETHROUGH_PARSER = 0b100000000000000,
|
||||
STRONG_PARSER = 0b1000000000000000,
|
||||
TABLE_PARSER = 0b10000000000000000,
|
||||
UNORDERED_LIST_PARSER = 0b100000000000000000,
|
||||
LATEX_BLOCK_PARSER = 0b1000000000000000000,
|
||||
|
||||
DEFAULT = 0b0111111111110111111,
|
||||
ALL = 0b1111111111111111111,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
} // namespace types
|
||||
|
||||
/**
|
||||
* ParserConfig
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
struct ParserConfig
|
||||
{
|
||||
/**
|
||||
* @deprecated will be removed in 1.4.0 latest
|
||||
*
|
||||
* this flag = false == `enabledParsers &= ~maddy::types::EMPHASIZED_PARSER`
|
||||
*/
|
||||
bool isEmphasizedParserEnabled;
|
||||
|
||||
/**
|
||||
* @deprecated will be removed in 1.4.0 latest
|
||||
*
|
||||
* this flag = false == `enabledParsers |= maddy::types::HTML_PARSER`
|
||||
*/
|
||||
bool isHTMLWrappedInParagraph;
|
||||
|
||||
/**
|
||||
* en-/disable headline inline-parsing
|
||||
*
|
||||
* default: enabled
|
||||
*/
|
||||
bool isHeadlineInlineParsingEnabled;
|
||||
|
||||
/**
|
||||
* enabled parsers bitfield
|
||||
*/
|
||||
uint32_t enabledParsers;
|
||||
|
||||
ParserConfig()
|
||||
: isEmphasizedParserEnabled(true)
|
||||
, isHTMLWrappedInParagraph(true)
|
||||
, isHeadlineInlineParsingEnabled(true)
|
||||
, enabledParsers(maddy::types::DEFAULT)
|
||||
{}
|
||||
}; // class ParserConfig
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
152
maddy/quoteparser.h
Normal file
152
maddy/quoteparser.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/blockparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* QuoteParser
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class QuoteParser : public BlockParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* ctor
|
||||
*
|
||||
* @method
|
||||
* @param {std::function<void(std::string&)>} parseLineCallback
|
||||
* @param {std::function<std::shared_ptr<BlockParser>(const std::string&
|
||||
* line)>} getBlockParserForLineCallback
|
||||
*/
|
||||
QuoteParser(
|
||||
std::function<void(std::string&)> parseLineCallback,
|
||||
std::function<std::shared_ptr<BlockParser>(const std::string& line)>
|
||||
getBlockParserForLineCallback
|
||||
)
|
||||
: BlockParser(parseLineCallback, getBlockParserForLineCallback)
|
||||
, isStarted(false)
|
||||
, isFinished(false)
|
||||
{}
|
||||
|
||||
/**
|
||||
* IsStartingLine
|
||||
*
|
||||
* A quote starts with `> `.
|
||||
*
|
||||
* @method
|
||||
* @param {const std::string&} line
|
||||
* @return {bool}
|
||||
*/
|
||||
static bool IsStartingLine(const std::string& line)
|
||||
{
|
||||
static std::regex re(R"(^\>.*)");
|
||||
return std::regex_match(line, re);
|
||||
}
|
||||
|
||||
/**
|
||||
* AddLine
|
||||
*
|
||||
* Adding a line which has to be parsed.
|
||||
*
|
||||
* @method
|
||||
* @param {std::string&} line
|
||||
* @return {void}
|
||||
*/
|
||||
void AddLine(std::string& line) override
|
||||
{
|
||||
if (!this->isStarted)
|
||||
{
|
||||
this->result << "<blockquote>";
|
||||
this->isStarted = true;
|
||||
}
|
||||
|
||||
bool finish = false;
|
||||
if (line.empty())
|
||||
{
|
||||
finish = true;
|
||||
}
|
||||
|
||||
this->parseBlock(line);
|
||||
|
||||
if (this->isInlineBlockAllowed() && !this->childParser)
|
||||
{
|
||||
this->childParser = this->getBlockParserForLine(line);
|
||||
}
|
||||
|
||||
if (this->childParser)
|
||||
{
|
||||
this->childParser->AddLine(line);
|
||||
|
||||
if (this->childParser->IsFinished())
|
||||
{
|
||||
this->result << this->childParser->GetResult().str();
|
||||
this->childParser = nullptr;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->isLineParserAllowed())
|
||||
{
|
||||
this->parseLine(line);
|
||||
}
|
||||
|
||||
if (finish)
|
||||
{
|
||||
this->result << "</blockquote>";
|
||||
this->isFinished = true;
|
||||
}
|
||||
|
||||
this->result << line;
|
||||
}
|
||||
|
||||
/**
|
||||
* IsFinished
|
||||
*
|
||||
* @method
|
||||
* @return {bool}
|
||||
*/
|
||||
bool IsFinished() const override { return this->isFinished; }
|
||||
|
||||
protected:
|
||||
bool isInlineBlockAllowed() const override { return true; }
|
||||
|
||||
bool isLineParserAllowed() const override { return true; }
|
||||
|
||||
void parseBlock(std::string& line) override
|
||||
{
|
||||
static std::regex lineRegexWithSpace(R"(^\> )");
|
||||
line = std::regex_replace(line, lineRegexWithSpace, "");
|
||||
static std::regex lineRegexWithoutSpace(R"(^\>)");
|
||||
line = std::regex_replace(line, lineRegexWithoutSpace, "");
|
||||
|
||||
if (!line.empty())
|
||||
{
|
||||
line += " ";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool isStarted;
|
||||
bool isFinished;
|
||||
}; // class QuoteParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
52
maddy/strikethroughparser.h
Normal file
52
maddy/strikethroughparser.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/lineparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* StrikeThroughParser
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class StrikeThroughParser : public LineParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* From Markdown: `text ~~text~~`
|
||||
*
|
||||
* To HTML: `text <s>text</s>`
|
||||
*
|
||||
* @method
|
||||
* @param {std::string&} line The line to interpret
|
||||
* @return {void}
|
||||
*/
|
||||
void Parse(std::string& line) override
|
||||
{
|
||||
static std::regex re(
|
||||
R"((?!.*`.*|.*<code>.*)\~\~(?!.*`.*|.*<\/code>.*)([^\~]*)\~\~(?!.*`.*|.*<\/code>.*))"
|
||||
);
|
||||
static std::string replacement = "<s>$1</s>";
|
||||
|
||||
line = std::regex_replace(line, re, replacement);
|
||||
}
|
||||
}; // class StrikeThroughParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
61
maddy/strongparser.h
Normal file
61
maddy/strongparser.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/lineparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* StrongParser
|
||||
*
|
||||
* Has to be used before the `EmphasizedParser`.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class StrongParser : public LineParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* From Markdown: `text **text** __text__`
|
||||
*
|
||||
* To HTML: `text <strong>text</strong> <strong>text</strong>`
|
||||
*
|
||||
* @method
|
||||
* @param {std::string&} line The line to interpret
|
||||
* @return {void}
|
||||
*/
|
||||
void Parse(std::string& line) override
|
||||
{
|
||||
static std::vector<std::regex> res{
|
||||
std::regex{
|
||||
R"((?!.*`.*|.*<code>.*)\*\*(?!.*`.*|.*<\/code>.*)([^\*\*]*)\*\*(?!.*`.*|.*<\/code>.*))"
|
||||
},
|
||||
std::regex{
|
||||
R"((?!.*`.*|.*<code>.*)__(?!.*`.*|.*<\/code>.*)([^__]*)__(?!.*`.*|.*<\/code>.*))"
|
||||
}
|
||||
};
|
||||
static std::string replacement = "<strong>$1</strong>";
|
||||
for (const auto& re : res)
|
||||
{
|
||||
line = std::regex_replace(line, re, replacement);
|
||||
}
|
||||
}
|
||||
}; // class StrongParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
233
maddy/tableparser.h
Normal file
233
maddy/tableparser.h
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/blockparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* TableParser
|
||||
*
|
||||
* For more information, see the docs folder.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class TableParser : public BlockParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* ctor
|
||||
*
|
||||
* @method
|
||||
* @param {std::function<void(std::string&)>} parseLineCallback
|
||||
* @param {std::function<std::shared_ptr<BlockParser>(const std::string&
|
||||
* line)>} getBlockParserForLineCallback
|
||||
*/
|
||||
TableParser(
|
||||
std::function<void(std::string&)> parseLineCallback,
|
||||
std::function<std::shared_ptr<BlockParser>(const std::string& line)>
|
||||
getBlockParserForLineCallback
|
||||
)
|
||||
: BlockParser(parseLineCallback, getBlockParserForLineCallback)
|
||||
, isStarted(false)
|
||||
, isFinished(false)
|
||||
, currentBlock(0)
|
||||
, currentRow(0)
|
||||
{}
|
||||
|
||||
/**
|
||||
* IsStartingLine
|
||||
*
|
||||
* If the line has exact `|table>`, then it is starting the table.
|
||||
*
|
||||
* @method
|
||||
* @param {const std::string&} line
|
||||
* @return {bool}
|
||||
*/
|
||||
static bool IsStartingLine(const std::string& line)
|
||||
{
|
||||
static std::string matchString("|table>");
|
||||
return line == matchString;
|
||||
}
|
||||
|
||||
/**
|
||||
* AddLine
|
||||
*
|
||||
* Adding a line which has to be parsed.
|
||||
*
|
||||
* @method
|
||||
* @param {std::string&} line
|
||||
* @return {void}
|
||||
*/
|
||||
void AddLine(std::string& line) override
|
||||
{
|
||||
if (!this->isStarted && line == "|table>")
|
||||
{
|
||||
this->isStarted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->isStarted)
|
||||
{
|
||||
if (line == "- | - | -")
|
||||
{
|
||||
++this->currentBlock;
|
||||
this->currentRow = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (line == "|<table")
|
||||
{
|
||||
static std::string emptyLine = "";
|
||||
this->parseBlock(emptyLine);
|
||||
this->isFinished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->table.size() < this->currentBlock + 1)
|
||||
{
|
||||
this->table.push_back(std::vector<std::vector<std::string>>());
|
||||
}
|
||||
this->table[this->currentBlock].push_back(std::vector<std::string>());
|
||||
|
||||
std::string segment;
|
||||
std::stringstream streamToSplit(line);
|
||||
|
||||
while (std::getline(streamToSplit, segment, '|'))
|
||||
{
|
||||
this->parseLine(segment);
|
||||
this->table[this->currentBlock][this->currentRow].push_back(segment);
|
||||
}
|
||||
|
||||
++this->currentRow;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IsFinished
|
||||
*
|
||||
* A table ends with `|<table`.
|
||||
*
|
||||
* @method
|
||||
* @return {bool}
|
||||
*/
|
||||
bool IsFinished() const override { return this->isFinished; }
|
||||
|
||||
protected:
|
||||
bool isInlineBlockAllowed() const override { return false; }
|
||||
|
||||
bool isLineParserAllowed() const override { return true; }
|
||||
|
||||
void parseBlock(std::string&) override
|
||||
{
|
||||
result << "<table>";
|
||||
|
||||
bool hasHeader = false;
|
||||
bool hasFooter = false;
|
||||
bool isFirstBlock = true;
|
||||
uint32_t currentBlockNumber = 0;
|
||||
|
||||
if (this->table.size() > 1)
|
||||
{
|
||||
hasHeader = true;
|
||||
}
|
||||
|
||||
if (this->table.size() >= 3)
|
||||
{
|
||||
hasFooter = true;
|
||||
}
|
||||
|
||||
for (const std::vector<std::vector<std::string>>& block : this->table)
|
||||
{
|
||||
bool isInHeader = false;
|
||||
bool isInFooter = false;
|
||||
++currentBlockNumber;
|
||||
|
||||
if (hasHeader && isFirstBlock)
|
||||
{
|
||||
result << "<thead>";
|
||||
isInHeader = true;
|
||||
}
|
||||
else if (hasFooter && currentBlockNumber == this->table.size())
|
||||
{
|
||||
result << "<tfoot>";
|
||||
isInFooter = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result << "<tbody>";
|
||||
}
|
||||
|
||||
for (const std::vector<std::string>& row : block)
|
||||
{
|
||||
result << "<tr>";
|
||||
|
||||
for (const std::string& column : row)
|
||||
{
|
||||
if (isInHeader)
|
||||
{
|
||||
result << "<th>";
|
||||
}
|
||||
else
|
||||
{
|
||||
result << "<td>";
|
||||
}
|
||||
|
||||
result << column;
|
||||
|
||||
if (isInHeader)
|
||||
{
|
||||
result << "</th>";
|
||||
}
|
||||
else
|
||||
{
|
||||
result << "</td>";
|
||||
}
|
||||
}
|
||||
|
||||
result << "</tr>";
|
||||
}
|
||||
|
||||
if (isInHeader)
|
||||
{
|
||||
result << "</thead>";
|
||||
}
|
||||
else if (isInFooter)
|
||||
{
|
||||
result << "</tfoot>";
|
||||
}
|
||||
else
|
||||
{
|
||||
result << "</tbody>";
|
||||
}
|
||||
|
||||
isFirstBlock = false;
|
||||
}
|
||||
|
||||
result << "</table>";
|
||||
}
|
||||
|
||||
private:
|
||||
bool isStarted;
|
||||
bool isFinished;
|
||||
uint32_t currentBlock;
|
||||
uint32_t currentRow;
|
||||
std::vector<std::vector<std::vector<std::string>>> table;
|
||||
}; // class TableParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
118
maddy/unorderedlistparser.h
Normal file
118
maddy/unorderedlistparser.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "maddy/blockparser.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace maddy {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* UnorderedListParser
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class UnorderedListParser : public BlockParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* ctor
|
||||
*
|
||||
* @method
|
||||
* @param {std::function<void(std::string&)>} parseLineCallback
|
||||
* @param {std::function<std::shared_ptr<BlockParser>(const std::string&
|
||||
* line)>} getBlockParserForLineCallback
|
||||
*/
|
||||
UnorderedListParser(
|
||||
std::function<void(std::string&)> parseLineCallback,
|
||||
std::function<std::shared_ptr<BlockParser>(const std::string& line)>
|
||||
getBlockParserForLineCallback
|
||||
)
|
||||
: BlockParser(parseLineCallback, getBlockParserForLineCallback)
|
||||
, isStarted(false)
|
||||
, isFinished(false)
|
||||
{}
|
||||
|
||||
/**
|
||||
* IsStartingLine
|
||||
*
|
||||
* An unordered list starts with `* `.
|
||||
*
|
||||
* @method
|
||||
* @param {const std::string&} line
|
||||
* @return {bool}
|
||||
*/
|
||||
static bool IsStartingLine(const std::string& line)
|
||||
{
|
||||
static std::regex re("^[+*-] .*");
|
||||
return std::regex_match(line, re);
|
||||
}
|
||||
|
||||
/**
|
||||
* IsFinished
|
||||
*
|
||||
* @method
|
||||
* @return {bool}
|
||||
*/
|
||||
bool IsFinished() const override { return this->isFinished; }
|
||||
|
||||
protected:
|
||||
bool isInlineBlockAllowed() const override { return true; }
|
||||
|
||||
bool isLineParserAllowed() const override { return true; }
|
||||
|
||||
void parseBlock(std::string& line) override
|
||||
{
|
||||
bool isStartOfNewListItem = IsStartingLine(line);
|
||||
uint32_t indentation = getIndentationWidth(line);
|
||||
|
||||
static std::regex lineRegex("^([+*-] )");
|
||||
line = std::regex_replace(line, lineRegex, "");
|
||||
|
||||
if (!this->isStarted)
|
||||
{
|
||||
line = "<ul><li>" + line;
|
||||
this->isStarted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (indentation >= 2)
|
||||
{
|
||||
line = line.substr(2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.empty() || line.find("</li><li>") != std::string::npos ||
|
||||
line.find("</li></ol>") != std::string::npos ||
|
||||
line.find("</li></ul>") != std::string::npos)
|
||||
{
|
||||
line = "</li></ul>" + line;
|
||||
this->isFinished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isStartOfNewListItem)
|
||||
{
|
||||
line = "</li><li>" + line;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool isStarted;
|
||||
bool isFinished;
|
||||
}; // class UnorderedListParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
||||
Reference in New Issue
Block a user