diff --git a/CMakeLists.txt b/CMakeLists.txt index a5069f7..f22b509 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,6 @@ option (WITH_UNIT_TESTS "enable building unit test executable" OFF) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -O3") add_subdirectory(src) if(WITH_UNIT_TESTS) - enable_testing() - add_subdirectory(test) + enable_testing() + add_subdirectory(test) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8850707..d24f8e7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,17 +1,17 @@ find_package(Boost 1.54 REQUIRED) include_directories( - ${CMAKE_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR} - ${Boost_INCLUDE_DIR}) + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR} + ${Boost_INCLUDE_DIR}) set(SRC - state/in_section.cpp - state/outside_section.cpp - mstch.cpp - render_context.cpp - template_type.cpp - token.cpp - utils.cpp) + state/in_section.cpp + state/outside_section.cpp + mstch.cpp + render_context.cpp + template_type.cpp + token.cpp + utils.cpp) add_library(mstch STATIC ${SRC}) diff --git a/src/mstch.cpp b/src/mstch.cpp index 2ff333b..63297fd 100644 --- a/src/mstch.cpp +++ b/src/mstch.cpp @@ -7,12 +7,12 @@ using namespace mstch; std::string mstch::render( - const std::string& tmplt, - const node& root, - const std::map& partials) + const std::string& tmplt, + const node& root, + const std::map& partials) { - std::map partial_templts; - for(auto& partial: partials) - partial_templts.insert({partial.first, {partial.second}}); - return render_context(root, partial_templts).render(tmplt); + std::map partial_templts; + for (auto& partial: partials) + partial_templts.insert({partial.first, {partial.second}}); + return render_context(root, partial_templts).render(tmplt); } diff --git a/src/render_context.cpp b/src/render_context.cpp index c715b9c..51477b4 100644 --- a/src/render_context.cpp +++ b/src/render_context.cpp @@ -5,62 +5,63 @@ #include "visitor/has_token.hpp" using namespace mstch; +using namespace mstch::visitor; const mstch::node render_context::null_node; render_context::push::push(render_context& context, const mstch::node& node): - context(context) + context(context) { - context.nodes.emplace_front(node); - context.state.push(std::unique_ptr( - new state::outside_section)); + context.nodes.emplace_front(node); + context.state.push(std::unique_ptr( + new state::outside_section)); } render_context::push::~push() { - context.nodes.pop_front(); - context.state.pop(); + context.nodes.pop_front(); + context.state.pop(); } std::string render_context::push::render(const template_type& templt) { - return context.render(templt); + return context.render(templt); } render_context::render_context( - const mstch::node& node, - const std::map& partials): - partials{partials}, - nodes{node} + const mstch::node& node, + const std::map& partials): + partials{partials}, + nodes{node} { - state.push(std::unique_ptr( - new state::outside_section)); + state.push(std::unique_ptr( + new state::outside_section)); } const mstch::node& render_context::find_node( - const std::string& token, - const std::deque& current_nodes) + const std::string& token, + const std::deque& 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& i: current_nodes) - if (boost::apply_visitor(visitor::has_token(token), i)) - return boost::apply_visitor(visitor::get_token(token, i), i); - return null_node; + 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, nodes); + return find_node(token, nodes); } std::string render_context::render(const template_type& templt) { - std::string output; - for (auto& token: templt) - output += state.top()->render(*this, token); - return output; + std::string output; + for (auto& token: templt) + output += state.top()->render(*this, token); + return output; } std::string render_context::render_partial(const std::string& partial_name) { - return (partials.count(partial_name))?render(partials.at(partial_name)):""; + return (partials.count(partial_name))?render(partials.at(partial_name)):""; } diff --git a/src/render_context.hpp b/src/render_context.hpp index 4dbd9be..fc9e553 100644 --- a/src/render_context.hpp +++ b/src/render_context.hpp @@ -10,34 +10,38 @@ #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& context; - }; - render_context( - const mstch::node& node, - const std::map& partials); - const mstch::node& get_node(const std::string& token); - std::string render(const template_type& templt); - std::string render_partial(const std::string& partial_name); - template - void set_state(Args&&... args) { - state.top() = std::unique_ptr( - new T(std::forward(args)...)); - } - private: - static const mstch::node null_node; - const mstch::node& find_node( - const std::string& token, - const std::deque& current_nodes); - const std::map& partials; - std::deque nodes; - std::stack> state; - }; + +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& context; + }; + + render_context( + const mstch::node& node, + const std::map& partials); + const mstch::node& get_node(const std::string& token); + std::string render(const template_type& templt); + std::string render_partial(const std::string& partial_name); + template + void set_state(Args&& ... args) { + state.top() = std::unique_ptr( + new T(std::forward(args)...)); + } + + private: + static const mstch::node null_node; + const mstch::node& find_node( + const std::string& token, + const std::deque& current_nodes); + const std::map& partials; + std::deque nodes; + std::stack> state; +}; + } diff --git a/src/state/in_section.cpp b/src/state/in_section.cpp index c676af7..67c233f 100644 --- a/src/state/in_section.cpp +++ b/src/state/in_section.cpp @@ -2,8 +2,10 @@ #include "outside_section.hpp" #include "visitor/is_node_empty.hpp" #include "visitor/render_section.hpp" +#include "utils.hpp" using namespace mstch; +using namespace mstch::visitor; state::in_section::in_section(type type, const std::string& section_name): m_type(type), section_name(section_name), skipped_openings(0) @@ -11,21 +13,20 @@ state::in_section::in_section(type type, const std::string& section_name): } std::string state::in_section::render(render_context& ctx, const token& token) { - if(token.token_type() == token::type::section_close) - if(token.name() == section_name && skipped_openings == 0) { - auto& sn = ctx.get_node(section_name); + if (token.token_type() == token::type::section_close) + if (token.name() == section_name && skipped_openings == 0) { + auto& node = ctx.get_node(section_name); std::string out; - if(m_type == type::normal && - !boost::apply_visitor(visitor::is_node_empty(), sn)) - out = boost::apply_visitor(visitor::render_section(ctx, section), sn); - else if(m_type == type::inverted && - boost::apply_visitor(visitor::is_node_empty(), sn)) + if (m_type == type::normal && !visit(is_node_empty(), node)) + out = visit(render_section(ctx, section), node); + else if (m_type == type::inverted && visit(is_node_empty(), node)) out = render_context::push(ctx).render(section); ctx.set_state(); return out; - } else + } else { skipped_openings--; - else if(token.token_type() == token::type::inverted_section_open || + } + else if (token.token_type() == token::type::inverted_section_open || token.token_type() == token::type::section_open) skipped_openings++; section << token; diff --git a/src/state/in_section.hpp b/src/state/in_section.hpp index 653d283..2980e20 100644 --- a/src/state/in_section.hpp +++ b/src/state/in_section.hpp @@ -1,23 +1,28 @@ #pragma once -#include "render_state.hpp" #include #include + +#include "render_state.hpp" #include "template_type.hpp" namespace mstch { - namespace state { - class in_section: public render_state { - public: - enum class type { inverted, normal }; - in_section(type type, const std::string& section_name); - std::string render( - render_context& context, const token& token) override; - private: - const type m_type; - const std::string section_name; - template_type section; - int skipped_openings; - }; - } +namespace state { + +class in_section: public render_state { + public: + enum class type { + inverted, normal + }; + in_section(type type, const std::string §ion_name); + std::string render(render_context &context, const token &token) override; + + private: + const type m_type; + const std::string section_name; + template_type section; + int skipped_openings; +}; + +} } diff --git a/src/state/outside_section.cpp b/src/state/outside_section.cpp index 0f96926..ab273be 100644 --- a/src/state/outside_section.cpp +++ b/src/state/outside_section.cpp @@ -1,33 +1,34 @@ -#include "visitor/render_node.hpp" #include "outside_section.hpp" + +#include "visitor/render_node.hpp" #include "in_section.hpp" #include "render_context.hpp" +#include "utils.hpp" using namespace mstch; +using namespace mstch::visitor; std::string state::outside_section::render( - render_context& ctx, const token& token) + render_context& ctx, const token& token) { - switch(token.token_type()) { + using flag = render_node::flag; + switch (token.token_type()) { case token::type::section_open: - ctx.set_state(in_section::type::normal, token.name()); - break; + ctx.set_state(in_section::type::normal, token.name()); + break; case token::type::inverted_section_open: - ctx.set_state(in_section::type::inverted, token.name()); - break; + ctx.set_state(in_section::type::inverted, token.name()); + break; case token::type::variable: - return boost::apply_visitor( - visitor::render_node(visitor::render_node::flag::escape_html), - ctx.get_node(token.name())); + return visit(render_node(flag::escape_html), ctx.get_node(token.name())); case token::type::unescaped_variable: - return boost::apply_visitor( - visitor::render_node(visitor::render_node::flag::none), - ctx.get_node(token.name())); + return visit(render_node(flag::none), ctx.get_node(token.name())); case token::type::text: - return token.raw(); + return token.raw(); case token::type::partial: - return ctx.render_partial(token.name()); - default: break; - } - return ""; + return ctx.render_partial(token.name()); + default: + break; + } + return ""; } diff --git a/src/state/outside_section.hpp b/src/state/outside_section.hpp index bc0d9c0..d5c6346 100644 --- a/src/state/outside_section.hpp +++ b/src/state/outside_section.hpp @@ -3,11 +3,12 @@ #include "render_state.hpp" namespace mstch { - namespace state { - class outside_section: public render_state { - public: - std::string render( - render_context& context, const token& token) override; - }; - } +namespace state { + +class outside_section: public render_state { + public: + std::string render(render_context& context, const token& token) override; +}; + +} } diff --git a/src/state/render_state.hpp b/src/state/render_state.hpp index d46fd26..968d9b0 100644 --- a/src/state/render_state.hpp +++ b/src/state/render_state.hpp @@ -1,15 +1,19 @@ #pragma once #include + #include "token.hpp" namespace mstch { - class render_context; - namespace state { - class render_state { - public: - virtual std::string render( - render_context& context, const token& token) = 0; - }; - } + +class render_context; + +namespace state { + +class render_state { + public: + virtual std::string render(render_context& context, const token& token) = 0; +}; + +} } diff --git a/src/template_type.cpp b/src/template_type.cpp index 795428d..7b40872 100644 --- a/src/template_type.cpp +++ b/src/template_type.cpp @@ -3,82 +3,82 @@ using namespace mstch; template_type::template_type(const std::string& str) { - tokenize(str); - strip_whitespace(); + tokenize(str); + strip_whitespace(); } void template_type::tokenize(const std::string& t) { - std::string delim_start{"{{"}; - std::string delim_end{"}}"}; - std::string::const_iterator tok_end, tok_start = t.begin(); - parse_state pstate = parse_state::start; - unsigned int del_pos = 0; - for (std::string::const_iterator it = t.begin(); it != t.end(); ++it) { - if (pstate == parse_state::start) { - if (*it == delim_start[0]) { - pstate = parse_state::in_del_start; - tok_end = it; - del_pos = 1; - } else if(*it == '\n') { - tokens.push_back({{tok_start, it + 1}}); - tok_start = it + 1; - } - } else if(pstate == parse_state::in_del_start) { - if (*it == delim_start[del_pos] && ++del_pos == delim_start.size()) - pstate = parse_state::in_del; - else - pstate = parse_state::start; - } else if(pstate == parse_state::in_del) { - if (*it == '{') - pstate = parse_state::in_esccontent; - else if (*it == delim_end[0] && (del_pos = 1)) - pstate = parse_state::in_del_end; - else - pstate = parse_state::in_content; - } else if(pstate == parse_state::in_esccontent && *it == '}') { - pstate = parse_state::in_content; - } else if(pstate == parse_state::in_content && *it == delim_end[0]) { - pstate = parse_state::in_del_end; - del_pos = 1; - } else if(pstate == parse_state::in_del_end) { - if (*it == delim_end[del_pos] && ++del_pos == delim_end.size()) { - pstate = parse_state::start; - tokens.push_back({{tok_start, tok_end}}); - tokens.push_back( - {{tok_end, it + 1}, - delim_start.size(), - delim_end.size()}); - tok_start = it + 1; - } else { - pstate = parse_state::start; - } - } + std::string delim_start{"{{"}; + std::string delim_end{"}}"}; + std::string::const_iterator tok_end, tok_start = t.begin(); + parse_state pstate = parse_state::start; + unsigned int del_pos = 0; + for (std::string::const_iterator it = t.begin(); it != t.end(); ++it) { + if (pstate == parse_state::start) { + if (*it == delim_start[0]) { + pstate = parse_state::in_del_start; + tok_end = it; + del_pos = 1; + } else if (*it == '\n') { + tokens.push_back({{tok_start, it + 1}}); + tok_start = it + 1; + } + } else if (pstate == parse_state::in_del_start) { + if (*it == delim_start[del_pos] && ++del_pos == delim_start.size()) + pstate = parse_state::in_del; + else + pstate = parse_state::start; + } else if (pstate == parse_state::in_del) { + if (*it == '{') + pstate = parse_state::in_esccontent; + else if (*it == delim_end[0] && (del_pos = 1)) + pstate = parse_state::in_del_end; + else + pstate = parse_state::in_content; + } else if (pstate == parse_state::in_esccontent && *it == '}') { + pstate = parse_state::in_content; + } else if (pstate == parse_state::in_content && *it == delim_end[0]) { + pstate = parse_state::in_del_end; + del_pos = 1; + } else if (pstate == parse_state::in_del_end) { + if (*it == delim_end[del_pos] && ++del_pos == delim_end.size()) { + pstate = parse_state::start; + tokens.push_back({{tok_start, tok_end}}); + tokens.push_back( + {{tok_end, it + 1}, + delim_start.size(), + delim_end.size()}); + tok_start = it + 1; + } else { + pstate = parse_state::start; + } } - tokens.push_back({{tok_start, t.end()}}); + } + tokens.push_back({{tok_start, t.end()}}); } void template_type::strip_whitespace() { - auto line_begin = tokens.begin(); - bool has_tag = false, non_space = false; - for (auto it = tokens.begin(); it != 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) { - auto line_it = line_begin; - for (; !(*line_it).eol(); ++line_it) - if ((*line_it).ws_only()) - line_it = tokens.erase(line_it); - if ((*line_it).ws_only()) - line_it = tokens.erase(line_it); - it = line_it - 1; - } - non_space = has_tag = false; - line_begin = it + 1; - } + auto line_begin = tokens.begin(); + bool has_tag = false, non_space = false; + for (auto it = tokens.begin(); it != 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) { + auto line_it = line_begin; + for (; !(*line_it).eol(); ++line_it) + if ((*line_it).ws_only()) + line_it = tokens.erase(line_it); + if ((*line_it).ws_only()) + line_it = tokens.erase(line_it); + it = line_it - 1; + } + non_space = has_tag = false; + line_begin = it + 1; } + } } diff --git a/src/template_type.hpp b/src/template_type.hpp index c99e359..bca45d1 100644 --- a/src/template_type.hpp +++ b/src/template_type.hpp @@ -6,19 +6,22 @@ #include "token.hpp" namespace mstch { - class template_type { - public: - template_type() = default; - template_type(const std::string& str); - std::vector::const_iterator begin() const{return tokens.begin();} - std::vector::const_iterator end() const{return tokens.end();} - void operator<<(const token& token) { tokens.push_back(token); } - private: - enum class parse_state { - start, in_del_start, in_del, in_content, in_esccontent, in_del_end - }; - void tokenize(const std::string& str); - void strip_whitespace(); - std::vector tokens; - }; + +class template_type { + public: + template_type() = default; + template_type(const std::string& str); + std::vector::const_iterator begin() const { return tokens.begin(); } + std::vector::const_iterator end() const { return tokens.end(); } + void operator<<(const token& token) { tokens.push_back(token); } + + private: + enum class parse_state { + start, in_del_start, in_del, in_content, in_esccontent, in_del_end + }; + void tokenize(const std::string& str); + void strip_whitespace(); + std::vector tokens; +}; + } diff --git a/src/token.cpp b/src/token.cpp index abb3b93..de58143 100644 --- a/src/token.cpp +++ b/src/token.cpp @@ -4,7 +4,7 @@ using namespace mstch; token::type token::token_info(char c) { - switch (c) { + switch (c) { case '>': return type::partial; case '^': return type::inverted_section_open; case '/': return type::section_close; @@ -12,27 +12,27 @@ token::type token::token_info(char c) { 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) + 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::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 first = first_not_ws(str.begin() + left, str.end() - right); - m_type = token_info(*first); - if(m_type != type::variable) - first = first_not_ws(first + 1, str.end() - right); - m_name = {first, first_not_ws(str.rbegin() + right, str.rend() - left) + 1}; - } + if (left != 0 && right != 0) { + 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 { - m_type = type::text; - m_eol = (str.size() > 0 && str[str.size() - 1] == '\n'); - m_ws_only = (str.find_first_not_of(" \n\t") == std::string::npos); + 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}; } + } else { + m_type = type::text; + m_eol = (str.size() > 0 && str[str.size() - 1] == '\n'); + m_ws_only = (str.find_first_not_of(" \n\t") == std::string::npos); + } } diff --git a/src/token.hpp b/src/token.hpp index d3b56ea..39971b6 100644 --- a/src/token.hpp +++ b/src/token.hpp @@ -3,24 +3,27 @@ #include namespace mstch { - class token { - public: - enum class type { - text, variable, section_open, section_close, inverted_section_open, - unescaped_variable, comment, partial - }; - 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; }; - bool eol() const { return m_eol; } - bool ws_only() const { return m_ws_only; } - private: - type m_type; - std::string m_name; - std::string m_raw; - bool m_eol; - bool m_ws_only; - type token_info(char c); - }; + +class token { + public: + enum class type { + text, variable, section_open, section_close, inverted_section_open, + unescaped_variable, comment, partial + }; + 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; }; + bool eol() const { return m_eol; } + bool ws_only() const { return m_ws_only; } + + private: + type m_type; + std::string m_name; + std::string m_raw; + bool m_eol; + bool m_ws_only; + type token_info(char c); +}; + } diff --git a/src/utils.cpp b/src/utils.cpp index b8a8895..610f429 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -3,23 +3,23 @@ #include 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; + 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()); + for (auto rit = begin; rit != end; ++rit) + if (*rit != ' ') return --(rit.base()); + return --(end.base()); } std::string mstch::html_escape(std::string str) { - boost::replace_all(str, "&", "&"); - boost::replace_all(str, "'", "'"); - boost::replace_all(str, "\"", """); - boost::replace_all(str, "<", "<"); - boost::replace_all(str, ">", ">"); - boost::replace_all(str, "/", "/"); - return str; + boost::replace_all(str, "&", "&"); + boost::replace_all(str, "'", "'"); + boost::replace_all(str, "\"", """); + boost::replace_all(str, "<", "<"); + boost::replace_all(str, ">", ">"); + boost::replace_all(str, "/", "/"); + return str; } diff --git a/src/utils.hpp b/src/utils.hpp index 358d49b..39bcc8b 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -1,11 +1,22 @@ #pragma once #include +#include 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(std::string str); + +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(std::string str); + +template +auto visit(Args&& ... args) -> decltype(boost::apply_visitor( + std::forward(args)...)) +{ + return boost::apply_visitor(std::forward(args)...); +} + } diff --git a/src/visitor/get_token.hpp b/src/visitor/get_token.hpp index 99c0b24..d0d37db 100644 --- a/src/visitor/get_token.hpp +++ b/src/visitor/get_token.hpp @@ -2,6 +2,7 @@ #include #include + #include "mstch/mstch.hpp" namespace mstch { diff --git a/src/visitor/has_token.hpp b/src/visitor/has_token.hpp index 81d26ba..3309688 100644 --- a/src/visitor/has_token.hpp +++ b/src/visitor/has_token.hpp @@ -2,6 +2,7 @@ #include #include + #include "mstch/mstch.hpp" namespace mstch { diff --git a/src/visitor/render_section.hpp b/src/visitor/render_section.hpp index ef2b8b8..173cf72 100644 --- a/src/visitor/render_section.hpp +++ b/src/visitor/render_section.hpp @@ -5,6 +5,7 @@ #include "render_context.hpp" #include "mstch/mstch.hpp" +#include "utils.hpp" namespace mstch { namespace visitor { @@ -49,8 +50,7 @@ inline std::string render_section::operator()(const array& array) const { out += render_context::push(ctx, array).render(section); else for (auto& item: array) - out += boost::apply_visitor( - render_section(ctx, section, flag::keep_array), item); + out += visit(render_section(ctx, section, flag::keep_array), item); return out; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1ea0291..83522a6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,44 +1,43 @@ find_package(Boost 1.54 COMPONENTS program_options REQUIRED) include_directories( - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR} - ${Boost_INCLUDE_DIR}) + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR} + ${Boost_INCLUDE_DIR}) add_executable(benchmark benchmark_main.cpp) target_link_libraries(benchmark mstch) -add_executable(filetoheader filetoheader.cpp) -target_link_libraries(filetoheader ${Boost_PROGRAM_OPTIONS_LIBRARY}) -set(filetoheader_exe ${CMAKE_CURRENT_BINARY_DIR}/filetoheader${CMAKE_EXECUTABLE_SUFFIX}) +add_executable(headerize headerize.cpp) +target_link_libraries(headerize ${Boost_PROGRAM_OPTIONS_LIBRARY}) +set(headerize_exe ${CMAKE_CURRENT_BINARY_DIR}/headerize${CMAKE_EXECUTABLE_SUFFIX}) file(GLOB data_files RELATIVE - "${CMAKE_SOURCE_DIR}/test/data" - "${CMAKE_SOURCE_DIR}/test/data/*.hpp") + "${CMAKE_SOURCE_DIR}/test/data" + "${CMAKE_SOURCE_DIR}/test/data/*.hpp") foreach(data_file ${data_files}) - list(APPEND genargs "-C${data_file}") - string(REGEX REPLACE "\\.hpp" "" test_name "${data_file}") - list(APPEND tests "${test_name}") + list(APPEND genargs "-C${data_file}") + string(REGEX REPLACE "\\.hpp" "" test_name "${data_file}") + list(APPEND tests "${test_name}") endforeach(data_file) file(GLOB string_files RELATIVE - "${CMAKE_SOURCE_DIR}/test/data" - "${CMAKE_SOURCE_DIR}/test/data/*.mustache" - "${CMAKE_SOURCE_DIR}/test/data/*.txt" - "${CMAKE_SOURCE_DIR}/test/data/*.partial" - ) + "${CMAKE_SOURCE_DIR}/test/data" + "${CMAKE_SOURCE_DIR}/test/data/*.mustache" + "${CMAKE_SOURCE_DIR}/test/data/*.txt" + "${CMAKE_SOURCE_DIR}/test/data/*.partial") foreach(string_file ${string_files}) - list(APPEND genargs "-S${string_file}") + list(APPEND genargs "-S${string_file}") endforeach(string_file) add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp - COMMAND ${filetoheader_exe} --output ${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp --namespace mstchtest ${genargs} - DEPENDS ${filetoheader_exe} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/test/data/) + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp + COMMAND ${headerize_exe} --output ${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp --namespace mstchtest ${genargs} + DEPENDS ${headerize_exe} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/test/data/) set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp PROPERTIES GENERATED TRUE) add_custom_target(test_data_hpp DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp) @@ -47,5 +46,5 @@ target_link_libraries(mstch_test mstch) add_dependencies(mstch_test test_data_hpp) foreach(test ${tests}) - add_test(NAME ${test} COMMAND mstch_test ${test}) + add_test(NAME ${test} COMMAND mstch_test ${test}) endforeach(test) diff --git a/test/benchmark_main.cpp b/test/benchmark_main.cpp index 31af1c1..5382b5c 100644 --- a/test/benchmark_main.cpp +++ b/test/benchmark_main.cpp @@ -3,40 +3,42 @@ #include #include +unsigned long current_msec() { + return std::chrono::system_clock::now().time_since_epoch() / + std::chrono::milliseconds(1); +} + int main() { - std::string comment_tmp{ - "

{{header}}

    " - "{{#comments}}
  • {{name}}
    " - "

    {{body}}

  • {{/comments}}
" - }; - auto comment_view = mstch::map{ - {"header", std::string{"My Post Comments"}}, - {"comments", mstch::array{ + std::string comment_tmp{ + "

{{header}}

    " + "{{#comments}}
  • {{name}}
    " + "

    {{body}}

  • {{/comments}}
" + }; + auto comment_view = mstch::map{ + {"header", std::string{"My Post Comments"}}, + {"comments", mstch::array{ mstch::map{{"name", std::string{"Joe"}}, {"body", std::string{"Thanks for this post!"}}}, mstch::map{{"name", std::string{"Sam"}}, {"body", std::string{"Thanks for this post!"}}}, mstch::map{{"name", std::string{"Heather"}}, {"body", std::string{"Thanks for this post!"}}}, mstch::map{{"name", std::string{"Kathy"}}, {"body", std::string{"Thanks for this post!"}}}, mstch::map{{"name", std::string{"George"}}, {"body", std::string{"Thanks for this post!"}}} - }} - }; + }} + }; - std::vector times; - for(int j = 0; j < 10; j++) { - unsigned long start = - std::chrono::system_clock::now().time_since_epoch() / - std::chrono::milliseconds(1); - for(int i = 0; i < 5000; i++) { - mstch::render(comment_tmp, comment_view); - } - times.push_back((std::chrono::system_clock::now().time_since_epoch() / - std::chrono::milliseconds(1)) - start); - } + std::vector times; + for (int j = 0; j < 10; j++) { + unsigned long start = current_msec(); + for (int i = 0; i < 5000; i++) + mstch::render(comment_tmp, comment_view); + times.push_back(current_msec() - start); + } - float avg = 0; - for(int i: times) avg += i; - avg /= times.size(); + float avg = 0; + for (auto i: times) + avg += i; + avg /= times.size(); - std::cout << avg << std::endl; + std::cout << avg << std::endl; - return 0; + return 0; } diff --git a/test/filetoheader.cpp b/test/filetoheader.cpp deleted file mode 100644 index 8f646e9..0000000 --- a/test/filetoheader.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -void wrap_code(std::istream& input, std::ostream& output) { - std::string line; - while (std::getline(input, line)) { - output << line; - if(!input.eof()) output << std::endl; - } - output << std::endl; -} - -void wrap_string(std::istream& input, std::ostream& output, const std::string& variable_name) { - output << "const std::string " << variable_name << "{" << std::endl;; - std::string line; - while (std::getline(input, line)) { - boost::replace_all(line, "\\", "\\\\"); - boost::replace_all(line, "\"", "\\\""); - output << " \"" << line; - if(!input.eof()) output << "\\n"; - output << "\"" << std::endl; - } - output << "};" << std::endl; -} - -int main(int argc, char* argv[]) { - namespace po = boost::program_options; - - po::options_description desc("Allowed options"); - desc.add_options() - ("help", "show help") - ("output", po::value(), "output file") - ("namespace", po::value(), "namespace to use") - ("input-string,S", po::value>(), "files to parse as strings") - ("input-code,C", po::value>(), "files to parse as code"); - po::variables_map vm; - po::store(po::parse_command_line(argc, argv, desc), vm); - po::notify(vm); - - if (vm.count("help")) { - std::cout << desc << std::endl; - return 1; - } - - if (!vm.count("output")) { - std::cout << "Output file not set" << std::endl; - return 1; - } - - std::ofstream output(vm["output"].as(), std::ios::out); - - if(vm.count("namespace")) - output << "namespace " << vm["namespace"].as() << " {" << std::endl; - - if(vm.count("input-string")) { - for (auto &string_filename: vm["input-string"].as>()) { - std::ifstream input(string_filename, std::ios::in); - wrap_string(input, output, boost::replace_all_copy(string_filename, ".", "_")); - input.close(); - } - } - - if(vm.count("input-code")) { - for(auto& data_filename: vm["input-code"].as>()) { - std::ifstream input(data_filename, std::ios::in); - wrap_code(input, output); - input.close(); - } - } - - if(vm.count("namespace")) - output << "}" << std::endl; - - output.close(); - - return 0; -} diff --git a/test/headerize.cpp b/test/headerize.cpp new file mode 100644 index 0000000..b976eb4 --- /dev/null +++ b/test/headerize.cpp @@ -0,0 +1,89 @@ +#include +#include + +#include +#include +#include +#include + +void wrap_code(std::istream& input, std::ostream& output) { + std::string line; + while (std::getline(input, line)) { + output << line; + if (!input.eof()) + output << std::endl; + } + output << std::endl; +} + +void wrap_string(std::istream& input, std::ostream& output, + const std::string& variable_name) +{ + output << "const std::string " << variable_name << "{" << std::endl;; + std::string line; + while (std::getline(input, line)) { + boost::replace_all(line, "\\", "\\\\"); + boost::replace_all(line, "\"", "\\\""); + output << " \"" << line; + if (!input.eof()) + output << "\\n"; + output << "\"" << std::endl; + } + output << "};" << std::endl; +} + +int main(int argc, char* argv[]) { + namespace po = boost::program_options; + + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "show help") + ("output", po::value(), "output file") + ("namespace", po::value(), "namespace to use") + ("input-string,S", po::value>(), + "files to parse as strings") + ("input-code,C", po::value>(), + "files to parse as code"); + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + if (vm.count("help")) { + std::cout << desc << std::endl; + return 1; + } + + if (!vm.count("output")) { + std::cout << "Output file not set" << std::endl; + return 1; + } + + std::ofstream output(vm["output"].as(), std::ios::out); + + if (vm.count("namespace")) + output << "namespace " << vm["namespace"].as() << " {" << std::endl; + + if (vm.count("input-string")) { + for (auto& string_filename: vm["input-string"].as>()) { + std::ifstream input(string_filename, std::ios::in); + wrap_string(input, output, + boost::replace_all_copy(string_filename, ".", "_")); + input.close(); + } + } + + if (vm.count("input-code")) { + for (auto& data_filename: vm["input-code"].as>()) { + std::ifstream input(data_filename, std::ios::in); + wrap_code(input, output); + input.close(); + } + } + + if (vm.count("namespace")) + output << "}" << std::endl; + + output.close(); + + return 0; +} diff --git a/test/test_main.cpp b/test/test_main.cpp index 4ded83e..61cd646 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -7,11 +7,11 @@ using namespace mstchtest; #define MSTCH_PARTIAL_TEST(x) TEST_CASE(#x) { \ - REQUIRE(x ## _txt == mstch::render(x ## _mustache, x ## _data, {{"partial", x ## _partial}})); \ + REQUIRE(x ## _txt == mstch::render(x ## _mustache, x ## _data, {{"partial", x ## _partial}})); \ } #define MSTCH_TEST(x) TEST_CASE(#x) { \ - REQUIRE(x ## _txt == mstch::render(x ## _mustache, x ## _data)); \ + REQUIRE(x ## _txt == mstch::render(x ## _mustache, x ## _data)); \ } MSTCH_TEST(ampersand_escape)