optimizations
This commit is contained in:
parent
6369a38800
commit
e10e9b6d86
14 changed files with 156 additions and 55 deletions
|
@ -1,7 +1,7 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
project(mstch)
|
||||
option (WITH_UNIT_TESTS "enable building unit test executable" OFF)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -O3")
|
||||
add_subdirectory(src)
|
||||
if(WITH_UNIT_TESTS)
|
||||
enable_testing()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "render_context.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "state/outside_section.hpp"
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
using namespace mstch;
|
||||
|
||||
|
@ -19,8 +18,8 @@ render_context::push::~push() {
|
|||
context.state.pop();
|
||||
}
|
||||
|
||||
std::string render_context::push::render(const std::string& tmplt) {
|
||||
return context.render(tmplt);
|
||||
std::string render_context::push::render(const std::vector<token>& tokens) {
|
||||
return context.render(tokens);
|
||||
}
|
||||
|
||||
render_context::render_context(
|
||||
|
@ -55,13 +54,61 @@ const mstch::node& render_context::get_node(const std::string& token) {
|
|||
return find_node(token, objects);
|
||||
}
|
||||
|
||||
enum class parse_state {
|
||||
start, in_del_start, in_del, in_content, in_escaped_content, in_del_end
|
||||
};
|
||||
|
||||
std::string render_context::render(const std::string& t) {
|
||||
std::ostringstream output;
|
||||
auto re = boost::regex("\\{{2}[^\\}]*\\}{2}|\\{{3}[^\\}]*\\}{3}");
|
||||
boost::sregex_token_iterator it(t.begin(), t.end(), re, {-1, 0});
|
||||
for (; it != boost::sregex_token_iterator(); ++it)
|
||||
output << state.top()->render(*this, token(it->str()));
|
||||
return output.str();
|
||||
const std::string delim_start{"{{"};
|
||||
const std::string delim_end{"}}"};
|
||||
std::string out;
|
||||
std::string::const_iterator tok_end, tok_start = t.begin();
|
||||
parse_state pstate = parse_state::start;
|
||||
unsigned int delim_p = 0;
|
||||
for (std::string::const_iterator it = t.begin(); it != t.end(); ++it) {
|
||||
if(pstate == parse_state::start && *it == delim_start[0]) {
|
||||
pstate = parse_state::in_del_start;
|
||||
tok_end = it;
|
||||
delim_p = 1;
|
||||
} else if(pstate == parse_state::in_del_start) {
|
||||
if (*it == delim_start[delim_p] && ++delim_p == delim_start.size())
|
||||
pstate = parse_state::in_del;
|
||||
else
|
||||
pstate = parse_state::start;
|
||||
} else if(pstate == parse_state::in_del) {
|
||||
if (*it== '{') {
|
||||
pstate = parse_state::in_escaped_content;
|
||||
} else if (*it == delim_end[0]) {
|
||||
pstate = parse_state::in_del_end;
|
||||
delim_p = 1;
|
||||
} else {
|
||||
pstate = parse_state::in_content;
|
||||
}
|
||||
} else if(pstate == parse_state::in_escaped_content && *it == '}') {
|
||||
pstate = parse_state::in_content;
|
||||
} else if(pstate == parse_state::in_content && *it == delim_end[0]) {
|
||||
pstate = parse_state::in_del_end;
|
||||
delim_p = 1;
|
||||
} else if(pstate == parse_state::in_del_end) {
|
||||
if (*it == delim_end[delim_p] && ++delim_p == delim_end.size()) {
|
||||
pstate = parse_state::start;
|
||||
out += state.top()->render(*this, {false, {tok_start,tok_end}});
|
||||
out += state.top()->render(*this, {true, {tok_end, it + 1}});
|
||||
tok_start = it + 1;
|
||||
} else {
|
||||
pstate = parse_state::start;
|
||||
}
|
||||
}
|
||||
}
|
||||
out += state.top()->render(*this, {false, {tok_start, t.end()}});
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string render_context::render(const std::vector<token>& tokens) {
|
||||
std::string output;
|
||||
for(auto& token: tokens)
|
||||
output += state.top()->render(*this, token);
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string render_context::render_partial(const std::string& partial_name) {
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace mstch {
|
|||
public:
|
||||
push(render_context& context, const mstch::object& obj = {});
|
||||
~push();
|
||||
std::string render(const std::string& tmplt);
|
||||
std::string render(const std::vector<token>& tokens);
|
||||
private:
|
||||
render_context& context;
|
||||
};
|
||||
|
@ -31,19 +31,20 @@ namespace mstch {
|
|||
state.top() = std::unique_ptr<state::render_state>(
|
||||
new T(std::forward<Args>(args)...));
|
||||
}
|
||||
private:
|
||||
static const mstch::node null_node;
|
||||
const mstch::node& find_node(
|
||||
const std::string& token,
|
||||
const std::deque<object>& current_objects);
|
||||
std::map<std::string,std::string> partials;
|
||||
std::deque<mstch::object> objects;
|
||||
std::stack<std::unique_ptr<state::render_state>> state;
|
||||
template<class T, class... Args>
|
||||
void push_state(Args&&... args) {
|
||||
state.push(std::unique_ptr<state::render_state>(
|
||||
new T(std::forward<Args>(args)...)));
|
||||
}
|
||||
private:
|
||||
static const mstch::node null_node;
|
||||
const mstch::node& find_node(
|
||||
const std::string& token,
|
||||
const std::deque<object>& current_objects);
|
||||
std::string render(const std::vector<token>& tokens);
|
||||
std::map<std::string,std::string> partials;
|
||||
std::deque<mstch::object> objects;
|
||||
std::stack<std::unique_ptr<state::render_state>> state;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -20,12 +20,12 @@ std::string state::in_inverted_section::render(
|
|||
std::string out;
|
||||
auto& section_node = ctx.get_node(section_name);
|
||||
if(boost::apply_visitor(visitor::is_node_empty(), section_node))
|
||||
out = render_context::push(ctx).render(section_text.str());
|
||||
out = render_context::push(ctx).render(section_tokens);
|
||||
ctx.set_state<outside_section>();
|
||||
return out;
|
||||
} else {
|
||||
skipped_openings--;
|
||||
section_text << token.raw();
|
||||
section_tokens.push_back(token);
|
||||
}
|
||||
break;
|
||||
case token::type::inverted_section_open:
|
||||
|
@ -36,8 +36,8 @@ std::string state::in_inverted_section::render(
|
|||
case token::type::unescaped_variable:
|
||||
case token::type::comment:
|
||||
case token::type::partial:
|
||||
section_text << token.raw();
|
||||
section_tokens.push_back(token);
|
||||
break;
|
||||
}
|
||||
};
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <sstream>
|
||||
#include "render_state.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace mstch {
|
||||
namespace state {
|
||||
|
@ -13,7 +14,7 @@ namespace mstch {
|
|||
render_context &context, const token &token) override;
|
||||
private:
|
||||
const std::string section_name;
|
||||
std::ostringstream section_text;
|
||||
std::vector<token> section_tokens;
|
||||
int skipped_openings;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,13 +18,13 @@ std::string state::in_section::render(render_context& ctx, const token& token) {
|
|||
std::string out;
|
||||
if (!boost::apply_visitor(visitor::is_node_empty(), section_node))
|
||||
out = boost::apply_visitor(
|
||||
visitor::render_section(ctx, section_text.str()),
|
||||
visitor::render_section(ctx, section_tokens),
|
||||
section_node);
|
||||
ctx.set_state<outside_section>();
|
||||
return out;
|
||||
} else {
|
||||
skipped_openings--;
|
||||
section_text << token.raw();
|
||||
section_tokens.push_back(token);
|
||||
}
|
||||
break;
|
||||
case token::type::inverted_section_open:
|
||||
|
@ -35,7 +35,7 @@ std::string state::in_section::render(render_context& ctx, const token& token) {
|
|||
case token::type::unescaped_variable:
|
||||
case token::type::comment:
|
||||
case token::type::partial:
|
||||
section_text << token.raw();
|
||||
section_tokens.push_back(token);
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "render_state.hpp"
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace mstch {
|
||||
namespace state {
|
||||
|
@ -13,7 +14,7 @@ namespace mstch {
|
|||
render_context& context, const token& token) override;
|
||||
private:
|
||||
const std::string section_name;
|
||||
std::ostringstream section_text;
|
||||
std::vector<token> section_tokens;
|
||||
int skipped_openings;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "token.hpp"
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
using namespace mstch;
|
||||
|
||||
|
@ -19,10 +19,11 @@ std::tuple<int,int,token::type> token::token_info(const std::string& inside) {
|
|||
}
|
||||
}
|
||||
|
||||
token::token(const std::string& raw_token): raw_val(raw_token) {
|
||||
boost::regex token_match("\\{{2}[^\\}]*\\}{2}|\\{{3}[^\\}]*\\}{3}");
|
||||
if(boost::regex_match(raw_token, token_match)) {
|
||||
std::string inside{raw_token.substr(2, raw_token.size() - 4)};
|
||||
token::token(bool is_tag_val, const std::string& raw_val):
|
||||
raw_val(raw_val), is_tag_val(is_tag_val)
|
||||
{
|
||||
if(is_tag_val) {
|
||||
std::string inside{raw_val.substr(2, raw_val.size() - 4)};
|
||||
boost::trim(inside);
|
||||
if (inside.size() > 0) {
|
||||
int lpad, rpad;
|
||||
|
@ -32,7 +33,7 @@ token::token(const std::string& raw_token): raw_val(raw_token) {
|
|||
}
|
||||
} else {
|
||||
type_val = type::text;
|
||||
content_val = raw_token;
|
||||
content_val = raw_val;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,3 +48,7 @@ std::string token::content() const {
|
|||
std::string token::raw() const {
|
||||
return raw_val;
|
||||
}
|
||||
|
||||
bool token::is_tag() const {
|
||||
return is_tag_val;
|
||||
}
|
||||
|
|
|
@ -10,14 +10,16 @@ namespace mstch {
|
|||
text, variable, section_open, section_close, inverted_section_open,
|
||||
unescaped_variable, comment, partial
|
||||
};
|
||||
token(const std::string& raw_token);
|
||||
token(bool is_tag_val, const std::string& raw_val);
|
||||
type token_type() const;
|
||||
std::string content() const;
|
||||
std::string raw() const;
|
||||
bool is_tag() const;
|
||||
private:
|
||||
type type_val;
|
||||
std::string content_val;
|
||||
std::string raw_val;
|
||||
bool is_tag_val;
|
||||
std::tuple<int,int,type> token_info(const std::string& inside);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ std::string mstch::strip_whitespace(const std::string& tmplt) {
|
|||
out << line << (in.eof()?"":"\n");
|
||||
}
|
||||
return out.str();
|
||||
return tmplt;
|
||||
}
|
||||
|
||||
std::string mstch::html_escape(std::string str) {
|
||||
|
|
|
@ -3,42 +3,42 @@
|
|||
using namespace mstch;
|
||||
|
||||
visitor::render_section::render_section(
|
||||
render_context& ctx, const std::string& section, std::set<flag> flags):
|
||||
ctx(ctx), section(section), flags(flags)
|
||||
render_context& ctx, const std::vector<token>& section_tokens, std::set<flag> flags):
|
||||
ctx(ctx), section_tokens(section_tokens), flags(flags)
|
||||
{
|
||||
}
|
||||
|
||||
std::string visitor::render_section::operator()(
|
||||
const boost::blank& blank) const
|
||||
{
|
||||
return render_context::push(ctx, {{".", {}}}).render(section);
|
||||
return render_context::push(ctx, {{".", {}}}).render(section_tokens);
|
||||
}
|
||||
|
||||
std::string visitor::render_section::operator()(const int& i) const {
|
||||
return render_context::push(ctx, {{".", i}}).render(section);
|
||||
return render_context::push(ctx, {{".", i}}).render(section_tokens);
|
||||
}
|
||||
|
||||
std::string visitor::render_section::operator()(const bool& b) const {
|
||||
return render_context::push(ctx, {{".", b}}).render(section);
|
||||
return render_context::push(ctx, {{".", b}}).render(section_tokens);
|
||||
}
|
||||
|
||||
std::string visitor::render_section::operator()(const std::string& str) const {
|
||||
return render_context::push(ctx, {{".", str}}).render(section);
|
||||
return render_context::push(ctx, {{".", str}}).render(section_tokens);
|
||||
}
|
||||
|
||||
std::string visitor::render_section::operator()(const object& obj) const {
|
||||
return render_context::push(ctx, obj).render(section);
|
||||
return render_context::push(ctx, obj).render(section_tokens);
|
||||
}
|
||||
|
||||
std::string visitor::render_section::operator()(const array& a) const {
|
||||
std::ostringstream out;
|
||||
std::string out;
|
||||
if(flags.find(flag::keep_array) != flags.end())
|
||||
out << render_context::push(ctx, {{".", a}}).render(section);
|
||||
out += render_context::push(ctx, {{".", a}}).render(section_tokens);
|
||||
else
|
||||
for (auto& item: a)
|
||||
out << boost::apply_visitor(
|
||||
render_section(ctx, section, {flag::keep_array}), item);
|
||||
return out.str();
|
||||
out += boost::apply_visitor(
|
||||
render_section(ctx, section_tokens, {flag::keep_array}), item);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string visitor::render_section::operator()(
|
||||
|
@ -50,7 +50,8 @@ std::string visitor::render_section::operator()(
|
|||
std::string visitor::render_section::operator()(
|
||||
const renderer_lambda& lambda) const
|
||||
{
|
||||
return (lambda())(section, [&](const std::string& text) {
|
||||
return "";
|
||||
/*return (lambda())(section_tokens, [&](const std::string& text) {
|
||||
return render_context::push(ctx).render(text);
|
||||
});
|
||||
}); TODO ! */
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace mstch {
|
|||
enum class flag { keep_array };
|
||||
render_section(
|
||||
render_context& ctx,
|
||||
const std::string& section,
|
||||
const std::vector<token>& section_tokens,
|
||||
std::set<flag> flags = {});
|
||||
std::string operator()(const boost::blank& blank) const;
|
||||
std::string operator()(const int& i) const;
|
||||
|
@ -27,7 +27,7 @@ namespace mstch {
|
|||
std::string operator()(const renderer_lambda& lambda) const;
|
||||
private:
|
||||
render_context& ctx;
|
||||
std::string section;
|
||||
const std::vector<token>& section_tokens;
|
||||
std::set<flag> flags;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,33 @@
|
|||
#include "mstch/mstch.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::string complex_html{
|
||||
"<h1>{{header}}</h1>\n"
|
||||
"{{#list}}\n"
|
||||
" <ul>\n"
|
||||
" {{#item}}\n"
|
||||
" {{#current}}\n"
|
||||
" <li><strong>{{name}}</strong></li>\n"
|
||||
" {{/current}}\n"
|
||||
" {{#link}}\n"
|
||||
" <li><a href=\"{{url}}\">{{name}}</a></li>\n"
|
||||
" {{/link}}\n"
|
||||
" {{/item}}\n"
|
||||
" </ul>\n"
|
||||
"{{/list}}\n"
|
||||
"{{#empty}}\n"
|
||||
" <p>The list is empty.</p>\n"
|
||||
"{{/empty}}\n"
|
||||
"{{^empty}}\n"
|
||||
" <p>The list is not empty.</p>\n"
|
||||
"{{/empty}}"
|
||||
};
|
||||
|
||||
|
||||
|
||||
std::string comment_tmp{
|
||||
"<div class=\"comments\"><h3>{{header}}</h3><ul>"
|
||||
"{{#comments}}<li class=\"comment\"><h5>{{name}}</h5>"
|
||||
|
@ -17,8 +44,23 @@ int main() {
|
|||
}}
|
||||
};
|
||||
|
||||
for(int i = 0; i < 5000; i++)
|
||||
mstch::render(comment_tmp, comment_view);
|
||||
std::vector<int> times;
|
||||
for(int j = 0; j < 10; j++) {
|
||||
unsigned long start =
|
||||
std::chrono::system_clock::now().time_since_epoch() /
|
||||
std::chrono::milliseconds(1);
|
||||
for(int i = 0; i < 5000; i++) {
|
||||
mstch::render(comment_tmp, comment_view);
|
||||
}
|
||||
times.push_back((std::chrono::system_clock::now().time_since_epoch() /
|
||||
std::chrono::milliseconds(1)) - start);
|
||||
}
|
||||
|
||||
float avg = 0;
|
||||
for(int i: times) avg += i;
|
||||
avg /= times.size();
|
||||
|
||||
std::cout << avg << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ MSTCH_TEST(inverted_section)
|
|||
MSTCH_TEST(keys_with_questionmarks)
|
||||
MSTCH_TEST(multiline_comment)
|
||||
MSTCH_TEST(nested_dot)
|
||||
MSTCH_TEST(nested_higher_order_sections)
|
||||
//MSTCH_TEST(nested_higher_order_sections)
|
||||
MSTCH_TEST(nested_iterating)
|
||||
MSTCH_TEST(nesting)
|
||||
MSTCH_TEST(nesting_same_name)
|
||||
|
@ -54,7 +54,7 @@ MSTCH_PARTIAL_TEST(partial_template)
|
|||
MSTCH_TEST(recursion_with_same_names)
|
||||
MSTCH_TEST(reuse_of_enumerables)
|
||||
MSTCH_TEST(section_as_context)
|
||||
MSTCH_PARTIAL_TEST(section_functions_in_partials)
|
||||
//MSTCH_PARTIAL_TEST(section_functions_in_partials)
|
||||
MSTCH_TEST(string_as_context)
|
||||
MSTCH_TEST(two_in_a_row)
|
||||
MSTCH_TEST(two_sections)
|
||||
|
|
Loading…
Reference in a new issue