This commit is contained in:
Daniel Sipka 2015-04-09 20:41:27 +02:00
parent 9637d0ec7b
commit eb98985815
207 changed files with 11155 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea

13
CMakeLists.txt Normal file
View File

@ -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)

13
include/mstch.h Normal file
View File

@ -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<std::string,std::string>& partials = std::map<std::string,std::string>());
}
#endif // _MSTCH_H_

19
include/types.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef _MSTCH_TYPES_H_
#define _MSTCH_TYPES_H_
#include <vector>
#include <map>
#include <string>
#include <boost/variant.hpp>
namespace mstch {
using node = boost::make_recursive_variant<
boost::blank, std::string, int, bool,
std::map<const std::string,boost::recursive_variant_>,
std::vector<boost::recursive_variant_>>::type;
using object = std::map<const std::string,node>;
using array = std::vector<node>;
}
#endif //_MSTCH_TYPES_H_

33
src/mstch.cpp Normal file
View File

@ -0,0 +1,33 @@
#include <regex>
#include <iostream>
#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<std::string,std::string>& partials)
{
return render_context(root_object, partials).render(strip_whitespace(tmplt));
}

49
src/render_context.cpp Normal file
View File

@ -0,0 +1,49 @@
#include "render_context.h"
#include "state/render_state.h"
#include "state/outside_section.h"
#include <regex>
using namespace mstch;
const mstch::node render_context::null_node;
render_context::render_context(const mstch::object &object, const std::map<std::string,std::string>& 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<object> &current_objects) {
/*if(token != "." && token.find('.') != std::string::npos) {
return find_node(token.substr(token.rfind('.') + 1),
{boost::get<object>(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();
}

31
src/render_context.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef _MSTCH_RENDER_CONTEXT_H_
#define _MSTCH_RENDER_CONTEXT_H_
#include <deque>
#include <sstream>
#include <string>
#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<object>& current_objects);
const std::map<std::string,std::string>& partials;
std::deque<mstch::object> objects;
std::unique_ptr<state::render_state> state;
public:
render_context(const mstch::object& object, const std::map<std::string,std::string>& 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<class T, class... Args>
void set_state(Args&&... args) {
state = std::unique_ptr<state::render_state>(new T(std::forward<Args>(args)...));
}
};
}
#endif //_MSTCH_RENDER_CONTEXT_H_

View File

@ -0,0 +1,45 @@
#include <visitor/render_section.h>
#include <visitor/to_json.h>
#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<outside_section>();
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 "";
}

View File

@ -0,0 +1,21 @@
#ifndef _MSTCH_IN_INVERTED_SECTION_H_
#define _MSTCH_IN_INVERTED_SECTION_H_
#include <sstream>
#include "render_state.h"
namespace mstch {
namespace state {
class in_inverted_section : public render_state, public std::enable_shared_from_this<in_inverted_section> {
private:
const std::string section_name;
std::ostringstream section_text;
int skipped_openings;
public:
in_inverted_section(const std::string &section_name);
std::string render(render_context &context, const token &token) override;
};
}
}
#endif //_MSTCH_IN_INVERTED_SECTION_H_

38
src/state/in_section.cpp Normal file
View File

@ -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<outside_section>();
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 "";
}

21
src/state/in_section.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef _MSTCH_IN_SECTION_H_
#define _MSTCH_IN_SECTION_H_
#include "render_state.h"
#include <sstream>
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_

View File

@ -0,0 +1,31 @@
#include <visitor/render_node.h>
#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<in_section>(token.content());
break;
case token_type::inverted_section_open:
context.set_state<in_inverted_section>(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 "";
}

View File

@ -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_

18
src/state/render_state.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef _MSTCH_RENDER_STATE_H_
#define _MSTCH_RENDER_STATE_H_
#include <memory>
#include <token.h>
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_

54
src/token.cpp Normal file
View File

@ -0,0 +1,54 @@
#include "token.h"
#include "utils.h"
#include <regex>
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;
}

25
src/token.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef _MSTCH_TOKEN_H_
#define _MSTCH_TOKEN_H_
#include <string>
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_

36
src/utils.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "utils.h"
#include <algorithm>
std::string& mstch::ltrim(std::string& s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(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<int, int>(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, "&", "&amp;");
str = replace_all(str, "'", "&#39;");
str = replace_all(str, "\"", "&quot;");
str = replace_all(str, "<", "&lt;");
str = replace_all(str, ">", "&gt;");
str = replace_all(str, "/", "&#x2F;");
return str;
}

14
src/utils.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef _MSTCH_UTILS_H_
#define _MSTCH_UTILS_H_
#include <string>
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_

View File

@ -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;
}

View File

@ -0,0 +1,23 @@
#ifndef _MSTCH_IS_NODE_EMPTY_H_
#define _MSTCH_IS_NODE_EMPTY_H_
#include <boost/variant/static_visitor.hpp>
#include <boost/blank.hpp>
#include "types.h"
namespace mstch {
namespace visitor {
class is_node_empty: public boost::static_visitor<bool> {
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_

View File

@ -0,0 +1,31 @@
#include <utils.h>
#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 "";
}

26
src/visitor/render_node.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef _MSTCH_RENDER_NODE_H_
#define _MSTCH_RENDER_NODE_H_
#include <boost/variant/static_visitor.hpp>
#include <boost/blank.hpp>
#include "types.h"
namespace mstch {
namespace visitor {
class render_node: public boost::static_visitor<std::string> {
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_

View File

@ -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);
}

View File

@ -0,0 +1,28 @@
#ifndef _MSTCH_RENDER_SECTION_H_
#define _MSTCH_RENDER_SECTION_H_
#include <boost/variant/static_visitor.hpp>
#include <boost/blank.hpp>
#include <render_context.h>
#include "types.h"
namespace mstch {
namespace visitor {
class render_section: public boost::static_visitor<std::string> {
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_

44
src/visitor/to_json.cpp Normal file
View File

@ -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();
}

24
src/visitor/to_json.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef _MSTCH_TO_JSON_H_
#define _MSTCH_TO_JSON_H_
#include <boost/variant/static_visitor.hpp>
#include <boost/blank.hpp>
#include <render_context.h>
#include "types.h"
namespace mstch {
namespace visitor {
class to_json: public boost::static_visitor<std::string> {
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_

View File

@ -0,0 +1,3 @@
auto data = mstch::object{
{"message", std::string{"Some <code>"}}
};

View File

@ -0,0 +1 @@
{{&message}}

View File

@ -0,0 +1 @@
Some <code>

4
test/data/apostrophe.h Normal file
View File

@ -0,0 +1,4 @@
auto data = mstch::object{
{"apos", std::string{"'"}},
{"control", std::string{"X"}}
};

View File

@ -0,0 +1 @@
{{apos}}{{control}}

1
test/data/apostrophe.txt Normal file
View File

@ -0,0 +1 @@
&#39;X

View File

@ -0,0 +1,3 @@
auto data = mstch::object{
{"array_of_strings", mstch::array{std::string{"hello"}, std::string{"world"}}}
};

View File

@ -0,0 +1 @@
{{#array_of_strings}}{{.}} {{/array_of_strings}}

View File

@ -0,0 +1 @@
hello world

3
test/data/backslashes.h Normal file
View File

@ -0,0 +1,3 @@
auto data = mstch::object{
{"value", std::string{"\\abc"}}
};

View File

@ -0,0 +1,7 @@
* {{value}}
* {{{value}}}
* {{&value}}
<script>
foo = { bar: 'abc\"xyz\"' };
foo = { bar: 'x\'y' };
</script>

View File

@ -0,0 +1,7 @@
* \abc
* \abc
* \abc
<script>
foo = { bar: 'abc\"xyz\"' };
foo = { bar: 'x\'y' };
</script>

View File

@ -0,0 +1,3 @@
auto data = mstch::object{
{"tag", std::string{"yo"}}
};

View File

@ -0,0 +1 @@
{{tag}} foo

View File

@ -0,0 +1 @@
yo foo

View File

@ -0,0 +1,3 @@
auto data = mstch::object{
{"length", std::string{"hello"}}
};

View File

@ -0,0 +1 @@
{{#length}}The length variable is: {{length}}{{/length}}

View File

@ -0,0 +1 @@
The length variable is: hello

View File

@ -0,0 +1,8 @@
auto data = mstch::object{
{"outer", mstch::object{
{"id", 1},
{"second", mstch::object{
{"nothing", 2}
}}
}}
};

View File

@ -0,0 +1 @@
{{#outer}}{{#second}}{{id}}{{/second}}{{/outer}}

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
({
"foo": "foooooooooooooo",
"bar": "<b>bar!</b>"
})

View File

@ -0,0 +1 @@
{{=<% %>=}}<% foo %> {{foo}} <%{bar}%> {{{bar}}}

View File

@ -0,0 +1 @@
foooooooooooooo {{foo}} <b>bar!</b> {{{bar}}}

View File

@ -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!."
})

View File

@ -0,0 +1,7 @@
{{=<% %>=}}*
<% first %>
* <% second %>
<%=| |=%>
* | third |
|={{ }}=|
* {{ fourth }}

View File

@ -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!.

View File

@ -0,0 +1,4 @@
auto data = mstch::object{
{"bedrooms", true},
{"total", 1}
};

View File

@ -0,0 +1 @@
{{#bedrooms}}{{total}}{{/bedrooms}} BED

View File

@ -0,0 +1 @@
1 BED

View File

@ -0,0 +1,5 @@
auto data = mstch::object{
{"foo", true},
{"bar", std::string{"{{win}}"}},
{"win", std::string{"FAIL"}}
};

View File

@ -0,0 +1 @@
{{#foo}}{{bar}}{{/foo}}

View File

@ -0,0 +1 @@
{{win}}

3
test/data/empty_list.h Normal file
View File

@ -0,0 +1,3 @@
auto data = mstch::object{
{"jobs", mstch::array{}}
};

View File

@ -0,0 +1,4 @@
These are the jobs:
{{#jobs}}
{{.}}
{{/jobs}}

1
test/data/empty_list.txt Normal file
View File

@ -0,0 +1 @@
These are the jobs:

View File

@ -0,0 +1 @@
auto data = mstch::object{};

View File

@ -0,0 +1 @@
{{#foo}}{{/foo}}foo{{#bar}}{{/bar}}

View File

@ -0,0 +1 @@
foo

6
test/data/empty_string.h Normal file
View File

@ -0,0 +1,6 @@
auto data = mstch::object{
{"description", std::string{"That is all!"}},
{"child", mstch::object{
{"description", std::string{""}}
}}
};

View File

@ -0,0 +1 @@
{{description}}{{#child}}{{description}}{{/child}}

View File

@ -0,0 +1 @@
That is all!

View File

@ -0,0 +1 @@
auto data = mstch::object{};

View File

@ -0,0 +1 @@
<html><head></head><body><h1>Test</h1></body></html>

View File

@ -0,0 +1 @@
<html><head></head><body><h1>Test</h1></body></html>

View File

@ -0,0 +1,3 @@
auto data = mstch::object{
{"bar", 2}
};

View File

@ -0,0 +1 @@
{{foo}}

View File

6
test/data/falsy.h Normal file
View File

@ -0,0 +1,6 @@
auto data = mstch::object{
{"emptyString", std::string{""}},
{"emptyArray", mstch::array{}},
{"zero", 0},
{"null", mstch::node{}}
};

8
test/data/falsy.mustache Normal file
View File

@ -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}}

8
test/data/falsy.txt Normal file
View File

@ -0,0 +1,8 @@
inverted empty string
inverted empty array
inverted zero
inverted null

8
test/data/falsy_array.h Normal file
View File

@ -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"}}
}}
};

View File

@ -0,0 +1,3 @@
{{#list}}
{{#.}}{{#.}}{{.}}{{/.}}{{^.}}inverted {{/.}}{{/.}}
{{/list}}

View File

@ -0,0 +1,4 @@
inverted emptyString
inverted emptyArray
inverted zero
inverted null

View File

@ -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"}}}
}}
}
}}
};

View File

@ -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}}

View File

@ -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

View File

@ -0,0 +1,8 @@
auto data = mstch::object{
{"data", mstch::object{
{"author", mstch::object{
{"twitter_id", 819606},
{"name", std::string{"janl"}}
}}
}}
};

View File

@ -0,0 +1,7 @@
{{# data.author.twitter_id }}
<meta name="twitter:site:id" content="{{.}}">
{{/ data.author.twitter_id }}
{{# data.author.name }}
<meta name="twitter:site" content="{{.}}">
{{/ data.author.name }}

View File

@ -0,0 +1,3 @@
<meta name="twitter:site:id" content="819606">
<meta name="twitter:site" content="janl">

3
test/data/included_tag.h Normal file
View File

@ -0,0 +1,3 @@
auto data = mstch::object{
{"html", std::string{"I like {{mustache}}"}}
};

View File

@ -0,0 +1 @@
You said "{{{html}}}" today

View File

@ -0,0 +1 @@
You said "I like {{mustache}}" today

View File

@ -0,0 +1,3 @@
auto data = mstch::object{
{"repos", mstch::array{}}
};

View File

@ -0,0 +1,3 @@
{{#repos}}<b>{{name}}</b>{{/repos}}
{{^repos}}No repos :({{/repos}}
{{^nothin}}Hello!{{/nothin}}

View File

@ -0,0 +1,3 @@
No repos :(
Hello!

View File

@ -0,0 +1,5 @@
auto data = mstch::object{
{"person?", mstch::object{
{"name", std::string{"Jon"}}
}}
};

View File

@ -0,0 +1,3 @@
{{#person?}}
Hi {{name}}!
{{/person?}}

View File

@ -0,0 +1 @@
Hi Jon!

View File

@ -0,0 +1,7 @@
({
number: function(text, render) {
return function(text, render) {
return +render(text);
}
}
})

View File

@ -0,0 +1 @@
<p>{{#number}}0{{/number}}</p>

View File

@ -0,0 +1 @@
<p>0</p>

View File

@ -0,0 +1,5 @@
({
title: function () {
return "A Comedy of Errors";
}
})

View File

@ -0,0 +1 @@
<h1>{{title}}{{! just something interesting... or not... }}</h1>

Some files were not shown because too many files have changed in this diff Show More