mirror of
https://git.wownero.com/wownero/onion-wownero-blockchain-explorer.git
synced 2024-08-15 00:33:12 +00:00
first commit
This commit is contained in:
commit
98661d9b67
65 changed files with 16558 additions and 0 deletions
17
ext/mstch/CMakeLists.txt
Normal file
17
ext/mstch/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
cmake_minimum_required(VERSION 3.0.2)
|
||||
project(mstch)
|
||||
|
||||
option(WITH_UNIT_TESTS "enable building unit test executable" OFF)
|
||||
option(WITH_BENCHMARK "enable building benchmark executable" OFF)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
|
||||
set(mstch_VERSION 1.0.1)
|
||||
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wextra -O3")
|
||||
endif()
|
||||
|
||||
add_subdirectory(src)
|
22
ext/mstch/LICENSE
Normal file
22
ext/mstch/LICENSE
Normal file
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Daniel Sipka
|
||||
|
||||
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.
|
||||
|
281
ext/mstch/README.md
Normal file
281
ext/mstch/README.md
Normal file
|
@ -0,0 +1,281 @@
|
|||
# mstch - {{mustache}} templates in C++11
|
||||
|
||||

|
||||
|
||||
mstch is a complete implementation of [{{mustache}}](http://mustache.github.io/)
|
||||
templates using modern C++. It's compliant with [specifications](https://github.com/mustache/spec)
|
||||
v1.1.3, including the lambda module.
|
||||
|
||||
[](http://melpon.org/wandbox/permlink/EqyOe7IBRYPGVk5f)
|
||||
[](http://badge.fury.io/gh/no1msd%2Fmstch)
|
||||
[](https://travis-ci.org/no1msd/mstch)
|
||||
[](https://ci.appveyor.com/project/no1msd/mstch)
|
||||
|
||||
## Supported features
|
||||
|
||||
mstch supports the complete feature set described in the `mustache(5)` [manpage](http://mustache.github.com/mustache.5.html):
|
||||
|
||||
- JSON-like data structure using [Boost.Variant](http://www.boost.org/libs/variant)
|
||||
- variables, sections, inverted sections
|
||||
- partials
|
||||
- changing the delimiter
|
||||
- C++11 lambdas
|
||||
- C++ objects as view models
|
||||
|
||||
## Basic usage
|
||||
|
||||
```c++
|
||||
#include <iostream>
|
||||
#include <mstch/mstch.hpp>
|
||||
|
||||
int main() {
|
||||
std::string view{"{{#names}}Hi {{name}}!\n{{/names}}"};
|
||||
mstch::map context{
|
||||
{"names", mstch::array{
|
||||
mstch::map{{"name", std::string{"Chris"}}},
|
||||
mstch::map{{"name", std::string{"Mark"}}},
|
||||
mstch::map{{"name", std::string{"Scott"}}},
|
||||
}}
|
||||
};
|
||||
|
||||
std::cout << mstch::render(view, context) << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The output of this example will be:
|
||||
|
||||
```html
|
||||
Hi Chris!
|
||||
Hi Mark!
|
||||
Hi Scott!
|
||||
```
|
||||
|
||||
### Data structure
|
||||
|
||||
The types in the example above, `mstch::array` and `mstch::map` are actually
|
||||
aliases for standard types:
|
||||
|
||||
```c++
|
||||
using map = std::map<const std::string, node>;
|
||||
using array = std::vector<node>;
|
||||
```
|
||||
|
||||
`mstch::node` is a `boost::variant` that can hold a `std::string`, `int`,
|
||||
`double`, `bool`, `mstch::lambda` or a `std::shared_ptr<mstch::object>`
|
||||
(see below), also a map or an array recursively. Essentially it works just like
|
||||
a JSON object.
|
||||
|
||||
Note that when using a `std::string` as value you must explicitly specify the
|
||||
type, since a `const char*` literal like `"foobar"` would be implicitly
|
||||
converted to `bool`. Alternatively you can use [C++14 string_literals](http://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s)
|
||||
if your compiler supports it.
|
||||
|
||||
## Advanced usage
|
||||
|
||||
### Partials
|
||||
|
||||
Partials can be passed in a `std::map` as the third parameter of the
|
||||
`mstch::render` function:
|
||||
|
||||
```c++
|
||||
std::string view{"{{#names}}{{> user}}{{/names}}"};
|
||||
std::string user_view{"<strong>{{name}}\n</strong>"};
|
||||
mstch::map context{
|
||||
{"names", mstch::array{
|
||||
mstch::map{{"name", std::string{"Chris"}}},
|
||||
mstch::map{{"name", std::string{"Mark"}}},
|
||||
mstch::map{{"name", std::string{"Scott"}}},
|
||||
}}
|
||||
};
|
||||
|
||||
std::cout << mstch::render(view, context, {{"user", user_view}}) << std::endl;
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```html
|
||||
<strong>Chris</strong>
|
||||
<strong>Mark</strong>
|
||||
<strong>Scott</strong>
|
||||
```
|
||||
|
||||
### Lambdas
|
||||
|
||||
C++11 lambda expressions can be used to add logic to your templates. Like a
|
||||
`const char*` literal, lambdas can be implicitly converted to `bool`, so they
|
||||
must be wrapped in a `mstch::lambda` object when used in a `mstch::node`. The
|
||||
lambda expression passed to `mstch::lambda` must itself return a `mstch::node`.
|
||||
The returned node will be rendered to a string, then it will be parsed as a
|
||||
template.
|
||||
|
||||
The lambda expression accepts either no parameters:
|
||||
|
||||
```c++
|
||||
std::string view{"Hello {{lambda}}!"};
|
||||
mstch::map context{
|
||||
{"lambda", mstch::lambda{[]() -> mstch::node {
|
||||
return std::string{"World"};
|
||||
}}}
|
||||
};
|
||||
|
||||
std::cout << mstch::render(view, context) << std::endl;
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```html
|
||||
Hello World!
|
||||
```
|
||||
|
||||
Or it accepts a `const std::string&` that gets the unrendered literal block:
|
||||
|
||||
```c++
|
||||
std::string view{"{{#bold}}{{yay}} :){{/bold}}"};
|
||||
mstch::map context{
|
||||
{"yay", std::string{"Yay!"}},
|
||||
{"bold", mstch::lambda{[](const std::string& text) -> mstch::node {
|
||||
return "<b>" + text + "</b>";
|
||||
}}}
|
||||
};
|
||||
|
||||
std::cout << mstch::render(view, context) << std::endl;
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```html
|
||||
<b>Yay! :)</b>
|
||||
```
|
||||
|
||||
### Objects
|
||||
|
||||
Custom objects can also be used as context for rendering templates. The class
|
||||
must inherit from `mstch::object`, and register it's exported methods with
|
||||
`register_methods`. Exported methods must have the return type of `mstch::node`.
|
||||
Objects must be created as a `std::shared_ptr`.
|
||||
|
||||
```c++
|
||||
class example: public mstch::object {
|
||||
public:
|
||||
example(): m_value(1) {
|
||||
register_methods(this, {
|
||||
{"count", &example::count},
|
||||
{"names", &example::names}
|
||||
});
|
||||
}
|
||||
|
||||
mstch::node count() {
|
||||
return m_value++;
|
||||
}
|
||||
|
||||
mstch::node names() {
|
||||
return mstch::array{
|
||||
std::string{"Chris"}, std::string{"Mark"}, std::string{"Scott"}};
|
||||
}
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
};
|
||||
|
||||
std::string view{"{{#names}}<b>{{count}}</b>: {{.}}\n{{/names}}"};
|
||||
const auto context = std::make_shared<example>();
|
||||
|
||||
std::cout << mstch::render(view, context) << std::endl;
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```html
|
||||
<b>1</b>: Chris
|
||||
<b>2</b>: Mark
|
||||
<b>3</b>: Scott
|
||||
```
|
||||
|
||||
### Custom escape function
|
||||
|
||||
By default, mstch uses HTML escaping on the output, as per specification. This
|
||||
is not useful if your output is not HTML, so mstch provides a way to supply
|
||||
your own escape implementation. Just assign any callable object to the static
|
||||
`mstch::config::escape`, which is an initially empty
|
||||
`std::function<std::string(const std::string&)>`.
|
||||
|
||||
For example you can turn off escaping entirely with a lambda:
|
||||
|
||||
```c++
|
||||
mstch::config::escape = [](const std::string& str) -> std::string {
|
||||
return str;
|
||||
};
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- A C++ compiler with decent C++11 support. Currently tested with:
|
||||
- GCC 4.7, 4.8, 4.9, 5.1
|
||||
- clang 3.5, 3.6, 3.7 (both libstdc++ and libc++ are supported)
|
||||
- MSVC 2013, 2015
|
||||
- Boost 1.54+ for [Boost.Variant](http://www.boost.org/libs/variant)
|
||||
- CMake 3.0+ for building
|
||||
|
||||
## Using mstch in your project
|
||||
|
||||
If you are using CMake, the easiest way to include mstch in your project is to
|
||||
copy the whole directory to your source tree, and use `add_subdirectory` in your
|
||||
CMakeLists.txt. This will set a variable named `mstch_INCLUDE_DIR` that contains
|
||||
its include path, and add a static library target named `mstch`. For example:
|
||||
|
||||
```cmake
|
||||
add_subdirectory(external/mstch)
|
||||
include_directories(${mstch_INCLUDE_DIR})
|
||||
target_link_libraries(your_project mstch)
|
||||
```
|
||||
|
||||
If you prefer to install the library globally, you can simply do the following
|
||||
from the root of the source tree:
|
||||
|
||||
```bash
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake ..
|
||||
$ make
|
||||
$ make install
|
||||
```
|
||||
|
||||
The install command may require root privileges. This will also install CMake
|
||||
config files, so you can use use `find_package` in your CMakeLists.txt:
|
||||
|
||||
```cmake
|
||||
find_package(mstch)
|
||||
target_link_libraries(your_project mstch::mstch)
|
||||
```
|
||||
|
||||
## Running the unit tests
|
||||
|
||||
Unit tests are using the [Catch](https://github.com/philsquared/Catch) framework
|
||||
and [rapidjson](http://rapidjson.org/) to parse the
|
||||
[Mustache specifications](https://github.com/mustache/spec), all of which are
|
||||
included in the repository as git submodules. Various
|
||||
[Boost](http://www.boost.org/) libraries are also required to build them.
|
||||
|
||||
Don't forget to initialize submodules:
|
||||
|
||||
```bash
|
||||
$ git submodule init
|
||||
$ git submodule update
|
||||
```
|
||||
|
||||
To build and run the unit tests:
|
||||
|
||||
```bash
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -DWITH_UNIT_TESTS=ON ..
|
||||
$ make
|
||||
$ make test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
mstch is licensed under the [MIT license](https://github.com/no1msd/mstch/blob/master/LICENSE).
|
1
ext/mstch/cmake/mstch-config.cmake
Normal file
1
ext/mstch/cmake/mstch-config.cmake
Normal file
|
@ -0,0 +1 @@
|
|||
include("${CMAKE_CURRENT_LIST_DIR}/mstch-targets.cmake")
|
113
ext/mstch/include/mstch/mstch.hpp
Normal file
113
ext/mstch/include/mstch/mstch.hpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace mstch {
|
||||
|
||||
struct config {
|
||||
static std::function<std::string(const std::string&)> escape;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<class N>
|
||||
class object_t {
|
||||
public:
|
||||
const N& at(const std::string& name) const {
|
||||
cache[name] = (methods.at(name))();
|
||||
return cache[name];
|
||||
}
|
||||
|
||||
bool has(const std::string name) const {
|
||||
return methods.count(name) != 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
template<class S>
|
||||
void register_methods(S* s, std::map<std::string,N(S::*)()> methods) {
|
||||
for(auto& item: methods)
|
||||
this->methods.insert({item.first, std::bind(item.second, s)});
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::string, std::function<N()>> methods;
|
||||
mutable std::map<std::string, N> cache;
|
||||
};
|
||||
|
||||
template<class T, class N>
|
||||
class is_fun {
|
||||
private:
|
||||
using not_fun = char;
|
||||
using fun_without_args = char[2];
|
||||
using fun_with_args = char[3];
|
||||
template <typename U, U> struct really_has;
|
||||
template <typename C> static fun_without_args& test(
|
||||
really_has<N(C::*)() const, &C::operator()>*);
|
||||
template <typename C> static fun_with_args& test(
|
||||
really_has<N(C::*)(const std::string&) const,
|
||||
&C::operator()>*);
|
||||
template <typename> static not_fun& test(...);
|
||||
|
||||
public:
|
||||
static bool const no_args = sizeof(test<T>(0)) == sizeof(fun_without_args);
|
||||
static bool const has_args = sizeof(test<T>(0)) == sizeof(fun_with_args);
|
||||
};
|
||||
|
||||
template<class N>
|
||||
using node_renderer = std::function<std::string(const N& n)>;
|
||||
|
||||
template<class N>
|
||||
class lambda_t {
|
||||
public:
|
||||
template<class F>
|
||||
lambda_t(F f, typename std::enable_if<is_fun<F, N>::no_args>::type* = 0):
|
||||
fun([f](node_renderer<N> renderer, const std::string&) {
|
||||
return renderer(f());
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
template<class F>
|
||||
lambda_t(F f, typename std::enable_if<is_fun<F, N>::has_args>::type* = 0):
|
||||
fun([f](node_renderer<N> renderer, const std::string& text) {
|
||||
return renderer(f(text));
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
std::string operator()(node_renderer<N> renderer,
|
||||
const std::string& text = "") const
|
||||
{
|
||||
return fun(renderer, text);
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<std::string(node_renderer<N> renderer, const std::string&)> fun;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using node = boost::make_recursive_variant<
|
||||
std::nullptr_t, std::string, int, double, bool,
|
||||
internal::lambda_t<boost::recursive_variant_>,
|
||||
std::shared_ptr<internal::object_t<boost::recursive_variant_>>,
|
||||
std::map<const std::string, boost::recursive_variant_>,
|
||||
std::vector<boost::recursive_variant_>>::type;
|
||||
using object = internal::object_t<node>;
|
||||
using lambda = internal::lambda_t<node>;
|
||||
using map = std::map<const std::string, node>;
|
||||
using array = std::vector<node>;
|
||||
|
||||
std::string render(
|
||||
const std::string& tmplt,
|
||||
const node& root,
|
||||
const std::map<std::string,std::string>& partials =
|
||||
std::map<std::string,std::string>());
|
||||
|
||||
}
|
66
ext/mstch/src/CMakeLists.txt
Normal file
66
ext/mstch/src/CMakeLists.txt
Normal file
|
@ -0,0 +1,66 @@
|
|||
find_package(Boost 1.54 REQUIRED)
|
||||
|
||||
set(mstch_INCLUDE_DIR
|
||||
${PROJECT_SOURCE_DIR}/include CACHE STRING "mstch include directory")
|
||||
|
||||
# /home/mwo/crow-monero-test/ext/mstch
|
||||
message(${PROJECT_SOURCE_DIR})
|
||||
#
|
||||
include_directories(
|
||||
${Boost_INCLUDE_DIR})
|
||||
|
||||
set(SRC
|
||||
state/in_section.cpp
|
||||
state/outside_section.cpp
|
||||
state/render_state.hpp
|
||||
visitor/get_token.hpp
|
||||
visitor/has_token.hpp
|
||||
visitor/is_node_empty.hpp
|
||||
visitor/render_node.hpp
|
||||
visitor/render_section.hpp
|
||||
mstch.cpp
|
||||
render_context.cpp
|
||||
template_type.cpp
|
||||
token.cpp
|
||||
utils.cpp)
|
||||
|
||||
add_library(mstch STATIC ${SRC})
|
||||
#
|
||||
set_property(TARGET mstch PROPERTY VERSION ${mstch_VERSION})
|
||||
#
|
||||
#install(
|
||||
# TARGETS mstch EXPORT mstchTargets
|
||||
# LIBRARY DESTINATION lib
|
||||
# ARCHIVE DESTINATION lib)
|
||||
#
|
||||
#install(
|
||||
# FILES "${PROJECT_SOURCE_DIR}/include/mstch/mstch.hpp"
|
||||
# DESTINATION include/mstch
|
||||
# COMPONENT Devel)
|
||||
#
|
||||
#include(CMakePackageConfigHelpers)
|
||||
#write_basic_package_version_file(
|
||||
# "${CMAKE_CURRENT_BINARY_DIR}/mstch/mstch-config-version.cmake"
|
||||
# VERSION ${mstch_VERSION}
|
||||
# COMPATIBILITY AnyNewerVersion)
|
||||
#
|
||||
#export(
|
||||
# EXPORT mstchTargets
|
||||
# FILE "${CMAKE_CURRENT_BINARY_DIR}/mstch/mstch-targets.cmake"
|
||||
# NAMESPACE mstch::)
|
||||
#
|
||||
#configure_file(
|
||||
# "${PROJECT_SOURCE_DIR}/cmake/mstch-config.cmake"
|
||||
# "${CMAKE_CURRENT_BINARY_DIR}/mstch/mstch-config.cmake")
|
||||
#
|
||||
#install(
|
||||
# EXPORT mstchTargets
|
||||
# FILE mstch-targets.cmake
|
||||
# NAMESPACE mstch::
|
||||
# DESTINATION lib/cmake/mstch)
|
||||
#
|
||||
#install(FILES
|
||||
# "${PROJECT_SOURCE_DIR}/cmake/mstch-config.cmake"
|
||||
# "${CMAKE_CURRENT_BINARY_DIR}/mstch/mstch-config-version.cmake"
|
||||
# DESTINATION lib/cmake/mstch
|
||||
# COMPONENT Devel)
|
20
ext/mstch/src/mstch.cpp
Normal file
20
ext/mstch/src/mstch.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include <iostream>
|
||||
|
||||
#include "mstch/mstch.hpp"
|
||||
#include "render_context.hpp"
|
||||
|
||||
using namespace mstch;
|
||||
|
||||
std::function<std::string(const std::string&)> mstch::config::escape;
|
||||
|
||||
std::string mstch::render(
|
||||
const std::string& tmplt,
|
||||
const node& root,
|
||||
const std::map<std::string,std::string>& partials)
|
||||
{
|
||||
std::map<std::string, template_type> partial_templates;
|
||||
for (auto& partial: partials)
|
||||
partial_templates.insert({partial.first, {partial.second}});
|
||||
|
||||
return render_context(root, partial_templates).render(tmplt);
|
||||
}
|
72
ext/mstch/src/render_context.cpp
Normal file
72
ext/mstch/src/render_context.cpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
#include "render_context.hpp"
|
||||
#include "state/outside_section.hpp"
|
||||
#include "visitor/get_token.hpp"
|
||||
|
||||
using namespace mstch;
|
||||
|
||||
const mstch::node render_context::null_node;
|
||||
|
||||
render_context::push::push(render_context& context, const mstch::node& node):
|
||||
m_context(context)
|
||||
{
|
||||
context.m_nodes.emplace_front(node);
|
||||
context.m_node_ptrs.emplace_front(&node);
|
||||
context.m_state.push(std::unique_ptr<render_state>(new outside_section));
|
||||
}
|
||||
|
||||
render_context::push::~push() {
|
||||
m_context.m_nodes.pop_front();
|
||||
m_context.m_node_ptrs.pop_front();
|
||||
m_context.m_state.pop();
|
||||
}
|
||||
|
||||
std::string render_context::push::render(const template_type& templt) {
|
||||
return m_context.render(templt);
|
||||
}
|
||||
|
||||
render_context::render_context(
|
||||
const mstch::node& node,
|
||||
const std::map<std::string, template_type>& partials):
|
||||
m_partials(partials), m_nodes(1, node), m_node_ptrs(1, &node)
|
||||
{
|
||||
m_state.push(std::unique_ptr<render_state>(new outside_section));
|
||||
}
|
||||
|
||||
const mstch::node& render_context::find_node(
|
||||
const std::string& token,
|
||||
std::list<node const*> current_nodes)
|
||||
{
|
||||
if (token != "." && token.find('.') != std::string::npos)
|
||||
return find_node(token.substr(token.rfind('.') + 1),
|
||||
{&find_node(token.substr(0, token.rfind('.')), current_nodes)});
|
||||
else
|
||||
for (auto& node: current_nodes)
|
||||
if (visit(has_token(token), *node))
|
||||
return visit(get_token(token, *node), *node);
|
||||
return null_node;
|
||||
}
|
||||
|
||||
const mstch::node& render_context::get_node(const std::string& token) {
|
||||
return find_node(token, m_node_ptrs);
|
||||
}
|
||||
|
||||
std::string render_context::render(
|
||||
const template_type& templt, const std::string& prefix)
|
||||
{
|
||||
std::string output;
|
||||
bool prev_eol = true;
|
||||
for (auto& token: templt) {
|
||||
if (prev_eol && prefix.length() != 0)
|
||||
output += m_state.top()->render(*this, {prefix});
|
||||
output += m_state.top()->render(*this, token);
|
||||
prev_eol = token.eol();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string render_context::render_partial(
|
||||
const std::string& partial_name, const std::string& prefix)
|
||||
{
|
||||
return m_partials.count(partial_name) ?
|
||||
render(m_partials.at(partial_name), prefix) : "";
|
||||
}
|
51
ext/mstch/src/render_context.hpp
Normal file
51
ext/mstch/src/render_context.hpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <stack>
|
||||
|
||||
#include "mstch/mstch.hpp"
|
||||
#include "state/render_state.hpp"
|
||||
#include "template_type.hpp"
|
||||
|
||||
namespace mstch {
|
||||
|
||||
class render_context {
|
||||
public:
|
||||
class push {
|
||||
public:
|
||||
push(render_context& context, const mstch::node& node = {});
|
||||
~push();
|
||||
std::string render(const template_type& templt);
|
||||
private:
|
||||
render_context& m_context;
|
||||
};
|
||||
|
||||
render_context(
|
||||
const mstch::node& node,
|
||||
const std::map<std::string, template_type>& partials);
|
||||
const mstch::node& get_node(const std::string& token);
|
||||
std::string render(
|
||||
const template_type& templt, const std::string& prefix = "");
|
||||
std::string render_partial(
|
||||
const std::string& partial_name, const std::string& prefix);
|
||||
template<class T, class... Args>
|
||||
void set_state(Args&& ... args) {
|
||||
m_state.top() = std::unique_ptr<render_state>(
|
||||
new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
private:
|
||||
static const mstch::node null_node;
|
||||
const mstch::node& find_node(
|
||||
const std::string& token,
|
||||
std::list<node const*> current_nodes);
|
||||
std::map<std::string, template_type> m_partials;
|
||||
std::deque<mstch::node> m_nodes;
|
||||
std::list<const mstch::node*> m_node_ptrs;
|
||||
std::stack<std::unique_ptr<render_state>> m_state;
|
||||
};
|
||||
|
||||
}
|
34
ext/mstch/src/state/in_section.cpp
Normal file
34
ext/mstch/src/state/in_section.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include "in_section.hpp"
|
||||
#include "outside_section.hpp"
|
||||
#include "visitor/is_node_empty.hpp"
|
||||
#include "visitor/render_section.hpp"
|
||||
|
||||
using namespace mstch;
|
||||
|
||||
in_section::in_section(type type, const token& start_token):
|
||||
m_type(type), m_start_token(start_token), m_skipped_openings(0)
|
||||
{
|
||||
}
|
||||
|
||||
std::string in_section::render(render_context& ctx, const token& token) {
|
||||
if (token.token_type() == token::type::section_close)
|
||||
if (token.name() == m_start_token.name() && m_skipped_openings == 0) {
|
||||
auto& node = ctx.get_node(m_start_token.name());
|
||||
std::string out;
|
||||
|
||||
if (m_type == type::normal && !visit(is_node_empty(), node))
|
||||
out = visit(render_section(ctx, m_section, m_start_token.delims()), node);
|
||||
else if (m_type == type::inverted && visit(is_node_empty(), node))
|
||||
out = render_context::push(ctx).render(m_section);
|
||||
|
||||
ctx.set_state<outside_section>();
|
||||
return out;
|
||||
} else
|
||||
m_skipped_openings--;
|
||||
else if (token.token_type() == token::type::inverted_section_open ||
|
||||
token.token_type() == token::type::section_open)
|
||||
m_skipped_openings++;
|
||||
|
||||
m_section << token;
|
||||
return "";
|
||||
}
|
24
ext/mstch/src/state/in_section.hpp
Normal file
24
ext/mstch/src/state/in_section.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "render_state.hpp"
|
||||
#include "template_type.hpp"
|
||||
|
||||
namespace mstch {
|
||||
|
||||
class in_section: public render_state {
|
||||
public:
|
||||
enum class type { inverted, normal };
|
||||
in_section(type type, const token& start_token);
|
||||
std::string render(render_context& context, const token& token) override;
|
||||
|
||||
private:
|
||||
const type m_type;
|
||||
const token& m_start_token;
|
||||
template_type m_section;
|
||||
int m_skipped_openings;
|
||||
};
|
||||
|
||||
}
|
32
ext/mstch/src/state/outside_section.cpp
Normal file
32
ext/mstch/src/state/outside_section.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include "outside_section.hpp"
|
||||
|
||||
#include "visitor/render_node.hpp"
|
||||
#include "in_section.hpp"
|
||||
#include "render_context.hpp"
|
||||
|
||||
using namespace mstch;
|
||||
|
||||
std::string outside_section::render(
|
||||
render_context& ctx, const token& token)
|
||||
{
|
||||
using flag = render_node::flag;
|
||||
switch (token.token_type()) {
|
||||
case token::type::section_open:
|
||||
ctx.set_state<in_section>(in_section::type::normal, token);
|
||||
break;
|
||||
case token::type::inverted_section_open:
|
||||
ctx.set_state<in_section>(in_section::type::inverted, token);
|
||||
break;
|
||||
case token::type::variable:
|
||||
return visit(render_node(ctx, flag::escape_html), ctx.get_node(token.name()));
|
||||
case token::type::unescaped_variable:
|
||||
return visit(render_node(ctx, flag::none), ctx.get_node(token.name()));
|
||||
case token::type::text:
|
||||
return token.raw();
|
||||
case token::type::partial:
|
||||
return ctx.render_partial(token.name(), token.partial_prefix());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
12
ext/mstch/src/state/outside_section.hpp
Normal file
12
ext/mstch/src/state/outside_section.hpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "render_state.hpp"
|
||||
|
||||
namespace mstch {
|
||||
|
||||
class outside_section: public render_state {
|
||||
public:
|
||||
std::string render(render_context& context, const token& token) override;
|
||||
};
|
||||
|
||||
}
|
17
ext/mstch/src/state/render_state.hpp
Normal file
17
ext/mstch/src/state/render_state.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "token.hpp"
|
||||
|
||||
namespace mstch {
|
||||
|
||||
class render_context;
|
||||
|
||||
class render_state {
|
||||
public:
|
||||
virtual ~render_state() {}
|
||||
virtual std::string render(render_context& context, const token& token) = 0;
|
||||
};
|
||||
|
||||
}
|
104
ext/mstch/src/template_type.cpp
Normal file
104
ext/mstch/src/template_type.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
#include "template_type.hpp"
|
||||
|
||||
using namespace mstch;
|
||||
|
||||
template_type::template_type(const std::string& str, const delim_type& delims):
|
||||
m_open(delims.first), m_close(delims.second)
|
||||
{
|
||||
tokenize(str);
|
||||
strip_whitespace();
|
||||
}
|
||||
|
||||
template_type::template_type(const std::string& str):
|
||||
m_open("{{"), m_close("}}")
|
||||
{
|
||||
tokenize(str);
|
||||
strip_whitespace();
|
||||
}
|
||||
|
||||
void template_type::process_text(citer begin, citer end) {
|
||||
if (begin == end)
|
||||
return;
|
||||
auto start = begin;
|
||||
for (auto it = begin; it != end; ++it)
|
||||
if (*it == '\n' || it == end - 1) {
|
||||
m_tokens.push_back({{start, it + 1}});
|
||||
start = it + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void template_type::tokenize(const std::string& tmp) {
|
||||
citer beg = tmp.begin();
|
||||
auto npos = std::string::npos;
|
||||
|
||||
for (std::size_t cur_pos = 0; cur_pos < tmp.size();) {
|
||||
auto open_pos = tmp.find(m_open, cur_pos);
|
||||
auto close_pos = tmp.find(
|
||||
m_close, open_pos == npos ? open_pos : open_pos + 1);
|
||||
|
||||
if (close_pos != npos && open_pos != npos) {
|
||||
if (*(beg + open_pos + m_open.size()) == '{' &&
|
||||
*(beg + close_pos + m_close.size()) == '}')
|
||||
++close_pos;
|
||||
|
||||
process_text(beg + cur_pos, beg + open_pos);
|
||||
cur_pos = close_pos + m_close.size();
|
||||
m_tokens.push_back({{beg + open_pos, beg + close_pos + m_close.size()},
|
||||
m_open.size(), m_close.size()});
|
||||
|
||||
if (cur_pos == tmp.size()) {
|
||||
m_tokens.push_back({{""}});
|
||||
m_tokens.back().eol(true);
|
||||
}
|
||||
|
||||
if (*(beg + open_pos + m_open.size()) == '=' &&
|
||||
*(beg + close_pos - 1) == '=')
|
||||
{
|
||||
auto tok_beg = beg + open_pos + m_open.size() + 1;
|
||||
auto tok_end = beg + close_pos - 1;
|
||||
auto front_skip = first_not_ws(tok_beg, tok_end);
|
||||
auto back_skip = first_not_ws(reverse(tok_end), reverse(tok_beg));
|
||||
m_open = {front_skip, beg + tmp.find(' ', front_skip - beg)};
|
||||
m_close = {beg + tmp.rfind(' ', back_skip - beg) + 1, back_skip + 1};
|
||||
}
|
||||
} else {
|
||||
process_text(beg + cur_pos, tmp.end());
|
||||
cur_pos = close_pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void template_type::strip_whitespace() {
|
||||
auto line_begin = m_tokens.begin();
|
||||
bool has_tag = false, non_space = false;
|
||||
|
||||
for (auto it = m_tokens.begin(); it != m_tokens.end(); ++it) {
|
||||
auto type = (*it).token_type();
|
||||
if (type != token::type::text && type != token::type::variable &&
|
||||
type != token::type::unescaped_variable)
|
||||
has_tag = true;
|
||||
else if (!(*it).ws_only())
|
||||
non_space = true;
|
||||
|
||||
if ((*it).eol()) {
|
||||
if (has_tag && !non_space) {
|
||||
store_prefixes(line_begin);
|
||||
|
||||
auto c = line_begin;
|
||||
for (bool end = false; !end; (*c).ws_only() ? c = m_tokens.erase(c) : ++c)
|
||||
if ((end = (*c).eol()))
|
||||
it = c - 1;
|
||||
}
|
||||
|
||||
non_space = has_tag = false;
|
||||
line_begin = it + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void template_type::store_prefixes(std::vector<token>::iterator beg) {
|
||||
for (auto cur = beg; !(*cur).eol(); ++cur)
|
||||
if ((*cur).token_type() == token::type::partial &&
|
||||
cur != beg && (*(cur - 1)).ws_only())
|
||||
(*cur).partial_prefix((*(cur - 1)).raw());
|
||||
}
|
30
ext/mstch/src/template_type.hpp
Normal file
30
ext/mstch/src/template_type.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "token.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace mstch {
|
||||
|
||||
class template_type {
|
||||
public:
|
||||
template_type() = default;
|
||||
template_type(const std::string& str);
|
||||
template_type(const std::string& str, const delim_type& delims);
|
||||
std::vector<token>::const_iterator begin() const { return m_tokens.begin(); }
|
||||
std::vector<token>::const_iterator end() const { return m_tokens.end(); }
|
||||
void operator<<(const token& token) { m_tokens.push_back(token); }
|
||||
|
||||
private:
|
||||
std::vector<token> m_tokens;
|
||||
std::string m_open;
|
||||
std::string m_close;
|
||||
void strip_whitespace();
|
||||
void process_text(citer beg, citer end);
|
||||
void tokenize(const std::string& tmp);
|
||||
void store_prefixes(std::vector<token>::iterator beg);
|
||||
};
|
||||
|
||||
}
|
42
ext/mstch/src/token.cpp
Normal file
42
ext/mstch/src/token.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include "token.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
using namespace mstch;
|
||||
|
||||
token::type token::token_info(char c) {
|
||||
switch (c) {
|
||||
case '>': return type::partial;
|
||||
case '^': return type::inverted_section_open;
|
||||
case '/': return type::section_close;
|
||||
case '&': return type::unescaped_variable;
|
||||
case '#': return type::section_open;
|
||||
case '!': return type::comment;
|
||||
default: return type::variable;
|
||||
}
|
||||
}
|
||||
|
||||
token::token(const std::string& str, std::size_t left, std::size_t right):
|
||||
m_raw(str), m_eol(false), m_ws_only(false)
|
||||
{
|
||||
if (left != 0 && right != 0) {
|
||||
if (str[left] == '=' && str[str.size() - right - 1] == '=') {
|
||||
m_type = type::delimiter_change;
|
||||
} else if (str[left] == '{' && str[str.size() - right - 1] == '}') {
|
||||
m_type = type::unescaped_variable;
|
||||
m_name = {first_not_ws(str.begin() + left + 1, str.end() - right),
|
||||
first_not_ws(str.rbegin() + 1 + right, str.rend() - left) + 1};
|
||||
} else {
|
||||
auto c = first_not_ws(str.begin() + left, str.end() - right);
|
||||
m_type = token_info(*c);
|
||||
if (m_type != type::variable)
|
||||
c = first_not_ws(c + 1, str.end() - right);
|
||||
m_name = {c, first_not_ws(str.rbegin() + right, str.rend() - left) + 1};
|
||||
m_delims = {{str.begin(), str.begin() + left},
|
||||
{str.end() - right, str.end()}};
|
||||
}
|
||||
} else {
|
||||
m_type = type::text;
|
||||
m_eol = (str.size() > 0 && str[str.size() - 1] == '\n');
|
||||
m_ws_only = (str.find_first_not_of(" \r\n\t") == std::string::npos);
|
||||
}
|
||||
}
|
39
ext/mstch/src/token.hpp
Normal file
39
ext/mstch/src/token.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mstch {
|
||||
|
||||
using delim_type = std::pair<std::string, std::string>;
|
||||
|
||||
class token {
|
||||
public:
|
||||
enum class type {
|
||||
text, variable, section_open, section_close, inverted_section_open,
|
||||
unescaped_variable, comment, partial, delimiter_change
|
||||
};
|
||||
token(const std::string& str, std::size_t left = 0, std::size_t right = 0);
|
||||
type token_type() const { return m_type; };
|
||||
const std::string& raw() const { return m_raw; };
|
||||
const std::string& name() const { return m_name; };
|
||||
const std::string& partial_prefix() const { return m_partial_prefix; };
|
||||
const delim_type& delims() const { return m_delims; };
|
||||
void partial_prefix(const std::string& p_partial_prefix) {
|
||||
m_partial_prefix = p_partial_prefix;
|
||||
};
|
||||
bool eol() const { return m_eol; }
|
||||
void eol(bool eol) { m_eol = eol; }
|
||||
bool ws_only() const { return m_ws_only; }
|
||||
|
||||
private:
|
||||
type m_type;
|
||||
std::string m_name;
|
||||
std::string m_raw;
|
||||
std::string m_partial_prefix;
|
||||
delim_type m_delims;
|
||||
bool m_eol;
|
||||
bool m_ws_only;
|
||||
type token_info(char c);
|
||||
};
|
||||
|
||||
}
|
44
ext/mstch/src/utils.cpp
Normal file
44
ext/mstch/src/utils.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "utils.hpp"
|
||||
#include "mstch/mstch.hpp"
|
||||
|
||||
mstch::citer mstch::first_not_ws(mstch::citer begin, mstch::citer end) {
|
||||
for (auto it = begin; it != end; ++it)
|
||||
if (*it != ' ') return it;
|
||||
return end;
|
||||
}
|
||||
|
||||
mstch::citer mstch::first_not_ws(mstch::criter begin, mstch::criter end) {
|
||||
for (auto rit = begin; rit != end; ++rit)
|
||||
if (*rit != ' ') return --(rit.base());
|
||||
return --(end.base());
|
||||
}
|
||||
|
||||
mstch::criter mstch::reverse(mstch::citer it) {
|
||||
return std::reverse_iterator<mstch::citer>(it);
|
||||
}
|
||||
|
||||
std::string mstch::html_escape(const std::string& str) {
|
||||
if (mstch::config::escape)
|
||||
return mstch::config::escape(str);
|
||||
|
||||
std::string out;
|
||||
citer start = str.begin();
|
||||
|
||||
auto add_escape = [&out, &start](const std::string& escaped, citer& it) {
|
||||
out += std::string{start, it} + escaped;
|
||||
start = it + 1;
|
||||
};
|
||||
|
||||
for (auto it = str.begin(); it != str.end(); ++it)
|
||||
switch (*it) {
|
||||
case '&': add_escape("&", it); break;
|
||||
case '\'': add_escape("'", it); break;
|
||||
case '"': add_escape(""", it); break;
|
||||
case '<': add_escape("<", it); break;
|
||||
case '>': add_escape(">", it); break;
|
||||
case '/': add_escape("/", it); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return out + std::string{start, str.end()};
|
||||
}
|
23
ext/mstch/src/utils.hpp
Normal file
23
ext/mstch/src/utils.hpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <boost/variant/apply_visitor.hpp>
|
||||
|
||||
namespace mstch {
|
||||
|
||||
using citer = std::string::const_iterator;
|
||||
using criter = std::string::const_reverse_iterator;
|
||||
|
||||
citer first_not_ws(citer begin, citer end);
|
||||
citer first_not_ws(criter begin, criter end);
|
||||
std::string html_escape(const std::string& str);
|
||||
criter reverse(citer it);
|
||||
|
||||
template<class... Args>
|
||||
auto visit(Args&&... args) -> decltype(boost::apply_visitor(
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
return boost::apply_visitor(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
}
|
35
ext/mstch/src/visitor/get_token.hpp
Normal file
35
ext/mstch/src/visitor/get_token.hpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
|
||||
#include "mstch/mstch.hpp"
|
||||
#include "has_token.hpp"
|
||||
|
||||
namespace mstch {
|
||||
|
||||
class get_token: public boost::static_visitor<const mstch::node&> {
|
||||
public:
|
||||
get_token(const std::string& token, const mstch::node& node):
|
||||
m_token(token), m_node(node)
|
||||
{
|
||||
}
|
||||
|
||||
template<class T>
|
||||
const mstch::node& operator()(const T&) const {
|
||||
return m_node;
|
||||
}
|
||||
|
||||
const mstch::node& operator()(const map& map) const {
|
||||
return map.at(m_token);
|
||||
}
|
||||
|
||||
const mstch::node& operator()(const std::shared_ptr<object>& object) const {
|
||||
return object->at(m_token);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string& m_token;
|
||||
const mstch::node& m_node;
|
||||
};
|
||||
|
||||
}
|
31
ext/mstch/src/visitor/has_token.hpp
Normal file
31
ext/mstch/src/visitor/has_token.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
|
||||
#include "mstch/mstch.hpp"
|
||||
|
||||
namespace mstch {
|
||||
|
||||
class has_token: public boost::static_visitor<bool> {
|
||||
public:
|
||||
has_token(const std::string& token): m_token(token) {
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool operator()(const T&) const {
|
||||
return m_token == ".";
|
||||
}
|
||||
|
||||
bool operator()(const map& map) const {
|
||||
return map.count(m_token) == 1;
|
||||
}
|
||||
|
||||
bool operator()(const std::shared_ptr<object>& object) const {
|
||||
return object->has(m_token);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string& m_token;
|
||||
};
|
||||
|
||||
}
|
41
ext/mstch/src/visitor/is_node_empty.hpp
Normal file
41
ext/mstch/src/visitor/is_node_empty.hpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
|
||||
#include "mstch/mstch.hpp"
|
||||
|
||||
namespace mstch {
|
||||
|
||||
class is_node_empty: public boost::static_visitor<bool> {
|
||||
public:
|
||||
template<class T>
|
||||
bool operator()(const T&) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator()(const std::nullptr_t&) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(const int& value) const {
|
||||
return value == 0;
|
||||
}
|
||||
|
||||
bool operator()(const double& value) const {
|
||||
return value == 0;
|
||||
}
|
||||
|
||||
bool operator()(const bool& value) const {
|
||||
return !value;
|
||||
}
|
||||
|
||||
bool operator()(const std::string& value) const {
|
||||
return value == "";
|
||||
}
|
||||
|
||||
bool operator()(const array& array) const {
|
||||
return array.size() == 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
56
ext/mstch/src/visitor/render_node.hpp
Normal file
56
ext/mstch/src/visitor/render_node.hpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
|
||||
#include "render_context.hpp"
|
||||
#include "mstch/mstch.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace mstch {
|
||||
|
||||
class render_node: public boost::static_visitor<std::string> {
|
||||
public:
|
||||
enum class flag { none, escape_html };
|
||||
render_node(render_context& ctx, flag p_flag = flag::none):
|
||||
m_ctx(ctx), m_flag(p_flag)
|
||||
{
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::string operator()(const T&) const {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string operator()(const int& value) const {
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
std::string operator()(const double& value) const {
|
||||
std::stringstream ss;
|
||||
ss << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string operator()(const bool& value) const {
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
std::string operator()(const lambda& value) const {
|
||||
template_type interpreted{value([this](const mstch::node& n) {
|
||||
return visit(render_node(m_ctx), n);
|
||||
})};
|
||||
auto rendered = render_context::push(m_ctx).render(interpreted);
|
||||
return (m_flag == flag::escape_html) ? html_escape(rendered) : rendered;
|
||||
}
|
||||
|
||||
std::string operator()(const std::string& value) const {
|
||||
return (m_flag == flag::escape_html) ? html_escape(value) : value;
|
||||
}
|
||||
|
||||
private:
|
||||
render_context& m_ctx;
|
||||
flag m_flag;
|
||||
};
|
||||
|
||||
}
|
57
ext/mstch/src/visitor/render_section.hpp
Normal file
57
ext/mstch/src/visitor/render_section.hpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
|
||||
#include "render_context.hpp"
|
||||
#include "mstch/mstch.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "render_node.hpp"
|
||||
|
||||
namespace mstch {
|
||||
|
||||
class render_section: public boost::static_visitor<std::string> {
|
||||
public:
|
||||
enum class flag { none, keep_array };
|
||||
render_section(
|
||||
render_context& ctx,
|
||||
const template_type& section,
|
||||
const delim_type& delims,
|
||||
flag p_flag = flag::none):
|
||||
m_ctx(ctx), m_section(section), m_delims(delims), m_flag(p_flag)
|
||||
{
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::string operator()(const T& t) const {
|
||||
return render_context::push(m_ctx, t).render(m_section);
|
||||
}
|
||||
|
||||
std::string operator()(const lambda& fun) const {
|
||||
std::string section_str;
|
||||
for (auto& token: m_section)
|
||||
section_str += token.raw();
|
||||
template_type interpreted{fun([this](const mstch::node& n) {
|
||||
return visit(render_node(m_ctx), n);
|
||||
}, section_str), m_delims};
|
||||
return render_context::push(m_ctx).render(interpreted);
|
||||
}
|
||||
|
||||
std::string operator()(const array& array) const {
|
||||
std::string out;
|
||||
if (m_flag == flag::keep_array)
|
||||
return render_context::push(m_ctx, array).render(m_section);
|
||||
else
|
||||
for (auto& item: array)
|
||||
out += visit(render_section(
|
||||
m_ctx, m_section, m_delims, flag::keep_array), item);
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
render_context& m_ctx;
|
||||
const template_type& m_section;
|
||||
const delim_type& m_delims;
|
||||
flag m_flag;
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue