Mercurial > embed
changeset 71:26304c6e4b69
mstch: import 1.0.2
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 18 Jul 2018 12:31:28 +0200 |
parents | bca1b8465997 |
children | ce077d93ce14 |
files | CMakeLists.txt LICENSE.mstch.txt VERSION.mstch.txt mstch/CMakeLists.txt mstch/include/mstch/mstch.hpp mstch/src/CMakeLists.txt mstch/src/mstch.cpp mstch/src/render_context.cpp mstch/src/render_context.hpp mstch/src/state/in_section.cpp mstch/src/state/in_section.hpp mstch/src/state/outside_section.cpp mstch/src/state/outside_section.hpp mstch/src/state/render_state.hpp mstch/src/template_type.cpp mstch/src/template_type.hpp mstch/src/token.cpp mstch/src/token.hpp mstch/src/utils.cpp mstch/src/utils.hpp mstch/src/visitor/get_token.hpp mstch/src/visitor/has_token.hpp mstch/src/visitor/is_node_empty.hpp mstch/src/visitor/render_node.hpp mstch/src/visitor/render_section.hpp tests/mstch/CMakeLists.txt tests/mstch/main.cpp |
diffstat | 27 files changed, 980 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Fri Jul 13 13:41:52 2018 +0200 +++ b/CMakeLists.txt Wed Jul 18 12:31:28 2018 +0200 @@ -25,12 +25,8 @@ add_subdirectory(tests/hoedown) add_subdirectory(tests/jansson) add_subdirectory(tests/json) +add_subdirectory(tests/mstch) add_subdirectory(tests/pugixml) add_subdirectory(tests/sqlite) +add_subdirectory(tests/uriparser) add_subdirectory(tests/yaml) -add_subdirectory(tests/uriparser) - -# dummy target for metadata files. -file(GLOB LICENSES LICENSE.*.txt) -file(GLOB VERSIONS VERSION.*.txt) -add_custom_target(manifest SOURCES ${LICENSES} ${VERSIONS})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE.mstch.txt Wed Jul 18 12:31:28 2018 +0200 @@ -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. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/VERSION.mstch.txt Wed Jul 18 12:31:28 2018 +0200 @@ -0,0 +1,1 @@ +1.0.2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/CMakeLists.txt Wed Jul 18 12:31:28 2018 +0200 @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.0.2) +project(mstch) +add_subdirectory(src)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/include/mstch/mstch.hpp Wed Jul 18 12:31:28 2018 +0200 @@ -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>()); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/CMakeLists.txt Wed Jul 18 12:31:28 2018 +0200 @@ -0,0 +1,27 @@ +find_package(Boost 1.54 REQUIRED) + +add_library( + libmstch + 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 +) + +target_include_directories( + libmstch + PRIVATE ${mstch_SOURCE_DIR}/src + PUBLIC $<BUILD_INTERFACE:${mstch_SOURCE_DIR}/include> +) + +target_link_libraries(libmstch Boost::boost) +set_target_properties(libmstch PROPERTIES PREFIX "")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/mstch.cpp Wed Jul 18 12:31:28 2018 +0200 @@ -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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/render_context.cpp Wed Jul 18 12:31:28 2018 +0200 @@ -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) : ""; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/render_context.hpp Wed Jul 18 12:31:28 2018 +0200 @@ -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; +}; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/state/in_section.cpp Wed Jul 18 12:31:28 2018 +0200 @@ -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 ""; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/state/in_section.hpp Wed Jul 18 12:31:28 2018 +0200 @@ -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; +}; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/state/outside_section.cpp Wed Jul 18 12:31:28 2018 +0200 @@ -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 ""; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/state/outside_section.hpp Wed Jul 18 12:31:28 2018 +0200 @@ -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; +}; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/state/render_state.hpp Wed Jul 18 12:31:28 2018 +0200 @@ -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; +}; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/template_type.cpp Wed Jul 18 12:31:28 2018 +0200 @@ -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()); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/template_type.hpp Wed Jul 18 12:31:28 2018 +0200 @@ -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); +}; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/token.cpp Wed Jul 18 12:31:28 2018 +0200 @@ -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); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/token.hpp Wed Jul 18 12:31:28 2018 +0200 @@ -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); +}; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/utils.cpp Wed Jul 18 12:31:28 2018 +0200 @@ -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()}; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/utils.hpp Wed Jul 18 12:31:28 2018 +0200 @@ -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)...); +} + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/visitor/get_token.hpp Wed Jul 18 12:31:28 2018 +0200 @@ -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; +}; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/visitor/has_token.hpp Wed Jul 18 12:31:28 2018 +0200 @@ -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; +}; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/visitor/is_node_empty.hpp Wed Jul 18 12:31:28 2018 +0200 @@ -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; + } +}; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/visitor/render_node.hpp Wed Jul 18 12:31:28 2018 +0200 @@ -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; +}; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mstch/src/visitor/render_section.hpp Wed Jul 18 12:31:28 2018 +0200 @@ -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; +}; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/mstch/CMakeLists.txt Wed Jul 18 12:31:28 2018 +0200 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- test project for mstch +# +# Copyright (c) 2016-2018 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +cmake_minimum_required(VERSION 3.7) +project(test-mstch) +add_subdirectory(../../mstch mstch-build-dir) +add_executable(test-mstch main.cpp) +target_link_libraries(test-mstch libmstch)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/mstch/main.cpp Wed Jul 18 12:31:28 2018 +0200 @@ -0,0 +1,25 @@ +/* + * main.cpp -- main file + * + * Copyright (c) 2016-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <mstch/mstch.hpp> + +int main() +{ + mstch::map map; + mstch::render("test", map); +}