diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d9f87b..fed4ad6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,11 +16,11 @@ set(INJA_INSTALL_INCLUDE_DIR "include") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake) if(CMAKE_COMPILER_IS_GNUCC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + add_compile_options(-Wall) endif() if(MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + add_compile_options(/W4 /permissive- /utf-8 /Zc:__cplusplus) endif() diff --git a/appveyor.yml b/appveyor.yml index b13849c..33ace31 100755 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,14 +8,14 @@ platform: - x64 configuration: -# - Debug + - Debug - Release environment: matrix: # Visual Studio 2015 -# - TOOLCHAIN: v140 -# STD: 14 + - TOOLCHAIN: v140 + STD: 14 # Visual Studio 2017 - TOOLCHAIN: v141 STD: 14 @@ -34,7 +34,7 @@ init: before_build: - mkdir -p build - cd build - - cmake .. -A %PLATFORM% -T %TOOLCHAIN% -DCMAKE_CXX_STANDARD=%STD% -DCMAKE_CXX_FLAGS="/permissive- /utf-8 /Zc:__cplusplus" + - cmake .. -A %PLATFORM% -T %TOOLCHAIN% -DCMAKE_CXX_STANDARD=%STD% build_script: - cmake --build . --config %CONFIGURATION% -- -verbosity:n diff --git a/include/inja/environment.hpp b/include/inja/environment.hpp index 05bb5e8..a8dd22d 100644 --- a/include/inja/environment.hpp +++ b/include/inja/environment.hpp @@ -28,84 +28,64 @@ using json = nlohmann::json; * \brief Class for changing the configuration. */ class Environment { - class Impl { - public: - std::string input_path; - std::string output_path; - - LexerConfig lexer_config; - ParserConfig parser_config; - - FunctionStorage callbacks; - TemplateStorage included_templates; - }; - - std::unique_ptr m_impl; - public: Environment(): Environment("") { } - explicit Environment(const std::string& global_path): m_impl(stdinja::make_unique()) { - m_impl->input_path = global_path; - m_impl->output_path = global_path; - } + explicit Environment(const std::string& global_path): m_input_path(global_path), m_output_path(global_path) {} - explicit Environment(const std::string& input_path, const std::string& output_path): m_impl(stdinja::make_unique()) { - m_impl->input_path = input_path; - m_impl->output_path = output_path; - } + Environment(const std::string& input_path, const std::string& output_path): m_input_path(input_path), m_output_path(output_path) {} /// Sets the opener and closer for template statements void set_statement(const std::string& open, const std::string& close) { - m_impl->lexer_config.statement_open = open; - m_impl->lexer_config.statement_close = close; - m_impl->lexer_config.update_open_chars(); + m_lexer_config.statement_open = open; + m_lexer_config.statement_close = close; + m_lexer_config.update_open_chars(); } /// Sets the opener for template line statements void set_line_statement(const std::string& open) { - m_impl->lexer_config.line_statement = open; - m_impl->lexer_config.update_open_chars(); + m_lexer_config.line_statement = open; + m_lexer_config.update_open_chars(); } /// Sets the opener and closer for template expressions void set_expression(const std::string& open, const std::string& close) { - m_impl->lexer_config.expression_open = open; - m_impl->lexer_config.expression_close = close; - m_impl->lexer_config.update_open_chars(); + m_lexer_config.expression_open = open; + m_lexer_config.expression_close = close; + m_lexer_config.update_open_chars(); } /// Sets the opener and closer for template comments void set_comment(const std::string& open, const std::string& close) { - m_impl->lexer_config.comment_open = open; - m_impl->lexer_config.comment_close = close; - m_impl->lexer_config.update_open_chars(); + m_lexer_config.comment_open = open; + m_lexer_config.comment_close = close; + m_lexer_config.update_open_chars(); } /// Sets whether to remove the first newline after a block void set_trim_blocks(bool trim_blocks) { - m_impl->lexer_config.trim_blocks = trim_blocks; + m_lexer_config.trim_blocks = trim_blocks; } /// Sets whether to strip the spaces and tabs from the start of a line to a block void set_lstrip_blocks(bool lstrip_blocks) { - m_impl->lexer_config.lstrip_blocks = lstrip_blocks; + m_lexer_config.lstrip_blocks = lstrip_blocks; } /// Sets the element notation syntax void set_element_notation(ElementNotation notation) { - m_impl->parser_config.notation = notation; + m_parser_config.notation = notation; } Template parse(nonstd::string_view input) { - Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates); + Parser parser(m_parser_config, m_lexer_config, m_included_templates); return parser.parse(input); } Template parse_template(const std::string& filename) { - Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates); - return parser.parse_template(m_impl->input_path + static_cast(filename)); + Parser parser(m_parser_config, m_lexer_config, m_included_templates); + return parser.parse_template(m_input_path + static_cast(filename)); } std::string render(nonstd::string_view input, const json& data) { @@ -128,13 +108,13 @@ class Environment { } void write(const std::string& filename, const json& data, const std::string& filename_out) { - std::ofstream file(m_impl->output_path + filename_out); + std::ofstream file(m_output_path + filename_out); file << render_file(filename, data); file.close(); } void write(const Template& temp, const json& data, const std::string& filename_out) { - std::ofstream file(m_impl->output_path + filename_out); + std::ofstream file(m_output_path + filename_out); file << render(temp, data); file.close(); } @@ -150,24 +130,24 @@ class Environment { } std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) { - Renderer(m_impl->included_templates, m_impl->callbacks).render_to(os, tmpl, data); + Renderer(m_included_templates, m_callbacks).render_to(os, tmpl, data); return os; } std::string load_file(const std::string& filename) { - Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates); - return parser.load_file(m_impl->input_path + filename); + Parser parser(m_parser_config, m_lexer_config, m_included_templates); + return parser.load_file(m_input_path + filename); } json load_json(const std::string& filename) { - std::ifstream file = open_file_or_throw(m_impl->input_path + filename); + std::ifstream file = open_file_or_throw(m_input_path + filename); json j; file >> j; return j; } void add_callback(const std::string& name, unsigned int numArgs, const CallbackFunction& callback) { - m_impl->callbacks.add_callback(name, numArgs, callback); + m_callbacks.add_callback(name, numArgs, callback); } /** Includes a template with a given name into the environment. @@ -175,8 +155,18 @@ class Environment { * include "" syntax. */ void include_template(const std::string& name, const Template& tmpl) { - m_impl->included_templates[name] = tmpl; + m_included_templates[name] = tmpl; } + + private: + std::string m_input_path; + std::string m_output_path; + + LexerConfig m_lexer_config; + ParserConfig m_parser_config; + + FunctionStorage m_callbacks; + TemplateStorage m_included_templates; }; /*! diff --git a/include/inja/string_view.hpp b/include/inja/string_view.hpp index 9d52d61..42cd4f0 100644 --- a/include/inja/string_view.hpp +++ b/include/inja/string_view.hpp @@ -12,7 +12,7 @@ #define NONSTD_SV_LITE_H_INCLUDED #define string_view_lite_MAJOR 1 -#define string_view_lite_MINOR 1 +#define string_view_lite_MINOR 4 #define string_view_lite_PATCH 0 #define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH) @@ -218,7 +218,7 @@ using std::operator<<; # define nssv_COMPILER_MSVC_VERSION 0 #endif -#define nssv_COMPILER_VERSION( major, minor, patch ) (10 * ( 10 * major + minor) + patch) +#define nssv_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) #if defined(__clang__) # define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) @@ -273,8 +273,10 @@ using std::operator<<; #define nssv_HAVE_WCHAR16_T nssv_CPP11_100 #define nssv_HAVE_WCHAR32_T nssv_CPP11_100 -#if ! ( ( nssv_CPP11 && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) ) +#if ! ( ( nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) ) # define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140 +#else +# define nssv_HAVE_STD_DEFINED_LITERALS 0 #endif // Presence of C++14 language features: @@ -402,6 +404,22 @@ nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 ) namespace nonstd { namespace sv_lite { +#if nssv_CPP11_OR_GREATER + +namespace detail { + +// Expect tail call optimization to make length() non-recursive: + +template< typename CharT > +inline constexpr std::size_t length( CharT * s, std::size_t result = 0 ) +{ + return *s == '\0' ? result : length( s + 1, result + 1 ); +} + +} // namespace detail + +#endif // nssv_CPP11_OR_GREATER + template < class CharT, @@ -455,14 +473,20 @@ public: {} #endif - nssv_constexpr basic_string_view( CharT const * s, size_type count ) + nssv_constexpr basic_string_view( CharT const * s, size_type count ) nssv_noexcept // non-standard noexcept : data_( s ) , size_( count ) {} - nssv_constexpr basic_string_view( CharT const * s) + nssv_constexpr basic_string_view( CharT const * s) nssv_noexcept // non-standard noexcept : data_( s ) +#if nssv_CPP17_OR_GREATER , size_( Traits::length(s) ) +#elif nssv_CPP11_OR_GREATER + , size_( detail::length(s) ) +#else + , size_( Traits::length(s) ) +#endif {} // Assignment: @@ -518,7 +542,7 @@ public: #else if ( pos >= size() ) { - throw std::out_of_range("nonst::string_view::at()"); + throw std::out_of_range("nonstd::string_view::at()"); } #endif return data_at( pos ); @@ -560,7 +584,7 @@ public: #else if ( pos > size() ) { - throw std::out_of_range("nonst::string_view::copy()"); + throw std::out_of_range("nonstd::string_view::copy()"); } #endif const size_type rlen = (std::min)( n, size() - pos ); @@ -577,7 +601,7 @@ public: #else if ( pos > size() ) { - throw std::out_of_range("nonst::string_view::substr()"); + throw std::out_of_range("nonstd::string_view::substr()"); } #endif return basic_string_view( data() + pos, (std::min)( n, size() - pos ) ); @@ -588,7 +612,9 @@ public: nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1) { if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) + { return result; + } return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; } @@ -684,10 +710,14 @@ public: nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) { if ( size() < v.size() ) + { return npos; + } if ( v.empty() ) + { return (std::min)( size(), pos ); + } const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size(); const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq ); @@ -825,7 +855,7 @@ private: { const basic_string_view v; - nssv_constexpr not_in_view( basic_string_view v ) : v( v ) {} + nssv_constexpr explicit not_in_view( basic_string_view v ) : v( v ) {} nssv_constexpr bool operator()( CharT c ) const { @@ -952,7 +982,167 @@ nssv_constexpr bool operator>= ( // constexpr and noexcept so that an object t with an implicit conversion // to S can be compared according to Table 67. -#if nssv_CPP11_OR_GREATER && ! nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 ) +#if ! nssv_CPP11_OR_GREATER || nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 ) + +// accomodate for older compilers: + +// == + +template< class CharT, class Traits> +nssv_constexpr bool operator==( + basic_string_view lhs, + char const * rhs ) nssv_noexcept +{ return lhs.compare( rhs ) == 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator==( + char const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) == 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator==( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator==( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } + +// != + +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + basic_string_view lhs, + char const * rhs ) nssv_noexcept +{ return lhs.compare( rhs ) != 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + char const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) != 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.size() != rhs.size() && lhs.compare( rhs ) != 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return lhs.size() != rhs.size() || rhs.compare( lhs ) != 0; } + +// < + +template< class CharT, class Traits> +nssv_constexpr bool operator<( + basic_string_view lhs, + char const * rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator<( + char const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) > 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator<( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator<( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) > 0; } + +// <= + +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + basic_string_view lhs, + char const * rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + char const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) >= 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) >= 0; } + +// > + +template< class CharT, class Traits> +nssv_constexpr bool operator>( + basic_string_view lhs, + char const * rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator>( + char const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) < 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator>( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator>( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) < 0; } + +// >= + +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + basic_string_view lhs, + char const * rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + char const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) <= 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) <= 0; } + +#else // newer compilers: #define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view >::type @@ -1049,7 +1239,7 @@ nssv_constexpr bool operator>= ( #undef nssv_MSVC_ORDER #undef nssv_BASIC_STRING_VIEW_I -#endif // nssv_CPP11_OR_GREATER +#endif // compiler-dependent approach to comparisons // 24.4.4 Inserters and extractors: diff --git a/single_include/inja/inja.hpp b/single_include/inja/inja.hpp index beb284e..6cd3bf5 100644 --- a/single_include/inja/inja.hpp +++ b/single_include/inja/inja.hpp @@ -1,5 +1,7 @@ -#ifndef PANTOR_INJA_HPP -#define PANTOR_INJA_HPP +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_INJA_HPP_ +#define INCLUDE_INJA_INJA_HPP_ #include #include @@ -12,8 +14,10 @@ #include // #include "environment.hpp" -#ifndef PANTOR_INJA_ENVIRONMENT_HPP -#define PANTOR_INJA_ENVIRONMENT_HPP +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_ENVIRONMENT_HPP_ +#define INCLUDE_INJA_ENVIRONMENT_HPP_ #include #include @@ -23,8 +27,10 @@ #include // #include "config.hpp" -#ifndef PANTOR_INJA_CONFIG_HPP -#define PANTOR_INJA_CONFIG_HPP +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_CONFIG_HPP_ +#define INCLUDE_INJA_CONFIG_HPP_ #include #include @@ -44,7 +50,7 @@ #define NONSTD_SV_LITE_H_INCLUDED #define string_view_lite_MAJOR 1 -#define string_view_lite_MINOR 1 +#define string_view_lite_MINOR 4 #define string_view_lite_PATCH 0 #define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH) @@ -250,7 +256,7 @@ using std::operator<<; # define nssv_COMPILER_MSVC_VERSION 0 #endif -#define nssv_COMPILER_VERSION( major, minor, patch ) (10 * ( 10 * major + minor) + patch) +#define nssv_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) #if defined(__clang__) # define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) @@ -305,8 +311,10 @@ using std::operator<<; #define nssv_HAVE_WCHAR16_T nssv_CPP11_100 #define nssv_HAVE_WCHAR32_T nssv_CPP11_100 -#if ! ( ( nssv_CPP11 && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) ) +#if ! ( ( nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) ) # define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140 +#else +# define nssv_HAVE_STD_DEFINED_LITERALS 0 #endif // Presence of C++14 language features: @@ -434,6 +442,22 @@ nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 ) namespace nonstd { namespace sv_lite { +#if nssv_CPP11_OR_GREATER + +namespace detail { + +// Expect tail call optimization to make length() non-recursive: + +template< typename CharT > +inline constexpr std::size_t length( CharT * s, std::size_t result = 0 ) +{ + return *s == '\0' ? result : length( s + 1, result + 1 ); +} + +} // namespace detail + +#endif // nssv_CPP11_OR_GREATER + template < class CharT, @@ -487,14 +511,20 @@ public: {} #endif - nssv_constexpr basic_string_view( CharT const * s, size_type count ) + nssv_constexpr basic_string_view( CharT const * s, size_type count ) nssv_noexcept // non-standard noexcept : data_( s ) , size_( count ) {} - nssv_constexpr basic_string_view( CharT const * s) + nssv_constexpr basic_string_view( CharT const * s) nssv_noexcept // non-standard noexcept : data_( s ) +#if nssv_CPP17_OR_GREATER , size_( Traits::length(s) ) +#elif nssv_CPP11_OR_GREATER + , size_( detail::length(s) ) +#else + , size_( Traits::length(s) ) +#endif {} // Assignment: @@ -550,7 +580,7 @@ public: #else if ( pos >= size() ) { - throw std::out_of_range("nonst::string_view::at()"); + throw std::out_of_range("nonstd::string_view::at()"); } #endif return data_at( pos ); @@ -592,7 +622,7 @@ public: #else if ( pos > size() ) { - throw std::out_of_range("nonst::string_view::copy()"); + throw std::out_of_range("nonstd::string_view::copy()"); } #endif const size_type rlen = (std::min)( n, size() - pos ); @@ -609,7 +639,7 @@ public: #else if ( pos > size() ) { - throw std::out_of_range("nonst::string_view::substr()"); + throw std::out_of_range("nonstd::string_view::substr()"); } #endif return basic_string_view( data() + pos, (std::min)( n, size() - pos ) ); @@ -620,7 +650,9 @@ public: nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1) { if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) + { return result; + } return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; } @@ -716,10 +748,14 @@ public: nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) { if ( size() < v.size() ) + { return npos; + } if ( v.empty() ) + { return (std::min)( size(), pos ); + } const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size(); const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq ); @@ -857,7 +893,7 @@ private: { const basic_string_view v; - nssv_constexpr not_in_view( basic_string_view v ) : v( v ) {} + nssv_constexpr explicit not_in_view( basic_string_view v ) : v( v ) {} nssv_constexpr bool operator()( CharT c ) const { @@ -984,7 +1020,167 @@ nssv_constexpr bool operator>= ( // constexpr and noexcept so that an object t with an implicit conversion // to S can be compared according to Table 67. -#if nssv_CPP11_OR_GREATER && ! nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 ) +#if ! nssv_CPP11_OR_GREATER || nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 ) + +// accomodate for older compilers: + +// == + +template< class CharT, class Traits> +nssv_constexpr bool operator==( + basic_string_view lhs, + char const * rhs ) nssv_noexcept +{ return lhs.compare( rhs ) == 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator==( + char const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) == 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator==( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator==( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } + +// != + +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + basic_string_view lhs, + char const * rhs ) nssv_noexcept +{ return lhs.compare( rhs ) != 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + char const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) != 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.size() != rhs.size() && lhs.compare( rhs ) != 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator!=( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return lhs.size() != rhs.size() || rhs.compare( lhs ) != 0; } + +// < + +template< class CharT, class Traits> +nssv_constexpr bool operator<( + basic_string_view lhs, + char const * rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator<( + char const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) > 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator<( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator<( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) > 0; } + +// <= + +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + basic_string_view lhs, + char const * rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + char const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) >= 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator<=( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) >= 0; } + +// > + +template< class CharT, class Traits> +nssv_constexpr bool operator>( + basic_string_view lhs, + char const * rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator>( + char const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) < 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator>( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator>( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) < 0; } + +// >= + +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + basic_string_view lhs, + char const * rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + char const * lhs, + basic_string_view rhs ) nssv_noexcept +{ return rhs.compare( lhs ) <= 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + basic_string_view lhs, + std::basic_string rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0; } + +template< class CharT, class Traits> +nssv_constexpr bool operator>=( + std::basic_string rhs, + basic_string_view lhs ) nssv_noexcept +{ return rhs.compare( lhs ) <= 0; } + +#else // newer compilers: #define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view >::type @@ -1081,7 +1277,7 @@ nssv_constexpr bool operator>= ( #undef nssv_MSVC_ORDER #undef nssv_BASIC_STRING_VIEW_I -#endif // nssv_CPP11_OR_GREATER +#endif // compiler-dependent approach to comparisons // 24.4.4 Inserters and extractors: @@ -1400,19 +1596,23 @@ struct ParserConfig { ElementNotation notation {ElementNotation::Dot}; }; -} +} // namespace inja -#endif // PANTOR_INJA_CONFIG_HPP +#endif // INCLUDE_INJA_CONFIG_HPP_ // #include "function_storage.hpp" -#ifndef PANTOR_INJA_FUNCTION_STORAGE_HPP -#define PANTOR_INJA_FUNCTION_STORAGE_HPP +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_ +#define INCLUDE_INJA_FUNCTION_STORAGE_HPP_ #include // #include "bytecode.hpp" -#ifndef PANTOR_INJA_BYTECODE_HPP -#define PANTOR_INJA_BYTECODE_HPP +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_BYTECODE_HPP_ +#define INCLUDE_INJA_BYTECODE_HPP_ #include #include @@ -1543,7 +1743,7 @@ struct Bytecode { } // namespace inja -#endif // PANTOR_INJA_BYTECODE_HPP +#endif // INCLUDE_INJA_BYTECODE_HPP_ // #include "string_view.hpp" @@ -1551,7 +1751,7 @@ struct Bytecode { namespace inja { -using namespace nlohmann; +using json = nlohmann::json; using Arguments = std::vector; using CallbackFunction = std::function; @@ -1616,11 +1816,13 @@ class FunctionStorage { } -#endif // PANTOR_INJA_FUNCTION_STORAGE_HPP +#endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_ // #include "parser.hpp" -#ifndef PANTOR_INJA_PARSER_HPP -#define PANTOR_INJA_PARSER_HPP +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_PARSER_HPP_ +#define INCLUDE_INJA_PARSER_HPP_ #include #include @@ -1634,8 +1836,10 @@ class FunctionStorage { // #include "function_storage.hpp" // #include "lexer.hpp" -#ifndef PANTOR_INJA_LEXER_HPP -#define PANTOR_INJA_LEXER_HPP +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_LEXER_HPP_ +#define INCLUDE_INJA_LEXER_HPP_ #include #include @@ -1643,8 +1847,10 @@ class FunctionStorage { // #include "config.hpp" // #include "token.hpp" -#ifndef PANTOR_INJA_TOKEN_HPP -#define PANTOR_INJA_TOKEN_HPP +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_TOKEN_HPP_ +#define INCLUDE_INJA_TOKEN_HPP_ #include @@ -1710,11 +1916,13 @@ struct Token { } -#endif // PANTOR_INJA_TOKEN_HPP +#endif // INCLUDE_INJA_TOKEN_HPP_ // #include "utils.hpp" -#ifndef PANTOR_INJA_UTILS_HPP -#define PANTOR_INJA_UTILS_HPP +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_UTILS_HPP_ +#define INCLUDE_INJA_UTILS_HPP_ #include #include @@ -1761,11 +1969,11 @@ namespace string_view { inline bool starts_with(nonstd::string_view view, nonstd::string_view prefix) { return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0); } -} // namespace string +} // namespace string_view } // namespace inja -#endif // PANTOR_INJA_UTILS_HPP +#endif // INCLUDE_INJA_UTILS_HPP_ @@ -1835,7 +2043,7 @@ class Lexer { inja::string_view::starts_with(open_str, m_config.line_statement)) { m_state = State::LineStart; } else { - m_pos += 1; // wasn't actually an opening sequence + m_pos += 1; // wasn't actually an opening sequence goto again; } @@ -2063,11 +2271,13 @@ class Lexer { } -#endif // PANTOR_INJA_LEXER_HPP +#endif // INCLUDE_INJA_LEXER_HPP_ // #include "template.hpp" -#ifndef PANTOR_INJA_TEMPLATE_HPP -#define PANTOR_INJA_TEMPLATE_HPP +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_TEMPLATE_HPP_ +#define INCLUDE_INJA_TEMPLATE_HPP_ #include #include @@ -2089,9 +2299,9 @@ struct Template { using TemplateStorage = std::map; -} +} // namespace inja -#endif // PANTOR_INJA_TEMPLATE_HPP +#endif // INCLUDE_INJA_TEMPLATE_HPP_ // #include "token.hpp" @@ -2644,11 +2854,13 @@ class Parser { } // namespace inja -#endif // PANTOR_INJA_PARSER_HPP +#endif // INCLUDE_INJA_PARSER_HPP_ // #include "polyfill.hpp" -#ifndef PANTOR_INJA_POLYFILL_HPP -#define PANTOR_INJA_POLYFILL_HPP +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_POLYFILL_HPP_ +#define INCLUDE_INJA_POLYFILL_HPP_ #if __cplusplus < 201402L @@ -2660,48 +2872,51 @@ class Parser { namespace stdinja { - template struct _Unique_if { - typedef std::unique_ptr _Single_object; - }; - template struct _Unique_if { - typedef std::unique_ptr _Unknown_bound; - }; +template struct _Unique_if { + typedef std::unique_ptr _Single_object; +}; - template struct _Unique_if { - typedef void _Known_bound; - }; +template struct _Unique_if { + typedef std::unique_ptr _Unknown_bound; +}; - template - typename _Unique_if::_Single_object - make_unique(Args&&... args) { - return std::unique_ptr(new T(std::forward(args)...)); - } +template struct _Unique_if { + typedef void _Known_bound; +}; - template - typename _Unique_if::_Unknown_bound - make_unique(size_t n) { - typedef typename std::remove_extent::type U; - return std::unique_ptr(new U[n]()); - } - - template - typename _Unique_if::_Known_bound - make_unique(Args&&...) = delete; +template +typename _Unique_if::_Single_object +make_unique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); } +template +typename _Unique_if::_Unknown_bound +make_unique(size_t n) { + typedef typename std::remove_extent::type U; + return std::unique_ptr(new U[n]()); +} + +template +typename _Unique_if::_Known_bound +make_unique(Args&&...) = delete; + +} // namespace stdinja + #else namespace stdinja = std; -#endif // memory */ +#endif // memory */ - -#endif // PANTOR_INJA_POLYFILL_HPP +#endif // INCLUDE_INJA_POLYFILL_HPP_ // #include "renderer.hpp" -#ifndef PANTOR_INJA_RENDERER_HPP -#define PANTOR_INJA_RENDERER_HPP +// Copyright (c) 2019 Pantor. All rights reserved. + +#ifndef INCLUDE_INJA_RENDERER_HPP_ +#define INCLUDE_INJA_RENDERER_HPP_ #include #include @@ -2843,22 +3058,21 @@ class Renderer { enum class Type { Map, Array }; Type loop_type; - nonstd::string_view key_name; // variable name for keys - nonstd::string_view value_name; // variable name for values - json data; // data with loop info added + nonstd::string_view key_name; // variable name for keys + nonstd::string_view value_name; // variable name for values + json data; // data with loop info added - json values; // values to iterate over + json values; // values to iterate over // loop over list - size_t index; // current list index - size_t size; // length of list + size_t index; // current list index + size_t size; // length of list // loop over map using KeyValue = std::pair; using MapValues = std::vector; - MapValues map_values; // values to iterate over - MapValues::iterator map_it; // iterator over values - + MapValues map_values; // values to iterate over + MapValues::iterator map_it; // iterator over values }; std::vector m_loop_stack; @@ -3213,7 +3427,8 @@ class Renderer { for (auto it = level.values.begin(), end = level.values.end(); it != end; ++it) { level.map_values.emplace_back(it.key(), &it.value()); } - std::sort(level.map_values.begin(), level.map_values.end(), [](const LoopLevel::KeyValue& a, const LoopLevel::KeyValue& b) { return a.first < b.first; }); + auto sort_lambda = [](const LoopLevel::KeyValue& a, const LoopLevel::KeyValue& b) { return a.first < b.first; }; + std::sort(level.map_values.begin(), level.map_values.end(), sort_lambda); level.map_it = level.map_values.begin(); } else { if (!level.values.is_array()) { @@ -3281,7 +3496,7 @@ class Renderer { } // namespace inja -#endif // PANTOR_INJA_RENDERER_HPP +#endif // INCLUDE_INJA_RENDERER_HPP_ // #include "string_view.hpp" @@ -3293,91 +3508,71 @@ class Renderer { namespace inja { -using namespace nlohmann; +using json = nlohmann::json; /*! * \brief Class for changing the configuration. */ class Environment { - class Impl { - public: - std::string input_path; - std::string output_path; - - LexerConfig lexer_config; - ParserConfig parser_config; - - FunctionStorage callbacks; - TemplateStorage included_templates; - }; - - std::unique_ptr m_impl; - public: Environment(): Environment("") { } - explicit Environment(const std::string& global_path): m_impl(stdinja::make_unique()) { - m_impl->input_path = global_path; - m_impl->output_path = global_path; - } + explicit Environment(const std::string& global_path): m_input_path(global_path), m_output_path(global_path) {} - explicit Environment(const std::string& input_path, const std::string& output_path): m_impl(stdinja::make_unique()) { - m_impl->input_path = input_path; - m_impl->output_path = output_path; - } + Environment(const std::string& input_path, const std::string& output_path): m_input_path(input_path), m_output_path(output_path) {} /// Sets the opener and closer for template statements void set_statement(const std::string& open, const std::string& close) { - m_impl->lexer_config.statement_open = open; - m_impl->lexer_config.statement_close = close; - m_impl->lexer_config.update_open_chars(); + m_lexer_config.statement_open = open; + m_lexer_config.statement_close = close; + m_lexer_config.update_open_chars(); } /// Sets the opener for template line statements void set_line_statement(const std::string& open) { - m_impl->lexer_config.line_statement = open; - m_impl->lexer_config.update_open_chars(); + m_lexer_config.line_statement = open; + m_lexer_config.update_open_chars(); } /// Sets the opener and closer for template expressions void set_expression(const std::string& open, const std::string& close) { - m_impl->lexer_config.expression_open = open; - m_impl->lexer_config.expression_close = close; - m_impl->lexer_config.update_open_chars(); + m_lexer_config.expression_open = open; + m_lexer_config.expression_close = close; + m_lexer_config.update_open_chars(); } /// Sets the opener and closer for template comments void set_comment(const std::string& open, const std::string& close) { - m_impl->lexer_config.comment_open = open; - m_impl->lexer_config.comment_close = close; - m_impl->lexer_config.update_open_chars(); + m_lexer_config.comment_open = open; + m_lexer_config.comment_close = close; + m_lexer_config.update_open_chars(); } /// Sets whether to remove the first newline after a block void set_trim_blocks(bool trim_blocks) { - m_impl->lexer_config.trim_blocks = trim_blocks; + m_lexer_config.trim_blocks = trim_blocks; } /// Sets whether to strip the spaces and tabs from the start of a line to a block void set_lstrip_blocks(bool lstrip_blocks) { - m_impl->lexer_config.lstrip_blocks = lstrip_blocks; + m_lexer_config.lstrip_blocks = lstrip_blocks; } /// Sets the element notation syntax void set_element_notation(ElementNotation notation) { - m_impl->parser_config.notation = notation; + m_parser_config.notation = notation; } Template parse(nonstd::string_view input) { - Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates); + Parser parser(m_parser_config, m_lexer_config, m_included_templates); return parser.parse(input); } Template parse_template(const std::string& filename) { - Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates); - return parser.parse_template(m_impl->input_path + static_cast(filename)); - } + Parser parser(m_parser_config, m_lexer_config, m_included_templates); + return parser.parse_template(m_input_path + static_cast(filename)); + } std::string render(nonstd::string_view input, const json& data) { return render(parse(input), data); @@ -3390,55 +3585,55 @@ class Environment { } std::string render_file(const std::string& filename, const json& data) { - return render(parse_template(filename), data); - } + return render(parse_template(filename), data); + } std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) { - const json data = load_json(filename_data); - return render_file(filename, data); - } + const json data = load_json(filename_data); + return render_file(filename, data); + } void write(const std::string& filename, const json& data, const std::string& filename_out) { - std::ofstream file(m_impl->output_path + filename_out); - file << render_file(filename, data); - file.close(); - } + std::ofstream file(m_output_path + filename_out); + file << render_file(filename, data); + file.close(); + } void write(const Template& temp, const json& data, const std::string& filename_out) { - std::ofstream file(m_impl->output_path + filename_out); - file << render(temp, data); - file.close(); - } + std::ofstream file(m_output_path + filename_out); + file << render(temp, data); + file.close(); + } - void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) { - const json data = load_json(filename_data); - write(filename, data, filename_out); - } + void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) { + const json data = load_json(filename_data); + write(filename, data, filename_out); + } - void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) { - const json data = load_json(filename_data); - write(temp, data, filename_out); - } + void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) { + const json data = load_json(filename_data); + write(temp, data, filename_out); + } std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) { - Renderer(m_impl->included_templates, m_impl->callbacks).render_to(os, tmpl, data); + Renderer(m_included_templates, m_callbacks).render_to(os, tmpl, data); return os; } std::string load_file(const std::string& filename) { - Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates); - return parser.load_file(m_impl->input_path + filename); - } + Parser parser(m_parser_config, m_lexer_config, m_included_templates); + return parser.load_file(m_input_path + filename); + } json load_json(const std::string& filename) { - std::ifstream file = open_file_or_throw(m_impl->input_path + filename); - json j; - file >> j; - return j; - } + std::ifstream file = open_file_or_throw(m_input_path + filename); + json j; + file >> j; + return j; + } void add_callback(const std::string& name, unsigned int numArgs, const CallbackFunction& callback) { - m_impl->callbacks.add_callback(name, numArgs, callback); + m_callbacks.add_callback(name, numArgs, callback); } /** Includes a template with a given name into the environment. @@ -3446,8 +3641,18 @@ class Environment { * include "" syntax. */ void include_template(const std::string& name, const Template& tmpl) { - m_impl->included_templates[name] = tmpl; + m_included_templates[name] = tmpl; } + + private: + std::string m_input_path; + std::string m_output_path; + + LexerConfig m_lexer_config; + ParserConfig m_parser_config; + + FunctionStorage m_callbacks; + TemplateStorage m_included_templates; }; /*! @@ -3467,7 +3672,7 @@ inline void render_to(std::ostream& os, nonstd::string_view input, const json& d } -#endif // PANTOR_INJA_ENVIRONMENT_HPP +#endif // INCLUDE_INJA_ENVIRONMENT_HPP_ // #include "string_view.hpp" @@ -3479,4 +3684,4 @@ inline void render_to(std::ostream& os, nonstd::string_view input, const json& d -#endif // PANTOR_INJA_HPP +#endif // INCLUDE_INJA_INJA_HPP_ diff --git a/test/unit.cpp b/test/unit.cpp index 96c87b1..fa3e3d5 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -2,3 +2,33 @@ #define CATCH_CONFIG_MAIN #include "catch/catch.hpp" +#include "inja/inja.hpp" + + +using json = nlohmann::json; + + +TEST_CASE("copy-environment") { + inja::Environment env; + env.add_callback("double", 1, [](inja::Arguments& args) { + int number = args.at(0)->get(); + return 2 * number; + }); + + inja::Template t1 = env.parse("{{ double(2) }}"); + env.include_template("tpl", t1); + std::string test_tpl = "{% include \"tpl\" %}"; + + REQUIRE(env.render(test_tpl, json()) == "4"); + + inja::Environment copy(env); + CHECK(copy.render(test_tpl, json()) == "4"); + + // overwrite template in source env + inja::Template t2 = env.parse("{{ double(4) }}"); + env.include_template("tpl", t2); + REQUIRE(env.render(test_tpl, json()) == "8"); + + // template is unchanged in copy + CHECK(copy.render(test_tpl, json()) == "4"); +}