initial release 1.0.0

This commit is contained in:
M. Petra Baranski 2017-12-25 12:22:35 +01:00
commit 569794c4b6
46 changed files with 4073 additions and 0 deletions

12
.editorconfig Normal file
View File

@ -0,0 +1,12 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.{h,hh,hpp,c,cc,cpp,cxx}]
indent_size = 2

497
.gitignore vendored Normal file
View File

@ -0,0 +1,497 @@
# Created by https://www.gitignore.io/api/vim,c++,xcode,clion,macos,valgrind,visualstudio,visualstudiocode
### C++ ###
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
### CLion ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Ruby plugin and RubyMine
/.rakeTasks
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### CLion Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/sonarlint
### macOS ###
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Valgrind ###
# Callgrind output files
callgrind.out
callgrind.out.*
# Cachegrind output files
cachegrind.out
cachegrind.out.*
# Massif output files
massif.out
massif.out.*
# BBV output files
bb.out
bb.out.*
### Vim ###
# swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history
### Xcode ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
### Xcode Patch ###
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Uncomment the next line to ignore your web deploy settings.
# By default, sensitive information, such as encrypted password
# should be stored in the .pubxml.user file.
#*.pubxml
*.pubxml.user
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Typescript v1 declaration files
typings/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
### VisualStudio Patch ###
# By default, sensitive information, such as encrypted password
# should be stored in the .pubxml.user file.
# End of https://www.gitignore.io/api/vim,c++,xcode,clion,macos,valgrind,visualstudio,visualstudiocode
tmp

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "libs/gtest"]
path = libs/gtest
url = https://github.com/google/googletest.git

7
AUTHORS Normal file
View File

@ -0,0 +1,7 @@
The following authors have all licensed their contributions to maddy under the
licensing terms detailed in LICENSE.
(Authors keep copyright of their contributions, of course; they just grant
a license to everyone to use it as detailed in LICENSE.)
M. Petra Baranski (info@progsource.de)

55
CMakeLists.txt Normal file
View File

@ -0,0 +1,55 @@
# This project is licensed under the MITlicense. For more information see the
# LICENSE file.
cmake_minimum_required(VERSION 2.8)
project(maddy)
enable_testing()
# ------------------------------------------------------------------------------
set(MADDY_CPP_VERSION 14)
add_definitions(-DCPP_VERSION=${MADDY_CPP_VERSION})
# ------------------------------------------------------------------------------
set(CMAKE_BINARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/build)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})
# ------------------------------------------------------------------------------
set(MADDY_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
file(GLOB_RECURSE MADDY_TESTS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/tests/maddy/*.cpp)
# ------------------------------------------------------------------------------
set(
CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -g -std=c++${MADDY_CPP_VERSION} -Wall -Wpedantic -Wextra -Wno-ignored-qualifiers -fno-rtti -fno-exceptions" # -O2
)
# ------------------------------------------------------------------------------
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/libs/gtest/googlemock)
add_subdirectory(libs)
# ------------------------------------------------------------------------------
include_directories(
${LIBS_INCLUDE_DIRS}
${MADDY_INCLUDE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/tests
)
# ------------------------------------------------------------------------------
add_executable(
MaddyTests
${MADDY_TESTS_FILES}
${CMAKE_CURRENT_SOURCE_DIR}/tests/main.cpp
)
target_link_libraries(MaddyTests gmock_main)
add_test(MaddyTests ${CMAKE_CURRENT_SOURCE_DIR}/build/MaddyTests)

18
LICENSE Normal file
View File

@ -0,0 +1,18 @@
Copyright 2017 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.

51
README.md Normal file
View File

@ -0,0 +1,51 @@
# maddy
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Version: 1.0.0](https://img.shields.io/badge/Version-1.0.0-brightgreen.svg)](https://semver.org/)
maddy is a C++ Markdown to HTML **header-only** parser library.
## Supportes OS
It actually should work on any OS, that supports the C++14 standard library.
It is tested to work on:
* Linux (without exceptions and without RTTI)
## Dependencies
* C++14
## Why maddy?
When I was needing a Markdown parser in C++ I couldn't find any, that was
fitting my needs. So I simply wrote my own one.
## Markdown syntax
The supported syntax can be found in the [definitions docs](docs/definitions.md).
## HowTo use
To use maddy in your project, simply add the include path of maddy to yours
and in the code, you can then do the following:
```c++
#include <memory>
#include <string>
#include "maddy/parser.h"
std::stringstream markdownInput("");
std::shared_ptr<maddy::Parser> parser = std::make_shared<maddy::Parser>();
std::string htmlOutput = parser->Parse(markdownInput);
```
## How to contribute
There are different possibilities:
* Create a GitHub issue
* Create a pull request with an own branch (don't forget to put yourself in the
AUTHORS file)

303
docs/definitions.md Normal file
View File

@ -0,0 +1,303 @@
# Markdown Definitions
This specification defines which markdown syntax can be parsed by maddy.
There is no HTML allowed in the markdown syntax - or said otherwise - it might
destroy the output, if there was HTML in your markdown.
The Parser expects you to use spaces and not tabs for indentation in the
markdown.
## Headlines
```
# h1 heading
## h2 heading
### h3 heading
#### h4 heading
##### h5 heading
###### h6 heading
```
results in:
```html
<h1>h1 heading</h1>
<h2>h2 heading</h2>
<h3>h3 heading</h3>
<h4>h4 heading</h4>
<h5>h5 heading</h5>
<h6>h6 heading</h6>
```
## Links
```
[Text of the link](http://example.com)
```
results in
```html
<a href="http://example.com">Text of the link</a>
```
## Lists
### unordered
```
* unordered
* list
* items
```
results in
```html
<ul>
<li>unordered</li>
<li>list</li>
<li>items</li>
</ul>
```
```
* unorederd
* list
* items
* in
* an
* hierarchy
```
results in
```html
<ul>
<li>list</li>
<li>items
<ul>
<li>in</li>
<li>an</li>
</ul>
</li>
<li>hierarchy</li>
</ul>
```
### ordered
```
1. ordered
* list
* items
```
results in
```html
<ol>
<li>ordered</li>
<li>list</li>
<li>items</li>
</ol>
```
```
1. ordered
* list
1. items
* in
1. an
* hierarchy
```
results in
```html
<ol>
<li>ordered</li>
<li>list
<ol>
<li>items</li>
<li>in
<ol>an</ol>
</li>
<li>hierarchy</li>
</ol>
</li>
</ol>
```
### combination
```
* combination
* of
1. unordered and
* ordered
* list
```
results in
```html
<ul>
<li>combination</li>
<li>of
<ol>
<li>unordered and</li>
<li>ordered</li>
</ol>
</li>
<li>list</li>
</ul>
```
### checklist
```
- [ ] some item
- [ ] another item
- [x] some checked item
```
results in
```html
<ul class="checklist">
<li><label><input type="checkbox"/>some item
<ul class="checklist">
<li><label><input type="checkbox"/><span>another item</label></li>
</ul>
</label></li>
<li><label><input type="checkbox" checked="checked"/>some checked item</label></li>
</ul>
```
might not work in combination with other lists
## Code Blocks
```
some code
```
results in
```html
<pre><code>
some code
</code></pre>
```
## Inline code
some text `some inline code` some other text
results in
```html
some text <code>some inline code</code> some other text
```
## quotes
```
> Some quote
```
results in
```html
<blockqoute>
<p>Some quote</p>
</blockquote>
```
## bold
```
**bold text**
```
results in
```html
<strong>bold text</strong>
```
## emphasized
```
*emphasized text*
```
results in
```html
<em>emphasized text</em>
```
## strikethrough
```
~~striked through text~~
```
results in
```html
<s>striked through text</s>
```
## horizontal line
```
---
```
results in
```html
<hr/>
```
## Images
```
![Image alt text](http://example.com/example.png)
```
results in
```html
<img src="http://example.com/example.png" alt="Image alt text"/>
```
## Tables
```
|table>
Left header | middle header | last header
- | - | -
cell 1 | cell 2 | cell 3
cell 4 | cell 5 | cell 6
- | - | -
foot a | foot b | foot c
|<table
```
becomes
```html
<table>
<thead>
<tr>
<th>Left header</th>
<th>middle header</th>
<th>last header</th>
</tr>
</thead>
<tbody>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
</tr>
<tr>
<td>cell 4</td>
<td>cell 5</td>
<td>cell 6</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>foot a</td>
<td>foot b</td>
<td>foot c</td>
</tr>
</tfoot>
</table>
```
table header and footer are optional

190
include/maddy/blockparser.h Normal file
View 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

View 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

View 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

View 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

View 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

View 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

View 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: `![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

View 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

View 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

View 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

View 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

View 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
View 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
View 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

View 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

View 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
View 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

View 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

14
libs/CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
# This project is licensed under the MIT license. For more information see the
# LICENSE file.
cmake_minimum_required(VERSION 2.8)
set(LIBS_INCLUDE_DIRS
# -- googletest / -mock --------------------------------------------------------
${CMAKE_CURRENT_SOURCE_DIR}/gtest/googletest/include
${CMAKE_CURRENT_SOURCE_DIR}/gtest/googlemock/include
# ------------------------------------------------------------------------------
PARENT_SCOPE)
set(LIBS_SRC_FILES
PARENT_SCOPE)

1
libs/gtest Submodule

@ -0,0 +1 @@
Subproject commit 5490beb0602eab560fa3969a4410e11d94bf12af

View File

@ -0,0 +1,99 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/checklistparser.h"
// -----------------------------------------------------------------------------
class MADDY_CHECKLISTPARSER : public ::testing::Test
{
protected:
std::shared_ptr<maddy::ChecklistParser> clParser;
void
SetUp() override
{
std::function<std::shared_ptr<maddy::BlockParser>(const std::string& line)> getBlockParserForLineCallback = [](const std::string& line)
{
if (maddy::ChecklistParser::IsStartingLine(line))
{
return std::static_pointer_cast<maddy::BlockParser>(
std::make_shared<maddy::ChecklistParser>(nullptr, nullptr)
);
}
std::shared_ptr<maddy::BlockParser> empty;
return empty;
};
this->clParser = std::make_shared<maddy::ChecklistParser>(
nullptr,
getBlockParserForLineCallback
);
}
};
// -----------------------------------------------------------------------------
TEST_F(MADDY_CHECKLISTPARSER, IsStartingLineReturnsTrueWhenFacedWithBeginningOfList)
{
ASSERT_TRUE(maddy::ChecklistParser::IsStartingLine("- [ ] a"));
ASSERT_TRUE(maddy::ChecklistParser::IsStartingLine("- [x] b"));
}
TEST_F(MADDY_CHECKLISTPARSER, IsFinishedAlwaysReturnsFalseInTheBeginning)
{
ASSERT_FALSE(clParser->IsFinished());
}
TEST_F(MADDY_CHECKLISTPARSER, ItReplacesMarkdownWithAnHtmlChecklist)
{
std::vector<std::string> markdown = {
"- [ ] a"
, "- [x] b"
, ""
};
std::string expected = "<ul class=\"checklist\"><li><label><input type=\"checkbox\"/> a</label></li><li><label><input type=\"checkbox\" checked=\"checked\"/> b</label></li></ul>";
for (std::string md : markdown)
{
clParser->AddLine(md);
}
ASSERT_TRUE(clParser->IsFinished());
std::stringstream& output(clParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected, outputString);
}
TEST_F(MADDY_CHECKLISTPARSER, ItReplacesMarkdownWithAnHierachicalHtmlList)
{
std::vector<std::string> markdown = {
"- [ ] a"
, " - [ ] d"
, " - [ ] e"
, "- [ ] b"
, " - [x] c"
, ""
};
std::string expected = "<ul class=\"checklist\"><li><label><input type=\"checkbox\"/> a<ul class=\"checklist\"><li><label><input type=\"checkbox\"/> d</label></li><li><label><input type=\"checkbox\"/> e</label></li></ul></label></li><li><label><input type=\"checkbox\"/> b<ul class=\"checklist\"><li><label><input type=\"checkbox\" checked=\"checked\"/> c</label></li></ul></label></li></ul>";
for (std::string md : markdown)
{
clParser->AddLine(md);
}
ASSERT_TRUE(clParser->IsFinished());
std::stringstream& output(clParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected, outputString);
}

View File

@ -0,0 +1,61 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/codeblockparser.h"
// -----------------------------------------------------------------------------
class MADDY_CODEBLOCKPARSER : public ::testing::Test
{
protected:
std::shared_ptr<maddy::CodeBlockParser> cbParser;
void
SetUp() override
{
this->cbParser = std::make_shared<maddy::CodeBlockParser>(
nullptr,
nullptr
);
}
};
// -----------------------------------------------------------------------------
TEST_F(MADDY_CODEBLOCKPARSER, IsStartingLineReturnsTrueWhenFacedWithThreeSigns)
{
ASSERT_TRUE(maddy::CodeBlockParser::IsStartingLine("```"));
}
TEST_F(MADDY_CODEBLOCKPARSER, IsFinishedReturnsFalseInTheBeginning)
{
ASSERT_FALSE(cbParser->IsFinished());
}
TEST_F(MADDY_CODEBLOCKPARSER, ItReplacesMarkdownWithAnHtmlCodeBlock)
{
std::vector<std::string> markdown = {
"```"
, "some code"
, "some other code"
, "```"
};
std::string expected = "<pre><code>\nsome code\nsome other code\n</code></pre>";
for (std::string md : markdown)
{
cbParser->AddLine(md);
}
ASSERT_TRUE(cbParser->IsFinished());
std::stringstream& output(cbParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected, outputString);
}

View File

@ -0,0 +1,22 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/emphasizedparser.h"
// -----------------------------------------------------------------------------
TEST(MADDY_EMPHASIZEDPARSER, ItReplacesMarkdownWithEmphasizedHTML)
{
std::string text = "some text *bla* text testing *it* out";
std::string expected = "some text <em>bla</em> text testing <em>it</em> out";
auto emphasizedParser = std::make_shared<maddy::EmphasizedParser>();
emphasizedParser->Parse(text);
ASSERT_EQ(expected, text);
}

View File

@ -0,0 +1,88 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/headlineparser.h"
// -----------------------------------------------------------------------------
class MADDY_HEADLINEPARSER : public ::testing::Test
{
protected:
std::shared_ptr<maddy::HeadlineParser> hlParser;
void
SetUp() override
{
this->hlParser = std::make_shared<maddy::HeadlineParser>(
nullptr,
nullptr
);
}
};
// -----------------------------------------------------------------------------
TEST_F(MADDY_HEADLINEPARSER, IsStartingLineReturnsTrueWhenFacedWithOneToSixHashes)
{
ASSERT_TRUE(maddy::HeadlineParser::IsStartingLine("# a"));
ASSERT_TRUE(maddy::HeadlineParser::IsStartingLine("## a"));
ASSERT_TRUE(maddy::HeadlineParser::IsStartingLine("### a"));
ASSERT_TRUE(maddy::HeadlineParser::IsStartingLine("#### a"));
ASSERT_TRUE(maddy::HeadlineParser::IsStartingLine("##### a"));
ASSERT_TRUE(maddy::HeadlineParser::IsStartingLine("###### a"));
}
TEST_F(MADDY_HEADLINEPARSER, IsFinishedAlwaysReturnsTrue)
{
ASSERT_TRUE(hlParser->IsFinished());
}
TEST_F(MADDY_HEADLINEPARSER, ItReplacesMarkdownWithAnHtmlHeadline)
{
std::vector<std::string> markdown = {
"# a"
, "## a"
, "### a"
, "#### a"
, "##### a"
, "###### a"
};
std::vector<std::string> expected = {
"<h1>a</h1>"
, "<h2>a</h2>"
, "<h3>a</h3>"
, "<h4>a</h4>"
, "<h5>a</h5>"
, "<h6>a</h6>"
};
for (uint8_t i = 0; i < 6; ++i)
{
hlParser->AddLine(markdown[i]);
std::stringstream& output(hlParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected[i], outputString);
hlParser->Clear();
}
}
TEST_F(MADDY_HEADLINEPARSER, ItReplacesInvalidMarkdownNotWithAnHtmlHeadline)
{
std::string markdown = "####### a";
std::string expected(markdown);
hlParser->AddLine(markdown);
std::stringstream& output(hlParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected, outputString);
}

View File

@ -0,0 +1,64 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/horizontallineparser.h"
// -----------------------------------------------------------------------------
class MADDY_HORIZONTALLINEPARSER : public ::testing::Test
{
protected:
std::shared_ptr<maddy::HorizontalLineParser> hlParser;
void
SetUp() override
{
this->hlParser = std::make_shared<maddy::HorizontalLineParser>(
nullptr,
nullptr
);
}
};
// -----------------------------------------------------------------------------
TEST_F(MADDY_HORIZONTALLINEPARSER, IsStartingLineReturnsTrueWhenFacedWithThreeDashes)
{
ASSERT_TRUE(maddy::HorizontalLineParser::IsStartingLine("---"));
}
TEST_F(MADDY_HORIZONTALLINEPARSER, IsFinishedAlwaysReturnsTrue)
{
ASSERT_TRUE(hlParser->IsFinished());
}
TEST_F(MADDY_HORIZONTALLINEPARSER, ItReplacesMarkdownWithAnHtmlLine)
{
std::string markdown = "---";
std::string expected = "<hr/>";
hlParser->AddLine(markdown);
std::stringstream& output(hlParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected, outputString);
}
TEST_F(MADDY_HORIZONTALLINEPARSER, ItReplacesInvalidMarkdownNotWithAnHtmlLine)
{
std::string markdown = "--- ";
std::string expected(markdown);
hlParser->AddLine(markdown);
std::stringstream& output(hlParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected, outputString);
}

View File

@ -0,0 +1,44 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/imageparser.h"
// -----------------------------------------------------------------------------
TEST(MADDY_IMAGEPARSER, ItReplacesMarkdownWithAnImage)
{
std::string text = "Some text ![Image Title](http://example.com/a.png)";
std::string expected = "Some text <img src=\"http://example.com/a.png\" alt=\"Image Title\"/>";
auto imageParser = std::make_shared<maddy::ImageParser>();
imageParser->Parse(text);
ASSERT_EQ(expected, text);
}
TEST(MADDY_IMAGEPARSER, ItReplacesMarkdownWithImages)
{
std::string text = "Some text ![Image Title](http://example.com/a.png) bla ![Image Title](http://example.com/a.png)";
std::string expected = "Some text <img src=\"http://example.com/a.png\" alt=\"Image Title\"/> bla <img src=\"http://example.com/a.png\" alt=\"Image Title\"/>";
auto imageParser = std::make_shared<maddy::ImageParser>();
imageParser->Parse(text);
ASSERT_EQ(expected, text);
}
TEST(MADDY_IMAGEPARSER, ItReplacesNoLinkMarkdownWithImages)
{
std::string text = "Some text [Image Title](http://example.com)";
std::string expected(text);
auto imageParser = std::make_shared<maddy::ImageParser>();
imageParser->Parse(text);
ASSERT_EQ(expected, text);
}

View File

@ -0,0 +1,22 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/inlinecodeparser.h"
// -----------------------------------------------------------------------------
TEST(MADDY_INLINECODEPARSER, ItReplacesMarkdownWithCodeHTML)
{
std::string text = "some text `bla` text testing `it` out";
std::string expected = "some text <code>bla</code> text testing <code>it</code> out";
auto emphasizedParser = std::make_shared<maddy::InlineCodeParser>();
emphasizedParser->Parse(text);
ASSERT_EQ(expected, text);
}

View File

@ -0,0 +1,50 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/linkparser.h"
// -----------------------------------------------------------------------------
TEST(MADDY_LINKPARSER, ItReplacesMarkdownWithALink)
{
std::string text = "Some text [Link Title](http://example.com)";
std::string expected = "Some text <a href=\"http://example.com\">Link Title</a>";
auto linkParser = std::make_shared<maddy::LinkParser>();
linkParser->Parse(text);
ASSERT_EQ(expected, text);
}
TEST(MADDY_LINKPARSER, ItReplacesMarkdownWithLinks)
{
std::string text = "Some text [Link Title](http://example.com) bla [Link Title](http://example.com)";
std::string expected = "Some text <a href=\"http://example.com\">Link Title</a> bla <a href=\"http://example.com\">Link Title</a>";
auto linkParser = std::make_shared<maddy::LinkParser>();
linkParser->Parse(text);
ASSERT_EQ(expected, text);
}
// -----------------------------------------------------------------------------
class DISABLED_MADDY_LINKPARSER : public ::testing::Test { };
// This test is disabled for now, so make sure, to first run the ImageParser
// before using the LinkParser
TEST_F(DISABLED_MADDY_LINKPARSER, ItReplacesNoImageMarkdownWithLinks)
{
std::string text = "Some text ![Image Title](http://example.com/example.png)";
std::string expected(text);
auto linkParser = std::make_shared<maddy::LinkParser>();
linkParser->Parse(text);
ASSERT_EQ(expected, text);
}

View File

@ -0,0 +1,98 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/orderedlistparser.h"
// -----------------------------------------------------------------------------
class MADDY_ORDEREDLISTPARSER : public ::testing::Test
{
protected:
std::shared_ptr<maddy::OrderedListParser> olParser;
void
SetUp() override
{
std::function<std::shared_ptr<maddy::BlockParser>(const std::string& line)> getBlockParserForLineCallback = [](const std::string& line)
{
if (maddy::OrderedListParser::IsStartingLine(line))
{
return std::static_pointer_cast<maddy::BlockParser>(
std::make_shared<maddy::OrderedListParser>(nullptr, nullptr)
);
}
std::shared_ptr<maddy::BlockParser> empty;
return empty;
};
this->olParser = std::make_shared<maddy::OrderedListParser>(
nullptr,
getBlockParserForLineCallback
);
}
};
// -----------------------------------------------------------------------------
TEST_F(MADDY_ORDEREDLISTPARSER, IsStartingLineReturnsTrueWhenFacedWithBeginningOfList)
{
ASSERT_TRUE(maddy::OrderedListParser::IsStartingLine("1. a"));
}
TEST_F(MADDY_ORDEREDLISTPARSER, IsFinishedAlwaysReturnsFalseInTheBeginning)
{
ASSERT_FALSE(olParser->IsFinished());
}
TEST_F(MADDY_ORDEREDLISTPARSER, ItReplacesMarkdownWithAnHtmlOrderedList)
{
std::vector<std::string> markdown = {
"1. a"
, "* b"
, ""
};
std::string expected = "<ol><li>a</li><li>b</li></ol>";
for (std::string md : markdown)
{
olParser->AddLine(md);
}
ASSERT_TRUE(olParser->IsFinished());
std::stringstream& output(olParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected, outputString);
}
TEST_F(MADDY_ORDEREDLISTPARSER, ItReplacesMarkdownWithAnHierachicalHtmlList)
{
std::vector<std::string> markdown = {
"1. a"
, " 1. d"
, " * e"
, "* b"
, " 1. c"
, ""
};
std::string expected = "<ol><li>a<ol><li>d</li><li>e</li></ol></li><li>b<ol><li>c</li></ol></li></ol>";
for (std::string md : markdown)
{
olParser->AddLine(md);
}
ASSERT_TRUE(olParser->IsFinished());
std::stringstream& output(olParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected, outputString);
}

View File

@ -0,0 +1,59 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/paragraphparser.h"
// -----------------------------------------------------------------------------
class MADDY_PARAGRAPHPARSER : public ::testing::Test
{
protected:
std::shared_ptr<maddy::ParagraphParser> pParser;
void
SetUp() override
{
this->pParser = std::make_shared<maddy::ParagraphParser>(
nullptr,
nullptr
);
}
};
// -----------------------------------------------------------------------------
TEST_F(MADDY_PARAGRAPHPARSER, IsStartingLineReturnsTrueWhenFacedWithThreeDashes)
{
ASSERT_TRUE(maddy::ParagraphParser::IsStartingLine("---"));
}
TEST_F(MADDY_PARAGRAPHPARSER, IsFinishedReturnsFalseInTheBeginning)
{
ASSERT_FALSE(pParser->IsFinished());
}
TEST_F(MADDY_PARAGRAPHPARSER, ItReplacesMarkdownWithAnHtmlLine)
{
std::vector<std::string> markdown = {
"Some text"
, "and some other text"
, ""
};
std::string expected = "<p>Some text and some other text </p>";
for (std::string md : markdown)
{
pParser->AddLine(md);
}
ASSERT_TRUE(pParser->IsFinished());
std::stringstream& output(pParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected, outputString);
}

View File

@ -0,0 +1,21 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include "gmock/gmock.h"
#include "maddy/parser.h"
#include "maddy/test_maddy_parser.h"
// -----------------------------------------------------------------------------
TEST(MADDY_PARSER, ItShouldParse)
{
auto parser = std::make_shared<maddy::Parser>();
std::stringstream markdown(testMarkdown);
const std::string output = parser->Parse(markdown);
ASSERT_EQ(testHtml, output);
}

View File

@ -0,0 +1,70 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#pragma once
#include <string>
const std::string testMarkdown = "# This is a test\n\
\n\
This should result in a praragraph\n\
it's that simple.\n\
\n\
* an unordered list\n\
* with some **hierarchy**\n\
1. and an *ordered*\n\
* list\n\
* directly\n\
* inside\n\
\n\
```\n\
var c = 'blub';\n\
```\n\
\n\
> A Quote\n\
>\n\
> With some ~~text~~ blocks inside\n\
>\n\
> * even a list\n\
> * should be\n\
> * possible\n\
>\n\
\n\
And well `inline code` should also work.\n\
\n\
## Another Headline\n\
\n\
And not to forget [link to progsource](http://progsource.de) should work.\n\
And well - let's see how an image would be shown:\n\
\n\
![an image](http://progsource.de/img/progsource.png)\n\
\n\
---\n\
\n\
### and more headlines\n\
\n\
- [ ] how\n\
- [ ] about\n\
- [ ] a\n\
- [x] nice\n\
- [x] check\n\
- [ ] list\n\
\n\
#### even a table\n\
\n\
|table>\n\
Left header|middle header|last header\n\
- | - | -\n\
cell 1|cell **2**|cell 3\n\
cell 4|cell 5|cell 6\n\
- | - | -\n\
foot a|foot b|foot c\n\
|<table\n\
\n\
##### h5\n\
###### h6\n\
\n\
";
const std::string testHtml = "<h1>This is a test</h1><p>This should result in a praragraph it's that simple. </p><ul><li>an unordered list<ul><li>with some <strong>hierarchy</strong><ol><li>and an <em>ordered</em></li><li>list</li><li>directly</li></ol></li><li>inside</li></ul></li></ul><pre><code>\nvar c = 'blub';\n</code></pre><blockquote><p>A Quote </p><p>With some <s>text</s> blocks inside </p><ul><li>even a list </li><li>should be </li><li>possible </li></ul></blockquote><p>And well <code>inline code</code> should also work. </p><h2>Another Headline</h2><p>And not to forget <a href=\"http://progsource.de\">link to progsource</a> should work. And well - let's see how an image would be shown: </p><p><img src=\"http://progsource.de/img/progsource.png\" alt=\"an image\"/> </p><hr/><h3>and more headlines</h3><ul class=\"checklist\"><li><label><input type=\"checkbox\"/> how</label></li><li><label><input type=\"checkbox\"/> about<ul class=\"checklist\"><li><label><input type=\"checkbox\"/> a</label></li><li><label><input type=\"checkbox\" checked=\"checked\"/> nice</label></li></ul></label></li><li><label><input type=\"checkbox\" checked=\"checked\"/> check</label></li><li><label><input type=\"checkbox\"/> list</label></li></ul><h4>even a table</h4><table><thead><tr><th>Left header</th><th>middle header</th><th>last header</th></tr></thead><tbody><tr><td>cell 1</td><td>cell <strong>2</strong></td><td>cell 3</td></tr><tr><td>cell 4</td><td>cell 5</td><td>cell 6</td></tr></tbody><tfoot><tr><td>foot a</td><td>foot b</td><td>foot c</td></tr></tfoot></table><h5>h5</h5><h6>h6</h6>";

View File

@ -0,0 +1,62 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/quoteparser.h"
// -----------------------------------------------------------------------------
class MADDY_QUOTEPARSER : public ::testing::Test
{
protected:
std::shared_ptr<maddy::QuoteParser> quoteParser;
void
SetUp() override
{
this->quoteParser = std::make_shared<maddy::QuoteParser>(
nullptr,
nullptr
);
}
};
// -----------------------------------------------------------------------------
TEST_F(MADDY_QUOTEPARSER, IsStartingLineReturnsTrueWhenFacedWithBeginningOfAQuote)
{
ASSERT_TRUE(maddy::QuoteParser::IsStartingLine("> a"));
}
TEST_F(MADDY_QUOTEPARSER, IsFinishedAlwaysReturnsFalseInTheBeginning)
{
ASSERT_FALSE(quoteParser->IsFinished());
}
TEST_F(MADDY_QUOTEPARSER, ItReplacesMarkdownWithAnHtmlBlockQuote)
{
std::vector<std::string> markdown = {
"> a"
, "> b"
, ">"
, "> c"
, ""
};
std::string expected = "<blockquote>a b c </blockquote>";
for (std::string md : markdown)
{
quoteParser->AddLine(md);
}
ASSERT_TRUE(quoteParser->IsFinished());
std::stringstream& output(quoteParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected, outputString);
}

View File

@ -0,0 +1,22 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/strikethroughparser.h"
// -----------------------------------------------------------------------------
TEST(MADDY_STRIKETHROUGHPARSER, ItReplacesMarkdownWithStrikeThroughHTML)
{
std::string text = "some text ~~bla~~ text testing ~~it~~ out";
std::string expected = "some text <s>bla</s> text testing <s>it</s> out";
auto strikeThroughParser = std::make_shared<maddy::StrikeThroughParser>();
strikeThroughParser->Parse(text);
ASSERT_EQ(expected, text);
}

View File

@ -0,0 +1,33 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/strongparser.h"
// -----------------------------------------------------------------------------
TEST(MADDY_STRONGPARSER, ItReplacesMarkdownWithStrongHTML)
{
std::string text = "some text **bla** text testing **it** out";
std::string expected = "some text <strong>bla</strong> text testing <strong>it</strong> out";
auto strongParser = std::make_shared<maddy::StrongParser>();
strongParser->Parse(text);
ASSERT_EQ(expected, text);
}
TEST(MADDY_STRONGPARSER, ItReplacesEmphasizedMarkdownNotWithStrongHTML)
{
std::string text = "some text *bla* text testing **it** out";
std::string expected = "some text *bla* text testing <strong>it</strong> out";
auto strongParser = std::make_shared<maddy::StrongParser>();
strongParser->Parse(text);
ASSERT_EQ(expected, text);
}

View File

@ -0,0 +1,66 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/tableparser.h"
// -----------------------------------------------------------------------------
class MADDY_TABLEPARSER : public ::testing::Test
{
protected:
std::shared_ptr<maddy::TableParser> tableParser;
void
SetUp() override
{
this->tableParser = std::make_shared<maddy::TableParser>(
nullptr,
nullptr
);
}
};
// -----------------------------------------------------------------------------
TEST_F(MADDY_TABLEPARSER, IsStartingLineReturnsTrueWhenFacedWithTheBeginningOfATable)
{
ASSERT_EQ(true, maddy::TableParser::IsStartingLine("|table>"));
}
TEST_F(MADDY_TABLEPARSER, IsFinishedReturnsFalseInTheBeginning)
{
ASSERT_FALSE(tableParser->IsFinished());
}
TEST_F(MADDY_TABLEPARSER, ItReplacesMarkdownWithAnHtmlTable)
{
std::vector<std::string> markdown = {
"|table>"
, "Left header|middle header|last header"
, "- | - | -"
, "cell 1|cell 2|cell 3"
, "cell 4|cell 5|cell 6"
, "- | - | -"
, "foot a|foot b|foot c"
, "|<table"
};
std::string expected = "<table><thead><tr><th>Left header</th><th>middle header</th><th>last header</th></tr></thead><tbody><tr><td>cell 1</td><td>cell 2</td><td>cell 3</td></tr><tr><td>cell 4</td><td>cell 5</td><td>cell 6</td></tr></tbody><tfoot><tr><td>foot a</td><td>foot b</td><td>foot c</td></tr></tfoot></table>";
for (std::string md : markdown)
{
tableParser->AddLine(md);
}
ASSERT_TRUE(tableParser->IsFinished());
std::stringstream& output(tableParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected, outputString);
}

View File

@ -0,0 +1,98 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <memory>
#include "gmock/gmock.h"
#include "maddy/unorderedlistparser.h"
// -----------------------------------------------------------------------------
class MADDY_UNORDEREDLISTPARSER : public ::testing::Test
{
protected:
std::shared_ptr<maddy::UnorderedListParser> ulParser;
void
SetUp() override
{
std::function<std::shared_ptr<maddy::BlockParser>(const std::string& line)> getBlockParserForLineCallback = [](const std::string& line)
{
if (maddy::UnorderedListParser::IsStartingLine(line))
{
return std::static_pointer_cast<maddy::BlockParser>(
std::make_shared<maddy::UnorderedListParser>(nullptr, nullptr)
);
}
std::shared_ptr<maddy::BlockParser> empty;
return empty;
};
this->ulParser = std::make_shared<maddy::UnorderedListParser>(
nullptr,
getBlockParserForLineCallback
);
}
};
// -----------------------------------------------------------------------------
TEST_F(MADDY_UNORDEREDLISTPARSER, IsStartingLineReturnsTrueWhenFacedWithBeginningOfList)
{
ASSERT_TRUE(maddy::UnorderedListParser::IsStartingLine("* a"));
}
TEST_F(MADDY_UNORDEREDLISTPARSER, IsFinishedAlwaysReturnsFalseInTheBeginning)
{
ASSERT_FALSE(ulParser->IsFinished());
}
TEST_F(MADDY_UNORDEREDLISTPARSER, ItReplacesMarkdownWithAnHtmlUnorderedList)
{
std::vector<std::string> markdown = {
"* a"
, "* b"
, ""
};
std::string expected = "<ul><li>a</li><li>b</li></ul>";
for (std::string md : markdown)
{
ulParser->AddLine(md);
}
ASSERT_TRUE(ulParser->IsFinished());
std::stringstream& output(ulParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected, outputString);
}
TEST_F(MADDY_UNORDEREDLISTPARSER, ItReplacesMarkdownWithAnHierachicalHtmlList)
{
std::vector<std::string> markdown = {
"* a"
, " * d"
, " * e"
, "* b"
, " * c"
, ""
};
std::string expected = "<ul><li>a<ul><li>d</li><li>e</li></ul></li><li>b<ul><li>c</li></ul></li></ul>";
for (std::string md : markdown)
{
ulParser->AddLine(md);
}
ASSERT_TRUE(ulParser->IsFinished());
std::stringstream& output(ulParser->GetResult());
const std::string& outputString = output.str();
ASSERT_EQ(expected, outputString);
}

14
tests/main.cpp Normal file
View File

@ -0,0 +1,14 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include "gmock/gmock.h"
#include "gtest/gtest.h"
// -----------------------------------------------------------------------------
int main (int argc, char** argv) {
::testing::GTEST_FLAG(throw_on_failure) = false;
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}