diff --git a/README.md b/README.md index 69b230d..ccb45aa 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 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.2, including the lambda module. +v1.1.3, including the lambda module. [![GitHub version](https://badge.fury.io/gh/no1msd%2Fmstch.svg)](http://badge.fury.io/gh/no1msd%2Fmstch) [![Build Status](https://travis-ci.org/no1msd/mstch.svg?branch=master)](https://travis-ci.org/no1msd/mstch) diff --git a/src/render_context.cpp b/src/render_context.cpp index 7617f0f..90b2ffc 100644 --- a/src/render_context.cpp +++ b/src/render_context.cpp @@ -7,29 +7,29 @@ using namespace mstch; const mstch::node render_context::null_node; render_context::push::push(render_context& context, const mstch::node& node): - context(context) + m_context(context) { - context.nodes.emplace_front(node); - context.node_ptrs.emplace_front(&node); - context.state.push(std::unique_ptr(new outside_section)); + context.m_nodes.emplace_front(node); + context.m_node_ptrs.emplace_front(&node); + context.m_state.push(std::unique_ptr(new outside_section)); } render_context::push::~push() { - context.nodes.pop_front(); - context.node_ptrs.pop_front(); - context.state.pop(); + 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 context.render(templt); + return m_context.render(templt); } render_context::render_context( const mstch::node& node, const std::map& partials): - partials(partials), nodes(1, node), node_ptrs(1, &node) + m_partials(partials), m_nodes(1, node), m_node_ptrs(1, &node) { - state.push(std::unique_ptr(new outside_section)); + m_state.push(std::unique_ptr(new outside_section)); } const mstch::node& render_context::find_node( @@ -47,7 +47,7 @@ const mstch::node& render_context::find_node( } const mstch::node& render_context::get_node(const std::string& token) { - return find_node(token, node_ptrs); + return find_node(token, m_node_ptrs); } std::string render_context::render( @@ -57,8 +57,8 @@ std::string render_context::render( bool prev_eol = true; for (auto& token: templt) { if (prev_eol && prefix.length() != 0) - output += state.top()->render(*this, {prefix}); - output += state.top()->render(*this, token); + output += m_state.top()->render(*this, {prefix}); + output += m_state.top()->render(*this, token); prev_eol = token.eol(); } return output; @@ -67,6 +67,6 @@ std::string render_context::render( std::string render_context::render_partial( const std::string& partial_name, const std::string& prefix) { - return partials.count(partial_name) ? - render(partials.at(partial_name), prefix) : ""; + return m_partials.count(partial_name) ? + render(m_partials.at(partial_name), prefix) : ""; } diff --git a/src/render_context.hpp b/src/render_context.hpp index 619c6e8..b97d41b 100644 --- a/src/render_context.hpp +++ b/src/render_context.hpp @@ -20,7 +20,7 @@ class render_context { ~push(); std::string render(const template_type& templt); private: - render_context& context; + render_context& m_context; }; render_context( @@ -33,7 +33,7 @@ class render_context { const std::string& partial_name, const std::string& prefix); template void set_state(Args&& ... args) { - state.top() = std::unique_ptr( + m_state.top() = std::unique_ptr( new T(std::forward(args)...)); } @@ -42,10 +42,10 @@ class render_context { const mstch::node& find_node( const std::string& token, std::list current_nodes); - std::map partials; - std::deque nodes; - std::list node_ptrs; - std::stack> state; + std::map m_partials; + std::deque m_nodes; + std::list m_node_ptrs; + std::stack> m_state; }; } diff --git a/src/state/in_section.cpp b/src/state/in_section.cpp index 420441f..a139913 100644 --- a/src/state/in_section.cpp +++ b/src/state/in_section.cpp @@ -5,30 +5,30 @@ using namespace mstch; -in_section::in_section(type type, const std::string& section_name): - m_type(type), section_name(section_name), skipped_openings(0) +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() == section_name && skipped_openings == 0) { - auto& node = ctx.get_node(section_name); + 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, section), 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(section); + out = render_context::push(ctx).render(m_section); ctx.set_state(); return out; } else - skipped_openings--; + m_skipped_openings--; else if (token.token_type() == token::type::inverted_section_open || token.token_type() == token::type::section_open) - skipped_openings++; + m_skipped_openings++; - section << token; + m_section << token; return ""; } diff --git a/src/state/in_section.hpp b/src/state/in_section.hpp index 5eaf3c9..14ca2f7 100644 --- a/src/state/in_section.hpp +++ b/src/state/in_section.hpp @@ -11,14 +11,14 @@ namespace mstch { 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; + in_section(type type, const token& start_token); + 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; + const token& m_start_token; + template_type m_section; + int m_skipped_openings; }; } diff --git a/src/state/outside_section.cpp b/src/state/outside_section.cpp index c165f9b..c9817b1 100644 --- a/src/state/outside_section.cpp +++ b/src/state/outside_section.cpp @@ -12,10 +12,10 @@ std::string outside_section::render( using flag = render_node::flag; switch (token.token_type()) { case token::type::section_open: - ctx.set_state(in_section::type::normal, token.name()); + ctx.set_state(in_section::type::normal, token); break; case token::type::inverted_section_open: - ctx.set_state(in_section::type::inverted, token.name()); + ctx.set_state(in_section::type::inverted, token); break; case token::type::variable: return visit(render_node(ctx, flag::escape_html), ctx.get_node(token.name())); diff --git a/src/template_type.cpp b/src/template_type.cpp index 41f0fd6..5b40e40 100644 --- a/src/template_type.cpp +++ b/src/template_type.cpp @@ -2,7 +2,16 @@ using namespace mstch; -template_type::template_type(const std::string& str) { +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(); } @@ -13,45 +22,44 @@ void template_type::process_text(citer begin, citer end) { auto start = begin; for (auto it = begin; it != end; ++it) if (*it == '\n' || it == end - 1) { - tokens.push_back({{start, it + 1}}); + m_tokens.push_back({{start, it + 1}}); start = it + 1; } } void template_type::tokenize(const std::string& tmp) { - std::string open{"{{"}, close{"}}"}; 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(open, cur_pos); + auto open_pos = tmp.find(m_open, cur_pos); auto close_pos = tmp.find( - close, open_pos == npos ? open_pos : open_pos + 1); + m_close, open_pos == npos ? open_pos : open_pos + 1); if (close_pos != npos && open_pos != npos) { - if (*(beg + open_pos + open.size()) == '{' && - *(beg + close_pos + close.size()) == '}') + 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 + close.size(); - tokens.push_back({{beg + open_pos, beg + close_pos + close.size()}, - open.size(), close.size()}); + 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()) { - tokens.push_back({{""}}); - tokens.back().eol(true); + if (cur_pos == tmp.size()) { + m_tokens.push_back({{""}}); + m_tokens.back().eol(true); } - if (*(beg + open_pos + open.size()) == '=' && + if (*(beg + open_pos + m_open.size()) == '=' && *(beg + close_pos - 1) == '=') { - auto tok_beg = beg + open_pos + open.size() + 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)); - open = {front_skip, beg + tmp.find(' ', front_skip - beg)}; - close = {beg + tmp.rfind(' ', back_skip - beg) + 1, back_skip + 1}; + 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()); @@ -61,10 +69,10 @@ void template_type::tokenize(const std::string& tmp) { } void template_type::strip_whitespace() { - auto line_begin = tokens.begin(); + auto line_begin = m_tokens.begin(); bool has_tag = false, non_space = false; - for (auto it = tokens.begin(); it != tokens.end(); ++it) { + 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) @@ -77,7 +85,7 @@ void template_type::strip_whitespace() { store_prefixes(line_begin); auto c = line_begin; - for (bool end = false; !end; (*c).ws_only() ? c = tokens.erase(c) : ++c) + for (bool end = false; !end; (*c).ws_only() ? c = m_tokens.erase(c) : ++c) if ((end = (*c).eol())) it = c - 1; } diff --git a/src/template_type.hpp b/src/template_type.hpp index 4dfe826..3c5bf91 100644 --- a/src/template_type.hpp +++ b/src/template_type.hpp @@ -12,12 +12,15 @@ 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); } + template_type(const std::string& str, const delim_type& delims); + std::vector::const_iterator begin() const { return m_tokens.begin(); } + std::vector::const_iterator end() const { return m_tokens.end(); } + void operator<<(const token& token) { m_tokens.push_back(token); } private: - std::vector tokens; + std::vector 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); diff --git a/src/token.cpp b/src/token.cpp index 8a15373..9443a22 100644 --- a/src/token.cpp +++ b/src/token.cpp @@ -31,6 +31,8 @@ token::token(const std::string& str, std::size_t left, std::size_t right): 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; diff --git a/src/token.hpp b/src/token.hpp index 4ee4ddf..fde8017 100644 --- a/src/token.hpp +++ b/src/token.hpp @@ -4,6 +4,8 @@ namespace mstch { +using delim_type = std::pair; + class token { public: enum class type { @@ -15,6 +17,7 @@ class token { 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; }; @@ -27,6 +30,7 @@ class token { 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); diff --git a/src/visitor/get_token.hpp b/src/visitor/get_token.hpp index 1915569..d41ab6e 100644 --- a/src/visitor/get_token.hpp +++ b/src/visitor/get_token.hpp @@ -10,26 +10,26 @@ namespace mstch { class get_token: public boost::static_visitor { public: get_token(const std::string& token, const mstch::node& node): - token(token), node(node) + m_token(token), m_node(node) { } template const mstch::node& operator()(const T&) const { - return node; + return m_node; } const mstch::node& operator()(const map& map) const { - return map.at(token); + return map.at(m_token); } const mstch::node& operator()(const std::shared_ptr& object) const { - return object->at(token); + return object->at(m_token); } private: - const std::string& token; - const mstch::node& node; + const std::string& m_token; + const mstch::node& m_node; }; } diff --git a/src/visitor/has_token.hpp b/src/visitor/has_token.hpp index 81c9cd3..5ab30d4 100644 --- a/src/visitor/has_token.hpp +++ b/src/visitor/has_token.hpp @@ -8,24 +8,24 @@ namespace mstch { class has_token: public boost::static_visitor { public: - has_token(const std::string& token): token(token) { + has_token(const std::string& token): m_token(token) { } template bool operator()(const T&) const { - return token == "."; + return m_token == "."; } bool operator()(const map& map) const { - return map.count(token) == 1; + return map.count(m_token) == 1; } bool operator()(const std::shared_ptr& object) const { - return object->has(token); + return object->has(m_token); } private: - const std::string& token; + const std::string& m_token; }; } diff --git a/src/visitor/render_node.hpp b/src/visitor/render_node.hpp index 03e9a47..633dd4d 100644 --- a/src/visitor/render_node.hpp +++ b/src/visitor/render_node.hpp @@ -13,7 +13,7 @@ class render_node: public boost::static_visitor { public: enum class flag { none, escape_html }; render_node(render_context& ctx, flag p_flag = flag::none): - ctx(ctx), m_flag(p_flag) + m_ctx(ctx), m_flag(p_flag) { } @@ -38,9 +38,9 @@ class render_node: public boost::static_visitor { std::string operator()(const lambda& value) const { template_type interpreted{value([this](const mstch::node& n) { - return visit(render_node(ctx), n); + return visit(render_node(m_ctx), n); })}; - auto rendered = render_context::push(ctx).render(interpreted); + auto rendered = render_context::push(m_ctx).render(interpreted); return (m_flag == flag::escape_html) ? html_escape(rendered) : rendered; } @@ -49,7 +49,7 @@ class render_node: public boost::static_visitor { } private: - render_context& ctx; + render_context& m_ctx; flag m_flag; }; diff --git a/src/visitor/render_section.hpp b/src/visitor/render_section.hpp index e2f6049..f2d5259 100644 --- a/src/visitor/render_section.hpp +++ b/src/visitor/render_section.hpp @@ -15,39 +15,42 @@ class render_section: public boost::static_visitor { render_section( render_context& ctx, const template_type& section, + const delim_type& delims, flag p_flag = flag::none): - ctx(ctx), section(section), m_flag(p_flag) + m_ctx(ctx), m_section(section), m_delims(delims), m_flag(p_flag) { } template std::string operator()(const T& t) const { - return render_context::push(ctx, t).render(section); + return render_context::push(m_ctx, t).render(m_section); } std::string operator()(const lambda& fun) const { std::string section_str; - for(auto& token: section) + for (auto& token: m_section) section_str += token.raw(); template_type interpreted{fun([this](const mstch::node& n) { - return visit(render_node(ctx), n); - }, section_str)}; - return render_context::push(ctx).render(interpreted); + 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(ctx, array).render(section); + 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(ctx, section, flag::keep_array), item); + out += visit(render_section( + m_ctx, m_section, m_delims, flag::keep_array), item); return out; } private: - render_context& ctx; - const template_type& section; + render_context& m_ctx; + const template_type& m_section; + const delim_type& m_delims; flag m_flag; }; diff --git a/test/specs_lambdas.hpp b/test/specs_lambdas.hpp index 6ec0d59..603c9b6 100644 --- a/test/specs_lambdas.hpp +++ b/test/specs_lambdas.hpp @@ -5,6 +5,9 @@ std::map specs_lambdas { {"Interpolation - Expansion", mstch::lambda{[](const std::string&) -> mstch::node { return std::string{"{{planet}}"}; }}}, + {"Interpolation - Alternate Delimiters", mstch::lambda{[](const std::string&) -> mstch::node { + return std::string{"|planet| => {{planet}}"}; + }}}, {"Interpolation - Multiple Calls", mstch::lambda{[](const std::string&) -> mstch::node { static int calls = 0; return ++calls; }}}, @@ -17,6 +20,9 @@ std::map specs_lambdas { {"Section - Expansion", mstch::lambda{[](const std::string& txt) -> mstch::node { return txt + std::string{"{{planet}}"} + txt; }}}, + {"Section - Alternate Delimiters", mstch::lambda{[](const std::string& txt) -> mstch::node { + return txt + std::string{"{{planet}} => |planet|"} + txt; + }}}, {"Section - Multiple Calls", mstch::lambda{[](const std::string& txt) -> mstch::node { return "__" + txt + "__"; }}},