diff --git a/.gitignore b/.gitignore index a0879b9..2e29fca 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,23 @@ Testing Makefile bin examples/company_report/company_report_stache +/.vs/boostache +/x64 +/boostache.VC.db +*.obj +Debug_Test/boostache.tlog/boostache.lastbuildstate +.vs/boostache - Copy/v15/.suo +.vs/boostache - Copy/v15/Browse.VC.db +*.ipch +.vs/ProjectSettings.json +.vs/slnx.sqlite +.vs/VSWorkspaceState.json +*.tlog +boostache.vcxproj.user +*.pdb +*.sln +*.vcxproj +*.vcxproj.* +/*.vcxproj +/*.filters +/*.sln diff --git a/Demo.txt b/Demo.txt new file mode 100644 index 0000000..6a3e3fd --- /dev/null +++ b/Demo.txt @@ -0,0 +1,59 @@ +https://mustache.github.io/#demo + + +// Mustache + +

{{header}}

+{{#bug}} +{{/bug}} + +{{#items}} + {{#first}} + {{#tag}} + {{#items2}} +
  • {{name}}
  • + {{/items2}} + {{/tag}} + {{/first}} + {{#link}} +
  • {{name}}
  • + {{/link}} +{{/items}} + +{{#empty}} +

    The list is empty.

    +{{/empty}} + + + +// JSON + +{ + "header": "Colors", + "name": "outer_color", + "items": [ + {"name": "red", "first": true, "url": "#Red"}, + {"name": "green", "link": true, "url": "#Green"}, + {"name": "blue", "link": true, "url": "#Blue"} + ], + "tag":{ + "name3":"inner_color", + "items2": [ + {"name2": "red2", "first": true, "url": "#Red"}, + {"name": "green2", "link": true, "url": "#Green"}, + {"name": "blue2", "link": true, "url": "#Blue"} + ] }, + "empty": false +} + + +// Rendered + +

    Colors

    +
  • red
  • +
  • green2
  • +
  • blue2
  • +
  • green
  • +
  • blue
  • + + 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..87d1a20 --- /dev/null +++ b/examples/django.cpp @@ -0,0 +1,213 @@ +/** +* \file django.cpp +* +* A simple example of how to use boostache. +* +* Copyright 2015 Jeroen Habraken +* Copyright 2017, 2018 Tobias Loew : tobi@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) +*/ +#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 django1() +{ + // ------------------------------------------------------------------ + // Describe the input template. We are going to use django format. + std::string input( + "My name is {{another.name}}. " + "{# This is a comment #}" + "I am {{pet}} years old." + "{%% 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 } } + }, + }; + // ------------------------------------------------------------------ + + // ------------------------------------------------------------------ + // 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; +} + + +extern std::string django2_input; + +int django2() +{ + // ------------------------------------------------------------------ + // Describe the input template. We are going to use django format. + // input moved to django_input.cpp + // 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 %%}" + // "{{contat.name}} does {{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" , map_t{ { "first", "Peter" },{ "last", "Smith"}} }, + { "job" , "lazy person" } }, + map_t{ { "name" , map_t{ { "first", "Barbara" },{ "last", "Miller"}} }, + { "job" , "chef" } } + } + }, + { "nester" , + map_t{ { "nested", + list_t{ map_t{ { "name" , "Tom" }, + { "job" , "sweep floors" } }, + map_t{ { "name" , "Sue" }, + { "job" , "write code" } } + } } } }, + }; + + + + // ------------------------------------------------------------------ + + // ------------------------------------------------------------------ + // Load the template. + // This parses the input and compiles the result. The return is the + // compiled data structure + using boostache::load_template; + + auto iter = django2_input.begin(); + auto templ = load_template(iter, django2_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/django_input.cpp b/examples/django_input.cpp new file mode 100644 index 0000000..e146c9d --- /dev/null +++ b/examples/django_input.cpp @@ -0,0 +1,92 @@ +/** +* \file django.cpp +* +* A simple example of how to use boostache. +* +* Copyright 2017, 2018 Tobias Loew +* +* 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 + +// ------------------------------------------------------------------ +// Describe the input template. We are going to use django format. + std::string django2_input( + +#if 0 + //"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 %%}" + "{{contact.name}} does {{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" + +#endif +"{{people.2.name}}\n" +"{%% for contact2 in people %%}" +"{{contact2.name}} does {{contact2.job}}\n" +"{%% endfor %%}" + +//" WAIT!!!\n" +//"{%% for contact in more_people %%}" +////"{{contact.name.first}} is a {{contact.job}}\n" +// +//"{%% for contact2 in people %%}" +//"{{contact.name.first}} does {{contact2.job}}\n" +//"{%% endfor %%}" +// +//"{%% endfor %%}" + +//" NOW!!!\n" +//"{{me}}\n" +//"{{another.name}}\n" +//"{%% if another.name %%}" +//"There are named-people.\n" +//"{%% else %%}" +//"No athletes.\n" +//"{%% endif %%}" +// +// +//"{{more_people}}\n" +//"{%% for bla in nester.nested %%}" +//"{%% if bla.name %%}" +//"There are named-people.\n" +//"{%% else %%}" +//"No athletes.\n" +//"{%% endif %%}" +//"{{bla.name}} does {{bla.job}}\n" +//"{%% endfor %%}" +//"{%% if foogjgh %%}" +//"There are people.\n" +//"{%% else %%}" +// "No athletes.\n" +// "{%% endif %%}" +); diff --git a/examples/example1.cpp b/examples/example1.cpp index 2dc8625..e8d3857 100644 --- a/examples/example1.cpp +++ b/examples/example1.cpp @@ -4,6 +4,7 @@ * A simple example of how to use boostache. * * Copyright 2015 Michael Caisse : ciere.com + * Copyright 2017, 2018 Tobias Loew : tobi@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) @@ -24,7 +25,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 +58,6 @@ int main() // print the result std::cout << stream.str(); + + return 0; } diff --git a/examples/example2.cpp b/examples/example2.cpp index ffb301d..07681b4 100644 --- a/examples/example2.cpp +++ b/examples/example2.cpp @@ -4,6 +4,7 @@ * A slightly more complex example. * * Copyright 2015 Michael Caisse : ciere.com + * Copyright 2017, 2018 Tobias Loew : tobi@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) @@ -32,17 +33,28 @@ using invoice_t = std::map; // ------------------------------------------------------- -int main() +int example2() { // ------------------------------------------------------------------ // The template describing an invoice. - std::string input( - "Invoice" - "\n" - "{{#lines}}" - " {{item_code}} {{description}} {{amount}}\n" - "{{/lines}}" - ); + //std::string input( + // "Invoice" + // "\n" + // "{{#lines}}" + // " {{item_code}} {{description}} {{amount}}\n" + // "{{/lines}}" + // ); + std::string input( + "Invoice" + "\n" + "{{#lines}}" + "inner start\n" + "{{#lines}}" + " {{item_codes}} {{description}} {{amount}}\n" + "{{/lines}}" + "inner end\n" + "{{/lines}}" + ); // ------------------------------------------------------------------ @@ -81,4 +93,6 @@ int main() // ------------------------------------------------------------------ std::cout << stream.str(); + + return 0; } diff --git a/examples/example3.cpp b/examples/example3.cpp index ef62887..6e975a7 100644 --- a/examples/example3.cpp +++ b/examples/example3.cpp @@ -5,6 +5,7 @@ * clean things up with a variant. * * Copyright 2015 Michael Caisse : ciere.com + * Copyright 2017, 2018 Tobias Loew : tobi@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) @@ -21,7 +22,9 @@ #include #include #include +#include +using vv = std::variant; namespace boostache = boost::boostache; @@ -42,9 +45,15 @@ struct value_t; using object_t = std::map; using list_t = std::vector; -struct value_t : boost::spirit::extended_variant< std::string +struct user_rendered_t { + std::string some_variable; +}; + +struct value_t : boost::spirit::extended_variant< double, std::string , object_t , list_t + , user_rendered_t + > { value_t() : base_type() {} @@ -52,11 +61,50 @@ struct value_t : boost::spirit::extended_variant< std::string value_t(char const * rhs) : base_type(std::string{rhs}) {} value_t(object_t const & rhs) : base_type(rhs) {} value_t(list_t const & rhs) : base_type(rhs) {} + value_t(double const & rhs) : base_type(rhs) {} + value_t(user_rendered_t const & rhs) : base_type(rhs) {} }; // ------------------------------------------------------- +// test for user-defined rendering +namespace boost { + namespace boostache { + namespace extension + { + // renders the whole type + template< typename Stream> + void render(Stream & stream, user_rendered_t const & context) { + stream << "user_defined rendering"; + } + + // outer test of type (#"user_rendered_t") + bool test(user_rendered_t const & context){ + // the result of a #-test + return true; + } + + // inner test of name in user_rendered_t (#-test) + boost::optional test_tag(user_rendered_t const & context, std::string const & tag) { + if (tag == "invoice_number") { + return boost::none; + } + return tag == "invoice_number"; + //return tag == "invoice_number"; + } + + // inner rendering of name in user_rendered_t + template< typename Stream, typename Stack, typename Global> + bool render_name(Stream & stream, user_rendered_t const & context, Stack const* stack, Global const* global, std::string const & name) { + stream << "user_rendered_t rendering of: " << name; + return true; + } + + } + } +} + -int main() +int example3() { // ------------------------------------------------------------------ // The template describing an invoice. @@ -65,12 +113,38 @@ 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" "{{#lines}}" + " just to be sure that we mentioned the invoice number: {{invoice_number}}\n" " {{item_code}} {{description}} {{amount}}\n" + " content of add_lines ...\n" + " {{add_lines}}\n " + " ... and the invoice_number for each element of add_lines\n" + "{{#add_lines}} " + "{{#invoice_number}}\n" + "first has invoice\n" + "{{/invoice_number}}\n" + "{{#invoice_number_opt}}\n" + "second has invoice\n" + "{{/invoice_number_opt}}\n" + "{{#innvoice_number}}\n" + "third has invoice\n" + "{{/innvoice_number}}\n" + "just rendering invoice_number: {{invoice_number}} " + " {{/add_lines}}\n" "{{/lines}}" ); // ------------------------------------------------------------------ @@ -80,20 +154,27 @@ int main() // The data description. object_t invoice = - {{"invoice_number", "1234"}, - {"company" , object_t{{"name" , "FizSoft"}, - {"street" , "42 Level St."}, - {"city" , "Ytic"}, - {"state" , "CA"}, - {"zip" , "98765"} }}, - {"lines" , list_t{ object_t{{"item_code" , "1234-2"}, - {"description" , "Jolt Case"}, - {"amount" , "$23"}}, - object_t{{"item_code" , "1235-1"}, - {"description" , "Computer"}, - {"amount" , "$9"}} }} + { {"invoice_number",4.425}, + //{"company" , object_t{{"name" , "FizSoft"}, + // {"street" , "42 Level St."}, + // {"city" , "Ytic"}, + // {"state" , "CA"}, + // {"zip" , "98765"} }}, + {"lines" , list_t{ + //object_t{{"item_code" , "1234-2"}, + // {"description" , "Jolt Case"}, + // {"amount" , "$23"}}, + //object_t{{"item_code" , "1235-1"}, + // {"description" , "Computer"}, + // {"amount" , "$9"}}, + //object_t{{"add_lines" , list_t {1,2,3,4} }}, + object_t{{"add_lines" , user_rendered_t {"some-value"} }} + + + }} }; - + + invoice["test"] = value_t{ 42.425 }; // ------------------------------------------------------------------ // ------------------------------------------------------------------ @@ -114,4 +195,6 @@ int main() // ------------------------------------------------------------------ std::cout << stream.str(); + + return 0; } diff --git a/examples/example4.cpp b/examples/example4.cpp new file mode 100644 index 0000000..bd30516 --- /dev/null +++ b/examples/example4.cpp @@ -0,0 +1,83 @@ +/** + * \file exampe1.cpp + * + * A simple example of how to use boostache. + * + * 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) + */ +#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS + +#include +#include // need to work out header only syntax +#include +#include +#include +#include + +namespace boostache = boost::boostache; + + +// Our input data type is going to be a map of strings to strings +using map_t = std::map; + + +int example4() +{ + // ------------------------------------------------------------------ + // Describe the input template. We are going to use mustache format. + std::string input("\ +

    {{header}}< / h1>\n\ + {{#bug}}\n\ + {{ / bug}}\n\ +\n\ + {{#items}}\n\ + {{#first}}\n\ + {{#tag}}\n\ + {{#items2}}\n\ +
  • {{name}}< / strong>< / li>\n\ + {{ / items2}}\n\ + {{ / tag}}\n\ + {{ / first}}\n\ + {{#link}}\n\ +
  • {{name}}< / a>< / li>\n\ + {{ / link}}\n\ + {{ / items}}\n\ +\n\ + {{#empty}}\n\ +

    The list is empty.< / p>\n\ + {{ / empty}}\ +"); + // ------------------------------------------------------------------ + + // ------------------------------------------------------------------ + // The data description. Just a simple map of strings to strings. + map_t data = { { "name" , "Jeroen" }, + { "age" , "42" } + }; + // ------------------------------------------------------------------ + + // ------------------------------------------------------------------ + // Load the template. + // This parses the input and compiles the result. The return is the + // compiled data structure + using boostache::load_template; + using boostache::format::stache; + + 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/main.cpp b/examples/main.cpp new file mode 100644 index 0000000..51edcff --- /dev/null +++ b/examples/main.cpp @@ -0,0 +1,34 @@ +#include +#include +#include + +int example1(); +int example2(); +int example3(); +int example4(); +int django1(); +int django2(); +int simple_generate2(); + +//#define BOOST_FOREACH_REPLACMENT(VAR, COL) for(VAR : COL) +// +//#define BOOST_REVERSE_FOREACH_REPLACMENT(VAR, COL) for(VAR : boost::adaptors::reverse(COL) ) +int main() +{ + //std::vector v{ 1,2,3,4 }; + //BOOST_FOREACH_REPLACMENT(int& i, v) { + + // int j = i; + //} + //BOOST_REVERSE_FOREACH_REPLACMENT(int& i, v) { + + // int j = i; + //} + //example1(); + example2(); + //example3(); + //example4(); + //django1(); + //django2(); + //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..4ca9d3c --- /dev/null +++ b/include/boost/boostache/backend/detail/django_compiler.hpp @@ -0,0 +1,145 @@ +/** + * \file detail/django_compiler.hpp + * + * Copyright 2014 Michael Caisse : ciere.com + * Copyright 2017, 2018 Tobias Loew : tobi@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.is_context_local = std::next(iter) != v.rend(); + 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_.back(); + 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.rbegin(); iter != for_in.set.rend(); ++iter) + { + vm::ast::select_context select; + select.tag = *iter; + select.body = std::move(body); + select.is_context_local = std::next(iter) != for_in.set.rend(); + 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..f395d1e 100644 --- a/include/boost/boostache/backend/detail/stache_compiler.hpp +++ b/include/boost/boostache/backend/detail/stache_compiler.hpp @@ -2,6 +2,7 @@ * \file detail/stache_compiler.hpp * * Copyright 2014, 2015 Michael Caisse : ciere.com + * Copyright 2017, 2018 Tobias Loew : tobi@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) @@ -23,7 +24,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,10 +171,12 @@ 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; + select.push_context = true; select.tag = sec.name; select.body = section_body; 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..6474db2 100644 --- a/include/boost/boostache/boostache.hpp +++ b/include/boost/boostache/boostache.hpp @@ -2,6 +2,7 @@ * \file boostache.hpp * * Copyright 2014 Michael Caisse : ciere.com + * Copyright 2017, 2018 Tobias Loew : tobi@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,8 +12,10 @@ #include #include +#include #include #include +#include #include @@ -30,12 +33,50 @@ namespace boost { namespace boostache return backend::compile(frontend::parse(input)); } + + + template + struct stack_t { + using variant_t = Variant; + + template + stack_t(Arg&& arg, boost::optional const& name, stack_t const* parent) + : current(std::forward(arg)) + , parent(parent) + , name(name) + { + } + + Variant current; + boost::optional name; + stack_t const* parent; + }; + + template + struct global_t { + using variant_t = Variant; + + template + global_t(Arg&& arg) + : context(std::forward(arg)) + { + } + + Variant context; + }; + template void generate( Stream & stream , vm::ast::node const & templ - , Context const & context) + , Context const & context + ) { - vm::generate(stream,templ,context); + using uniform_data_t = typename vm::uniform_data_t; + + stack_t* stack = {}; + global_t global{context}; + + vm::generate(stream, templ, context, stack, &global); } }} 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..391c984 --- /dev/null +++ b/include/boost/boostache/frontend/django/grammar_def.hpp @@ -0,0 +1,129 @@ +/** + * \file grammar_def.hpp + * + * Copyright 2015 Jeroen Habraken + * Copyright 2017, 2018 Tobias Loew : tobi@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_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::digit_type digit; + 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_('_'))) | +digit + ; + + 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..1c0e038 100644 --- a/include/boost/boostache/model/basic_render_extension.hpp +++ b/include/boost/boostache/model/basic_render_extension.hpp @@ -2,6 +2,7 @@ * \file basic_render_extension.hpp * * Copyright 2014 Michael Caisse : ciere.com + * Copyright 2017, 2018 Tobias Loew : tobi@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) @@ -16,80 +17,241 @@ #include + + + +namespace boost { + namespace boostache { + namespace extension + { + template< typename Stream, typename T> + void render(Stream & stream, T const & context); + + + + template< typename Stream + , typename T + , typename Enable = typename std::enable_if::value>::type + > + void render(Stream && stream, T const & context + , plain_attribute) + { + (std::forward(stream) << context); + } + + + template< typename Stream + , typename T + > + void render(Stream && stream, T const & context + , optional_attribute) + { + if (context) + { + render(std::forward(stream), *context); + } + } + + + template< typename Stream + , typename T + > + void render(Stream && stream, T const & context + , unused_attribute) + { + } + + + template< typename Stream + , typename T + > + void render(Stream && stream, T const & context + , associative_attribute) + { + bool not_first = false; + for (auto const & item : context) + { + if (not_first) { + stream << ","; + } + else { + not_first = true; + } + stream << "["; + render(std::forward(stream), item.first); + stream << ":"; + render(std::forward(stream), item.second); + stream << "]"; + } + } + + + template< typename Stream + , typename T + + > + void render(Stream && stream, T const & context + , sequence_attribute) + { + bool not_first = false; + for (auto const & item : context) + { + if (not_first) { + stream << ","; + } + else { + not_first = true; + } + render(std::forward(stream), item); + } + } + + + + // -------------------------------------------------------------------------- + // -------------------------------------------------------------------------- + + template< typename Stream, typename T> + void render(Stream & stream, T const & context) + { + render(stream + , context + , render_category_t{}); + + } + + } + } +} + + + namespace boost { namespace boostache { namespace extension { - template< typename Stream, typename T > - void render(Stream & stream, T const & context, std::string const & name); + template< typename Stream, typename T, typename Stack, typename Global> + bool render_name(Stream & stream, T const & context, Stack const* stack, Global const* global, 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_name(Stream && stream, T const & context, Stack const* stack, Global const* global, std::string const & name + , plain_attribute) { - return (std::forward(stream) << context); + //(std::forward(stream) << context); + return false; } template< typename Stream , typename T + , typename Stack, typename Global > - void render( Stream && stream, T const & context, std::string const & name + bool render_name( Stream && stream, T const & context, Stack const* stack, Global const* global, std::string const & name , optional_attribute) { - render(std::forward(stream),*context,name); + //if (context) + //{ + // render_name(std::forward(stream), *context, stack, global, name); + //} + return false; } template< typename Stream , typename T + , typename Stack, typename Global > - void render( Stream && stream, T const & context, std::string const & name + bool render_name( Stream && stream, T const & context, Stack const* stack, Global const* global, std::string const & name , unused_attribute) { + return false; } template< typename Stream , typename T + , typename Stack, typename Global > - void render( Stream && stream, T const & context, std::string const & name + bool render_name( Stream && stream, T const & context, Stack const* stack, Global const* global, std::string const & name , associative_attribute) { auto iter = context.find(name); if(iter!=context.end()) { - render(std::forward(stream),iter->second,name); - } + render(std::forward(stream), iter->second); + 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 + , typename Stack, typename Global + > + bool render_name(Stream && stream, T const & context, Stack const* stack, Global const* global, std::string const & name + , sequence_attribute) { - for(auto const & item : context) - { - render(std::forward(stream),item,name); - } + //for (auto const & item : context) + //{ + // render_name(std::forward(stream), item, stack, global, name); + //} + return false; } + // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- - template< typename Stream, typename T > - void render(Stream & stream, T const & context, std::string const & name) + template< typename Stream, typename T, typename Stack, typename Global> + bool render_name(Stream & stream, T const & context, Stack const* stack, Global const* global, std::string const & name) { - render( stream - , context - , name - , render_category_t{} ); + if (render_name(stream + , context + , stack, global + , name + , render_category_t{})) + { + return true; + } + + + // ToDo: add condition whether to also check parent contexts + + Stack const* s = stack; + while (s) { + + if (boost::apply_visitor(boostache::detail::make_unwrap_variant_visitor( + [&stream, &s, &name, &global](auto ctx) -> bool + { + return render_name(stream + , ctx + , s->parent + , global + , name + , render_category_t{} + ); + + } + ) + , s->current)) + { + return true; + } + + s = s->parent; + } + return false; + + } }}} diff --git a/include/boost/boostache/model/basic_test_extension.hpp b/include/boost/boostache/model/basic_test_extension.hpp index ba3ff8f..ab457fe 100644 --- a/include/boost/boostache/model/basic_test_extension.hpp +++ b/include/boost/boostache/model/basic_test_extension.hpp @@ -2,6 +2,7 @@ * \file basic_test_extension.hpp * * Copyright 2014 Michael Caisse : ciere.com + * Copyright 2017, 2018 Tobias Loew : tobi@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) @@ -18,6 +19,8 @@ namespace boost { namespace boostache { namespace extension { +// struct optional_test_tag {}; + // -------------------------------------------------------------------------- // Test // -------------------------------------------------------------------------- @@ -65,71 +68,174 @@ namespace boost { namespace boostache { namespace extension // Test with tag // -------------------------------------------------------------------------- - template - bool test(T const & context, std::string const & tag); + //template + //bool test_tag(T const & context, std::string const & tag); + + //template + //bool test_tag(T const & context, std::string const & tag); + + //template + //bool test_tag( T const & context, std::string const & tag + // , unused_attribute) + //{ + // return false; + //} + + //template + //bool test_tag( T const & context, std::string const & tag + // , plain_attribute) + //{ + // return false; + //} + + //template + //bool test_tag( T const & context, std::string const & tag + // , sequence_attribute) + //{ + // return false; + //} + + //template + //bool test_tag( T const & context, std::string const & tag + // , optional_attribute) + //{ + // return false; + //} + + //template + //bool test_tag( T const & context, std::string const & tag + // , associative_attribute) + //{ + // auto iter = context.find(tag); + // return iter != context.end(); + //} + + + +// -------------------------------------------------------------------------- +// Test with tag +// -------------------------------------------------------------------------- + + template + std::pair test_with_stack(T const & context, Stack const* stack, Global const* global, std::string const & tag); template - bool test( T const & context, std::string const & tag - , unused_attribute) + boost::optional test_tag(T const & context, std::string const & tag + , unused_attribute) { - return test(context, unused_attribute{}); + return boost::none; +// return test(context, unused_attribute{}); } template - bool test( T const & context, std::string const & tag - , plain_attribute) + boost::optional test_tag(T const & context, std::string const & tag + , plain_attribute) { - return test(context, plain_attribute{}); + return boost::none; + // return test(context, plain_attribute{}); } template - bool test( T const & context, std::string const & tag - , sequence_attribute) + boost::optional test_tag(T const & context, std::string const & tag + , sequence_attribute) { - return test(context, sequence_attribute{}); + return boost::none; + // return test(context, sequence_attribute{}); } template - bool test( T const & context, std::string const & tag - , optional_attribute) + boost::optional test_tag(T const & context, std::string const & tag + , optional_attribute) { - return test(context, optional_attribute{}); + return boost::none; + // return test(context, optional_attribute{}); } template - bool test( T const & context, std::string const & tag - , associative_attribute) + boost::optional test_tag(T const & context, std::string const & tag + , associative_attribute) + { + auto iter = context.find(tag); + if (iter != context.end()) + { + return test(iter->second + // , test_category_tsecond)>{}); + ); + } + else + { + return boost::none; + } + } + + + template + boost::optional test_tag(T const & context, std::string const & tag) { - auto iter = context.find(tag); - if(iter!=context.end()) - { - return test( iter->second - , test_category_tsecond)>{}); - } - else - { - return false; - } + return test_tag(context, tag, test_category_t{}); } + + + // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- 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 + std::pair test_with_stack(T const & context, Stack const* stack, Global const* global, std::string const & tag) + { + auto&& result = test_tag(context + , tag + ); + if (result) { + return { *result, nullptr }; + } + + // ToDo: add condition whether to also check parent contexts + + Stack const* s = stack; + while(s){ + + if (auto&& result = boost::apply_visitor(boostache::detail::make_unwrap_variant_visitor>>( + [&s, &tag](auto ctx) -> boost::optional> + { + if (auto&& result = test_tag(ctx + , tag + )) { + return std::make_pair(*result, s); + } + else { + return boost::none; + } + + } + ) + , s->current)) + { + return *result; + } + + s = s->parent; + } + return { false, nullptr }; } }}} diff --git a/include/boost/boostache/model/category.hpp b/include/boost/boostache/model/category.hpp index 33debd1..5eade37 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, 2018 Tobias Loew : tobi@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) @@ -19,7 +20,6 @@ namespace boost { namespace boostache { namespace extension struct plain_attribute : category_attribute {}; struct sequence_attribute : category_attribute {}; struct associative_attribute : category_attribute {}; - struct tuple_attribute : category_attribute {}; struct variant_attribute : category_attribute {}; struct optional_attribute : category_attribute {}; diff --git a/include/boost/boostache/model/render_traits.hpp b/include/boost/boostache/model/render_traits.hpp index d28688e..69e90de 100644 --- a/include/boost/boostache/model/render_traits.hpp +++ b/include/boost/boostache/model/render_traits.hpp @@ -2,6 +2,7 @@ * \file render_traits.hpp * * Copyright 2014, 2015 Michael Caisse : ciere.com + * Copyright 2017, 2018 Tobias Loew : tobi@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 diff --git a/include/boost/boostache/model/select_traits.hpp b/include/boost/boostache/model/select_traits.hpp index 50c7533..6397c5d 100644 --- a/include/boost/boostache/model/select_traits.hpp +++ b/include/boost/boostache/model/select_traits.hpp @@ -2,6 +2,7 @@ * \file select_traits.hpp * * Copyright 2014, 2015 Michael Caisse : ciere.com + * Copyright 2017, 2018 Tobias Loew : tobi@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) @@ -12,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -42,8 +44,8 @@ namespace boost { namespace boostache { namespace extension : mpl::identity {}; template - struct select_category> - : mpl::identity {}; + 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..f1090ba 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, 2018 Tobias Loew : tobi@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) diff --git a/include/boost/boostache/model/unwrap_variant.hpp b/include/boost/boostache/model/unwrap_variant.hpp index acbbe1c..2b7aa8c 100644 --- a/include/boost/boostache/model/unwrap_variant.hpp +++ b/include/boost/boostache/model/unwrap_variant.hpp @@ -5,6 +5,7 @@ * to the contained data type. This is a helper to do that for us. * * Copyright 2014 Michael Caisse : ciere.com + * Copyright 2017, 2018 Tobias Loew : tobi@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,13 +21,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_with_stack(ctx, tag/*, optional_test_tag{}*/); } ) , context); @@ -34,30 +35,59 @@ namespace boost { namespace boostache { namespace extension template - bool test( T const & context - , variant_attribute) + bool test(T const & context + , variant_attribute) { - return boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( - [](auto ctx) - { - return test(ctx); - } - ) - , context); + return boost::apply_visitor(boostache::detail::make_unwrap_variant_visitor( + [](auto ctx) + { + return test(ctx); + } + ) + , context); } - template< typename Stream, typename T > - void render( Stream & stream, T const & context, std::string const & name - , variant_attribute) + template + boost::optional test_tag(T const & context, std::string const & tag + , variant_attribute) { - return boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( - [&stream,&name](auto ctx) - { - render(stream,ctx,name); - } - ) - , context); + return boost::apply_visitor(boostache::detail::make_unwrap_variant_visitor>( + [&tag](auto ctx) + { + return test_tag(ctx, tag); + } + ) + , context); + } + + + template< typename Stream, typename T> + void render(Stream & stream, T const & context + , variant_attribute) + { + boost::apply_visitor(boostache::detail::make_unwrap_variant_visitor( + [&stream](auto ctx) + { +// don't do the following line: it disables user-defined render-overloading +// render(stream, ctx, render_category_t{}); + render(stream, ctx); + } + ) + , context); + } + + template< typename Stream, typename T, typename Stack, typename Global> + bool render_name(Stream & stream, T const & context, Stack const* stack, Global const* global, std::string const & name + , variant_attribute) + { + return boost::apply_visitor(boostache::detail::make_unwrap_variant_visitor( + [&stream, &stack, &global, &name](auto ctx) + { + return render_name(stream, ctx, stack, global, name, render_category_t{}); + } + ) + , context); } }}} diff --git a/include/boost/boostache/vm/detail/engine_visitor.hpp b/include/boost/boostache/vm/detail/engine_visitor.hpp index bc9f40f..e847abc 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, 2018 Tobias Loew : tobi@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) @@ -15,116 +16,164 @@ #include -////////// some test hackery //////////////////////// + ////////// some test hackery //////////////////////// -namespace boost { namespace boostache { namespace extension -{ - template - void render(Stream & stream, Object const & v) - { - stream << v; - } +namespace boost { + namespace boostache { + namespace extension + { + template + bool render_lit(Stream & stream, Object const & v) + { + stream << v; + return true; + } - template - bool test(T const & context, std::string const & name); + template + std::pair test_with_stack(T const & context, Stack const* stack, Global const* global, std::string const & tag); - template - bool test(T const & context); - - template< typename Stream, typename T > - void render(Stream & stream, T const & context, std::string const & name); -}}} + template< typename Stream, typename T, typename Stack, typename Global> + bool render_name(Stream & stream, T const & context, Stack const* stack, Global const* global, std::string const & name); + } + } +} ///////////////////////////////////////////////////// -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) - {} - - void operator()(ast::undefined) const - {} - - void operator()(ast::nop) const - {} - - void operator()(ast::literal const & lit) const - { - using boost::boostache::extension::render; - render(stream, lit.value); - } - - void operator()(ast::variable const &) const - {} - - void operator()(ast::render const & r) const - { - using boost::boostache::extension::render; - render(stream, context, r.name); - } - - void operator()(ast::for_each const & v) const - { - using boost::boostache::vm::detail::foreach; - foreach(stream, v, context); - } - - void operator()(ast::if_then_else const & v) const - { - using boost::boostache::extension::test; - if(test(context, v.condition_.name)) - { - boost::apply_visitor(*this, v.then_); - } - else - { - boost::apply_visitor(*this, v.else_); - } - } - - void operator()(ast::select_context const & select_ctx) const - { - select_context_dispatch( stream, select_ctx, context - , extension::select_category_t{} ); - } - - void operator()(ast::node_list const & nodes) const - { - for(auto const & node : nodes.nodes) - { - boost::apply_visitor(*this, node); - } - } - - void operator()(ast::node const & node) const - { - boost::apply_visitor(*this, node); - } - - private: - Stream & stream; - Context const & context; - }; - - - template - void generate( Stream & stream - , Template const & templ - , Context const & ctx) - { - engine_visitor_base engine(stream, ctx); - engine(templ); - } - -}}}} +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, Stack const* stack, Global const* global) + : stream(s) + , context(c) + , stack(stack) + , global(global) + {} + + void operator()(ast::undefined) const + {} + + void operator()(ast::nop) const + {} + + void operator()(ast::literal const & lit) const + { + using boost::boostache::extension::render_lit; + render_lit(stream, lit.value); + } + + void operator()(ast::variable const &) const + {} + + void operator()(ast::render const & r) const + { + using boost::boostache::extension::render_name; + render_name(stream, context, stack, global, r.name); + } + + void operator()(ast::for_each const & v) const + { + using boost::boostache::vm::detail::foreach; + + foreach(stream, v, context, stack, global); + } + + void operator()(ast::if_then_else const & v) const + { + using boost::boostache::extension::test_with_stack; + auto&& result = test_with_stack(context, stack, global, v.condition_.name); + if (!result.second) { + // found in current stack + if (result.first) + { + boost::apply_visitor(*this, v.then_); + } + else + { + boost::apply_visitor(*this, v.else_); + } + } + else { + // continue in the stack where element got found + + boost::apply_visitor(boostache::detail::make_unwrap_variant_visitor( + [this,&result,&v](auto ctx) + { + //Stack local(ctx, stack, global); + engine_visitor_base engine(stream, ctx, stack, global); + //engine(v); + if (result.first) + { + engine(v.then_); + } + else + { + engine(v.else_); + } + } + ) + , result.second->current); + + //Stack local(*result.second, &stack); + //engine_visitor_base engine(stream, context, local); + //engine(v); + } + } + + void operator()(ast::select_context const & select_ctx) const + { + select_context_dispatch(stream, select_ctx, context, stack, global, false); + } + + void operator()(ast::node_list const & nodes) const + { + for (auto const & node : nodes.nodes) + { + boost::apply_visitor(*this, node); + } + } + + void operator()(ast::node const & node) const + { + boost::apply_visitor(*this, node); + } + + private: + Stream & stream; + Context const & context; + Stack const* stack; + Global const* global; + }; + + + template + void generate(Stream & stream + , Template const & templ + , Context const & ctx + , Stack const* stack, Global const* global + ) + { + engine_visitor_base engine(stream, ctx, stack, global); + engine(templ); + } + + } + } + } +} #endif diff --git a/include/boost/boostache/vm/detail/foreach.hpp b/include/boost/boostache/vm/detail/foreach.hpp index 507e7e6..d612cde 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, 2018 Tobias Loew : tobi@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) @@ -29,8 +30,8 @@ namespace boost { namespace boostache { namespace vm namespace detail { - template - void foreach(Stream & stream, Node const & node, Context const & context); + template + void foreach(Stream & stream, Node const & node, Context const & context, Stack const* stack, Global const* global); } }}} @@ -84,73 +85,90 @@ namespace boost { namespace boostache { namespace extension namespace boost { namespace boostache { namespace vm { namespace detail { - template + + template void foreach( Stream & stream , Node const & node , Context const & context + , Stack const* stack, Global const* global , Category) { - generate(stream, node.value, context); + generate(stream, node.value, context, stack, global); } - template + template void foreach( Stream & stream , Node const & node , Context const & context - , extension::variant_attribute) + , Stack const* stack, Global const* global + , extension::variant_attribute) { - boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( - [&stream,&node](auto ctx) + boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( + [&stream,&node,&stack,&global](auto ctx) { - vm::detail::foreach(stream, node, ctx); + vm::detail::foreach(stream, node, ctx, stack, global); } ) , context); } - template + template void foreach( Stream & stream , Node const & node , Context const & context - , extension::sequence_attribute) + , Stack const* stack, Global const* global + , extension::sequence_attribute) { - for(auto const & item : context) + for(auto const & item : context) { - generate(stream, node.value, item); + + if (node.name) { + Stack local(item, node.name, stack); + + generate(stream, node.value, item/*context*/, &local, global); + } + else { + generate(stream, node.value, item, stack, global); + } + + } } - template + template void foreach( Stream & stream , Node const & node , Context const & ctx - , extension::optional_attribute) + , Stack const* stack, Global const* global + , extension::optional_attribute) { if(ctx) { - foreach( stream, node, *ctx - , extension::foreach_category_t{}); + foreach(stream, node, *ctx, stack, global + , extension::foreach_category_t{}); } else { - generate(stream, node.value, ctx); + generate(stream, node.value, ctx, stack, global); } } + /** * Entry point for foreach */ - template - void foreach(Stream & stream, Node const & node, Context const & context) + template + void foreach(Stream & stream, Node const & node, Context const & context, Stack const* stack, Global const* global) { using boostache::vm::detail::foreach; foreach( stream , node , context + , stack, global , extension::foreach_category_t{}); } 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..37d96f8 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, 2018 Tobias Loew : tobi@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) @@ -12,98 +13,143 @@ #include #include #include +#include namespace boost { namespace boostache { namespace vm { namespace detail { + + template < typename Stream, typename Template - , typename Context1, typename Context2 + , typename Context1, typename Context2, typename Stack, typename Global , typename CategoryChild > void select_context( Stream & stream, Template const & templ , Context1 const & ctx_parent - , Context2 const & /*ctx_child*/ + , Context2 const & ctx_child + , Stack const* stack, Global const* global , CategoryChild) { - generate(stream, templ, ctx_parent); + generate(stream, templ, ctx_child, /*ctx_parent,*/ stack, global); } template < typename Stream, typename Template - , typename Context1, typename Context2 + , typename Context1, typename Context2, typename Stack, typename Global > void select_context( Stream & stream, Template const & templ - , Context1 const & /*ctx_parent*/ + , Context1 const & ctx_parent , Context2 const & ctx_child - , extension::associative_attribute) + , Stack const* stack, Global const* global + , extension::associative_attribute) { - generate(stream, templ, ctx_child); + generate(stream, templ, ctx_child, stack, global); } template < typename Stream, typename Template - , typename Context1, typename Context2 + , typename Context1, typename Context2, typename Stack, typename Global > void select_context( Stream & stream, Template const & templ - , Context1 const & /*ctx_parent*/ + , Context1 const & ctx_parent , Context2 const & ctx_child - , extension::sequence_attribute) + , Stack const* stack, Global const* global + , extension::sequence_attribute) { - generate(stream, templ, ctx_child); +// Stack local(ctx_parent, &stack); + generate(stream, templ, ctx_child, stack, global); } template < typename Stream, typename Template - , typename Context1, typename Context2 + , typename Context1, typename Context2, typename Stack, typename Global > void select_context( Stream & stream, Template const & templ , Context1 const & ctx_parent , Context2 const & ctx_child - , extension::variant_attribute) + , Stack const* stack, Global const* global + , extension::variant_attribute) { boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( - [&stream,&templ,&ctx_parent](auto ctx) + [&stream,&templ,&ctx_parent,&stack,&global](auto ctx) { - select_context( stream, templ, ctx_parent, ctx + select_context( stream, templ, ctx_parent, ctx, stack, global , extension::select_category_t{}); }) , ctx_child); } - template + + + + template void select_context_dispatch( Stream & stream , ast::select_context const & templ - , Context const & ctx, Category) + , Context const & ctx, Stack const* stack, Global const* global, bool global_check, Category) + { + generate(stream, templ.body, ctx, stack, global); + } + + + template + void select_context_dispatch(Stream & stream + , ast::select_context const & templ + , Context const & ctx, Stack const* stack, Global const* global, bool global_check + , extension::sequence_attribute) { - generate(stream, templ.body, ctx); + size_t pos; + int n = std::stoi(templ.tag, &pos); + if (pos == templ.tag.size() && n >= 0 && n < std::size(ctx)) { + generate(stream, templ.body, ctx[n], stack, global); + + } } - template + template void select_context_dispatch( Stream & stream , ast::select_context const & templ - , Context const & ctx + , Context const & ctx, Stack const* stack, Global const* global, bool global_check , extension::variant_attribute) { boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( - [&stream,&templ](auto ctx) + [&stream,&templ,&stack, &global, &global_check](auto ctx) { - select_context_dispatch( stream, templ, ctx + select_context_dispatch( stream, templ, ctx, stack, global, global_check , extension::select_category_t{}); }) , ctx); } - template + template void select_context_dispatch( Stream & stream , ast::select_context const & templ - , Context const & ctx + , Context const & ctx, Stack const* stack, Global const* global, bool global_check , extension::associative_attribute) { + if (!templ.is_context_local) + { + auto s = stack; + while (s) { + auto&& n = s->name; + if (n && *n == templ.tag) { + select_context(stream, templ.body, ctx, s->current, stack, global, + extension::select_category_tcurrent)>{}); + return; + } + s = s->parent; + } + } + + + + + + auto iter = ctx.find(templ.tag); if(iter != ctx.end()) { @@ -113,18 +159,56 @@ namespace boost { namespace boostache { namespace vm { namespace detail // << typeid(iter->second).name() // << " category " // << typeid(extension::select_category_tsecond)>{}).name(); - - select_context( stream, templ.body, ctx, iter->second - , extension::select_category_tsecond)>{}); + + // store current context for name lookup + if (templ.push_context) { + Stack local(ctx, std::string{}, stack); + + select_context(stream, templ.body, ctx, iter->second, &local, global, + extension::select_category_tsecond)>{}); + } + else { + select_context(stream, templ.body, ctx, iter->second, stack, global, + extension::select_category_tsecond)>{}); + } } else { - generate(stream, templ.body, ctx); + if(templ.is_context_local) + { + // generate nothing, since we didn't find the base of the variable + stream << "<< '" << templ.tag << "' NOT FOUND >>"; + } + else + { + + if (!global_check) { + // get global context and probe + select_context_dispatch(stream, templ, global->context, stack, global, true); + } + else { + // generate(stream, templ.body, ctx, stack, global); + } + } } } // ------------------------------------------------------------------ // ------------------------------------------------------------------ + + template + void select_context_dispatch(Stream & stream + , ast::select_context const & templ + , Context const & ctx + , Stack const* stack, Global const* global + , bool global_check + // , std::function callback = {} + ) + { + select_context_dispatch(stream, templ, ctx, stack, global, global_check, extension::select_category_t{}); + } + + }}}} diff --git a/include/boost/boostache/vm/engine_ast.hpp b/include/boost/boostache/vm/engine_ast.hpp index 6f0d4ba..667e8e7 100644 --- a/include/boost/boostache/vm/engine_ast.hpp +++ b/include/boost/boostache/vm/engine_ast.hpp @@ -2,6 +2,7 @@ * \file engine_ast.hpp * * Copyright 2014, 2015 Michael Caisse : ciere.com + * Copyright 2017, 2018 Tobias Loew : tobi@die-loews.de * * * Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -11,17 +12,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 +57,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 +115,8 @@ namespace boost { namespace boostache { namespace vm { namespace ast { std::string tag; node body; + bool push_context = {}; + bool is_context_local = {}; }; struct node_list @@ -103,6 +124,8 @@ namespace boost { namespace boostache { namespace vm { namespace ast std::vector nodes; }; + + }}}} #endif diff --git a/include/boost/boostache/vm/generate.hpp b/include/boost/boostache/vm/generate.hpp index d452d0f..1dfaca2 100644 --- a/include/boost/boostache/vm/generate.hpp +++ b/include/boost/boostache/vm/generate.hpp @@ -2,6 +2,7 @@ * \file generate.hpp * * Copyright 2014 Michael Caisse : ciere.com + * Copyright 2017, 2018 Tobias Loew : tobi@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,15 +12,21 @@ #include -namespace boost { namespace boostache { namespace vm -{ - template - void generate( Stream & stream +namespace boost { + namespace boostache { + namespace vm + { + template + void generate(Stream & stream , Template const & templ - , Context const & context) - { - vm::detail::generate(stream, templ, context); - } -}}} + , Context const & context + , Stack const* stack, Global const* global + ) + { + vm::detail::generate(stream, templ, context, stack, global); + } + } + } +} #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/include/boost/boostache/vm/unificator.hpp b/include/boost/boostache/vm/unificator.hpp new file mode 100644 index 0000000..a65b77c --- /dev/null +++ b/include/boost/boostache/vm/unificator.hpp @@ -0,0 +1,291 @@ +/** + * \file generate.hpp + * + * Copyright 2017, 2018 Tobias Loew : tobi@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_UNIFORM_HPP +#define BOOST_BOOSTACHE_VM_UNIFORM_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + + + +namespace boost { namespace boostache { namespace vm +{ + namespace detail { + +// gather_types gathers all types from a recursive structure into a set + +template +struct gather_types { + using type = boost::mpl::set; +}; + +template +struct gather_types { + using type = boost::mpl::set<>; +}; + + +template +struct gather_types { + using type = boost::mpl::set; +}; + + + +template +struct gather_types_helper { + + template + struct apply{ + static constexpr bool already_seen = boost::mpl::has_key::type::value; + using type = typename gather_types::type; + }; + +}; + + +struct append_set { + + template + struct apply { + using type = typename boost::mpl::fold< + T + , S + , boost::mpl::insert + >::type; + }; + +}; + + +template +struct gather_types< T, Gathered, false + , vm::trait::enable_if_is_variant_t +> { + using GatheredT = typename boost::mpl::insert< + Gathered, + T + >::type; + + using data_type_sets = typename boost::mpl::transform< + typename T::types, + gather_types_helper + >::type; + + + using type = typename boost::mpl::fold< + data_type_sets + , boost::mpl::set + , append_set + >::type; +}; + +template +struct gather_types< T, Gathered, false + , vm::trait::enable_if_sequence_not_map_t +> { + using GatheredT = typename boost::mpl::insert< + Gathered, + T + >::type; + + + using value_type = typename std::remove_const< + typename std::remove_reference< + decltype(*std::begin(std::declval()))>::type>::type; + + static constexpr bool already_seen = boost::mpl::has_key::type::value; + + using base_types = typename gather_types::type; + + using type = typename boost::mpl::insert< + base_types, + T + >::type; +}; + +template +struct gather_types, Gathered, false> { + using GatheredT = typename boost::mpl::insert< + Gathered, + boost::optional + >::type; + + static constexpr bool already_seen = boost::mpl::has_key::type::value; + using base_types = typename gather_types::type; + + using type = typename boost::mpl::insert< + base_types, + boost::optional + >::type; +}; + +template +struct gather_types, Gathered, false> { + using GatheredT = typename boost::mpl::insert< + Gathered, + std::map + >::type; + + static constexpr bool already_seen = boost::mpl::has_key::type::value; + using base_types = typename gather_types::type; + + using type = typename boost::mpl::insert< + base_types, + std::map + >::type; +}; + + + + + //// foreach category + //template + //struct collect_map_data { + // using type = boost::mpl::set<>; + //}; + + //template + //struct collect_map_data { + // using type = boost::mpl::set<>; + //}; + + + //template + //struct lazy_collect_map_data { + // using type = collect_map_data::type; + //}; + + //template + //struct collect_map_data< T, Seen + // , vm::trait::enable_if_is_variant_t + //> { + // using SeenT = typename boost::mpl::insert< + // Seen, + // T + // >::type; + + // using data_types = typename boost::mpl::transform< + // typename T::types, + // // boost::mpl::_1 + + // boost::mpl::eval_if< + // typename boost::mpl::has_key::type + // , collect_map_data + // , lazy_collect_map_data + // >::type; + + // using type = typename boost::mpl::fold< + // data_types + // , boost::mpl::set<> + // , boost::mpl::insert + // >::type; + //}; + + //template + //struct collect_map_data< T, Seen + // , vm::trait::enable_if_sequence_not_map_t + //> { + // using SeenT = typename boost::mpl::insert< + // Seen, + // T + // >::type; + + // using value_type = typename std::remove_const< + // typename std::remove_reference< + // decltype(*std::begin(std::declval()))>::type>::type; + + // using type = typename collect_map_data::type; + //}; + + //template + //struct collect_map_data, Seen> { + // using SeenT = typename boost::mpl::insert< + // Seen, + // boost::optional + // >::type; + // using type = typename collect_map_data::type; + //}; + + //template + //struct collect_map_data, Seen> { + // using SeenT = typename boost::mpl::insert< + // Seen, + // std::map + // >::type; + + // using base_types = typename collect_map_data::type; + + // using type = typename boost::mpl::insert< + // base_types, + // std::map + // >::type; + //}; + + +template +struct is_map_data : public boost::mpl::false_ {}; + +template +struct is_map_data> : public boost::mpl::true_ {}; + + struct tag_empty {}; + + + template + struct make_uniform_data_t { + + // mpl-set containing all map-types from Data + using data_types = typename gather_types, false>::type; + //using map_data_types = typename collect_map_data>::type; + + // convert set to vector + using vectorized = typename boost::mpl::copy< + data_types + , boost::mpl::back_inserter< boost::mpl::vector<> > + >::type; + + // filter out non-maps + //using vectorized_map_data = typename boost::mpl::fold< + // vectorized + // , boost::mpl::vector<> + // , boost::mpl::if_< is_map_data, + // boost::mpl::push_back, + // boost::mpl::_1 + // > + //>::type; + + using vectorized_map_data = vectorized; + + + + // ensure const& + using data_types_cr = typename boost::mpl::transform< + vectorized_map_data, + std::add_lvalue_reference>> + >::type; + + using type = typename boost::make_variant_over::type; + }; + + } + + template + using uniform_data_t = typename detail::make_uniform_data_t::type; + + +}}} + +#endif 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 }}