mirror of
https://github.com/quitesimpleorg/qsmaddy.git
synced 2025-06-26 21:22:05 +02:00
initial release 1.0.0
This commit is contained in:
190
include/maddy/blockparser.h
Normal file
190
include/maddy/blockparser.h
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
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)
|
||||
{}
|
||||
|
||||
/**
|
||||
* 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 = 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
|
140
include/maddy/checklistparser.h
Normal file
140
include/maddy/checklistparser.h
Normal file
@ -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
|
137
include/maddy/codeblockparser.h
Normal file
137
include/maddy/codeblockparser.h
Normal file
@ -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
|
53
include/maddy/emphasizedparser.h
Normal file
53
include/maddy/emphasizedparser.h
Normal file
@ -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("\\*([^\\*]*)\\*");
|
||||
static std::string replacement = "<em>$1</em>";
|
||||
|
||||
line = std::regex_replace(line, re, replacement);
|
||||
}
|
||||
}; // class EmphasizedParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
138
include/maddy/headlineparser.h
Normal file
138
include/maddy/headlineparser.h
Normal file
@ -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
|
106
include/maddy/horizontallineparser.h
Normal file
106
include/maddy/horizontallineparser.h
Normal file
@ -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
|
53
include/maddy/imageparser.h
Normal file
53
include/maddy/imageparser.h
Normal file
@ -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: ``
|
||||
*
|
||||
* 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
|
51
include/maddy/inlinecodeparser.h
Normal file
51
include/maddy/inlinecodeparser.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 <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
|
39
include/maddy/lineparser.h
Normal file
39
include/maddy/lineparser.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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:
|
||||
/**
|
||||
* 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
|
53
include/maddy/linkparser.h
Normal file
53
include/maddy/linkparser.h
Normal file
@ -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
|
142
include/maddy/orderedlistparser.h
Normal file
142
include/maddy/orderedlistparser.h
Normal file
@ -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\\. ");
|
||||
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\\. |\\* ).*");
|
||||
return std::regex_match(line, re);
|
||||
}
|
||||
}; // class OrderedListParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
114
include/maddy/paragraphparser.h
Normal file
114
include/maddy/paragraphparser.h
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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. So 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
|
255
include/maddy/parser.h
Normal file
255
include/maddy/parser.h
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. For more information see the
|
||||
* LICENSE file.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
// BlockParser
|
||||
#include "maddy/checklistparser.h"
|
||||
#include "maddy/codeblockparser.h"
|
||||
#include "maddy/headlineparser.h"
|
||||
#include "maddy/horizontallineparser.h"
|
||||
#include "maddy/orderedlistparser.h"
|
||||
#include "maddy/paragraphparser.h"
|
||||
#include "maddy/quoteparser.h"
|
||||
#include "maddy/tableparser.h"
|
||||
#include "maddy/unorderedlistparser.h"
|
||||
|
||||
// LineParser
|
||||
#include "maddy/emphasizedparser.h"
|
||||
#include "maddy/imageparser.h"
|
||||
#include "maddy/inlinecodeparser.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()
|
||||
: emphasizedParser(std::make_shared<EmphasizedParser>())
|
||||
, imageParser(std::make_shared<ImageParser>())
|
||||
, inlineCodeParser(std::make_shared<InlineCodeParser>())
|
||||
, linkParser(std::make_shared<LinkParser>())
|
||||
, strikeThroughParser(std::make_shared<StrikeThroughParser>())
|
||||
, strongParser(std::make_shared<StrongParser>())
|
||||
{}
|
||||
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* @method
|
||||
* @param {const std::stringstream&} markdown
|
||||
* @return {std::string} HTML
|
||||
*/
|
||||
std::string
|
||||
Parse(std::stringstream& 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<EmphasizedParser> emphasizedParser;
|
||||
std::shared_ptr<ImageParser> imageParser;
|
||||
std::shared_ptr<InlineCodeParser> inlineCodeParser;
|
||||
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);
|
||||
this->emphasizedParser->Parse(line);
|
||||
|
||||
this->strikeThroughParser->Parse(line);
|
||||
|
||||
this->inlineCodeParser->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 (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
|
165
include/maddy/quoteparser.h
Normal file
165
include/maddy/quoteparser.h
Normal file
@ -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
|
51
include/maddy/strikethroughparser.h
Normal file
51
include/maddy/strikethroughparser.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 <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("\\~\\~([^\\~]*)\\~\\~");
|
||||
static std::string replacement = "<s>$1</s>";
|
||||
|
||||
line = std::regex_replace(line, re, replacement);
|
||||
}
|
||||
}; // class StrikeThroughParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
53
include/maddy/strongparser.h
Normal file
53
include/maddy/strongparser.h
Normal file
@ -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 {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* StrongParser
|
||||
*
|
||||
* Has to be used before the `EmphasizedParser`.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class StrongParser : public LineParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* From Markdown: `text **text**`
|
||||
*
|
||||
* To HTML: `text <strong>text</strong>`
|
||||
*
|
||||
* @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 = "<strong>$1</strong>";
|
||||
|
||||
line = std::regex_replace(line, re, replacement);
|
||||
}
|
||||
}; // class StrongParser
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
} // namespace maddy
|
246
include/maddy/tableparser.h
Normal file
246
include/maddy/tableparser.h
Normal file
@ -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
|
133
include/maddy/unorderedlistparser.h
Normal file
133
include/maddy/unorderedlistparser.h
Normal file
@ -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
|
Reference in New Issue
Block a user