aboutsummaryrefslogtreecommitdiffstats
path: root/mustache.h
diff options
context:
space:
mode:
authoripknHama <ipknhama@gmail.com>2014-08-07 01:18:33 +0900
committeripknHama <ipknhama@gmail.com>2014-08-07 01:18:33 +0900
commit031615ac866cc3c8f1900dd4b4aae2106ad31230 (patch)
treeb8b7206ffbd2043368580ec269c97436929fe452 /mustache.h
parenta0c93f5b84cc11b30bc6320ac26127832ef8bf7a (diff)
downloadcrow-031615ac866cc3c8f1900dd4b4aae2106ad31230.tar.gz
crow-031615ac866cc3c8f1900dd4b4aae2106ad31230.zip
source resturcturing + CMake
Diffstat (limited to 'mustache.h')
-rw-r--r--mustache.h550
1 files changed, 0 insertions, 550 deletions
diff --git a/mustache.h b/mustache.h
deleted file mode 100644
index 7218ae8..0000000
--- a/mustache.h
+++ /dev/null
@@ -1,550 +0,0 @@
-#pragma once
-#include <string>
-#include <vector>
-#include <fstream>
-#include <iterator>
-#include <functional>
-#include "json.h"
-namespace crow
-{
- namespace mustache
- {
- using context = json::wvalue;
-
- template_t load(const std::string& filename);
-
- class invalid_template_exception : public std::exception
- {
- public:
- invalid_template_exception(const std::string& msg)
- : msg("crow::mustache error: " + msg)
- {
- }
- virtual const char* what() const throw()
- {
- return msg.c_str();
- }
- std::string msg;
- };
-
- enum class ActionType
- {
- Ignore,
- Tag,
- UnescapeTag,
- OpenBlock,
- CloseBlock,
- ElseBlock,
- Partial,
- };
-
- struct Action
- {
- int start;
- int end;
- int pos;
- ActionType t;
- Action(ActionType t, int start, int end, int pos = 0)
- : start(start), end(end), pos(pos), t(t)
- {}
- };
-
- class template_t
- {
- public:
- template_t(std::string body)
- : body_(std::move(body))
- {
- // {{ {{# {{/ {{^ {{! {{> {{=
- parse();
- }
-
- private:
- std::string tag_name(const Action& action)
- {
- return body_.substr(action.start, action.end - action.start);
- }
- auto find_context(const std::string& name, const std::vector<context*>& stack)->std::pair<bool, context&>
- {
- if (name == ".")
- {
- return {true, *stack.back()};
- }
- int dotPosition = name.find(".");
- if (dotPosition == (int)name.npos)
- {
- for(auto it = stack.rbegin(); it != stack.rend(); ++it)
- {
- if ((*it)->t() == json::type::Object)
- {
- if ((*it)->count(name))
- return {true, (**it)[name]};
- }
- }
- }
- else
- {
- std::vector<int> dotPositions;
- dotPositions.push_back(-1);
- while(dotPosition != (int)name.npos)
- {
- dotPositions.push_back(dotPosition);
- dotPosition = name.find(".", dotPosition+1);
- }
- dotPositions.push_back(name.size());
- std::vector<std::string> names;
- names.reserve(dotPositions.size()-1);
- for(int i = 1; i < (int)dotPositions.size(); i ++)
- names.emplace_back(name.substr(dotPositions[i-1]+1, dotPositions[i]-dotPositions[i-1]-1));
-
- for(auto it = stack.rbegin(); it != stack.rend(); ++it)
- {
- context* view = *it;
- bool found = true;
- for(auto jt = names.begin(); jt != names.end(); ++jt)
- {
- if (view->t() == json::type::Object &&
- view->count(*jt))
- {
- view = &(*view)[*jt];
- }
- else
- {
- found = false;
- break;
- }
- }
- if (found)
- return {true, *view};
- }
-
- }
-
- static json::wvalue empty_str;
- empty_str = "";
- return {false, empty_str};
- }
-
- void escape(const std::string& in, std::string& out)
- {
- out.reserve(out.size() + in.size());
- for(auto it = in.begin(); it != in.end(); ++it)
- {
- switch(*it)
- {
- case '&': out += "&amp;"; break;
- case '<': out += "&lt;"; break;
- case '>': out += "&gt;"; break;
- case '"': out += "&quot;"; break;
- case '\'': out += "&#39;"; break;
- case '/': out += "&#x2F;"; break;
- default: out += *it; break;
- }
- }
- }
-
- void render_internal(int actionBegin, int actionEnd, std::vector<context*>& stack, std::string& out, int indent)
- {
- int current = actionBegin;
-
- if (indent)
- out.insert(out.size(), indent, ' ');
-
- while(current < actionEnd)
- {
- auto& fragment = fragments_[current];
- auto& action = actions_[current];
- render_fragment(fragment, indent, out);
- switch(action.t)
- {
- case ActionType::Ignore:
- // do nothing
- break;
- case ActionType::Partial:
- {
- std::string partial_name = tag_name(action);
- auto partial_templ = load(partial_name);
- int partial_indent = action.pos;
- partial_templ.render_internal(0, partial_templ.fragments_.size()-1, stack, out, partial_indent?indent+partial_indent:0);
- }
- break;
- case ActionType::UnescapeTag:
- case ActionType::Tag:
- {
- auto optional_ctx = find_context(tag_name(action), stack);
- auto& ctx = optional_ctx.second;
- switch(ctx.t())
- {
- case json::type::Number:
- out += json::dump(ctx);
- break;
- case json::type::String:
- if (action.t == ActionType::Tag)
- escape(ctx.s, out);
- else
- out += ctx.s;
- break;
- default:
- throw std::runtime_error("not implemented tag type" + boost::lexical_cast<std::string>((int)ctx.t()));
- }
- }
- break;
- case ActionType::ElseBlock:
- {
- static context nullContext;
- auto optional_ctx = find_context(tag_name(action), stack);
- if (!optional_ctx.first)
- {
- stack.emplace_back(&nullContext);
- break;
- }
-
- auto& ctx = optional_ctx.second;
- switch(ctx.t())
- {
- case json::type::List:
- if (ctx.l && !ctx.l->empty())
- current = action.pos;
- else
- stack.emplace_back(&nullContext);
- break;
- case json::type::False:
- case json::type::Null:
- stack.emplace_back(&nullContext);
- break;
- default:
- current = action.pos;
- break;
- }
- break;
- }
- case ActionType::OpenBlock:
- {
- auto optional_ctx = find_context(tag_name(action), stack);
- if (!optional_ctx.first)
- {
- current = action.pos;
- break;
- }
-
- auto& ctx = optional_ctx.second;
- switch(ctx.t())
- {
- case json::type::List:
- if (ctx.l)
- for(auto it = ctx.l->begin(); it != ctx.l->end(); ++it)
- {
- stack.push_back(&*it);
- render_internal(current+1, action.pos, stack, out, indent);
- stack.pop_back();
- }
- current = action.pos;
- break;
- case json::type::Number:
- case json::type::String:
- case json::type::Object:
- case json::type::True:
- stack.push_back(&ctx);
- break;
- case json::type::False:
- case json::type::Null:
- current = action.pos;
- break;
- default:
- throw std::runtime_error("{{#: not implemented context type: " + boost::lexical_cast<std::string>((int)ctx.t()));
- break;
- }
- break;
- }
- case ActionType::CloseBlock:
- stack.pop_back();
- break;
- default:
- throw std::runtime_error("not implemented " + boost::lexical_cast<std::string>((int)action.t));
- }
- current++;
- }
- auto& fragment = fragments_[actionEnd];
- render_fragment(fragment, indent, out);
- }
- void render_fragment(const std::pair<int, int> fragment, int indent, std::string& out)
- {
- if (indent)
- {
- for(int i = fragment.first; i < fragment.second; i ++)
- {
- out += body_[i];
- if (body_[i] == '\n' && i+1 != (int)body_.size())
- out.insert(out.size(), indent, ' ');
- }
- }
- else
- out.insert(out.size(), body_, fragment.first, fragment.second-fragment.first);
- }
- public:
- std::string render()
- {
- context empty_ctx;
- std::vector<context*> stack;
- stack.emplace_back(&empty_ctx);
-
- std::string ret;
- render_internal(0, fragments_.size()-1, stack, ret, 0);
- return ret;
- }
- std::string render(context& ctx)
- {
- std::vector<context*> stack;
- stack.emplace_back(&ctx);
-
- std::string ret;
- render_internal(0, fragments_.size()-1, stack, ret, 0);
- return ret;
- }
-
- private:
-
- void parse()
- {
- std::string tag_open = "{{";
- std::string tag_close = "}}";
-
- std::vector<int> blockPositions;
-
- size_t current = 0;
- while(1)
- {
- size_t idx = body_.find(tag_open, current);
- if (idx == body_.npos)
- {
- fragments_.emplace_back(current, body_.size());
- actions_.emplace_back(ActionType::Ignore, 0, 0);
- break;
- }
- fragments_.emplace_back(current, idx);
-
- idx += tag_open.size();
- size_t endIdx = body_.find(tag_close, idx);
- if (endIdx == idx)
- {
- throw invalid_template_exception("empty tag is not allowed");
- }
- if (endIdx == body_.npos)
- {
- // error, no matching tag
- throw invalid_template_exception("not matched opening tag");
- }
- current = endIdx + tag_close.size();
- switch(body_[idx])
- {
- case '#':
- idx++;
- while(body_[idx] == ' ') idx++;
- while(body_[endIdx-1] == ' ') endIdx--;
- blockPositions.emplace_back(actions_.size());
- actions_.emplace_back(ActionType::OpenBlock, idx, endIdx);
- break;
- case '/':
- idx++;
- while(body_[idx] == ' ') idx++;
- while(body_[endIdx-1] == ' ') endIdx--;
- {
- auto& matched = actions_[blockPositions.back()];
- if (body_.compare(idx, endIdx-idx,
- body_, matched.start, matched.end - matched.start) != 0)
- {
- throw invalid_template_exception("not matched {{# {{/ pair: " +
- body_.substr(matched.start, matched.end - matched.start) + ", " +
- body_.substr(idx, endIdx-idx));
- }
- matched.pos = actions_.size();
- }
- actions_.emplace_back(ActionType::CloseBlock, idx, endIdx, blockPositions.back());
- blockPositions.pop_back();
- break;
- case '^':
- idx++;
- while(body_[idx] == ' ') idx++;
- while(body_[endIdx-1] == ' ') endIdx--;
- blockPositions.emplace_back(actions_.size());
- actions_.emplace_back(ActionType::ElseBlock, idx, endIdx);
- break;
- case '!':
- // do nothing action
- actions_.emplace_back(ActionType::Ignore, idx+1, endIdx);
- break;
- case '>': // partial
- idx++;
- while(body_[idx] == ' ') idx++;
- while(body_[endIdx-1] == ' ') endIdx--;
- actions_.emplace_back(ActionType::Partial, idx, endIdx);
- break;
- case '{':
- if (tag_open != "{{" || tag_close != "}}")
- throw invalid_template_exception("cannot use triple mustache when delimiter changed");
-
- idx ++;
- if (body_[endIdx+2] != '}')
- {
- throw invalid_template_exception("{{{: }}} not matched");
- }
- while(body_[idx] == ' ') idx++;
- while(body_[endIdx-1] == ' ') endIdx--;
- actions_.emplace_back(ActionType::UnescapeTag, idx, endIdx);
- current++;
- break;
- case '&':
- idx ++;
- while(body_[idx] == ' ') idx++;
- while(body_[endIdx-1] == ' ') endIdx--;
- actions_.emplace_back(ActionType::UnescapeTag, idx, endIdx);
- break;
- case '=':
- // tag itself is no-op
- idx ++;
- actions_.emplace_back(ActionType::Ignore, idx, endIdx);
- endIdx --;
- if (body_[endIdx] != '=')
- throw invalid_template_exception("{{=: not matching = tag: "+body_.substr(idx, endIdx-idx));
- endIdx --;
- while(body_[idx] == ' ') idx++;
- while(body_[endIdx] == ' ') endIdx--;
- endIdx++;
- {
- bool succeeded = false;
- for(size_t i = idx; i < endIdx; i++)
- {
- if (body_[i] == ' ')
- {
- tag_open = body_.substr(idx, i-idx);
- while(body_[i] == ' ') i++;
- tag_close = body_.substr(i, endIdx-i);
- if (tag_open.empty())
- throw invalid_template_exception("{{=: empty open tag");
- if (tag_close.empty())
- throw invalid_template_exception("{{=: empty close tag");
-
- if (tag_close.find(" ") != tag_close.npos)
- throw invalid_template_exception("{{=: invalid open/close tag: "+tag_open+" " + tag_close);
- succeeded = true;
- break;
- }
- }
- if (!succeeded)
- throw invalid_template_exception("{{=: cannot find space between new open/close tags");
- }
- break;
- default:
- // normal tag case;
- while(body_[idx] == ' ') idx++;
- while(body_[endIdx-1] == ' ') endIdx--;
- actions_.emplace_back(ActionType::Tag, idx, endIdx);
- break;
- }
- }
-
- // removing standalones
- for(int i = actions_.size()-2; i >= 0; i --)
- {
- if (actions_[i].t == ActionType::Tag || actions_[i].t == ActionType::UnescapeTag)
- continue;
- auto& fragment_before = fragments_[i];
- auto& fragment_after = fragments_[i+1];
- bool is_last_action = i == (int)actions_.size()-2;
- bool all_space_before = true;
- int j, k;
- for(j = fragment_before.second-1;j >= fragment_before.first;j--)
- {
- if (body_[j] != ' ')
- {
- all_space_before = false;
- break;
- }
- }
- if (all_space_before && i > 0)
- continue;
- if (!all_space_before && body_[j] != '\n')
- continue;
- bool all_space_after = true;
- for(k = fragment_after.first; k < (int)body_.size() && k < fragment_after.second; k ++)
- {
- if (body_[k] != ' ')
- {
- all_space_after = false;
- break;
- }
- }
- if (all_space_after && !is_last_action)
- continue;
- if (!all_space_after &&
- !(
- body_[k] == '\n'
- ||
- (body_[k] == '\r' &&
- k + 1 < (int)body_.size() &&
- body_[k+1] == '\n')))
- continue;
- if (actions_[i].t == ActionType::Partial)
- {
- actions_[i].pos = fragment_before.second - j - 1;
- }
- fragment_before.second = j+1;
- if (!all_space_after)
- {
- if (body_[k] == '\n')
- k++;
- else
- k += 2;
- fragment_after.first = k;
- }
- }
- }
-
- std::vector<std::pair<int,int>> fragments_;
- std::vector<Action> actions_;
- std::string body_;
- };
-
- template_t compile(const std::string& body)
- {
- return template_t(body);
- }
- namespace detail
- {
- std::string template_base_directory = "templates";
- }
-
- std::string default_loader(const std::string& filename)
- {
- std::ifstream inf(detail::template_base_directory + filename);
- if (!inf)
- return {};
- return {std::istreambuf_iterator<char>(inf), std::istreambuf_iterator<char>()};
- }
-
- namespace detail
- {
- std::function<std::string (std::string)> loader = default_loader;
- }
-
- void set_base(const std::string& path)
- {
- detail::template_base_directory = path;
- if (detail::template_base_directory.back() != '\\' &&
- detail::template_base_directory.back() != '/')
- {
- detail::template_base_directory += '/';
- }
- }
-
- void set_loader(std::function<std::string(std::string)> loader)
- {
- detail::loader = std::move(loader);
- }
-
- template_t load(const std::string& filename)
- {
- return compile(detail::loader(filename));
- }
- }
-}