import
This commit is contained in:
parent
9637d0ec7b
commit
eb98985815
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.idea
|
13
CMakeLists.txt
Normal file
13
CMakeLists.txt
Normal 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
13
include/mstch.h
Normal 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
19
include/types.h
Normal 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
33
src/mstch.cpp
Normal 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
49
src/render_context.cpp
Normal 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> ¤t_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
31
src/render_context.h
Normal 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_
|
45
src/state/in_inverted_section.cpp
Normal file
45
src/state/in_inverted_section.cpp
Normal 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 "";
|
||||||
|
}
|
21
src/state/in_inverted_section.h
Normal file
21
src/state/in_inverted_section.h
Normal 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 §ion_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
38
src/state/in_section.cpp
Normal 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
21
src/state/in_section.h
Normal 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_
|
31
src/state/outside_section.cpp
Normal file
31
src/state/outside_section.cpp
Normal 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 "";
|
||||||
|
}
|
15
src/state/outside_section.h
Normal file
15
src/state/outside_section.h
Normal 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
18
src/state/render_state.h
Normal 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
54
src/token.cpp
Normal 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
25
src/token.h
Normal 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
36
src/utils.cpp
Normal 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, "&", "&");
|
||||||
|
str = replace_all(str, "'", "'");
|
||||||
|
str = replace_all(str, "\"", """);
|
||||||
|
str = replace_all(str, "<", "<");
|
||||||
|
str = replace_all(str, ">", ">");
|
||||||
|
str = replace_all(str, "/", "/");
|
||||||
|
return str;
|
||||||
|
}
|
14
src/utils.h
Normal file
14
src/utils.h
Normal 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_
|
27
src/visitor/is_node_empty.cpp
Normal file
27
src/visitor/is_node_empty.cpp
Normal 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;
|
||||||
|
}
|
23
src/visitor/is_node_empty.h
Normal file
23
src/visitor/is_node_empty.h
Normal 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_
|
31
src/visitor/render_node.cpp
Normal file
31
src/visitor/render_node.cpp
Normal 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
26
src/visitor/render_node.h
Normal 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_
|
38
src/visitor/render_section.cpp
Normal file
38
src/visitor/render_section.cpp
Normal 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);
|
||||||
|
}
|
28
src/visitor/render_section.h
Normal file
28
src/visitor/render_section.h
Normal 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
44
src/visitor/to_json.cpp
Normal 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
24
src/visitor/to_json.h
Normal 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_
|
3
test/data/ampersand_escape.h
Normal file
3
test/data/ampersand_escape.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"message", std::string{"Some <code>"}}
|
||||||
|
};
|
1
test/data/ampersand_escape.mustache
Normal file
1
test/data/ampersand_escape.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{&message}}
|
1
test/data/ampersand_escape.txt
Normal file
1
test/data/ampersand_escape.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Some <code>
|
4
test/data/apostrophe.h
Normal file
4
test/data/apostrophe.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"apos", std::string{"'"}},
|
||||||
|
{"control", std::string{"X"}}
|
||||||
|
};
|
1
test/data/apostrophe.mustache
Normal file
1
test/data/apostrophe.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{apos}}{{control}}
|
1
test/data/apostrophe.txt
Normal file
1
test/data/apostrophe.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
'X
|
3
test/data/array_of_strings.h
Normal file
3
test/data/array_of_strings.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"array_of_strings", mstch::array{std::string{"hello"}, std::string{"world"}}}
|
||||||
|
};
|
1
test/data/array_of_strings.mustache
Normal file
1
test/data/array_of_strings.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{#array_of_strings}}{{.}} {{/array_of_strings}}
|
1
test/data/array_of_strings.txt
Normal file
1
test/data/array_of_strings.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
hello world
|
3
test/data/backslashes.h
Normal file
3
test/data/backslashes.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"value", std::string{"\\abc"}}
|
||||||
|
};
|
7
test/data/backslashes.mustache
Normal file
7
test/data/backslashes.mustache
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
* {{value}}
|
||||||
|
* {{{value}}}
|
||||||
|
* {{&value}}
|
||||||
|
<script>
|
||||||
|
foo = { bar: 'abc\"xyz\"' };
|
||||||
|
foo = { bar: 'x\'y' };
|
||||||
|
</script>
|
7
test/data/backslashes.txt
Normal file
7
test/data/backslashes.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
* \abc
|
||||||
|
* \abc
|
||||||
|
* \abc
|
||||||
|
<script>
|
||||||
|
foo = { bar: 'abc\"xyz\"' };
|
||||||
|
foo = { bar: 'x\'y' };
|
||||||
|
</script>
|
3
test/data/bug_11_eating_whitespace.h
Normal file
3
test/data/bug_11_eating_whitespace.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"tag", std::string{"yo"}}
|
||||||
|
};
|
1
test/data/bug_11_eating_whitespace.mustache
Normal file
1
test/data/bug_11_eating_whitespace.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{tag}} foo
|
1
test/data/bug_11_eating_whitespace.txt
Normal file
1
test/data/bug_11_eating_whitespace.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
yo foo
|
3
test/data/bug_length_property.h
Normal file
3
test/data/bug_length_property.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"length", std::string{"hello"}}
|
||||||
|
};
|
1
test/data/bug_length_property.mustache
Normal file
1
test/data/bug_length_property.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{#length}}The length variable is: {{length}}{{/length}}
|
1
test/data/bug_length_property.txt
Normal file
1
test/data/bug_length_property.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
The length variable is: hello
|
8
test/data/context_lookup.h
Normal file
8
test/data/context_lookup.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"outer", mstch::object{
|
||||||
|
{"id", 1},
|
||||||
|
{"second", mstch::object{
|
||||||
|
{"nothing", 2}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
};
|
1
test/data/context_lookup.mustache
Normal file
1
test/data/context_lookup.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{#outer}}{{#second}}{{id}}{{/second}}{{/outer}}
|
1
test/data/context_lookup.txt
Normal file
1
test/data/context_lookup.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
1
|
4
test/data/delimiter/changing_delimiters.js
Normal file
4
test/data/delimiter/changing_delimiters.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
({
|
||||||
|
"foo": "foooooooooooooo",
|
||||||
|
"bar": "<b>bar!</b>"
|
||||||
|
})
|
1
test/data/delimiter/changing_delimiters.mustache
Normal file
1
test/data/delimiter/changing_delimiters.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{=<% %>=}}<% foo %> {{foo}} <%{bar}%> {{{bar}}}
|
1
test/data/delimiter/changing_delimiters.txt
Normal file
1
test/data/delimiter/changing_delimiters.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
foooooooooooooo {{foo}} <b>bar!</b> {{{bar}}}
|
6
test/data/delimiter/delimiters.js
Normal file
6
test/data/delimiter/delimiters.js
Normal 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!."
|
||||||
|
})
|
7
test/data/delimiter/delimiters.mustache
Normal file
7
test/data/delimiter/delimiters.mustache
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{{=<% %>=}}*
|
||||||
|
<% first %>
|
||||||
|
* <% second %>
|
||||||
|
<%=| |=%>
|
||||||
|
* | third |
|
||||||
|
|={{ }}=|
|
||||||
|
* {{ fourth }}
|
5
test/data/delimiter/delimiters.txt
Normal file
5
test/data/delimiter/delimiters.txt
Normal 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!.
|
4
test/data/disappearing_whitespace.h
Normal file
4
test/data/disappearing_whitespace.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"bedrooms", true},
|
||||||
|
{"total", 1}
|
||||||
|
};
|
1
test/data/disappearing_whitespace.mustache
Normal file
1
test/data/disappearing_whitespace.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{#bedrooms}}{{total}}{{/bedrooms}} BED
|
1
test/data/disappearing_whitespace.txt
Normal file
1
test/data/disappearing_whitespace.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
1 BED
|
5
test/data/double_render.h
Normal file
5
test/data/double_render.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"foo", true},
|
||||||
|
{"bar", std::string{"{{win}}"}},
|
||||||
|
{"win", std::string{"FAIL"}}
|
||||||
|
};
|
1
test/data/double_render.mustache
Normal file
1
test/data/double_render.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{#foo}}{{bar}}{{/foo}}
|
1
test/data/double_render.txt
Normal file
1
test/data/double_render.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{win}}
|
3
test/data/empty_list.h
Normal file
3
test/data/empty_list.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"jobs", mstch::array{}}
|
||||||
|
};
|
4
test/data/empty_list.mustache
Normal file
4
test/data/empty_list.mustache
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
These are the jobs:
|
||||||
|
{{#jobs}}
|
||||||
|
{{.}}
|
||||||
|
{{/jobs}}
|
1
test/data/empty_list.txt
Normal file
1
test/data/empty_list.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
These are the jobs:
|
1
test/data/empty_sections.h
Normal file
1
test/data/empty_sections.h
Normal file
@ -0,0 +1 @@
|
|||||||
|
auto data = mstch::object{};
|
1
test/data/empty_sections.mustache
Normal file
1
test/data/empty_sections.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{#foo}}{{/foo}}foo{{#bar}}{{/bar}}
|
1
test/data/empty_sections.txt
Normal file
1
test/data/empty_sections.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo
|
6
test/data/empty_string.h
Normal file
6
test/data/empty_string.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"description", std::string{"That is all!"}},
|
||||||
|
{"child", mstch::object{
|
||||||
|
{"description", std::string{""}}
|
||||||
|
}}
|
||||||
|
};
|
1
test/data/empty_string.mustache
Normal file
1
test/data/empty_string.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{description}}{{#child}}{{description}}{{/child}}
|
1
test/data/empty_string.txt
Normal file
1
test/data/empty_string.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
That is all!
|
1
test/data/empty_template.h
Normal file
1
test/data/empty_template.h
Normal file
@ -0,0 +1 @@
|
|||||||
|
auto data = mstch::object{};
|
1
test/data/empty_template.mustache
Normal file
1
test/data/empty_template.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
<html><head></head><body><h1>Test</h1></body></html>
|
1
test/data/empty_template.txt
Normal file
1
test/data/empty_template.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
<html><head></head><body><h1>Test</h1></body></html>
|
3
test/data/error_not_found.h
Normal file
3
test/data/error_not_found.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"bar", 2}
|
||||||
|
};
|
1
test/data/error_not_found.mustache
Normal file
1
test/data/error_not_found.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{foo}}
|
0
test/data/error_not_found.txt
Normal file
0
test/data/error_not_found.txt
Normal file
6
test/data/falsy.h
Normal file
6
test/data/falsy.h
Normal 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
8
test/data/falsy.mustache
Normal 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
8
test/data/falsy.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
inverted empty string
|
||||||
|
|
||||||
|
inverted empty array
|
||||||
|
|
||||||
|
inverted zero
|
||||||
|
|
||||||
|
inverted null
|
8
test/data/falsy_array.h
Normal file
8
test/data/falsy_array.h
Normal 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"}}
|
||||||
|
}}
|
||||||
|
};
|
3
test/data/falsy_array.mustache
Normal file
3
test/data/falsy_array.mustache
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{{#list}}
|
||||||
|
{{#.}}{{#.}}{{.}}{{/.}}{{^.}}inverted {{/.}}{{/.}}
|
||||||
|
{{/list}}
|
4
test/data/falsy_array.txt
Normal file
4
test/data/falsy_array.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
inverted emptyString
|
||||||
|
inverted emptyArray
|
||||||
|
inverted zero
|
||||||
|
inverted null
|
19
test/data/grandparent_context.h
Normal file
19
test/data/grandparent_context.h
Normal 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"}}}
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
};
|
10
test/data/grandparent_context.mustache
Normal file
10
test/data/grandparent_context.mustache
Normal 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}}
|
17
test/data/grandparent_context.txt
Normal file
17
test/data/grandparent_context.txt
Normal 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
|
8
test/data/implicit_iterator.h
Normal file
8
test/data/implicit_iterator.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"data", mstch::object{
|
||||||
|
{"author", mstch::object{
|
||||||
|
{"twitter_id", 819606},
|
||||||
|
{"name", std::string{"janl"}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
};
|
7
test/data/implicit_iterator.mustache
Normal file
7
test/data/implicit_iterator.mustache
Normal 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 }}
|
3
test/data/implicit_iterator.txt
Normal file
3
test/data/implicit_iterator.txt
Normal 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
3
test/data/included_tag.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"html", std::string{"I like {{mustache}}"}}
|
||||||
|
};
|
1
test/data/included_tag.mustache
Normal file
1
test/data/included_tag.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
You said "{{{html}}}" today
|
1
test/data/included_tag.txt
Normal file
1
test/data/included_tag.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
You said "I like {{mustache}}" today
|
3
test/data/inverted_section.h
Normal file
3
test/data/inverted_section.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"repos", mstch::array{}}
|
||||||
|
};
|
3
test/data/inverted_section.mustache
Normal file
3
test/data/inverted_section.mustache
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{{#repos}}<b>{{name}}</b>{{/repos}}
|
||||||
|
{{^repos}}No repos :({{/repos}}
|
||||||
|
{{^nothin}}Hello!{{/nothin}}
|
3
test/data/inverted_section.txt
Normal file
3
test/data/inverted_section.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
No repos :(
|
||||||
|
Hello!
|
5
test/data/keys_with_questionmarks.h
Normal file
5
test/data/keys_with_questionmarks.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
auto data = mstch::object{
|
||||||
|
{"person?", mstch::object{
|
||||||
|
{"name", std::string{"Jon"}}
|
||||||
|
}}
|
||||||
|
};
|
3
test/data/keys_with_questionmarks.mustache
Normal file
3
test/data/keys_with_questionmarks.mustache
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{{#person?}}
|
||||||
|
Hi {{name}}!
|
||||||
|
{{/person?}}
|
1
test/data/keys_with_questionmarks.txt
Normal file
1
test/data/keys_with_questionmarks.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hi Jon!
|
7
test/data/lambda/check_falsy.js
Normal file
7
test/data/lambda/check_falsy.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
({
|
||||||
|
number: function(text, render) {
|
||||||
|
return function(text, render) {
|
||||||
|
return +render(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
1
test/data/lambda/check_falsy.mustache
Normal file
1
test/data/lambda/check_falsy.mustache
Normal file
@ -0,0 +1 @@
|
|||||||
|
<p>{{#number}}0{{/number}}</p>
|
1
test/data/lambda/check_falsy.txt
Normal file
1
test/data/lambda/check_falsy.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
<p>0</p>
|
5
test/data/lambda/comments.js
Normal file
5
test/data/lambda/comments.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
({
|
||||||
|
title: function () {
|
||||||
|
return "A Comedy of Errors";
|
||||||
|
}
|
||||||
|
})
|
1
test/data/lambda/comments.mustache
Normal file
1
test/data/lambda/comments.mustache
Normal 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
Loading…
Reference in New Issue
Block a user