diff --git a/README.md b/README.md
index ae147c5..e649ad7 100644
--- a/README.md
+++ b/README.md
@@ -103,15 +103,17 @@ Output:
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`.
+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 passed to `mstch::lambda` returns a `std::string` and
-accepts either no parameters:
+The lambda expression accepts either no parameters:
```c++
std::string view{"Hello {{lambda}}!"};
mstch::map context{
- {"lambda", mstch::lambda{[]() {
+ {"lambda", mstch::lambda{[]() -> mstch::node {
return std::string{"World"};
}}}
};
@@ -125,14 +127,13 @@ Output:
Hello World!
```
-Or it accepts a `const std::string&` that gets the unrendered literal block.
-The returned string will be parsed in both cases:
+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) {
+ {"bold", mstch::lambda{[](const std::string& text) -> mstch::node {
return "" + text + "";
}}}
};
diff --git a/include/mstch/mstch.hpp b/include/mstch/mstch.hpp
index b5675f6..4c677f5 100644
--- a/include/mstch/mstch.hpp
+++ b/include/mstch/mstch.hpp
@@ -35,7 +35,7 @@ class object_t {
mutable std::map cache;
};
-template
+template
class is_fun {
private:
using not_fun = char;
@@ -43,9 +43,9 @@ class is_fun {
using fun_with_args = char[3];
template struct really_has;
template static fun_without_args& test(
- really_has*);
+ really_has*);
template static fun_with_args& test(
- really_has*);
template static not_fun& test(...);
@@ -54,36 +54,48 @@ class is_fun {
static bool const has_args = sizeof(test(0)) == sizeof(fun_with_args);
};
-}
+template
+using node_renderer = std::function;
-class lambda {
+template
+class lambda_t {
public:
template
- lambda(F f, typename std::enable_if::no_args>::type* =0):
- fun([f](const std::string&){return f();})
+ lambda_t(F f, typename std::enable_if::no_args>::type* = 0):
+ fun([f](node_renderer renderer, const std::string&) {
+ return renderer(f());
+ })
{
}
template
- lambda(F f, typename std::enable_if::has_args>::type* =0):
- fun(f)
+ lambda_t(F f, typename std::enable_if::has_args>::type* = 0):
+ fun([f](node_renderer renderer, const std::string& text) {
+ return renderer(f(text));
+ })
{
}
- std::string operator()(const std::string& text = "") const {
- return fun(text);
+ std::string operator()(node_renderer renderer,
+ const std::string& text = "") const
+ {
+ return fun(renderer, text);
}
private:
- std::function fun;
+ std::function renderer, const std::string&)> fun;
};
+}
+
using node = boost::make_recursive_variant<
- std::nullptr_t, std::string, int, double, bool, lambda,
+ std::nullptr_t, std::string, int, double, bool,
+ internal::lambda_t,
std::shared_ptr>,
std::map,
std::vector>::type;
using object = internal::object_t;
+using lambda = internal::lambda_t;
using map = std::map;
using array = std::vector;
diff --git a/src/visitor/render_node.hpp b/src/visitor/render_node.hpp
index 63b7682..aef8027 100644
--- a/src/visitor/render_node.hpp
+++ b/src/visitor/render_node.hpp
@@ -37,7 +37,10 @@ class render_node: public boost::static_visitor {
}
std::string operator()(const lambda& value) const {
- auto rendered = render_context::push(ctx).render(template_type{value()});
+ template_type interpreted{value([this](const mstch::node& n) {
+ return visit(render_node(ctx), n);
+ })};
+ auto rendered = render_context::push(ctx).render(interpreted);
return (m_flag == flag::escape_html) ? html_escape(rendered) : rendered;
}
diff --git a/src/visitor/render_section.hpp b/src/visitor/render_section.hpp
index 25592ad..e2f6049 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"
+#include "render_node.hpp"
namespace mstch {
@@ -28,8 +29,10 @@ class render_section: public boost::static_visitor {
std::string section_str;
for(auto& token: section)
section_str += token.raw();
-
- return render_context::push(ctx).render(template_type{fun(section_str)});
+ template_type interpreted{fun([this](const mstch::node& n) {
+ return visit(render_node(ctx), n);
+ }, section_str)};
+ return render_context::push(ctx).render(interpreted);
}
std::string operator()(const array& array) const {
diff --git a/test/data/comments.hpp b/test/data/comments.hpp
index ca97699..96baccb 100644
--- a/test/data/comments.hpp
+++ b/test/data/comments.hpp
@@ -1,3 +1,3 @@
const mstch::node comments_data = mstch::map{
- {"title", mstch::lambda{[](){return std::string{"A Comedy of Errors"};}}}
+ {"title", mstch::lambda{[]()->mstch::node{return std::string{"A Comedy of Errors"};}}}
};
\ No newline at end of file
diff --git a/test/data/escaped.hpp b/test/data/escaped.hpp
index b6aecf3..bcb21bb 100644
--- a/test/data/escaped.hpp
+++ b/test/data/escaped.hpp
@@ -1,4 +1,4 @@
const mstch::node escaped_data = mstch::map{
- {"title", mstch::lambda{[](){ return std::string{"Bear > Shark"}; }}},
- {"entities", mstch::lambda{[](){ return std::string{"" \"'<>/"}; }}}
+ {"title", mstch::lambda{[]()->mstch::node{ return std::string{"Bear > Shark"}; }}},
+ {"entities", mstch::lambda{[]()->mstch::node{ return std::string{"" \"'<>/"}; }}}
};
\ No newline at end of file
diff --git a/test/data/higher_order_sections.hpp b/test/data/higher_order_sections.hpp
index 5a81935..d99a5a5 100644
--- a/test/data/higher_order_sections.hpp
+++ b/test/data/higher_order_sections.hpp
@@ -19,7 +19,7 @@ class higher_order_sections: public mstch::object {
}
mstch::node bolder() {
- return mstch::lambda{[this](const std::string& text) {
+ return mstch::lambda{[this](const std::string& text) -> mstch::node {
return "" + text + " " + m_helper;
}};
}
diff --git a/test/data/nested_higher_order_sections.hpp b/test/data/nested_higher_order_sections.hpp
index fc56a4a..26ed72a 100644
--- a/test/data/nested_higher_order_sections.hpp
+++ b/test/data/nested_higher_order_sections.hpp
@@ -1,5 +1,5 @@
const mstch::node nested_higher_order_sections_data = mstch::map{
- {"bold", mstch::lambda{[](const std::string& text) {
+ {"bold", mstch::lambda{[](const std::string& text) -> mstch::node {
return std::string{""} + text + std::string{""};
}}},
{"person", mstch::map{{"name", std::string{"Jonas"}}}}
diff --git a/test/data/partial_template.hpp b/test/data/partial_template.hpp
index 932b936..88c659b 100644
--- a/test/data/partial_template.hpp
+++ b/test/data/partial_template.hpp
@@ -1,4 +1,4 @@
const mstch::node partial_template_data = mstch::map{
- {"title", mstch::lambda{[](){ return std::string{"Welcome"}; }}},
- {"again", mstch::lambda{[](){ return std::string{"Goodbye"}; }}},
+ {"title", mstch::lambda{[]()->mstch::node{ return std::string{"Welcome"}; }}},
+ {"again", mstch::lambda{[]()->mstch::node{ return std::string{"Goodbye"}; }}},
};
\ No newline at end of file
diff --git a/test/data/section_functions_in_partials.hpp b/test/data/section_functions_in_partials.hpp
index 57fa9b5..1fc4434 100644
--- a/test/data/section_functions_in_partials.hpp
+++ b/test/data/section_functions_in_partials.hpp
@@ -1,5 +1,5 @@
const mstch::node section_functions_in_partials_data = mstch::map{
- {"bold", mstch::lambda{[](const std::string& text) {
+ {"bold", mstch::lambda{[](const std::string& text) -> mstch::node {
return std::string{""} + text + std::string{""};
}}}
};
\ No newline at end of file
diff --git a/test/data/unescaped.hpp b/test/data/unescaped.hpp
index c4e6ef3..66914a9 100644
--- a/test/data/unescaped.hpp
+++ b/test/data/unescaped.hpp
@@ -1,3 +1,3 @@
const mstch::node unescaped_data = mstch::map{
- {"title", mstch::lambda{[](){ return std::string{"Bear > Shark"}; }}}
+ {"title", mstch::lambda{[]()->mstch::node{ return std::string{"Bear > Shark"}; }}}
};
\ No newline at end of file
diff --git a/test/specs_lambdas.hpp b/test/specs_lambdas.hpp
index 951b1b4..13bf4ca 100644
--- a/test/specs_lambdas.hpp
+++ b/test/specs_lambdas.hpp
@@ -1,26 +1,26 @@
-std::map> specs_lambdas {
+std::map> specs_lambdas {
{"Interpolation", [](const std::string&) {
- return "world";
+ return std::string{"world"};
}},
{"Interpolation - Expansion", [](const std::string&) {
- return "{{planet}}";
+ return std::string{"{{planet}}"};
}},
{"Interpolation - Multiple Calls", [](const std::string&) {
- static int calls = 0; return std::to_string(++calls);
+ static int calls = 0; return ++calls;
}},
{"Escaping", [](const std::string&) {
- return ">";
+ return std::string{">"};
}},
{"Section", [](const std::string& txt) {
- return (txt == "{{x}}") ? "yes" : "no";
+ return std::string{(txt == "{{x}}") ? "yes" : "no"};
}},
{"Section - Expansion", [](const std::string& txt) {
- return txt + "{{planet}}" + txt;
+ return txt + std::string{"{{planet}}"} + txt;
}},
{"Section - Multiple Calls", [](const std::string& txt) {
return "__" + txt + "__";
}},
{"Inverted Section", [](const std::string& txt) {
- return "";
+ return false;
}}
};
\ No newline at end of file