diff --git a/examples/Jamfile b/examples/Jamfile index a52f006..493131f 100644 --- a/examples/Jamfile +++ b/examples/Jamfile @@ -14,6 +14,10 @@ project boostache_examples . ; +exe django + : django.cpp + ; + exe example1 : example1.cpp ; diff --git a/examples/django.cpp b/examples/django.cpp new file mode 100644 index 0000000..8dc5e7c --- /dev/null +++ b/examples/django.cpp @@ -0,0 +1,133 @@ +/** + * \file django.cpp + * + * A simple example of how to use boostache. + * + * Copyright 2015 Jeroen Habraken + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS + +#include +#include +#include // need to work out header only syntax +#include +#include +#include + +namespace boostache = boost::boostache; + +struct my_node_t; +using map_t = std::map; +using list_t = std::vector; +struct my_node_t : boost::spirit::extended_variant< + bool + , std::string + , map_t + , list_t + > +{ + my_node_t() : base_type() {} + my_node_t(bool rhs) : base_type(rhs) {} + my_node_t(std::string const & rhs) : base_type(rhs) {} + my_node_t(char const * rhs) : base_type(std::string{rhs}) {} + my_node_t(map_t const & rhs) : base_type(rhs) {} + my_node_t(list_t const & rhs) : base_type(rhs) {} +}; + +int django() +{ + // ------------------------------------------------------------------ + // Describe the input template. We are going to use django format. + std::string input( + //"try fifdgrst.sefdshcond.another.name: {{fifdgrst.sefdshcond.another.name}}. \n" + //"try another.name: {{another.name}}.\n" + //"{%% for contact in people %%}" + //"My name is {{fifdgrst.sefdshcond.another.name}}.\n" + //"{%% endfor %%}" + //"-----------------------\n" + //"{%% for contact in people %%}" + //"{{contact.job}}\n" + ////"now from a nested context: people\n" + ////"try fifdgrst.sefdshcond.another.name: {{fifdgrst.sefdshcond.another.name}}. \n" + ////"try another.name: {{another.name}}.\n" + //"{%% endfor %%}" + "{%% for contact in people %%}" + "{%% for more_contact in more_people %%}" + "{{more_contact.job}}\n" + //"now from a nested context: people\n" + //"try fifdgrst.sefdshcond.another.name: {{fifdgrst.sefdshcond.another.name}}. \n" + //"try another.name: {{another.name}}.\n" + "{%% endfor %%}" + "{%% endfor %%}" + + // "{# This is a comment #}" + // "I am {{pet}} years old." + //"{%% for contact in people %%}" + // "a contact\n" + // "My name is still {{another.name}}. \n\n" + // "{%% endfor %%}" + // "{%% if dontshowme %%}" + // "Yep" + // "{%% else %%}" + // "Nope" + // "{%% endif %%}\n" + ); + // ------------------------------------------------------------------ + + // ------------------------------------------------------------------ + // The data model definition + map_t data = { + {"contacts" , map_t{{"foo","gorp"}}}, + {"foo" , "bar"}, + {"me" , "Daniel"}, + {"pet" , "turtles"}, + {"lpet" , "Turtles"}, + {"people" , list_t{ map_t{{"name" , "Tom"}, + {"job" , "sweep floors"} }, + map_t{{"name" , "Sue"}, + {"job" , "write code"} } + } + }, + {"title" , "Multiple Mustaches"}, + {"comment" , "this shouldn't be here"}, + {"showme" , true}, + {"showme2" , true}, + {"dontshowme" , false}, + {"next_more" , "I like {{pet}}."}, + {"another" , map_t{{"name" , "Sam"}, + {"ok" , true }, + {"not_ok" , false}} + }, + { "more_people" , list_t{ map_t{ { "name" , "Peter" }, + { "job" , "lazy" } }, + map_t{ { "name" , "Barbara" }, + { "job" , "chef" } } + } + }, + }; + // ------------------------------------------------------------------ + + // ------------------------------------------------------------------ + // Load the template. + // This parses the input and compiles the result. The return is the + // compiled data structure + using boostache::load_template; + + auto iter = input.begin(); + auto templ = load_template(iter, input.end()); + // ------------------------------------------------------------------ + + // ------------------------------------------------------------------ + // Apply the compiled template and the data model to generate the result + std::stringstream stream; + boostache::generate(stream, templ, data); + // ------------------------------------------------------------------ + + // print the result + std::cout << stream.str(); + + return 0; +} diff --git a/examples/example1.cpp b/examples/example1.cpp index 2dc8625..fb5fb45 100644 --- a/examples/example1.cpp +++ b/examples/example1.cpp @@ -24,7 +24,7 @@ namespace boostache = boost::boostache; using map_t = std::map; -int main() +int example1() { // ------------------------------------------------------------------ // Describe the input template. We are going to use mustache format. @@ -57,4 +57,6 @@ int main() // print the result std::cout << stream.str(); + + return 0; } diff --git a/examples/example2.cpp b/examples/example2.cpp index ffb301d..e4db631 100644 --- a/examples/example2.cpp +++ b/examples/example2.cpp @@ -32,7 +32,7 @@ using invoice_t = std::map; // ------------------------------------------------------- -int main() +int example2() { // ------------------------------------------------------------------ // The template describing an invoice. @@ -81,4 +81,6 @@ int main() // ------------------------------------------------------------------ std::cout << stream.str(); + + return 0; } diff --git a/examples/example3.cpp b/examples/example3.cpp index ef62887..117790b 100644 --- a/examples/example3.cpp +++ b/examples/example3.cpp @@ -56,7 +56,7 @@ struct value_t : boost::spirit::extended_variant< std::string // ------------------------------------------------------- -int main() +int example3() { // ------------------------------------------------------------------ // The template describing an invoice. @@ -65,7 +65,17 @@ int main() "\n" "{{# company}}" "Company: {{name}}\n" - " {{street}}\n" + "Invoice again: {{invoice_number}}\n" + "{{#invoice_number}}\n" + "has invoice\n" + "{{/invoice_number}}\n" + "{{#paied}}\n" + "has paied\n" + "{{/paied}}\n" + "{{^paied}}\n" + "has not paied\n" + "{{/paied}}\n" + " {{street}}\n" " {{city}}, {{state}} {{zip}}\n" "{{/ company}}" "------------------------------------------------\n" @@ -114,4 +124,6 @@ int main() // ------------------------------------------------------------------ std::cout << stream.str(); + + return 0; } diff --git a/examples/main.cpp b/examples/main.cpp new file mode 100644 index 0000000..077f907 --- /dev/null +++ b/examples/main.cpp @@ -0,0 +1,15 @@ + +int example1(); +int example2(); +int example3(); +int django(); +int simple_generate2(); + +int main() +{ + //example1(); + //example2(); + //example3(); + django(); + //simple_generate2(); +} \ No newline at end of file diff --git a/examples/simple_generate2.cpp b/examples/simple_generate2.cpp index 0e2b968..e851395 100644 --- a/examples/simple_generate2.cpp +++ b/examples/simple_generate2.cpp @@ -64,7 +64,7 @@ namespace extn = bstache::extension; -int main() +int simple_generate2() { // ------------------------------------------------------------------ // The input template diff --git a/include/boost/boostache/backend/detail/django_compiler.hpp b/include/boost/boostache/backend/detail/django_compiler.hpp new file mode 100644 index 0000000..c5ccccd --- /dev/null +++ b/include/boost/boostache/backend/detail/django_compiler.hpp @@ -0,0 +1,144 @@ +/** + * \file detail/django_compiler.hpp + * + * Copyright 2014 Michael Caisse : ciere.com + * Copyright 2017 Tobias Loew : die-loews.de + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef BOOST_BOOSTACHE_BACKEND_DETAIL_DJANGO_COMPILER_HPP +#define BOOST_BOOSTACHE_BACKEND_DETAIL_DJANGO_COMPILER_HPP + +#include +#include +#include + +namespace boost { namespace boostache { namespace backend { namespace django_compiler +{ + namespace fe = boost::boostache::frontend; + namespace detail + { + class django_visit + { + public: + typedef vm::ast::node result_type; + + django_visit(std::ostream& out) + : out(out) + {} + + vm::ast::node operator()(fe::django::ast::undefined) const + { + out << "WHOA! we have an undefined" << std::endl; + return vm::ast::node{}; + } + + vm::ast::node operator()(fe::django::ast::literal_text const & v) const + { + return vm::ast::literal{v}; + } + + vm::ast::node operator()(fe::django::ast::variable const & v) const + { + vm::ast::node body = vm::ast::render{v.back()}; + for(auto iter = ++v.rbegin(); iter != v.rend(); ++iter) + { + vm::ast::select_context select; + select.tag = *iter; + select.body = std::move(body); + select.make_local = true; + body = std::move(select); + } + return body; + } + + vm::ast::node operator()(fe::django::ast::comment const & v) const + { + return vm::ast::literal{}; + } + + vm::ast::node operator()(fe::django::ast::node_list const & nodes) const + { + vm::ast::node_list node_list; + for(auto const & node : nodes) + { + node_list.nodes.push_back(boost::apply_visitor(*this, node)); + } + return node_list; + } + + vm::ast::node operator()(fe::django::ast::if_elif_else const & if_elif_else) const + { + vm::ast::node_list then_; + for(auto const & node : if_elif_else.if_.body) + { + then_.nodes.push_back(boost::apply_visitor(*this, node)); + } + + vm::ast::if_then_else if_then_else; + if_then_else.condition_.name = if_elif_else.if_.condition_.front(); + if_then_else.then_ = std::move(then_); + if(static_cast(if_elif_else.else_)) + { + vm::ast::node_list else_; + for(auto const & node : if_elif_else.else_.get()) + { + else_.nodes.push_back(boost::apply_visitor(*this, node)); + } + if_then_else.else_ = std::move(else_); + } + + return if_then_else; + } + + vm::ast::node operator()(fe::django::ast::for_in const & for_in) const + { + vm::ast::node_list vm_ast; + for (auto const & node : for_in.body) + { + vm_ast.nodes.push_back(boost::apply_visitor(*this, node)); + } + + vm::ast::for_each for_each_; + for_each_.name = for_in.iterator; + for_each_.value = vm_ast; + + vm::ast::node body = for_each_; + for (auto iter = for_in.set.begin(); iter != for_in.set.end(); ++iter) + { + vm::ast::select_context select; + select.tag = *iter; + select.body = std::move(body); + body = std::move(select); + } + return body; + + //out << "WHOA! for_in not yet implemented" << std::endl; + //return vm::ast::node{}; + } + + vm::ast::node operator()(fe::django::ast::root const & nodes) const + { + vm::ast::node_list node_list; + for(auto const & node : nodes) + { + node_list.nodes.push_back(boost::apply_visitor(*this, node)); + } + return node_list; + } + + private: + std::ostream& out; + }; + } + + inline vm::ast::node compile(fe::django::ast::root const & ast) + { + detail::django_visit visit(std::cout); + return visit(ast); + } +}}}} + +#endif + diff --git a/include/boost/boostache/backend/detail/stache_compiler.hpp b/include/boost/boostache/backend/detail/stache_compiler.hpp index ce148c6..a5407e4 100644 --- a/include/boost/boostache/backend/detail/stache_compiler.hpp +++ b/include/boost/boostache/backend/detail/stache_compiler.hpp @@ -23,7 +23,7 @@ namespace boost { namespace boostache { namespace backend { namespace stache_com /** * Check if the string simply contains white-space. */ - bool is_blank(std::string const & s) + inline bool is_blank(std::string const & s) { return( s.find_first_not_of(std::string{" \t\r\n"}) == std::string::npos ); @@ -170,7 +170,8 @@ namespace boost { namespace boostache { namespace backend { namespace stache_com else { vm::ast::for_each section_body; - section_body.name = sec.name; +// section_body.name not used in moustache section +// section_body.name = sec.name; section_body.value = vm_ast; vm::ast::select_context select; diff --git a/include/boost/boostache/backend/django_compiler.hpp b/include/boost/boostache/backend/django_compiler.hpp new file mode 100644 index 0000000..74e9ff0 --- /dev/null +++ b/include/boost/boostache/backend/django_compiler.hpp @@ -0,0 +1,24 @@ +/** + * \file stache_compiler.hpp + * + * Copyright 2014 Michael Caisse : ciere.com + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef BOOST_DJANGO_BACKEND_STACHE_COMPILER_HPP +#define BOOST_DJANGO_BACKEND_STACHE_COMPILER_HPP + +#include +#include +#include + +namespace boost { namespace boostache { namespace backend +{ + inline vm::ast::node compile(frontend::django::ast::root const & ast) + { + return django_compiler::compile(ast); + } +}}} + +#endif diff --git a/include/boost/boostache/boostache.hpp b/include/boost/boostache/boostache.hpp index 52776b5..dd77dac 100644 --- a/include/boost/boostache/boostache.hpp +++ b/include/boost/boostache/boostache.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include diff --git a/include/boost/boostache/django.hpp b/include/boost/boostache/django.hpp new file mode 100644 index 0000000..8d3b4a7 --- /dev/null +++ b/include/boost/boostache/django.hpp @@ -0,0 +1,29 @@ +/** + * \file django.hpp + * + * Copyright 2015 Jeroen Habraken + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef BOOST_BOOSTACHE_FRONT_END_DJANGO_HPP +#define BOOST_BOOSTACHE_FRONT_END_DJANGO_HPP + +#include +#include +#include + +namespace boost { namespace boostache { namespace format +{ + struct django + { + template + using grammar_t = frontend::django::grammar; + + using ast_t = frontend::django::ast::root; + using skipper_t = boost::spirit::qi::space_type; + }; +}}} + + +#endif diff --git a/include/boost/boostache/frontend/django/ast.hpp b/include/boost/boostache/frontend/django/ast.hpp new file mode 100644 index 0000000..cd9ca97 --- /dev/null +++ b/include/boost/boostache/frontend/django/ast.hpp @@ -0,0 +1,82 @@ +/** + * \file ast.hpp + * + * Copyright 2015 Jeroen Habraken + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef BOOST_BOOSTACHE_FRONT_END_DJANGO_AST_HPP +#define BOOST_BOOSTACHE_FRONT_END_DJANGO_AST_HPP + +#include +#include + +#include +#include + +namespace boost { namespace boostache { namespace frontend { namespace django { namespace ast +{ + struct node; + + struct undefined {}; + + struct comment {}; + + struct identifier : std::string + {}; + + struct literal_text : std::string + {}; + + struct variable : std::vector + {}; + + struct if_elif_else; + + struct for_in; + + struct node : boost::spirit::extended_variant< + undefined + , comment + , literal_text + , variable + , boost::recursive_wrapper + , boost::recursive_wrapper + > + { + node() : base_type() {} + node(comment const & rhs) : base_type(rhs) {} + node(literal_text const & rhs) : base_type(rhs) {} + node(variable const & rhs) : base_type(rhs) {} + node(if_elif_else const & rhs) : base_type(rhs) {} + node(for_in const & rhs) : base_type(rhs) {} + }; + + struct node_list : std::vector {}; + + struct condition + { + variable condition_; + node_list body; + }; + + struct if_elif_else + { + condition if_; + std::vector elif; + boost::optional else_; + }; + + struct for_in + { + identifier iterator; + variable set; + node_list body; + }; + + struct root : node_list {}; + +}}}}} + +#endif diff --git a/include/boost/boostache/frontend/django/ast_adapted.hpp b/include/boost/boostache/frontend/django/ast_adapted.hpp new file mode 100644 index 0000000..cab8821 --- /dev/null +++ b/include/boost/boostache/frontend/django/ast_adapted.hpp @@ -0,0 +1,38 @@ +/** + * \file ast_adapted.hpp + * + * Fusion adaption for the stache ast + * + * Copyright 2015 Jeroen Habraken + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef BOOST_BOOSTACHE_FRONT_END_DJANGO_AST_ADAPTED_HPP +#define BOOST_BOOSTACHE_FRONT_END_DJANGO_AST_ADAPTED_HPP + +#include +#include + +BOOST_FUSION_ADAPT_STRUCT( + boost::boostache::frontend::django::ast::condition, + (boost::boostache::frontend::django::ast::variable, condition_) + (boost::boostache::frontend::django::ast::node_list, body) +) + +BOOST_FUSION_ADAPT_STRUCT( + boost::boostache::frontend::django::ast::if_elif_else, + (boost::boostache::frontend::django::ast::condition, if_) + (std::vector, elif) + (boost::optional, else_) +) + +BOOST_FUSION_ADAPT_STRUCT( + boost::boostache::frontend::django::ast::for_in, + (boost::boostache::frontend::django::ast::identifier, iterator) + (boost::boostache::frontend::django::ast::variable, set) + (boost::boostache::frontend::django::ast::node_list, body) +) + +#endif + diff --git a/include/boost/boostache/frontend/django/grammar.hpp b/include/boost/boostache/frontend/django/grammar.hpp new file mode 100644 index 0000000..b4a6945 --- /dev/null +++ b/include/boost/boostache/frontend/django/grammar.hpp @@ -0,0 +1,65 @@ +/** + * \file grammar.hpp + * + * Copyright 2015 Jeroen Habraken + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef BOOST_BOOSTACHE_FRONT_END_DJANGO_GRAMMAR_HPP +#define BOOST_BOOSTACHE_FRONT_END_DJANGO_GRAMMAR_HPP + +#include +#include +#include + +namespace boost { namespace boostache { namespace frontend { namespace django +{ + namespace qi = boost::spirit::qi; + + template + struct grammar + : qi::grammar + { + grammar(); + + qi::rule + node_list + ; + + qi::rule + node + ; + + qi::rule + identifier + ; + + qi::rule + comment + ; + + qi::rule + literal_text + ; + + qi::rule + variable + ; + + qi::rule + condition + ; + + qi::rule + if_elif_else + ; + + qi::rule + for_in + ; + }; +}}}} + +#endif + diff --git a/include/boost/boostache/frontend/django/grammar_def.hpp b/include/boost/boostache/frontend/django/grammar_def.hpp new file mode 100644 index 0000000..267cb1a --- /dev/null +++ b/include/boost/boostache/frontend/django/grammar_def.hpp @@ -0,0 +1,127 @@ +/** + * \file grammar_def.hpp + * + * Copyright 2015 Jeroen Habraken + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef BOOST_BOOSTACHE_FRONT_END_DJANGO_GRAMMAR_DEF_HPP +#define BOOST_BOOSTACHE_FRONT_END_DJANGO_GRAMMAR_DEF_HPP + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace boostache { namespace frontend { namespace django +{ + namespace qi = boost::spirit::qi; + namespace spirit = boost::spirit; + + template + grammar::grammar() + : grammar::base_type(node_list) + { + qi::alnum_type alnum; + qi::alpha_type alpha; + qi::attr_type attr; + qi::char_type char_; + qi::lexeme_type lexeme; + qi::lit_type lit; + qi::no_skip_type no_skip; + qi::omit_type omit; + + node = + no_skip[literal_text] + | comment + | variable + | if_elif_else + | for_in + ; + + node_list = + *node + ; + + literal_text = + +(char_ - (lit("{{") | "{%%" | "{#")) + ; + + comment = + lit("{#") + >> omit[*(char_ - "#}")] + >> "#}" + ; + + identifier = + alpha >> *(alnum | char_('_')) + ; + + variable = + lit("{{") + >> lexeme[identifier % "."] + >> "}}" + ; + + condition = + lexeme[identifier % "."] + >> "%%}" + >> node_list + ; + + if_elif_else = + lit("{%%") + >> "if" + >> condition + >> *( + lit("{%%") + >> "elif" + >> condition + ) + >> -( + lit("{%%") + >> "else" + >> "%%}" + >> node_list + ) + >> "{%%" + >> "endif" + >> "%%}" + ; + + for_in = + lit("{%%") + >> "for" + >> identifier + >> "in" + >> lexeme[identifier % "."] + >> "%%}" + >> node_list + >> "{%%" + >> "endfor" + >> "%%}" + ; + }; +}}}} + +#endif diff --git a/include/boost/boostache/frontend/django/printer.hpp b/include/boost/boostache/frontend/django/printer.hpp new file mode 100644 index 0000000..264f6ed --- /dev/null +++ b/include/boost/boostache/frontend/django/printer.hpp @@ -0,0 +1,85 @@ +/** + * \file django/printer.hpp + * + * Copyright 2015 Michael Caisse : ciere.com + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef BOOST_BOOSTACHE_FRONT_END_DJANGO_PRINTER_HPP +#define BOOST_BOOSTACHE_FRONT_END_DJANGO_PRINTER_HPP + +#include +#include + +namespace boost { namespace boostache { namespace frontend { namespace django { namespace ast +{ + namespace detail + { + class printer + { + public: + typedef void result_type; + + printer(std::ostream& out) + : out(out) + {} + + template + void operator()(T& v) const + { + out << "WHOA! We have an unimplemented type: " + << typeid(v).name() << std::endl; + } + + void operator()(undefined) const + { + out << "undefined" << std::endl; + } + + void operator()(comment) const + { + out << "{# some comment #}"; + } + + void operator()(literal_text const & v) const + { + out << v; + } + + void operator()(variable const & var) const + { + out << "{% "; + auto iter = var.begin(); + auto iter_end = var.end(); + while(iter != iter_end) + { + out << *iter; + if(++iter != iter_end) + { + out << "."; + } + } + } + + void operator()(if_elif_else const & v) const + { + // TODO + } + + private: + std::ostream& out; + }; + } + + inline void print(std::ostream& out, node_list const& nodes) + { + detail::printer p(out); + for(auto const & node : nodes) + { + boost::apply_visitor(p, node); + } + } +}}}}} + +#endif diff --git a/include/boost/boostache/frontend/parse.hpp b/include/boost/boostache/frontend/parse.hpp index f7d3a99..398520e 100644 --- a/include/boost/boostache/frontend/parse.hpp +++ b/include/boost/boostache/frontend/parse.hpp @@ -27,13 +27,13 @@ namespace boost { namespace boostache { namespace frontend typename Format::template grammar_t grammar; // TODO mjc : should throw with parse error location - // if(!boost::spirit::qi::phrase_parse( begin, end - // , grammar - // , typename Format::skipper_t{} - // , ast )) - if(!boost::spirit::qi::parse( begin, end - , grammar - , ast )) + if(!boost::spirit::qi::phrase_parse( begin, end + , grammar + , typename Format::skipper_t{} + , ast )) + //if(!boost::spirit::qi::parse( begin, end + // , grammar + // , ast )) { ast = typename Format::ast_t{}; } diff --git a/include/boost/boostache/model/basic_render_extension.hpp b/include/boost/boostache/model/basic_render_extension.hpp index 42df975..7607e89 100644 --- a/include/boost/boostache/model/basic_render_extension.hpp +++ b/include/boost/boostache/model/basic_render_extension.hpp @@ -19,64 +19,111 @@ namespace boost { namespace boostache { namespace extension { template< typename Stream, typename T > - void render(Stream & stream, T const & context, std::string const & name); + bool render(Stream & stream, T const & context, std::string const & name); template< typename Stream - , typename T - , typename Enable = typename std::enable_if::value>::type - > - auto render( Stream && stream, T const & context, std::string const & name - , plain_attribute) -> decltype(std::forward(stream)<::value>::type + > + bool render(Stream && stream, T const & context, std::string const & name + , plain_attribute) { - return (std::forward(stream) << context); + (std::forward(stream) << context); + return true; } template< typename Stream , typename T > - void render( Stream && stream, T const & context, std::string const & name + bool render( Stream && stream, T const & context, std::string const & name , optional_attribute) { - render(std::forward(stream),*context,name); + if (context) + { + render(std::forward(stream), *context, name); + } + return true; } template< typename Stream , typename T > - void render( Stream && stream, T const & context, std::string const & name + bool render( Stream && stream, T const & context, std::string const & name , unused_attribute) { + return true; } template< typename Stream , typename T > - void render( Stream && stream, T const & context, std::string const & name + bool render( Stream && stream, T const & context, std::string const & name , associative_attribute) { auto iter = context.find(name); if(iter!=context.end()) { render(std::forward(stream),iter->second,name); - } + return true; + } + else + { + return false; + } } template< typename Stream - , typename T - > - void render( Stream && stream, T const & context, std::string const & name - , sequence_attribute) + , typename T + > + bool render(Stream && stream, T const & context, std::string const & name + , sequence_attribute) { - for(auto const & item : context) - { - render(std::forward(stream),item,name); - } + for (auto const & item : context) + { + render(std::forward(stream), item, name); + } + return true; + } + + + template< typename Stream + , typename T + > + bool render(Stream && stream, T const & context, std::string const & name + , stacked_context_attribute) + { + if (!render(std::forward(stream), context.child, name)) + { + return render(std::forward(stream), context.parent, name); + } + else + { + return true; + } + } + + + template< typename Stream + , typename T + > + bool render(Stream && stream, T const & context, std::string const & name + , multi_context_attribute) + { + if (!render(std::forward(stream), context.context, name)) + { + return false; +// return render(std::forward(stream), context.parent, name); + } + else + { + return true; + } } @@ -84,9 +131,9 @@ namespace boost { namespace boostache { namespace extension // -------------------------------------------------------------------------- template< typename Stream, typename T > - void render(Stream & stream, T const & context, std::string const & name) + bool render(Stream & stream, T const & context, std::string const & name) { - render( stream + return render( stream , context , name , render_category_t{} ); diff --git a/include/boost/boostache/model/basic_test_extension.hpp b/include/boost/boostache/model/basic_test_extension.hpp index ba3ff8f..f1b0867 100644 --- a/include/boost/boostache/model/basic_test_extension.hpp +++ b/include/boost/boostache/model/basic_test_extension.hpp @@ -18,6 +18,8 @@ namespace boost { namespace boostache { namespace extension { + struct optional_test_tag {}; + // -------------------------------------------------------------------------- // Test // -------------------------------------------------------------------------- @@ -68,37 +70,39 @@ namespace boost { namespace boostache { namespace extension template bool test(T const & context, std::string const & tag); + template + boost::optional test(T const & context, std::string const & tag, optional_test_tag); template - bool test( T const & context, std::string const & tag + boost::optional test( T const & context, std::string const & tag , unused_attribute) { return test(context, unused_attribute{}); } template - bool test( T const & context, std::string const & tag + boost::optional test( T const & context, std::string const & tag , plain_attribute) { return test(context, plain_attribute{}); } template - bool test( T const & context, std::string const & tag + boost::optional test( T const & context, std::string const & tag , sequence_attribute) { return test(context, sequence_attribute{}); } template - bool test( T const & context, std::string const & tag + boost::optional test( T const & context, std::string const & tag , optional_attribute) { return test(context, optional_attribute{}); } template - bool test( T const & context, std::string const & tag + boost::optional test( T const & context, std::string const & tag , associative_attribute) { auto iter = context.find(tag); @@ -109,27 +113,68 @@ namespace boost { namespace boostache { namespace extension } else { - return false; + return boost::none; } } + template + boost::optional test(T const & context, std::string const & tag + , stacked_context_attribute) + { + auto&& optional_result = test(context.child, tag, optional_test_tag{}); + if (optional_result) + { + return *optional_result; + } + else + { + return test(context.parent, tag); + } + } + + template + boost::optional test(T const & context, std::string const & tag + , multi_context_attribute) + { + auto&& optional_result = test(context.context, tag, optional_test_tag{}); + if (optional_result) + { + return *optional_result; + } + else + { + return boost::none; +// return test(context.parent, tag); + } + } + // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- template bool test(T const & context) { - return test( context - , test_category_t{}); + return test(context + , test_category_t{}); } template bool test(T const & context, std::string const & tag) { - return test( context - , tag - , test_category_t{}); + return test(context + , tag + , test_category_t{} + ).value_or(false); + } + + template + boost::optional test(T const & context, std::string const & tag, optional_test_tag) + { + return test(context + , tag + , test_category_t{} + ); } }}} diff --git a/include/boost/boostache/model/category.hpp b/include/boost/boostache/model/category.hpp index 33debd1..44561e0 100644 --- a/include/boost/boostache/model/category.hpp +++ b/include/boost/boostache/model/category.hpp @@ -2,6 +2,7 @@ * \file category.hpp * * Copyright 2014, 2015 Michael Caisse : ciere.com + * Copyright 2017 Tobias Loew : die-loews.de * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -22,6 +23,8 @@ namespace boost { namespace boostache { namespace extension struct tuple_attribute : category_attribute {}; struct variant_attribute : category_attribute {}; struct optional_attribute : category_attribute {}; + struct stacked_context_attribute : category_attribute {}; + struct multi_context_attribute : category_attribute {}; }}} diff --git a/include/boost/boostache/model/render_traits.hpp b/include/boost/boostache/model/render_traits.hpp index d28688e..4384fd6 100644 --- a/include/boost/boostache/model/render_traits.hpp +++ b/include/boost/boostache/model/render_traits.hpp @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include #include @@ -46,6 +48,15 @@ namespace boost { namespace boostache { namespace extension > : mpl::identity {}; + template + struct render_category> + : mpl::identity {}; + + template + struct render_category> + : mpl::identity {}; + + template using render_category_t = typename render_category::type; diff --git a/include/boost/boostache/model/select_traits.hpp b/include/boost/boostache/model/select_traits.hpp index 50c7533..1651950 100644 --- a/include/boost/boostache/model/select_traits.hpp +++ b/include/boost/boostache/model/select_traits.hpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -42,8 +44,16 @@ namespace boost { namespace boostache { namespace extension : mpl::identity {}; template - struct select_category> - : mpl::identity {}; + struct select_category> + : mpl::identity {}; + + template + struct select_category> + : mpl::identity {}; + + template + struct select_category> + : mpl::identity {}; template using select_category_t = typename select_category::type; diff --git a/include/boost/boostache/model/test_traits.hpp b/include/boost/boostache/model/test_traits.hpp index 11e125a..3ae21aa 100644 --- a/include/boost/boostache/model/test_traits.hpp +++ b/include/boost/boostache/model/test_traits.hpp @@ -2,6 +2,7 @@ * \file test_traits.hpp * * Copyright 2014, 2015 Michael Caisse : ciere.com + * Copyright 2017 Tobias Loew : die-loews.de * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,6 +12,7 @@ #include #include +#include #include #include #include @@ -54,6 +56,14 @@ namespace boost { namespace boostache { namespace extension struct test_category> : mpl::identity {}; + template + struct test_category> + : mpl::identity {}; + + template + struct test_category> + : mpl::identity {}; + template using test_category_t = typename test_category::type; }}} diff --git a/include/boost/boostache/model/unwrap_variant.hpp b/include/boost/boostache/model/unwrap_variant.hpp index acbbe1c..2ffe4a8 100644 --- a/include/boost/boostache/model/unwrap_variant.hpp +++ b/include/boost/boostache/model/unwrap_variant.hpp @@ -20,13 +20,13 @@ namespace boost { namespace boostache { namespace extension { template - bool test( T const & context, std::string const & tag + boost::optional test( T const & context, std::string const & tag , variant_attribute) { - return boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( + return boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor>( [&tag](auto ctx) { - return test(ctx, tag); + return test(ctx, tag, optional_test_tag{}); } ) , context); @@ -48,16 +48,18 @@ namespace boost { namespace boostache { namespace extension template< typename Stream, typename T > - void render( Stream & stream, T const & context, std::string const & name + bool render( Stream & stream, T const & context, std::string const & name , variant_attribute) { - return boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( + boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( [&stream,&name](auto ctx) { render(stream,ctx,name); } ) , context); + + return true; } }}} diff --git a/include/boost/boostache/vm/detail/engine_visitor.hpp b/include/boost/boostache/vm/detail/engine_visitor.hpp index bc9f40f..aacb140 100644 --- a/include/boost/boostache/vm/detail/engine_visitor.hpp +++ b/include/boost/boostache/vm/detail/engine_visitor.hpp @@ -2,6 +2,7 @@ * \file detail/engine_visitor.hpp * * Copyright 2014, 2015 Michael Caisse : ciere.com + * Copyright 2017 Tobias Loew : die-loews.de * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -20,9 +21,10 @@ namespace boost { namespace boostache { namespace extension { template - void render(Stream & stream, Object const & v) + bool render(Stream & stream, Object const & v) { stream << v; + return true; } template @@ -32,7 +34,7 @@ namespace boost { namespace boostache { namespace extension bool test(T const & context); template< typename Stream, typename T > - void render(Stream & stream, T const & context, std::string const & name); + bool render(Stream & stream, T const & context, std::string const & name); }}} ///////////////////////////////////////////////////// @@ -40,18 +42,23 @@ namespace boost { namespace boostache { namespace extension namespace boost { namespace boostache { namespace vm { namespace detail { + + + + + template class engine_visitor_base { public: typedef void result_type; - engine_visitor_base(Stream & s, Context const & c) - : stream(s) - , context(c) - {} + engine_visitor_base(Stream & s, Context const & c) + : stream(s) + , context(c) + {} - void operator()(ast::undefined) const + void operator()(ast::undefined) const {} void operator()(ast::nop) const @@ -93,8 +100,7 @@ namespace boost { namespace boostache { namespace vm { namespace detail void operator()(ast::select_context const & select_ctx) const { - select_context_dispatch( stream, select_ctx, context - , extension::select_category_t{} ); + select_context_dispatch( stream, select_ctx, context); } void operator()(ast::node_list const & nodes) const @@ -112,7 +118,7 @@ namespace boost { namespace boostache { namespace vm { namespace detail private: Stream & stream; - Context const & context; + Context const & context; }; diff --git a/include/boost/boostache/vm/detail/foreach.hpp b/include/boost/boostache/vm/detail/foreach.hpp index 507e7e6..3ef66a6 100644 --- a/include/boost/boostache/vm/detail/foreach.hpp +++ b/include/boost/boostache/vm/detail/foreach.hpp @@ -2,6 +2,7 @@ * \file detail/foreach.hpp * * Copyright 2014, 2015 Michael Caisse : ciere.com + * Copyright 2017 Tobias Loew : die-loews.de * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,6 +11,7 @@ #define BOOST_BOOSTACHE_VM_DETAIL_FOREACH_HPP #include +#include #include #include #include @@ -76,6 +78,11 @@ namespace boost { namespace boostache { namespace extension struct foreach_category> : mpl::identity {}; + // returns the category of the child context !!! + template + struct foreach_category> + : foreach_category {}; + template using foreach_category_t = typename foreach_category::type; @@ -84,7 +91,34 @@ namespace boost { namespace boostache { namespace extension namespace boost { namespace boostache { namespace vm { namespace detail { - template + template + const Context & extract_child_context(const Context & context) + { + return context; + } + + template + const ChildContext & extract_child_context(const stacked_context & context) + { + return context.child; + } + + template + boost::optional extract_bound_variable(const Context & context) + { + return boost::none; + } + + template + boost::optional extract_bound_variable(const stacked_context & context) + { + return context.bound_variable; + } + + + + + template void foreach( Stream & stream , Node const & node , Context const & context @@ -101,12 +135,12 @@ namespace boost { namespace boostache { namespace vm { namespace detail , extension::variant_attribute) { boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( - [&stream,&node](auto ctx) + [&stream,&node,&context](auto ctx) { - vm::detail::foreach(stream, node, ctx); + vm::detail::foreach(stream, node, merge_contexts(context, ctx, extract_bound_variable(context))); } ) - , context); + , extract_child_context(context)); } @@ -116,9 +150,9 @@ namespace boost { namespace boostache { namespace vm { namespace detail , Context const & context , extension::sequence_attribute) { - for(auto const & item : context) + for(auto const & item : extract_child_context(context)) { - generate(stream, node.value, item); + generate(stream, node.value, merge_contexts(context, item, extract_bound_variable(context))); } } @@ -126,13 +160,13 @@ namespace boost { namespace boostache { namespace vm { namespace detail template void foreach( Stream & stream , Node const & node - , Context const & ctx + , Context const & context , extension::optional_attribute) { + auto&& ctx = extract_child_context(context); if(ctx) { - foreach( stream, node, *ctx - , extension::foreach_category_t{}); + foreach( stream, node, merge_contexts(context, *ctx, extract_bound_variable(context))); } else { @@ -141,6 +175,7 @@ namespace boost { namespace boostache { namespace vm { namespace detail } + /** * Entry point for foreach */ diff --git a/include/boost/boostache/vm/detail/multi_context.hpp b/include/boost/boostache/vm/detail/multi_context.hpp new file mode 100644 index 0000000..bf83bc9 --- /dev/null +++ b/include/boost/boostache/vm/detail/multi_context.hpp @@ -0,0 +1,41 @@ +/** + * \file detail/select_context.hpp + * + * Copyright 2017 Tobias Loew : die-loews.de + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef BOOST_BOOSTACHE_VM_DETAIL_MULTI_CONTEXT_HPP +#define BOOST_BOOSTACHE_VM_DETAIL_MULTI_CONTEXT_HPP + +#include +#include +#include + + +namespace boost { namespace boostache { namespace vm { namespace detail +{ + template + struct multi_context { + multi_context(Context const & context, std::function const & fun) + : context(context) + , fun(fun) + {} + + Context const & context; + std::function const & fun; + }; + + + template + auto make_multi_context(Context const & context, std::function const & fun) + { + return multi_context{context, fun}; + } + + +}}}} + + +#endif diff --git a/include/boost/boostache/vm/detail/select_context.hpp b/include/boost/boostache/vm/detail/select_context.hpp index 1d566c0..d95b6f0 100644 --- a/include/boost/boostache/vm/detail/select_context.hpp +++ b/include/boost/boostache/vm/detail/select_context.hpp @@ -2,6 +2,7 @@ * \file detail/select_context.hpp * * Copyright 2015 Michael Caisse : ciere.com + * Copyright 2017 Tobias Loew : die-loews.de * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,22 +11,46 @@ #define BOOST_BOOSTACHE_VM_DETAIL_SELECT_CONTEXT_HPP #include +#include #include #include +#include namespace boost { namespace boostache { namespace vm { namespace detail { + + struct extract_variable_visitor { + template + boost::optional operator()(Template const & v) const + { + return boost::none; + } + + boost::optional operator()(ast::for_each const & v) const + { + return v.name; + } + }; + template + boost::optional extract_variable(Template const & templ) + { + return boost::apply_visitor(extract_variable_visitor(), templ); + } + + + + template < typename Stream, typename Template , typename Context1, typename Context2 , typename CategoryChild > void select_context( Stream & stream, Template const & templ , Context1 const & ctx_parent - , Context2 const & /*ctx_child*/ + , Context2 const & ctx_child , CategoryChild) { - generate(stream, templ, ctx_parent); + generate(stream, templ, make_stacked_context(ctx_parent, ctx_child, extract_variable(templ))); } @@ -33,11 +58,11 @@ namespace boost { namespace boostache { namespace vm { namespace detail , typename Context1, typename Context2 > void select_context( Stream & stream, Template const & templ - , Context1 const & /*ctx_parent*/ + , Context1 const & ctx_parent , Context2 const & ctx_child , extension::associative_attribute) { - generate(stream, templ, ctx_child); + generate(stream, templ, make_stacked_context(ctx_parent, ctx_child, extract_variable(templ))); } @@ -45,11 +70,11 @@ namespace boost { namespace boostache { namespace vm { namespace detail , typename Context1, typename Context2 > void select_context( Stream & stream, Template const & templ - , Context1 const & /*ctx_parent*/ + , Context1 const & ctx_parent , Context2 const & ctx_child , extension::sequence_attribute) { - generate(stream, templ, ctx_child); + generate(stream, templ, make_stacked_context(ctx_parent, ctx_child, extract_variable(templ))); } @@ -81,6 +106,63 @@ namespace boost { namespace boostache { namespace vm { namespace detail } + template + void select_context_dispatch(Stream & stream + , ast::select_context const & templ + , Context const & ctx + , extension::stacked_context_attribute) + { + if(ctx.bound_variable && *ctx.bound_variable == templ.tag) + { + // select local variable + generate(stream, templ.body, ctx.child); + } + else if(test(ctx.child, templ.tag, extension::optional_test_tag{})) + { + if(templ.make_local) + { + generate(stream, templ.body, ctx.child); + } + else + { + generate(stream, templ.body, ctx); + } + } + else + { + select_context_dispatch(stream, templ, ctx.parent); + //std::function callback = [&ctx](Stream & stream, ast::select_context const & templ) + //{ + // select_context_dispatch(stream, templ, ctx); + //}; + //select_context_dispatch(stream, templ, make_multi_context(ctx.parent, callback)); + } + } + + template + void select_context_dispatch(Stream & stream + , ast::select_context const & templ + , Context const & ctx + , extension::multi_context_attribute) + { + if (test(ctx.context, templ.tag, extension::optional_test_tag{})) + { + generate(stream, templ.body, ctx); + //if (templ.make_local) + //{ + // generate(stream, templ.body, ctx.context); + //} + //else + //{ + // generate(stream, templ.body, ctx); + //} + } + else + { + ctx.fun(stream, templ); + } + } + template void select_context_dispatch( Stream & stream , ast::select_context const & templ @@ -119,12 +201,31 @@ namespace boost { namespace boostache { namespace vm { namespace detail } else { - generate(stream, templ.body, ctx); + if(templ.make_local) + { + // generate nothing, since we didn't find the base of the variable + } + else + { + generate(stream, templ.body, ctx); + } } } // ------------------------------------------------------------------ // ------------------------------------------------------------------ + + template + void select_context_dispatch(Stream & stream + , ast::select_context const & templ + , Context const & ctx + // , std::function callback = {} + ) + { + select_context_dispatch(stream, templ, ctx, /*callback, */extension::select_category_t{}); + } + + }}}} diff --git a/include/boost/boostache/vm/detail/stacked_context.hpp b/include/boost/boostache/vm/detail/stacked_context.hpp new file mode 100644 index 0000000..7513d79 --- /dev/null +++ b/include/boost/boostache/vm/detail/stacked_context.hpp @@ -0,0 +1,64 @@ +/** + * \file detail/select_context.hpp + * + * Copyright 2017 Tobias Loew : die-loews.de + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef BOOST_BOOSTACHE_VM_DETAIL_STACKED_CONTEXT_HPP +#define BOOST_BOOSTACHE_VM_DETAIL_STACKED_CONTEXT_HPP + +#include +#include + +namespace boost { namespace boostache { namespace vm { namespace detail +{ + template + struct stacked_context { + stacked_context(ParentContext const & parent, ChildContext const & child, boost::optional bound_variable) + : parent(parent) + , child(child) + , bound_variable(bound_variable) + {} + + ParentContext const & parent; + ChildContext const & child; + boost::optional bound_variable; + }; + + // return a parent/child stacked_context + template + auto make_stacked_context(ParentContext const & parent, ChildContext const & child) + { + return stacked_context{parent, child, boost::none}; + } + + template + auto make_stacked_context(ParentContext const & parent, ChildContext const & child, boost::optional bound_variable) + { + return stacked_context{parent, child, bound_variable}; + } + + + // merge_contexts will put the child-context on top of parent's parent context + // if there's no parent's parent context it returns just the child-context + // this is used to transfer on the correct contexts from foreach and ensures + // that the returned type complexity is smaller than that of parent + // (for the stacked_context-case, child is always less complex than parent.child) + template + auto merge_contexts(ParentContext const & parent, ChildContext const & child, boost::optional bound_variable) + { + return child; + } + + template + auto merge_contexts(stacked_context const & parent, ChildContext const & child, boost::optional bound_variable) + { + return make_stacked_context(parent.parent, child, bound_variable); + } + +}}}} + + +#endif diff --git a/include/boost/boostache/vm/engine_ast.hpp b/include/boost/boostache/vm/engine_ast.hpp index 6f0d4ba..1fa030d 100644 --- a/include/boost/boostache/vm/engine_ast.hpp +++ b/include/boost/boostache/vm/engine_ast.hpp @@ -11,17 +11,25 @@ #define BOOST_BOOSTACHE_VM_ENGINE_AST_HPP #include +#include #include #include namespace boost { namespace boostache { namespace vm { namespace ast { + // unary functions supported by the vm + enum class unary_function_enum { + lower + }; + + struct literal; struct variable; struct for_each; struct if_then_else; struct select_context; struct node_list; + struct unary_function; struct undefined {}; @@ -48,32 +56,42 @@ namespace boost { namespace boostache { namespace vm { namespace ast std::string name; }; + struct unary_function + { + unary_function_enum function; + variable argument; + }; + struct node : boost::spirit::extended_variant< undefined , nop , literal , variable , render + , unary_function , boost::recursive_wrapper , boost::recursive_wrapper , boost::recursive_wrapper , boost::recursive_wrapper > { node() : base_type() {} - node(nop const & rhs) : base_type(rhs) {} - node(literal const & rhs) : base_type(rhs) {} + //template + // node(T const & rhs) : base_type(rhs) {} + node(nop const & rhs) : base_type(rhs) {} + node(literal const & rhs) : base_type(rhs) {} node(variable const & rhs) : base_type(rhs) {} node(render const & rhs) : base_type(rhs) {} node(for_each const & rhs) : base_type(rhs) {} node(if_then_else const & rhs) : base_type(rhs) {} node(select_context const & rhs) : base_type(rhs) {} - node(node_list const & rhs) : base_type(rhs) {} + node(node_list const & rhs) : base_type(rhs) {} + node(unary_function const & rhs) : base_type(rhs) {} }; struct for_each { - std::string name; - node value; + boost::optional name; // optional name of variable binding the iterated element + node value; }; struct condition @@ -96,6 +114,7 @@ namespace boost { namespace boostache { namespace vm { namespace ast { std::string tag; node body; + bool make_local={}; }; struct node_list @@ -103,6 +122,8 @@ namespace boost { namespace boostache { namespace vm { namespace ast std::vector nodes; }; + + }}}} #endif diff --git a/include/boost/boostache/vm/printer.hpp b/include/boost/boostache/vm/printer.hpp index 83a52c5..d9109bd 100644 --- a/include/boost/boostache/vm/printer.hpp +++ b/include/boost/boostache/vm/printer.hpp @@ -50,14 +50,19 @@ namespace boost { namespace boostache { namespace vm { namespace ast out << "[ : " << v.name << "]"; } - void operator()(for_each const & v) const - { - out << "[ :" << std::endl; - boost::apply_visitor(*this, v.value); - out << "\n]" << std::endl; - } - - void operator()(condition const & v) const + void operator()(for_each const & v) const + { + out << "[ :" << std::endl; + boost::apply_visitor(*this, v.value); + out << "\n]" << std::endl; + } + + void operator()(node const & v) const + { + boost::apply_visitor(*this, v); + } + + void operator()(condition const & v) const {} void operator()(select_context const & v) const diff --git a/test/Jamfile b/test/Jamfile index ac4fa71..ec16806 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -50,4 +50,11 @@ project boostache_test : --log_level=message ] ; + + test-suite boostache/django : + [ run django/django_parser.cpp + shared/parser_test.cpp + ] + ; + } diff --git a/test/django/django_parser.cpp b/test/django/django_parser.cpp new file mode 100644 index 0000000..43e75da --- /dev/null +++ b/test/django/django_parser.cpp @@ -0,0 +1,42 @@ +/** + * \file test/django/django_parser.cpp + * + * Link with shared/parser_test to test the django parser + * + * Copyright 2015 Michael Caisse : ciere.com + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bstache = boost::boostache; +namespace fe = boost::boostache::frontend; + +std::string print_ast(std::string const & filename) +{ + std::ifstream file(filename.c_str()); + if(!file) + { + BOOST_CHECK_MESSAGE(false, "Failed to open " << filename); + return ""; + } + + std::ifstream istream(filename.c_str()); + auto ast = fe::parse(istream); + std::ostringstream stream; + fe::django::ast::print(stream,ast); + return stream.str(); +} + + +std::string test_dir = "django/parser_test_dir"; diff --git a/test/django/parser_test_dir/comment.expect b/test/django/parser_test_dir/comment.expect new file mode 100644 index 0000000..e69de29 diff --git a/test/django/parser_test_dir/comment.input b/test/django/parser_test_dir/comment.input new file mode 100644 index 0000000..cdc383e --- /dev/null +++ b/test/django/parser_test_dir/comment.input @@ -0,0 +1 @@ +{# This is a comment #} diff --git a/test/django/parser_test_dir/variable.input b/test/django/parser_test_dir/variable.input new file mode 100644 index 0000000..59a7b35 --- /dev/null +++ b/test/django/parser_test_dir/variable.input @@ -0,0 +1,3 @@ +{{ foo }} + +{{ foo.bar }} diff --git a/test/django/parser_test_dir/variable.output b/test/django/parser_test_dir/variable.output new file mode 100644 index 0000000..59a7b35 --- /dev/null +++ b/test/django/parser_test_dir/variable.output @@ -0,0 +1,3 @@ +{{ foo }} + +{{ foo.bar }}