This commit is contained in:
Daniel Sipka 2015-04-23 12:54:08 +02:00
parent 29980e299c
commit f4dd438fcc
24 changed files with 457 additions and 413 deletions

View File

@ -5,6 +5,7 @@
#include "visitor/has_token.hpp"
using namespace mstch;
using namespace mstch::visitor;
const mstch::node render_context::null_node;
@ -44,9 +45,9 @@ const mstch::node& render_context::find_node(
token.substr(token.rfind('.') + 1),
{find_node(token.substr(0, token.rfind('.')), current_nodes)});
else
for (auto& i: current_nodes)
if (boost::apply_visitor(visitor::has_token(token), i))
return boost::apply_visitor(visitor::get_token(token, i), i);
for (auto& node: current_nodes)
if (visit(has_token(token), node))
return visit(get_token(token, node), node);
return null_node;
}

View File

@ -10,6 +10,7 @@
#include "template_type.hpp"
namespace mstch {
class render_context {
public:
class push {
@ -20,6 +21,7 @@ namespace mstch {
private:
render_context& context;
};
render_context(
const mstch::node& node,
const std::map<std::string, template_type>& partials);
@ -31,6 +33,7 @@ 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(
@ -40,4 +43,5 @@ namespace mstch {
std::deque<mstch::node> nodes;
std::stack<std::unique_ptr<state::render_state>> state;
};
}

View File

@ -2,8 +2,10 @@
#include "outside_section.hpp"
#include "visitor/is_node_empty.hpp"
#include "visitor/render_section.hpp"
#include "utils.hpp"
using namespace mstch;
using namespace mstch::visitor;
state::in_section::in_section(type type, const std::string& section_name):
m_type(type), section_name(section_name), skipped_openings(0)
@ -13,18 +15,17 @@ state::in_section::in_section(type type, const std::string& section_name):
std::string state::in_section::render(render_context& ctx, const token& token) {
if (token.token_type() == token::type::section_close)
if (token.name() == section_name && skipped_openings == 0) {
auto& sn = ctx.get_node(section_name);
auto& node = ctx.get_node(section_name);
std::string out;
if(m_type == type::normal &&
!boost::apply_visitor(visitor::is_node_empty(), sn))
out = boost::apply_visitor(visitor::render_section(ctx, section), sn);
else if(m_type == type::inverted &&
boost::apply_visitor(visitor::is_node_empty(), sn))
if (m_type == type::normal && !visit(is_node_empty(), node))
out = visit(render_section(ctx, section), node);
else if (m_type == type::inverted && visit(is_node_empty(), node))
out = render_context::push(ctx).render(section);
ctx.set_state<outside_section>();
return out;
} else
} else {
skipped_openings--;
}
else if (token.token_type() == token::type::inverted_section_open ||
token.token_type() == token::type::section_open)
skipped_openings++;

View File

@ -1,23 +1,28 @@
#pragma once
#include "render_state.hpp"
#include <sstream>
#include <vector>
#include "render_state.hpp"
#include "template_type.hpp"
namespace mstch {
namespace state {
class in_section: public render_state {
public:
enum class type { inverted, normal };
enum class type {
inverted, normal
};
in_section(type type, const std::string &section_name);
std::string render(
render_context& context, const token& token) override;
std::string render(render_context &context, const token &token) override;
private:
const type m_type;
const std::string section_name;
template_type section;
int skipped_openings;
};
}
}

View File

@ -1,13 +1,17 @@
#include "visitor/render_node.hpp"
#include "outside_section.hpp"
#include "visitor/render_node.hpp"
#include "in_section.hpp"
#include "render_context.hpp"
#include "utils.hpp"
using namespace mstch;
using namespace mstch::visitor;
std::string state::outside_section::render(
render_context& ctx, const token& token)
{
using flag = render_node::flag;
switch (token.token_type()) {
case token::type::section_open:
ctx.set_state<in_section>(in_section::type::normal, token.name());
@ -16,18 +20,15 @@ std::string state::outside_section::render(
ctx.set_state<in_section>(in_section::type::inverted, token.name());
break;
case token::type::variable:
return boost::apply_visitor(
visitor::render_node(visitor::render_node::flag::escape_html),
ctx.get_node(token.name()));
return visit(render_node(flag::escape_html), ctx.get_node(token.name()));
case token::type::unescaped_variable:
return boost::apply_visitor(
visitor::render_node(visitor::render_node::flag::none),
ctx.get_node(token.name()));
return visit(render_node(flag::none), ctx.get_node(token.name()));
case token::type::text:
return token.raw();
case token::type::partial:
return ctx.render_partial(token.name());
default: break;
default:
break;
}
return "";
}

View File

@ -4,10 +4,11 @@
namespace mstch {
namespace state {
class outside_section: public render_state {
public:
std::string render(
render_context& context, const token& token) override;
std::string render(render_context& context, const token& token) override;
};
}
}

View File

@ -1,15 +1,19 @@
#pragma once
#include <memory>
#include "token.hpp"
namespace mstch {
class render_context;
namespace state {
class render_state {
public:
virtual std::string render(
render_context& context, const token& token) = 0;
virtual std::string render(render_context& context, const token& token) = 0;
};
}
}

View File

@ -6,6 +6,7 @@
#include "token.hpp"
namespace mstch {
class template_type {
public:
template_type() = default;
@ -13,6 +14,7 @@ namespace mstch {
std::vector<token>::const_iterator begin() const { return tokens.begin(); }
std::vector<token>::const_iterator end() const { return tokens.end(); }
void operator<<(const token& token) { tokens.push_back(token); }
private:
enum class parse_state {
start, in_del_start, in_del, in_content, in_esccontent, in_del_end
@ -21,4 +23,5 @@ namespace mstch {
void strip_whitespace();
std::vector<token> tokens;
};
}

View File

@ -24,11 +24,11 @@ token::token(const std::string& str, std::size_t left, std::size_t right):
m_name = {first_not_ws(str.begin() + left + 1, str.end() - right),
first_not_ws(str.rbegin() + 1 + right, str.rend() - left) + 1};
} else {
auto first = first_not_ws(str.begin() + left, str.end() - right);
m_type = token_info(*first);
auto c = first_not_ws(str.begin() + left, str.end() - right);
m_type = token_info(*c);
if (m_type != type::variable)
first = first_not_ws(first + 1, str.end() - right);
m_name = {first, first_not_ws(str.rbegin() + right, str.rend() - left) + 1};
c = first_not_ws(c + 1, str.end() - right);
m_name = {c, first_not_ws(str.rbegin() + right, str.rend() - left) + 1};
}
} else {
m_type = type::text;

View File

@ -3,6 +3,7 @@
#include <string>
namespace mstch {
class token {
public:
enum class type {
@ -15,6 +16,7 @@ namespace mstch {
const std::string& name() const { return m_name; };
bool eol() const { return m_eol; }
bool ws_only() const { return m_ws_only; }
private:
type m_type;
std::string m_name;
@ -23,4 +25,5 @@ namespace mstch {
bool m_ws_only;
type token_info(char c);
};
}

View File

@ -1,11 +1,22 @@
#pragma once
#include <string>
#include <boost/variant/apply_visitor.hpp>
namespace mstch {
using citer = std::string::const_iterator;
using criter = std::string::const_reverse_iterator;
citer first_not_ws(citer begin, citer end);
citer first_not_ws(criter begin, criter end);
std::string html_escape(std::string str);
template<class... Args>
auto visit(Args&& ... args) -> decltype(boost::apply_visitor(
std::forward<Args>(args)...))
{
return boost::apply_visitor(std::forward<Args>(args)...);
}
}

View File

@ -2,6 +2,7 @@
#include <boost/variant/static_visitor.hpp>
#include <boost/blank.hpp>
#include "mstch/mstch.hpp"
namespace mstch {

View File

@ -2,6 +2,7 @@
#include <boost/variant/static_visitor.hpp>
#include <boost/blank.hpp>
#include "mstch/mstch.hpp"
namespace mstch {

View File

@ -5,6 +5,7 @@
#include "render_context.hpp"
#include "mstch/mstch.hpp"
#include "utils.hpp"
namespace mstch {
namespace visitor {
@ -49,8 +50,7 @@ inline std::string render_section::operator()<array>(const array& array) const {
out += render_context::push(ctx, array).render(section);
else
for (auto& item: array)
out += boost::apply_visitor(
render_section(ctx, section, flag::keep_array), item);
out += visit(render_section(ctx, section, flag::keep_array), item);
return out;
}

View File

@ -9,9 +9,9 @@ include_directories(
add_executable(benchmark benchmark_main.cpp)
target_link_libraries(benchmark mstch)
add_executable(filetoheader filetoheader.cpp)
target_link_libraries(filetoheader ${Boost_PROGRAM_OPTIONS_LIBRARY})
set(filetoheader_exe ${CMAKE_CURRENT_BINARY_DIR}/filetoheader${CMAKE_EXECUTABLE_SUFFIX})
add_executable(headerize headerize.cpp)
target_link_libraries(headerize ${Boost_PROGRAM_OPTIONS_LIBRARY})
set(headerize_exe ${CMAKE_CURRENT_BINARY_DIR}/headerize${CMAKE_EXECUTABLE_SUFFIX})
file(GLOB data_files RELATIVE
"${CMAKE_SOURCE_DIR}/test/data"
@ -27,8 +27,7 @@ file(GLOB string_files RELATIVE
"${CMAKE_SOURCE_DIR}/test/data"
"${CMAKE_SOURCE_DIR}/test/data/*.mustache"
"${CMAKE_SOURCE_DIR}/test/data/*.txt"
"${CMAKE_SOURCE_DIR}/test/data/*.partial"
)
"${CMAKE_SOURCE_DIR}/test/data/*.partial")
foreach(string_file ${string_files})
list(APPEND genargs "-S${string_file}")
@ -36,8 +35,8 @@ endforeach(string_file)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp
COMMAND ${filetoheader_exe} --output ${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp --namespace mstchtest ${genargs}
DEPENDS ${filetoheader_exe}
COMMAND ${headerize_exe} --output ${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp --namespace mstchtest ${genargs}
DEPENDS ${headerize_exe}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/test/data/)
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp PROPERTIES GENERATED TRUE)
add_custom_target(test_data_hpp DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp)

View File

@ -3,6 +3,11 @@
#include <chrono>
#include <iostream>
unsigned long current_msec() {
return std::chrono::system_clock::now().time_since_epoch() /
std::chrono::milliseconds(1);
}
int main() {
std::string comment_tmp{
"<div class=\"comments\"><h3>{{header}}</h3><ul>"
@ -20,20 +25,17 @@ int main() {
}}
};
std::vector<int> times;
std::vector<unsigned long> 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++) {
unsigned long start = current_msec();
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);
times.push_back(current_msec() - start);
}
float avg = 0;
for(int i: times) avg += i;
for (auto i: times)
avg += i;
avg /= times.size();
std::cout << avg << std::endl;

View File

@ -1,82 +0,0 @@
#include <iostream>
#include <fstream>
#include <boost/algorithm/string/replace.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/parsers.hpp>
void wrap_code(std::istream& input, std::ostream& output) {
std::string line;
while (std::getline(input, line)) {
output << line;
if(!input.eof()) output << std::endl;
}
output << std::endl;
}
void wrap_string(std::istream& input, std::ostream& output, const std::string& variable_name) {
output << "const std::string " << variable_name << "{" << std::endl;;
std::string line;
while (std::getline(input, line)) {
boost::replace_all(line, "\\", "\\\\");
boost::replace_all(line, "\"", "\\\"");
output << " \"" << line;
if(!input.eof()) output << "\\n";
output << "\"" << std::endl;
}
output << "};" << std::endl;
}
int main(int argc, char* argv[]) {
namespace po = boost::program_options;
po::options_description desc("Allowed options");
desc.add_options()
("help", "show help")
("output", po::value<std::string>(), "output file")
("namespace", po::value<std::string>(), "namespace to use")
("input-string,S", po::value<std::vector<std::string>>(), "files to parse as strings")
("input-code,C", po::value<std::vector<std::string>>(), "files to parse as code");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help")) {
std::cout << desc << std::endl;
return 1;
}
if (!vm.count("output")) {
std::cout << "Output file not set" << std::endl;
return 1;
}
std::ofstream output(vm["output"].as<std::string>(), std::ios::out);
if(vm.count("namespace"))
output << "namespace " << vm["namespace"].as<std::string>() << " {" << std::endl;
if(vm.count("input-string")) {
for (auto &string_filename: vm["input-string"].as<std::vector<std::string>>()) {
std::ifstream input(string_filename, std::ios::in);
wrap_string(input, output, boost::replace_all_copy(string_filename, ".", "_"));
input.close();
}
}
if(vm.count("input-code")) {
for(auto& data_filename: vm["input-code"].as<std::vector<std::string>>()) {
std::ifstream input(data_filename, std::ios::in);
wrap_code(input, output);
input.close();
}
}
if(vm.count("namespace"))
output << "}" << std::endl;
output.close();
return 0;
}

89
test/headerize.cpp Normal file
View File

@ -0,0 +1,89 @@
#include <iostream>
#include <fstream>
#include <boost/algorithm/string/replace.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/parsers.hpp>
void wrap_code(std::istream& input, std::ostream& output) {
std::string line;
while (std::getline(input, line)) {
output << line;
if (!input.eof())
output << std::endl;
}
output << std::endl;
}
void wrap_string(std::istream& input, std::ostream& output,
const std::string& variable_name)
{
output << "const std::string " << variable_name << "{" << std::endl;;
std::string line;
while (std::getline(input, line)) {
boost::replace_all(line, "\\", "\\\\");
boost::replace_all(line, "\"", "\\\"");
output << " \"" << line;
if (!input.eof())
output << "\\n";
output << "\"" << std::endl;
}
output << "};" << std::endl;
}
int main(int argc, char* argv[]) {
namespace po = boost::program_options;
po::options_description desc("Allowed options");
desc.add_options()
("help", "show help")
("output", po::value<std::string>(), "output file")
("namespace", po::value<std::string>(), "namespace to use")
("input-string,S", po::value<std::vector<std::string>>(),
"files to parse as strings")
("input-code,C", po::value<std::vector<std::string>>(),
"files to parse as code");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help")) {
std::cout << desc << std::endl;
return 1;
}
if (!vm.count("output")) {
std::cout << "Output file not set" << std::endl;
return 1;
}
std::ofstream output(vm["output"].as<std::string>(), std::ios::out);
if (vm.count("namespace"))
output << "namespace " << vm["namespace"].as<std::string>() << " {" << std::endl;
if (vm.count("input-string")) {
for (auto& string_filename: vm["input-string"].as<std::vector<std::string>>()) {
std::ifstream input(string_filename, std::ios::in);
wrap_string(input, output,
boost::replace_all_copy(string_filename, ".", "_"));
input.close();
}
}
if (vm.count("input-code")) {
for (auto& data_filename: vm["input-code"].as<std::vector<std::string>>()) {
std::ifstream input(data_filename, std::ios::in);
wrap_code(input, output);
input.close();
}
}
if (vm.count("namespace"))
output << "}" << std::endl;
output.close();
return 0;
}