optimizations
This commit is contained in:
parent
e10e9b6d86
commit
bb21e64a51
@ -1,4 +1,4 @@
|
||||
find_package(Boost 1.54 COMPONENTS regex REQUIRED)
|
||||
find_package(Boost 1.54 REQUIRED)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
@ -18,4 +18,3 @@ set(SRC
|
||||
visitor/render_section.cpp)
|
||||
|
||||
add_library(mstch STATIC ${SRC})
|
||||
target_link_libraries(mstch ${Boost_REGEX_LIBRARY})
|
@ -11,5 +11,5 @@ std::string mstch::render(
|
||||
const object& root,
|
||||
const std::map<std::string,std::string>& partials)
|
||||
{
|
||||
return render_context(root, partials).render(strip_whitespace(tmplt));
|
||||
return render_context(root, partials).render(tmplt);
|
||||
}
|
||||
|
@ -9,8 +9,9 @@ const mstch::node render_context::null_node;
|
||||
render_context::push::push(render_context& context, const mstch::object& obj):
|
||||
context(context)
|
||||
{
|
||||
context.objects.push_front(obj);
|
||||
context.push_state<state::outside_section>();
|
||||
context.objects.emplace_front(obj);
|
||||
context.state.push(std::unique_ptr<state::render_state>(
|
||||
new state::outside_section));
|
||||
}
|
||||
|
||||
render_context::push::~push() {
|
||||
@ -26,11 +27,11 @@ render_context::render_context(
|
||||
const mstch::object& object,
|
||||
const std::map<std::string,std::string>& partials):
|
||||
partials{partials},
|
||||
objects{object}
|
||||
objects{object},
|
||||
delim_start{"{{"},
|
||||
delim_end{"}}"}
|
||||
{
|
||||
push_state<state::outside_section>();
|
||||
for(auto& partial: this->partials)
|
||||
partial.second = strip_whitespace(partial.second);
|
||||
state.push(std::unique_ptr<state::render_state>(new state::outside_section));
|
||||
}
|
||||
|
||||
const mstch::node& render_context::find_node(
|
||||
@ -54,54 +55,86 @@ 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) {
|
||||
const std::string delim_start{"{{"};
|
||||
const std::string delim_end{"}}"};
|
||||
std::string out;
|
||||
void render_context::tokenize(const std::string& t, std::vector<token>& toks) {
|
||||
std::string::const_iterator tok_end, tok_start = t.begin();
|
||||
parse_state pstate = parse_state::start;
|
||||
unsigned int delim_p = 0;
|
||||
unsigned int del_pos = 0;
|
||||
bool ws_only = true;
|
||||
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;
|
||||
if(pstate == parse_state::start) {
|
||||
if(*it == delim_start[0]) {
|
||||
pstate = parse_state::in_del_start;
|
||||
tok_end = it;
|
||||
del_pos = 1;
|
||||
} else if(*it == '\n') {
|
||||
toks.push_back({false, true, ws_only, {tok_start, it + 1}});
|
||||
ws_only = true;
|
||||
tok_start = it + 1;
|
||||
} else if (*it != ' ' && *it != '\t') {
|
||||
ws_only = false;
|
||||
}
|
||||
} else if(pstate == parse_state::in_del_start) {
|
||||
if (*it == delim_start[delim_p] && ++delim_p == delim_start.size())
|
||||
if (*it == delim_start[del_pos] && ++del_pos == 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;
|
||||
pstate = parse_state::in_esccontent;
|
||||
} else if (*it == delim_end[0]) {
|
||||
pstate = parse_state::in_del_end;
|
||||
delim_p = 1;
|
||||
del_pos = 1;
|
||||
} else {
|
||||
pstate = parse_state::in_content;
|
||||
}
|
||||
} else if(pstate == parse_state::in_escaped_content && *it == '}') {
|
||||
} else if(pstate == parse_state::in_esccontent && *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;
|
||||
del_pos = 1;
|
||||
} else if(pstate == parse_state::in_del_end) {
|
||||
if (*it == delim_end[delim_p] && ++delim_p == delim_end.size()) {
|
||||
if (*it == delim_end[del_pos] && ++del_pos == 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}});
|
||||
toks.push_back({false, false, ws_only, {tok_start, tok_end}});
|
||||
toks.push_back({true, false, false, {tok_end, it + 1}});
|
||||
ws_only = true;
|
||||
tok_start = it + 1;
|
||||
} else {
|
||||
pstate = parse_state::start;
|
||||
}
|
||||
}
|
||||
}
|
||||
out += state.top()->render(*this, {false, {tok_start, t.end()}});
|
||||
return out;
|
||||
toks.push_back({false, false, ws_only, {tok_start, t.end()}});
|
||||
}
|
||||
|
||||
void render_context::strip_whitespace(std::vector<token>& tokens) {
|
||||
auto line_begin = tokens.begin();
|
||||
bool has_tag = false, non_space = false;
|
||||
for(auto it = tokens.begin(); it != tokens.end(); ++it) {
|
||||
auto type = (*it).token_type();
|
||||
if(type != token::type::text && type != token::type::variable &&
|
||||
type != token::type::unescaped_variable)
|
||||
has_tag = true;
|
||||
else if(!(*it).is_ws_only())
|
||||
non_space = true;
|
||||
if((*it).is_eol()) {
|
||||
if(has_tag && !non_space)
|
||||
for (auto line_it = line_begin; line_it != it + 1; ++line_it)
|
||||
if ((*line_it).is_ws_only())
|
||||
(*line_it).mark();
|
||||
non_space = has_tag = false;
|
||||
line_begin = it + 1;
|
||||
}
|
||||
}
|
||||
for(auto it = tokens.begin(); it != tokens.end();)
|
||||
((*it).is_marked())?(it = tokens.erase(it)):(++it);
|
||||
}
|
||||
|
||||
std::string render_context::render(const std::string& tmplt) {
|
||||
std::vector<token> tokens;
|
||||
tokenize(tmplt, tokens);
|
||||
strip_whitespace(tokens);
|
||||
return render(tokens);
|
||||
}
|
||||
|
||||
std::string render_context::render(const std::vector<token>& tokens) {
|
||||
|
@ -31,20 +31,22 @@ namespace mstch {
|
||||
state.top() = std::unique_ptr<state::render_state>(
|
||||
new T(std::forward<Args>(args)...));
|
||||
}
|
||||
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:
|
||||
enum class parse_state {
|
||||
start, in_del_start, in_del, in_content, in_esccontent, in_del_end
|
||||
};
|
||||
static const mstch::node null_node;
|
||||
const mstch::node& find_node(
|
||||
const std::string& token,
|
||||
const std::deque<object>& current_objects);
|
||||
void tokenize(const std::string& tmplt, std::vector<token>& tokens);
|
||||
void strip_whitespace(std::vector<token>& tokens);
|
||||
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;
|
||||
std::string delim_start;
|
||||
std::string delim_end;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -9,9 +9,9 @@ namespace mstch {
|
||||
namespace state {
|
||||
class in_inverted_section: public render_state {
|
||||
public:
|
||||
in_inverted_section(const std::string §ion_name);
|
||||
in_inverted_section(const std::string& section_name);
|
||||
std::string render(
|
||||
render_context &context, const token &token) override;
|
||||
render_context& context, const token& token) override;
|
||||
private:
|
||||
const std::string section_name;
|
||||
std::vector<token> section_tokens;
|
||||
|
@ -8,7 +8,7 @@ namespace mstch {
|
||||
class outside_section: public render_state {
|
||||
public:
|
||||
std::string render(
|
||||
render_context &context, const token &token) override;
|
||||
render_context& context, const token& token) override;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,10 @@ std::tuple<int,int,token::type> token::token_info(const std::string& inside) {
|
||||
}
|
||||
}
|
||||
|
||||
token::token(bool is_tag_val, const std::string& raw_val):
|
||||
raw_val(raw_val), is_tag_val(is_tag_val)
|
||||
token::token(bool is_tag, bool eol, bool ws_only, const std::string& raw_val):
|
||||
raw_val(raw_val), eol(eol), ws_only(ws_only), marked(false)
|
||||
{
|
||||
if(is_tag_val) {
|
||||
if(is_tag) {
|
||||
std::string inside{raw_val.substr(2, raw_val.size() - 4)};
|
||||
boost::trim(inside);
|
||||
if (inside.size() > 0) {
|
||||
@ -36,19 +36,3 @@ token::token(bool is_tag_val, const std::string& raw_val):
|
||||
content_val = raw_val;
|
||||
}
|
||||
}
|
||||
|
||||
token::type token::token_type() const {
|
||||
return type_val;
|
||||
}
|
||||
|
||||
std::string token::content() const {
|
||||
return content_val;
|
||||
}
|
||||
|
||||
std::string token::raw() const {
|
||||
return raw_val;
|
||||
}
|
||||
|
||||
bool token::is_tag() const {
|
||||
return is_tag_val;
|
||||
}
|
||||
|
@ -10,16 +10,21 @@ namespace mstch {
|
||||
text, variable, section_open, section_close, inverted_section_open,
|
||||
unescaped_variable, comment, partial
|
||||
};
|
||||
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;
|
||||
token(bool is_tag, bool eol, bool ws_only, const std::string& raw_val);
|
||||
type token_type() const { return type_val; };
|
||||
const std::string& content() const { return content_val; };
|
||||
const std::string& raw() const { return raw_val; };
|
||||
bool is_eol() const { return eol; }
|
||||
bool is_ws_only() const { return ws_only; }
|
||||
bool is_marked() const { return marked; }
|
||||
void mark() { marked = true; };
|
||||
private:
|
||||
type type_val;
|
||||
std::string content_val;
|
||||
std::string raw_val;
|
||||
bool is_tag_val;
|
||||
bool eol;
|
||||
bool ws_only;
|
||||
bool marked;
|
||||
std::tuple<int,int,type> token_info(const std::string& inside);
|
||||
};
|
||||
}
|
||||
|
@ -1,25 +1,7 @@
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
std::string mstch::strip_whitespace(const std::string& tmplt) {
|
||||
boost::regex comment_match("\\{\\{![^\\}]*\\}\\}");
|
||||
boost::regex tag_match("\\{{2}[ ]*[#|/|^|!|>]{1}[^\\}]*\\}{2}");
|
||||
boost::regex whitespace_match("^\\s*$");
|
||||
std::ostringstream out;
|
||||
std::istringstream in(boost::regex_replace(tmplt, comment_match, "{{!}}"));
|
||||
for(std::string line; std::getline(in, line);) {
|
||||
std::string no_tags = boost::regex_replace(line, tag_match, "");
|
||||
if (no_tags != line && boost::regex_match(no_tags, whitespace_match))
|
||||
out << boost::regex_replace(line, boost::regex("\\s"), "");
|
||||
else
|
||||
out << line << (in.eof()?"":"\n");
|
||||
}
|
||||
return out.str();
|
||||
return tmplt;
|
||||
}
|
||||
|
||||
std::string mstch::html_escape(std::string str) {
|
||||
boost::replace_all(str, "&", "&");
|
||||
boost::replace_all(str, "'", "'");
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include <string>
|
||||
|
||||
namespace mstch {
|
||||
std::string strip_whitespace(const std::string& tmplt);
|
||||
std::string html_escape(std::string str);
|
||||
}
|
||||
|
||||
|
@ -4,30 +4,6 @@
|
||||
#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>"
|
||||
|
Loading…
x
Reference in New Issue
Block a user