aboutsummaryrefslogtreecommitdiff
path: root/inc/maddy
diff options
context:
space:
mode:
authorMaël Gassmann <mael.gassmann@students.bfh.ch>2022-02-27 14:06:15 +0100
committerMaël Gassmann <mael.gassmann@students.bfh.ch>2022-02-27 15:09:36 +0100
commit7d3c9ec7368a5d25235e818c2c147ba7e89f6567 (patch)
treed655395133c9ed44f48f72e9070abc42a715b850 /inc/maddy
[+] Added base project
Diffstat (limited to 'inc/maddy')
-rw-r--r--inc/maddy/LICENSE18
-rw-r--r--inc/maddy/blockparser.h203
-rw-r--r--inc/maddy/breaklineparser.h51
-rw-r--r--inc/maddy/checklistparser.h140
-rw-r--r--inc/maddy/codeblockparser.h137
-rw-r--r--inc/maddy/emphasizedparser.h53
-rw-r--r--inc/maddy/headlineparser.h138
-rw-r--r--inc/maddy/horizontallineparser.h106
-rw-r--r--inc/maddy/htmlparser.h127
-rw-r--r--inc/maddy/imageparser.h53
-rw-r--r--inc/maddy/inlinecodeparser.h51
-rw-r--r--inc/maddy/italicparser.h50
-rw-r--r--inc/maddy/lineparser.h46
-rw-r--r--inc/maddy/linkparser.h53
-rw-r--r--inc/maddy/orderedlistparser.h142
-rw-r--r--inc/maddy/paragraphparser.h115
-rw-r--r--inc/maddy/parser.h294
-rw-r--r--inc/maddy/parserconfig.h31
-rw-r--r--inc/maddy/quoteparser.h165
-rw-r--r--inc/maddy/strikethroughparser.h51
-rw-r--r--inc/maddy/strongparser.h59
-rw-r--r--inc/maddy/tableparser.h246
-rw-r--r--inc/maddy/unorderedlistparser.h133
23 files changed, 2462 insertions, 0 deletions
diff --git a/inc/maddy/LICENSE b/inc/maddy/LICENSE
new file mode 100644
index 0000000..3879e0d
--- /dev/null
+++ b/inc/maddy/LICENSE
@@ -0,0 +1,18 @@
+Copyright 2017, 2018, 2019, 2020 M. Petra Baranski
+
+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.
diff --git a/inc/maddy/blockparser.h b/inc/maddy/blockparser.h
new file mode 100644
index 0000000..9df4fa6
--- /dev/null
+++ b/inc/maddy/blockparser.h
@@ -0,0 +1,203 @@
+/*
+ * 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 <cctype>
+#include <algorithm>
+
+// -----------------------------------------------------------------------------
+
+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
diff --git a/inc/maddy/breaklineparser.h b/inc/maddy/breaklineparser.h
new file mode 100644
index 0000000..e3ee6fc
--- /dev/null
+++ b/inc/maddy/breaklineparser.h
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+#include <string>
+#include <regex>
+
+#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
diff --git a/inc/maddy/checklistparser.h b/inc/maddy/checklistparser.h
new file mode 100644
index 0000000..bfc487c
--- /dev/null
+++ b/inc/maddy/checklistparser.h
@@ -0,0 +1,140 @@
+/*
+ * 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("^- \\[[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("^\\[ \\]");
+ static std::string emptyBoxReplacement = "<input type=\"checkbox\"/>";
+ line = std::regex_replace(line, emptyBoxRegex, emptyBoxReplacement);
+
+ static std::regex boxRegex("^\\[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
diff --git a/inc/maddy/codeblockparser.h b/inc/maddy/codeblockparser.h
new file mode 100644
index 0000000..2cb4a97
--- /dev/null
+++ b/inc/maddy/codeblockparser.h
@@ -0,0 +1,137 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+#include <functional>
+#include <string>
+#include <regex>
+
+#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;
+ }
+ }
+
+ line += "\n";
+ }
+
+private:
+ bool isStarted;
+ bool isFinished;
+}; // class CodeBlockParser
+
+// -----------------------------------------------------------------------------
+
+} // namespace maddy
diff --git a/inc/maddy/emphasizedparser.h b/inc/maddy/emphasizedparser.h
new file mode 100644
index 0000000..6838d83
--- /dev/null
+++ b/inc/maddy/emphasizedparser.h
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+#include <string>
+#include <regex>
+
+#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("(?!.*`.*|.*<code>.*)_(?!.*`.*|.*<\\/code>.*)([^_]*)_(?!.*`.*|.*<\\/code>.*)");
+ static std::string replacement = "<em>$1</em>";
+
+ line = std::regex_replace(line, re, replacement);
+ }
+}; // class EmphasizedParser
+
+// -----------------------------------------------------------------------------
+
+} // namespace maddy
diff --git a/inc/maddy/headlineparser.h b/inc/maddy/headlineparser.h
new file mode 100644
index 0000000..9eafec5
--- /dev/null
+++ b/inc/maddy/headlineparser.h
@@ -0,0 +1,138 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+#include <functional>
+#include <string>
+#include <regex>
+
+#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
+ )
+ : BlockParser(parseLineCallback, getBlockParserForLineCallback)
+ {}
+
+ /**
+ * 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 false;
+ }
+
+ 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]);
+ }
+ }
+}; // class HeadlineParser
+
+// -----------------------------------------------------------------------------
+
+} // namespace maddy
diff --git a/inc/maddy/horizontallineparser.h b/inc/maddy/horizontallineparser.h
new file mode 100644
index 0000000..add6bf1
--- /dev/null
+++ b/inc/maddy/horizontallineparser.h
@@ -0,0 +1,106 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+#include <functional>
+#include <string>
+#include <regex>
+
+#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
diff --git a/inc/maddy/htmlparser.h b/inc/maddy/htmlparser.h
new file mode 100644
index 0000000..b6845a7
--- /dev/null
+++ b/inc/maddy/htmlparser.h
@@ -0,0 +1,127 @@
+/*
+ * 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
diff --git a/inc/maddy/imageparser.h b/inc/maddy/imageparser.h
new file mode 100644
index 0000000..3ac77ae
--- /dev/null
+++ b/inc/maddy/imageparser.h
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+#include <string>
+#include <regex>
+
+#include "maddy/lineparser.h"
+
+// -----------------------------------------------------------------------------
+
+namespace maddy {
+
+// -----------------------------------------------------------------------------
+
+/**
+ * ImageParser
+ *
+ * Has to be used before the `LinkParser`.
+ *
+ * @class
+ */
+class ImageParser : public LineParser
+{
+public:
+ /**
+ * Parse
+ *
+ * From Markdown: `![text](http://example.com/a.png)`
+ *
+ * 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("\\!\\[([^\\]]*)\\]\\(([^\\]]*)\\)");
+ static std::string replacement = "<img src=\"$2\" alt=\"$1\"/>";
+
+ line = std::regex_replace(line, re, replacement);
+ }
+}; // class ImageParser
+
+// -----------------------------------------------------------------------------
+
+} // namespace maddy
diff --git a/inc/maddy/inlinecodeparser.h b/inc/maddy/inlinecodeparser.h
new file mode 100644
index 0000000..3699501
--- /dev/null
+++ b/inc/maddy/inlinecodeparser.h
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+#include <string>
+#include <regex>
+
+#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
diff --git a/inc/maddy/italicparser.h b/inc/maddy/italicparser.h
new file mode 100644
index 0000000..ed59744
--- /dev/null
+++ b/inc/maddy/italicparser.h
@@ -0,0 +1,50 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+#include <string>
+#include <regex>
+
+#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("(?!.*`.*|.*<code>.*)\\*(?!.*`.*|.*<\\/code>.*)([^\\*]*)\\*(?!.*`.*|.*<\\/code>.*)");
+ static std::string replacement = "<i>$1</i>";
+ line = std::regex_replace(line, re, replacement);
+ }
+}; // class ItalicParser
+
+// -----------------------------------------------------------------------------
+
+} // namespace maddy
diff --git a/inc/maddy/lineparser.h b/inc/maddy/lineparser.h
new file mode 100644
index 0000000..55ec759
--- /dev/null
+++ b/inc/maddy/lineparser.h
@@ -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
diff --git a/inc/maddy/linkparser.h b/inc/maddy/linkparser.h
new file mode 100644
index 0000000..e382f21
--- /dev/null
+++ b/inc/maddy/linkparser.h
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+#include <string>
+#include <regex>
+
+#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("\\[([^\\]]*)\\]\\(([^\\]]*)\\)");
+ static std::string replacement = "<a href=\"$2\">$1</a>";
+
+ line = std::regex_replace(line, re, replacement);
+ }
+}; // class LinkParser
+
+// -----------------------------------------------------------------------------
+
+} // namespace maddy
diff --git a/inc/maddy/orderedlistparser.h b/inc/maddy/orderedlistparser.h
new file mode 100644
index 0000000..e41d3b1
--- /dev/null
+++ b/inc/maddy/orderedlistparser.h
@@ -0,0 +1,142 @@
+/*
+ * 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("^[1-9]+[0-9]*\\. ");
+ line = std::regex_replace(line, orderedlineRegex, "");
+ static std::regex unorderedlineRegex("^\\* ");
+ 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("^(?:[1-9]+[0-9]*\\. |\\* ).*");
+ return std::regex_match(line, re);
+ }
+}; // class OrderedListParser
+
+// -----------------------------------------------------------------------------
+
+} // namespace maddy
diff --git a/inc/maddy/paragraphparser.h b/inc/maddy/paragraphparser.h
new file mode 100644
index 0000000..303e3b0
--- /dev/null
+++ b/inc/maddy/paragraphparser.h
@@ -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
+ )
+ : BlockParser(parseLineCallback, getBlockParserForLineCallback)
+ , isStarted(false)
+ , isFinished(false)
+ {}
+
+ /**
+ * 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->isStarted)
+ {
+ line = "<p>" + line + " ";
+ this->isStarted = true;
+ return;
+ }
+
+ if (line.empty())
+ {
+ line += "</p>";
+ this->isFinished = true;
+ return;
+ }
+
+ line += " ";
+ }
+
+private:
+ bool isStarted;
+ bool isFinished;
+}; // class ParagraphParser
+
+// -----------------------------------------------------------------------------
+
+} // namespace maddy
diff --git a/inc/maddy/parser.h b/inc/maddy/parser.h
new file mode 100644
index 0000000..add4c45
--- /dev/null
+++ b/inc/maddy/parser.h
@@ -0,0 +1,294 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+#include <memory>
+#include <functional>
+#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/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:
+ /**
+ * ctor
+ *
+ * Initializes all `LineParser`
+ *
+ * @method
+ */
+ Parser(std::shared_ptr<ParserConfig> config = nullptr)
+ : config(config)
+ , breakLineParser(std::make_shared<BreakLineParser>())
+ , emphasizedParser(std::make_shared<EmphasizedParser>())
+ , imageParser(std::make_shared<ImageParser>())
+ , inlineCodeParser(std::make_shared<InlineCodeParser>())
+ , italicParser(std::make_shared<ItalicParser>())
+ , linkParser(std::make_shared<LinkParser>())
+ , strikeThroughParser(std::make_shared<StrikeThroughParser>())
+ , 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
+ this->imageParser->Parse(line);
+ this->linkParser->Parse(line);
+
+ // Attention! StrongParser has to be before EmphasizedParser
+ this->strongParser->Parse(line);
+
+ if (!this->config || this->config->isEmphasizedParserEnabled)
+ {
+ this->emphasizedParser->Parse(line);
+ }
+
+ this->strikeThroughParser->Parse(line);
+
+ this->inlineCodeParser->Parse(line);
+
+ this->italicParser->Parse(line);
+
+ this->breakLineParser->Parse(line);
+ }
+
+ std::shared_ptr<BlockParser>
+ getBlockParserForLine(const std::string& line) const
+ {
+ std::shared_ptr<BlockParser> parser;
+
+ if (maddy::CodeBlockParser::IsStartingLine(line))
+ {
+ parser = std::make_shared<maddy::CodeBlockParser>(
+ nullptr,
+ nullptr
+ );
+ }
+ else if (maddy::HeadlineParser::IsStartingLine(line))
+ {
+ parser = std::make_shared<maddy::HeadlineParser>(
+ nullptr,
+ nullptr
+ );
+ }
+ else if (maddy::HorizontalLineParser::IsStartingLine(line))
+ {
+ parser = std::make_shared<maddy::HorizontalLineParser>(
+ nullptr,
+ nullptr
+ );
+ }
+ else if (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 (maddy::TableParser::IsStartingLine(line))
+ {
+ parser = std::make_shared<maddy::TableParser>(
+ [this](std::string& line){ this->runLineParser(line); },
+ nullptr
+ );
+ }
+ else if (maddy::ChecklistParser::IsStartingLine(line))
+ {
+ parser = this->createChecklistParser();
+ }
+ else if (maddy::OrderedListParser::IsStartingLine(line))
+ {
+ parser = this->createOrderedListParser();
+ }
+ else if (maddy::UnorderedListParser::IsStartingLine(line))
+ {
+ parser = this->createUnorderedListParser();
+ }
+ else if (
+ this->config &&
+ !this->config->isHTMLWrappedInParagraph &&
+ 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
+ );
+ }
+
+ 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 (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 (maddy::OrderedListParser::IsStartingLine(line))
+ {
+ parser = this->createOrderedListParser();
+ }
+ else if (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 (maddy::OrderedListParser::IsStartingLine(line))
+ {
+ parser = this->createOrderedListParser();
+ }
+ else if (maddy::UnorderedListParser::IsStartingLine(line))
+ {
+ parser = this->createUnorderedListParser();
+ }
+
+ return parser;
+ }
+ );
+ }
+}; // class Parser
+
+// -----------------------------------------------------------------------------
+
+} // namespace maddy
diff --git a/inc/maddy/parserconfig.h b/inc/maddy/parserconfig.h
new file mode 100644
index 0000000..e1b10ae
--- /dev/null
+++ b/inc/maddy/parserconfig.h
@@ -0,0 +1,31 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+namespace maddy {
+
+// -----------------------------------------------------------------------------
+
+/**
+ * ParserConfig
+ *
+ * @class
+ */
+struct ParserConfig
+{
+ bool isEmphasizedParserEnabled;
+ bool isHTMLWrappedInParagraph;
+
+ ParserConfig()
+ : isEmphasizedParserEnabled(true)
+ , isHTMLWrappedInParagraph(true)
+ {}
+}; // class ParserConfig
+
+// -----------------------------------------------------------------------------
+
+} // namespace maddy
diff --git a/inc/maddy/quoteparser.h b/inc/maddy/quoteparser.h
new file mode 100644
index 0000000..a3b48d0
--- /dev/null
+++ b/inc/maddy/quoteparser.h
@@ -0,0 +1,165 @@
+/*
+ * 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("^\\>.*");
+ 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("^\\> ");
+ line = std::regex_replace(line, lineRegexWithSpace, "");
+ static std::regex lineRegexWithoutSpace("^\\>");
+ line = std::regex_replace(line, lineRegexWithoutSpace, "");
+
+ if (!line.empty())
+ {
+ line += " ";
+ }
+ }
+
+private:
+ bool isStarted;
+ bool isFinished;
+}; // class QuoteParser
+
+// -----------------------------------------------------------------------------
+
+} // namespace maddy
diff --git a/inc/maddy/strikethroughparser.h b/inc/maddy/strikethroughparser.h
new file mode 100644
index 0000000..2640459
--- /dev/null
+++ b/inc/maddy/strikethroughparser.h
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+#include <string>
+#include <regex>
+
+#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("(?!.*`.*|.*<code>.*)\\~\\~(?!.*`.*|.*<\\/code>.*)([^\\~]*)\\~\\~(?!.*`.*|.*<\\/code>.*)");
+ static std::string replacement = "<s>$1</s>";
+
+ line = std::regex_replace(line, re, replacement);
+ }
+}; // class StrikeThroughParser
+
+// -----------------------------------------------------------------------------
+
+} // namespace maddy
diff --git a/inc/maddy/strongparser.h b/inc/maddy/strongparser.h
new file mode 100644
index 0000000..589dacc
--- /dev/null
+++ b/inc/maddy/strongparser.h
@@ -0,0 +1,59 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+#include <string>
+#include <regex>
+
+#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{"(?!.*`.*|.*<code>.*)\\*\\*(?!.*`.*|.*<\\/code>.*)([^\\*\\*]*)\\*\\*(?!.*`.*|.*<\\/code>.*)"},
+ std::regex{"(?!.*`.*|.*<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
diff --git a/inc/maddy/tableparser.h b/inc/maddy/tableparser.h
new file mode 100644
index 0000000..c230cc6
--- /dev/null
+++ b/inc/maddy/tableparser.h
@@ -0,0 +1,246 @@
+/*
+ * This project is licensed under the MIT license. For more information see the
+ * LICENSE file.
+ */
+#pragma once
+
+// -----------------------------------------------------------------------------
+
+#include <functional>
+#include <string>
+#include <regex>
+
+#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
diff --git a/inc/maddy/unorderedlistparser.h b/inc/maddy/unorderedlistparser.h
new file mode 100644
index 0000000..0c1aa5d
--- /dev/null
+++ b/inc/maddy/unorderedlistparser.h
@@ -0,0 +1,133 @@
+/*
+ * 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