diff --git a/examples/example1.cpp b/examples/example1.cpp index 195b831..d4b253c 100644 --- a/examples/example1.cpp +++ b/examples/example1.cpp @@ -4,6 +4,7 @@ * A simple example of how to use boostache. * * Copyright 2015 Michael Caisse : ciere.com + * Copyright 2015 Michele Santullo * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,6 +12,7 @@ #define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS #include +#include #include // need to work out header only syntax #include #include @@ -45,7 +47,7 @@ int main() using boostache::load_template; auto iter = input.begin(); - auto templ = load_template(iter, input.end()); + auto templ = load_template(iter, input.end(), boostache::frontend::file_mapper()); // ------------------------------------------------------------------ // ------------------------------------------------------------------ diff --git a/examples/example2.cpp b/examples/example2.cpp index ffb301d..40c1b00 100644 --- a/examples/example2.cpp +++ b/examples/example2.cpp @@ -4,6 +4,7 @@ * A slightly more complex example. * * Copyright 2015 Michael Caisse : ciere.com + * Copyright 2015 Michele Santullo * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,6 +14,7 @@ #include #include // need to work out header only syntax #include +#include #include #include #include @@ -24,7 +26,7 @@ namespace boostache = boost::boostache; // ------------------------------------------------------- // The data will be an invoice this time. The invoice // consists of a list of line items. Each line item -// can be describes as map of string to strings. +// can be described as map of string to strings. // using item_t = std::map; using item_list_t = std::vector; @@ -36,7 +38,7 @@ int main() { // ------------------------------------------------------------------ // The template describing an invoice. - std::string input( + std::string input( "Invoice" "\n" "{{#lines}}" @@ -49,13 +51,13 @@ int main() // ------------------------------------------------------------------ // The data description. invoice_items is a list of maps that // describe each item. - item_list_t invoice_items = { + item_list_t invoice_items = { { {"item_code" , "1234"}, {"description" , "teddy bear"}, {"amount" , "$23"} }, { {"item_code" , "1235"}, {"description" , "computer"}, - {"amount" , "$9"} } + {"amount" , "$9"} } }; // we need to put the list into a map so that tag 'lines' can @@ -70,7 +72,7 @@ int main() using boostache::load_template; auto iter = input.begin(); - auto templ = load_template(iter, input.end()); + auto templ = load_template(iter, input.end(), boostache::frontend::file_mapper()); // ------------------------------------------------------------------ // ------------------------------------------------------------------ diff --git a/examples/example3.cpp b/examples/example3.cpp index ef62887..e52d0d4 100644 --- a/examples/example3.cpp +++ b/examples/example3.cpp @@ -5,6 +5,7 @@ * clean things up with a variant. * * Copyright 2015 Michael Caisse : ciere.com + * Copyright 2015 Michele Santullo * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,6 +15,7 @@ #include #include // need to work out header only syntax #include +#include #include #include #include @@ -60,7 +62,7 @@ int main() { // ------------------------------------------------------------------ // The template describing an invoice. - std::string input( + std::string input( "Invoice {{invoice_number}}" "\n" "{{# company}}" @@ -79,7 +81,7 @@ int main() // ------------------------------------------------------------------ // The data description. - object_t invoice = + object_t invoice = {{"invoice_number", "1234"}, {"company" , object_t{{"name" , "FizSoft"}, {"street" , "42 Level St."}, @@ -93,7 +95,7 @@ int main() {"description" , "Computer"}, {"amount" , "$9"}} }} }; - + // ------------------------------------------------------------------ // ------------------------------------------------------------------ @@ -101,9 +103,10 @@ int main() // This parses the input and compiles the result. The return is the // compiled data structure using boostache::load_template; + using boostache::frontend::file_mapper; auto iter = input.begin(); - auto templ = load_template(iter, input.end()); + auto templ = load_template(iter, input.end(), file_mapper()); // ------------------------------------------------------------------ // ------------------------------------------------------------------ diff --git a/include/boost/boostache/backend/detail/stache_compiler.hpp b/include/boost/boostache/backend/detail/stache_compiler.hpp index 91feee0..4e1724e 100644 --- a/include/boost/boostache/backend/detail/stache_compiler.hpp +++ b/include/boost/boostache/backend/detail/stache_compiler.hpp @@ -2,6 +2,7 @@ * \file detail/stache_compiler.hpp * * Copyright 2014, 2015 Michael Caisse : ciere.com + * Copyright 2015 Michele Santullo * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -28,7 +29,7 @@ namespace boost { namespace boostache { namespace backend { namespace stache_com return( s.find_first_not_of(std::string{" \t\r\n"}) == std::string::npos ); } - + class stache_visit { public: @@ -103,13 +104,17 @@ namespace boost { namespace boostache { namespace backend { namespace stache_com vm::ast::node operator()(fe::stache::ast::comment const & v) const { return vm::ast::nop{}; - } + } - vm::ast::node operator()(fe::stache::ast::partial const & v) const + vm::ast::node operator()(fe::stache::ast::partial const & p) const { - // TODO: need to implement partials - return vm::ast::nop{}; - } + vm::ast::node_list node_list; + for (auto const & node : p.nodes) + { + node_list.nodes.push_back(boost::apply_visitor(*this, node)); + } + return node_list; + } vm::ast::node operator()(fe::stache::ast::node_list const & nodes) const { diff --git a/include/boost/boostache/boostache.hpp b/include/boost/boostache/boostache.hpp index 52776b5..2f64b5e 100644 --- a/include/boost/boostache/boostache.hpp +++ b/include/boost/boostache/boostache.hpp @@ -2,6 +2,7 @@ * \file boostache.hpp * * Copyright 2014 Michael Caisse : ciere.com + * Copyright 2015 Michele Santullo * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,20 +15,21 @@ #include #include #include +#include namespace boost { namespace boostache { - template - inline vm::ast::node load_template(Iterator & begin, Iterator const & end) + template + inline vm::ast::node load_template(Iterator & begin, Iterator const & end, PartialFunctor mapper_type) { - return backend::compile(frontend::parse(begin,end)); + return backend::compile(frontend::parse(begin,end, std::move(mapper_type))); } - template - inline vm::ast::node load_template(std::istream & input) + template + inline vm::ast::node load_template(std::istream & input, PartialFunctor mapper_type) { - return backend::compile(frontend::parse(input)); + return backend::compile(frontend::parse(input, std::move(mapper_type))); } template diff --git a/include/boost/boostache/frontend/file_mapper.hpp b/include/boost/boostache/frontend/file_mapper.hpp new file mode 100644 index 0000000..c9e6f89 --- /dev/null +++ b/include/boost/boostache/frontend/file_mapper.hpp @@ -0,0 +1,67 @@ +/** + * \file file_mapper.hpp + * + * Copyright 2015 Michele Santullo + * + * Utility class that maps a partial name to a file on the filesystem. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_BOOSTACHE_FILE_MAPPER_HPP +#define BOOST_BOOSTACHE_FILE_MAPPER_HPP + +#include +#include +#include + +namespace boost { namespace boostache { namespace frontend +{ +template +class file_mapper +{ +public: + using string_type = std::basic_string; + + file_mapper (file_mapper&&) = default; + file_mapper (const file_mapper&) = default; + explicit file_mapper (string_type&& ext=".mustache", string_type&& base_path="") : + extension(std::move(ext)), + base_path(std::move(base_path)) + { + } + + void set_extension (string_type&& ext) + { + extension = std::move(ext); + } + void set_base_path (string_type&& bpath) + { + base_path = std::move(bpath); + if (!base_path.empty() && base_path.back() != static_cast('/') && base_path.back() != static_cast('\\')) + base_path += '/'; + } + + string_type operator() (const string_type& tag) const + { + //TODO: don't open the file every time, add some sort of buffering instead + string_type path(base_path + tag + extension); + std::basic_ifstream ifs(path); + ifs.seekg(0, std::ios_base::end); + const auto ssize = ifs.tellg(); + ifs.seekg(0, std::ios_base::beg); + + string_type retval; + retval.resize(ssize, ' '); + ifs.read(&*retval.begin(), ssize); + ifs.close(); + return retval; + } + +private: + string_type extension; + string_type base_path; +}; +}}} +#endif diff --git a/include/boost/boostache/frontend/parse.hpp b/include/boost/boostache/frontend/parse.hpp index 84eeeea..f97a648 100644 --- a/include/boost/boostache/frontend/parse.hpp +++ b/include/boost/boostache/frontend/parse.hpp @@ -2,6 +2,7 @@ * \file parse.hpp * * Copyright 2014 Michael Caisse : ciere.com + * Copyright 2015 Michele Santullo * * Generic parser entry point. Call parse with the input format * as the template parameter: @@ -17,15 +18,16 @@ #include #include #include +#include namespace boost { namespace boostache { namespace frontend { - template - typename Format::ast_t parse(Iterator & begin, Iterator const & end) + template + typename Format::ast_t parse(Iterator & begin, Iterator const & end, PartialFunctor mapper_type) { typename Format::ast_t ast; - typename Format::template grammar_t grammar; - + typename Format::template grammar_t grammar(std::move(mapper_type)); + // TODO mjc : should throw with parse error location if(!boost::spirit::qi::phrase_parse( begin, end , grammar @@ -38,14 +40,15 @@ namespace boost { namespace boostache { namespace frontend } - template - typename Format::ast_t parse(std::istream& input) + template + typename Format::ast_t parse(std::istream& input, PartialFunctor mapper_type) { // TODO mjc : store/restore ios state? input.unsetf(std::ios::skipws); boost::spirit::istream_iterator iter{input}; return parse( iter - , boost::spirit::istream_iterator{} ); + , boost::spirit::istream_iterator{} + , std::move(mapper_type) ); } }}} diff --git a/include/boost/boostache/frontend/stache/ast.hpp b/include/boost/boostache/frontend/stache/ast.hpp index 256f6d0..5217881 100644 --- a/include/boost/boostache/frontend/stache/ast.hpp +++ b/include/boost/boostache/frontend/stache/ast.hpp @@ -3,6 +3,7 @@ * * Copyright 2014 Michael Caisse : ciere.com * Copyright 2014 Jeroen Habraken + * Copyright 2015 Michele Santullo * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -34,9 +35,7 @@ namespace boost { namespace boostache { namespace frontend { namespace stache { identifier value; }; - struct partial : identifier - {}; - + struct partial; struct section; struct node : boost::spirit::extended_variant< @@ -45,7 +44,7 @@ namespace boost { namespace boostache { namespace frontend { namespace stache { , literal_text , variable , boost::recursive_wrapper
- , partial + , boost::recursive_wrapper > { node() : base_type() {} @@ -65,6 +64,12 @@ namespace boost { namespace boostache { namespace frontend { namespace stache { node_list nodes; }; + struct partial + { + identifier name; + node_list nodes; + }; + struct root : node_list {}; }}}}} diff --git a/include/boost/boostache/frontend/stache/grammar.hpp b/include/boost/boostache/frontend/stache/grammar.hpp index 2d8b757..a91c4f0 100644 --- a/include/boost/boostache/frontend/stache/grammar.hpp +++ b/include/boost/boostache/frontend/stache/grammar.hpp @@ -3,6 +3,7 @@ * * Copyright 2014 Michael Caisse : ciere.com * Copyright 2014 Jeroen Habraken + * Copyright 2015 Michele Santullo * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,17 +15,20 @@ #include #include +#include +#include namespace boost { namespace boostache { namespace frontend { namespace stache { namespace qi = boost::spirit::qi; - template + template struct grammar : qi::grammar { - grammar(); + explicit grammar(PartialFunctor&& partial_mapper); + explicit grammar(const PartialFunctor& partial_mapper); qi::rule node_list @@ -69,6 +73,11 @@ namespace boost { namespace boostache { namespace frontend { namespace stache qi::rule partial ; + + private: + ast::partial on_partial (const ast::identifier& name, const boost::fusion::unused_type& context, bool& pass) const; + + PartialFunctor partial_mapper; }; }}}} diff --git a/include/boost/boostache/frontend/stache/grammar_def.hpp b/include/boost/boostache/frontend/stache/grammar_def.hpp index 3ff6bc8..aa5063f 100644 --- a/include/boost/boostache/frontend/stache/grammar_def.hpp +++ b/include/boost/boostache/frontend/stache/grammar_def.hpp @@ -3,6 +3,7 @@ * * Copyright 2014 Michael Caisse : ciere.com * Copyright 2014 Jeroen Habraken + * Copyright 2015 Michele Santullo * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -12,6 +13,7 @@ #include #include +#include #include #include @@ -31,15 +33,26 @@ #include #include #include +#include +#include +#include namespace boost { namespace boostache { namespace frontend { namespace stache { namespace qi = boost::spirit::qi; namespace spirit = boost::spirit; + namespace phx = boost::phoenix; + + template + grammar::grammar(const PartialFunctor& partial_mapper) + : grammar(PartialFunctor(partial_mapper)) + { + } - template - grammar::grammar() - : grammar::base_type(node_list) + template + grammar::grammar (PartialFunctor&& partial_mapper) + : grammar::base_type(node_list), + partial_mapper(std::move(partial_mapper)) { spirit::_1_type _1; spirit::_a_type _a; @@ -53,6 +66,7 @@ namespace boost { namespace boostache { namespace frontend { namespace stache qi::matches_type matches; qi::no_skip_type no_skip; qi::omit_type omit; + qi::_val_type _val; stache_node = @@ -122,10 +136,24 @@ namespace boost { namespace boostache { namespace frontend { namespace stache partial = lit("{{") >> '>' - >> identifier + >> identifier[_val = phx::bind(&grammar::on_partial, this, _1, phx::placeholders::_2, phx::placeholders::_3)] >> "}}" ; }; + + template + ast::partial grammar::on_partial (const ast::identifier& name, const boost::fusion::unused_type& context, bool& pass) const { + grammar gramm(partial_mapper); + const auto text = partial_mapper(name); + auto beg = text.begin(); + + ast::partial retval; + retval.name = name; + retval.nodes = boost::boostache::frontend::parse(beg, text.end(), partial_mapper); + pass = (text.end() == beg); + + return retval; + } }}}} #endif diff --git a/include/boost/boostache/frontend/stache/printer.hpp b/include/boost/boostache/frontend/stache/printer.hpp index 3067d11..b526384 100644 --- a/include/boost/boostache/frontend/stache/printer.hpp +++ b/include/boost/boostache/frontend/stache/printer.hpp @@ -3,6 +3,7 @@ * * Copyright 2014 Michael Caisse : ciere.com * Copyright 2014 Jeroen Habraken + * Copyright 2015 Michele Santullo * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -67,7 +68,7 @@ namespace boost { namespace boostache { namespace frontend { namespace stache { void operator()(partial const & v) const { - out << "{{>" << v << "}}"; + out << "{{>" << v.name << "}}"; } private: diff --git a/include/boost/boostache/stache.hpp b/include/boost/boostache/stache.hpp index 31b86a3..f84d95f 100644 --- a/include/boost/boostache/stache.hpp +++ b/include/boost/boostache/stache.hpp @@ -2,6 +2,7 @@ * \file stache.hpp * * Copyright 2014 Michael Caisse : ciere.com + * Copyright 2015 Michele Santullo * * * Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -18,8 +19,8 @@ namespace boost { namespace boostache { namespace format { struct stache { - template - using grammar_t = frontend::stache::grammar; + template + using grammar_t = frontend::stache::grammar; using ast_t = frontend::stache::ast::root; using skipper_t = boost::spirit::qi::space_type; diff --git a/test/frontend/CMakeLists.txt b/test/frontend/CMakeLists.txt index 2ef70fb..84223a8 100644 --- a/test/frontend/CMakeLists.txt +++ b/test/frontend/CMakeLists.txt @@ -1,2 +1,4 @@ add_boost_test(adapt_test adapt_test.cpp) add_boost_test(grammar_basic grammar_basic.cpp) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/partial.mustache ${CMAKE_CURRENT_BINARY_DIR}/partial.mustache COPYONLY) diff --git a/test/frontend/adapt_test.cpp b/test/frontend/adapt_test.cpp index 53dc76a..6c2fd25 100644 --- a/test/frontend/adapt_test.cpp +++ b/test/frontend/adapt_test.cpp @@ -1,6 +1,7 @@ // file used during initial dev // will be removed #include +#include #include #include #include @@ -12,6 +13,7 @@ using boostache::frontend::parse; int main() { std::string input("foo"); + boostache::frontend::file_mapper fmapper; auto iter = input.begin(); - auto ast = parse(iter,input.end()); + auto ast = parse(iter,input.end(), fmapper); } diff --git a/test/frontend/grammar_basic.cpp b/test/frontend/grammar_basic.cpp index 5424e13..3a49e40 100644 --- a/test/frontend/grammar_basic.cpp +++ b/test/frontend/grammar_basic.cpp @@ -2,6 +2,7 @@ * \file frontend/grammar_basic.cpp * * Copyright 2014 Michael Caisse : ciere.com + * Copyright 2015 Michele Santullo * * Basic stache gramar test * @@ -14,6 +15,7 @@ #include #include #include +#include #include #include @@ -41,7 +43,7 @@ BOOST_AUTO_TEST_CASE(stache_parse_test) ); auto iter = input.begin(); - auto ast = parse(iter,input.end()); + auto ast = parse(iter,input.end(), boostache::frontend::file_mapper()); // we expect everything got parsed BOOST_CHECK(iter==input.end()); diff --git a/test/frontend/partial.mustache b/test/frontend/partial.mustache new file mode 100644 index 0000000..988beeb --- /dev/null +++ b/test/frontend/partial.mustache @@ -0,0 +1 @@ +replacement for partial \ No newline at end of file diff --git a/test/mustache/mustache_compiler.cpp b/test/mustache/mustache_compiler.cpp index 174ea0a..351ae7e 100644 --- a/test/mustache/mustache_compiler.cpp +++ b/test/mustache/mustache_compiler.cpp @@ -4,6 +4,7 @@ * Link with shared/parser_test to utilize loading and parsing test files. * * Copyright 2015 Michael Caisse : ciere.com + * Copyright 2015 Michele Santullo * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -12,6 +13,7 @@ #include #include #include +#include #include #include @@ -33,7 +35,7 @@ std::string print_ast(std::string const & filename) } std::ifstream istream(filename.c_str()); - auto ast = fe::parse(istream); + auto ast = fe::parse(istream, boostache::frontend::file_mapper()); auto engine_ast = boostache::backend::compile(ast); std::ostringstream stream; diff --git a/test/mustache/mustache_end2end.cpp b/test/mustache/mustache_end2end.cpp index 4548192..dc9ebad 100644 --- a/test/mustache/mustache_end2end.cpp +++ b/test/mustache/mustache_end2end.cpp @@ -4,6 +4,7 @@ * Link with shared/parser_test to utilize loading and parsing test files. * * Copyright 2015 Michael Caisse : ciere.com + * Copyright 2015 Michele Santullo * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -12,6 +13,7 @@ #include #include +#include #include // need to work out header only syntax #include #include @@ -92,7 +94,7 @@ std::string print_ast(std::string const & filename) // ------------------------------------------------------------------ // load and compile the template - auto templ = boostache::load_template(file); + auto templ = boostache::load_template(file, boostache::frontend::file_mapper()); std::ostringstream stream; boostache::generate(stream, templ, data); return stream.str(); diff --git a/test/mustache/mustache_parser.cpp b/test/mustache/mustache_parser.cpp index 7c8b9e9..4be6b4e 100644 --- a/test/mustache/mustache_parser.cpp +++ b/test/mustache/mustache_parser.cpp @@ -4,6 +4,7 @@ * Link with shared/parser_test to test the mustache parser * * Copyright 2015 Michael Caisse : ciere.com + * Copyright 2015 Michele Santullo * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +34,7 @@ std::string print_ast(std::string const & filename) } std::ifstream istream(filename.c_str()); - auto ast = fe::parse(istream); + auto ast = fe::parse(istream, bstache::frontend::file_mapper()); std::ostringstream stream; fe::stache::ast::print(stream,ast); return stream.str();