diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..485dee6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.idea
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..2b01784
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
+project(mstch)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+
+find_package(Boost 1.54 REQUIRED)
+include_directories(src include vendor/include ${Boost_INCLUDE_DIR})
+
+set(SRC
+ src/mstch.cpp
+ test/main.cpp)
+
+add_executable(mstch ${SRC} include/types.h src/utils.h src/utils.cpp src/token.cpp src/token.h src/render_context.cpp src/render_context.h src/state/render_state.h src/state/in_section.cpp src/state/in_section.h src/state/in_inverted_section.cpp src/state/in_inverted_section.h src/state/outside_section.cpp src/state/outside_section.h src/visitor/is_node_empty.cpp src/visitor/is_node_empty.h src/visitor/render_node.cpp src/visitor/render_node.h src/visitor/render_section.cpp src/visitor/render_section.h src/visitor/to_json.cpp src/visitor/to_json.h)
diff --git a/include/mstch.h b/include/mstch.h
new file mode 100644
index 0000000..495a54e
--- /dev/null
+++ b/include/mstch.h
@@ -0,0 +1,13 @@
+#ifndef _MSTCH_H_
+#define _MSTCH_H_
+
+#include "types.h"
+
+namespace mstch {
+ std::string render(
+ const std::string& tmplt,
+ const object& context,
+ const std::map& partials = std::map());
+}
+
+#endif // _MSTCH_H_
diff --git a/include/types.h b/include/types.h
new file mode 100644
index 0000000..9ffb49e
--- /dev/null
+++ b/include/types.h
@@ -0,0 +1,19 @@
+#ifndef _MSTCH_TYPES_H_
+#define _MSTCH_TYPES_H_
+
+#include
+#include
+#include
+
+#include
+
+namespace mstch {
+ using node = boost::make_recursive_variant<
+ boost::blank, std::string, int, bool,
+ std::map,
+ std::vector>::type;
+ using object = std::map;
+ using array = std::vector;
+}
+
+#endif //_MSTCH_TYPES_H_
diff --git a/src/mstch.cpp b/src/mstch.cpp
new file mode 100644
index 0000000..4be0b55
--- /dev/null
+++ b/src/mstch.cpp
@@ -0,0 +1,33 @@
+#include
+#include
+
+#include "mstch.h"
+#include "render_context.h"
+
+using namespace mstch;
+
+std::string strip_whitespace(std::string tmplt) {
+ tmplt = std::regex_replace(tmplt, std::regex("\\{\\{![^\\}]*\\}\\}"), "{{!}}");
+ std::ostringstream out;
+ std::istringstream in(tmplt);
+ std::string line;
+ std::regex tag_match("\\{{2}[ ]*[#|/|^|!]{1}[^\\}]*\\}{2}");
+ std::regex whitespace_match("^\\s*$");
+ while (std::getline(in, line)) {
+ std::string no_tags = std::regex_replace(line, tag_match, "");
+ if(no_tags != line && std::regex_match(no_tags, whitespace_match)) {
+ out << std::regex_replace(line, std::regex("\\s"), "");
+ } else {
+ out << line;
+ if(!in.eof()) out << std::endl;
+ }
+ }
+ return out.str();
+}
+
+std::string mstch::render(
+ const std::string& tmplt, const object& root_object,
+ const std::map& partials)
+{
+ return render_context(root_object, partials).render(strip_whitespace(tmplt));
+}
diff --git a/src/render_context.cpp b/src/render_context.cpp
new file mode 100644
index 0000000..71e7b3d
--- /dev/null
+++ b/src/render_context.cpp
@@ -0,0 +1,49 @@
+#include "render_context.h"
+#include "state/render_state.h"
+#include "state/outside_section.h"
+
+#include
+
+using namespace mstch;
+
+const mstch::node render_context::null_node;
+
+render_context::render_context(const mstch::object &object, const std::map& partials):
+ objects{object},
+ partials(partials),
+ state(new state::outside_section)
+{
+}
+
+render_context::render_context(const mstch::object& object, const render_context& context):
+ objects(context.objects),
+ partials(context.partials),
+ state(new state::outside_section)
+{
+ objects.push_front(object);
+}
+
+const mstch::node& render_context::find_node(const std::string &token, const std::deque ¤t_objects) {
+ /*if(token != "." && token.find('.') != std::string::npos) {
+ return find_node(token.substr(token.rfind('.') + 1),
+ {boost::get(find_node(token.substr(0, token.rfind('.')), current_objects))});
+ } else {*/
+ for (auto& object: current_objects)
+ if (object.count(token) != 0)
+ return object.at(token);
+ return null_node;
+ //}
+}
+
+const mstch::node& render_context::get_node(const std::string& token) {
+ return find_node(token, objects);
+}
+
+std::string render_context::render(const std::string& t) {
+ std::ostringstream output;
+ auto re = std::regex("\\{{2}[^\\}]*\\}{2}|\\{{3}[^\\}]*\\}{3}");
+ std::sregex_token_iterator it(t.begin(), t.end(), re, {-1, 0});
+ for(; it != std::sregex_token_iterator(); ++it)
+ output << state->render(*this, token(it->str()));
+ return output.str();
+}
diff --git a/src/render_context.h b/src/render_context.h
new file mode 100644
index 0000000..ad56f06
--- /dev/null
+++ b/src/render_context.h
@@ -0,0 +1,31 @@
+#ifndef _MSTCH_RENDER_CONTEXT_H_
+#define _MSTCH_RENDER_CONTEXT_H_
+
+#include
+#include
+#include
+
+#include "types.h"
+#include "state/render_state.h"
+
+namespace mstch {
+ class render_context {
+ private:
+ static const mstch::node null_node;
+ const mstch::node& find_node(const std::string& token, const std::deque& current_objects);
+ const std::map& partials;
+ std::deque objects;
+ std::unique_ptr state;
+ public:
+ render_context(const mstch::object& object, const std::map& partials);
+ render_context(const mstch::object& object, const render_context& context);
+ const mstch::node& get_node(const std::string& token);
+ std::string render(const std::string& tmplt);
+ template
+ void set_state(Args&&... args) {
+ state = std::unique_ptr(new T(std::forward(args)...));
+ }
+ };
+}
+
+#endif //_MSTCH_RENDER_CONTEXT_H_
diff --git a/src/state/in_inverted_section.cpp b/src/state/in_inverted_section.cpp
new file mode 100644
index 0000000..6d6ed8c
--- /dev/null
+++ b/src/state/in_inverted_section.cpp
@@ -0,0 +1,45 @@
+#include
+#include
+#include "visitor/is_node_empty.h"
+#include "in_inverted_section.h"
+#include "outside_section.h"
+#include "render_context.h"
+
+using namespace mstch;
+
+state::in_inverted_section::in_inverted_section(const std::string& section_name):
+ section_name(section_name),
+ skipped_openings(0)
+{
+}
+
+std::string state::in_inverted_section::render(render_context& context, const token& token) {
+ switch(token.type()) {
+ case token_type::section_close:
+ if(token.content() == section_name && skipped_openings == 0) {
+ std::ostringstream out;
+ out << boost::apply_visitor(visitor::to_json(), context.get_node(section_name));
+ if(boost::apply_visitor(visitor::is_node_empty(), context.get_node(section_name)))
+ out << "ÜRES: " + section_name + " " + section_text.str();//render_context(mstch::object{}, context).render(section_text.str());
+ else
+ out << "NEMÜRES: " + section_name + " " + section_text.str();
+ context.set_state();
+ return out.str();
+ } else {
+ skipped_openings--;
+ section_text << token.raw();
+ }
+ break;
+ case token_type::inverted_section_open:
+ case token_type::section_open:
+ skipped_openings++;
+ case token_type::text:
+ case token_type::variable:
+ case token_type::unescaped_variable:
+ case token_type::comment:
+ case token_type::partial:
+ section_text << token.raw();
+ break;
+ }
+ return "";
+}
diff --git a/src/state/in_inverted_section.h b/src/state/in_inverted_section.h
new file mode 100644
index 0000000..d10e411
--- /dev/null
+++ b/src/state/in_inverted_section.h
@@ -0,0 +1,21 @@
+#ifndef _MSTCH_IN_INVERTED_SECTION_H_
+#define _MSTCH_IN_INVERTED_SECTION_H_
+
+#include
+#include "render_state.h"
+
+namespace mstch {
+ namespace state {
+ class in_inverted_section : public render_state, public std::enable_shared_from_this {
+ private:
+ const std::string section_name;
+ std::ostringstream section_text;
+ int skipped_openings;
+ public:
+ in_inverted_section(const std::string §ion_name);
+ std::string render(render_context &context, const token &token) override;
+ };
+ }
+}
+
+#endif //_MSTCH_IN_INVERTED_SECTION_H_
diff --git a/src/state/in_section.cpp b/src/state/in_section.cpp
new file mode 100644
index 0000000..c8fc384
--- /dev/null
+++ b/src/state/in_section.cpp
@@ -0,0 +1,38 @@
+#include "visitor/is_node_empty.h"
+#include "visitor/render_section.h"
+#include "in_section.h"
+#include "outside_section.h"
+
+using namespace mstch;
+
+state::in_section::in_section(const std::string& section_name): section_name(section_name), skipped_openings(0) {
+}
+
+std::string state::in_section::render(render_context& context, const token& token) {
+ switch(token.type()) {
+ case token_type::section_close:
+ if(token.content() == section_name && skipped_openings == 0) {
+ auto section_node = context.get_node(section_name);
+ std::string out("");
+ if (!boost::apply_visitor(visitor::is_node_empty(), section_node))
+ out = boost::apply_visitor(visitor::render_section(context, section_text.str()), section_node);
+ context.set_state();
+ return out;
+ } else {
+ skipped_openings--;
+ section_text << token.raw();
+ }
+ break;
+ case token_type::inverted_section_open:
+ case token_type::section_open:
+ skipped_openings++;
+ case token_type::text:
+ case token_type::variable:
+ case token_type::unescaped_variable:
+ case token_type::comment:
+ case token_type::partial:
+ section_text << token.raw();
+ break;
+ }
+ return "";
+}
diff --git a/src/state/in_section.h b/src/state/in_section.h
new file mode 100644
index 0000000..e6e679c
--- /dev/null
+++ b/src/state/in_section.h
@@ -0,0 +1,21 @@
+#ifndef _MSTCH_IN_SECTION_H_
+#define _MSTCH_IN_SECTION_H_
+
+#include "render_state.h"
+#include
+
+namespace mstch {
+ namespace state {
+ class in_section: public render_state {
+ private:
+ const std::string section_name;
+ std::ostringstream section_text;
+ int skipped_openings;
+ public:
+ in_section(const std::string& section_name);
+ std::string render(render_context& context, const token& token) override;
+ };
+ }
+}
+
+#endif //_MSTCH_IN_SECTION_H_
diff --git a/src/state/outside_section.cpp b/src/state/outside_section.cpp
new file mode 100644
index 0000000..5bf5cac
--- /dev/null
+++ b/src/state/outside_section.cpp
@@ -0,0 +1,31 @@
+#include
+#include "outside_section.h"
+#include "in_section.h"
+#include "in_inverted_section.h"
+#include "render_context.h"
+
+using namespace mstch;
+
+std::string state::outside_section::render(render_context& context, const token& token) {
+ switch(token.type()) {
+ case token_type::section_open:
+ context.set_state(token.content());
+ break;
+ case token_type::inverted_section_open:
+ context.set_state(token.content());
+ break;
+ case token_type::variable:
+ case token_type::unescaped_variable:
+ return boost::apply_visitor(visitor::render_node(token.type() == token_type::variable),
+ context.get_node(token.content()));
+ case token_type::comment: break;
+ case token_type::text:
+ return token.raw();
+ case token_type::partial:
+ break;//render_context(mstch::object{{".", i}}, context).render(section);
+ case token_type::section_close:
+ // TODO ERROR
+ break;
+ }
+ return "";
+}
diff --git a/src/state/outside_section.h b/src/state/outside_section.h
new file mode 100644
index 0000000..2afd3c8
--- /dev/null
+++ b/src/state/outside_section.h
@@ -0,0 +1,15 @@
+#ifndef _MSTCH_OUTSIDE_SECTION_H_
+#define _MSTCH_OUTSIDE_SECTION_H_
+
+#include "render_state.h"
+
+namespace mstch {
+ namespace state {
+ class outside_section : public render_state {
+ public:
+ std::string render(render_context &context, const token &token) override;
+ };
+ }
+}
+
+#endif //_MSTCH_OUTSIDE_SECTION_H_
diff --git a/src/state/render_state.h b/src/state/render_state.h
new file mode 100644
index 0000000..e3be626
--- /dev/null
+++ b/src/state/render_state.h
@@ -0,0 +1,18 @@
+#ifndef _MSTCH_RENDER_STATE_H_
+#define _MSTCH_RENDER_STATE_H_
+
+#include
+#include
+
+namespace mstch {
+ class render_context;
+ namespace state {
+ class render_state {
+ public:
+ virtual ~render_state() {}
+ virtual std::string render(render_context& context, const token& token) = 0;
+ };
+ }
+}
+
+#endif //_MSTCH_RENDER_STATE_H_
diff --git a/src/token.cpp b/src/token.cpp
new file mode 100644
index 0000000..ce8af47
--- /dev/null
+++ b/src/token.cpp
@@ -0,0 +1,54 @@
+#include "token.h"
+
+#include "utils.h"
+#include
+
+using namespace mstch;
+
+token::token(const std::string& raw_token): raw_val(raw_token) {
+ if(std::regex_match(raw_token, std::regex("\\{{2}[^\\}]*\\}{2}|\\{{3}[^\\}]*\\}{3}"))) {
+ std::string inside = raw_token.substr(2, raw_token.size() - 4);
+ inside = trim(inside);
+ if (inside.size() > 0 && inside.at(0) == '#') {
+ type_val = token_type::section_open;
+ content_val = inside.substr(1);
+ } else if (inside.size() > 0 && inside.at(0) == '>') {
+ type_val = token_type::partial;
+ content_val = inside.substr(1);
+ } else if (inside.size() > 0 && inside.at(0) == '^') {
+ type_val = token_type::inverted_section_open;
+ content_val = inside.substr(1);
+ } else if (inside.size() > 0 && inside.at(0) == '/') {
+ type_val = token_type::section_close;
+ content_val = inside.substr(1);
+ } else if (inside.size() > 0 && inside.at(0) == '&') {
+ type_val = token_type::unescaped_variable;
+ content_val = inside.substr(1);
+ } else if (inside.size() > 0 && inside.at(0) == '{' && inside.at(inside.size() - 1) == '}') {
+ type_val = token_type::unescaped_variable;
+ content_val = inside.substr(1, inside.size() - 2);
+ } else if (inside.size() > 0 && inside.at(0) == '!') {
+ type_val = token_type::comment;
+ content_val = inside.substr(1);
+ } else {
+ type_val = token_type::variable;
+ content_val = inside;
+ }
+ content_val = trim(content_val);
+ } else {
+ type_val = token_type::text;
+ content_val = raw_token;
+ }
+}
+
+token_type token::type() const {
+ return type_val;
+}
+
+std::string token::content() const {
+ return content_val;
+}
+
+std::string token::raw() const {
+ return raw_val;
+}
diff --git a/src/token.h b/src/token.h
new file mode 100644
index 0000000..d681cc5
--- /dev/null
+++ b/src/token.h
@@ -0,0 +1,25 @@
+#ifndef _MSTCH_TOKEN_H_
+#define _MSTCH_TOKEN_H_
+
+#include
+
+namespace mstch {
+ enum class token_type {
+ text, variable, section_open, section_close, inverted_section_open, unescaped_variable, comment, partial
+ };
+
+ class token {
+ private:
+ token_type type_val;
+ std::string content_val;
+ std::string raw_val;
+
+ public:
+ token(const std::string& raw_token);
+ token_type type() const;
+ std::string content() const;
+ std::string raw() const;
+ };
+}
+
+#endif //_MSTCH_TOKEN_H_
diff --git a/src/utils.cpp b/src/utils.cpp
new file mode 100644
index 0000000..57d17a2
--- /dev/null
+++ b/src/utils.cpp
@@ -0,0 +1,36 @@
+#include "utils.h"
+
+#include
+
+std::string& mstch::ltrim(std::string& s) {
+ s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace))));
+ return s;
+}
+
+std::string& mstch::rtrim(std::string& s) {
+ s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end());
+ return s;
+}
+
+std::string& mstch::trim(std::string& s) {
+ return ltrim(rtrim(s));
+}
+
+std::string mstch::replace_all(std::string str, const std::string& from, const std::string& to) {
+ size_t start_pos = 0;
+ while((start_pos = str.find(from, start_pos)) != std::string::npos) {
+ str.replace(start_pos, from.length(), to);
+ start_pos += to.length();
+ }
+ return str;
+}
+
+std::string mstch::html_escape(std::string str) {
+ str = replace_all(str, "&", "&");
+ str = replace_all(str, "'", "'");
+ str = replace_all(str, "\"", """);
+ str = replace_all(str, "<", "<");
+ str = replace_all(str, ">", ">");
+ str = replace_all(str, "/", "/");
+ return str;
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..ba1a13e
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,14 @@
+#ifndef _MSTCH_UTILS_H_
+#define _MSTCH_UTILS_H_
+
+#include
+
+namespace mstch {
+ std::string& ltrim(std::string& s);
+ std::string& rtrim(std::string& s);
+ std::string& trim(std::string& s);
+ std::string replace_all(std::string str, const std::string& from, const std::string& to);
+ std::string html_escape(std::string str);
+}
+
+#endif //_MSTCH_UTILS_H_
diff --git a/src/visitor/is_node_empty.cpp b/src/visitor/is_node_empty.cpp
new file mode 100644
index 0000000..6aa9988
--- /dev/null
+++ b/src/visitor/is_node_empty.cpp
@@ -0,0 +1,27 @@
+#include "is_node_empty.h"
+
+using namespace mstch;
+
+bool visitor::is_node_empty::operator()(const boost::blank& blank) const {
+ return true;
+}
+
+bool visitor::is_node_empty::operator()(const int& i) const {
+ return i == 0;
+}
+
+bool visitor::is_node_empty::operator()(const bool& b) const {
+ return !b;
+}
+
+bool visitor::is_node_empty::operator()(const std::string& str) const {
+ return str == "";
+}
+
+bool visitor::is_node_empty::operator()(const array& arr) const {
+ return arr.size() == 0;
+}
+
+bool visitor::is_node_empty::operator()(const object& obj) const {
+ return false;
+}
diff --git a/src/visitor/is_node_empty.h b/src/visitor/is_node_empty.h
new file mode 100644
index 0000000..4843619
--- /dev/null
+++ b/src/visitor/is_node_empty.h
@@ -0,0 +1,23 @@
+#ifndef _MSTCH_IS_NODE_EMPTY_H_
+#define _MSTCH_IS_NODE_EMPTY_H_
+
+#include
+#include
+
+#include "types.h"
+
+namespace mstch {
+ namespace visitor {
+ class is_node_empty: public boost::static_visitor {
+ public:
+ bool operator()(const boost::blank& blank) const;
+ bool operator()(const int& i) const;
+ bool operator()(const bool& b) const;
+ bool operator()(const std::string& str) const;
+ bool operator()(const array& arr) const;
+ bool operator()(const object& obj) const;
+ };
+ }
+}
+
+#endif //_MSTCH_IS_NODE_EMPTY_H_
diff --git a/src/visitor/render_node.cpp b/src/visitor/render_node.cpp
new file mode 100644
index 0000000..1de510a
--- /dev/null
+++ b/src/visitor/render_node.cpp
@@ -0,0 +1,31 @@
+#include
+#include "render_node.h"
+
+using namespace mstch;
+
+visitor::render_node::render_node(bool html_escaped): html_escaped(html_escaped) {
+}
+
+std::string visitor::render_node::operator()(const boost::blank& blank) const {
+ return "";
+}
+
+std::string visitor::render_node::operator()(const int& i) const {
+ return std::to_string(i);
+}
+
+std::string visitor::render_node::operator()(const bool& b) const {
+ return b?"true":"false";
+}
+
+std::string visitor::render_node::operator()(const std::string& str) const {
+ return html_escaped?html_escape(str):str;
+}
+
+std::string visitor::render_node::operator()(const array& arr) const {
+ return "";
+}
+
+std::string visitor::render_node::operator()(const object& obj) const {
+ return "";
+}
diff --git a/src/visitor/render_node.h b/src/visitor/render_node.h
new file mode 100644
index 0000000..d95874f
--- /dev/null
+++ b/src/visitor/render_node.h
@@ -0,0 +1,26 @@
+#ifndef _MSTCH_RENDER_NODE_H_
+#define _MSTCH_RENDER_NODE_H_
+
+#include
+#include
+
+#include "types.h"
+
+namespace mstch {
+ namespace visitor {
+ class render_node: public boost::static_visitor {
+ private:
+ bool html_escaped;
+ public:
+ render_node(bool html_escaped);
+ std::string operator()(const boost::blank& blank) const;
+ std::string operator()(const int& i) const;
+ std::string operator()(const bool& b) const;
+ std::string operator()(const std::string& str) const;
+ std::string operator()(const array& arr) const;
+ std::string operator()(const object& obj) const;
+ };
+ }
+}
+
+#endif //_MSTCH_RENDER_NODE_H_
diff --git a/src/visitor/render_section.cpp b/src/visitor/render_section.cpp
new file mode 100644
index 0000000..bbc525a
--- /dev/null
+++ b/src/visitor/render_section.cpp
@@ -0,0 +1,38 @@
+#include "render_section.h"
+#include "is_node_empty.h"
+
+using namespace mstch;
+
+visitor::render_section::render_section(render_context& context, const std::string& section):
+ context(context),
+ section(section)
+{
+}
+
+std::string visitor::render_section::operator()(const boost::blank& blank) const {
+ return "";
+}
+
+std::string visitor::render_section::operator()(const int& i) const {
+ return render_context(mstch::object{{".", i}}, context).render(section);
+}
+
+std::string visitor::render_section::operator()(const bool& b) const {
+ return render_context(mstch::object{{".", b}}, context).render(section);
+}
+
+std::string visitor::render_section::operator()(const std::string& str) const {
+ return render_context(mstch::object{{".", str}}, context).render(section);
+}
+
+std::string visitor::render_section::operator()(const array& arr) const {
+ std::ostringstream out;
+ for (auto& item: arr)
+ if (!boost::apply_visitor(visitor::is_node_empty(), item))
+ out << boost::apply_visitor(*this, item);
+ return out.str();
+}
+
+std::string visitor::render_section::operator()(const object& obj) const {
+ return render_context(obj, context).render(section);
+}
diff --git a/src/visitor/render_section.h b/src/visitor/render_section.h
new file mode 100644
index 0000000..f6a4cb8
--- /dev/null
+++ b/src/visitor/render_section.h
@@ -0,0 +1,28 @@
+#ifndef _MSTCH_RENDER_SECTION_H_
+#define _MSTCH_RENDER_SECTION_H_
+
+#include
+#include
+#include
+
+#include "types.h"
+
+namespace mstch {
+ namespace visitor {
+ class render_section: public boost::static_visitor {
+ private:
+ render_context& context;
+ std::string section;
+ public:
+ render_section(render_context& context, const std::string& section);
+ std::string operator()(const boost::blank& blank) const;
+ std::string operator()(const int& i) const;
+ std::string operator()(const bool& b) const;
+ std::string operator()(const std::string& str) const;
+ std::string operator()(const array& arr) const;
+ std::string operator()(const object& obj) const;
+ };
+ }
+}
+
+#endif //_MSTCH_RENDER_SECTION_H_
diff --git a/src/visitor/to_json.cpp b/src/visitor/to_json.cpp
new file mode 100644
index 0000000..8233687
--- /dev/null
+++ b/src/visitor/to_json.cpp
@@ -0,0 +1,44 @@
+#include "to_json.h"
+
+using namespace mstch;
+
+std::string visitor::to_json::operator()(const boost::blank& blank) const {
+ return "";
+}
+
+std::string visitor::to_json::operator()(const int& i) const {
+ return std::to_string(i);
+}
+
+std::string visitor::to_json::operator()(const bool& b) const {
+ return b?"true":"false";
+}
+
+std::string visitor::to_json::operator()(const std::string& str) const {
+ return "\"" + str + "\"";
+}
+
+std::string visitor::to_json::operator()(const array& arr) const {
+ bool first = true;
+ std::ostringstream out;
+ out << "[";
+ for(auto& item: arr) {
+ first?(first = false):(out << ", ");
+ boost::apply_visitor(*this, item);
+ }
+ out << "]";
+ return out.str();
+}
+
+std::string visitor::to_json::operator()(const object& obj) const {
+ bool first = true;
+ std::ostringstream out;
+ out << "{";
+ for(auto& item: obj) {
+ first?(first = false):(out << ", ");
+ out << item.first << ": ";
+ boost::apply_visitor(*this, item.second);
+ }
+ out << "}";
+ return out.str();
+}
diff --git a/src/visitor/to_json.h b/src/visitor/to_json.h
new file mode 100644
index 0000000..90562a4
--- /dev/null
+++ b/src/visitor/to_json.h
@@ -0,0 +1,24 @@
+#ifndef _MSTCH_TO_JSON_H_
+#define _MSTCH_TO_JSON_H_
+
+#include
+#include
+#include
+
+#include "types.h"
+
+namespace mstch {
+ namespace visitor {
+ class to_json: public boost::static_visitor {
+ public:
+ std::string operator()(const boost::blank& blank) const;
+ std::string operator()(const int& i) const;
+ std::string operator()(const bool& b) const;
+ std::string operator()(const std::string& str) const;
+ std::string operator()(const array& arr) const;
+ std::string operator()(const object& obj) const;
+ };
+ }
+}
+
+#endif //_MSTCH_TO_JSON_H_
diff --git a/test/data/ampersand_escape.h b/test/data/ampersand_escape.h
new file mode 100644
index 0000000..1d7414f
--- /dev/null
+++ b/test/data/ampersand_escape.h
@@ -0,0 +1,3 @@
+auto data = mstch::object{
+ {"message", std::string{"Some "}}
+};
diff --git a/test/data/ampersand_escape.mustache b/test/data/ampersand_escape.mustache
new file mode 100644
index 0000000..6501a48
--- /dev/null
+++ b/test/data/ampersand_escape.mustache
@@ -0,0 +1 @@
+{{&message}}
diff --git a/test/data/ampersand_escape.txt b/test/data/ampersand_escape.txt
new file mode 100644
index 0000000..2ed3fd3
--- /dev/null
+++ b/test/data/ampersand_escape.txt
@@ -0,0 +1 @@
+Some
diff --git a/test/data/apostrophe.h b/test/data/apostrophe.h
new file mode 100644
index 0000000..14417be
--- /dev/null
+++ b/test/data/apostrophe.h
@@ -0,0 +1,4 @@
+auto data = mstch::object{
+ {"apos", std::string{"'"}},
+ {"control", std::string{"X"}}
+};
diff --git a/test/data/apostrophe.mustache b/test/data/apostrophe.mustache
new file mode 100644
index 0000000..e8687aa
--- /dev/null
+++ b/test/data/apostrophe.mustache
@@ -0,0 +1 @@
+{{apos}}{{control}}
diff --git a/test/data/apostrophe.txt b/test/data/apostrophe.txt
new file mode 100644
index 0000000..4427c30
--- /dev/null
+++ b/test/data/apostrophe.txt
@@ -0,0 +1 @@
+'X
diff --git a/test/data/array_of_strings.h b/test/data/array_of_strings.h
new file mode 100644
index 0000000..0882fc0
--- /dev/null
+++ b/test/data/array_of_strings.h
@@ -0,0 +1,3 @@
+auto data = mstch::object{
+ {"array_of_strings", mstch::array{std::string{"hello"}, std::string{"world"}}}
+};
diff --git a/test/data/array_of_strings.mustache b/test/data/array_of_strings.mustache
new file mode 100644
index 0000000..4d65738
--- /dev/null
+++ b/test/data/array_of_strings.mustache
@@ -0,0 +1 @@
+{{#array_of_strings}}{{.}} {{/array_of_strings}}
diff --git a/test/data/array_of_strings.txt b/test/data/array_of_strings.txt
new file mode 100644
index 0000000..4a1f475
--- /dev/null
+++ b/test/data/array_of_strings.txt
@@ -0,0 +1 @@
+hello world
diff --git a/test/data/backslashes.h b/test/data/backslashes.h
new file mode 100644
index 0000000..1ec1514
--- /dev/null
+++ b/test/data/backslashes.h
@@ -0,0 +1,3 @@
+auto data = mstch::object{
+ {"value", std::string{"\\abc"}}
+};
diff --git a/test/data/backslashes.mustache b/test/data/backslashes.mustache
new file mode 100644
index 0000000..fe7745b
--- /dev/null
+++ b/test/data/backslashes.mustache
@@ -0,0 +1,7 @@
+* {{value}}
+* {{{value}}}
+* {{&value}}
+
diff --git a/test/data/backslashes.txt b/test/data/backslashes.txt
new file mode 100644
index 0000000..038dd37
--- /dev/null
+++ b/test/data/backslashes.txt
@@ -0,0 +1,7 @@
+* \abc
+* \abc
+* \abc
+
diff --git a/test/data/bug_11_eating_whitespace.h b/test/data/bug_11_eating_whitespace.h
new file mode 100644
index 0000000..dcec017
--- /dev/null
+++ b/test/data/bug_11_eating_whitespace.h
@@ -0,0 +1,3 @@
+auto data = mstch::object{
+ {"tag", std::string{"yo"}}
+};
diff --git a/test/data/bug_11_eating_whitespace.mustache b/test/data/bug_11_eating_whitespace.mustache
new file mode 100644
index 0000000..8d5cd92
--- /dev/null
+++ b/test/data/bug_11_eating_whitespace.mustache
@@ -0,0 +1 @@
+{{tag}} foo
diff --git a/test/data/bug_11_eating_whitespace.txt b/test/data/bug_11_eating_whitespace.txt
new file mode 100644
index 0000000..f5bbc85
--- /dev/null
+++ b/test/data/bug_11_eating_whitespace.txt
@@ -0,0 +1 @@
+yo foo
diff --git a/test/data/bug_length_property.h b/test/data/bug_length_property.h
new file mode 100644
index 0000000..224221c
--- /dev/null
+++ b/test/data/bug_length_property.h
@@ -0,0 +1,3 @@
+auto data = mstch::object{
+ {"length", std::string{"hello"}}
+};
diff --git a/test/data/bug_length_property.mustache b/test/data/bug_length_property.mustache
new file mode 100644
index 0000000..b000887
--- /dev/null
+++ b/test/data/bug_length_property.mustache
@@ -0,0 +1 @@
+{{#length}}The length variable is: {{length}}{{/length}}
diff --git a/test/data/bug_length_property.txt b/test/data/bug_length_property.txt
new file mode 100644
index 0000000..f5355d3
--- /dev/null
+++ b/test/data/bug_length_property.txt
@@ -0,0 +1 @@
+The length variable is: hello
diff --git a/test/data/context_lookup.h b/test/data/context_lookup.h
new file mode 100644
index 0000000..9a52db0
--- /dev/null
+++ b/test/data/context_lookup.h
@@ -0,0 +1,8 @@
+auto data = mstch::object{
+ {"outer", mstch::object{
+ {"id", 1},
+ {"second", mstch::object{
+ {"nothing", 2}
+ }}
+ }}
+};
diff --git a/test/data/context_lookup.mustache b/test/data/context_lookup.mustache
new file mode 100644
index 0000000..3c7b767
--- /dev/null
+++ b/test/data/context_lookup.mustache
@@ -0,0 +1 @@
+{{#outer}}{{#second}}{{id}}{{/second}}{{/outer}}
diff --git a/test/data/context_lookup.txt b/test/data/context_lookup.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/test/data/context_lookup.txt
@@ -0,0 +1 @@
+1
diff --git a/test/data/delimiter/changing_delimiters.js b/test/data/delimiter/changing_delimiters.js
new file mode 100644
index 0000000..b808f4c
--- /dev/null
+++ b/test/data/delimiter/changing_delimiters.js
@@ -0,0 +1,4 @@
+({
+ "foo": "foooooooooooooo",
+ "bar": "bar! "
+})
diff --git a/test/data/delimiter/changing_delimiters.mustache b/test/data/delimiter/changing_delimiters.mustache
new file mode 100644
index 0000000..0cd044c
--- /dev/null
+++ b/test/data/delimiter/changing_delimiters.mustache
@@ -0,0 +1 @@
+{{=<% %>=}}<% foo %> {{foo}} <%{bar}%> {{{bar}}}
diff --git a/test/data/delimiter/changing_delimiters.txt b/test/data/delimiter/changing_delimiters.txt
new file mode 100644
index 0000000..1b1510d
--- /dev/null
+++ b/test/data/delimiter/changing_delimiters.txt
@@ -0,0 +1 @@
+foooooooooooooo {{foo}} bar! {{{bar}}}
diff --git a/test/data/delimiter/delimiters.js b/test/data/delimiter/delimiters.js
new file mode 100644
index 0000000..365d01e
--- /dev/null
+++ b/test/data/delimiter/delimiters.js
@@ -0,0 +1,6 @@
+({
+ first: "It worked the first time.",
+ second: "And it worked the second time.",
+ third: "Then, surprisingly, it worked the third time.",
+ fourth: "Fourth time also fine!."
+})
diff --git a/test/data/delimiter/delimiters.mustache b/test/data/delimiter/delimiters.mustache
new file mode 100644
index 0000000..7fac846
--- /dev/null
+++ b/test/data/delimiter/delimiters.mustache
@@ -0,0 +1,7 @@
+{{=<% %>=}}*
+<% first %>
+* <% second %>
+<%=| |=%>
+* | third |
+|={{ }}=|
+* {{ fourth }}
diff --git a/test/data/delimiter/delimiters.txt b/test/data/delimiter/delimiters.txt
new file mode 100644
index 0000000..698a6bb
--- /dev/null
+++ b/test/data/delimiter/delimiters.txt
@@ -0,0 +1,5 @@
+*
+It worked the first time.
+* And it worked the second time.
+* Then, surprisingly, it worked the third time.
+* Fourth time also fine!.
diff --git a/test/data/disappearing_whitespace.h b/test/data/disappearing_whitespace.h
new file mode 100644
index 0000000..923c971
--- /dev/null
+++ b/test/data/disappearing_whitespace.h
@@ -0,0 +1,4 @@
+auto data = mstch::object{
+ {"bedrooms", true},
+ {"total", 1}
+};
diff --git a/test/data/disappearing_whitespace.mustache b/test/data/disappearing_whitespace.mustache
new file mode 100644
index 0000000..16c16e0
--- /dev/null
+++ b/test/data/disappearing_whitespace.mustache
@@ -0,0 +1 @@
+{{#bedrooms}}{{total}}{{/bedrooms}} BED
diff --git a/test/data/disappearing_whitespace.txt b/test/data/disappearing_whitespace.txt
new file mode 100644
index 0000000..66e98ef
--- /dev/null
+++ b/test/data/disappearing_whitespace.txt
@@ -0,0 +1 @@
+1 BED
diff --git a/test/data/double_render.h b/test/data/double_render.h
new file mode 100644
index 0000000..2b64ee6
--- /dev/null
+++ b/test/data/double_render.h
@@ -0,0 +1,5 @@
+auto data = mstch::object{
+ {"foo", true},
+ {"bar", std::string{"{{win}}"}},
+ {"win", std::string{"FAIL"}}
+};
diff --git a/test/data/double_render.mustache b/test/data/double_render.mustache
new file mode 100644
index 0000000..4500fd7
--- /dev/null
+++ b/test/data/double_render.mustache
@@ -0,0 +1 @@
+{{#foo}}{{bar}}{{/foo}}
diff --git a/test/data/double_render.txt b/test/data/double_render.txt
new file mode 100644
index 0000000..b6e652d
--- /dev/null
+++ b/test/data/double_render.txt
@@ -0,0 +1 @@
+{{win}}
diff --git a/test/data/empty_list.h b/test/data/empty_list.h
new file mode 100644
index 0000000..a847639
--- /dev/null
+++ b/test/data/empty_list.h
@@ -0,0 +1,3 @@
+auto data = mstch::object{
+ {"jobs", mstch::array{}}
+};
diff --git a/test/data/empty_list.mustache b/test/data/empty_list.mustache
new file mode 100644
index 0000000..4fdf13d
--- /dev/null
+++ b/test/data/empty_list.mustache
@@ -0,0 +1,4 @@
+These are the jobs:
+{{#jobs}}
+{{.}}
+{{/jobs}}
diff --git a/test/data/empty_list.txt b/test/data/empty_list.txt
new file mode 100644
index 0000000..d9b4a67
--- /dev/null
+++ b/test/data/empty_list.txt
@@ -0,0 +1 @@
+These are the jobs:
diff --git a/test/data/empty_sections.h b/test/data/empty_sections.h
new file mode 100644
index 0000000..f040324
--- /dev/null
+++ b/test/data/empty_sections.h
@@ -0,0 +1 @@
+auto data = mstch::object{};
diff --git a/test/data/empty_sections.mustache b/test/data/empty_sections.mustache
new file mode 100644
index 0000000..b6065db
--- /dev/null
+++ b/test/data/empty_sections.mustache
@@ -0,0 +1 @@
+{{#foo}}{{/foo}}foo{{#bar}}{{/bar}}
diff --git a/test/data/empty_sections.txt b/test/data/empty_sections.txt
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/test/data/empty_sections.txt
@@ -0,0 +1 @@
+foo
diff --git a/test/data/empty_string.h b/test/data/empty_string.h
new file mode 100644
index 0000000..e9e9d49
--- /dev/null
+++ b/test/data/empty_string.h
@@ -0,0 +1,6 @@
+auto data = mstch::object{
+ {"description", std::string{"That is all!"}},
+ {"child", mstch::object{
+ {"description", std::string{""}}
+ }}
+};
diff --git a/test/data/empty_string.mustache b/test/data/empty_string.mustache
new file mode 100644
index 0000000..f568441
--- /dev/null
+++ b/test/data/empty_string.mustache
@@ -0,0 +1 @@
+{{description}}{{#child}}{{description}}{{/child}}
diff --git a/test/data/empty_string.txt b/test/data/empty_string.txt
new file mode 100644
index 0000000..22e2a6e
--- /dev/null
+++ b/test/data/empty_string.txt
@@ -0,0 +1 @@
+That is all!
diff --git a/test/data/empty_template.h b/test/data/empty_template.h
new file mode 100644
index 0000000..f040324
--- /dev/null
+++ b/test/data/empty_template.h
@@ -0,0 +1 @@
+auto data = mstch::object{};
diff --git a/test/data/empty_template.mustache b/test/data/empty_template.mustache
new file mode 100644
index 0000000..bb2367a
--- /dev/null
+++ b/test/data/empty_template.mustache
@@ -0,0 +1 @@
+Test
\ No newline at end of file
diff --git a/test/data/empty_template.txt b/test/data/empty_template.txt
new file mode 100644
index 0000000..bb2367a
--- /dev/null
+++ b/test/data/empty_template.txt
@@ -0,0 +1 @@
+Test
\ No newline at end of file
diff --git a/test/data/error_not_found.h b/test/data/error_not_found.h
new file mode 100644
index 0000000..f30ac4e
--- /dev/null
+++ b/test/data/error_not_found.h
@@ -0,0 +1,3 @@
+auto data = mstch::object{
+ {"bar", 2}
+};
diff --git a/test/data/error_not_found.mustache b/test/data/error_not_found.mustache
new file mode 100644
index 0000000..24369f7
--- /dev/null
+++ b/test/data/error_not_found.mustache
@@ -0,0 +1 @@
+{{foo}}
\ No newline at end of file
diff --git a/test/data/error_not_found.txt b/test/data/error_not_found.txt
new file mode 100644
index 0000000..e69de29
diff --git a/test/data/falsy.h b/test/data/falsy.h
new file mode 100644
index 0000000..163c58b
--- /dev/null
+++ b/test/data/falsy.h
@@ -0,0 +1,6 @@
+auto data = mstch::object{
+ {"emptyString", std::string{""}},
+ {"emptyArray", mstch::array{}},
+ {"zero", 0},
+ {"null", mstch::node{}}
+};
diff --git a/test/data/falsy.mustache b/test/data/falsy.mustache
new file mode 100644
index 0000000..4d992fd
--- /dev/null
+++ b/test/data/falsy.mustache
@@ -0,0 +1,8 @@
+{{#emptyString}}empty string{{/emptyString}}
+{{^emptyString}}inverted empty string{{/emptyString}}
+{{#emptyArray}}empty array{{/emptyArray}}
+{{^emptyArray}}inverted empty array{{/emptyArray}}
+{{#zero}}zero{{/zero}}
+{{^zero}}inverted zero{{/zero}}
+{{#null}}null{{/null}}
+{{^null}}inverted null{{/null}}
diff --git a/test/data/falsy.txt b/test/data/falsy.txt
new file mode 100644
index 0000000..fde133c
--- /dev/null
+++ b/test/data/falsy.txt
@@ -0,0 +1,8 @@
+
+inverted empty string
+
+inverted empty array
+
+inverted zero
+
+inverted null
diff --git a/test/data/falsy_array.h b/test/data/falsy_array.h
new file mode 100644
index 0000000..d7b44c4
--- /dev/null
+++ b/test/data/falsy_array.h
@@ -0,0 +1,8 @@
+auto data = mstch::object{
+ {"list", mstch::array{
+ mstch::array{std::string{""}, std::string{"emptyString"}},
+ mstch::array{mstch::array{}, std::string{"emptyArray"}},
+ mstch::array{0, std::string{"zero"}},
+ mstch::array{mstch::node{}, std::string{"null"}}
+ }}
+};
diff --git a/test/data/falsy_array.mustache b/test/data/falsy_array.mustache
new file mode 100644
index 0000000..2be7b37
--- /dev/null
+++ b/test/data/falsy_array.mustache
@@ -0,0 +1,3 @@
+{{#list}}
+{{#.}}{{#.}}{{.}}{{/.}}{{^.}}inverted {{/.}}{{/.}}
+{{/list}}
\ No newline at end of file
diff --git a/test/data/falsy_array.txt b/test/data/falsy_array.txt
new file mode 100644
index 0000000..a001172
--- /dev/null
+++ b/test/data/falsy_array.txt
@@ -0,0 +1,4 @@
+inverted emptyString
+inverted emptyArray
+inverted zero
+inverted null
diff --git a/test/data/grandparent_context.h b/test/data/grandparent_context.h
new file mode 100644
index 0000000..01015ed
--- /dev/null
+++ b/test/data/grandparent_context.h
@@ -0,0 +1,19 @@
+auto data = mstch::object{
+ {"grand_parent_id", std::string{"grand_parent1"}},
+ {"parent_contexts", mstch::array{
+ mstch::object{
+ {"parent_id", std::string{"parent1"}},
+ {"child_contexts", mstch::array{
+ mstch::object{{"child_id", std::string{"parent1-child1"}}},
+ mstch::object{{"child_id", std::string{"parent1-child2"}}}
+ }}
+ },
+ mstch::object{
+ {"parent_id", std::string{"parent2"}},
+ {"child_contexts", mstch::array{
+ mstch::object{{"child_id", std::string{"parent2-child1"}}},
+ mstch::object{{"child_id", std::string{"parent2-child2"}}}
+ }}
+ }
+ }}
+};
diff --git a/test/data/grandparent_context.mustache b/test/data/grandparent_context.mustache
new file mode 100644
index 0000000..e6c07a2
--- /dev/null
+++ b/test/data/grandparent_context.mustache
@@ -0,0 +1,10 @@
+{{grand_parent_id}}
+{{#parent_contexts}}
+{{grand_parent_id}}
+{{parent_id}}
+{{#child_contexts}}
+{{grand_parent_id}}
+{{parent_id}}
+{{child_id}}
+{{/child_contexts}}
+{{/parent_contexts}}
diff --git a/test/data/grandparent_context.txt b/test/data/grandparent_context.txt
new file mode 100644
index 0000000..64996ad
--- /dev/null
+++ b/test/data/grandparent_context.txt
@@ -0,0 +1,17 @@
+grand_parent1
+grand_parent1
+parent1
+grand_parent1
+parent1
+parent1-child1
+grand_parent1
+parent1
+parent1-child2
+grand_parent1
+parent2
+grand_parent1
+parent2
+parent2-child1
+grand_parent1
+parent2
+parent2-child2
diff --git a/test/data/implicit_iterator.h b/test/data/implicit_iterator.h
new file mode 100644
index 0000000..8765cd2
--- /dev/null
+++ b/test/data/implicit_iterator.h
@@ -0,0 +1,8 @@
+auto data = mstch::object{
+ {"data", mstch::object{
+ {"author", mstch::object{
+ {"twitter_id", 819606},
+ {"name", std::string{"janl"}}
+ }}
+ }}
+};
diff --git a/test/data/implicit_iterator.mustache b/test/data/implicit_iterator.mustache
new file mode 100644
index 0000000..ae31f34
--- /dev/null
+++ b/test/data/implicit_iterator.mustache
@@ -0,0 +1,7 @@
+{{# data.author.twitter_id }}
+
+{{/ data.author.twitter_id }}
+
+{{# data.author.name }}
+
+{{/ data.author.name }}
diff --git a/test/data/implicit_iterator.txt b/test/data/implicit_iterator.txt
new file mode 100644
index 0000000..0fccefd
--- /dev/null
+++ b/test/data/implicit_iterator.txt
@@ -0,0 +1,3 @@
+
+
+
diff --git a/test/data/included_tag.h b/test/data/included_tag.h
new file mode 100644
index 0000000..1885ed6
--- /dev/null
+++ b/test/data/included_tag.h
@@ -0,0 +1,3 @@
+auto data = mstch::object{
+ {"html", std::string{"I like {{mustache}}"}}
+};
diff --git a/test/data/included_tag.mustache b/test/data/included_tag.mustache
new file mode 100644
index 0000000..70631c2
--- /dev/null
+++ b/test/data/included_tag.mustache
@@ -0,0 +1 @@
+You said "{{{html}}}" today
diff --git a/test/data/included_tag.txt b/test/data/included_tag.txt
new file mode 100644
index 0000000..1af4556
--- /dev/null
+++ b/test/data/included_tag.txt
@@ -0,0 +1 @@
+You said "I like {{mustache}}" today
diff --git a/test/data/inverted_section.h b/test/data/inverted_section.h
new file mode 100644
index 0000000..c2e4d6a
--- /dev/null
+++ b/test/data/inverted_section.h
@@ -0,0 +1,3 @@
+auto data = mstch::object{
+ {"repos", mstch::array{}}
+};
diff --git a/test/data/inverted_section.mustache b/test/data/inverted_section.mustache
new file mode 100644
index 0000000..b0a183b
--- /dev/null
+++ b/test/data/inverted_section.mustache
@@ -0,0 +1,3 @@
+{{#repos}}{{name}} {{/repos}}
+{{^repos}}No repos :({{/repos}}
+{{^nothin}}Hello!{{/nothin}}
diff --git a/test/data/inverted_section.txt b/test/data/inverted_section.txt
new file mode 100644
index 0000000..b421582
--- /dev/null
+++ b/test/data/inverted_section.txt
@@ -0,0 +1,3 @@
+
+No repos :(
+Hello!
diff --git a/test/data/keys_with_questionmarks.h b/test/data/keys_with_questionmarks.h
new file mode 100644
index 0000000..c99c2ac
--- /dev/null
+++ b/test/data/keys_with_questionmarks.h
@@ -0,0 +1,5 @@
+auto data = mstch::object{
+ {"person?", mstch::object{
+ {"name", std::string{"Jon"}}
+ }}
+};
diff --git a/test/data/keys_with_questionmarks.mustache b/test/data/keys_with_questionmarks.mustache
new file mode 100644
index 0000000..417f17f
--- /dev/null
+++ b/test/data/keys_with_questionmarks.mustache
@@ -0,0 +1,3 @@
+{{#person?}}
+ Hi {{name}}!
+{{/person?}}
diff --git a/test/data/keys_with_questionmarks.txt b/test/data/keys_with_questionmarks.txt
new file mode 100644
index 0000000..0f69b94
--- /dev/null
+++ b/test/data/keys_with_questionmarks.txt
@@ -0,0 +1 @@
+ Hi Jon!
diff --git a/test/data/lambda/check_falsy.js b/test/data/lambda/check_falsy.js
new file mode 100644
index 0000000..5a599ca
--- /dev/null
+++ b/test/data/lambda/check_falsy.js
@@ -0,0 +1,7 @@
+({
+ number: function(text, render) {
+ return function(text, render) {
+ return +render(text);
+ }
+ }
+})
diff --git a/test/data/lambda/check_falsy.mustache b/test/data/lambda/check_falsy.mustache
new file mode 100644
index 0000000..30e2547
--- /dev/null
+++ b/test/data/lambda/check_falsy.mustache
@@ -0,0 +1 @@
+{{#number}}0{{/number}}
diff --git a/test/data/lambda/check_falsy.txt b/test/data/lambda/check_falsy.txt
new file mode 100644
index 0000000..3bb2f51
--- /dev/null
+++ b/test/data/lambda/check_falsy.txt
@@ -0,0 +1 @@
+0
diff --git a/test/data/lambda/comments.js b/test/data/lambda/comments.js
new file mode 100644
index 0000000..f20b8b1
--- /dev/null
+++ b/test/data/lambda/comments.js
@@ -0,0 +1,5 @@
+({
+ title: function () {
+ return "A Comedy of Errors";
+ }
+})
diff --git a/test/data/lambda/comments.mustache b/test/data/lambda/comments.mustache
new file mode 100644
index 0000000..5036801
--- /dev/null
+++ b/test/data/lambda/comments.mustache
@@ -0,0 +1 @@
+{{title}}{{! just something interesting... or not... }}
diff --git a/test/data/lambda/comments.txt b/test/data/lambda/comments.txt
new file mode 100644
index 0000000..0133517
--- /dev/null
+++ b/test/data/lambda/comments.txt
@@ -0,0 +1 @@
+A Comedy of Errors
diff --git a/test/data/lambda/complex.js b/test/data/lambda/complex.js
new file mode 100644
index 0000000..68a4809
--- /dev/null
+++ b/test/data/lambda/complex.js
@@ -0,0 +1,19 @@
+({
+ header: function () {
+ return "Colors";
+ },
+ item: [
+ {name: "red", current: true, url: "#Red"},
+ {name: "green", current: false, url: "#Green"},
+ {name: "blue", current: false, url: "#Blue"}
+ ],
+ link: function () {
+ return this["current"] !== true;
+ },
+ list: function () {
+ return this.item.length !== 0;
+ },
+ empty: function () {
+ return this.item.length === 0;
+ }
+})
diff --git a/test/data/lambda/complex.mustache b/test/data/lambda/complex.mustache
new file mode 100644
index 0000000..869a4f0
--- /dev/null
+++ b/test/data/lambda/complex.mustache
@@ -0,0 +1,16 @@
+{{header}}
+{{#list}}
+
+ {{#item}}
+ {{#current}}
+ {{name}}
+ {{/current}}
+ {{#link}}
+ {{name}}
+ {{/link}}
+ {{/item}}
+
+{{/list}}
+{{#empty}}
+ The list is empty.
+{{/empty}}
diff --git a/test/data/lambda/complex.txt b/test/data/lambda/complex.txt
new file mode 100644
index 0000000..596d3f6
--- /dev/null
+++ b/test/data/lambda/complex.txt
@@ -0,0 +1,6 @@
+Colors
+
diff --git a/test/data/lambda/dot_notation.js b/test/data/lambda/dot_notation.js
new file mode 100644
index 0000000..de06a03
--- /dev/null
+++ b/test/data/lambda/dot_notation.js
@@ -0,0 +1,23 @@
+({
+ name: "A Book",
+ authors: ["John Power", "Jamie Walsh"],
+ price: {
+ value: 200,
+ vat: function () {
+ return this.value * 0.2;
+ },
+ currency: {
+ symbol: '$',
+ name: 'USD'
+ }
+ },
+ availability: {
+ status: true,
+ text: "In Stock"
+ },
+ // And now, some truthy false values
+ truthy: {
+ zero: 0,
+ notTrue: false
+ }
+})
diff --git a/test/data/lambda/dot_notation.mustache b/test/data/lambda/dot_notation.mustache
new file mode 100644
index 0000000..f89d70b
--- /dev/null
+++ b/test/data/lambda/dot_notation.mustache
@@ -0,0 +1,9 @@
+
+{{name}}
+Authors:
{{#authors}}{{.}} {{/authors}}
+Price: {{{price.currency.symbol}}}{{price.value}} {{#price.currency}}{{name}} {{availability.text}} {{/price.currency}}
+VAT: {{{price.currency.symbol}}}{{#price}}{{vat}}{{/price}}
+
+Test truthy false values:
+Zero: {{truthy.zero}}
+False: {{truthy.notTrue}}
diff --git a/test/data/lambda/dot_notation.txt b/test/data/lambda/dot_notation.txt
new file mode 100644
index 0000000..08afa05
--- /dev/null
+++ b/test/data/lambda/dot_notation.txt
@@ -0,0 +1,9 @@
+
+A Book
+Authors:
+Price: $200 USD In Stock
+VAT: $40
+
+Test truthy false values:
+Zero: 0
+False: false
diff --git a/test/data/lambda/escaped.js b/test/data/lambda/escaped.js
new file mode 100644
index 0000000..cd77c1f
--- /dev/null
+++ b/test/data/lambda/escaped.js
@@ -0,0 +1,6 @@
+({
+ title: function () {
+ return "Bear > Shark";
+ },
+ entities: "" \"'<>/"
+})
diff --git a/test/data/lambda/escaped.mustache b/test/data/lambda/escaped.mustache
new file mode 100644
index 0000000..93e800b
--- /dev/null
+++ b/test/data/lambda/escaped.mustache
@@ -0,0 +1,2 @@
+{{title}}
+And even {{entities}}, but not {{{entities}}}.
diff --git a/test/data/lambda/escaped.txt b/test/data/lambda/escaped.txt
new file mode 100644
index 0000000..c1527d5
--- /dev/null
+++ b/test/data/lambda/escaped.txt
@@ -0,0 +1,2 @@
+Bear > Shark
+And even " "'<>/, but not " "'<>/.
diff --git a/test/data/lambda/higher_order_sections.js b/test/data/lambda/higher_order_sections.js
new file mode 100644
index 0000000..bacb0a4
--- /dev/null
+++ b/test/data/lambda/higher_order_sections.js
@@ -0,0 +1,9 @@
+({
+ name: "Tater",
+ helper: "To tinker?",
+ bolder: function () {
+ return function (text, render) {
+ return text + ' => ' + render(text) + ' ' + this.helper;
+ }
+ }
+})
diff --git a/test/data/lambda/higher_order_sections.mustache b/test/data/lambda/higher_order_sections.mustache
new file mode 100644
index 0000000..04f5318
--- /dev/null
+++ b/test/data/lambda/higher_order_sections.mustache
@@ -0,0 +1 @@
+{{#bolder}}Hi {{name}}.{{/bolder}}
diff --git a/test/data/lambda/higher_order_sections.txt b/test/data/lambda/higher_order_sections.txt
new file mode 100644
index 0000000..be50ad7
--- /dev/null
+++ b/test/data/lambda/higher_order_sections.txt
@@ -0,0 +1 @@
+Hi {{name}}. => Hi Tater. To tinker?
diff --git a/test/data/lambda/nested_higher_order_sections.js b/test/data/lambda/nested_higher_order_sections.js
new file mode 100644
index 0000000..3ccf4d3
--- /dev/null
+++ b/test/data/lambda/nested_higher_order_sections.js
@@ -0,0 +1,8 @@
+({
+ bold: function () {
+ return function (text, render) {
+ return '' + render(text) + ' ';
+ };
+ },
+ person: { name: 'Jonas' }
+});
diff --git a/test/data/lambda/nested_higher_order_sections.mustache b/test/data/lambda/nested_higher_order_sections.mustache
new file mode 100644
index 0000000..e312fe7
--- /dev/null
+++ b/test/data/lambda/nested_higher_order_sections.mustache
@@ -0,0 +1 @@
+{{#bold}}{{#person}}My name is {{name}}!{{/person}}{{/bold}}
diff --git a/test/data/lambda/nested_higher_order_sections.txt b/test/data/lambda/nested_higher_order_sections.txt
new file mode 100644
index 0000000..0ee6a40
--- /dev/null
+++ b/test/data/lambda/nested_higher_order_sections.txt
@@ -0,0 +1 @@
+My name is Jonas!
diff --git a/test/data/lambda/null_string.js b/test/data/lambda/null_string.js
new file mode 100644
index 0000000..984ee51
--- /dev/null
+++ b/test/data/lambda/null_string.js
@@ -0,0 +1,10 @@
+({
+ name: "Elise",
+ glytch: true,
+ binary: false,
+ value: null,
+ undef: undefined,
+ numeric: function() {
+ return NaN;
+ }
+})
diff --git a/test/data/lambda/null_string.mustache b/test/data/lambda/null_string.mustache
new file mode 100644
index 0000000..a6f3300
--- /dev/null
+++ b/test/data/lambda/null_string.mustache
@@ -0,0 +1,6 @@
+Hello {{name}}
+glytch {{glytch}}
+binary {{binary}}
+value {{value}}
+undef {{undef}}
+numeric {{numeric}}
diff --git a/test/data/lambda/null_string.txt b/test/data/lambda/null_string.txt
new file mode 100644
index 0000000..bcabe0a
--- /dev/null
+++ b/test/data/lambda/null_string.txt
@@ -0,0 +1,6 @@
+Hello Elise
+glytch true
+binary false
+value
+undef
+numeric NaN
diff --git a/test/data/lambda/simple.js b/test/data/lambda/simple.js
new file mode 100644
index 0000000..1d8d6f4
--- /dev/null
+++ b/test/data/lambda/simple.js
@@ -0,0 +1,8 @@
+({
+ name: "Chris",
+ value: 10000,
+ taxed_value: function () {
+ return this.value - (this.value * 0.4);
+ },
+ in_ca: true
+})
diff --git a/test/data/lambda/simple.mustache b/test/data/lambda/simple.mustache
new file mode 100644
index 0000000..2fea632
--- /dev/null
+++ b/test/data/lambda/simple.mustache
@@ -0,0 +1,5 @@
+Hello {{name}}
+You have just won ${{value}}!
+{{#in_ca}}
+Well, ${{ taxed_value }}, after taxes.
+{{/in_ca}}
diff --git a/test/data/lambda/simple.txt b/test/data/lambda/simple.txt
new file mode 100644
index 0000000..5d75d65
--- /dev/null
+++ b/test/data/lambda/simple.txt
@@ -0,0 +1,3 @@
+Hello Chris
+You have just won $10000!
+Well, $6000, after taxes.
diff --git a/test/data/lambda/unescaped.js b/test/data/lambda/unescaped.js
new file mode 100644
index 0000000..b6d064f
--- /dev/null
+++ b/test/data/lambda/unescaped.js
@@ -0,0 +1,5 @@
+({
+ title: function () {
+ return "Bear > Shark";
+ }
+})
diff --git a/test/data/lambda/unescaped.mustache b/test/data/lambda/unescaped.mustache
new file mode 100644
index 0000000..6b07d7b
--- /dev/null
+++ b/test/data/lambda/unescaped.mustache
@@ -0,0 +1 @@
+{{{title}}}
diff --git a/test/data/lambda/unescaped.txt b/test/data/lambda/unescaped.txt
new file mode 100644
index 0000000..089ad79
--- /dev/null
+++ b/test/data/lambda/unescaped.txt
@@ -0,0 +1 @@
+Bear > Shark
diff --git a/test/data/multiline_comment.h b/test/data/multiline_comment.h
new file mode 100644
index 0000000..f040324
--- /dev/null
+++ b/test/data/multiline_comment.h
@@ -0,0 +1 @@
+auto data = mstch::object{};
diff --git a/test/data/multiline_comment.mustache b/test/data/multiline_comment.mustache
new file mode 100644
index 0000000..dff0893
--- /dev/null
+++ b/test/data/multiline_comment.mustache
@@ -0,0 +1,6 @@
+{{!
+
+This is a multi-line comment.
+
+}}
+Hello world!
diff --git a/test/data/multiline_comment.txt b/test/data/multiline_comment.txt
new file mode 100644
index 0000000..cd08755
--- /dev/null
+++ b/test/data/multiline_comment.txt
@@ -0,0 +1 @@
+Hello world!
diff --git a/test/data/nested_dot.h b/test/data/nested_dot.h
new file mode 100644
index 0000000..3d0ded5
--- /dev/null
+++ b/test/data/nested_dot.h
@@ -0,0 +1 @@
+auto data = mstch::object{{"name", std::string{"Bruno"}}};
diff --git a/test/data/nested_dot.mustache b/test/data/nested_dot.mustache
new file mode 100644
index 0000000..12b0728
--- /dev/null
+++ b/test/data/nested_dot.mustache
@@ -0,0 +1 @@
+{{#name}}Hello {{.}}{{/name}}
\ No newline at end of file
diff --git a/test/data/nested_dot.txt b/test/data/nested_dot.txt
new file mode 100644
index 0000000..58df047
--- /dev/null
+++ b/test/data/nested_dot.txt
@@ -0,0 +1 @@
+Hello Bruno
\ No newline at end of file
diff --git a/test/data/nested_iterating.h b/test/data/nested_iterating.h
new file mode 100644
index 0000000..d488f5d
--- /dev/null
+++ b/test/data/nested_iterating.h
@@ -0,0 +1,8 @@
+auto data = mstch::object{
+ {"inner", mstch::array{mstch::object{
+ {"foo", std::string{"foo"}},
+ {"inner", mstch::array{mstch::object{
+ {"bar", std::string{"bar"}}
+ }}}
+ }}}
+};
diff --git a/test/data/nested_iterating.mustache b/test/data/nested_iterating.mustache
new file mode 100644
index 0000000..1a3bb1a
--- /dev/null
+++ b/test/data/nested_iterating.mustache
@@ -0,0 +1 @@
+{{#inner}}{{foo}}{{#inner}}{{bar}}{{/inner}}{{/inner}}
diff --git a/test/data/nested_iterating.txt b/test/data/nested_iterating.txt
new file mode 100644
index 0000000..323fae0
--- /dev/null
+++ b/test/data/nested_iterating.txt
@@ -0,0 +1 @@
+foobar
diff --git a/test/data/nesting.h b/test/data/nesting.h
new file mode 100644
index 0000000..1a1e9d1
--- /dev/null
+++ b/test/data/nesting.h
@@ -0,0 +1,7 @@
+auto data = mstch::object{
+ {"foo", mstch::array{
+ mstch::object{{"a", mstch::object{{"b", 1}}}},
+ mstch::object{{"a", mstch::object{{"b", 2}}}},
+ mstch::object{{"a", mstch::object{{"b", 3}}}}
+ }}
+};
diff --git a/test/data/nesting.mustache b/test/data/nesting.mustache
new file mode 100644
index 0000000..551366d
--- /dev/null
+++ b/test/data/nesting.mustache
@@ -0,0 +1,5 @@
+{{#foo}}
+ {{#a}}
+ {{b}}
+ {{/a}}
+{{/foo}}
diff --git a/test/data/nesting.txt b/test/data/nesting.txt
new file mode 100644
index 0000000..7db34b1
--- /dev/null
+++ b/test/data/nesting.txt
@@ -0,0 +1,3 @@
+ 1
+ 2
+ 3
diff --git a/test/data/nesting_same_name.h b/test/data/nesting_same_name.h
new file mode 100644
index 0000000..5a71a12
--- /dev/null
+++ b/test/data/nesting_same_name.h
@@ -0,0 +1,8 @@
+auto data = mstch::object{
+ {"items", mstch::array{
+ mstch::object{
+ {"name", std::string{"name"}},
+ {"items", mstch::array{1, 2, 3, 4}}
+ }
+ }}
+};
diff --git a/test/data/nesting_same_name.mustache b/test/data/nesting_same_name.mustache
new file mode 100644
index 0000000..777dbd6
--- /dev/null
+++ b/test/data/nesting_same_name.mustache
@@ -0,0 +1 @@
+{{#items}}{{name}}{{#items}}{{.}}{{/items}}{{/items}}
diff --git a/test/data/nesting_same_name.txt b/test/data/nesting_same_name.txt
new file mode 100644
index 0000000..34fcfd3
--- /dev/null
+++ b/test/data/nesting_same_name.txt
@@ -0,0 +1 @@
+name1234
diff --git a/test/data/null_lookup_array.h b/test/data/null_lookup_array.h
new file mode 100644
index 0000000..3f167da
--- /dev/null
+++ b/test/data/null_lookup_array.h
@@ -0,0 +1,8 @@
+auto data = mstch::object{
+ {"name", std::string{"David"}},
+ {"twitter", std::string{"@dasilvacontin"}},
+ {"farray", mstch::array{
+ mstch::array{std::string{"Flor"}, std::string{"@florrts"}},
+ mstch::array{std::string{"Miquel"}, mstch::node{}},
+ }}
+};
diff --git a/test/data/null_lookup_array.mustache b/test/data/null_lookup_array.mustache
new file mode 100644
index 0000000..0543895
--- /dev/null
+++ b/test/data/null_lookup_array.mustache
@@ -0,0 +1,3 @@
+{{#farray}}
+{{#.}}{{#.}}{{.}} {{/.}}{{^.}}no twitter{{/.}}{{/.}}
+{{/farray}}
diff --git a/test/data/null_lookup_array.txt b/test/data/null_lookup_array.txt
new file mode 100644
index 0000000..d4f4dc5
--- /dev/null
+++ b/test/data/null_lookup_array.txt
@@ -0,0 +1,2 @@
+Flor @florrts
+Miquel no twitter
diff --git a/test/data/null_lookup_object.h b/test/data/null_lookup_object.h
new file mode 100644
index 0000000..ce12fa8
--- /dev/null
+++ b/test/data/null_lookup_object.h
@@ -0,0 +1,14 @@
+auto data = mstch::object{
+ {"name", std::string{"David"}},
+ {"twitter", std::string{"@dasilvacontin"}},
+ {"fobject", mstch::array{
+ mstch::object{
+ {"name", std::string{"Flor"}},
+ {"twitter", std::string{"@florrts"}}
+ },
+ mstch::object{
+ {"name", std::string{"Miquel"}},
+ {"twitter", mstch::node{}}
+ }
+ }}
+};
diff --git a/test/data/null_lookup_object.mustache b/test/data/null_lookup_object.mustache
new file mode 100644
index 0000000..e709ae4
--- /dev/null
+++ b/test/data/null_lookup_object.mustache
@@ -0,0 +1,3 @@
+{{#fobject}}
+{{name}}'s twitter: {{#twitter}}{{.}}{{/twitter}}{{^twitter}}unknown{{/twitter}}.
+{{/fobject}}
diff --git a/test/data/null_lookup_object.txt b/test/data/null_lookup_object.txt
new file mode 100644
index 0000000..d1291ee
--- /dev/null
+++ b/test/data/null_lookup_object.txt
@@ -0,0 +1,2 @@
+Flor's twitter: @florrts.
+Miquel's twitter: unknown.
diff --git a/test/data/null_view.h b/test/data/null_view.h
new file mode 100644
index 0000000..7b277d7
--- /dev/null
+++ b/test/data/null_view.h
@@ -0,0 +1,4 @@
+auto data = mstch::object{
+ {"name", std::string{"Joe"}},
+ {"friends", mstch::node{}}
+};
diff --git a/test/data/null_view.mustache b/test/data/null_view.mustache
new file mode 100644
index 0000000..115b376
--- /dev/null
+++ b/test/data/null_view.mustache
@@ -0,0 +1 @@
+{{name}}'s friends: {{#friends}}{{name}}, {{/friends}}
\ No newline at end of file
diff --git a/test/data/null_view.txt b/test/data/null_view.txt
new file mode 100644
index 0000000..15ed2ab
--- /dev/null
+++ b/test/data/null_view.txt
@@ -0,0 +1 @@
+Joe's friends:
\ No newline at end of file
diff --git a/test/data/partial/partial_array.js b/test/data/partial/partial_array.js
new file mode 100644
index 0000000..2a6ddf1
--- /dev/null
+++ b/test/data/partial/partial_array.js
@@ -0,0 +1,3 @@
+({
+ array: ['1', '2', '3', '4']
+})
diff --git a/test/data/partial/partial_array.mustache b/test/data/partial/partial_array.mustache
new file mode 100644
index 0000000..7a336fe
--- /dev/null
+++ b/test/data/partial/partial_array.mustache
@@ -0,0 +1 @@
+{{>partial}}
\ No newline at end of file
diff --git a/test/data/partial/partial_array.partial b/test/data/partial/partial_array.partial
new file mode 100644
index 0000000..0ba652c
--- /dev/null
+++ b/test/data/partial/partial_array.partial
@@ -0,0 +1,4 @@
+Here's a non-sense array of values
+{{#array}}
+ {{.}}
+{{/array}}
diff --git a/test/data/partial/partial_array.txt b/test/data/partial/partial_array.txt
new file mode 100644
index 0000000..892837c
--- /dev/null
+++ b/test/data/partial/partial_array.txt
@@ -0,0 +1,5 @@
+Here's a non-sense array of values
+ 1
+ 2
+ 3
+ 4
diff --git a/test/data/partial/partial_array_of_partials.js b/test/data/partial/partial_array_of_partials.js
new file mode 100644
index 0000000..03f13c9
--- /dev/null
+++ b/test/data/partial/partial_array_of_partials.js
@@ -0,0 +1,8 @@
+({
+ numbers: [
+ {i: '1'},
+ {i: '2'},
+ {i: '3'},
+ {i: '4'}
+ ]
+})
diff --git a/test/data/partial/partial_array_of_partials.mustache b/test/data/partial/partial_array_of_partials.mustache
new file mode 100644
index 0000000..1af6d68
--- /dev/null
+++ b/test/data/partial/partial_array_of_partials.mustache
@@ -0,0 +1,4 @@
+Here is some stuff!
+{{#numbers}}
+{{>partial}}
+{{/numbers}}
diff --git a/test/data/partial/partial_array_of_partials.partial b/test/data/partial/partial_array_of_partials.partial
new file mode 100644
index 0000000..bdde77d
--- /dev/null
+++ b/test/data/partial/partial_array_of_partials.partial
@@ -0,0 +1 @@
+{{i}}
diff --git a/test/data/partial/partial_array_of_partials.txt b/test/data/partial/partial_array_of_partials.txt
new file mode 100644
index 0000000..f622375
--- /dev/null
+++ b/test/data/partial/partial_array_of_partials.txt
@@ -0,0 +1,5 @@
+Here is some stuff!
+1
+2
+3
+4
diff --git a/test/data/partial/partial_array_of_partials_implicit.js b/test/data/partial/partial_array_of_partials_implicit.js
new file mode 100644
index 0000000..9ec0c00
--- /dev/null
+++ b/test/data/partial/partial_array_of_partials_implicit.js
@@ -0,0 +1,3 @@
+({
+ numbers: ['1', '2', '3', '4']
+})
diff --git a/test/data/partial/partial_array_of_partials_implicit.mustache b/test/data/partial/partial_array_of_partials_implicit.mustache
new file mode 100644
index 0000000..1af6d68
--- /dev/null
+++ b/test/data/partial/partial_array_of_partials_implicit.mustache
@@ -0,0 +1,4 @@
+Here is some stuff!
+{{#numbers}}
+{{>partial}}
+{{/numbers}}
diff --git a/test/data/partial/partial_array_of_partials_implicit.partial b/test/data/partial/partial_array_of_partials_implicit.partial
new file mode 100644
index 0000000..12f7159
--- /dev/null
+++ b/test/data/partial/partial_array_of_partials_implicit.partial
@@ -0,0 +1 @@
+{{.}}
diff --git a/test/data/partial/partial_array_of_partials_implicit.txt b/test/data/partial/partial_array_of_partials_implicit.txt
new file mode 100644
index 0000000..f622375
--- /dev/null
+++ b/test/data/partial/partial_array_of_partials_implicit.txt
@@ -0,0 +1,5 @@
+Here is some stuff!
+1
+2
+3
+4
diff --git a/test/data/partial/partial_empty.js b/test/data/partial/partial_empty.js
new file mode 100644
index 0000000..82b8c22
--- /dev/null
+++ b/test/data/partial/partial_empty.js
@@ -0,0 +1,3 @@
+({
+ foo: 1
+})
diff --git a/test/data/partial/partial_empty.mustache b/test/data/partial/partial_empty.mustache
new file mode 100644
index 0000000..a710047
--- /dev/null
+++ b/test/data/partial/partial_empty.mustache
@@ -0,0 +1,2 @@
+hey {{foo}}
+{{>partial}}
diff --git a/test/data/partial/partial_empty.partial b/test/data/partial/partial_empty.partial
new file mode 100644
index 0000000..e69de29
diff --git a/test/data/partial/partial_empty.txt b/test/data/partial/partial_empty.txt
new file mode 100644
index 0000000..1a67907
--- /dev/null
+++ b/test/data/partial/partial_empty.txt
@@ -0,0 +1 @@
+hey 1
diff --git a/test/data/partial/partial_template.js b/test/data/partial/partial_template.js
new file mode 100644
index 0000000..a913f87
--- /dev/null
+++ b/test/data/partial/partial_template.js
@@ -0,0 +1,6 @@
+({
+ title: function () {
+ return "Welcome";
+ },
+ again: "Goodbye"
+})
diff --git a/test/data/partial/partial_template.mustache b/test/data/partial/partial_template.mustache
new file mode 100644
index 0000000..6a7492e
--- /dev/null
+++ b/test/data/partial/partial_template.mustache
@@ -0,0 +1,2 @@
+{{title}}
+{{>partial}}
diff --git a/test/data/partial/partial_template.partial b/test/data/partial/partial_template.partial
new file mode 100644
index 0000000..a404529
--- /dev/null
+++ b/test/data/partial/partial_template.partial
@@ -0,0 +1 @@
+Again, {{again}}!
diff --git a/test/data/partial/partial_template.txt b/test/data/partial/partial_template.txt
new file mode 100644
index 0000000..692698f
--- /dev/null
+++ b/test/data/partial/partial_template.txt
@@ -0,0 +1,2 @@
+Welcome
+Again, Goodbye!
diff --git a/test/data/partial/partial_view.js b/test/data/partial/partial_view.js
new file mode 100644
index 0000000..3ad70d3
--- /dev/null
+++ b/test/data/partial/partial_view.js
@@ -0,0 +1,14 @@
+({
+ greeting: function () {
+ return "Welcome";
+ },
+ farewell: function () {
+ return "Fair enough, right?";
+ },
+ name: "Chris",
+ value: 10000,
+ taxed_value: function () {
+ return this.value - (this.value * 0.4);
+ },
+ in_ca: true
+})
diff --git a/test/data/partial/partial_view.mustache b/test/data/partial/partial_view.mustache
new file mode 100644
index 0000000..f8f6a5b
--- /dev/null
+++ b/test/data/partial/partial_view.mustache
@@ -0,0 +1,3 @@
+{{greeting}}
+{{>partial}}
+{{farewell}}
diff --git a/test/data/partial/partial_view.partial b/test/data/partial/partial_view.partial
new file mode 100644
index 0000000..03df206
--- /dev/null
+++ b/test/data/partial/partial_view.partial
@@ -0,0 +1,5 @@
+Hello {{name}}
+You have just won ${{value}}!
+{{#in_ca}}
+Well, ${{ taxed_value }}, after taxes.
+{{/in_ca}}
\ No newline at end of file
diff --git a/test/data/partial/partial_view.txt b/test/data/partial/partial_view.txt
new file mode 100644
index 0000000..c09147c
--- /dev/null
+++ b/test/data/partial/partial_view.txt
@@ -0,0 +1,5 @@
+Welcome
+Hello Chris
+You have just won $10000!
+Well, $6000, after taxes.
+Fair enough, right?
diff --git a/test/data/partial/partial_whitespace.js b/test/data/partial/partial_whitespace.js
new file mode 100644
index 0000000..3ad70d3
--- /dev/null
+++ b/test/data/partial/partial_whitespace.js
@@ -0,0 +1,14 @@
+({
+ greeting: function () {
+ return "Welcome";
+ },
+ farewell: function () {
+ return "Fair enough, right?";
+ },
+ name: "Chris",
+ value: 10000,
+ taxed_value: function () {
+ return this.value - (this.value * 0.4);
+ },
+ in_ca: true
+})
diff --git a/test/data/partial/partial_whitespace.mustache b/test/data/partial/partial_whitespace.mustache
new file mode 100644
index 0000000..48bd1ff
--- /dev/null
+++ b/test/data/partial/partial_whitespace.mustache
@@ -0,0 +1,3 @@
+{{ greeting }}
+{{> partial }}
+{{ farewell }}
diff --git a/test/data/partial/partial_whitespace.partial b/test/data/partial/partial_whitespace.partial
new file mode 100644
index 0000000..30de8f6
--- /dev/null
+++ b/test/data/partial/partial_whitespace.partial
@@ -0,0 +1,5 @@
+Hello {{ name}}
+You have just won ${{value }}!
+{{# in_ca }}
+Well, ${{ taxed_value }}, after taxes.
+{{/ in_ca }}
\ No newline at end of file
diff --git a/test/data/partial/partial_whitespace.txt b/test/data/partial/partial_whitespace.txt
new file mode 100644
index 0000000..c09147c
--- /dev/null
+++ b/test/data/partial/partial_whitespace.txt
@@ -0,0 +1,5 @@
+Welcome
+Hello Chris
+You have just won $10000!
+Well, $6000, after taxes.
+Fair enough, right?
diff --git a/test/data/partial/section_functions_in_partials.js b/test/data/partial/section_functions_in_partials.js
new file mode 100644
index 0000000..4672778
--- /dev/null
+++ b/test/data/partial/section_functions_in_partials.js
@@ -0,0 +1,7 @@
+({
+ bold: function(){
+ return function(text, render) {
+ return "" + render(text) + " ";
+ }
+ }
+})
diff --git a/test/data/partial/section_functions_in_partials.mustache b/test/data/partial/section_functions_in_partials.mustache
new file mode 100644
index 0000000..8164932
--- /dev/null
+++ b/test/data/partial/section_functions_in_partials.mustache
@@ -0,0 +1,3 @@
+{{> partial}}
+
+some more text
diff --git a/test/data/partial/section_functions_in_partials.partial b/test/data/partial/section_functions_in_partials.partial
new file mode 100644
index 0000000..3e90b00
--- /dev/null
+++ b/test/data/partial/section_functions_in_partials.partial
@@ -0,0 +1 @@
+{{#bold}}Hello There{{/bold}}
diff --git a/test/data/partial/section_functions_in_partials.txt b/test/data/partial/section_functions_in_partials.txt
new file mode 100644
index 0000000..2f5955c
--- /dev/null
+++ b/test/data/partial/section_functions_in_partials.txt
@@ -0,0 +1,3 @@
+Hello There
+
+some more text
diff --git a/test/data/recursion_with_same_names.h b/test/data/recursion_with_same_names.h
new file mode 100644
index 0000000..ed07a15
--- /dev/null
+++ b/test/data/recursion_with_same_names.h
@@ -0,0 +1,8 @@
+auto data = mstch::object{
+ {"name", std::string{"name"}},
+ {"description", std::string{"desc"}},
+ {"terms", mstch::array{
+ mstch::object{{"name", std::string{"t1"}}, {"index", 0}},
+ mstch::object{{"name", std::string{"t2"}}, {"index", 1}}
+ }}
+};
diff --git a/test/data/recursion_with_same_names.mustache b/test/data/recursion_with_same_names.mustache
new file mode 100644
index 0000000..c331d04
--- /dev/null
+++ b/test/data/recursion_with_same_names.mustache
@@ -0,0 +1,7 @@
+{{ name }}
+{{ description }}
+
+{{#terms}}
+ {{name}}
+ {{index}}
+{{/terms}}
diff --git a/test/data/recursion_with_same_names.txt b/test/data/recursion_with_same_names.txt
new file mode 100644
index 0000000..cb15d75
--- /dev/null
+++ b/test/data/recursion_with_same_names.txt
@@ -0,0 +1,7 @@
+name
+desc
+
+ t1
+ 0
+ t2
+ 1
diff --git a/test/data/reuse_of_enumerables.h b/test/data/reuse_of_enumerables.h
new file mode 100644
index 0000000..1560f77
--- /dev/null
+++ b/test/data/reuse_of_enumerables.h
@@ -0,0 +1,6 @@
+auto data = mstch::object{
+ {"terms", mstch::array{
+ mstch::object{{"name", std::string{"t1"}}, {"index", 0}},
+ mstch::object{{"name", std::string{"t2"}}, {"index", 1}}
+ }}
+};
diff --git a/test/data/reuse_of_enumerables.mustache b/test/data/reuse_of_enumerables.mustache
new file mode 100644
index 0000000..cc0cb7a
--- /dev/null
+++ b/test/data/reuse_of_enumerables.mustache
@@ -0,0 +1,8 @@
+{{#terms}}
+ {{name}}
+ {{index}}
+{{/terms}}
+{{#terms}}
+ {{name}}
+ {{index}}
+{{/terms}}
diff --git a/test/data/reuse_of_enumerables.txt b/test/data/reuse_of_enumerables.txt
new file mode 100644
index 0000000..6d05d96
--- /dev/null
+++ b/test/data/reuse_of_enumerables.txt
@@ -0,0 +1,8 @@
+ t1
+ 0
+ t2
+ 1
+ t1
+ 0
+ t2
+ 1
diff --git a/test/data/section_as_context.h b/test/data/section_as_context.h
new file mode 100644
index 0000000..2955e5e
--- /dev/null
+++ b/test/data/section_as_context.h
@@ -0,0 +1,10 @@
+auto data = mstch::object{
+ {"a_object", mstch::object{
+ {"title", std::string{"this is an object"}},
+ {"description", std::string{"one of its attributes is a list"}},
+ {"a_list", mstch::array{
+ mstch::object{{"label", std::string{"listitem1"}}},
+ mstch::object{{"label", std::string{"listitem2"}}}
+ }}
+ }}
+};
diff --git a/test/data/section_as_context.mustache b/test/data/section_as_context.mustache
new file mode 100644
index 0000000..59990f6
--- /dev/null
+++ b/test/data/section_as_context.mustache
@@ -0,0 +1,9 @@
+{{#a_object}}
+ {{title}}
+ {{description}}
+
+ {{#a_list}}
+ {{label}}
+ {{/a_list}}
+
+{{/a_object}}
diff --git a/test/data/section_as_context.txt b/test/data/section_as_context.txt
new file mode 100644
index 0000000..d834e80
--- /dev/null
+++ b/test/data/section_as_context.txt
@@ -0,0 +1,6 @@
+ this is an object
+ one of its attributes is a list
+
+ listitem1
+ listitem2
+
diff --git a/test/data/string_as_context.h b/test/data/string_as_context.h
new file mode 100644
index 0000000..8c876a0
--- /dev/null
+++ b/test/data/string_as_context.h
@@ -0,0 +1,4 @@
+auto data = mstch::object{
+ {"a_string", std::string{"aa"}},
+ {"a_list", mstch::array{std::string{"a"},std::string{"b"},std::string{"c"}}}
+};
diff --git a/test/data/string_as_context.mustache b/test/data/string_as_context.mustache
new file mode 100644
index 0000000..00f7181
--- /dev/null
+++ b/test/data/string_as_context.mustache
@@ -0,0 +1,5 @@
+
+{{#a_list}}
+ {{a_string}}/{{.}}
+{{/a_list}}
+
\ No newline at end of file
diff --git a/test/data/string_as_context.txt b/test/data/string_as_context.txt
new file mode 100644
index 0000000..8bd87ff
--- /dev/null
+++ b/test/data/string_as_context.txt
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/test/data/two_in_a_row.h b/test/data/two_in_a_row.h
new file mode 100644
index 0000000..d12724a
--- /dev/null
+++ b/test/data/two_in_a_row.h
@@ -0,0 +1,4 @@
+auto data = mstch::object{
+ {"name", std::string{"Joe"}},
+ {"greeting", std::string{"Welcome"}}
+};
diff --git a/test/data/two_in_a_row.mustache b/test/data/two_in_a_row.mustache
new file mode 100644
index 0000000..b23f29e
--- /dev/null
+++ b/test/data/two_in_a_row.mustache
@@ -0,0 +1 @@
+{{greeting}}, {{name}}!
diff --git a/test/data/two_in_a_row.txt b/test/data/two_in_a_row.txt
new file mode 100644
index 0000000..c6d6a9b
--- /dev/null
+++ b/test/data/two_in_a_row.txt
@@ -0,0 +1 @@
+Welcome, Joe!
diff --git a/test/data/two_sections.h b/test/data/two_sections.h
new file mode 100644
index 0000000..f040324
--- /dev/null
+++ b/test/data/two_sections.h
@@ -0,0 +1 @@
+auto data = mstch::object{};
diff --git a/test/data/two_sections.mustache b/test/data/two_sections.mustache
new file mode 100644
index 0000000..a4b9f2a
--- /dev/null
+++ b/test/data/two_sections.mustache
@@ -0,0 +1,4 @@
+{{#foo}}
+{{/foo}}
+{{#bar}}
+{{/bar}}
diff --git a/test/data/two_sections.txt b/test/data/two_sections.txt
new file mode 100644
index 0000000..e69de29
diff --git a/test/data/whitespace.h b/test/data/whitespace.h
new file mode 100644
index 0000000..712b220
--- /dev/null
+++ b/test/data/whitespace.h
@@ -0,0 +1,4 @@
+auto data = mstch::object{
+ {"tag1", std::string{"Hello"}},
+ {"tag2", std::string{"World"}}
+};
diff --git a/test/data/whitespace.mustache b/test/data/whitespace.mustache
new file mode 100644
index 0000000..aa76e08
--- /dev/null
+++ b/test/data/whitespace.mustache
@@ -0,0 +1,4 @@
+{{tag1}}
+
+
+{{tag2}}.
diff --git a/test/data/whitespace.txt b/test/data/whitespace.txt
new file mode 100644
index 0000000..851fa74
--- /dev/null
+++ b/test/data/whitespace.txt
@@ -0,0 +1,4 @@
+Hello
+
+
+World.
diff --git a/test/data/zero_view.h b/test/data/zero_view.h
new file mode 100644
index 0000000..45fd810
--- /dev/null
+++ b/test/data/zero_view.h
@@ -0,0 +1 @@
+auto data = mstch::object{{"nums", mstch::array{0, 1, 2}}};
diff --git a/test/data/zero_view.mustache b/test/data/zero_view.mustache
new file mode 100644
index 0000000..1cdc190
--- /dev/null
+++ b/test/data/zero_view.mustache
@@ -0,0 +1 @@
+{{#nums}}{{.}},{{/nums}}
\ No newline at end of file
diff --git a/test/data/zero_view.txt b/test/data/zero_view.txt
new file mode 100644
index 0000000..2aee585
--- /dev/null
+++ b/test/data/zero_view.txt
@@ -0,0 +1 @@
+0,1,2,
\ No newline at end of file
diff --git a/test/main.cpp b/test/main.cpp
new file mode 100644
index 0000000..fa6ff14
--- /dev/null
+++ b/test/main.cpp
@@ -0,0 +1,274 @@
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
+
+#include
+
+#include
+#include
+#include
+
+std::string file_to_string(const std::string& filename) {
+ std::ifstream t(filename);
+ std::string str((std::istreambuf_iterator(t)),
+ std::istreambuf_iterator());
+ return str;
+}
+
+/*TEST_CASE("Ampersand escape") {
+ #include "data/ampersand_escape.h"
+ auto tpl = file_to_string("data/ampersand_escape.mustache");
+ auto exp = file_to_string("data/ampersand_escape.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Apostrophe") {
+ #include "data/apostrophe.h"
+ auto tpl = file_to_string("data/apostrophe.mustache");
+ auto exp = file_to_string("data/apostrophe.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Array of strings") {
+ #include "data/array_of_strings.h"
+ auto tpl = file_to_string("data/array_of_strings.mustache");
+ auto exp = file_to_string("data/array_of_strings.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Backslashes") {
+ #include "data/backslashes.h"
+ auto tpl = file_to_string("data/backslashes.mustache");
+ auto exp = file_to_string("data/backslashes.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Bug 11 eating whitespace") {
+ #include "data/bug_11_eating_whitespace.h"
+ auto tpl = file_to_string("data/bug_11_eating_whitespace.mustache");
+ auto exp = file_to_string("data/bug_11_eating_whitespace.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Bug length property") {
+ #include "data/bug_length_property.h"
+ auto tpl = file_to_string("data/bug_length_property.mustache");
+ auto exp = file_to_string("data/bug_length_property.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Context lookup") {
+ #include "data/context_lookup.h"
+ auto tpl = file_to_string("data/context_lookup.mustache");
+ auto exp = file_to_string("data/context_lookup.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Disappearing whitespace") {
+ #include "data/disappearing_whitespace.h"
+ auto tpl = file_to_string("data/disappearing_whitespace.mustache");
+ auto exp = file_to_string("data/disappearing_whitespace.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Double render") {
+ #include "data/double_render.h"
+ auto tpl = file_to_string("data/double_render.mustache");
+ auto exp = file_to_string("data/double_render.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Empty list") {
+ #include "data/empty_list.h"
+ auto tpl = file_to_string("data/empty_list.mustache");
+ auto exp = file_to_string("data/empty_list.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Empty sections") {
+ #include "data/empty_sections.h"
+ auto tpl = file_to_string("data/empty_sections.mustache");
+ auto exp = file_to_string("data/empty_sections.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Empty string") {
+ #include "data/empty_string.h"
+ auto tpl = file_to_string("data/empty_string.mustache");
+ auto exp = file_to_string("data/empty_string.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Empty template") {
+ #include "data/empty_template.h"
+ auto tpl = file_to_string("data/empty_template.mustache");
+ auto exp = file_to_string("data/empty_template.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Error not found") {
+ #include "data/error_not_found.h"
+ auto tpl = file_to_string("data/error_not_found.mustache");
+ auto exp = file_to_string("data/error_not_found.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Falsy") {
+ #include "data/falsy.h"
+ auto tpl = file_to_string("data/falsy.mustache");
+ auto exp = file_to_string("data/falsy.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}*/
+
+TEST_CASE("Falsy array") {
+ #include "data/falsy_array.h"
+ auto tpl = file_to_string("data/falsy_array.mustache");
+ auto exp = file_to_string("data/falsy_array.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+/*TEST_CASE("Grandparent context") {
+ #include "data/grandparent_context.h"
+ auto tpl = file_to_string("data/grandparent_context.mustache");
+ auto exp = file_to_string("data/grandparent_context.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Implicit iterator") {
+ #include "data/implicit_iterator.h"
+ auto tpl = file_to_string("data/implicit_iterator.mustache");
+ auto exp = file_to_string("data/implicit_iterator.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Included tag") {
+ #include "data/included_tag.h"
+ auto tpl = file_to_string("data/included_tag.mustache");
+ auto exp = file_to_string("data/included_tag.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Inverted section") {
+ #include "data/inverted_section.h"
+ auto tpl = file_to_string("data/inverted_section.mustache");
+ auto exp = file_to_string("data/inverted_section.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Keys with questionmarks") {
+ #include "data/keys_with_questionmarks.h"
+ auto tpl = file_to_string("data/keys_with_questionmarks.mustache");
+ auto exp = file_to_string("data/keys_with_questionmarks.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Multiline comment") {
+ #include "data/multiline_comment.h"
+ auto tpl = file_to_string("data/multiline_comment.mustache");
+ auto exp = file_to_string("data/multiline_comment.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Nested dot") {
+ #include "data/nested_dot.h"
+ auto tpl = file_to_string("data/nested_dot.mustache");
+ auto exp = file_to_string("data/nested_dot.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Nested iterating") {
+ #include "data/nested_iterating.h"
+ auto tpl = file_to_string("data/nested_iterating.mustache");
+ auto exp = file_to_string("data/nested_iterating.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Nesting") {
+ #include "data/nesting.h"
+ auto tpl = file_to_string("data/nesting.mustache");
+ auto exp = file_to_string("data/nesting.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Nesting same name") {
+ #include "data/nesting_same_name.h"
+ auto tpl = file_to_string("data/nesting_same_name.mustache");
+ auto exp = file_to_string("data/nesting_same_name.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Null lookup array") {
+ #include "data/null_lookup_array.h"
+ auto tpl = file_to_string("data/null_lookup_array.mustache");
+ auto exp = file_to_string("data/null_lookup_array.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Null lookup object") {
+ #include "data/null_lookup_object.h"
+ auto tpl = file_to_string("data/null_lookup_object.mustache");
+ auto exp = file_to_string("data/null_lookup_object.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Null view") {
+ #include "data/null_view.h"
+ auto tpl = file_to_string("data/null_view.mustache");
+ auto exp = file_to_string("data/null_view.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Recursion with same names") {
+ #include "data/recursion_with_same_names.h"
+ auto tpl = file_to_string("data/recursion_with_same_names.mustache");
+ auto exp = file_to_string("data/recursion_with_same_names.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Reuse of enumerables") {
+ #include "data/reuse_of_enumerables.h"
+ auto tpl = file_to_string("data/reuse_of_enumerables.mustache");
+ auto exp = file_to_string("data/reuse_of_enumerables.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Section as context") {
+ #include "data/section_as_context.h"
+ auto tpl = file_to_string("data/section_as_context.mustache");
+ auto exp = file_to_string("data/section_as_context.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("String as context") {
+ #include "data/string_as_context.h"
+ auto tpl = file_to_string("data/string_as_context.mustache");
+ auto exp = file_to_string("data/string_as_context.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Two in a row") {
+ #include "data/two_in_a_row.h"
+ auto tpl = file_to_string("data/two_in_a_row.mustache");
+ auto exp = file_to_string("data/two_in_a_row.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Two sections") {
+ #include "data/two_sections.h"
+ auto tpl = file_to_string("data/two_sections.mustache");
+ auto exp = file_to_string("data/two_sections.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Whitespace") {
+ #include "data/whitespace.h"
+ auto tpl = file_to_string("data/whitespace.mustache");
+ auto exp = file_to_string("data/whitespace.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}
+
+TEST_CASE("Zero view") {
+ #include "data/zero_view.h"
+ auto tpl = file_to_string("data/zero_view.mustache");
+ auto exp = file_to_string("data/zero_view.txt");
+ REQUIRE(exp == mstch::render(tpl, data));
+}*/
diff --git a/vendor/include/catch.hpp b/vendor/include/catch.hpp
new file mode 100644
index 0000000..2964790
--- /dev/null
+++ b/vendor/include/catch.hpp
@@ -0,0 +1,9427 @@
+/*
+ * CATCH v1.1 build 14 (develop branch)
+ * Generated: 2015-03-04 18:32:24.627737
+ * ----------------------------------------------------------
+ * This file has been merged from multiple headers. Please don't edit it directly
+ * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
+#define TWOBLUECUBES_CATCH_HPP_INCLUDED
+
+// #included from: internal/catch_suppress_warnings.h
+
+#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+# ifdef __ICC // icpc defines the __clang__ macro
+# pragma warning(push)
+# pragma warning(disable: 161 1682)
+# else // __ICC
+# pragma clang diagnostic ignored "-Wglobal-constructors"
+# pragma clang diagnostic ignored "-Wvariadic-macros"
+# pragma clang diagnostic ignored "-Wc99-extensions"
+# pragma clang diagnostic ignored "-Wunused-variable"
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wpadded"
+# pragma clang diagnostic ignored "-Wc++98-compat"
+# pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+# endif
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wvariadic-macros"
+# pragma GCC diagnostic ignored "-Wunused-variable"
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wpadded"
+#endif
+
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+# define CATCH_IMPL
+#endif
+
+#ifdef CATCH_IMPL
+# ifndef CLARA_CONFIG_MAIN
+# define CLARA_CONFIG_MAIN_NOT_DEFINED
+# define CLARA_CONFIG_MAIN
+# endif
+#endif
+
+// #included from: internal/catch_notimplemented_exception.h
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
+
+// #included from: catch_common.h
+#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
+
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+
+#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
+#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+
+#include
+#include
+#include
+
+// #included from: catch_compiler_capabilities.h
+#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+
+// Much of the following code is based on Boost (1.53)
+
+#ifdef __clang__
+
+# if __has_feature(cxx_nullptr)
+# define CATCH_CONFIG_CPP11_NULLPTR
+# endif
+
+# if __has_feature(cxx_noexcept)
+# define CATCH_CONFIG_CPP11_NOEXCEPT
+# endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// Borland
+#ifdef __BORLANDC__
+
+#if (__BORLANDC__ > 0x582 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __BORLANDC__
+
+////////////////////////////////////////////////////////////////////////////////
+// EDG
+#ifdef __EDG_VERSION__
+
+#if (__EDG_VERSION__ > 238 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __EDG_VERSION__
+
+////////////////////////////////////////////////////////////////////////////////
+// Digital Mars
+#ifdef __DMC__
+
+#if (__DMC__ > 0x840 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __DMC__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#if __GNUC__ < 3
+
+#if (__GNUC_MINOR__ >= 96 )
+//#define CATCH_CONFIG_SFINAE
+#endif
+
+#elif __GNUC__ >= 3
+
+// #define CATCH_CONFIG_SFINAE // Taking this out completely for now
+
+#endif // __GNUC__ < 3
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) )
+
+#define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#if (_MSC_VER >= 1600)
+#define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+
+#if (_MSC_VER >= 1310 ) // (VC++ 7.0+)
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // _MSC_VER
+
+// Use variadic macros if the compiler supports them
+#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
+ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
+ ( defined __GNUC__ && __GNUC__ >= 3 ) || \
+ ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
+
+#ifndef CATCH_CONFIG_NO_VARIADIC_MACROS
+#define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// detect language version:
+#if (__cplusplus == 201103L)
+# define CATCH_CPP11
+# define CATCH_CPP11_OR_GREATER
+#elif (__cplusplus >= 201103L)
+# define CATCH_CPP11_OR_GREATER
+#endif
+
+// noexcept support:
+#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
+# define CATCH_NOEXCEPT noexcept
+# define CATCH_NOEXCEPT_IS(x) noexcept(x)
+#else
+# define CATCH_NOEXCEPT throw()
+# define CATCH_NOEXCEPT_IS(x)
+#endif
+
+namespace Catch {
+
+ class NonCopyable {
+#ifdef CATCH_CPP11_OR_GREATER
+ NonCopyable( NonCopyable const& ) = delete;
+ NonCopyable( NonCopyable && ) = delete;
+ NonCopyable& operator = ( NonCopyable const& ) = delete;
+ NonCopyable& operator = ( NonCopyable && ) = delete;
+#else
+ NonCopyable( NonCopyable const& info );
+ NonCopyable& operator = ( NonCopyable const& );
+#endif
+
+ protected:
+ NonCopyable() {}
+ virtual ~NonCopyable();
+ };
+
+ class SafeBool {
+ public:
+ typedef void (SafeBool::*type)() const;
+
+ static type makeSafe( bool value ) {
+ return value ? &SafeBool::trueValue : 0;
+ }
+ private:
+ void trueValue() const {}
+ };
+
+ template
+ inline void deleteAll( ContainerT& container ) {
+ typename ContainerT::const_iterator it = container.begin();
+ typename ContainerT::const_iterator itEnd = container.end();
+ for(; it != itEnd; ++it )
+ delete *it;
+ }
+ template
+ inline void deleteAllValues( AssociativeContainerT& container ) {
+ typename AssociativeContainerT::const_iterator it = container.begin();
+ typename AssociativeContainerT::const_iterator itEnd = container.end();
+ for(; it != itEnd; ++it )
+ delete it->second;
+ }
+
+ bool startsWith( std::string const& s, std::string const& prefix );
+ bool endsWith( std::string const& s, std::string const& suffix );
+ bool contains( std::string const& s, std::string const& infix );
+ void toLowerInPlace( std::string& s );
+ std::string toLower( std::string const& s );
+ std::string trim( std::string const& str );
+ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
+
+ struct pluralise {
+ pluralise( std::size_t count, std::string const& label );
+
+ friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+
+ std::size_t m_count;
+ std::string m_label;
+ };
+
+ struct SourceLineInfo {
+
+ SourceLineInfo();
+ SourceLineInfo( char const* _file, std::size_t _line );
+ SourceLineInfo( SourceLineInfo const& other );
+# ifdef CATCH_CPP11_OR_GREATER
+ SourceLineInfo( SourceLineInfo && ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo && ) = default;
+# endif
+ bool empty() const;
+ bool operator == ( SourceLineInfo const& other ) const;
+ bool operator < ( SourceLineInfo const& other ) const;
+
+ std::string file;
+ std::size_t line;
+ };
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+ // This is just here to avoid compiler warnings with macro constants and boolean literals
+ inline bool isTrue( bool value ){ return value; }
+ inline bool alwaysTrue() { return true; }
+ inline bool alwaysFalse() { return false; }
+
+ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
+
+ // Use this in variadic streaming macros to allow
+ // >> +StreamEndStop
+ // as well as
+ // >> stuff +StreamEndStop
+ struct StreamEndStop {
+ std::string operator+() {
+ return std::string();
+ }
+ };
+ template
+ T const& operator + ( T const& value, StreamEndStop ) {
+ return value;
+ }
+}
+
+#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) )
+#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
+
+#include
+
+namespace Catch {
+
+ class NotImplementedException : public std::exception
+ {
+ public:
+ NotImplementedException( SourceLineInfo const& lineInfo );
+ NotImplementedException( NotImplementedException const& ) {}
+
+ virtual ~NotImplementedException() CATCH_NOEXCEPT {}
+
+ virtual const char* what() const CATCH_NOEXCEPT;
+
+ private:
+ std::string m_what;
+ SourceLineInfo m_lineInfo;
+ };
+
+} // end namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
+
+// #included from: internal/catch_context.h
+#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
+
+// #included from: catch_interfaces_generators.h
+#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
+
+#include
+
+namespace Catch {
+
+ struct IGeneratorInfo {
+ virtual ~IGeneratorInfo();
+ virtual bool moveNext() = 0;
+ virtual std::size_t getCurrentIndex() const = 0;
+ };
+
+ struct IGeneratorsForTest {
+ virtual ~IGeneratorsForTest();
+
+ virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
+ virtual bool moveNext() = 0;
+ };
+
+ IGeneratorsForTest* createGeneratorsForTest();
+
+} // end namespace Catch
+
+// #included from: catch_ptr.hpp
+#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ // An intrusive reference counting smart pointer.
+ // T must implement addRef() and release() methods
+ // typically implementing the IShared interface
+ template
+ class Ptr {
+ public:
+ Ptr() : m_p( NULL ){}
+ Ptr( T* p ) : m_p( p ){
+ if( m_p )
+ m_p->addRef();
+ }
+ Ptr( Ptr const& other ) : m_p( other.m_p ){
+ if( m_p )
+ m_p->addRef();
+ }
+ ~Ptr(){
+ if( m_p )
+ m_p->release();
+ }
+ void reset() {
+ if( m_p )
+ m_p->release();
+ m_p = NULL;
+ }
+ Ptr& operator = ( T* p ){
+ Ptr temp( p );
+ swap( temp );
+ return *this;
+ }
+ Ptr& operator = ( Ptr const& other ){
+ Ptr temp( other );
+ swap( temp );
+ return *this;
+ }
+ void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
+ T* get() { return m_p; }
+ const T* get() const{ return m_p; }
+ T& operator*() const { return *m_p; }
+ T* operator->() const { return m_p; }
+ bool operator !() const { return m_p == NULL; }
+ operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); }
+
+ private:
+ T* m_p;
+ };
+
+ struct IShared : NonCopyable {
+ virtual ~IShared();
+ virtual void addRef() const = 0;
+ virtual void release() const = 0;
+ };
+
+ template
+ struct SharedImpl : T {
+
+ SharedImpl() : m_rc( 0 ){}
+
+ virtual void addRef() const {
+ ++m_rc;
+ }
+ virtual void release() const {
+ if( --m_rc == 0 )
+ delete this;
+ }
+
+ mutable unsigned int m_rc;
+ };
+
+} // end namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#include
+#include
+#include
+
+namespace Catch {
+
+ class TestCase;
+ class Stream;
+ struct IResultCapture;
+ struct IRunner;
+ struct IGeneratorsForTest;
+ struct IConfig;
+
+ struct IContext
+ {
+ virtual ~IContext();
+
+ virtual IResultCapture* getResultCapture() = 0;
+ virtual IRunner* getRunner() = 0;
+ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
+ virtual bool advanceGeneratorsForCurrentTest() = 0;
+ virtual Ptr getConfig() const = 0;
+ };
+
+ struct IMutableContext : IContext
+ {
+ virtual ~IMutableContext();
+ virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+ virtual void setRunner( IRunner* runner ) = 0;
+ virtual void setConfig( Ptr const& config ) = 0;
+ };
+
+ IContext& getCurrentContext();
+ IMutableContext& getCurrentMutableContext();
+ void cleanUpContext();
+ Stream createStream( std::string const& streamName );
+
+}
+
+// #included from: internal/catch_test_registry.hpp
+#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_interfaces_testcase.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
+
+#include
+
+namespace Catch {
+
+ class TestSpec;
+
+ struct ITestCase : IShared {
+ virtual void invoke () const = 0;
+ protected:
+ virtual ~ITestCase();
+ };
+
+ class TestCase;
+ struct IConfig;
+
+ struct ITestCaseRegistry {
+ virtual ~ITestCaseRegistry();
+ virtual std::vector const& getAllTests() const = 0;
+ virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector& matchingTestCases, bool negated = false ) const = 0;
+
+ };
+}
+
+namespace Catch {
+
+template
+class MethodTestCase : public SharedImpl {
+
+public:
+ MethodTestCase( void (C::*method)() ) : m_method( method ) {}
+
+ virtual void invoke() const {
+ C obj;
+ (obj.*m_method)();
+ }
+
+private:
+ virtual ~MethodTestCase() {}
+
+ void (C::*m_method)();
+};
+
+typedef void(*TestFunction)();
+
+struct NameAndDesc {
+ NameAndDesc( const char* _name = "", const char* _description= "" )
+ : name( _name ), description( _description )
+ {}
+
+ const char* name;
+ const char* description;
+};
+
+struct AutoReg {
+
+ AutoReg( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc );
+
+ template
+ AutoReg( void (C::*method)(),
+ char const* className,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo ) {
+ registerTestCase( new MethodTestCase( method ),
+ className,
+ nameAndDesc,
+ lineInfo );
+ }
+
+ void registerTestCase( ITestCase* testCase,
+ char const* className,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo );
+
+ ~AutoReg();
+
+private:
+ AutoReg( AutoReg const& );
+ void operator= ( AutoReg const& );
+};
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE( ... ) \
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\
+ namespace{ \
+ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+
+#else
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
+ static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+ namespace{ \
+ struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+
+#endif
+
+// #included from: internal/catch_capture.hpp
+#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
+
+// #included from: catch_result_builder.h
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
+
+// #included from: catch_result_type.h
+#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
+
+namespace Catch {
+
+ // ResultWas::OfType enum
+ struct ResultWas { enum OfType {
+ Unknown = -1,
+ Ok = 0,
+ Info = 1,
+ Warning = 2,
+
+ FailureBit = 0x10,
+
+ ExpressionFailed = FailureBit | 1,
+ ExplicitFailure = FailureBit | 2,
+
+ Exception = 0x100 | FailureBit,
+
+ ThrewException = Exception | 1,
+ DidntThrowException = Exception | 2,
+
+ FatalErrorCondition = 0x200 | FailureBit
+
+ }; };
+
+ inline bool isOk( ResultWas::OfType resultType ) {
+ return ( resultType & ResultWas::FailureBit ) == 0;
+ }
+ inline bool isJustInfo( int flags ) {
+ return flags == ResultWas::Info;
+ }
+
+ // ResultDisposition::Flags enum
+ struct ResultDisposition { enum Flags {
+ Normal = 0x00,
+
+ ContinueOnFailure = 0x01, // Failures fail test, but execution continues
+ FalseTest = 0x02, // Prefix expression with !
+ SuppressFail = 0x04 // Failures are reported but do not fail the test
+ }; };
+
+ inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+ return static_cast( static_cast( lhs ) | static_cast( rhs ) );
+ }
+
+ inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+ inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.h
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
+
+#include
+
+namespace Catch {
+
+ struct AssertionInfo
+ {
+ AssertionInfo() {}
+ AssertionInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ std::string const& _capturedExpression,
+ ResultDisposition::Flags _resultDisposition );
+
+ std::string macroName;
+ SourceLineInfo lineInfo;
+ std::string capturedExpression;
+ ResultDisposition::Flags resultDisposition;
+ };
+
+ struct AssertionResultData
+ {
+ AssertionResultData() : resultType( ResultWas::Unknown ) {}
+
+ std::string reconstructedExpression;
+ std::string message;
+ ResultWas::OfType resultType;
+ };
+
+ class AssertionResult {
+ public:
+ AssertionResult();
+ AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+ ~AssertionResult();
+# ifdef CATCH_CPP11_OR_GREATER
+ AssertionResult( AssertionResult const& ) = default;
+ AssertionResult( AssertionResult && ) = default;
+ AssertionResult& operator = ( AssertionResult const& ) = default;
+ AssertionResult& operator = ( AssertionResult && ) = default;
+# endif
+
+ bool isOk() const;
+ bool succeeded() const;
+ ResultWas::OfType getResultType() const;
+ bool hasExpression() const;
+ bool hasMessage() const;
+ std::string getExpression() const;
+ std::string getExpressionInMacro() const;
+ bool hasExpandedExpression() const;
+ std::string getExpandedExpression() const;
+ std::string getMessage() const;
+ SourceLineInfo getSourceInfo() const;
+ std::string getTestMacroName() const;
+
+ protected:
+ AssertionInfo m_info;
+ AssertionResultData m_resultData;
+ };
+
+} // end namespace Catch
+
+namespace Catch {
+
+ struct TestFailureException{};
+
+ template class ExpressionLhs;
+
+ struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+ struct CopyableStream {
+ CopyableStream() {}
+ CopyableStream( CopyableStream const& other ) {
+ oss << other.oss.str();
+ }
+ CopyableStream& operator=( CopyableStream const& other ) {
+ oss.str("");
+ oss << other.oss.str();
+ return *this;
+ }
+ std::ostringstream oss;
+ };
+
+ class ResultBuilder {
+ public:
+ ResultBuilder( char const* macroName,
+ SourceLineInfo const& lineInfo,
+ char const* capturedExpression,
+ ResultDisposition::Flags resultDisposition );
+
+ template
+ ExpressionLhs operator->* ( T const& operand );
+ ExpressionLhs operator->* ( bool value );
+
+ template
+ ResultBuilder& operator << ( T const& value ) {
+ m_stream.oss << value;
+ return *this;
+ }
+
+ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+ ResultBuilder& setResultType( ResultWas::OfType result );
+ ResultBuilder& setResultType( bool result );
+ ResultBuilder& setLhs( std::string const& lhs );
+ ResultBuilder& setRhs( std::string const& rhs );
+ ResultBuilder& setOp( std::string const& op );
+
+ void endExpression();
+
+ std::string reconstructExpression() const;
+ AssertionResult build() const;
+
+ void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+ void captureResult( ResultWas::OfType resultType );
+ void captureExpression();
+ void react();
+ bool shouldDebugBreak() const;
+ bool allowThrows() const;
+
+ private:
+ AssertionInfo m_assertionInfo;
+ AssertionResultData m_data;
+ struct ExprComponents {
+ ExprComponents() : testFalse( false ) {}
+ bool testFalse;
+ std::string lhs, rhs, op;
+ } m_exprComponents;
+ CopyableStream m_stream;
+
+ bool m_shouldDebugBreak;
+ bool m_shouldThrow;
+ };
+
+} // namespace Catch
+
+// Include after due to circular dependency:
+// #included from: catch_expression_lhs.hpp
+#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+
+// #included from: catch_evaluate.hpp
+#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#endif
+
+#include
+
+namespace Catch {
+namespace Internal {
+
+ enum Operator {
+ IsEqualTo,
+ IsNotEqualTo,
+ IsLessThan,
+ IsGreaterThan,
+ IsLessThanOrEqualTo,
+ IsGreaterThanOrEqualTo
+ };
+
+ template struct OperatorTraits { static const char* getName(){ return "*error*"; } };
+ template<> struct OperatorTraits { static const char* getName(){ return "=="; } };
+ template<> struct OperatorTraits { static const char* getName(){ return "!="; } };
+ template<> struct OperatorTraits { static const char* getName(){ return "<"; } };
+ template<> struct OperatorTraits { static const char* getName(){ return ">"; } };
+ template<> struct OperatorTraits { static const char* getName(){ return "<="; } };
+ template<> struct OperatorTraits{ static const char* getName(){ return ">="; } };
+
+ template
+ inline T& opCast(T const& t) { return const_cast(t); }
+
+// nullptr_t support based on pull request #154 from Konstantin Baumann
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+ inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+ // So the compare overloads can be operator agnostic we convey the operator as a template
+ // enum, which is used to specialise an Evaluator for doing the comparison.
+ template
+ class Evaluator{};
+
+ template
+ struct Evaluator {
+ static bool evaluate( T1 const& lhs, T2 const& rhs) {
+ return opCast( lhs ) == opCast( rhs );
+ }
+ };
+ template
+ struct Evaluator {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) != opCast( rhs );
+ }
+ };
+ template
+ struct Evaluator {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) < opCast( rhs );
+ }
+ };
+ template
+ struct Evaluator {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) > opCast( rhs );
+ }
+ };
+ template
+ struct Evaluator {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) >= opCast( rhs );
+ }
+ };
+ template
+ struct Evaluator {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return opCast( lhs ) <= opCast( rhs );
+ }
+ };
+
+ template
+ bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
+ return Evaluator::evaluate( lhs, rhs );
+ }
+
+ // This level of indirection allows us to specialise for integer types
+ // to avoid signed/ unsigned warnings
+
+ // "base" overload
+ template
+ bool compare( T1 const& lhs, T2 const& rhs ) {
+ return Evaluator::evaluate( lhs, rhs );
+ }
+
+ // unsigned X to int
+ template bool compare( unsigned int lhs, int rhs ) {
+ return applyEvaluator( lhs, static_cast( rhs ) );
+ }
+ template bool compare( unsigned long lhs, int rhs ) {
+ return applyEvaluator( lhs, static_cast( rhs ) );
+ }
+ template bool compare( unsigned char lhs, int rhs ) {
+ return applyEvaluator( lhs, static_cast( rhs ) );
+ }
+
+ // unsigned X to long
+ template bool compare( unsigned int lhs, long rhs ) {
+ return applyEvaluator( lhs, static_cast( rhs ) );
+ }
+ template bool compare( unsigned long lhs, long rhs ) {
+ return applyEvaluator( lhs, static_cast( rhs ) );
+ }
+ template bool compare( unsigned char lhs, long rhs ) {
+ return applyEvaluator( lhs, static_cast( rhs ) );
+ }
+
+ // int to unsigned X
+ template bool compare( int lhs, unsigned int rhs ) {
+ return applyEvaluator( static_cast( lhs ), rhs );
+ }
+ template bool compare( int lhs, unsigned long rhs ) {
+ return applyEvaluator( static_cast( lhs ), rhs );
+ }
+ template bool compare( int lhs, unsigned char rhs ) {
+ return applyEvaluator( static_cast( lhs ), rhs );
+ }
+
+ // long to unsigned X
+ template bool compare( long lhs, unsigned int rhs ) {
+ return applyEvaluator( static_cast( lhs ), rhs );
+ }
+ template bool compare( long lhs, unsigned long rhs ) {
+ return applyEvaluator( static_cast( lhs ), rhs );
+ }
+ template bool compare( long lhs, unsigned char rhs ) {
+ return applyEvaluator( static_cast( lhs ), rhs );
+ }
+
+ // pointer to long (when comparing against NULL)
+ template bool compare( long lhs, T* rhs ) {
+ return Evaluator::evaluate( reinterpret_cast( lhs ), rhs );
+ }
+ template bool compare( T* lhs, long rhs ) {
+ return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) );
+ }
+
+ // pointer to int (when comparing against NULL)
+ template bool compare( int lhs, T* rhs ) {
+ return Evaluator::evaluate( reinterpret_cast( lhs ), rhs );
+ }
+ template bool compare( T* lhs, int rhs ) {
+ return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) );
+ }
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+ // pointer to nullptr_t (when comparing against nullptr)
+ template bool compare( std::nullptr_t, T* rhs ) {
+ return Evaluator::evaluate( NULL, rhs );
+ }
+ template bool compare( T* lhs, std::nullptr_t ) {
+ return Evaluator::evaluate( lhs, NULL );
+ }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+} // end of namespace Internal
+} // end of namespace Catch
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// #included from: catch_tostring.h
+#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
+
+// #included from: catch_sfinae.hpp
+#define TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED
+
+// Try to detect if the current compiler supports SFINAE
+
+namespace Catch {
+
+ struct TrueType {
+ static const bool value = true;
+ typedef void Enable;
+ char sizer[1];
+ };
+ struct FalseType {
+ static const bool value = false;
+ typedef void Disable;
+ char sizer[2];
+ };
+
+#ifdef CATCH_CONFIG_SFINAE
+
+ template struct NotABooleanExpression;
+
+ template struct If : NotABooleanExpression {};
+ template<> struct If : TrueType {};
+ template<> struct If : FalseType {};
+
+ template struct SizedIf;
+ template<> struct SizedIf : TrueType {};
+ template<> struct SizedIf : FalseType {};
+
+#endif // CATCH_CONFIG_SFINAE
+
+} // end namespace Catch
+
+#include
+#include
+#include
+#include
+#include
+
+#ifdef __OBJC__
+// #included from: catch_objc_arc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
+
+#import
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+ [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+#endif
+
+#ifdef CATCH_CPP11_OR_GREATER
+#include
+#include
+#endif
+
+namespace Catch {
+
+// Why we're here.
+template
+std::string toString( T const& value );
+
+// Built in overloads
+
+std::string toString( std::string const& value );
+std::string toString( std::wstring const& value );
+std::string toString( const char* const value );
+std::string toString( char* const value );
+std::string toString( const wchar_t* const value );
+std::string toString( wchar_t* const value );
+std::string toString( int value );
+std::string toString( unsigned long value );
+std::string toString( unsigned int value );
+std::string toString( const double value );
+std::string toString( const float value );
+std::string toString( bool value );
+std::string toString( char value );
+std::string toString( signed char value );
+std::string toString( unsigned char value );
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t );
+#endif
+
+#ifdef __OBJC__
+ std::string toString( NSString const * const& nsstring );
+ std::string toString( NSString * CATCH_ARC_STRONG const& nsstring );
+ std::string toString( NSObject* const& nsObject );
+#endif
+
+namespace Detail {
+
+ extern std::string unprintableString;
+
+// SFINAE is currently disabled by default for all compilers.
+// If the non SFINAE version of IsStreamInsertable is ambiguous for you
+// and your compiler supports SFINAE, try #defining CATCH_CONFIG_SFINAE
+#ifdef CATCH_CONFIG_SFINAE
+
+ template
+ class IsStreamInsertableHelper {
+ template struct TrueIfSizeable : TrueType {};
+
+ template
+ static TrueIfSizeable dummy(T2*);
+ static FalseType dummy(...);
+
+ public:
+ typedef SizedIf type;
+ };
+
+ template
+ struct IsStreamInsertable : IsStreamInsertableHelper::type {};
+
+#else
+
+ struct BorgType {
+ template BorgType( T const& );
+ };
+
+ TrueType& testStreamable( std::ostream& );
+ FalseType testStreamable( FalseType );
+
+ FalseType operator<<( std::ostream const&, BorgType const& );
+
+ template
+ struct IsStreamInsertable {
+ static std::ostream &s;
+ static T const&t;
+ enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
+ };
+
+#endif
+
+#if defined(CATCH_CPP11_OR_GREATER)
+ template::value
+ >
+ struct EnumStringMaker
+ {
+ static std::string convert( T const& ) { return unprintableString; }
+ };
+
+ template
+ struct EnumStringMaker
+ {
+ static std::string convert( T const& v )
+ {
+ return ::Catch::toString(
+ static_cast::type>(v)
+ );
+ }
+ };
+#endif
+ template
+ struct StringMakerBase {
+#if defined(CATCH_CPP11_OR_GREATER)
+ template
+ static std::string convert( T const& v )
+ {
+ return EnumStringMaker::convert( v );
+ }
+#else
+ template
+ static std::string convert( T const& ) { return unprintableString; }
+#endif
+ };
+
+ template<>
+ struct StringMakerBase {
+ template
+ static std::string convert( T const& _value ) {
+ std::ostringstream oss;
+ oss << _value;
+ return oss.str();
+ }
+ };
+
+ std::string rawMemoryToString( const void *object, std::size_t size );
+
+ template
+ inline std::string rawMemoryToString( const T& object ) {
+ return rawMemoryToString( &object, sizeof(object) );
+ }
+
+} // end namespace Detail
+
+template
+struct StringMaker :
+ Detail::StringMakerBase::value> {};
+
+template
+struct StringMaker {
+ template
+ static std::string convert( U* p ) {
+ if( !p )
+ return INTERNAL_CATCH_STRINGIFY( NULL );
+ else
+ return Detail::rawMemoryToString( p );
+ }
+};
+
+template
+struct StringMaker {
+ static std::string convert( R C::* p ) {
+ if( !p )
+ return INTERNAL_CATCH_STRINGIFY( NULL );
+ else
+ return Detail::rawMemoryToString( p );
+ }
+};
+
+namespace Detail {
+ template
+ std::string rangeToString( InputIterator first, InputIterator last );
+}
+
+//template
+//struct StringMaker > {
+// static std::string convert( std::vector const& v ) {
+// return Detail::rangeToString( v.begin(), v.end() );
+// }
+//};
+
+template
+std::string toString( std::vector const& v ) {
+ return Detail::rangeToString( v.begin(), v.end() );
+}
+
+#ifdef CATCH_CPP11_OR_GREATER
+
+// toString for tuples
+namespace TupleDetail {
+ template<
+ typename Tuple,
+ std::size_t N = 0,
+ bool = (N < std::tuple_size::value)
+ >
+ struct ElementPrinter {
+ static void print( const Tuple& tuple, std::ostream& os )
+ {
+ os << ( N ? ", " : " " )
+ << Catch::toString(std::get(tuple));
+ ElementPrinter::print(tuple,os);
+ }
+ };
+
+ template<
+ typename Tuple,
+ std::size_t N
+ >
+ struct ElementPrinter {
+ static void print( const Tuple&, std::ostream& ) {}
+ };
+
+}
+
+template
+struct StringMaker> {
+
+ static std::string convert( const std::tuple& tuple )
+ {
+ std::ostringstream os;
+ os << '{';
+ TupleDetail::ElementPrinter>::print( tuple, os );
+ os << " }";
+ return os.str();
+ }
+};
+#endif
+
+namespace Detail {
+ template
+ std::string makeString( T const& value ) {
+ return StringMaker::convert( value );
+ }
+} // end namespace Detail
+
+/// \brief converts any type to a string
+///
+/// The default template forwards on to ostringstream - except when an
+/// ostringstream overload does not exist - in which case it attempts to detect
+/// that and writes {?}.
+/// Overload (not specialise) this template for custom typs that you don't want
+/// to provide an ostream overload for.
+template
+std::string toString( T const& value ) {
+ return StringMaker::convert( value );
+}
+
+ namespace Detail {
+ template
+ std::string rangeToString( InputIterator first, InputIterator last ) {
+ std::ostringstream oss;
+ oss << "{ ";
+ if( first != last ) {
+ oss << Catch::toString( *first );
+ for( ++first ; first != last ; ++first )
+ oss << ", " << Catch::toString( *first );
+ }
+ oss << " }";
+ return oss.str();
+ }
+}
+
+} // end namespace Catch
+
+namespace Catch {
+
+// Wraps the LHS of an expression and captures the operator and RHS (if any) -
+// wrapping them all in a ResultBuilder object
+template
+class ExpressionLhs {
+ ExpressionLhs& operator = ( ExpressionLhs const& );
+# ifdef CATCH_CPP11_OR_GREATER
+ ExpressionLhs& operator = ( ExpressionLhs && ) = delete;
+# endif
+
+public:
+ ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {}
+# ifdef CATCH_CPP11_OR_GREATER
+ ExpressionLhs( ExpressionLhs const& ) = default;
+ ExpressionLhs( ExpressionLhs && ) = default;
+# endif
+
+ template
+ ResultBuilder& operator == ( RhsT const& rhs ) {
+ return captureExpression( rhs );
+ }
+
+ template
+ ResultBuilder& operator != ( RhsT const& rhs ) {
+ return captureExpression( rhs );
+ }
+
+ template
+ ResultBuilder& operator < ( RhsT const& rhs ) {
+ return captureExpression( rhs );
+ }
+
+ template
+ ResultBuilder& operator > ( RhsT const& rhs ) {
+ return captureExpression( rhs );
+ }
+
+ template
+ ResultBuilder& operator <= ( RhsT const& rhs ) {
+ return captureExpression( rhs );
+ }
+
+ template
+ ResultBuilder& operator >= ( RhsT const& rhs ) {
+ return captureExpression( rhs );
+ }
+
+ ResultBuilder& operator == ( bool rhs ) {
+ return captureExpression( rhs );
+ }
+
+ ResultBuilder& operator != ( bool rhs ) {
+ return captureExpression( rhs );
+ }
+
+ void endExpression() {
+ bool value = m_lhs ? true : false;
+ m_rb
+ .setLhs( Catch::toString( value ) )
+ .setResultType( value )
+ .endExpression();
+ }
+
+ // Only simple binary expressions are allowed on the LHS.
+ // If more complex compositions are required then place the sub expression in parentheses
+ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& );
+ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& );
+ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& );
+ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& );
+ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+ template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+private:
+ template
+ ResultBuilder& captureExpression( RhsT const& rhs ) {
+ return m_rb
+ .setResultType( Internal::compare( m_lhs, rhs ) )
+ .setLhs( Catch::toString( m_lhs ) )
+ .setRhs( Catch::toString( rhs ) )
+ .setOp( Internal::OperatorTraits::getName() );
+ }
+
+private:
+ ResultBuilder& m_rb;
+ T m_lhs;
+};
+
+} // end namespace Catch
+
+
+namespace Catch {
+
+ template
+ inline ExpressionLhs ResultBuilder::operator->* ( T const& operand ) {
+ return ExpressionLhs( *this, operand );
+ }
+
+ inline ExpressionLhs ResultBuilder::operator->* ( bool value ) {
+ return ExpressionLhs( *this, value );
+ }
+
+} // namespace Catch
+
+// #included from: catch_message.h
+#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
+
+#include
+
+namespace Catch {
+
+ struct MessageInfo {
+ MessageInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ ResultWas::OfType _type );
+
+ std::string macroName;
+ SourceLineInfo lineInfo;
+ ResultWas::OfType type;
+ std::string message;
+ unsigned int sequence;
+
+ bool operator == ( MessageInfo const& other ) const {
+ return sequence == other.sequence;
+ }
+ bool operator < ( MessageInfo const& other ) const {
+ return sequence < other.sequence;
+ }
+ private:
+ static unsigned int globalCount;
+ };
+
+ struct MessageBuilder {
+ MessageBuilder( std::string const& macroName,
+ SourceLineInfo const& lineInfo,
+ ResultWas::OfType type )
+ : m_info( macroName, lineInfo, type )
+ {}
+
+ template
+ MessageBuilder& operator << ( T const& value ) {
+ m_stream << value;
+ return *this;
+ }
+
+ MessageInfo m_info;
+ std::ostringstream m_stream;
+ };
+
+ class ScopedMessage {
+ public:
+ ScopedMessage( MessageBuilder const& builder );
+ ScopedMessage( ScopedMessage const& other );
+ ~ScopedMessage();
+
+ MessageInfo m_info;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_capture.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
+
+#include
+
+namespace Catch {
+
+ class TestCase;
+ class AssertionResult;
+ struct AssertionInfo;
+ struct SectionInfo;
+ struct MessageInfo;
+ class ScopedMessageBuilder;
+ struct Counts;
+
+ struct IResultCapture {
+
+ virtual ~IResultCapture();
+
+ virtual void assertionEnded( AssertionResult const& result ) = 0;
+ virtual bool sectionStarted( SectionInfo const& sectionInfo,
+ Counts& assertions ) = 0;
+ virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0;
+ virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+ virtual void popScopedMessage( MessageInfo const& message ) = 0;
+
+ virtual std::string getCurrentTestName() const = 0;
+ virtual const AssertionResult* getLastResult() const = 0;
+
+ virtual void handleFatalErrorCondition( std::string const& message ) = 0;
+ };
+
+ IResultCapture& getResultCapture();
+}
+
+// #included from: catch_debugger.h
+#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
+
+// #included from: catch_platform.h
+#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
+
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+#define CATCH_PLATFORM_MAC
+#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#define CATCH_PLATFORM_IPHONE
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define CATCH_PLATFORM_WINDOWS
+#endif
+
+#include
+
+namespace Catch{
+
+ bool isDebuggerActive();
+ void writeToDebugConsole( std::string const& text );
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+ // The following code snippet based on:
+ // http://cocoawithlove.com/2008/03/break-into-debugger.html
+ #ifdef DEBUG
+ #if defined(__ppc64__) || defined(__ppc__)
+ #define CATCH_BREAK_INTO_DEBUGGER() \
+ if( Catch::isDebuggerActive() ) { \
+ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+ : : : "memory","r0","r3","r4" ); \
+ }
+ #else
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );}
+ #endif
+ #endif
+
+#elif defined(_MSC_VER)
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); }
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); }
+#endif
+
+#ifndef CATCH_BREAK_INTO_DEBUGGER
+#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
+#endif
+
+// #included from: catch_interfaces_runner.h
+#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
+
+namespace Catch {
+ class TestCase;
+
+ struct IRunner {
+ virtual ~IRunner();
+ virtual bool aborting() const = 0;
+ };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// In the event of a failure works out if the debugger needs to be invoked
+// and/or an exception thrown and takes appropriate action.
+// This needs to be done as a macro so the debugger will stop in the user
+// source code rather than in Catch library code
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
+ resultBuilder.react();
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ ( __catchResult->*expr ).endExpression(); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
+ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+ if( Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \
+ INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+ if( !Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ expr; \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ expr; \
+ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+ } \
+ catch( ... ) { \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ else \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ expr; \
+ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+ } \
+ catch( exceptionType ) { \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ else \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
+ __catchResult.captureResult( messageType ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+#else
+ #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+ __catchResult << log + ::Catch::StreamEndStop(); \
+ __catchResult.captureResult( messageType ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_INFO( log, macroName ) \
+ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \
+ try { \
+ std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \
+ __catchResult \
+ .setLhs( Catch::toString( arg ) ) \
+ .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \
+ .setOp( "matches" ) \
+ .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \
+ __catchResult.captureExpression(); \
+ } catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
+
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
+
+namespace Catch {
+
+ struct SectionInfo {
+ SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name,
+ std::string const& _description = std::string() );
+
+ std::string name;
+ std::string description;
+ SourceLineInfo lineInfo;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_totals.hpp
+#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
+
+#include
+
+namespace Catch {
+
+ struct Counts {
+ Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
+
+ Counts operator - ( Counts const& other ) const {
+ Counts diff;
+ diff.passed = passed - other.passed;
+ diff.failed = failed - other.failed;
+ diff.failedButOk = failedButOk - other.failedButOk;
+ return diff;
+ }
+ Counts& operator += ( Counts const& other ) {
+ passed += other.passed;
+ failed += other.failed;
+ failedButOk += other.failedButOk;
+ return *this;
+ }
+
+ std::size_t total() const {
+ return passed + failed + failedButOk;
+ }
+ bool allPassed() const {
+ return failed == 0 && failedButOk == 0;
+ }
+ bool allOk() const {
+ return failed == 0;
+ }
+
+ std::size_t passed;
+ std::size_t failed;
+ std::size_t failedButOk;
+ };
+
+ struct Totals {
+
+ Totals operator - ( Totals const& other ) const {
+ Totals diff;
+ diff.assertions = assertions - other.assertions;
+ diff.testCases = testCases - other.testCases;
+ return diff;
+ }
+
+ Totals delta( Totals const& prevTotals ) const {
+ Totals diff = *this - prevTotals;
+ if( diff.assertions.failed > 0 )
+ ++diff.testCases.failed;
+ else if( diff.assertions.failedButOk > 0 )
+ ++diff.testCases.failedButOk;
+ else
+ ++diff.testCases.passed;
+ return diff;
+ }
+
+ Totals& operator += ( Totals const& other ) {
+ assertions += other.assertions;
+ testCases += other.testCases;
+ return *this;
+ }
+
+ Counts assertions;
+ Counts testCases;
+ };
+}
+
+// #included from: catch_timer.h
+#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
+
+#ifdef CATCH_PLATFORM_WINDOWS
+typedef unsigned long long uint64_t;
+#else
+#include
+#endif
+
+namespace Catch {
+
+ class Timer {
+ public:
+ Timer() : m_ticks( 0 ) {}
+ void start();
+ unsigned int getElapsedMicroseconds() const;
+ unsigned int getElapsedMilliseconds() const;
+ double getElapsedSeconds() const;
+
+ private:
+ uint64_t m_ticks;
+ };
+
+} // namespace Catch
+
+#include
+
+namespace Catch {
+
+ class Section : NonCopyable {
+ public:
+ Section( SectionInfo const& info );
+ ~Section();
+
+ // This indicates whether the section should be executed or not
+ operator bool() const;
+
+ private:
+ SectionInfo m_info;
+
+ std::string m_name;
+ Counts m_assertions;
+ bool m_sectionIncluded;
+ Timer m_timer;
+ };
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define INTERNAL_CATCH_SECTION( ... ) \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
+#else
+ #define INTERNAL_CATCH_SECTION( name, desc ) \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
+#endif
+
+// #included from: internal/catch_generators.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
+
+#include
+#include
+#include
+#include
+
+namespace Catch {
+
+template
+struct IGenerator {
+ virtual ~IGenerator() {}
+ virtual T getValue( std::size_t index ) const = 0;
+ virtual std::size_t size () const = 0;
+};
+
+template
+class BetweenGenerator : public IGenerator {
+public:
+ BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){}
+
+ virtual T getValue( std::size_t index ) const {
+ return m_from+static_cast( index );
+ }
+
+ virtual std::size_t size() const {
+ return static_cast( 1+m_to-m_from );
+ }
+
+private:
+
+ T m_from;
+ T m_to;
+};
+
+template
+class ValuesGenerator : public IGenerator {
+public:
+ ValuesGenerator(){}
+
+ void add( T value ) {
+ m_values.push_back( value );
+ }
+
+ virtual T getValue( std::size_t index ) const {
+ return m_values[index];
+ }
+
+ virtual std::size_t size() const {
+ return m_values.size();
+ }
+
+private:
+ std::vector m_values;
+};
+
+template
+class CompositeGenerator {
+public:
+ CompositeGenerator() : m_totalSize( 0 ) {}
+
+ // *** Move semantics, similar to auto_ptr ***
+ CompositeGenerator( CompositeGenerator& other )
+ : m_fileInfo( other.m_fileInfo ),
+ m_totalSize( 0 )
+ {
+ move( other );
+ }
+
+ CompositeGenerator& setFileInfo( const char* fileInfo ) {
+ m_fileInfo = fileInfo;
+ return *this;
+ }
+
+ ~CompositeGenerator() {
+ deleteAll( m_composed );
+ }
+
+ operator T () const {
+ size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
+
+ typename std::vector*>::const_iterator it = m_composed.begin();
+ typename std::vector*>::const_iterator itEnd = m_composed.end();
+ for( size_t index = 0; it != itEnd; ++it )
+ {
+ const IGenerator* generator = *it;
+ if( overallIndex >= index && overallIndex < index + generator->size() )
+ {
+ return generator->getValue( overallIndex-index );
+ }
+ index += generator->size();
+ }
+ CATCH_INTERNAL_ERROR( "Indexed past end of generated range" );
+ return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so
+ }
+
+ void add( const IGenerator* generator ) {
+ m_totalSize += generator->size();
+ m_composed.push_back( generator );
+ }
+
+ CompositeGenerator& then( CompositeGenerator& other ) {
+ move( other );
+ return *this;
+ }
+
+ CompositeGenerator& then( T value ) {
+ ValuesGenerator* valuesGen = new ValuesGenerator();
+ valuesGen->add( value );
+ add( valuesGen );
+ return *this;
+ }
+
+private:
+
+ void move( CompositeGenerator& other ) {
+ std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) );
+ m_totalSize += other.m_totalSize;
+ other.m_composed.clear();
+ }
+
+ std::vector*> m_composed;
+ std::string m_fileInfo;
+ size_t m_totalSize;
+};
+
+namespace Generators
+{
+ template
+ CompositeGenerator between( T from, T to ) {
+ CompositeGenerator generators;
+ generators.add( new BetweenGenerator( from, to ) );
+ return generators;
+ }
+
+ template
+ CompositeGenerator values( T val1, T val2 ) {
+ CompositeGenerator generators;
+ ValuesGenerator* valuesGen = new ValuesGenerator();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+ template
+ CompositeGenerator values( T val1, T val2, T val3 ){
+ CompositeGenerator generators;
+ ValuesGenerator* valuesGen = new ValuesGenerator();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ valuesGen->add( val3 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+ template
+ CompositeGenerator