├── .gitignore ├── CMakeLists.txt ├── Jamroot ├── README.md ├── doc └── liaw │ ├── LibraryInAWeek2014-templateengine-day1-slides.pdf │ ├── LibraryInAWeek2014-templateengine-day2-slides.pdf │ ├── README.md │ ├── liaw-cpp_text_template_engine-day1_notes.pdf │ └── liaw-cpp_text_template_engine-day2_notes.pdf ├── examples ├── CMakeLists.txt ├── Jamfile ├── company_report │ ├── .gitignore │ ├── company_data.json │ ├── company_report_stache.cpp │ └── report.tpl ├── ctemplate │ ├── hello.cpp │ ├── hello.tpl │ ├── invoice.cpp │ └── invoice.tpl ├── example1.cpp ├── example2.cpp ├── example3.cpp └── simple_generate2.cpp ├── include └── boost │ └── boostache │ ├── backend │ ├── compiler.hpp │ ├── detail │ │ └── stache_compiler.hpp │ └── stache_compiler.hpp │ ├── boostache.hpp │ ├── detail │ └── unwrap_variant_visitor.hpp │ ├── frontend │ ├── parse.hpp │ └── stache │ │ ├── ast.hpp │ │ ├── ast_adapted.hpp │ │ ├── grammar.hpp │ │ ├── grammar_def.hpp │ │ └── printer.hpp │ ├── model │ ├── basic_render_extension.hpp │ ├── basic_test_extension.hpp │ ├── category.hpp │ ├── default_adapter.hpp │ ├── dynamic_model_printer.hpp │ ├── helper.hpp │ ├── property_tree_adapter.hpp │ ├── render_traits.hpp │ ├── select_traits.hpp │ ├── stache_model.hpp │ ├── stache_model_printer.hpp │ ├── test_traits.hpp │ └── unwrap_variant.hpp │ ├── stache.hpp │ └── vm │ ├── detail │ ├── engine_visitor.hpp │ ├── foreach.hpp │ └── select_context.hpp │ ├── engine_ast.hpp │ ├── generate.hpp │ ├── printer.hpp │ └── traits.hpp └── test ├── .gitignore ├── CMakeLists.txt ├── Jamfile ├── frontend ├── .gitignore ├── CMakeLists.txt ├── Jamfile ├── adapt_test.cpp └── grammar_basic.cpp ├── model ├── CMakeLists.txt ├── render_traits.cpp ├── simple_parser.hpp ├── test_default_model.cpp ├── test_ptree_model.cpp ├── traits.cpp └── unwrap_variant.cpp ├── mustache ├── CMakeLists.txt ├── README.md ├── compiler_test_dir │ ├── initial.expect │ └── initial.input ├── end2end_test_dir │ ├── collections1.expect │ ├── collections1.input │ ├── collections2.expect │ ├── collections2.input.disable │ ├── implicit.expect │ ├── implicit.input │ ├── implicit2.expect │ ├── implicit2.input │ ├── inverted_section1.expect │ ├── inverted_section1.input │ ├── king_duckz_1.expect │ ├── king_duckz_1.input │ ├── multiple.expect │ ├── multiple.input │ ├── nested_sections1.expect │ ├── nested_sections1.input │ ├── partials1.expect │ ├── partials1.input.disable │ ├── sections1.expect │ ├── sections1.input │ ├── spec_comment.expect │ ├── spec_comment.input │ ├── spec_interp.expect │ ├── spec_interp.input │ ├── spec_sections.expect │ └── spec_sections.input ├── mustache_compiler.cpp ├── mustache_end2end.cpp ├── mustache_parser.cpp └── parser_test_dir │ ├── section.expect │ ├── section.input │ ├── simple.expect │ ├── simple.input │ ├── typical.expect │ └── typical.input ├── shared └── parser_test.cpp └── vm ├── CMakeLists.txt └── foreach.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.a 3 | *.so 4 | cmake_install.cmake 5 | CMakeCache.txt 6 | CMakeFiles 7 | CTestTestfile.cmake 8 | Makefile 9 | Testing 10 | Makefile 11 | bin 12 | examples/company_report/company_report_stache 13 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(booststache CXX) 3 | enable_testing() 4 | 5 | set(Boost_USE_STATIC_LIBS OFF) 6 | set(Boost_USE_MULTITHREADED ON) 7 | set(Boost_USE_STATIC_RUNTIME OFF) 8 | find_package(Boost 1.53 COMPONENTS unit_test_framework system filesystem) 9 | 10 | add_definitions( -DBOOST_SPIRIT_USE_PHOENIX_V3=1 ) 11 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 12 | add_definitions( -std=c++14 -ftemplate-depth=512 -Wno-unused-local-typedefs -Wno-deprecated-declarations ) 13 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 14 | add_definitions( -std=c++14 -ftemplate-depth=512 -stdlib=libc++ ) 15 | endif() 16 | # Not sure what to do with the darwin 17 | 18 | include_directories( include ) 19 | include_directories( SYSTEM ${Boost_INCLUDE_DIRS} ) 20 | 21 | add_subdirectory(test) 22 | add_subdirectory(examples) 23 | -------------------------------------------------------------------------------- /Jamroot: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2014, 2015 Michael Caisse, ciere.com 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 5 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | # 7 | 8 | import os ; 9 | local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; 10 | 11 | if $(BOOST_ROOT) 12 | { 13 | use-project /boost : $(BOOST_ROOT) ; 14 | } 15 | 16 | 17 | project 18 | : requirements 19 | gcc:-std=c++14 20 | gcc:-ftemplate-depth=512 21 | gcc:-Wno-unused-local-typedefs 22 | gcc:-Wno-deprecated-declarations 23 | clang:-std=c++14 24 | clang:-ftemplate-depth-512 25 | clang:-stdlib=libc++ 26 | darwin:-std=c++14 27 | darwin:-ftemplate-depth-512 28 | darwin:clang:-stdlib=libc++ 29 | BOOST_SPIRIT_USE_PHOENIX_V3=1 30 | /boost//headers 31 | ./include 32 | ; 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Boostache 2 | ======== 3 | 4 | Boostache is a text template processing engine. The initial release uses the Mustache (http://mustache.github.io/) template language. 5 | 6 | Disclaimer: Boostache is NOT an official Boost library 7 | 8 | 9 | Status 10 | ============== 11 | 12 | This library is starting to get some attention again. Usage, build, and reference docs coming. Examples too! 13 | 14 | The CI status: 15 | 16 | Platform | Master | Develop 17 | ------------ | ------------- | ------------- 18 | Linux Clang | ![clang build result](https://bamboo.cierecloud.com/plugins/servlet/wittified/build-status/BSTCH-FMC) | ![clang build result](https://bamboo.cierecloud.com/plugins/servlet/wittified/build-status/BSTCH-FMC0) 19 | Linux GCC | ![gcc build result](https://bamboo.cierecloud.com/plugins/servlet/wittified/build-status/BSTCH-MAS) | ![gcc build result](https://bamboo.cierecloud.com/plugins/servlet/wittified/build-status/BSTCH-MAS0) 20 | 21 | 22 | History 23 | ============== 24 | 25 | Boostache started as the C++Now 2014 Conference's Library in a Week challenge. 26 | 27 | The original LiaW team included: 28 | 29 | - Michal Bukovský 30 | - Michael Caisse 31 | - Jeff Garland 32 | - Jeroen Habraken 33 | - Kevin Harris 34 | - Dan Nuffer 35 | 36 | and was developed here: 37 | https://github.com/JeffGarland/liaw2014 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /doc/liaw/LibraryInAWeek2014-templateengine-day1-slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cierelabs/boostache/38e96396b5ada0da90ea458e989c213ff24ed184/doc/liaw/LibraryInAWeek2014-templateengine-day1-slides.pdf -------------------------------------------------------------------------------- /doc/liaw/LibraryInAWeek2014-templateengine-day2-slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cierelabs/boostache/38e96396b5ada0da90ea458e989c213ff24ed184/doc/liaw/LibraryInAWeek2014-templateengine-day2-slides.pdf -------------------------------------------------------------------------------- /doc/liaw/README.md: -------------------------------------------------------------------------------- 1 | Title: 2014 Library in a Week - c++ templating engine 2 | 3 | Library in a week 2014 will attempt to build a C++ template engine library. Templating 4 | engines are very popular libraries that provide a way to separate an application 5 | from output. Often the output is a html or text that may be developed by an 6 | non-programmer or other development team. 7 | 8 | 9 | Consider the following program which uses the ctemplate library: 10 | 11 | //user.tpl 12 | Hello {{NAME}}! 13 | 14 | //user.cpp 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | int main() { 21 | std::string user = getenv("USER"); 22 | ctemplate::TemplateDictionary dict("example"); 23 | dict["NAME"] = user; 24 | std::string output; 25 | ctemplate::ExpandTemplate("example.tpl", ctemplate::DO_NOT_STRIP, &dict, &output); 26 | std::cout << output; 27 | return 0; 28 | } 29 | 30 | 31 | In this example the output is simple text, but the template file could be replaced 32 | with html or json and the application would not need to be changed. 33 | 34 | A templating engine has two languages - the markup language the allows 35 | a text template to be filled in by an application and the library language 36 | for the application to be developed. In our case the library language will 37 | be C++11. 38 | 39 | The workshop will work like this. On day 1 I will provide motivation 40 | and an overview of the library development. We will split into individuals 41 | and groups to attack various aspects of the library and markup language 42 | design. Some groups may look at existing libraries for inspiration providing 43 | presentations on the best aspects of the other libraries. From that point 44 | forward it will be up to the group to direct the remainder of the workshop. 45 | 46 | Collaboration Tools: 47 | 48 | Library in a week 2014 will use a git repository for collaboration and sharing 49 | of information. 50 | 51 | git clone https://github.com/JeffGarland/liaw2014.git 52 | 53 | Also the group will use an email list for discussions beyond face to face 54 | meetings. 55 | 56 | Level: Beginner to Expert 57 | 58 | Attendees should have a basic background in C++. 59 | 60 | 61 | -------------------------------------------------------------------------------- /doc/liaw/liaw-cpp_text_template_engine-day1_notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cierelabs/boostache/38e96396b5ada0da90ea458e989c213ff24ed184/doc/liaw/liaw-cpp_text_template_engine-day1_notes.pdf -------------------------------------------------------------------------------- /doc/liaw/liaw-cpp_text_template_engine-day2_notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cierelabs/boostache/38e96396b5ada0da90ea458e989c213ff24ed184/doc/liaw/liaw-cpp_text_template_engine-day2_notes.pdf -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | foreach( EXAMPLE example1 example2 example3 ) 2 | add_executable( ${EXAMPLE} ${EXAMPLE}.cpp ) 3 | endforeach() 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/Jamfile: -------------------------------------------------------------------------------- 1 | #============================================================================== 2 | # Copyright (c) 2014, 2015 Michael Caisse 3 | # 4 | # Use, modification and distribution is subject to the Boost Software 5 | # License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 6 | # http://www.boost.org/LICENSE_1_0.txt) 7 | #============================================================================== 8 | 9 | import feature ; 10 | 11 | ############################################################################### 12 | project boostache_examples 13 | : requirements 14 | . 15 | ; 16 | 17 | exe example1 18 | : example1.cpp 19 | ; 20 | 21 | exe example2 22 | : example2.cpp 23 | ; 24 | 25 | exe example3 26 | : example3.cpp 27 | ; 28 | -------------------------------------------------------------------------------- /examples/company_report/.gitignore: -------------------------------------------------------------------------------- 1 | company_report_stache 2 | -------------------------------------------------------------------------------- /examples/company_report/company_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "NAME": "BigCorp", 3 | "YEAR FOUNDED": "1999", 4 | "DIVISIONS": [ 5 | { "NAME": "R&D", "BUDGET":100000, 6 | "EMPLOYEES": [ 7 | { "NAME": "JOE", "EMPLOYEE ID":1, "SALARY":10000 }, 8 | { "NAME": "Sally", "EMPLOYEE ID":2, "SALARY":12000 } 9 | ] 10 | }, 11 | { "NAME": "Finance", "BUDGET":200000, 12 | "EMPLOYEES": [ 13 | { "NAME": "Betty", "EMPLOYEE ID":5, "SALARY":8000 }, 14 | { "NAME": "Jim", "EMPLOYEE ID":8, "SALARY":12000 } 15 | ] 16 | } 17 | { "NAME": "HiddenDivision", 18 | "EMPLOYEES": [ 19 | ] 20 | } 21 | 22 | ] 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /examples/company_report/company_report_stache.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace boost::boostache::frontend; 14 | 15 | namespace 16 | { 17 | std::string print(const ast::stache_root& ast, const stache_model& model) 18 | { 19 | std::ostringstream out; 20 | boost::boostache::frontend::ast::print(out, ast, model); 21 | return out.str(); 22 | } 23 | 24 | ast::stache_root parse(const std::string& text) 25 | { 26 | ast::stache_root ast; 27 | if( !boost::boostache::simple_parse_template(text, ast) ) 28 | { 29 | throw std::runtime_error("Parse failed"); 30 | } 31 | return ast; 32 | } 33 | } 34 | 35 | 36 | std::string report = 37 | "Company {{NAME}}\n" 38 | "\n" 39 | "{{#YEARS}}\n" 40 | "{{#DIVISIONS}}\n" 41 | "Division: {{NAME}} Budget for year: {{BUDGET}}\n" 42 | "Employees:\n" 43 | "{{#EMPLOYEES}} {{NAME}} salary {{SALARY}}\n" 44 | "{{/EMPLOYEES}}\n" 45 | "{{^EMPLOYEES}}\n" 46 | "Has no employees\n" 47 | "{{/EMPLOYEES}}\n" 48 | "{{/DIVISIONS}}\n" 49 | "{{/YEARS}}\n" 50 | ; 51 | 52 | int main(int argc, char* argv[]) 53 | { 54 | using std::cout; 55 | 56 | if (argc>1) 57 | { 58 | std::ifstream source(argv[1], std::ios::binary); 59 | std::vector data((std::istreambuf_iterator(source)), 60 | std::istreambuf_iterator()); 61 | report.assign(data.begin(), data.end()); 62 | } 63 | 64 | stache_model company; 65 | 66 | company["NAME"] = "BigCorp"; 67 | company["YEAR FOUNDED"] = "1999"; 68 | company["YEARS"] = stache_model_vector { stache_model { { "DIVISIONS", stache_model_vector { 69 | stache_model { 70 | { "Name", "R&D" }, 71 | { "BUDGET", "1000000" }, 72 | { "EMPLOYEES", stache_model_vector { 73 | stache_model{ { "NAME", "JOE" }, { "EMPLOYEE ID", "1" }, { "SALARY", "10000" } }, 74 | stache_model{ { "NAME", "Sally" }, { "EMPLOYEE ID", "2" }, { "SALARY", "12000" } }, 75 | }, 76 | }, 77 | }, 78 | stache_model { 79 | { "NAME", "Finance" }, 80 | { "BUDGET", "200000" }, 81 | { "EMPLOYEES", stache_model_vector { 82 | stache_model{ { "NAME", "Betty" }, { "EMPLOYEE ID", "5" }, { "SALARY", "8000" } }, 83 | stache_model{ { "NAME", "Jim" }, { "EMPLOYEE ID", "8" }, { "SALARY", "12000" } }, 84 | }, 85 | }, 86 | }, 87 | stache_model { 88 | { "NAME", "HiddenDivision", }, 89 | { "EMPLOYEES", stache_model_vector { 90 | }, 91 | }, 92 | } 93 | }}}}; 94 | 95 | ast::stache_root ast = parse(report); 96 | cout< 4 | #include 5 | #include 6 | #include 7 | 8 | int main() { 9 | std::string user = getenv("USER"); 10 | ctemplate::TemplateDictionary dict("example"); 11 | dict["NAME"] = user; 12 | std::string output; 13 | ctemplate::ExpandTemplate("hello.tpl", ctemplate::DO_NOT_STRIP, &dict, &output); 14 | std::cout << output; 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /examples/ctemplate/hello.tpl: -------------------------------------------------------------------------------- 1 | Hello {{NAME}}! 2 | -------------------------------------------------------------------------------- /examples/ctemplate/invoice.cpp: -------------------------------------------------------------------------------- 1 | //invoice.cpp 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() { 9 | 10 | 11 | ctemplate::TemplateDictionary dict("example"); 12 | dict["COMPANY"] = "Joes Hardware"; 13 | 14 | std::string output; 15 | ctemplate::ExpandTemplate("invoice.tpl", ctemplate::DO_NOT_STRIP, &dict, &output); 16 | std::cout << output; 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /examples/ctemplate/invoice.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | Company: {{COMPANY}} 4 | Date: {{DATE}} 5 | 6 | 7 | {{#ITEMS}} {{ITEM_NAME}} {{COUNT}} at {{UNIT_PRICE}} {{SUBTOTAL}} {{/ITEMS}} 8 | 9 | Due: {{TOTAL}} -------------------------------------------------------------------------------- /examples/example1.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file exampe1.cpp 3 | * 4 | * A simple example of how to use boostache. 5 | * 6 | * Copyright 2015 Michael Caisse : ciere.com 7 | * 8 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 9 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | */ 11 | #define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS 12 | 13 | #include 14 | #include // need to work out header only syntax 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace boostache = boost::boostache; 21 | 22 | 23 | // Our input data type is going to be a map of strings to strings 24 | using map_t = std::map; 25 | 26 | 27 | int main() 28 | { 29 | // ------------------------------------------------------------------ 30 | // Describe the input template. We are going to use mustache format. 31 | std::string input("My name is {{name}}. I am {{age}} years old.\n"); 32 | // ------------------------------------------------------------------ 33 | 34 | // ------------------------------------------------------------------ 35 | // The data description. Just a simple map of strings to strings. 36 | map_t data = { { "name" , "Jeroen" }, 37 | { "age" , "42" } 38 | }; 39 | // ------------------------------------------------------------------ 40 | 41 | // ------------------------------------------------------------------ 42 | // Load the template. 43 | // This parses the input and compiles the result. The return is the 44 | // compiled data structure 45 | using boostache::load_template; 46 | using boostache::format::stache; 47 | 48 | auto iter = input.begin(); 49 | auto templ = load_template(iter, input.end()); 50 | // ------------------------------------------------------------------ 51 | 52 | // ------------------------------------------------------------------ 53 | // Apply the compiled template and the data model to generate the result 54 | std::stringstream stream; 55 | boostache::generate(stream, templ, data); 56 | // ------------------------------------------------------------------ 57 | 58 | // print the result 59 | std::cout << stream.str(); 60 | } 61 | -------------------------------------------------------------------------------- /examples/example2.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file example2.cpp 3 | * 4 | * A slightly more complex example. 5 | * 6 | * Copyright 2015 Michael Caisse : ciere.com 7 | * 8 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 9 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | */ 11 | #define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS 12 | 13 | #include 14 | #include // need to work out header only syntax 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | namespace boostache = boost::boostache; 22 | 23 | 24 | // ------------------------------------------------------- 25 | // The data will be an invoice this time. The invoice 26 | // consists of a list of line items. Each line item 27 | // can be describes as map of string to strings. 28 | // 29 | using item_t = std::map; 30 | using item_list_t = std::vector; 31 | using invoice_t = std::map; 32 | // ------------------------------------------------------- 33 | 34 | 35 | int main() 36 | { 37 | // ------------------------------------------------------------------ 38 | // The template describing an invoice. 39 | std::string input( 40 | "Invoice" 41 | "\n" 42 | "{{#lines}}" 43 | " {{item_code}} {{description}} {{amount}}\n" 44 | "{{/lines}}" 45 | ); 46 | // ------------------------------------------------------------------ 47 | 48 | 49 | // ------------------------------------------------------------------ 50 | // The data description. invoice_items is a list of maps that 51 | // describe each item. 52 | item_list_t invoice_items = { 53 | { {"item_code" , "1234"}, 54 | {"description" , "teddy bear"}, 55 | {"amount" , "$23"} }, 56 | { {"item_code" , "1235"}, 57 | {"description" , "computer"}, 58 | {"amount" , "$9"} } 59 | }; 60 | 61 | // we need to put the list into a map so that tag 'lines' can 62 | // be used to look up the items. 63 | invoice_t invoice = {{"lines", invoice_items}}; 64 | // ------------------------------------------------------------------ 65 | 66 | // ------------------------------------------------------------------ 67 | // Load the template 68 | // This parses the input and compiles the result. The return is the 69 | // compiled data structure 70 | using boostache::load_template; 71 | 72 | auto iter = input.begin(); 73 | auto templ = load_template(iter, input.end()); 74 | // ------------------------------------------------------------------ 75 | 76 | // ------------------------------------------------------------------ 77 | // Apply the compiled template and the data model to the generate 78 | // method 79 | std::stringstream stream; 80 | boostache::generate(stream, templ, invoice); 81 | // ------------------------------------------------------------------ 82 | 83 | std::cout << stream.str(); 84 | } 85 | -------------------------------------------------------------------------------- /examples/example3.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file example2.cpp 3 | * 4 | * This is almost like example2, except we are going to 5 | * clean things up with a variant. 6 | * 7 | * Copyright 2015 Michael Caisse : ciere.com 8 | * 9 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 10 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 11 | */ 12 | #define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS 13 | 14 | #include 15 | #include // need to work out header only syntax 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | namespace boostache = boost::boostache; 27 | 28 | 29 | // ------------------------------------------------------- 30 | // From example2 we realize that if we had a data structure 31 | // that could map strings to a list, string, or even another 32 | // map then we could represent a much more complete invoice. 33 | // A key/value map with the value being able to store these 34 | // different types starts to look like an object in some 35 | // languages. 36 | // This is fairly simple to do with a variant. We are going 37 | // to use the spirit version of it because it makes 38 | // recursive structures easier to describe. 39 | // 40 | 41 | struct value_t; 42 | using object_t = std::map; 43 | using list_t = std::vector; 44 | 45 | struct value_t : boost::spirit::extended_variant< std::string 46 | , object_t 47 | , list_t 48 | > 49 | { 50 | value_t() : base_type() {} 51 | value_t(std::string const & rhs) : base_type(rhs) {} 52 | value_t(char const * rhs) : base_type(std::string{rhs}) {} 53 | value_t(object_t const & rhs) : base_type(rhs) {} 54 | value_t(list_t const & rhs) : base_type(rhs) {} 55 | }; 56 | // ------------------------------------------------------- 57 | 58 | 59 | int main() 60 | { 61 | // ------------------------------------------------------------------ 62 | // The template describing an invoice. 63 | std::string input( 64 | "Invoice {{invoice_number}}" 65 | "\n" 66 | "{{# company}}" 67 | "Company: {{name}}\n" 68 | " {{street}}\n" 69 | " {{city}}, {{state}} {{zip}}\n" 70 | "{{/ company}}" 71 | "------------------------------------------------\n" 72 | "{{#lines}}" 73 | " {{item_code}} {{description}} {{amount}}\n" 74 | "{{/lines}}" 75 | ); 76 | // ------------------------------------------------------------------ 77 | 78 | 79 | // ------------------------------------------------------------------ 80 | // The data description. 81 | 82 | object_t invoice = 83 | {{"invoice_number", "1234"}, 84 | {"company" , object_t{{"name" , "FizSoft"}, 85 | {"street" , "42 Level St."}, 86 | {"city" , "Ytic"}, 87 | {"state" , "CA"}, 88 | {"zip" , "98765"} }}, 89 | {"lines" , list_t{ object_t{{"item_code" , "1234-2"}, 90 | {"description" , "Jolt Case"}, 91 | {"amount" , "$23"}}, 92 | object_t{{"item_code" , "1235-1"}, 93 | {"description" , "Computer"}, 94 | {"amount" , "$9"}} }} 95 | }; 96 | 97 | // ------------------------------------------------------------------ 98 | 99 | // ------------------------------------------------------------------ 100 | // Load the template 101 | // This parses the input and compiles the result. The return is the 102 | // compiled data structure 103 | using boostache::load_template; 104 | 105 | auto iter = input.begin(); 106 | auto templ = load_template(iter, input.end()); 107 | // ------------------------------------------------------------------ 108 | 109 | // ------------------------------------------------------------------ 110 | // Apply the compiled template and the data model to the generate 111 | // method 112 | std::stringstream stream; 113 | boostache::generate(stream, templ, invoice); 114 | // ------------------------------------------------------------------ 115 | 116 | std::cout << stream.str(); 117 | } 118 | -------------------------------------------------------------------------------- /examples/simple_generate2.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file simple_generate2.cpp 3 | * 4 | * Copyright 2014, 2015 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS 10 | 11 | #include 12 | #include // need to work out header only syntax 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // ------------ includes for our own model type ------------ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace boost { namespace boostache { namespace extension 26 | { 27 | // template 28 | // struct test_category> 29 | // : mpl::identity 30 | // {}; 31 | }}} 32 | // --------------------------------------------------------- 33 | 34 | 35 | 36 | struct my_type; 37 | using my_object_t = std::map; 38 | using my_list_t = std::list; 39 | 40 | struct my_type : boost::spirit::extended_variant< 41 | bool 42 | , std::string 43 | , int 44 | , my_object_t 45 | , my_list_t 46 | > 47 | { 48 | my_type() : base_type() {} 49 | my_type(bool rhs) : base_type(rhs) {} 50 | my_type(std::string const & rhs) : base_type(rhs) {} 51 | my_type(const char * rhs) : base_type(std::string(rhs)) {} 52 | my_type(int rhs) : base_type(rhs) {} 53 | my_type(my_object_t const & rhs) : base_type(rhs) {} 54 | my_type(my_list_t const & rhs) : base_type(rhs) {} 55 | }; 56 | 57 | 58 | // ---------------------------------------- 59 | // ---------------------------------------- 60 | 61 | 62 | namespace bstache = boost::boostache; 63 | namespace extn = bstache::extension; 64 | 65 | 66 | 67 | int main() 68 | { 69 | // ------------------------------------------------------------------ 70 | // The input template 71 | std::string input( 72 | "Hello world \n" 73 | "{{name}} is here.\n" 74 | "{{& escaped_name}} is here\n" 75 | "{{#foo}}\n" 76 | " Some cool section {{whoot}} is here.\n" 77 | " {{^bar}}\n" 78 | " Some cool empty section {{whoot}} is here.\n" 79 | " {{/bar}} done.\n" 80 | "{{/foo}} done.\n" 81 | ); 82 | // ------------------------------------------------------------------ 83 | 84 | 85 | // ------------------------------------------------------------------ 86 | // The data model definition 87 | // my_object_t data; 88 | my_object_t data = { 89 | {"name","Jeff"}, 90 | {"escaped_name","

Jeff

"}, 91 | {"whoot","yipee"}, 92 | {"bar",false}, 93 | {"foo", my_list_t { my_object_t{{"whoot","yipee 1"}} 94 | , my_object_t{{"whoot","yipee 2"},{"bar",true}} 95 | , my_object_t{{"whoot","yipee 3"}} }} 96 | }; 97 | // ------------------------------------------------------------------ 98 | 99 | 100 | // ------------------------------------------------------------------ 101 | // load the template 102 | // This parses the input and compiles the result. The return is the 103 | // compiled data structure 104 | auto iter = input.begin(); 105 | auto templ = bstache::load_template(iter,input.end()); 106 | // ------------------------------------------------------------------ 107 | 108 | // ------------------------------------------------------------------ 109 | // Apply the compiled template and the data model to the generate 110 | // method 111 | std::stringstream stream; 112 | bstache::generate(stream, templ, data); 113 | // ------------------------------------------------------------------ 114 | 115 | std::cout << stream.str(); 116 | 117 | return -1; 118 | } 119 | 120 | -------------------------------------------------------------------------------- /include/boost/boostache/backend/compiler.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file compiler.hpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_BACKEND_COMPILER_HPP 10 | #define BOOST_BOOSTACHE_BACKEND_COMPILER_HPP 11 | 12 | #include 13 | 14 | namespace boost { namespace boostache { namespace backend 15 | { 16 | template 17 | vm::ast::node_list compile(T const & ast) 18 | { 19 | std::cout << "generic compile for : " << typeid(ast).name() << std::endl; 20 | // compiler not implemented... do something smart 21 | return vm::ast::node_list{}; 22 | } 23 | }}} 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/boost/boostache/backend/detail/stache_compiler.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file detail/stache_compiler.hpp 3 | * 4 | * Copyright 2014, 2015 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_BACKEND_DETAIL_STACHE_COMPILER_HPP 10 | #define BOOST_BOOSTACHE_BACKEND_DETAIL_STACHE_COMPILER_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | namespace boost { namespace boostache { namespace backend { namespace stache_compiler 19 | { 20 | namespace fe = boost::boostache::frontend; 21 | namespace detail 22 | { 23 | /** 24 | * Check if the string simply contains white-space. 25 | */ 26 | bool is_blank(std::string const & s) 27 | { 28 | return( s.find_first_not_of(std::string{" \t\r\n"}) 29 | == std::string::npos ); 30 | } 31 | 32 | class state 33 | { 34 | public: 35 | void add_ws(std::string const & ws) 36 | { 37 | white_space += ws; 38 | } 39 | 40 | void add_eol(std::string const & ws) 41 | { 42 | if(!tag_count) 43 | { 44 | white_space.clear(); 45 | } 46 | else 47 | { 48 | white_space += ws; 49 | } 50 | 51 | tag_count = 0; 52 | } 53 | 54 | std::string get_ws() 55 | { 56 | std::string ws; 57 | std::swap(ws,white_space); 58 | return ws; 59 | } 60 | 61 | void flush_ws() 62 | { 63 | white_space.clear(); 64 | } 65 | 66 | void standalone_tag() 67 | { 68 | } 69 | 70 | void tag() 71 | { 72 | ++tag_count; 73 | } 74 | 75 | private: 76 | std::string white_space; 77 | int tag_count = 0; 78 | }; 79 | 80 | class stache_visit 81 | { 82 | public: 83 | using result_type = vm::ast::node; 84 | 85 | stache_visit(std::ostream& out, state & s) 86 | : out(out), state_(s) 87 | {} 88 | 89 | vm::ast::node operator()(fe::stache::ast::undefined) const 90 | { 91 | out << "WHOA! we have an undefined" << std::endl; 92 | return vm::ast::node{}; 93 | } 94 | 95 | vm::ast::node operator()(fe::stache::ast::literal_text const & v) const 96 | { 97 | /** 98 | * In stache, the literals are trimmed if they are just whitespace. 99 | */ 100 | // if(is_blank(v)) 101 | // { 102 | // return vm::ast::nop{}; 103 | // } 104 | state_.tag(); 105 | return vm::ast::literal{state_.get_ws()+v}; 106 | } 107 | 108 | vm::ast::node operator()(fe::stache::ast::blank_text const & v) const 109 | { 110 | state_.add_ws(v); 111 | //return vm::ast::literal{""}; 112 | return vm::ast::nop{}; 113 | } 114 | 115 | vm::ast::node operator()(fe::stache::ast::eol const & v) const 116 | { 117 | state_.add_eol(v); 118 | auto ws = state_.get_ws(); 119 | if(!ws.empty()) 120 | { 121 | return vm::ast::literal{ws}; 122 | } 123 | else 124 | { 125 | return vm::ast::nop{}; 126 | } 127 | } 128 | 129 | vm::ast::node operator()(fe::stache::ast::variable const & v) const 130 | { 131 | state_.tag(); 132 | std::string white_space{state_.get_ws()}; 133 | if(white_space.empty()) 134 | { 135 | return vm::ast::render{v.value}; 136 | } 137 | else 138 | { 139 | vm::ast::node_list vm_ast; 140 | vm_ast.nodes.push_back(vm::ast::literal{std::move(white_space)}); 141 | vm_ast.nodes.push_back(vm::ast::render{v.value}); 142 | return vm_ast; 143 | } 144 | } 145 | 146 | /** 147 | * In stache, the section is a conditional that then loops over 148 | * the section body for each data member. 149 | * 150 | * Stache doesn't have an if-then-else structure... just a 151 | * conditional that will execute the body if the data element 152 | * evaluates to true (or false if marked inverted). 153 | */ 154 | vm::ast::node operator()(fe::stache::ast::section const & sec) const 155 | { 156 | state_.standalone_tag(); 157 | vm::ast::node_list vm_ast; 158 | for(auto const & node : sec.nodes) 159 | { 160 | vm_ast.nodes.push_back(boost::apply_visitor(*this, node)); 161 | } 162 | 163 | vm::ast::if_then_else if_block; 164 | if_block.condition_.name = sec.name; 165 | 166 | if(sec.is_inverted) 167 | { 168 | if_block.else_ = vm_ast; 169 | } 170 | else 171 | { 172 | vm::ast::for_each section_body; 173 | section_body.name = sec.name; 174 | section_body.value = vm_ast; 175 | 176 | vm::ast::select_context select; 177 | select.tag = sec.name; 178 | select.body = section_body; 179 | 180 | if_block.then_ = select; 181 | } 182 | 183 | return if_block; 184 | } 185 | 186 | vm::ast::node operator()(fe::stache::ast::comment const & v) const 187 | { 188 | state_.standalone_tag(); 189 | return vm::ast::nop{}; 190 | } 191 | 192 | vm::ast::node operator()(fe::stache::ast::partial const & v) const 193 | { 194 | // TODO: need to implement partials 195 | state_.standalone_tag(); 196 | return vm::ast::nop{}; 197 | } 198 | 199 | vm::ast::node operator()(fe::stache::ast::node_list const & nodes) const 200 | { 201 | vm::ast::node_list node_list; 202 | for(auto const & node : nodes) 203 | { 204 | node_list.nodes.push_back(boost::apply_visitor(*this, node)); 205 | } 206 | return node_list; 207 | } 208 | 209 | vm::ast::node operator()(fe::stache::ast::root const & nodes) const 210 | { 211 | vm::ast::node_list node_list; 212 | for(auto const & node : nodes) 213 | { 214 | node_list.nodes.push_back(boost::apply_visitor(*this, node)); 215 | } 216 | return node_list; 217 | } 218 | 219 | private: 220 | std::ostream& out; 221 | state & state_; 222 | }; 223 | } 224 | 225 | inline vm::ast::node compile(fe::stache::ast::root const & ast) 226 | { 227 | detail::state s{}; 228 | detail::stache_visit visit(std::cout,s); 229 | return visit(ast); 230 | } 231 | }}}} 232 | 233 | #endif 234 | -------------------------------------------------------------------------------- /include/boost/boostache/backend/stache_compiler.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file stache_compiler.hpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_BACKEND_STACHE_COMPILER_HPP 10 | #define BOOST_BOOSTACHE_BACKEND_STACHE_COMPILER_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace boost { namespace boostache { namespace backend 17 | { 18 | inline vm::ast::node compile(frontend::stache::ast::root const & ast) 19 | { 20 | return stache_compiler::compile(ast); 21 | } 22 | }}} 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /include/boost/boostache/boostache.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file boostache.hpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_BOOSTACHE_HPP 10 | #define BOOST_BOOSTACHE_BOOSTACHE_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | namespace boost { namespace boostache 20 | { 21 | template 22 | inline vm::ast::node load_template(Iterator & begin, Iterator const & end) 23 | { 24 | return backend::compile(frontend::parse(begin,end)); 25 | } 26 | 27 | template 28 | inline vm::ast::node load_template(std::istream & input) 29 | { 30 | return backend::compile(frontend::parse(input)); 31 | } 32 | 33 | template 34 | void generate( Stream & stream 35 | , vm::ast::node const & templ 36 | , Context const & context) 37 | { 38 | vm::generate(stream,templ,context); 39 | } 40 | }} 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /include/boost/boostache/detail/unwrap_variant_visitor.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file detail/unwrap_variant_visitor.hpp 3 | * 4 | * Copyright 2015 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_DETAIL_UNWRAP_VARIANT_VISITOR_HPP 10 | #define BOOST_BOOSTACHE_DETAIL_UNWRAP_VARIANT_VISITOR_HPP 11 | 12 | 13 | namespace boost { namespace boostache { namespace detail 14 | { 15 | template 16 | struct unwrap_variant_visitor 17 | { 18 | using result_type = R; 19 | 20 | template 21 | unwrap_variant_visitor(U && f) 22 | : f_(f) 23 | {} 24 | 25 | template 26 | R operator()(T const & context) const 27 | { 28 | return f_(context); 29 | } 30 | 31 | F f_; 32 | }; 33 | 34 | template 35 | struct unwrap_variant_visitor 36 | { 37 | using result_type = void; 38 | 39 | template 40 | unwrap_variant_visitor(U && f) 41 | : f_(f) 42 | {} 43 | 44 | template 45 | void operator()(T const & context) const 46 | { 47 | f_(context); 48 | } 49 | 50 | F f_; 51 | }; 52 | 53 | 54 | template 55 | unwrap_variant_visitor make_unwrap_variant_visitor(F && f) 56 | { 57 | return unwrap_variant_visitor{std::forward(f)}; 58 | } 59 | }}} 60 | 61 | 62 | #endif // BOOST_BOOSTACHE_DETAIL_UNWRAP_VARIANT_VISITOR_HPP 63 | -------------------------------------------------------------------------------- /include/boost/boostache/frontend/parse.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file parse.hpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * 6 | * Generic parser entry point. Call parse with the input format 7 | * as the template parameter: 8 | * 9 | * auto ast = parse(...); 10 | * 11 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 12 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 13 | */ 14 | #ifndef BOOST_BOOSTACHE_FRONT_END_TAGS_HPP 15 | #define BOOST_BOOSTACHE_FRONT_END_TAGS_HPP 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | namespace boost { namespace boostache { namespace frontend 22 | { 23 | template 24 | typename Format::ast_t parse(Iterator & begin, Iterator const & end) 25 | { 26 | typename Format::ast_t ast; 27 | typename Format::template grammar_t grammar; 28 | 29 | // TODO mjc : should throw with parse error location 30 | // if(!boost::spirit::qi::phrase_parse( begin, end 31 | // , grammar 32 | // , typename Format::skipper_t{} 33 | // , ast )) 34 | if(!boost::spirit::qi::parse( begin, end 35 | , grammar 36 | , ast )) 37 | { 38 | ast = typename Format::ast_t{}; 39 | } 40 | return ast; 41 | } 42 | 43 | 44 | template 45 | typename Format::ast_t parse(std::istream& input) 46 | { 47 | // TODO mjc : store/restore ios state? 48 | input.unsetf(std::ios::skipws); 49 | boost::spirit::istream_iterator iter{input}; 50 | return parse( iter 51 | , boost::spirit::istream_iterator{} ); 52 | } 53 | }}} 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/boost/boostache/frontend/stache/ast.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file stache_ast.hpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * Copyright 2014 Jeroen Habraken 6 | * 7 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | */ 10 | #ifndef BOOST_BOOSTACHE_FRONT_END_STACHE_AST_HPP 11 | #define BOOST_BOOSTACHE_FRONT_END_STACHE_AST_HPP 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace boost { namespace boostache { namespace frontend { namespace stache { namespace ast 18 | { 19 | struct node; 20 | 21 | struct undefined {}; 22 | 23 | struct comment {}; 24 | 25 | struct identifier : std::string 26 | {}; 27 | 28 | struct literal_text : std::string 29 | {}; 30 | 31 | struct blank_text : std::string 32 | {}; 33 | 34 | struct eol : std::string 35 | { 36 | using std::string::string; 37 | }; 38 | 39 | struct variable 40 | { 41 | bool is_unescaped; 42 | identifier value; 43 | }; 44 | 45 | struct partial : identifier 46 | {}; 47 | 48 | struct section; 49 | 50 | struct node : boost::spirit::extended_variant< 51 | undefined 52 | , comment 53 | , literal_text 54 | , blank_text 55 | , eol 56 | , variable 57 | , boost::recursive_wrapper
58 | , partial 59 | > 60 | { 61 | node() : base_type() {} 62 | node(comment const & rhs) : base_type(rhs) {} 63 | node(literal_text const & rhs) : base_type(rhs) {} 64 | node(blank_text const & rhs) : base_type(rhs) {} 65 | node(eol const & rhs) : base_type(rhs) {} 66 | node(variable const & rhs) : base_type(rhs) {} 67 | node(section const & rhs) : base_type(rhs) {} 68 | node(partial const & rhs) : base_type(rhs) {} 69 | }; 70 | 71 | struct node_list : std::vector {}; 72 | 73 | struct section 74 | { 75 | bool is_inverted; 76 | identifier name; 77 | node_list nodes; 78 | }; 79 | 80 | struct root : node_list {}; 81 | 82 | }}}}} 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /include/boost/boostache/frontend/stache/ast_adapted.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file stache_ast_adapted.hpp 3 | * 4 | * Fusion adaption for the stache ast 5 | * 6 | * Copyright 2014 Michael Caisse : ciere.com 7 | * Copyright 2014 Jeroen Habraken 8 | * 9 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 10 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 11 | */ 12 | #ifndef BOOST_BOOSTACHE_FRONT_END_STACHE_AST_ADAPTED_HPP 13 | #define BOOST_BOOSTACHE_FRONT_END_STACHE_AST_ADAPTED_HPP 14 | 15 | #include 16 | #include 17 | 18 | BOOST_FUSION_ADAPT_STRUCT( 19 | boost::boostache::frontend::stache::ast::variable, 20 | (bool, is_unescaped) 21 | (boost::boostache::frontend::stache::ast::identifier, value) 22 | ) 23 | 24 | BOOST_FUSION_ADAPT_STRUCT( 25 | boost::boostache::frontend::stache::ast::section, 26 | (bool, is_inverted) 27 | (boost::boostache::frontend::stache::ast::identifier, name) 28 | (boost::boostache::frontend::stache::ast::node_list, nodes) 29 | ) 30 | 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /include/boost/boostache/frontend/stache/grammar.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file stache_grammar.hpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * Copyright 2014 Jeroen Habraken 6 | * 7 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | */ 10 | #ifndef BOOST_BOOSTACHE_FRONT_END_STACHE_GRAMMAR_HPP 11 | #define BOOST_BOOSTACHE_FRONT_END_STACHE_GRAMMAR_HPP 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | 19 | namespace boost { namespace boostache { namespace frontend { namespace stache 20 | { 21 | namespace qi = boost::spirit::qi; 22 | 23 | template 24 | struct grammar 25 | : qi::grammar 26 | { 27 | grammar(); 28 | 29 | qi::rule 30 | node_list 31 | ; 32 | 33 | qi::rule 34 | stache_node 35 | ; 36 | 37 | qi::rule 38 | identifier 39 | ; 40 | 41 | qi::rule 42 | comment 43 | ; 44 | 45 | qi::rule 46 | literal_text 47 | ; 48 | 49 | qi::rule 50 | blank_text 51 | ; 52 | 53 | qi::rule 54 | end_of_line 55 | ; 56 | 57 | qi::rule 58 | variable 59 | ; 60 | 61 | qi::rule 62 | variable_unescaped 63 | ; 64 | 65 | qi::rule/*, qi::space_type*/> 66 | section 67 | ; 68 | 69 | qi::rule 70 | section_begin 71 | ; 72 | 73 | qi::rule 74 | section_end 75 | ; 76 | 77 | qi::rule 78 | partial 79 | ; 80 | }; 81 | }}}} 82 | 83 | #endif 84 | 85 | -------------------------------------------------------------------------------- /include/boost/boostache/frontend/stache/grammar_def.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file stache/grammar_def.hpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * Copyright 2014 Jeroen Habraken 6 | * 7 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | */ 10 | #ifndef BOOST_BOOSTACHE_FRONT_END_STACHE_GRAMMAR_DEF_HPP 11 | #define BOOST_BOOSTACHE_FRONT_END_STACHE_GRAMMAR_DEF_HPP 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | namespace boost { namespace boostache { namespace frontend { namespace stache 38 | { 39 | namespace qi = boost::spirit::qi; 40 | namespace spirit = boost::spirit; 41 | 42 | template 43 | grammar::grammar() 44 | : grammar::base_type(node_list) 45 | { 46 | spirit::_1_type _1; 47 | spirit::_a_type _a; 48 | spirit::_r1_type _r1; 49 | spirit::eol_type eol; 50 | qi::alnum_type alnum; 51 | qi::alpha_type alpha; 52 | qi::blank_type blank; 53 | qi::attr_type attr; 54 | qi::char_type char_; 55 | qi::lexeme_type lexeme; 56 | qi::lit_type lit; 57 | qi::matches_type matches; 58 | qi::no_skip_type no_skip; 59 | qi::skip_type skip; 60 | qi::omit_type omit; 61 | 62 | 63 | stache_node = 64 | end_of_line 65 | | blank_text 66 | | no_skip[literal_text] 67 | | comment 68 | | variable 69 | | variable_unescaped 70 | | section 71 | | partial 72 | ; 73 | 74 | 75 | node_list = 76 | *stache_node 77 | ; 78 | 79 | literal_text = 80 | +(char_ - (eol|"{{")) 81 | ; 82 | 83 | blank_text = 84 | no_skip[+blank] 85 | ; 86 | 87 | end_of_line = 88 | char_('\n') 89 | >> ( (char_("\r") >> attr(ast::eol{"\n\r"})) 90 | | attr(ast::eol{"\n"}) ); 91 | ; 92 | 93 | comment = 94 | lit("{{") 95 | >> '!' 96 | >> omit[*(char_ - "}}")] 97 | >> "}}" 98 | ; 99 | 100 | identifier = 101 | lexeme[alpha >> *(alnum | char_('_'))] 102 | ; 103 | 104 | variable = 105 | lit("{{") 106 | >> skip(qi::space_type{}) 107 | [ 108 | matches['&'] 109 | >> ("." | identifier) 110 | >> "}}" 111 | ] 112 | ; 113 | 114 | variable_unescaped = 115 | lit("{{{") 116 | >> skip(qi::space_type{}) 117 | [ 118 | attr(true) 119 | >> identifier 120 | >> "}}}" 121 | ] 122 | ; 123 | 124 | section %= 125 | matches[&(lit("{{") >> '^')] 126 | >> section_begin[_a = _1] 127 | >> *stache_node 128 | >> section_end(_a) 129 | ; 130 | 131 | section_begin = 132 | lit("{{") 133 | >> skip(qi::space_type{}) 134 | [ 135 | (lit('#') | '^') 136 | >> (("." >> attr(std::string("."))) | identifier) 137 | >> "}}" 138 | // >> omit[ no_skip[ (*char_(" ") >> eol) ] ] 139 | ] 140 | ; 141 | 142 | section_end = 143 | lit("{{") 144 | >> skip(qi::space_type{}) 145 | [ 146 | '/' 147 | >> lit(_r1) 148 | >> "}}" 149 | ] 150 | // >> omit[ no_skip[ (*char_(" ") >> eol) ] ] 151 | ; 152 | 153 | partial = 154 | lit("{{") 155 | >> skip(qi::space_type{}) 156 | [ 157 | '>' 158 | >> identifier 159 | >> "}}" 160 | ] 161 | ; 162 | }; 163 | }}}} 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /include/boost/boostache/frontend/stache/printer.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file stache_printer.hpp 3 | * 4 | * Copyright 2014, 2015 Michael Caisse : ciere.com 5 | * Copyright 2014 Jeroen Habraken 6 | * 7 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | */ 10 | #ifndef BOOST_BOOSTACHE_FRONT_END_STACHE_PRINTER_HPP 11 | #define BOOST_BOOSTACHE_FRONT_END_STACHE_PRINTER_HPP 12 | 13 | #include 14 | #include 15 | 16 | namespace boost { namespace boostache { namespace frontend { namespace stache { namespace ast 17 | { 18 | namespace detail 19 | { 20 | class printer 21 | { 22 | public: 23 | typedef void result_type; 24 | 25 | printer(std::ostream& out) 26 | : out(out) 27 | {} 28 | 29 | void operator()(undefined) const 30 | { 31 | out << "WHOA! we have an undefined" << std::endl; 32 | } 33 | 34 | void operator()(blank_text const & v) const 35 | { 36 | out << "" << v; 37 | } 38 | 39 | void operator()(eol const & v) const 40 | { 41 | out << "" << v; 42 | } 43 | 44 | void operator()(comment) const 45 | { 46 | out << ""; 47 | } 48 | 49 | void operator()(literal_text const & v) const 50 | { 51 | out << v; 52 | } 53 | 54 | void operator()(variable const & v) const 55 | { 56 | out << "{{"; 57 | if(v.is_unescaped) 58 | { 59 | out << "&"; 60 | } 61 | out << v.value << "}}"; 62 | } 63 | 64 | void operator()(section const & v) const 65 | { 66 | out << "{{"; 67 | if(v.is_inverted) { out << "^"; } 68 | else { out << "#"; } 69 | out << v.name << "}}"; 70 | 71 | for(auto const & node : v.nodes) 72 | { 73 | boost::apply_visitor(*this, node); 74 | } 75 | 76 | out << "{{/" << v.name << "}}"; 77 | } 78 | 79 | void operator()(partial const & v) const 80 | { 81 | out << "{{>" << v << "}}"; 82 | } 83 | 84 | private: 85 | std::ostream& out; 86 | }; 87 | } 88 | 89 | inline void print(std::ostream& out, node_list const& nodes) 90 | { 91 | detail::printer p(out); 92 | for(auto const & node : nodes) 93 | { 94 | boost::apply_visitor(p, node); 95 | } 96 | } 97 | }}}}} 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /include/boost/boostache/model/basic_render_extension.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file basic_render_extension.hpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_MODEL_BASIC_RENDER_EXTENSION_HPP 10 | #define BOOST_BOOSTACHE_MODEL_BASIC_RENDER_EXTENSION_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | namespace boost { namespace boostache { namespace extension 20 | { 21 | template< typename Stream, typename T > 22 | void render(Stream & stream, T const & context, std::string const & name); 23 | 24 | 25 | 26 | template< typename Stream 27 | , typename T 28 | , typename Enable = typename std::enable_if::value>::type 29 | > 30 | auto render( Stream && stream, T const & context, std::string const & name 31 | , plain_attribute) -> decltype(std::forward(stream)<(stream) << context); 34 | } 35 | 36 | 37 | template< typename Stream 38 | , typename T 39 | > 40 | void render( Stream && stream, T const & context, std::string const & name 41 | , optional_attribute) 42 | { 43 | render(std::forward(stream),*context,name); 44 | } 45 | 46 | 47 | template< typename Stream 48 | , typename T 49 | > 50 | void render( Stream && stream, T const & context, std::string const & name 51 | , unused_attribute) 52 | { 53 | } 54 | 55 | 56 | template< typename Stream 57 | , typename T 58 | > 59 | void render( Stream && stream, T const & context, std::string const & name 60 | , associative_attribute) 61 | { 62 | auto iter = context.find(name); 63 | if(iter!=context.end()) 64 | { 65 | render(std::forward(stream),iter->second,name); 66 | } 67 | } 68 | 69 | 70 | template< typename Stream 71 | , typename T 72 | > 73 | void render( Stream && stream, T const & context, std::string const & name 74 | , sequence_attribute) 75 | { 76 | for(auto const & item : context) 77 | { 78 | render(std::forward(stream),item,name); 79 | } 80 | } 81 | 82 | 83 | // -------------------------------------------------------------------------- 84 | // -------------------------------------------------------------------------- 85 | 86 | template< typename Stream, typename T > 87 | void render(Stream & stream, T const & context, std::string const & name) 88 | { 89 | render( stream 90 | , context 91 | , name 92 | , render_category_t{} ); 93 | } 94 | 95 | }}} 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /include/boost/boostache/model/basic_test_extension.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file basic_test_extension.hpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_MODEL_BASIC_TEST_EXTENSION_HPP 10 | #define BOOST_BOOSTACHE_MODEL_BASIC_TEST_EXTENSION_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | namespace boost { namespace boostache { namespace extension 20 | { 21 | // -------------------------------------------------------------------------- 22 | // Test 23 | // -------------------------------------------------------------------------- 24 | template 25 | bool test(T const & context); 26 | 27 | 28 | template 29 | bool test( T const & context 30 | , unused_attribute) 31 | { 32 | return false; 33 | } 34 | 35 | template 36 | bool test( T const & context 37 | , plain_attribute) 38 | { 39 | return !!context; 40 | } 41 | 42 | template 43 | bool test( T const & context 44 | , sequence_attribute) 45 | { 46 | return !context.empty(); 47 | } 48 | 49 | template 50 | bool test( T const & context 51 | , optional_attribute) 52 | { 53 | return !!context; 54 | } 55 | 56 | template 57 | bool test( T const & context 58 | , associative_attribute) 59 | { 60 | return !context.empty(); 61 | } 62 | 63 | 64 | // -------------------------------------------------------------------------- 65 | // Test with tag 66 | // -------------------------------------------------------------------------- 67 | 68 | template 69 | bool test(T const & context, std::string const & tag); 70 | 71 | 72 | template 73 | bool test( T const & context, std::string const & tag 74 | , unused_attribute) 75 | { 76 | return test(context, unused_attribute{}); 77 | } 78 | 79 | template 80 | bool test( T const & context, std::string const & tag 81 | , plain_attribute) 82 | { 83 | return test(context, plain_attribute{}); 84 | } 85 | 86 | template 87 | bool test( T const & context, std::string const & tag 88 | , sequence_attribute) 89 | { 90 | return test(context, sequence_attribute{}); 91 | } 92 | 93 | template 94 | bool test( T const & context, std::string const & tag 95 | , optional_attribute) 96 | { 97 | return test(context, optional_attribute{}); 98 | } 99 | 100 | template 101 | bool test( T const & context, std::string const & tag 102 | , associative_attribute) 103 | { 104 | auto iter = context.find(tag); 105 | if(iter!=context.end()) 106 | { 107 | return test( iter->second 108 | , test_category_tsecond)>{}); 109 | } 110 | else 111 | { 112 | return false; 113 | } 114 | } 115 | 116 | 117 | // -------------------------------------------------------------------------- 118 | // -------------------------------------------------------------------------- 119 | 120 | template 121 | bool test(T const & context) 122 | { 123 | return test( context 124 | , test_category_t{}); 125 | } 126 | 127 | template 128 | bool test(T const & context, std::string const & tag) 129 | { 130 | return test( context 131 | , tag 132 | , test_category_t{}); 133 | } 134 | 135 | }}} 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /include/boost/boostache/model/category.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file category.hpp 3 | * 4 | * Copyright 2014, 2015 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_MODEL_CATEGORY_HPP 10 | #define BOOST_BOOSTACHE_MODEL_CATEGORY_HPP 11 | 12 | 13 | namespace boost { namespace boostache { namespace extension 14 | { 15 | // -------------------------------- 16 | // classifications 17 | struct category_attribute {}; 18 | struct unused_attribute : category_attribute {}; 19 | struct plain_attribute : category_attribute {}; 20 | struct sequence_attribute : category_attribute {}; 21 | struct associative_attribute : category_attribute {}; 22 | struct tuple_attribute : category_attribute {}; 23 | struct variant_attribute : category_attribute {}; 24 | struct optional_attribute : category_attribute {}; 25 | 26 | }}} 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /include/boost/boostache/model/default_adapter.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file default_adapater.hpp 3 | * 4 | * Copyright 2014 Michal Bukovsky 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | 10 | #ifndef BOOST_BOOSTACHE_MODEL_DEFAULT_ADAPTER_HPP 11 | #define BOOST_BOOSTACHE_MODEL_DEFAULT_ADAPTER_HPP 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | namespace boost { namespace boostache { namespace model 22 | { 23 | 24 | namespace detail 25 | { 26 | 27 | struct section_getter_visitor 28 | { 29 | typedef void result_type; 30 | 31 | section_getter_visitor(section_range_sink &sink): sink(sink) {} 32 | 33 | template 34 | void operator()(const param_type &) const 35 | { 36 | sink("<<>>"); 37 | } 38 | 39 | void operator()(const std::vector &model) const 40 | { 41 | sink(model); 42 | } 43 | 44 | void operator()(const frontend::stache_model &model) const 45 | { 46 | sink(std::array{{&model}}); 47 | } 48 | 49 | section_range_sink &sink; 50 | }; 51 | 52 | struct variable_getter_visitor 53 | { 54 | typedef void result_type; 55 | 56 | variable_getter_visitor(variable_sink &sink): sink(sink) {} 57 | 58 | template 59 | void operator()(const param_type &value) const 60 | { 61 | sink(value); 62 | } 63 | 64 | template 65 | void operator()(std::function function) const 66 | { 67 | (*this)(function()); 68 | } 69 | 70 | void operator()(const std::vector &) const 71 | { 72 | sink("<<>>"); 73 | } 74 | 75 | void operator()(const frontend::stache_model &) const 76 | { 77 | sink("<<>>"); 78 | } 79 | 80 | variable_sink &sink; 81 | }; 82 | 83 | } // namespace detail 84 | 85 | template <> 86 | void get_variable_value(const frontend::stache_model &model, 87 | const std::string &key, 88 | variable_sink &sink) 89 | { 90 | auto ivar = model.find(key); 91 | if (ivar != model.end()) { 92 | detail::variable_getter_visitor get_variable(sink); 93 | boost::apply_visitor(get_variable, ivar->second); 94 | } 95 | } 96 | 97 | // TODO(burlog): if variant change remove this specialization 98 | template <> 99 | void get_variable_value(const frontend::stache_variant &model, 100 | const std::string &key, 101 | variable_sink &sink) 102 | { 103 | auto m = boost::get(&model); 104 | if (m) get_variable_value(*m, key, sink); 105 | } 106 | 107 | template <> 108 | void get_section_value(const frontend::stache_model &model, 109 | const std::string &key, 110 | section_range_sink &sink) 111 | { 112 | auto isubmodel = model.find(key); 113 | if (isubmodel != model.end()) { 114 | detail::section_getter_visitor get_section(sink); 115 | boost::apply_visitor(get_section, isubmodel->second); 116 | } 117 | } 118 | 119 | }}} 120 | 121 | #endif // BOOST_BOOSTACHE_MODEL_DEFAULT_ADAPTER_HPP 122 | 123 | -------------------------------------------------------------------------------- /include/boost/boostache/model/dynamic_model_printer.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file dynamic_model_printer.hpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * Copyright 2014 Kevin Harris 6 | * Copyright 2014 Michal Bukovsky 7 | * 8 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 9 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | */ 11 | 12 | #ifndef BOOST_BOOSTACHE_MODEL_DYNAMIC_MODEL_PRINTER_HPP 13 | #define BOOST_BOOSTACHE_MODEL_DYNAMIC_MODEL_PRINTER_HPP 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | namespace boost { namespace boostache { namespace model 24 | { 25 | 26 | namespace detail 27 | { 28 | 29 | namespace fe = boost::boostache::frontend; 30 | 31 | struct empty_model {}; 32 | struct root_parent_printer {}; 33 | 34 | typedef boost::function parent_lookup_type; 35 | 36 | template 37 | class dynamic_model_printer 38 | { 39 | public: 40 | typedef void result_type; 41 | 42 | dynamic_model_printer(std::ostream &out, 43 | const model_type &model, 44 | parent_lookup_type parent_lookup 45 | = parent_lookup_type()) 46 | : out(out), model(model), parent_lookup(parent_lookup) 47 | {} 48 | 49 | void operator()(fe::stache::ast::comment) const 50 | { 51 | // just ignore comments 52 | } 53 | 54 | void operator()(fe::stache::ast::partial) const 55 | { 56 | // FIXME: implement me please 57 | } 58 | 59 | void operator()(fe::stache::ast::undefined) const 60 | { 61 | out << "WHOA! we have an undefined" << std::endl; 62 | } 63 | 64 | void operator()(fe::stache::ast::literal_text const &v) const 65 | { 66 | out << v; 67 | } 68 | 69 | void operator()(fe::stache::ast::variable const &v) const; 70 | 71 | void operator()(fe::stache::ast::section const &v) const; 72 | 73 | private: 74 | std::ostream &out; 75 | const model_type &model; 76 | parent_lookup_type parent_lookup; 77 | }; 78 | 79 | template 80 | parent_lookup_type make_parent_lookup(printer_type *printer) 81 | { 82 | return [printer] (const fe::stache::ast::variable &v) { (*printer)(v);}; 83 | } 84 | 85 | } // namespace detail 86 | 87 | struct variable_sink: public boost::noncopyable 88 | { 89 | variable_sink(std::ostream &out, frontend::stache::ast::variable const &v) 90 | : out(out), v(v), printed(false) 91 | {} 92 | 93 | template 94 | void operator()(const variable_value_type &value) 95 | { 96 | printed = true; 97 | out << value; 98 | } 99 | 100 | bool isprinted() const { return printed;} 101 | 102 | private: 103 | std::ostream &out; 104 | const frontend::stache::ast::variable &v; 105 | bool printed; 106 | }; 107 | 108 | struct section_range_sink: public boost::noncopyable 109 | { 110 | section_range_sink(std::ostream &out, 111 | frontend::stache::ast::section const &v, 112 | detail::parent_lookup_type parent_lookup 113 | = detail::parent_lookup_type()) 114 | : out(out), v(v), printed(false), parent_lookup(parent_lookup) 115 | {} 116 | 117 | template 118 | void operator()(const submodel_range_type &submodels) 119 | { 120 | printed = true; 121 | if (!v.is_inverted) 122 | { 123 | for (const auto &submodel: submodels) 124 | { 125 | auto submodel_printer = make_printer(submodel); 126 | for (const auto &node: v.nodes) 127 | { 128 | boost::apply_visitor(submodel_printer, node); 129 | } 130 | } 131 | 132 | } 133 | else if (v.is_inverted && boost::empty(submodels)) 134 | { 135 | auto submodel_printer = make_printer(detail::empty_model()); 136 | for(const auto &node: v.nodes) 137 | { 138 | boost::apply_visitor(submodel_printer, node); 139 | } 140 | } 141 | } 142 | 143 | template 144 | detail::dynamic_model_printer 145 | make_printer(const submodel_type *submodel) const 146 | { 147 | return make_printer(*submodel); 148 | } 149 | 150 | template 151 | detail::dynamic_model_printer 152 | make_printer(const submodel_type &submodel) const 153 | { 154 | return detail::dynamic_model_printer 155 | (out, submodel, parent_lookup); 156 | } 157 | 158 | template 159 | detail::dynamic_model_printer 160 | make_printer(const std::pair &pair) const 161 | { 162 | return detail::dynamic_model_printer 163 | (out, pair.second, parent_lookup); 164 | } 165 | 166 | bool isprinted() const { return printed;} 167 | 168 | private: 169 | std::ostream &out; 170 | const frontend::stache::ast::section &v; 171 | bool printed; 172 | detail::parent_lookup_type parent_lookup; 173 | }; 174 | 175 | /** This template function is intended to specialization for user own 176 | * types and should return variable for given key. 177 | */ 178 | template 179 | void get_variable_value(const model_type &, 180 | const std::string &key, 181 | variable_sink &) 182 | { 183 | throw std::runtime_error("you should write specialization for " 184 | "get_variable_value(" + key + ") for type: " 185 | + typeid(model_type).name()); 186 | } 187 | 188 | template 189 | void get_section_value(const model_type &, 190 | const std::string &key, 191 | section_range_sink &) 192 | { 193 | throw std::runtime_error("you should write specialization for " 194 | "get_section_value(" + key + ") for type: " 195 | + typeid(model_type).name()); 196 | } 197 | 198 | template 199 | void detail::dynamic_model_printer::operator() 200 | (frontend::stache::ast::variable const &v) const 201 | { 202 | variable_sink sink(out, v); 203 | get_variable_value(model, v.value, sink); 204 | if (!sink.isprinted()) 205 | { 206 | // if user don't call sink it means that no variable exist 207 | if (parent_lookup) parent_lookup(v); 208 | } 209 | } 210 | 211 | template 212 | void detail::dynamic_model_printer::operator() 213 | (frontend::stache::ast::section const &v) const 214 | { 215 | section_range_sink sink(out, v, make_parent_lookup(this)); 216 | get_section_value(model, v.name, sink); 217 | if (!sink.isprinted()) 218 | { 219 | // if user don't call sink it means that no section exist 220 | section_range_sink sink(out, v, make_parent_lookup(this)); 221 | sink(std::array()); 222 | } 223 | } 224 | 225 | template 226 | void print(std::ostream &out, 227 | const frontend::stache::ast::root &root, 228 | const model_type &model) 229 | { 230 | // TODO(burlog): solve it 231 | // HACK - make the stache_root into a section 232 | frontend::stache::ast::section section; 233 | section.is_inverted = false; 234 | section.nodes = root; 235 | section_range_sink sink(out, section); 236 | sink(std::array{{&model}}); 237 | } 238 | 239 | }}} 240 | 241 | #endif // BOOST_BOOSTACHE_MODEL_DYNAMIC_MODEL_PRINTER_HPP 242 | 243 | -------------------------------------------------------------------------------- /include/boost/boostache/model/helper.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file helper.hpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * 6 | * Includes various files that will help with the adaption/extension of 7 | * data types to be identified and used by the data model portion of the 8 | * engine. 9 | * 10 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 11 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 12 | */ 13 | #ifndef BOOST_BOOSTACHE_MODEL_HELPER_HPP 14 | #define BOOST_BOOSTACHE_MODEL_HELPER_HPP 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /include/boost/boostache/model/property_tree_adapter.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file test_json_model.cpp 3 | * 4 | * Copyright 2014 Michal Bukovsky 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | 10 | #ifndef BOOST_BOOSTACHE_MODEL_PROPERTY_TREE_ADAPTER_HPP 11 | #define BOOST_BOOSTACHE_MODEL_PROPERTY_TREE_ADAPTER_HPP 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | namespace boost { namespace boostache { namespace model 20 | { 21 | 22 | template <> 23 | void get_variable_value(const boost::property_tree::ptree &model, 24 | const std::string &key, 25 | variable_sink &sink) 26 | { 27 | auto ivar = model.find(key); 28 | if (ivar != model.not_found()) sink(ivar->second.data()); 29 | } 30 | 31 | template <> 32 | void get_section_value(const boost::property_tree::ptree &model, 33 | const std::string &key, 34 | section_range_sink &sink) 35 | { 36 | auto isubmodel = model.find(key); 37 | if (isubmodel != model.not_found()) 38 | { 39 | auto &submodel = isubmodel->second; 40 | if (!submodel.empty()) 41 | { 42 | // in terms of property tree world: nodes with empty key are array 43 | // entries so pass them directly into printer otherwise create 44 | // temporary array 45 | if (submodel.front().first.empty()) sink(submodel); 46 | else sink(std::array{{&submodel}}); 47 | } 48 | } 49 | } 50 | 51 | }}} 52 | 53 | #endif // BOOST_BOOSTACHE_MODEL_PROPERTY_TREE_ADAPTER_HPP 54 | 55 | -------------------------------------------------------------------------------- /include/boost/boostache/model/render_traits.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file render_traits.hpp 3 | * 4 | * Copyright 2014, 2015, 2019 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_MODEL_RENDER_TRAITS_HPP 10 | #define BOOST_BOOSTACHE_MODEL_RENDER_TRAITS_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | namespace boost { namespace boostache { namespace extension 21 | { 22 | template 23 | struct render_category 24 | : mpl::identity {}; 25 | 26 | template <> 27 | struct render_category 28 | : mpl::identity {}; 29 | 30 | template 31 | struct render_category< T 32 | , vm::trait::enable_if_is_variant_t > 33 | : mpl::identity {}; 34 | 35 | template 36 | struct render_category> 37 | : mpl::identity {}; 38 | 39 | template 40 | struct render_category> 41 | : mpl::identity {}; 42 | 43 | template 44 | struct render_category> 45 | : mpl::identity {}; 46 | 47 | template 48 | struct render_category< T 49 | , vm::trait::enable_if_sequence_not_map_t 50 | > 51 | : mpl::identity {}; 52 | 53 | template 54 | using render_category_t = typename render_category::type; 55 | 56 | }}} 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /include/boost/boostache/model/select_traits.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file select_traits.hpp 3 | * 4 | * Copyright 2014, 2015, 2019 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_MODEL_SELECT_TRAITS_HPP 10 | #define BOOST_BOOSTACHE_MODEL_SELECT_TRAITS_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | namespace boost { namespace boostache { namespace extension 22 | { 23 | // ------------------------------------------- 24 | // test category 25 | template 26 | struct select_category 27 | : mpl::identity {}; 28 | 29 | template 30 | struct select_category< T 31 | , vm::trait::enable_if_is_variant_t 32 | > 33 | : mpl::identity {}; 34 | 35 | template 36 | struct select_category< T 37 | , vm::trait::enable_if_sequence_not_map_t 38 | > 39 | : mpl::identity {}; 40 | 41 | template 42 | struct select_category> 43 | : mpl::identity {}; 44 | 45 | template 46 | struct select_category> 47 | : mpl::identity {}; 48 | 49 | template 50 | struct select_category> 51 | : mpl::identity {}; 52 | 53 | template 54 | using select_category_t = typename select_category::type; 55 | 56 | }}} 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /include/boost/boostache/model/stache_model.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file stache_model.hpp 3 | * 4 | * Copyright 2014 Kevin Harris 5 | * Copyright 2014 Michael Caisse : ciere.com 6 | * 7 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | */ 10 | #pragma once 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace boost { namespace boostache { namespace model 20 | { 21 | struct stache_variant; 22 | 23 | using stache_string_function = std::function; 24 | using stache_bool_function = std::function; 25 | using stache_model_vector = std::vector; 26 | using stache_model = std::map; 27 | 28 | 29 | struct stache_variant : boost::spirit::extended_variant< 30 | bool 31 | , std::string 32 | , stache_string_function 33 | , stache_bool_function 34 | , stache_model_vector 35 | , stache_model 36 | > 37 | { 38 | stache_variant() : base_type() {} 39 | stache_variant(bool rhs) : base_type(rhs) {} 40 | stache_variant(std::string const & rhs) : base_type(rhs) {} 41 | stache_variant(const char * rhs) : base_type(std::string{rhs}) {} 42 | stache_variant(stache_string_function const & rhs) : base_type(rhs) {} 43 | stache_variant(stache_bool_function const & rhs) : base_type(rhs) {} 44 | stache_variant(std::vector const & rhs) : base_type(rhs) {} 45 | stache_variant(std::map const & rhs) : base_type(rhs) {} 46 | 47 | template 48 | stache_variant(std::initializer_list l) : base_type(l) {} 49 | 50 | template 51 | stache_variant & operator=(T const & rhs) 52 | { 53 | base_type::operator=(rhs); 54 | return *this; 55 | } 56 | }; 57 | }}} 58 | -------------------------------------------------------------------------------- /include/boost/boostache/model/stache_model_printer.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file stache_model_printer.hpp 3 | * 4 | * 5 | * Copyright 2014 Michael Caisse : ciere.com 6 | * Copyright 2014 Kevin Harris 7 | * 8 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 9 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | */ 11 | #ifndef BOOST_BOOSTACHE_FRONT_END_STACHE_MODEL_PRINTER_HPP 12 | #define BOOST_BOOSTACHE_FRONT_END_STACHE_MODEL_PRINTER_HPP 13 | 14 | #include 15 | #include 16 | 17 | namespace boost { namespace boostache { namespace model { namespace ast 18 | { 19 | namespace detail 20 | { 21 | namespace fe = boost::boostache::frontend; 22 | 23 | template 24 | void apply_visitor_to_root( const visitor& v 25 | , std::vector const & root) 26 | { 27 | for( const auto& node : root ) 28 | { 29 | boost::apply_visitor(v, node); 30 | } 31 | } 32 | 33 | struct stache_model_visitor 34 | { 35 | typedef std::string result_type; 36 | std::string operator()(const stache_string_function& f) const 37 | { 38 | return operator()(f()); 39 | } 40 | std::string operator()(const stache_bool_function& f) const 41 | { 42 | return f() ? "true" : "false"; 43 | } 44 | std::string operator()(const bool v) const 45 | { 46 | return v ? "true" : "false"; 47 | } 48 | std::string operator()(const std::string& s) const 49 | { 50 | return s; 51 | } 52 | std::string operator()(const stache_model& m) const 53 | { 54 | // TODO: Do something useful 55 | return "Oops. Don't know how to format a stache model.\n"; 56 | } 57 | std::string operator()(const stache_model_vector& v) const 58 | { 59 | return "Oops. Don't know how to format a stache model vector.\n"; 60 | } 61 | }; 62 | 63 | class stache_model_printer 64 | { 65 | public: 66 | typedef void result_type; 67 | 68 | stache_model_printer(std::ostream& out, const stache_model& model, const stache_model_printer* parent = nullptr) 69 | : out(out) 70 | , model(model) 71 | , parent(parent) 72 | {} 73 | 74 | void operator()(fe::stache::ast::undefined) const 75 | { 76 | out << "WHOA! we have an undefined" << std::endl; 77 | } 78 | 79 | void operator()(fe::stache::ast::literal_text const & v) const 80 | { 81 | out << v; 82 | } 83 | 84 | void operator()(fe::stache::ast::variable const & v) const 85 | { 86 | // TODO: Escaping. 87 | if( auto location = lookup(v.value) ) 88 | { 89 | stache_model_visitor visitor; 90 | out << boost::apply_visitor(visitor, *location); 91 | } 92 | } 93 | 94 | void operator()(const fe::stache::ast::comment& c) const 95 | { 96 | // Nothing to do. 97 | } 98 | 99 | void operator()(const fe::stache::ast::partial& p) const 100 | { 101 | // TODO: Something. 102 | } 103 | 104 | void operator()(fe::stache::ast::section const & v) const 105 | { 106 | const stache_variant* location = lookup(v.name); 107 | bool positive = have_value(location); 108 | if( positive && !v.is_inverted ) 109 | { 110 | if (auto vec = get(location)) 111 | { 112 | for( const auto& entry : *vec ) 113 | { 114 | const stache_model* m = boost::get(&entry); 115 | if( m ) 116 | { 117 | stache_model_printer section_printer(out, *m, this); 118 | apply_visitor_to_root(section_printer, v.nodes); 119 | } 120 | else 121 | { 122 | apply_visitor_to_root(*this, v.nodes); 123 | } 124 | } 125 | } 126 | else if (auto model = get(location)) 127 | { 128 | stache_model_printer section_printer(out, *model, this); 129 | apply_visitor_to_root(section_printer, v.nodes); 130 | } 131 | else 132 | { 133 | // This is the odd case that they requested a section, but it was 134 | // some non-mapped type. We can handle this by recursively calling 135 | // the printer with the nodes of the section. Dynamic lookup can 136 | // still find the section tag for proper display within the 137 | // section. 138 | apply_visitor_to_root(*this, v.nodes); 139 | } 140 | } 141 | else if( !positive && v.is_inverted ) 142 | { 143 | apply_visitor_to_root(stache_model_printer(out, stache_model(), this), v.nodes); 144 | } 145 | } 146 | 147 | private: 148 | template 149 | const T* get(const stache_variant* location) const 150 | { 151 | return boost::get(location); 152 | } 153 | 154 | bool have_value(const stache_variant* location) const 155 | { 156 | if( location != nullptr ) 157 | { 158 | if( auto f = get(location) ) 159 | { 160 | return (*f)(); 161 | } 162 | return true; 163 | } 164 | return false; 165 | } 166 | 167 | // Recursive lookup to any parent printers to support scoped name lookup. 168 | const stache_variant* lookup(const std::string& name) const 169 | { 170 | auto location = model.find(name); 171 | if( location != model.end() ) 172 | { 173 | return &(location->second); 174 | } 175 | else if( parent ) 176 | { 177 | return parent->lookup(name); 178 | } 179 | return nullptr; 180 | } 181 | 182 | std::ostream& out; 183 | const stache_model& model; 184 | const stache_model_printer* parent; 185 | }; 186 | } 187 | 188 | inline void print( std::ostream& out 189 | , frontend::stache::ast::root const & root 190 | , stache_model const & model) 191 | { 192 | detail::stache_model_printer p(out, model); 193 | apply_visitor_to_root(p, root); 194 | } 195 | }}}} 196 | 197 | #endif 198 | -------------------------------------------------------------------------------- /include/boost/boostache/model/test_traits.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file test_traits.hpp 3 | * 4 | * Copyright 2014, 2015 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_MODEL_TEST_TRAITS_HPP 10 | #define BOOST_BOOSTACHE_MODEL_TEST_TRAITS_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | namespace boost { namespace boostache { namespace extension 22 | { 23 | // ------------------------------------------- 24 | // test category 25 | template 26 | struct test_category 27 | : mpl::identity {}; 28 | 29 | template 30 | struct test_category< T 31 | , vm::trait::enable_if_is_variant_t 32 | > 33 | : mpl::identity {}; 34 | 35 | template <> 36 | struct test_category 37 | : mpl::identity {}; 38 | 39 | template 40 | struct test_category< T 41 | , vm::trait::enable_if_sequence_not_map_t 42 | > 43 | : mpl::identity {}; 44 | 45 | // template 46 | // struct test_category 48 | // : mpl::identity {}; 49 | 50 | template 51 | struct test_category> 52 | : mpl::identity {}; 53 | 54 | template 55 | struct test_category> 56 | : mpl::identity {}; 57 | 58 | template 59 | struct test_category> 60 | : mpl::identity {}; 61 | 62 | template 63 | using test_category_t = typename test_category::type; 64 | }}} 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /include/boost/boostache/model/unwrap_variant.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file unwrap_variant.hpp 3 | * 4 | * When the model node is a variant, we need to unwrap it and dispatch 5 | * to the contained data type. This is a helper to do that for us. 6 | * 7 | * Copyright 2014 Michael Caisse : ciere.com 8 | * 9 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 10 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 11 | */ 12 | #ifndef BOOST_BOOSTACHE_MODEL_UNWRAP_VARIANT_HPP 13 | #define BOOST_BOOSTACHE_MODEL_UNWRAP_VARIANT_HPP 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | namespace boost { namespace boostache { namespace extension 21 | { 22 | template 23 | bool test( T const & context, std::string const & tag 24 | , variant_attribute) 25 | { 26 | return boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( 27 | [&tag](auto ctx) 28 | { 29 | return test(ctx, tag); 30 | } 31 | ) 32 | , context); 33 | } 34 | 35 | 36 | template 37 | bool test( T const & context 38 | , variant_attribute) 39 | { 40 | return boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( 41 | [](auto ctx) 42 | { 43 | return test(ctx); 44 | } 45 | ) 46 | , context); 47 | } 48 | 49 | 50 | template< typename Stream, typename T > 51 | void render( Stream & stream, T const & context, std::string const & name 52 | , variant_attribute) 53 | { 54 | return boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( 55 | [&stream,&name](auto ctx) 56 | { 57 | render(stream,ctx,name); 58 | } 59 | ) 60 | , context); 61 | } 62 | 63 | }}} 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /include/boost/boostache/stache.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file stache.hpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * 6 | * 7 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | */ 10 | #ifndef BOOST_BOOSTACHE_FRONT_END_STACHE_HPP 11 | #define BOOST_BOOSTACHE_FRONT_END_STACHE_HPP 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace boost { namespace boostache { namespace format 18 | { 19 | struct stache 20 | { 21 | template 22 | using grammar_t = frontend::stache::grammar; 23 | 24 | using ast_t = frontend::stache::ast::root; 25 | using skipper_t = boost::spirit::qi::space_type; 26 | }; 27 | }}} 28 | 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/boost/boostache/vm/detail/engine_visitor.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file detail/engine_visitor.hpp 3 | * 4 | * Copyright 2014, 2015 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_VM_DETAIL_ENGINE_VISITOR_HPP 10 | #define BOOST_BOOSTACHE_VM_DETAIL_ENGINE_VISITOR_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | ////////// some test hackery //////////////////////// 19 | 20 | namespace boost { namespace boostache { namespace extension 21 | { 22 | template 23 | void render(Stream & stream, Object const & v) 24 | { 25 | stream << v; 26 | } 27 | 28 | template 29 | bool test(T const & context, std::string const & name); 30 | 31 | template 32 | bool test(T const & context); 33 | 34 | template< typename Stream, typename T > 35 | void render(Stream & stream, T const & context, std::string const & name); 36 | }}} 37 | 38 | ///////////////////////////////////////////////////// 39 | 40 | 41 | namespace boost { namespace boostache { namespace vm { namespace detail 42 | { 43 | template 44 | class engine_visitor_base 45 | { 46 | public: 47 | typedef void result_type; 48 | 49 | engine_visitor_base(Stream & s, Context const & c) 50 | : stream(s) 51 | , context(c) 52 | {} 53 | 54 | void operator()(ast::undefined) const 55 | {} 56 | 57 | void operator()(ast::nop) const 58 | {} 59 | 60 | void operator()(ast::literal const & lit) const 61 | { 62 | using boost::boostache::extension::render; 63 | render(stream, lit.value); 64 | } 65 | 66 | void operator()(ast::variable const &) const 67 | {} 68 | 69 | void operator()(ast::render const & r) const 70 | { 71 | using boost::boostache::extension::render; 72 | render(stream, context, r.name); 73 | } 74 | 75 | void operator()(ast::for_each const & v) const 76 | { 77 | using boost::boostache::vm::detail::foreach; 78 | foreach(stream, v, context); 79 | } 80 | 81 | void operator()(ast::if_then_else const & v) const 82 | { 83 | using boost::boostache::extension::test; 84 | if(test(context, v.condition_.name)) 85 | { 86 | boost::apply_visitor(*this, v.then_); 87 | } 88 | else 89 | { 90 | boost::apply_visitor(*this, v.else_); 91 | } 92 | } 93 | 94 | void operator()(ast::select_context const & select_ctx) const 95 | { 96 | select_context_dispatch( stream, select_ctx, context 97 | , extension::select_category_t{} ); 98 | } 99 | 100 | void operator()(ast::node_list const & nodes) const 101 | { 102 | for(auto const & node : nodes.nodes) 103 | { 104 | boost::apply_visitor(*this, node); 105 | } 106 | } 107 | 108 | void operator()(ast::node const & node) const 109 | { 110 | boost::apply_visitor(*this, node); 111 | } 112 | 113 | private: 114 | Stream & stream; 115 | Context const & context; 116 | }; 117 | 118 | 119 | template 120 | void generate( Stream & stream 121 | , Template const & templ 122 | , Context const & ctx) 123 | { 124 | engine_visitor_base engine(stream, ctx); 125 | engine(templ); 126 | } 127 | 128 | }}}} 129 | 130 | #endif 131 | -------------------------------------------------------------------------------- /include/boost/boostache/vm/detail/foreach.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file detail/foreach.hpp 3 | * 4 | * Copyright 2014, 2015 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_VM_DETAIL_FOREACH_HPP 10 | #define BOOST_BOOSTACHE_VM_DETAIL_FOREACH_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | 24 | namespace boost { namespace boostache { namespace vm 25 | { 26 | template 27 | void generate( Stream & stream 28 | , Template const & templ 29 | , Context const & context); 30 | 31 | namespace detail 32 | { 33 | template 34 | void foreach(Stream & stream, Node const & node, Context const & context); 35 | } 36 | }}} 37 | 38 | 39 | namespace boost { namespace boostache 40 | { 41 | template < typename T 42 | , typename Enable = void> 43 | struct supports_foreach : boost::mpl::identity 44 | {}; 45 | }} 46 | 47 | 48 | namespace boost { namespace boostache { namespace extension 49 | { 50 | // ------------------------------------------- 51 | // foreach category 52 | template 53 | struct foreach_category 54 | : mpl::identity {}; 55 | 56 | template <> 57 | struct foreach_category 58 | : mpl::identity {}; 59 | 60 | template 61 | struct foreach_category< T 62 | , vm::trait::enable_if_is_variant_t 63 | > 64 | : mpl::identity {}; 65 | 66 | template 67 | struct foreach_category< T 68 | , vm::trait::enable_if_sequence_not_map_t 69 | > 70 | : mpl::identity {}; 71 | 72 | template 73 | struct foreach_category> 74 | : mpl::identity {}; 75 | 76 | template 77 | struct foreach_category> 78 | : mpl::identity {}; 79 | 80 | template 81 | struct foreach_category> 82 | : mpl::identity {}; 83 | 84 | template 85 | using foreach_category_t = typename foreach_category::type; 86 | 87 | }}} 88 | 89 | 90 | namespace boost { namespace boostache { namespace vm { namespace detail 91 | { 92 | template 93 | void foreach( Stream & stream 94 | , Node const & node 95 | , Context const & context 96 | , Category) 97 | { 98 | generate(stream, node.value, context); 99 | } 100 | 101 | 102 | template 103 | void foreach( Stream & stream 104 | , Node const & node 105 | , Context const & context 106 | , extension::variant_attribute) 107 | { 108 | boost::apply_visitor( boostache::detail::make_unwrap_variant_visitor( 109 | [&stream,&node](auto ctx) 110 | { 111 | vm::detail::foreach(stream, node, ctx); 112 | } 113 | ) 114 | , context); 115 | } 116 | 117 | 118 | template 119 | void foreach( Stream & stream 120 | , Node const & node 121 | , Context const & context 122 | , extension::sequence_attribute) 123 | { 124 | for(auto const & item : context) 125 | { 126 | generate(stream, node.value, item); 127 | } 128 | } 129 | 130 | 131 | template 132 | void foreach( Stream & stream 133 | , Node const & node 134 | , Context const & ctx 135 | , extension::optional_attribute) 136 | { 137 | if(ctx) 138 | { 139 | foreach( stream, node, *ctx 140 | , extension::foreach_category_t{}); 141 | } 142 | else 143 | { 144 | generate(stream, node.value, ctx); 145 | } 146 | } 147 | 148 | 149 | /** 150 | * Entry point for foreach 151 | */ 152 | template 153 | void foreach(Stream & stream, Node const & node, Context const & context) 154 | { 155 | using boostache::vm::detail::foreach; 156 | foreach( stream 157 | , node 158 | , context 159 | , extension::foreach_category_t{}); 160 | } 161 | 162 | }}}} 163 | 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /include/boost/boostache/vm/detail/select_context.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file detail/select_context.hpp 3 | * 4 | * Copyright 2015 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_VM_DETAIL_SELECT_CONTEXT_HPP 10 | #define BOOST_BOOSTACHE_VM_DETAIL_SELECT_CONTEXT_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | namespace boost { namespace boostache { namespace vm { namespace detail 18 | { 19 | template < typename Stream, typename Template 20 | , typename Context1, typename Context2 21 | , typename CategoryChild 22 | > 23 | void select_context( Stream & stream, Template const & templ 24 | , Context1 const & ctx_parent 25 | , Context2 const & /*ctx_child*/ 26 | , CategoryChild) 27 | { 28 | generate(stream, templ, ctx_parent); 29 | } 30 | 31 | 32 | template < typename Stream, typename Template 33 | , typename Context1, typename Context2 34 | > 35 | void select_context( Stream & stream, Template const & templ 36 | , Context1 const & /*ctx_parent*/ 37 | , Context2 const & ctx_child 38 | , extension::associative_attribute) 39 | { 40 | generate(stream, templ, ctx_child); 41 | } 42 | 43 | 44 | template < typename Stream, typename Template 45 | , typename Context1, typename Context2 46 | > 47 | void select_context( Stream & stream, Template const & templ 48 | , Context1 const & /*ctx_parent*/ 49 | , Context2 const & ctx_child 50 | , extension::sequence_attribute) 51 | { 52 | generate(stream, templ, ctx_child); 53 | } 54 | 55 | 56 | template < typename Stream, typename Template 57 | , typename Context1, typename Context2 58 | > 59 | void select_context( Stream & stream, Template const & templ 60 | , Context1 const & ctx_parent 61 | , Context2 const & ctx_child 62 | , extension::variant_attribute) 63 | { 64 | boost::apply_visitor( 65 | boostache::detail::make_unwrap_variant_visitor( 66 | [&stream,&templ,&ctx_parent](auto ctx) 67 | { 68 | select_context( stream, templ, ctx_parent, ctx 69 | , extension::select_category_t{}); 70 | }) 71 | , ctx_child); 72 | } 73 | 74 | 75 | template 76 | void select_context_dispatch( Stream & stream 77 | , ast::select_context const & templ 78 | , Context const & ctx, Category) 79 | { 80 | generate(stream, templ.body, ctx); 81 | } 82 | 83 | 84 | template 85 | void select_context_dispatch( Stream & stream 86 | , ast::select_context const & templ 87 | , Context const & ctx 88 | , extension::variant_attribute) 89 | { 90 | boost::apply_visitor( 91 | boostache::detail::make_unwrap_variant_visitor( 92 | [&stream,&templ](auto ctx) 93 | { 94 | select_context_dispatch( stream, templ, ctx 95 | , extension::select_category_t{}); 96 | }) 97 | , ctx); 98 | } 99 | 100 | 101 | template 102 | void select_context_dispatch( Stream & stream 103 | , ast::select_context const & templ 104 | , Context const & ctx 105 | , extension::associative_attribute) 106 | { 107 | auto iter = ctx.find(templ.tag); 108 | if(iter != ctx.end()) 109 | { 110 | // std::cout << "select_context ctx_parent " 111 | // << typeid(ctx).name() 112 | // << " ctx_child " 113 | // << typeid(iter->second).name() 114 | // << " category " 115 | // << typeid(extension::select_category_tsecond)>{}).name(); 116 | 117 | select_context( stream, templ.body, ctx, iter->second 118 | , extension::select_category_tsecond)>{}); 119 | } 120 | else 121 | { 122 | generate(stream, templ.body, ctx); 123 | } 124 | } 125 | // ------------------------------------------------------------------ 126 | // ------------------------------------------------------------------ 127 | 128 | }}}} 129 | 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /include/boost/boostache/vm/engine_ast.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file engine_ast.hpp 3 | * 4 | * Copyright 2014, 2015 Michael Caisse : ciere.com 5 | * 6 | * 7 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | */ 10 | #ifndef BOOST_BOOSTACHE_VM_ENGINE_AST_HPP 11 | #define BOOST_BOOSTACHE_VM_ENGINE_AST_HPP 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace boost { namespace boostache { namespace vm { namespace ast 18 | { 19 | struct literal; 20 | struct variable; 21 | struct for_each; 22 | struct if_then_else; 23 | struct select_context; 24 | struct node_list; 25 | 26 | struct undefined {}; 27 | 28 | struct nop {}; 29 | 30 | struct literal 31 | { 32 | literal(){} 33 | literal(std::string const & v) : value(v) {} 34 | std::string value; 35 | }; 36 | 37 | struct variable 38 | { 39 | variable(){} 40 | variable(std::string const & v) : name(v) {} 41 | std::string name; 42 | }; 43 | 44 | struct render 45 | { 46 | render(){} 47 | render(std::string const & v) : name(v) {} 48 | std::string name; 49 | }; 50 | 51 | struct node : boost::spirit::extended_variant< 52 | undefined 53 | , nop 54 | , literal 55 | , variable 56 | , render 57 | , boost::recursive_wrapper 58 | , boost::recursive_wrapper 59 | , boost::recursive_wrapper 60 | , boost::recursive_wrapper > 61 | { 62 | node() : base_type() {} 63 | node(nop const & rhs) : base_type(rhs) {} 64 | node(literal const & rhs) : base_type(rhs) {} 65 | node(variable const & rhs) : base_type(rhs) {} 66 | node(render const & rhs) : base_type(rhs) {} 67 | node(for_each const & rhs) : base_type(rhs) {} 68 | node(if_then_else const & rhs) : base_type(rhs) {} 69 | node(select_context const & rhs) : base_type(rhs) {} 70 | node(node_list const & rhs) : base_type(rhs) {} 71 | }; 72 | 73 | struct for_each 74 | { 75 | std::string name; 76 | node value; 77 | }; 78 | 79 | struct condition 80 | { 81 | // hack for now until proper conditionals 82 | std::string name; 83 | // lhs 84 | // rhs 85 | // op 86 | }; 87 | 88 | struct if_then_else 89 | { 90 | condition condition_; 91 | node then_; 92 | node else_; 93 | }; 94 | 95 | struct select_context 96 | { 97 | std::string tag; 98 | node body; 99 | }; 100 | 101 | struct node_list 102 | { 103 | std::vector nodes; 104 | }; 105 | 106 | }}}} 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /include/boost/boostache/vm/generate.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file generate.hpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_VM_GENERATE_HPP 10 | #define BOOST_BOOSTACHE_VM_GENERATE_HPP 11 | 12 | #include 13 | 14 | namespace boost { namespace boostache { namespace vm 15 | { 16 | template 17 | void generate( Stream & stream 18 | , Template const & templ 19 | , Context const & context) 20 | { 21 | vm::detail::generate(stream, templ, context); 22 | } 23 | }}} 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/boost/boostache/vm/printer.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file vm/printer.hpp 3 | * 4 | * Copyright 2014, 2015 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_VM_PRINTER_HPP 10 | #define BOOST_BOOSTACHE_VM_PRINTER_HPP 11 | 12 | #include 13 | #include 14 | 15 | namespace boost { namespace boostache { namespace vm { namespace ast 16 | { 17 | namespace detail 18 | { 19 | class printer 20 | { 21 | public: 22 | typedef void result_type; 23 | 24 | printer(std::ostream& out) 25 | : out(out) 26 | {} 27 | 28 | void operator()(undefined) const 29 | { 30 | out << "[]"; 31 | } 32 | 33 | void operator()(nop) const 34 | { 35 | out << "[]"; 36 | } 37 | 38 | void operator()(literal const & v) const 39 | { 40 | out << "[ : " << v.value << "]"; 41 | } 42 | 43 | void operator()(variable const & v) const 44 | { 45 | out << "[ : " << v.name << "]"; 46 | } 47 | 48 | void operator()(render const & v) const 49 | { 50 | out << "[ : " << v.name << "]"; 51 | } 52 | 53 | void operator()(for_each const & v) const 54 | { 55 | out << "[ :" << std::endl; 56 | boost::apply_visitor(*this, v.value); 57 | out << "\n]" << std::endl; 58 | } 59 | 60 | void operator()(condition const & v) const 61 | {} 62 | 63 | void operator()(select_context const & v) const 64 | { 65 | out << "[ [" << v.tag << "] :" << std::endl; 66 | boost::apply_visitor(*this, v.body); 67 | out << "\n]" << std::endl; 68 | } 69 | 70 | void operator()(if_then_else const & v) const 71 | { 72 | out << "[ : --------------------------- " << std::endl; 73 | //boost::apply_visitor(*this, v.condition_); 74 | out << "[ : --------------------------- " << std::endl; 75 | boost::apply_visitor(*this, v.then_); 76 | out << "\n]\n"; 77 | out << "[ : --------------------------- " << std::endl; 78 | boost::apply_visitor(*this, v.else_); 79 | out << "\n]" << std::endl; 80 | } 81 | 82 | void operator()(node_list const & v) const 83 | { 84 | for(auto const & node : v.nodes) 85 | { 86 | boost::apply_visitor(*this, node); 87 | } 88 | } 89 | 90 | private: 91 | std::ostream& out; 92 | }; 93 | } 94 | 95 | inline void print(std::ostream& out, node const& root) 96 | { 97 | detail::printer p(out); 98 | boost::apply_visitor(p, root); 99 | } 100 | }}}} 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /include/boost/boostache/vm/traits.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file traits.hpp 3 | * 4 | * Copyright 2014 - 2016 Michael Caisse : ciere.com 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | #ifndef BOOST_BOOSTACHE_VM_TRAITS_HPP 10 | #define BOOST_BOOSTACHE_VM_TRAITS_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | namespace boost { namespace boostache { namespace vm { namespace trait 20 | { 21 | // ---------------------------------------------------------------- 22 | // variant traits 23 | // ---------------------------------------------------------------- 24 | namespace detail 25 | { 26 | // this trait is enabled for the Spirit extended variant 27 | BOOST_MPL_HAS_XXX_TRAIT_DEF(adapted_variant_tag) 28 | } 29 | 30 | template 31 | struct is_variant 32 | : detail::has_adapted_variant_tag 33 | {}; 34 | 35 | template 36 | struct is_variant< boost::variant > 37 | : std::true_type 38 | {}; 39 | 40 | template 41 | using enable_if_is_variant_t = typename std::enable_if::value>::type; 42 | // ---------------------------------------------------------------- 43 | // ---------------------------------------------------------------- 44 | 45 | 46 | // ---------------------------------------------------------------- 47 | // begin traits 48 | // ---------------------------------------------------------------- 49 | using std::begin; 50 | template< class T 51 | , class = decltype(begin(std::declval())) 52 | > 53 | std::true_type has_begin_test(const T&); 54 | 55 | std::false_type has_begin_test(...); 56 | 57 | template 58 | struct has_begin 59 | { 60 | using type = decltype(has_begin_test(std::declval())); 61 | }; 62 | 63 | template 64 | using has_begin_t = typename has_begin::type; 65 | 66 | 67 | // ---------------------------------------------------------------- 68 | // map traits 69 | // ---------------------------------------------------------------- 70 | template 71 | struct is_map 72 | : std::false_type 73 | {}; 74 | 75 | template 76 | struct is_map> 77 | : std::true_type 78 | {}; 79 | 80 | template 81 | struct is_map> 82 | : std::true_type 83 | {}; 84 | 85 | template 86 | using is_map_t = typename is_map::type; 87 | 88 | template 89 | using enable_if_sequence_not_map_t = 90 | typename std::enable_if< 91 | std::integral_constant< bool 92 | , vm::trait::has_begin_t::value 93 | & !vm::trait::is_map::value >::value 94 | >::type; 95 | // ---------------------------------------------------------------- 96 | // ---------------------------------------------------------------- 97 | 98 | }}}} 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | test_change_delimiter 2 | test_collections 3 | test_liaw 4 | test_sections 5 | test_simple 6 | test_multiple 7 | test_nested_sections 8 | test_partials 9 | test_nested_sections 10 | test_html_escape 11 | test_inverted_sections 12 | test_simple_parser 13 | test_model 14 | test_dynamic_model 15 | test_mustache_demo 16 | test_json_model 17 | test_default_model 18 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | macro(add_boost_test name source) 4 | add_executable( ${ARGV} ) 5 | 6 | # compilation setup 7 | set_source_files_properties( ${source} PROPERTIES COMPILE_DEFINITIONS BOOST_TEST_DYN_LINK ) 8 | 9 | # link setup 10 | target_link_libraries(${name} ${Boost_LIBRARIES}) 11 | 12 | # unit test configuration 13 | add_test(NAME ${name} COMMAND ${name}) 14 | set_tests_properties( ${name} PROPERTIES WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/.. ) 15 | endmacro(add_boost_test) 16 | 17 | add_subdirectory(vm) 18 | add_subdirectory(frontend) 19 | add_subdirectory(model) 20 | add_subdirectory(mustache) 21 | -------------------------------------------------------------------------------- /test/Jamfile: -------------------------------------------------------------------------------- 1 | #============================================================================== 2 | # Copyright (c) 2014 Jeff Garland 3 | # Copyright (c) 2014, 2015 Michael Caisse 4 | # 5 | # Use, modification and distribution is subject to the Boost Software 6 | # License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 7 | # http://www.boost.org/LICENSE_1_0.txt) 8 | #============================================================================== 9 | 10 | # bring in rules for testing 11 | import testing ; 12 | import modules ; 13 | import feature ; 14 | 15 | ############################################################################### 16 | project boostache_test 17 | : requirements 18 | . 19 | /boost//unit_test_framework 20 | /boost//system 21 | /boost//filesystem 22 | BOOST_TEST_DYN_LINK 23 | ; 24 | 25 | 26 | { 27 | test-suite vm : 28 | [ run vm/foreach.cpp ] 29 | ; 30 | 31 | test-suite boostache/frontend : 32 | [ run frontend/grammar_basic.cpp ] 33 | ; 34 | 35 | test-suite boostache/model : 36 | [ run model/traits.cpp ] 37 | [ run model/render_traits.cpp ] 38 | # [ run model/unwrap_variant.cpp ] 39 | ; 40 | 41 | test-suite boostache/mustache : 42 | [ run mustache/mustache_parser.cpp 43 | shared/parser_test.cpp 44 | ] 45 | [ run mustache/mustache_compiler.cpp 46 | shared/parser_test.cpp 47 | ] 48 | [ run mustache/mustache_end2end.cpp 49 | shared/parser_test.cpp 50 | : --log_level=message 51 | ] 52 | ; 53 | } 54 | -------------------------------------------------------------------------------- /test/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | adapt_test 2 | grammar_basic 3 | compiler_basic 4 | -------------------------------------------------------------------------------- /test/frontend/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_boost_test(adapt_test adapt_test.cpp) 2 | add_boost_test(grammar_basic grammar_basic.cpp) 3 | -------------------------------------------------------------------------------- /test/frontend/Jamfile: -------------------------------------------------------------------------------- 1 | #============================================================================== 2 | # Copyright (c) 2014 Michael Caisse 3 | # 4 | # Use, modification and distribution is subject to the Boost Software 5 | # License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 6 | # http://www.boost.org/LICENSE_1_0.txt) 7 | #============================================================================== 8 | 9 | 10 | # bring in rules for testing 11 | import testing ; 12 | import modules ; 13 | import feature ; 14 | 15 | ############################################################################### 16 | project boostache_test_frontend 17 | : requirements 18 | . 19 | ; 20 | 21 | 22 | { 23 | test-suite boostache/frontend : 24 | 25 | [ run grammar_basic.cpp ] 26 | [ run compiler_basic.cpp ] 27 | 28 | ; 29 | } -------------------------------------------------------------------------------- /test/frontend/adapt_test.cpp: -------------------------------------------------------------------------------- 1 | // file used during initial dev 2 | // will be removed 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace boostache = boost::boostache; 9 | using boostache::frontend::parse; 10 | 11 | 12 | int main() 13 | { 14 | std::string input("foo"); 15 | auto iter = input.begin(); 16 | auto ast = parse(iter,input.end()); 17 | } 18 | -------------------------------------------------------------------------------- /test/frontend/grammar_basic.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file frontend/grammar_basic.cpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * 6 | * Basic stache gramar test 7 | * 8 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 9 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | */ 11 | #define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | #define BOOST_TEST_MAIN 22 | #include 23 | 24 | namespace boostache = boost::boostache; 25 | namespace fe = boostache::frontend; 26 | using boostache::frontend::parse; 27 | 28 | BOOST_AUTO_TEST_CASE(stache_parse_test) 29 | { 30 | std::string input( "Hello world \n" 31 | "{{name}} is here.\n" 32 | "{{& escaped_name}} is here\n" 33 | "{{ # foo}}\n" 34 | "Some cool section {{whoot}} is {{foo}} {{bar}} here.\n" 35 | "{{/foo}} done.\n" 36 | "{{! no comment }}" 37 | "{{^ bar}}\n" 38 | "Some cool empty section {{{ whoot }}} is here.\n" 39 | "{{ /bar}} done.\n" 40 | "{{> partial}}\n" 41 | ); 42 | 43 | auto iter = input.begin(); 44 | auto ast = parse(iter,input.end()); 45 | 46 | // we expect everything got parsed 47 | BOOST_CHECK(iter==input.end()); 48 | 49 | std::stringstream stream; 50 | fe::stache::ast::print(stream, ast); 51 | 52 | std::string expected( "Hello world \n" 53 | "{{name}} is here.\n" 54 | "{{&escaped_name}} is here\n" 55 | "{{#foo}}\n" 56 | "Some cool section {{whoot}} is {{foo}} {{bar}} here.\n" 57 | "{{/foo}} done.\n" 58 | "{{^bar}}\n" 59 | "Some cool empty section {{&whoot}} is here.\n" 60 | "{{/bar}} done.\n" 61 | "{{>partial}}\n" 62 | ); 63 | 64 | auto input_ast = stream.str(); 65 | auto diff_iters = std::mismatch(input_ast.begin(), input_ast.end(), 66 | expected.begin(), expected.end()); 67 | 68 | if(std::get<0>(diff_iters) != input_ast.end()) 69 | { 70 | auto iter = std::get<0>(diff_iters); 71 | std::cout << "generated difference at:\n" 72 | << " offset: " << std::distance(input_ast.begin(), iter) << "\n" 73 | << " value: " << *iter << "\n" 74 | << " value: 0x" << std::hex << int(*iter) << std::dec << "\n"; 75 | } 76 | 77 | if(std::get<1>(diff_iters) != expected.end()) 78 | { 79 | auto iter = std::get<1>(diff_iters); 80 | std::cout << "expected difference at:\n" 81 | << " offset: " << std::distance(expected.begin(), iter) << "\n" 82 | << " value: " << *iter << "\n" 83 | << " value: 0x" << std::hex << int(*iter) << std::dec << "\n"; 84 | } 85 | 86 | BOOST_CHECK_EQUAL(input_ast,expected); 87 | } 88 | 89 | -------------------------------------------------------------------------------- /test/model/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_boost_test( traits traits.cpp ) 2 | add_boost_test( render_traits render_traits.cpp ) 3 | -------------------------------------------------------------------------------- /test/model/render_traits.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file render_traits.cpp 3 | * 4 | * Copyright 2015 Michael Caisse : ciere.com 5 | * 6 | * Tests for render traits to make sure we get the correct category out 7 | * 8 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 9 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | */ 11 | 12 | // #include 13 | // #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #define BOOST_TEST_MAIN 21 | #include 22 | 23 | namespace boostache = boost::boostache; 24 | 25 | 26 | // ---------------------------------------------------------------------- 27 | // attribute testing 28 | // ---------------------------------------------------------------------- 29 | template 30 | constexpr bool is_plain_attribute(T const &) 31 | { 32 | return false; 33 | } 34 | 35 | constexpr bool is_plain_attribute(boostache::extension::plain_attribute const &) 36 | { 37 | return true; 38 | } 39 | 40 | template 41 | constexpr bool is_variant_attribute(T const &) 42 | { 43 | return false; 44 | } 45 | 46 | constexpr bool is_variant_attribute(boostache::extension::variant_attribute const &) 47 | { 48 | return true; 49 | } 50 | 51 | template 52 | constexpr bool is_optional_attribute(T const &) 53 | { 54 | return false; 55 | } 56 | 57 | constexpr bool is_optional_attribute(boostache::extension::optional_attribute const &) 58 | { 59 | return true; 60 | } 61 | 62 | template 63 | constexpr bool is_associative_attribute(T const &) 64 | { 65 | return false; 66 | } 67 | 68 | constexpr bool is_associative_attribute(boostache::extension::associative_attribute const &) 69 | { 70 | return true; 71 | } 72 | 73 | template 74 | constexpr bool is_sequence_attribute(T const &) 75 | { 76 | return false; 77 | } 78 | 79 | constexpr bool is_sequence_attribute(boostache::extension::sequence_attribute const &) 80 | { 81 | return true; 82 | } 83 | // ---------------------------------------------------------------------- 84 | // ---------------------------------------------------------------------- 85 | 86 | 87 | 88 | struct foo 89 | {}; 90 | 91 | using map1_t = std::map; 92 | using map2_t = std::map; 93 | using map3_t = std::unordered_map; 94 | using map4_t = std::unordered_map; 95 | 96 | 97 | 98 | 99 | BOOST_AUTO_TEST_CASE(map1_render_trait) 100 | { 101 | BOOST_CHECK( 102 | ! is_plain_attribute( 103 | boostache::extension::render_category::type{} ) 104 | ); 105 | 106 | BOOST_CHECK( 107 | ! is_variant_attribute( 108 | boostache::extension::render_category::type{} ) 109 | ); 110 | 111 | BOOST_CHECK( 112 | ! is_optional_attribute( 113 | boostache::extension::render_category::type{} ) 114 | ); 115 | 116 | BOOST_CHECK( 117 | is_associative_attribute( 118 | boostache::extension::render_category::type{} ) 119 | ); 120 | 121 | BOOST_CHECK( 122 | ! is_sequence_attribute( 123 | boostache::extension::render_category::type{} ) 124 | ); 125 | } 126 | 127 | BOOST_AUTO_TEST_CASE(map2_render_trait) 128 | { 129 | BOOST_CHECK( 130 | ! is_plain_attribute( 131 | boostache::extension::render_category::type{} ) 132 | ); 133 | 134 | BOOST_CHECK( 135 | ! is_variant_attribute( 136 | boostache::extension::render_category::type{} ) 137 | ); 138 | 139 | BOOST_CHECK( 140 | ! is_optional_attribute( 141 | boostache::extension::render_category::type{} ) 142 | ); 143 | 144 | BOOST_CHECK( 145 | is_associative_attribute( 146 | boostache::extension::render_category::type{} ) 147 | ); 148 | 149 | BOOST_CHECK( 150 | ! is_sequence_attribute( 151 | boostache::extension::render_category::type{} ) 152 | ); 153 | } 154 | 155 | 156 | BOOST_AUTO_TEST_CASE(map3_render_trait) 157 | { 158 | BOOST_CHECK( 159 | ! is_plain_attribute( 160 | boostache::extension::render_category::type{} ) 161 | ); 162 | 163 | BOOST_CHECK( 164 | ! is_variant_attribute( 165 | boostache::extension::render_category::type{} ) 166 | ); 167 | 168 | BOOST_CHECK( 169 | ! is_optional_attribute( 170 | boostache::extension::render_category::type{} ) 171 | ); 172 | 173 | BOOST_CHECK( 174 | is_associative_attribute( 175 | boostache::extension::render_category::type{} ) 176 | ); 177 | 178 | BOOST_CHECK( 179 | ! is_sequence_attribute( 180 | boostache::extension::render_category::type{} ) 181 | ); 182 | } 183 | 184 | 185 | BOOST_AUTO_TEST_CASE(map4_render_trait) 186 | { 187 | BOOST_CHECK( 188 | ! is_plain_attribute( 189 | boostache::extension::render_category::type{} ) 190 | ); 191 | 192 | BOOST_CHECK( 193 | ! is_variant_attribute( 194 | boostache::extension::render_category::type{} ) 195 | ); 196 | 197 | BOOST_CHECK( 198 | ! is_optional_attribute( 199 | boostache::extension::render_category::type{} ) 200 | ); 201 | 202 | BOOST_CHECK( 203 | is_associative_attribute( 204 | boostache::extension::render_category::type{} ) 205 | ); 206 | 207 | BOOST_CHECK( 208 | ! is_sequence_attribute( 209 | boostache::extension::render_category::type{} ) 210 | ); 211 | } 212 | -------------------------------------------------------------------------------- /test/model/simple_parser.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _SIMPLE_PARSER_HPP_ 2 | #define _SIMPLE_PARSER_HPP_ 0x100 3 | 4 | #include 5 | #include 6 | 7 | namespace boost { namespace boostache 8 | { 9 | namespace frontend 10 | { 11 | bool simple_parse(std::istream& input, stache::ast::root& ast); 12 | bool simple_parse(const std::string& input, stache::ast::root& ast); 13 | } 14 | 15 | using boostache::frontend::stache::ast::root; 16 | 17 | template 18 | bool simple_parse_template(InputType&& input, root& ast) 19 | { 20 | return boostache::frontend::simple_parse(std::forward(input), ast); 21 | } 22 | }} 23 | 24 | #endif // _SIMPLE_PARSER_HPP_ 25 | -------------------------------------------------------------------------------- /test/model/test_default_model.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file test_default_model.cpp 3 | * 4 | * Copyright 2014 Michal Bukovsky 5 | * 6 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | */ 9 | 10 | #define BOOST_TEST_MAIN 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "test_utils.hpp" 18 | 19 | BOOST_AUTO_TEST_CASE(test_default_simple_value) 20 | { 21 | // prepare model 22 | namespace bfe = boost::boostache::frontend; 23 | bfe::stache_model model; 24 | model["NAME"] = std::string("Muchomurka"); 25 | 26 | // parse template 27 | bfe::ast::stache_root ast = parse( 28 | "Amanita in czech is {{NAME}}"); 29 | 30 | // render and check 31 | BOOST_CHECK_EQUAL( 32 | "Amanita in czech is Muchomurka", 33 | print(ast, model)); 34 | } 35 | 36 | // TODO(burlog): if we add int into variant... 37 | //BOOST_AUTO_TEST_CASE(test_simple_int_value) 38 | //{ 39 | // // prepare model 40 | // namespace bpt = boost::property_tree; 41 | // namespace bfe = boost::boostache::frontend; 42 | // bpt::ptree model; 43 | // model.put("NUMBER", 3); 44 | // 45 | // // parse template 46 | // bfe::ast::stache_root ast = parse( 47 | // "{{NUMBER}} is three"); 48 | // 49 | // // render and check 50 | // BOOST_CHECK_EQUAL( 51 | // "3 is three", 52 | // print(ast, model)); 53 | //} 54 | 55 | BOOST_AUTO_TEST_CASE(test_parent_variable) 56 | { 57 | // prepare model 58 | namespace bfe = boost::boostache::frontend; 59 | bfe::stache_model model; 60 | model["EXCLAMATION"] = std::string("!"); 61 | 62 | bfe::stache_model mushroom; 63 | mushroom["NAME"] = std::string("Muchomurka Zelena"); 64 | 65 | model["MUSHROOMS"] = mushroom; 66 | 67 | // parse template 68 | bfe::ast::stache_root ast = parse( 69 | "{{#MUSHROOMS}}{{NAME}}{{EXCLAMATION}}\n{{/MUSHROOMS}}"); 70 | 71 | // render and check 72 | BOOST_CHECK_EQUAL( 73 | "Muchomurka Zelena!\n", 74 | print(ast, model)); 75 | } 76 | 77 | BOOST_AUTO_TEST_CASE(test_single_obj_as_section) 78 | { 79 | // prepare model 80 | namespace bfe = boost::boostache::frontend; 81 | bfe::stache_model model; 82 | 83 | bfe::stache_model mushroom; 84 | mushroom["NAME"] = std::string("Muchomurka Zelena"); 85 | 86 | model["MUSHROOMS"] = mushroom; 87 | 88 | // parse template 89 | bfe::ast::stache_root ast = parse( 90 | "{{#MUSHROOMS}}{{NAME}}\n{{/MUSHROOMS}}"); 91 | 92 | // render and check 93 | BOOST_CHECK_EQUAL( 94 | "Muchomurka Zelena\n", 95 | print(ast, model)); 96 | } 97 | 98 | BOOST_AUTO_TEST_CASE(test_array_as_section) 99 | { 100 | // prepare model 101 | namespace bfe = boost::boostache::frontend; 102 | bfe::stache_model model; 103 | 104 | bfe::stache_model_vector mushrooms; 105 | 106 | bfe::stache_model mushroom1; 107 | mushroom1["NAME"] = std::string("Muchomurka Zelena"); 108 | mushrooms.push_back(mushroom1); 109 | 110 | bfe::stache_model mushroom2; 111 | mushroom2["NAME"] = std::string("Hrib Hnedy"); 112 | mushrooms.push_back(mushroom2); 113 | 114 | model["MUSHROOMS"] = mushrooms; 115 | 116 | // parse template 117 | bfe::ast::stache_root ast = parse( 118 | "{{#MUSHROOMS}}{{NAME}}\n{{/MUSHROOMS}}"); 119 | 120 | // render and check 121 | BOOST_CHECK_EQUAL( 122 | "Muchomurka Zelena\n" 123 | "Hrib Hnedy\n", 124 | print(ast, model)); 125 | } 126 | 127 | BOOST_AUTO_TEST_CASE(test_single_obj_as_inv_section) 128 | { 129 | // prepare model 130 | namespace bfe = boost::boostache::frontend; 131 | bfe::stache_model model; 132 | 133 | // parse template 134 | bfe::ast::stache_root ast = parse( 135 | "{{#MUSHROOMS}}{{NAME}}\n{{/MUSHROOMS}}" 136 | "{{^MUSHROOMS}}No mushrooms\n{{/MUSHROOMS}}" 137 | ); 138 | 139 | // render and check 140 | BOOST_CHECK_EQUAL( 141 | "No mushrooms\n", 142 | print(ast, model)); 143 | } 144 | 145 | BOOST_AUTO_TEST_CASE(test_single_obj_as_inv_empty_section) 146 | { 147 | // prepare model 148 | namespace bfe = boost::boostache::frontend; 149 | bfe::stache_model model; 150 | bfe::stache_model_vector mushrooms; 151 | model["MUSHROOMS"] = mushrooms; 152 | 153 | // parse template 154 | bfe::ast::stache_root ast = parse( 155 | "{{#MUSHROOMS}}{{NAME}}\n{{/MUSHROOMS}}" 156 | "{{^MUSHROOMS}}No mushrooms\n{{/MUSHROOMS}}" 157 | ); 158 | 159 | // render and check 160 | BOOST_CHECK_EQUAL( 161 | "No mushrooms\n", 162 | print(ast, model)); 163 | } 164 | 165 | BOOST_AUTO_TEST_CASE(test_section_printing) 166 | { 167 | namespace bfe = boost::boostache::frontend; 168 | bfe::stache_model model; 169 | 170 | bfe::stache_model user; 171 | user["NAME"] = std::string("Bob"); 172 | user["LOCATION"] = std::string("Earth"); 173 | 174 | bfe::stache_model_vector favorites; 175 | 176 | bfe::stache_model favorite1; 177 | favorite1["FOOD"] = std::string("Pizza"); 178 | favorite1["MUSIC"] = std::string("Classical"); 179 | favorites.push_back(favorite1); 180 | 181 | bfe::stache_model favorite2; 182 | favorite2["FOOD"] = std::string("Knedlik"); 183 | favorite2["MUSIC"] = std::string("Folk"); 184 | favorites.push_back(favorite2); 185 | 186 | user["FAVORITES"] = favorites; 187 | model["USER"] = user; 188 | 189 | bfe::ast::stache_root ast = parse( 190 | "{{#USER}}" 191 | "user.name={{NAME}}\n" 192 | "user.location={{LOCATION}}\n" 193 | "{{#FAVORITES}}" 194 | "user.favorite.food={{FOOD}}\n" 195 | "user.favorite.music={{MUSIC}}\n" 196 | "{{/FAVORITES}}" 197 | "{{/USER}}"); 198 | 199 | BOOST_CHECK_EQUAL( 200 | "user.name=Bob\n" 201 | "user.location=Earth\n" 202 | "user.favorite.food=Pizza\n" 203 | "user.favorite.music=Classical\n" 204 | "user.favorite.food=Knedlik\n" 205 | "user.favorite.music=Folk\n", 206 | print(ast, model)); 207 | } 208 | 209 | -------------------------------------------------------------------------------- /test/model/test_ptree_model.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file test_json_model.cpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * Copyright 2014 Kevin Harris 6 | * Copyright 2014 Michal Bukovsky 7 | * 8 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 9 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | */ 11 | 12 | #define BOOST_TEST_MAIN 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "test_utils.hpp" 20 | 21 | BOOST_AUTO_TEST_CASE(test_json_simple_value) 22 | { 23 | // prepare model 24 | namespace bpt = boost::property_tree; 25 | namespace bfe = boost::boostache::frontend; 26 | bpt::ptree model; 27 | model.put("NAME", "Muchomurka"); 28 | 29 | // parse template 30 | bfe::stache::ast::root ast = parse( 31 | "Amanita in czech is {{NAME}}"); 32 | 33 | // render and check 34 | BOOST_CHECK_EQUAL( 35 | "Amanita in czech is Muchomurka", 36 | print(ast, model)); 37 | } 38 | 39 | BOOST_AUTO_TEST_CASE(test_json_simple_int_value) 40 | { 41 | // prepare model 42 | namespace bpt = boost::property_tree; 43 | namespace bfe = boost::boostache::frontend; 44 | bpt::ptree model; 45 | model.put("NUMBER", 3); 46 | 47 | // parse template 48 | bfe::stache::ast::root ast = parse( 49 | "{{NUMBER}} is three"); 50 | 51 | // render and check 52 | BOOST_CHECK_EQUAL( 53 | "3 is three", 54 | print(ast, model)); 55 | } 56 | 57 | BOOST_AUTO_TEST_CASE(test_json_parent_variable) 58 | { 59 | // prepare model 60 | namespace bpt = boost::property_tree; 61 | namespace bfe = boost::boostache::frontend; 62 | bpt::ptree model; 63 | model.put("EXCLAMATION", "!"); 64 | model.put("MUSHROOMS.NAME", "Muchomurka Zelena"); 65 | 66 | // parse template 67 | bfe::stache::ast::root ast = parse( 68 | "{{#MUSHROOMS}}{{NAME}}{{EXCLAMATION}}\n{{/MUSHROOMS}}"); 69 | 70 | // render and check 71 | BOOST_CHECK_EQUAL( 72 | "Muchomurka Zelena!\n", 73 | print(ast, model)); 74 | } 75 | 76 | BOOST_AUTO_TEST_CASE(test_json_single_obj_as_section) 77 | { 78 | // prepare model 79 | namespace bpt = boost::property_tree; 80 | namespace bfe = boost::boostache::frontend; 81 | bpt::ptree model; 82 | model.put("MUSHROOMS.NAME", "Muchomurka Zelena"); 83 | 84 | // parse template 85 | bfe::stache::ast::root ast = parse( 86 | "{{#MUSHROOMS}}{{NAME}}\n{{/MUSHROOMS}}"); 87 | 88 | // render and check 89 | BOOST_CHECK_EQUAL( 90 | "Muchomurka Zelena\n", 91 | print(ast, model)); 92 | } 93 | 94 | BOOST_AUTO_TEST_CASE(test_json_single_empty_obj_as_section) 95 | { 96 | // prepare model 97 | namespace bpt = boost::property_tree; 98 | namespace bfe = boost::boostache::frontend; 99 | bpt::ptree model; 100 | bpt::ptree empty; 101 | model.add_child("MUSHROOMS", empty); 102 | 103 | // parse template 104 | bfe::stache::ast::root ast = parse( 105 | "{{#MUSHROOMS}}{{NAME}}\n{{/MUSHROOMS}}"); 106 | 107 | // render and check 108 | BOOST_CHECK_EQUAL( 109 | "", // TODO(burlog): something better? 110 | print(ast, model)); 111 | } 112 | 113 | BOOST_AUTO_TEST_CASE(test_json_array_as_section) 114 | { 115 | // prepare model 116 | namespace bpt = boost::property_tree; 117 | namespace bfe = boost::boostache::frontend; 118 | bpt::ptree model; 119 | 120 | bpt::ptree mushrooms; 121 | 122 | bpt::ptree mushroom1; 123 | mushroom1.put("NAME", "Muchomurka Zelena"); 124 | mushrooms.push_back(std::make_pair("", mushroom1)); 125 | 126 | bpt::ptree mushroom2; 127 | mushroom2.put("NAME", "Hrib Hnedy"); 128 | mushrooms.push_back(std::make_pair("", mushroom2)); 129 | 130 | model.add_child("MUSHROOMS", mushrooms); 131 | 132 | // parse template 133 | bfe::stache::ast::root ast = parse( 134 | "{{#MUSHROOMS}}{{NAME}}\n{{/MUSHROOMS}}"); 135 | 136 | // render and check 137 | BOOST_CHECK_EQUAL( 138 | "Muchomurka Zelena\n" 139 | "Hrib Hnedy\n", 140 | print(ast, model)); 141 | } 142 | 143 | BOOST_AUTO_TEST_CASE(test_json_single_obj_as_inv_section) 144 | { 145 | // prepare model 146 | namespace bpt = boost::property_tree; 147 | namespace bfe = boost::boostache::frontend; 148 | bpt::ptree model; 149 | 150 | // parse template 151 | bfe::stache::ast::root ast = parse( 152 | "{{#MUSHROOMS}}{{NAME}}\n{{/MUSHROOMS}}" 153 | "{{^MUSHROOMS}}No mushrooms\n{{/MUSHROOMS}}" 154 | ); 155 | 156 | // render and check 157 | BOOST_CHECK_EQUAL( 158 | "No mushrooms\n", 159 | print(ast, model)); 160 | } 161 | 162 | BOOST_AUTO_TEST_CASE(test_json_single_obj_as_inv_empty_section) 163 | { 164 | // prepare model 165 | namespace bpt = boost::property_tree; 166 | namespace bfe = boost::boostache::frontend; 167 | bpt::ptree model; 168 | bpt::ptree empty; 169 | model.add_child("MUSHROOMS", empty); 170 | 171 | // parse template 172 | bfe::stache::ast::root ast = parse( 173 | "{{#MUSHROOMS}}{{NAME}}\n{{/MUSHROOMS}}" 174 | "{{^MUSHROOMS}}No mushrooms\n{{/MUSHROOMS}}" 175 | ); 176 | 177 | // render and check 178 | BOOST_CHECK_EQUAL( 179 | "No mushrooms\n", 180 | print(ast, model)); 181 | } 182 | 183 | BOOST_AUTO_TEST_CASE(test_json_section_printing) 184 | { 185 | namespace bpt = boost::property_tree; 186 | namespace bfe = boost::boostache::frontend; 187 | bpt::ptree model; 188 | model.put("USER.NAME", "Bob"); 189 | model.put("USER.LOCATION", "Earth"); 190 | 191 | bpt::ptree fs; 192 | bpt::ptree f1; 193 | f1.put("FOOD", "Pizza"); 194 | f1.put("MUSIC", "Classical"); 195 | fs.push_back(std::make_pair("", f1)); 196 | 197 | bpt::ptree f2; 198 | f2.put("FOOD", "Knedlik"); 199 | f2.put("MUSIC", "Folk"); 200 | fs.push_back(std::make_pair("", f2)); 201 | 202 | model.add_child("USER.FAVORITES", fs); 203 | 204 | bfe::stache::ast::root ast = parse( 205 | "{{#USER}}" 206 | "user.name={{NAME}}\n" 207 | "user.location={{LOCATION}}\n" 208 | "{{#FAVORITES}}" 209 | "user.favorite.food={{FOOD}}\n" 210 | "user.favorite.music={{MUSIC}}\n" 211 | "{{/FAVORITES}}" 212 | "{{/USER}}"); 213 | 214 | BOOST_CHECK_EQUAL( 215 | "user.name=Bob\n" 216 | "user.location=Earth\n" 217 | "user.favorite.food=Pizza\n" 218 | "user.favorite.music=Classical\n" 219 | "user.favorite.food=Knedlik\n" 220 | "user.favorite.music=Folk\n", 221 | print(ast, model)); 222 | } 223 | 224 | -------------------------------------------------------------------------------- /test/model/traits.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file traits.cpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * 6 | * Tests for boostache traits 7 | * 8 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 9 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #define BOOST_TEST_MAIN 17 | #include 18 | 19 | 20 | struct foo 21 | {}; 22 | 23 | typedef boost::variant< foo, int > my_variant; 24 | 25 | struct ext_variant : boost::spirit::extended_variant< 26 | foo 27 | , int > 28 | {}; 29 | 30 | BOOST_AUTO_TEST_CASE(not_variant_trait) 31 | { 32 | BOOST_CHECK(!boost::boostache::vm::trait::is_variant::value); 33 | } 34 | 35 | BOOST_AUTO_TEST_CASE(variant_trait) 36 | { 37 | BOOST_CHECK(boost::boostache::vm::trait::is_variant::value); 38 | } 39 | 40 | BOOST_AUTO_TEST_CASE(extended_variant_trait) 41 | { 42 | BOOST_CHECK(boost::boostache::vm::trait::is_variant::value); 43 | } 44 | -------------------------------------------------------------------------------- /test/model/unwrap_variant.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file unwrap_variant.cpp 3 | * 4 | * Copyright 2014 Michael Caisse : ciere.com 5 | * 6 | * Tests for generic variant unwrapping 7 | * 8 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 9 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define BOOST_TEST_MAIN 20 | #include 21 | 22 | namespace bstache = boost::boostache; 23 | namespace extn = bstache::extension; 24 | 25 | struct foo 26 | {}; 27 | 28 | struct bar 29 | { 30 | int value; 31 | }; 32 | 33 | 34 | 35 | namespace boost { namespace boostache { namespace extension 36 | { 37 | template <> 38 | struct test_category : mpl::identity {}; 39 | }}} 40 | 41 | 42 | namespace boost { namespace boostache { namespace extension 43 | { 44 | bool test(std::string const & name, bar b) 45 | { 46 | return !!b.value; 47 | } 48 | 49 | template 50 | void render(Stream & stream, foo f, std::string const & name, category_attribute) 51 | { 52 | stream << " "; 53 | } 54 | 55 | template 56 | void render(Stream & stream, bar b, std::string const & name, plain_attribute) 57 | { 58 | stream << "< got a bar(" << b.value << ") >"; 59 | } 60 | }}} 61 | 62 | 63 | 64 | 65 | typedef boost::variant< foo, bar > my_variant; 66 | typedef boost::variant< foo, int > your_variant; 67 | typedef boost::variant< foo, bar, bool, std::string > another_variant; 68 | 69 | 70 | using extn::test; 71 | using extn::render; 72 | 73 | BOOST_AUTO_TEST_CASE(unwrap_variant_test) 74 | { 75 | { 76 | my_variant v; 77 | BOOST_CHECK(!test("foo",v)); 78 | 79 | v = bar{12}; 80 | BOOST_CHECK(test("foo",v)); 81 | 82 | v = bar{0}; 83 | BOOST_CHECK(!test("foo",v)); 84 | } 85 | 86 | { 87 | your_variant v; 88 | v = 12; 89 | BOOST_CHECK(test("foo",v)); 90 | 91 | v = 0; 92 | BOOST_CHECK(!test("foo",v)); 93 | 94 | v = foo{}; 95 | BOOST_CHECK(!test("foo",v)); 96 | } 97 | 98 | { 99 | another_variant v; 100 | v = true; 101 | BOOST_CHECK(test("foo",v)); 102 | 103 | v = false; 104 | BOOST_CHECK(!test("foo",v)); 105 | 106 | v = bar{42}; 107 | BOOST_CHECK(test("foo",v)); 108 | 109 | v = bar{0}; 110 | BOOST_CHECK(!test("foo",v)); 111 | 112 | v = foo{}; 113 | BOOST_CHECK(!test("foo",v)); 114 | 115 | v = std::string{"test"}; 116 | BOOST_CHECK(test("foo",v)); 117 | 118 | v = std::string{}; 119 | BOOST_CHECK(!test("foo",v)); 120 | 121 | std::map m; 122 | m["foo"] = v; 123 | BOOST_CHECK(!test("foo",m)); 124 | 125 | BOOST_CHECK(!test("bar",m)); 126 | 127 | v = std::string{"test"}; 128 | m["bar"] = v; 129 | BOOST_CHECK(test("bar",m)); 130 | } 131 | 132 | } 133 | 134 | 135 | // BOOST_AUTO_TEST_CASE(unwrap_variant_render) 136 | // { 137 | // { 138 | // my_variant v; 139 | // render(std::cout,v,"foo"); 140 | 141 | // v = bar{12}; 142 | // render(std::cout,v,"foo"); 143 | 144 | // v = bar{0}; 145 | // render(std::cout,v,"foo"); 146 | // BOOST_CHECK(false); 147 | // } 148 | 149 | // { 150 | // your_variant v; 151 | // v = 12; 152 | // render(std::cout,v,"foo"); 153 | 154 | // v = 0; 155 | // render(std::cout,v,"foo"); 156 | 157 | // v = foo{}; 158 | // render(std::cout,v,"foo"); 159 | // } 160 | 161 | // { 162 | // another_variant v; 163 | // v = true; 164 | // render(std::cout,v,"foo"); 165 | 166 | // v = false; 167 | // render(std::cout,v,"foo"); 168 | 169 | // v = bar{42}; 170 | // render(std::cout,v,"foo"); 171 | 172 | // v = bar{0}; 173 | // render(std::cout,v,"foo"); 174 | 175 | // v = foo{}; 176 | // render(std::cout,v,"foo"); 177 | // } 178 | 179 | // } 180 | 181 | 182 | -------------------------------------------------------------------------------- /test/mustache/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_boost_test( mustache_parser mustache_parser.cpp ../shared/parser_test.cpp ) 2 | add_boost_test( mustache_compiler mustache_compiler.cpp ../shared/parser_test.cpp ) 3 | add_boost_test( mustache_end2end mustache_end2end.cpp ../shared/parser_test.cpp ) 4 | -------------------------------------------------------------------------------- /test/mustache/README.md: -------------------------------------------------------------------------------- 1 | Mustache related tests. 2 | 3 | There are three main types of tests 4 | * Parser 5 | * Compiler 6 | * End-to-end 7 | 8 | ## Parser Tests 9 | 10 | Most parser tests are driven by `mustache_parser.cpp`. The input and expect files can be found in the `parser_test_dir` directory. 11 | 12 | ### Still needed (to-do) 13 | 14 | * Failed parsing tests 15 | * Don't support changing of delimeter 16 | * Don't handle html escaping 17 | 18 | 19 | ## Compiler Tests 20 | 21 | Compiler tests are driven by `mustache_compiler.cpp`. The input and expect files can be found in the `compiler_test_dir` directory. They test reading input through generation of the internal VM AST. 22 | 23 | 24 | ## End-to-end Tests 25 | 26 | End-to-end tests are driven by `mustache_end2end.cpp`. The input and expect files can be found in the `end2end_test_dir` directory. They test reading input through generation of final output. 27 | 28 | * What are the whitespace rules? -------------------------------------------------------------------------------- /test/mustache/compiler_test_dir/initial.expect: -------------------------------------------------------------------------------- 1 | [ : Hello world ][ : 2 | ][ : name][][ : is here.][ : 3 | ][ : escaped_name][][ : is here][ : 4 | ][ : --------------------------- 5 | [ : --------------------------- 6 | [ [foo] : 7 | [ : 8 | [][ : Some cool section ][ : whoot][][ : is here.][ : 9 | ][ : --------------------------- 10 | [ : --------------------------- 11 | [] 12 | ] 13 | [ : --------------------------- 14 | [][ : Some cool empty section ][ : whoot][][ : is here.][ : 15 | ] 16 | ] 17 | [][ : done.][ : 18 | ] 19 | ] 20 | 21 | ] 22 | 23 | ] 24 | [ : --------------------------- 25 | [] 26 | ] 27 | [][ : done.][ : 28 | ] -------------------------------------------------------------------------------- /test/mustache/compiler_test_dir/initial.input: -------------------------------------------------------------------------------- 1 | Hello world 2 | {{name}} is here. 3 | {{& escaped_name}} is here 4 | {{#foo}} 5 | Some cool section {{whoot}} is here. 6 | {{^bar}} 7 | Some cool empty section {{whoot}} is here. 8 | {{/bar}} done. 9 | {{/foo}} done. 10 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/collections1.expect: -------------------------------------------------------------------------------- 1 | Hi I am Daniel. 2 | Hi Tom! 3 | Hi Sue! 4 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/collections1.input: -------------------------------------------------------------------------------- 1 | Hi I am {{me}}. 2 | {{# people}}Hi {{name}}! 3 | {{/ people}} -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/collections2.expect: -------------------------------------------------------------------------------- 1 | Hi I am Daniel. 2 | Hi Daniel, I am Tom, I sweep floors. 3 | Hi Daniel, I am Sue, I write code. 4 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/collections2.input.disable: -------------------------------------------------------------------------------- 1 | Hi I am {{me}}. 2 | {{# people}}Hi {{me}}, I am {{name}}, I {{job}}. 3 | {{/ people}} -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/implicit.expect: -------------------------------------------------------------------------------- 1 | Some colours: 2 | - blue 3 | - red 4 | - green 5 | - yellow 6 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/implicit.input: -------------------------------------------------------------------------------- 1 | Some colours: 2 | {{# colours}} 3 | - {{.}} 4 | {{/ colours}} -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/implicit2.expect: -------------------------------------------------------------------------------- 1 | Lists: 2 | >>> 3 | - dog 4 | - cat 5 | - mouse 6 | <<< 7 | >>> 8 | - small 9 | - medium 10 | - large 11 | <<< 12 | >>> 13 | - earth 14 | - wind 15 | - fire 16 | - water 17 | <<< 18 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/implicit2.input: -------------------------------------------------------------------------------- 1 | Lists: 2 | {{# lists}} 3 | >>> 4 | {{# .}} 5 | - {{.}} 6 | {{/ .}} 7 | <<< 8 | {{/ lists}} -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/inverted_section1.expect: -------------------------------------------------------------------------------- 1 | Hi I am Daniel. 2 | I like turtles. 3 | Hope you can see me. 4 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/inverted_section1.input: -------------------------------------------------------------------------------- 1 | Hi I am {{me}}. 2 | {{# showme}} 3 | I like {{pet}}. 4 | {{/ showme}} 5 | {{^ dontshowme}} 6 | Hope you can see me. 7 | {{/ dontshowme}} 8 | {{^ showme2}} 9 | not me. 10 | {{/ showme2}} 11 | {{#another}}{{^ ok}} 12 | I'm invisible. 13 | {{/ ok}}{{/another}} -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/king_duckz_1.expect: -------------------------------------------------------------------------------- 1 | hahahah 2 | hahahah 3 | hahahah and hahahah 4 | ----------------------- 5 | rigahello 6 | name: "red" 7 | name: "green" 8 | name: "blue" 9 | name: "yellow" 10 | name: "black" 11 | name: "white" 12 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/king_duckz_1.input: -------------------------------------------------------------------------------- 1 | {{lol }} 2 | {{ lol}} 3 | {{ lol }} and {{lol}} 4 | ----------------------- 5 | rigahello{{#hello}} 6 | {{#items}} 7 | name: "{{name}}" 8 | {{/items}} 9 | {{/hello}} 10 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/multiple.expect: -------------------------------------------------------------------------------- 1 | Multiple Mustaches 2 | Hi I am Daniel. 3 | I like turtles. -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/multiple.input: -------------------------------------------------------------------------------- 1 | {{title}} 2 | Hi I am {{me}}{{lastname}}. 3 | I like {{pet}}.{{!comment}} -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/nested_sections1.expect: -------------------------------------------------------------------------------- 1 | Hi I am Daniel. 2 | I like turtles. 3 | If you don't see this, something went wrong. 4 | It is ok to show this. 5 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/nested_sections1.input: -------------------------------------------------------------------------------- 1 | Hi I am {{me}}. 2 | {{# showme}} 3 | I like {{pet}}. 4 | {{# another}} 5 | If you don't see this, something went wrong. 6 | {{# ok}} 7 | It is ok to show this. 8 | {{/ ok}} 9 | {{# not_ok}} 10 | But not ok to show this. 11 | {{/ not_ok}} 12 | {{/ another}} 13 | {{/ showme}} 14 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/partials1.expect: -------------------------------------------------------------------------------- 1 | Hi I am Daniel. 2 | I like turtles. 3 | What do I like? Turtles!! 4 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/partials1.input.disable: -------------------------------------------------------------------------------- 1 | Hi I am {{me}}. 2 | {{> next_more}} 3 | What do I like? {{lpet}}!! 4 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/sections1.expect: -------------------------------------------------------------------------------- 1 | Hi I am Daniel. 2 | I like turtles. 3 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/sections1.input: -------------------------------------------------------------------------------- 1 | Hi I am {{me}}. 2 | {{# showme}} 3 | I like {{pet}}. 4 | {{/ showme}} 5 | {{# dontshowme}} 6 | If you see this, something went wrong. 7 | {{/ dontshowme}} 8 | -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/spec_comment.expect: -------------------------------------------------------------------------------- 1 | 1234567890 2 | ~~~ 3 | | 4 | 1234567890 5 | ~~~ 6 | | 7 | Begin. 8 | End. 9 | ~~~ 10 | | 11 | Begin. 12 | End. 13 | ~~~ 14 | ! 15 | ~~~ 16 | | 17 | Begin. 18 | End. 19 | ~~~ 20 | 12 21 | ~~~ 22 | 12345 67890 23 | ~~~ 24 | | 25 | test -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/spec_comment.input: -------------------------------------------------------------------------------- 1 | 12345{{! Comment Block! }}67890 2 | ~~~ 3 | | 4 | 12345{{! 5 | This is a 6 | multi-line comment... 7 | }}67890 8 | ~~~ 9 | | 10 | Begin. 11 | {{! Comment Block! }} 12 | End. 13 | ~~~ 14 | | 15 | Begin. 16 | {{! Indented Comment Block! }} 17 | End. 18 | ~~~ 19 | {{! I'm Still Standalone }} 20 | ! 21 | ~~~ 22 | | 23 | Begin. 24 | {{! 25 | Multiline 26 | comment 27 | ! 28 | }} 29 | End. 30 | ~~~ 31 | 12 {{! 34 }} 32 | ~~~ 33 | 12345 {{! Comment Block! }} 67890 34 | ~~~ 35 | | 36 | {{! comment 1 }} {{! comment 2 }} 37 | test -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/spec_interp.expect: -------------------------------------------------------------------------------- 1 | |---| 2 | ~~~ 3 | --- 4 | ~~~ 5 | --- 6 | ~~~ -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/spec_interp.input: -------------------------------------------------------------------------------- 1 | |{{ string }}| 2 | ~~~ 3 | {{{string}}} 4 | ~~~ 5 | {{{string}}} {{! some comment }} 6 | ~~~ -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/spec_sections.expect: -------------------------------------------------------------------------------- 1 | | 2 | | This Is 3 | | 4 | | A Line 5 | ~~~ 6 | | 7 | | This Is 8 | --- 9 | | A Line 10 | ~~~ -------------------------------------------------------------------------------- /test/mustache/end2end_test_dir/spec_sections.input: -------------------------------------------------------------------------------- 1 | | 2 | | This Is 3 | {{#showme}} 4 | | 5 | {{/showme}} 6 | | A Line 7 | ~~~ 8 | | 9 | | This Is 10 | {{#showme}} {{string }} {{/showme}} 11 | | A Line 12 | ~~~ -------------------------------------------------------------------------------- /test/mustache/mustache_compiler.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file test/mustache/mustache_compiler.cpp 3 | * 4 | * Link with shared/parser_test to utilize loading and parsing test files. 5 | * 6 | * Copyright 2015 Michael Caisse : ciere.com 7 | * 8 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 9 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | */ 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | namespace boostache = boost::boostache; 24 | namespace fe = boost::boostache::frontend; 25 | 26 | std::string print_ast(std::string const & filename) 27 | { 28 | std::ifstream file(filename.c_str()); 29 | if(!file) 30 | { 31 | BOOST_CHECK_MESSAGE(false, "Failed to open " << filename); 32 | return ""; 33 | } 34 | 35 | std::ifstream istream(filename.c_str()); 36 | auto ast = fe::parse(istream); 37 | auto engine_ast = boostache::backend::compile(ast); 38 | 39 | std::ostringstream stream; 40 | boostache::vm::ast::print(stream,engine_ast); 41 | return stream.str(); 42 | } 43 | 44 | 45 | std::string test_dir = "mustache/compiler_test_dir"; 46 | -------------------------------------------------------------------------------- /test/mustache/mustache_end2end.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file test/mustache/mustache_compiler.cpp 3 | * 4 | * Link with shared/parser_test to utilize loading and parsing test files. 5 | * 6 | * Copyright 2015 Michael Caisse : ciere.com 7 | * 8 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 9 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | */ 11 | #include 12 | 13 | #include 14 | #include // need to work out header only syntax 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace boostache = boost::boostache; 24 | namespace fe = boost::boostache::frontend; 25 | 26 | 27 | struct my_node_t; 28 | using map_t = std::unordered_map; 29 | using list_t = std::vector; 30 | struct my_node_t : boost::spirit::extended_variant< 31 | bool 32 | , std::string 33 | , map_t 34 | , list_t 35 | > 36 | { 37 | my_node_t() : base_type() {} 38 | my_node_t(bool rhs) : base_type(rhs) {} 39 | my_node_t(std::string const & rhs) : base_type(rhs) {} 40 | my_node_t(char const * rhs) : base_type(std::string{rhs}) {} 41 | my_node_t(map_t const & rhs) : base_type(rhs) {} 42 | my_node_t(list_t const & rhs) : base_type(rhs) {} 43 | }; 44 | 45 | 46 | std::string print_ast(std::string const & filename) 47 | { 48 | std::ifstream file(filename.c_str()); 49 | if(!file) 50 | { 51 | BOOST_CHECK_MESSAGE(false, "Failed to open " << filename); 52 | return ""; 53 | } 54 | 55 | // ------------------------------------------------------------------ 56 | // The data model definition 57 | map_t data = { 58 | {"contacts" , map_t{{"foo","gorp"}}}, 59 | {"foo" , "bar"}, 60 | {"string" , "---"}, 61 | {"me" , "Daniel"}, 62 | {"pet" , "turtles"}, 63 | {"lpet" , "Turtles"}, 64 | {"people" , list_t{ map_t{{"name" , "Tom"}, 65 | {"job" , "sweep floors"} }, 66 | map_t{{"name" , "Sue"}, 67 | {"job" , "write code"} } 68 | } 69 | }, 70 | {"colours" , list_t{ "blue", "red", "green", "yellow" }}, 71 | {"lists" , list_t{ list_t{ "dog", "cat", "mouse"}, 72 | list_t{ "small", "medium", "large"}, 73 | list_t{ "earth", "wind", "fire", "water"} } 74 | }, 75 | {"title" , "Multiple Mustaches"}, 76 | {"comment" , "this shouldn't be here"}, 77 | {"showme" , true}, 78 | {"showme2" , true}, 79 | {"dontshowme" , false}, 80 | {"next_more" , "I like {{pet}}."}, 81 | {"another" , map_t{{"name" , "Sam"}, 82 | {"ok" , true }, 83 | {"not_ok" , false}} 84 | }, 85 | // KingDuckZ add 86 | {"hello" , list_t{ map_t{{"items", 87 | list_t{ map_t{{"name", "red"}}, 88 | map_t{{"name", "green"}}, 89 | map_t{{"name", "blue"}} } } }, 90 | map_t{{"items", 91 | list_t{ map_t{{"name", "yellow"}}, 92 | map_t{{"name", "black"}}, 93 | map_t{{"name", "white"}} } } } } 94 | }, 95 | {"lol" , "hahahah" } 96 | }; 97 | // ------------------------------------------------------------------ 98 | 99 | // load and compile the template 100 | auto templ = boostache::load_template(file); 101 | std::ostringstream stream; 102 | boostache::generate(stream, templ, data); 103 | return stream.str(); 104 | } 105 | 106 | 107 | std::string test_dir = "mustache/end2end_test_dir"; 108 | -------------------------------------------------------------------------------- /test/mustache/mustache_parser.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file test/mustache/mustache_parser.cpp 3 | * 4 | * Link with shared/parser_test to test the mustache parser 5 | * 6 | * Copyright 2015 Michael Caisse : ciere.com 7 | * 8 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 9 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace bstache = boost::boostache; 23 | namespace fe = boost::boostache::frontend; 24 | 25 | std::string print_ast(std::string const & filename) 26 | { 27 | std::ifstream file(filename.c_str()); 28 | if(!file) 29 | { 30 | BOOST_CHECK_MESSAGE(false, "Failed to open " << filename); 31 | return ""; 32 | } 33 | 34 | std::ifstream istream(filename.c_str()); 35 | auto ast = fe::parse(istream); 36 | std::ostringstream stream; 37 | fe::stache::ast::print(stream,ast); 38 | return stream.str(); 39 | } 40 | 41 | 42 | std::string test_dir = "mustache/parser_test_dir"; 43 | -------------------------------------------------------------------------------- /test/mustache/parser_test_dir/section.expect: -------------------------------------------------------------------------------- 1 | {{#repo}} 2 | {{name}} 3 | {{/repo}} 4 | {{^repo}} 5 | No repos :( 6 | {{/repo}} 7 | -------------------------------------------------------------------------------- /test/mustache/parser_test_dir/section.input: -------------------------------------------------------------------------------- 1 | {{#repo}} 2 | {{name}} 3 | {{/repo}} 4 | {{^repo}} 5 | No repos :( 6 | {{/repo}} 7 | -------------------------------------------------------------------------------- /test/mustache/parser_test_dir/simple.expect: -------------------------------------------------------------------------------- 1 | Simple text only 2 | with 3 | some 4 | lines. -------------------------------------------------------------------------------- /test/mustache/parser_test_dir/simple.input: -------------------------------------------------------------------------------- 1 | Simple text only 2 | with 3 | some 4 | lines. -------------------------------------------------------------------------------- /test/mustache/parser_test_dir/typical.expect: -------------------------------------------------------------------------------- 1 | Hello {{name}} 2 | You have just won {{value}} dollars! 3 | {{#in_ca}} 4 | Well, {{taxed_value}} dollars, after taxes. 5 | {{/in_ca}} 6 | -------------------------------------------------------------------------------- /test/mustache/parser_test_dir/typical.input: -------------------------------------------------------------------------------- 1 | Hello {{name}} 2 | You have just won {{value}} dollars! 3 | {{#in_ca}} 4 | Well, {{taxed_value}} dollars, after taxes. 5 | {{/in_ca}} 6 | -------------------------------------------------------------------------------- /test/shared/parser_test.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file test/mustache/parser_test.cpp 3 | * 4 | * Test driver for parsers. Calls parsers on input file, generates a text output from 5 | * the AST and then compares that result to the contents of an expected file. 6 | * 7 | * Simply set the value of a global std::string (test_dir) to the location 8 | * of where to find the input and expect files. The file pairs must be named [file].input 9 | * and [file].expect. Add more files to the test_dir will add the additional test. 10 | * 11 | * Copyright 2015 Michael Caisse : ciere.com 12 | * 13 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 14 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 15 | */ 16 | #define BOOST_TEST_NO_MAIN 17 | #define BOOST_TEST_ALTERNATIVE_INIT_API 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | 27 | /** 28 | * Implemented by the specific parser test. Filename is the 29 | * input file. The function will parse the contents of 30 | * the input file and then convert the resulting AST 31 | * into a std::string representation. The representation 32 | * is what the expect file contains. 33 | */ 34 | std::string print_ast(std::string const & filename); 35 | 36 | 37 | void test_parse(std::string const & filename) 38 | { 39 | BOOST_TEST_MESSAGE("Checking: " << filename); 40 | auto input_ast = print_ast(filename+".input"); 41 | 42 | std::ifstream expect_stream(std::string(filename + ".expect").c_str()); 43 | if(!expect_stream) 44 | { 45 | BOOST_CHECK_MESSAGE(false, "Failed to open expect file for " << filename); 46 | return; 47 | } 48 | 49 | std::string expected( std::istreambuf_iterator{expect_stream} 50 | , std::istreambuf_iterator{}); 51 | 52 | auto diff_iters = std::mismatch(input_ast.begin(), input_ast.end(), 53 | expected.begin(), expected.end()); 54 | 55 | if(std::get<0>(diff_iters) != input_ast.end()) 56 | { 57 | auto iter = std::get<0>(diff_iters); 58 | std::cout << "generated difference at:\n" 59 | << " offset: " << std::distance(input_ast.begin(), iter) << "\n" 60 | << " value: " << *iter << "\n" 61 | << " value: 0x" << std::hex << int(*iter) << std::dec << "\n"; 62 | } 63 | 64 | if(std::get<1>(diff_iters) != expected.end()) 65 | { 66 | auto iter = std::get<1>(diff_iters); 67 | std::cout << "expected difference at:\n" 68 | << " offset: " << std::distance(expected.begin(), iter) << "\n" 69 | << " value: " << *iter << "\n" 70 | << " value: 0x" << std::hex << int(*iter) << std::dec << "\n"; 71 | } 72 | 73 | BOOST_CHECK_EQUAL(input_ast,expected); 74 | } 75 | 76 | 77 | extern std::string test_dir; 78 | 79 | bool init_unit_test_suite() 80 | { 81 | if(!boost::filesystem::is_directory(test_dir.c_str())) 82 | { 83 | std::cerr << "unable to open directory: " << test_dir << std::endl; 84 | return false; 85 | } 86 | 87 | std::vector test_files; 88 | 89 | boost::filesystem::directory_iterator dir_iter(test_dir); 90 | boost::filesystem::directory_iterator end_dir_iter; 91 | 92 | std::for_each( dir_iter, end_dir_iter 93 | , [&test_files](boost::filesystem::path file) 94 | { 95 | if(file.extension() == ".input") 96 | { 97 | file.replace_extension(""); 98 | test_files.push_back(file.string()); 99 | } 100 | } ); 101 | 102 | std::cout << "Found " << test_files.size() << " test cases." << std::endl; 103 | for(auto & test : test_files) 104 | { 105 | std::cout << " " << test << std::endl; 106 | } 107 | 108 | boost::unit_test::framework::master_test_suite().add( BOOST_PARAM_TEST_CASE( &test_parse 109 | , test_files.begin() 110 | , test_files.end() ) ); 111 | 112 | return true; 113 | } 114 | 115 | int main(int argc, char* argv[]) 116 | { 117 | for(int i=0; i 38 | constexpr const char * type_string(std::vector const &) 39 | { 40 | return "vector"; 41 | }; 42 | 43 | template 44 | constexpr const char * type_string(std::map const &) 45 | { 46 | return "map"; 47 | }; 48 | 49 | template 50 | constexpr const char * type_string(boost::optional const &) 51 | { 52 | return "optional"; 53 | }; 54 | 55 | 56 | namespace boost { namespace boostache { namespace vm 57 | { 58 | template 59 | void generate(Stream & stream, Template const & templ, Context const & context) 60 | { 61 | stream << "generate for context " << type_string(context) << std::endl; 62 | } 63 | }}} 64 | 65 | 66 | BOOST_AUTO_TEST_CASE(int_foreach) 67 | { 68 | vm::ast::for_each for_each_node{"test", vm::ast::literal{"test node"}}; 69 | int ctx = 42; 70 | 71 | std::ostringstream stream; 72 | vm::detail::foreach(stream, for_each_node, ctx); 73 | BOOST_CHECK_EQUAL( stream.str() 74 | , "generate for context int\n" 75 | ); 76 | } 77 | 78 | BOOST_AUTO_TEST_CASE(vector1_foreach) 79 | { 80 | using vector_t = std::vector; 81 | 82 | vm::ast::for_each for_each_node{"test", vm::ast::literal{"test node"}}; 83 | vector_t ctx = {1,2,3,4}; 84 | 85 | std::ostringstream stream; 86 | vm::detail::foreach(stream, for_each_node, ctx); 87 | BOOST_CHECK_EQUAL( stream.str() 88 | , "generate for context int\n" 89 | "generate for context int\n" 90 | "generate for context int\n" 91 | "generate for context int\n" 92 | ); 93 | } 94 | 95 | BOOST_AUTO_TEST_CASE(map1_foreach) 96 | { 97 | using map_t = std::map; 98 | 99 | vm::ast::for_each for_each_node{"test", vm::ast::literal{"test node"}}; 100 | map_t ctx = {{"test",42}}; 101 | 102 | std::ostringstream stream; 103 | vm::detail::foreach(stream, for_each_node, ctx); 104 | BOOST_CHECK_EQUAL( stream.str() 105 | , "generate for context map\n" 106 | ); 107 | } 108 | 109 | BOOST_AUTO_TEST_CASE(map2_foreach) 110 | { 111 | using vector_t = std::vector; 112 | using map_t = std::map; 113 | 114 | vm::ast::for_each for_each_node{"test", vm::ast::literal{"test node"}}; 115 | map_t ctx = {{"test",{1,2,3,4}}}; 116 | 117 | std::ostringstream stream; 118 | vm::detail::foreach(stream, for_each_node, ctx); 119 | BOOST_CHECK_EQUAL( stream.str() 120 | , "generate for context map\n" 121 | ); 122 | } 123 | 124 | BOOST_AUTO_TEST_CASE(map3_foreach) 125 | { 126 | using map_t = std::map; 127 | 128 | vm::ast::for_each for_each_node{"test", vm::ast::literal{"test node"}}; 129 | map_t ctx = {{"test","bar"}}; 130 | 131 | std::ostringstream stream; 132 | vm::detail::foreach(stream, for_each_node, ctx); 133 | BOOST_CHECK_EQUAL( stream.str() 134 | , "generate for context map\n" 135 | ); 136 | } 137 | 138 | 139 | 140 | // --------------------------------------------------------------------- 141 | struct v1_t : boost::spirit::extended_variant< 142 | int 143 | , std::string 144 | , std::vector 145 | > 146 | { 147 | v1_t() : base_type() {} 148 | v1_t(int rhs) : base_type(rhs) {} 149 | v1_t(std::string const & rhs) : base_type(rhs) {} 150 | v1_t(const char * rhs) : base_type(std::string{rhs}) {} 151 | v1_t(std::vector const & rhs) : base_type(rhs) {} 152 | }; 153 | 154 | 155 | BOOST_AUTO_TEST_CASE(variant1_foreach) 156 | { 157 | vm::ast::for_each for_each_node{"test", vm::ast::literal{"test node"}}; 158 | v1_t ctx; 159 | 160 | { 161 | ctx = "gorp"; 162 | std::ostringstream stream; 163 | vm::detail::foreach(stream, for_each_node, ctx); 164 | BOOST_CHECK_EQUAL( stream.str() 165 | , "generate for context unknown\n" 166 | ); 167 | } 168 | 169 | { 170 | ctx = std::vector{1,2,3,4}; 171 | std::ostringstream stream; 172 | vm::detail::foreach(stream, for_each_node, ctx); 173 | BOOST_CHECK_EQUAL( stream.str() 174 | , "generate for context int\n" 175 | "generate for context int\n" 176 | "generate for context int\n" 177 | "generate for context int\n" 178 | ); 179 | } 180 | } 181 | // --------------------------------------------------------------------- 182 | 183 | 184 | BOOST_AUTO_TEST_CASE(optional_foreach) 185 | { 186 | vm::ast::for_each for_each_node{"test", vm::ast::literal{"test node"}}; 187 | 188 | { 189 | boost::optional ctx; 190 | std::ostringstream stream; 191 | vm::detail::foreach(stream, for_each_node, ctx); 192 | BOOST_CHECK_EQUAL( stream.str() 193 | , "generate for context optional\n" 194 | ); 195 | } 196 | { 197 | boost::optional ctx = 42; 198 | std::ostringstream stream; 199 | vm::detail::foreach(stream, for_each_node, ctx); 200 | BOOST_CHECK_EQUAL( stream.str() 201 | , "generate for context int\n" 202 | ); 203 | } 204 | 205 | { 206 | boost::optional> ctx; 207 | std::ostringstream stream; 208 | vm::detail::foreach(stream, for_each_node, ctx); 209 | BOOST_CHECK_EQUAL( stream.str() 210 | , "generate for context optional\n" 211 | ); 212 | } 213 | 214 | { 215 | boost::optional> ctx = std::vector{1,2,3,4}; 216 | std::ostringstream stream; 217 | vm::detail::foreach(stream, for_each_node, ctx); 218 | BOOST_CHECK_EQUAL( stream.str() 219 | , "generate for context int\n" 220 | "generate for context int\n" 221 | "generate for context int\n" 222 | "generate for context int\n" 223 | ); 224 | } 225 | } 226 | --------------------------------------------------------------------------------