2019-10-01 01:12:13 +02:00
// MIT License
// Copyright (c) 2018 lbersch
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// ---
// Copyright (c) 2009-2018 FIRST
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the FIRST nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
// THIS SOFTWARE IS PROVIDED BY FIRST AND CONTRIBUTORS``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY NONINFRINGEMENT AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FIRST OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2019-04-27 21:50:53 +02:00
# ifndef PANTOR_INJA_HPP
# define PANTOR_INJA_HPP
# include <functional>
# include <iostream>
# include <map>
# include <memory>
# include <sstream>
# include <string>
# include <vector>
# include <nlohmann/json.hpp>
// #include "environment.hpp"
# ifndef PANTOR_INJA_ENVIRONMENT_HPP
# define PANTOR_INJA_ENVIRONMENT_HPP
# include <memory>
# include <fstream>
# include <sstream>
# include <string>
# include <nlohmann/json.hpp>
// #include "config.hpp"
# ifndef PANTOR_INJA_CONFIG_HPP
# define PANTOR_INJA_CONFIG_HPP
# include <functional>
# include <string>
// #include "string_view.hpp"
// Copyright 2017-2019 by Martin Moene
//
// string-view lite, a C++17-like string_view for C++98 and later.
// For more information see https://github.com/martinmoene/string-view-lite
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
# ifndef NONSTD_SV_LITE_H_INCLUDED
# define NONSTD_SV_LITE_H_INCLUDED
# define string_view_lite_MAJOR 1
# define string_view_lite_MINOR 1
# 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)
# define nssv_STRINGIFY( x ) nssv_STRINGIFY_( x )
# define nssv_STRINGIFY_( x ) #x
// string-view lite configuration:
# define nssv_STRING_VIEW_DEFAULT 0
# define nssv_STRING_VIEW_NONSTD 1
# define nssv_STRING_VIEW_STD 2
# if !defined( nssv_CONFIG_SELECT_STRING_VIEW )
# define nssv_CONFIG_SELECT_STRING_VIEW ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD )
# endif
# if defined( nssv_CONFIG_SELECT_STD_STRING_VIEW ) || defined( nssv_CONFIG_SELECT_NONSTD_STRING_VIEW )
# error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_...
# endif
# ifndef nssv_CONFIG_STD_SV_OPERATOR
# define nssv_CONFIG_STD_SV_OPERATOR 0
# endif
# ifndef nssv_CONFIG_USR_SV_OPERATOR
# define nssv_CONFIG_USR_SV_OPERATOR 1
# endif
# ifdef nssv_CONFIG_CONVERSION_STD_STRING
# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING
# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING
# endif
# ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1
# endif
# ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1
# endif
// Control presence of exception handling (try and auto discover):
# ifndef nssv_CONFIG_NO_EXCEPTIONS
# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
# define nssv_CONFIG_NO_EXCEPTIONS 0
# else
# define nssv_CONFIG_NO_EXCEPTIONS 1
# endif
# endif
// C++ language version detection (C++20 is speculative):
// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
# ifndef nssv_CPLUSPLUS
# if defined(_MSVC_LANG ) && !defined(__clang__)
# define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
# else
# define nssv_CPLUSPLUS __cplusplus
# endif
# endif
# define nssv_CPP98_OR_GREATER ( nssv_CPLUSPLUS >= 199711L )
# define nssv_CPP11_OR_GREATER ( nssv_CPLUSPLUS >= 201103L )
# define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L )
# define nssv_CPP14_OR_GREATER ( nssv_CPLUSPLUS >= 201402L )
# define nssv_CPP17_OR_GREATER ( nssv_CPLUSPLUS >= 201703L )
# define nssv_CPP20_OR_GREATER ( nssv_CPLUSPLUS >= 202000L )
// use C++17 std::string_view if available and requested:
# if nssv_CPP17_OR_GREATER && defined(__has_include )
# if __has_include( <string_view> )
# define nssv_HAVE_STD_STRING_VIEW 1
# else
# define nssv_HAVE_STD_STRING_VIEW 0
# endif
# else
# define nssv_HAVE_STD_STRING_VIEW 0
# endif
# define nssv_USES_STD_STRING_VIEW ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) )
# define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW )
# define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH
//
// Use C++17 std::string_view:
//
# if nssv_USES_STD_STRING_VIEW
# include <string_view>
// Extensions for std::string:
# if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
namespace nonstd {
template < class CharT , class Traits , class Allocator = std : : allocator < CharT > >
std : : basic_string < CharT , Traits , Allocator >
to_string ( std : : basic_string_view < CharT , Traits > v , Allocator const & a = Allocator ( ) )
{
return std : : basic_string < CharT , Traits , Allocator > ( v . begin ( ) , v . end ( ) , a ) ;
}
template < class CharT , class Traits , class Allocator >
std : : basic_string_view < CharT , Traits >
to_string_view ( std : : basic_string < CharT , Traits , Allocator > const & s )
{
return std : : basic_string_view < CharT , Traits > ( s . data ( ) , s . size ( ) ) ;
}
// Literal operators sv and _sv:
# if nssv_CONFIG_STD_SV_OPERATOR
using namespace std : : literals : : string_view_literals ;
# endif
# if nssv_CONFIG_USR_SV_OPERATOR
inline namespace literals {
inline namespace string_view_literals {
constexpr std : : string_view operator " " _sv ( const char * str , size_t len ) noexcept // (1)
{
return std : : string_view { str , len } ;
}
constexpr std : : u16string_view operator " " _sv ( const char16_t * str , size_t len ) noexcept // (2)
{
return std : : u16string_view { str , len } ;
}
constexpr std : : u32string_view operator " " _sv ( const char32_t * str , size_t len ) noexcept // (3)
{
return std : : u32string_view { str , len } ;
}
constexpr std : : wstring_view operator " " _sv ( const wchar_t * str , size_t len ) noexcept // (4)
{
return std : : wstring_view { str , len } ;
}
} } // namespace literals::string_view_literals
# endif // nssv_CONFIG_USR_SV_OPERATOR
} // namespace nonstd
# endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
namespace nonstd {
using std : : string_view ;
using std : : wstring_view ;
using std : : u16string_view ;
using std : : u32string_view ;
using std : : basic_string_view ;
// literal "sv" and "_sv", see above
using std : : operator = = ;
using std : : operator ! = ;
using std : : operator < ;
using std : : operator < = ;
using std : : operator > ;
using std : : operator > = ;
using std : : operator < < ;
} // namespace nonstd
# else // nssv_HAVE_STD_STRING_VIEW
//
// Before C++17: use string_view lite:
//
// Compiler versions:
//
// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0)
// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002)
// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003)
// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017)
# if defined(_MSC_VER ) && !defined(__clang__)
# define nssv_COMPILER_MSVC_VER (_MSC_VER )
# define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
# else
# define nssv_COMPILER_MSVC_VER 0
# define nssv_COMPILER_MSVC_VERSION 0
# endif
# 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__)
# else
# define nssv_COMPILER_CLANG_VERSION 0
# endif
# if defined(__GNUC__) && !defined(__clang__)
# define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
# else
# define nssv_COMPILER_GNUC_VERSION 0
# endif
// half-open range [lo..hi):
# define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
// Presence of language and library features:
# ifdef _HAS_CPP0X
# define nssv_HAS_CPP0X _HAS_CPP0X
# else
# define nssv_HAS_CPP0X 0
# endif
// Unless defined otherwise below, consider VC14 as C++11 for variant-lite:
# if nssv_COMPILER_MSVC_VER >= 1900
# undef nssv_CPP11_OR_GREATER
# define nssv_CPP11_OR_GREATER 1
# endif
# define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500)
# define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600)
# define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700)
# define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800)
# define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900)
# define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910)
# define nssv_CPP14_000 (nssv_CPP14_OR_GREATER)
# define nssv_CPP17_000 (nssv_CPP17_OR_GREATER)
// Presence of C++11 language features:
# define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140
# define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140
# define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140
# define nssv_HAVE_NOEXCEPT nssv_CPP11_140
# define nssv_HAVE_NULLPTR nssv_CPP11_100
# define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140
# define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140
# define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140
# 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 ) )
# define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140
# endif
// Presence of C++14 language features:
# define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000
// Presence of C++17 language features:
# define nssv_HAVE_NODISCARD nssv_CPP17_000
// Presence of C++ library features:
# define nssv_HAVE_STD_HASH nssv_CPP11_120
// C++ feature usage:
# if nssv_HAVE_CONSTEXPR_11
# define nssv_constexpr constexpr
# else
# define nssv_constexpr /*constexpr*/
# endif
# if nssv_HAVE_CONSTEXPR_14
# define nssv_constexpr14 constexpr
# else
# define nssv_constexpr14 /*constexpr*/
# endif
# if nssv_HAVE_EXPLICIT_CONVERSION
# define nssv_explicit explicit
# else
# define nssv_explicit /*explicit*/
# endif
# if nssv_HAVE_INLINE_NAMESPACE
# define nssv_inline_ns inline
# else
# define nssv_inline_ns /*inline*/
# endif
# if nssv_HAVE_NOEXCEPT
# define nssv_noexcept noexcept
# else
# define nssv_noexcept /*noexcept*/
# endif
//#if nssv_HAVE_REF_QUALIFIER
//# define nssv_ref_qual &
//# define nssv_refref_qual &&
//#else
//# define nssv_ref_qual /*&*/
//# define nssv_refref_qual /*&&*/
//#endif
# if nssv_HAVE_NULLPTR
# define nssv_nullptr nullptr
# else
# define nssv_nullptr NULL
# endif
# if nssv_HAVE_NODISCARD
# define nssv_nodiscard [[nodiscard]]
# else
# define nssv_nodiscard /*[[nodiscard]]*/
# endif
// Additional includes:
# include <algorithm>
# include <cassert>
# include <iterator>
# include <limits>
# include <ostream>
# include <string> // std::char_traits<>
# if ! nssv_CONFIG_NO_EXCEPTIONS
# include <stdexcept>
# endif
# if nssv_CPP11_OR_GREATER
# include <type_traits>
# endif
// Clang, GNUC, MSVC warning suppression macros:
# if defined(__clang__)
# pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wuser-defined-literals"
# elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wliteral-suffix"
# endif // __clang__
# if nssv_COMPILER_MSVC_VERSION >= 140
# define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]]
# define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) )
# define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes))
# else
# define nssv_SUPPRESS_MSGSL_WARNING(expr)
# define nssv_SUPPRESS_MSVC_WARNING(code, descr)
# define nssv_DISABLE_MSVC_WARNINGS(codes)
# endif
# if defined(__clang__)
# define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop")
# elif defined(__GNUC__)
# define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop")
# elif nssv_COMPILER_MSVC_VERSION >= 140
# define nssv_RESTORE_WARNINGS() __pragma(warning(pop ))
# else
# define nssv_RESTORE_WARNINGS()
# endif
// Suppress the following MSVC (GSL) warnings:
// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not
// start with an underscore are reserved
// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
// use brace initialization, gsl::narrow_cast or gsl::narow
// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
nssv_DISABLE_MSVC_WARNINGS ( 4455 26481 26472 )
//nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" )
//nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix )
namespace nonstd { namespace sv_lite {
template
<
class CharT ,
class Traits = std : : char_traits < CharT >
>
class basic_string_view ;
//
// basic_string_view:
//
template
<
class CharT ,
class Traits /* = std::char_traits<CharT> */
>
class basic_string_view
{
public :
// Member types:
typedef Traits traits_type ;
typedef CharT value_type ;
typedef CharT * pointer ;
typedef CharT const * const_pointer ;
typedef CharT & reference ;
typedef CharT const & const_reference ;
typedef const_pointer iterator ;
typedef const_pointer const_iterator ;
typedef std : : reverse_iterator < const_iterator > reverse_iterator ;
2019-09-30 20:30:42 +02:00
typedef std : : reverse_iterator < const_iterator > const_reverse_iterator ;
2019-04-27 21:50:53 +02:00
typedef std : : size_t size_type ;
typedef std : : ptrdiff_t difference_type ;
// 24.4.2.1 Construction and assignment:
nssv_constexpr basic_string_view ( ) nssv_noexcept
: data_ ( nssv_nullptr )
, size_ ( 0 )
{ }
# if nssv_CPP11_OR_GREATER
nssv_constexpr basic_string_view ( basic_string_view const & other ) nssv_noexcept = default ;
# else
nssv_constexpr basic_string_view ( basic_string_view const & other ) nssv_noexcept
: data_ ( other . data_ )
, size_ ( other . size_ )
{ }
# endif
nssv_constexpr basic_string_view ( CharT const * s , size_type count )
: data_ ( s )
, size_ ( count )
{ }
nssv_constexpr basic_string_view ( CharT const * s )
: data_ ( s )
, size_ ( Traits : : length ( s ) )
{ }
// Assignment:
# if nssv_CPP11_OR_GREATER
nssv_constexpr14 basic_string_view & operator = ( basic_string_view const & other ) nssv_noexcept = default ;
# else
nssv_constexpr14 basic_string_view & operator = ( basic_string_view const & other ) nssv_noexcept
{
data_ = other . data_ ;
size_ = other . size_ ;
return * this ;
}
# endif
// 24.4.2.2 Iterator support:
nssv_constexpr const_iterator begin ( ) const nssv_noexcept { return data_ ; }
nssv_constexpr const_iterator end ( ) const nssv_noexcept { return data_ + size_ ; }
nssv_constexpr const_iterator cbegin ( ) const nssv_noexcept { return begin ( ) ; }
nssv_constexpr const_iterator cend ( ) const nssv_noexcept { return end ( ) ; }
nssv_constexpr const_reverse_iterator rbegin ( ) const nssv_noexcept { return const_reverse_iterator ( end ( ) ) ; }
nssv_constexpr const_reverse_iterator rend ( ) const nssv_noexcept { return const_reverse_iterator ( begin ( ) ) ; }
nssv_constexpr const_reverse_iterator crbegin ( ) const nssv_noexcept { return rbegin ( ) ; }
nssv_constexpr const_reverse_iterator crend ( ) const nssv_noexcept { return rend ( ) ; }
// 24.4.2.3 Capacity:
nssv_constexpr size_type size ( ) const nssv_noexcept { return size_ ; }
nssv_constexpr size_type length ( ) const nssv_noexcept { return size_ ; }
nssv_constexpr size_type max_size ( ) const nssv_noexcept { return ( std : : numeric_limits < size_type > : : max ) ( ) ; }
// since C++20
nssv_nodiscard nssv_constexpr bool empty ( ) const nssv_noexcept
{
return 0 = = size_ ;
}
// 24.4.2.4 Element access:
nssv_constexpr const_reference operator [ ] ( size_type pos ) const
{
return data_at ( pos ) ;
}
nssv_constexpr14 const_reference at ( size_type pos ) const
{
# if nssv_CONFIG_NO_EXCEPTIONS
assert ( pos < size ( ) ) ;
# else
if ( pos > = size ( ) )
{
throw std : : out_of_range ( " nonst::string_view::at() " ) ;
}
# endif
return data_at ( pos ) ;
}
nssv_constexpr const_reference front ( ) const { return data_at ( 0 ) ; }
nssv_constexpr const_reference back ( ) const { return data_at ( size ( ) - 1 ) ; }
nssv_constexpr const_pointer data ( ) const nssv_noexcept { return data_ ; }
// 24.4.2.5 Modifiers:
nssv_constexpr14 void remove_prefix ( size_type n )
{
assert ( n < = size ( ) ) ;
data_ + = n ;
size_ - = n ;
}
nssv_constexpr14 void remove_suffix ( size_type n )
{
assert ( n < = size ( ) ) ;
size_ - = n ;
}
nssv_constexpr14 void swap ( basic_string_view & other ) nssv_noexcept
{
using std : : swap ;
swap ( data_ , other . data_ ) ;
swap ( size_ , other . size_ ) ;
}
// 24.4.2.6 String operations:
size_type copy ( CharT * dest , size_type n , size_type pos = 0 ) const
{
# if nssv_CONFIG_NO_EXCEPTIONS
assert ( pos < = size ( ) ) ;
# else
if ( pos > size ( ) )
{
throw std : : out_of_range ( " nonst::string_view::copy() " ) ;
}
# endif
const size_type rlen = ( std : : min ) ( n , size ( ) - pos ) ;
( void ) Traits : : copy ( dest , data ( ) + pos , rlen ) ;
return rlen ;
}
nssv_constexpr14 basic_string_view substr ( size_type pos = 0 , size_type n = npos ) const
{
# if nssv_CONFIG_NO_EXCEPTIONS
assert ( pos < = size ( ) ) ;
# else
if ( pos > size ( ) )
{
throw std : : out_of_range ( " nonst::string_view::substr() " ) ;
}
# endif
return basic_string_view ( data ( ) + pos , ( std : : min ) ( n , size ( ) - pos ) ) ;
}
// compare(), 6x:
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 ;
}
nssv_constexpr int compare ( size_type pos1 , size_type n1 , basic_string_view other ) const // (2)
{
return substr ( pos1 , n1 ) . compare ( other ) ;
}
nssv_constexpr int compare ( size_type pos1 , size_type n1 , basic_string_view other , size_type pos2 , size_type n2 ) const // (3)
{
return substr ( pos1 , n1 ) . compare ( other . substr ( pos2 , n2 ) ) ;
}
nssv_constexpr int compare ( CharT const * s ) const // (4)
{
return compare ( basic_string_view ( s ) ) ;
}
nssv_constexpr int compare ( size_type pos1 , size_type n1 , CharT const * s ) const // (5)
{
return substr ( pos1 , n1 ) . compare ( basic_string_view ( s ) ) ;
}
nssv_constexpr int compare ( size_type pos1 , size_type n1 , CharT const * s , size_type n2 ) const // (6)
{
return substr ( pos1 , n1 ) . compare ( basic_string_view ( s , n2 ) ) ;
}
// 24.4.2.7 Searching:
// starts_with(), 3x, since C++20:
nssv_constexpr bool starts_with ( basic_string_view v ) const nssv_noexcept // (1)
{
return size ( ) > = v . size ( ) & & compare ( 0 , v . size ( ) , v ) = = 0 ;
}
nssv_constexpr bool starts_with ( CharT c ) const nssv_noexcept // (2)
{
return starts_with ( basic_string_view ( & c , 1 ) ) ;
}
nssv_constexpr bool starts_with ( CharT const * s ) const // (3)
{
return starts_with ( basic_string_view ( s ) ) ;
}
// ends_with(), 3x, since C++20:
nssv_constexpr bool ends_with ( basic_string_view v ) const nssv_noexcept // (1)
{
return size ( ) > = v . size ( ) & & compare ( size ( ) - v . size ( ) , npos , v ) = = 0 ;
}
nssv_constexpr bool ends_with ( CharT c ) const nssv_noexcept // (2)
{
return ends_with ( basic_string_view ( & c , 1 ) ) ;
}
nssv_constexpr bool ends_with ( CharT const * s ) const // (3)
{
return ends_with ( basic_string_view ( s ) ) ;
}
// find(), 4x:
nssv_constexpr14 size_type find ( basic_string_view v , size_type pos = 0 ) const nssv_noexcept // (1)
{
return assert ( v . size ( ) = = 0 | | v . data ( ) ! = nssv_nullptr )
, pos > = size ( )
? npos
: to_pos ( std : : search ( cbegin ( ) + pos , cend ( ) , v . cbegin ( ) , v . cend ( ) , Traits : : eq ) ) ;
}
nssv_constexpr14 size_type find ( CharT c , size_type pos = 0 ) const nssv_noexcept // (2)
{
return find ( basic_string_view ( & c , 1 ) , pos ) ;
}
nssv_constexpr14 size_type find ( CharT const * s , size_type pos , size_type n ) const // (3)
{
return find ( basic_string_view ( s , n ) , pos ) ;
}
nssv_constexpr14 size_type find ( CharT const * s , size_type pos = 0 ) const // (4)
{
return find ( basic_string_view ( s ) , pos ) ;
}
// rfind(), 4x:
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 ) ;
return result ! = last ? size_type ( result - cbegin ( ) ) : npos ;
}
nssv_constexpr14 size_type rfind ( CharT c , size_type pos = npos ) const nssv_noexcept // (2)
{
return rfind ( basic_string_view ( & c , 1 ) , pos ) ;
}
nssv_constexpr14 size_type rfind ( CharT const * s , size_type pos , size_type n ) const // (3)
{
return rfind ( basic_string_view ( s , n ) , pos ) ;
}
nssv_constexpr14 size_type rfind ( CharT const * s , size_type pos = npos ) const // (4)
{
return rfind ( basic_string_view ( s ) , pos ) ;
}
// find_first_of(), 4x:
nssv_constexpr size_type find_first_of ( basic_string_view v , size_type pos = 0 ) const nssv_noexcept // (1)
{
return pos > = size ( )
? npos
: to_pos ( std : : find_first_of ( cbegin ( ) + pos , cend ( ) , v . cbegin ( ) , v . cend ( ) , Traits : : eq ) ) ;
}
nssv_constexpr size_type find_first_of ( CharT c , size_type pos = 0 ) const nssv_noexcept // (2)
{
return find_first_of ( basic_string_view ( & c , 1 ) , pos ) ;
}
nssv_constexpr size_type find_first_of ( CharT const * s , size_type pos , size_type n ) const // (3)
{
return find_first_of ( basic_string_view ( s , n ) , pos ) ;
}
nssv_constexpr size_type find_first_of ( CharT const * s , size_type pos = 0 ) const // (4)
{
return find_first_of ( basic_string_view ( s ) , pos ) ;
}
// find_last_of(), 4x:
nssv_constexpr size_type find_last_of ( basic_string_view v , size_type pos = npos ) const nssv_noexcept // (1)
{
return empty ( )
? npos
: pos > = size ( )
? find_last_of ( v , size ( ) - 1 )
: to_pos ( std : : find_first_of ( const_reverse_iterator ( cbegin ( ) + pos + 1 ) , crend ( ) , v . cbegin ( ) , v . cend ( ) , Traits : : eq ) ) ;
}
nssv_constexpr size_type find_last_of ( CharT c , size_type pos = npos ) const nssv_noexcept // (2)
{
return find_last_of ( basic_string_view ( & c , 1 ) , pos ) ;
}
nssv_constexpr size_type find_last_of ( CharT const * s , size_type pos , size_type count ) const // (3)
{
return find_last_of ( basic_string_view ( s , count ) , pos ) ;
}
nssv_constexpr size_type find_last_of ( CharT const * s , size_type pos = npos ) const // (4)
{
return find_last_of ( basic_string_view ( s ) , pos ) ;
}
// find_first_not_of(), 4x:
nssv_constexpr size_type find_first_not_of ( basic_string_view v , size_type pos = 0 ) const nssv_noexcept // (1)
{
return pos > = size ( )
? npos
: to_pos ( std : : find_if ( cbegin ( ) + pos , cend ( ) , not_in_view ( v ) ) ) ;
}
nssv_constexpr size_type find_first_not_of ( CharT c , size_type pos = 0 ) const nssv_noexcept // (2)
{
return find_first_not_of ( basic_string_view ( & c , 1 ) , pos ) ;
}
nssv_constexpr size_type find_first_not_of ( CharT const * s , size_type pos , size_type count ) const // (3)
{
return find_first_not_of ( basic_string_view ( s , count ) , pos ) ;
}
nssv_constexpr size_type find_first_not_of ( CharT const * s , size_type pos = 0 ) const // (4)
{
return find_first_not_of ( basic_string_view ( s ) , pos ) ;
}
// find_last_not_of(), 4x:
nssv_constexpr size_type find_last_not_of ( basic_string_view v , size_type pos = npos ) const nssv_noexcept // (1)
{
return empty ( )
? npos
: pos > = size ( )
? find_last_not_of ( v , size ( ) - 1 )
: to_pos ( std : : find_if ( const_reverse_iterator ( cbegin ( ) + pos + 1 ) , crend ( ) , not_in_view ( v ) ) ) ;
}
nssv_constexpr size_type find_last_not_of ( CharT c , size_type pos = npos ) const nssv_noexcept // (2)
{
return find_last_not_of ( basic_string_view ( & c , 1 ) , pos ) ;
}
nssv_constexpr size_type find_last_not_of ( CharT const * s , size_type pos , size_type count ) const // (3)
{
return find_last_not_of ( basic_string_view ( s , count ) , pos ) ;
}
nssv_constexpr size_type find_last_not_of ( CharT const * s , size_type pos = npos ) const // (4)
{
return find_last_not_of ( basic_string_view ( s ) , pos ) ;
}
// Constants:
# if nssv_CPP17_OR_GREATER
static nssv_constexpr size_type npos = size_type ( - 1 ) ;
# elif nssv_CPP11_OR_GREATER
enum : size_type { npos = size_type ( - 1 ) } ;
# else
enum { npos = size_type ( - 1 ) } ;
# endif
private :
struct not_in_view
{
const basic_string_view v ;
nssv_constexpr not_in_view ( basic_string_view v ) : v ( v ) { }
nssv_constexpr bool operator ( ) ( CharT c ) const
{
return npos = = v . find_first_of ( c ) ;
}
} ;
nssv_constexpr size_type to_pos ( const_iterator it ) const
{
return it = = cend ( ) ? npos : size_type ( it - cbegin ( ) ) ;
}
nssv_constexpr size_type to_pos ( const_reverse_iterator it ) const
{
return it = = crend ( ) ? npos : size_type ( crend ( ) - it - 1 ) ;
}
nssv_constexpr const_reference data_at ( size_type pos ) const
{
# if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 )
return data_ [ pos ] ;
# else
return assert ( pos < size ( ) ) , data_ [ pos ] ;
# endif
}
private :
const_pointer data_ ;
size_type size_ ;
public :
# if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
template < class Allocator >
basic_string_view ( std : : basic_string < CharT , Traits , Allocator > const & s ) nssv_noexcept
: data_ ( s . data ( ) )
, size_ ( s . size ( ) )
{ }
# if nssv_HAVE_EXPLICIT_CONVERSION
template < class Allocator >
explicit operator std : : basic_string < CharT , Traits , Allocator > ( ) const
{
return to_string ( Allocator ( ) ) ;
}
# endif // nssv_HAVE_EXPLICIT_CONVERSION
# if nssv_CPP11_OR_GREATER
template < class Allocator = std : : allocator < CharT > >
std : : basic_string < CharT , Traits , Allocator >
to_string ( Allocator const & a = Allocator ( ) ) const
{
return std : : basic_string < CharT , Traits , Allocator > ( begin ( ) , end ( ) , a ) ;
}
# else
std : : basic_string < CharT , Traits >
to_string ( ) const
{
return std : : basic_string < CharT , Traits > ( begin ( ) , end ( ) ) ;
}
template < class Allocator >
std : : basic_string < CharT , Traits , Allocator >
to_string ( Allocator const & a ) const
{
return std : : basic_string < CharT , Traits , Allocator > ( begin ( ) , end ( ) , a ) ;
}
# endif // nssv_CPP11_OR_GREATER
# endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
} ;
//
// Non-member functions:
//
// 24.4.3 Non-member comparison functions:
// lexicographically compare two string views (function template):
template < class CharT , class Traits >
nssv_constexpr bool operator = = (
basic_string_view < CharT , Traits > lhs ,
basic_string_view < CharT , Traits > rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) = = 0 ; }
template < class CharT , class Traits >
nssv_constexpr bool operator ! = (
basic_string_view < CharT , Traits > lhs ,
basic_string_view < CharT , Traits > rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) ! = 0 ; }
template < class CharT , class Traits >
nssv_constexpr bool operator < (
basic_string_view < CharT , Traits > lhs ,
basic_string_view < CharT , Traits > rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) < 0 ; }
template < class CharT , class Traits >
nssv_constexpr bool operator < = (
basic_string_view < CharT , Traits > lhs ,
basic_string_view < CharT , Traits > rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) < = 0 ; }
template < class CharT , class Traits >
nssv_constexpr bool operator > (
basic_string_view < CharT , Traits > lhs ,
basic_string_view < CharT , Traits > rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) > 0 ; }
template < class CharT , class Traits >
nssv_constexpr bool operator > = (
basic_string_view < CharT , Traits > lhs ,
basic_string_view < CharT , Traits > rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) > = 0 ; }
// Let S be basic_string_view<CharT, Traits>, and sv be an instance of S.
// Implementations shall provide sufficient additional overloads marked
// 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 )
# define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view<T,U> >::type
# if nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 140, 150 )
# define nssv_MSVC_ORDER(x) , int=x
# else
# define nssv_MSVC_ORDER(x) /*, int=x*/
# endif
// ==
template < class CharT , class Traits nssv_MSVC_ORDER ( 1 ) >
nssv_constexpr bool operator = = (
basic_string_view < CharT , Traits > lhs ,
nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) = = 0 ; }
template < class CharT , class Traits nssv_MSVC_ORDER ( 2 ) >
nssv_constexpr bool operator = = (
nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) lhs ,
basic_string_view < CharT , Traits > rhs ) nssv_noexcept
{ return lhs . size ( ) = = rhs . size ( ) & & lhs . compare ( rhs ) = = 0 ; }
// !=
template < class CharT , class Traits nssv_MSVC_ORDER ( 1 ) >
nssv_constexpr bool operator ! = (
basic_string_view < CharT , Traits > lhs ,
nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) rhs ) nssv_noexcept
{ return lhs . size ( ) ! = rhs . size ( ) | | lhs . compare ( rhs ) ! = 0 ; }
template < class CharT , class Traits nssv_MSVC_ORDER ( 2 ) >
nssv_constexpr bool operator ! = (
nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) lhs ,
basic_string_view < CharT , Traits > rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) ! = 0 ; }
// <
template < class CharT , class Traits nssv_MSVC_ORDER ( 1 ) >
nssv_constexpr bool operator < (
basic_string_view < CharT , Traits > lhs ,
nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) < 0 ; }
template < class CharT , class Traits nssv_MSVC_ORDER ( 2 ) >
nssv_constexpr bool operator < (
nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) lhs ,
basic_string_view < CharT , Traits > rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) < 0 ; }
// <=
template < class CharT , class Traits nssv_MSVC_ORDER ( 1 ) >
nssv_constexpr bool operator < = (
basic_string_view < CharT , Traits > lhs ,
nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) < = 0 ; }
template < class CharT , class Traits nssv_MSVC_ORDER ( 2 ) >
nssv_constexpr bool operator < = (
nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) lhs ,
basic_string_view < CharT , Traits > rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) < = 0 ; }
// >
template < class CharT , class Traits nssv_MSVC_ORDER ( 1 ) >
nssv_constexpr bool operator > (
basic_string_view < CharT , Traits > lhs ,
nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) > 0 ; }
template < class CharT , class Traits nssv_MSVC_ORDER ( 2 ) >
nssv_constexpr bool operator > (
nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) lhs ,
basic_string_view < CharT , Traits > rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) > 0 ; }
// >=
template < class CharT , class Traits nssv_MSVC_ORDER ( 1 ) >
nssv_constexpr bool operator > = (
basic_string_view < CharT , Traits > lhs ,
nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) > = 0 ; }
template < class CharT , class Traits nssv_MSVC_ORDER ( 2 ) >
nssv_constexpr bool operator > = (
nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) lhs ,
basic_string_view < CharT , Traits > rhs ) nssv_noexcept
{ return lhs . compare ( rhs ) > = 0 ; }
# undef nssv_MSVC_ORDER
# undef nssv_BASIC_STRING_VIEW_I
# endif // nssv_CPP11_OR_GREATER
// 24.4.4 Inserters and extractors:
namespace detail {
template < class Stream >
void write_padding ( Stream & os , std : : streamsize n )
{
for ( std : : streamsize i = 0 ; i < n ; + + i )
os . rdbuf ( ) - > sputc ( os . fill ( ) ) ;
}
template < class Stream , class View >
Stream & write_to_stream ( Stream & os , View const & sv )
{
typename Stream : : sentry sentry ( os ) ;
if ( ! os )
return os ;
const std : : streamsize length = static_cast < std : : streamsize > ( sv . length ( ) ) ;
// Whether, and how, to pad:
const bool pad = ( length < os . width ( ) ) ;
const bool left_pad = pad & & ( os . flags ( ) & std : : ios_base : : adjustfield ) = = std : : ios_base : : right ;
if ( left_pad )
write_padding ( os , os . width ( ) - length ) ;
// Write span characters:
os . rdbuf ( ) - > sputn ( sv . begin ( ) , length ) ;
if ( pad & & ! left_pad )
write_padding ( os , os . width ( ) - length ) ;
// Reset output stream width:
os . width ( 0 ) ;
return os ;
}
} // namespace detail
template < class CharT , class Traits >
std : : basic_ostream < CharT , Traits > &
operator < < (
std : : basic_ostream < CharT , Traits > & os ,
basic_string_view < CharT , Traits > sv )
{
return detail : : write_to_stream ( os , sv ) ;
}
// Several typedefs for common character types are provided:
typedef basic_string_view < char > string_view ;
typedef basic_string_view < wchar_t > wstring_view ;
# if nssv_HAVE_WCHAR16_T
typedef basic_string_view < char16_t > u16string_view ;
typedef basic_string_view < char32_t > u32string_view ;
# endif
} } // namespace nonstd::sv_lite
//
// 24.4.6 Suffix for basic_string_view literals:
//
# if nssv_HAVE_USER_DEFINED_LITERALS
namespace nonstd {
nssv_inline_ns namespace literals {
nssv_inline_ns namespace string_view_literals {
# if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
nssv_constexpr nonstd : : sv_lite : : string_view operator " " sv ( const char * str , size_t len ) nssv_noexcept // (1)
{
return nonstd : : sv_lite : : string_view { str , len } ;
}
nssv_constexpr nonstd : : sv_lite : : u16string_view operator " " sv ( const char16_t * str , size_t len ) nssv_noexcept // (2)
{
return nonstd : : sv_lite : : u16string_view { str , len } ;
}
nssv_constexpr nonstd : : sv_lite : : u32string_view operator " " sv ( const char32_t * str , size_t len ) nssv_noexcept // (3)
{
return nonstd : : sv_lite : : u32string_view { str , len } ;
}
nssv_constexpr nonstd : : sv_lite : : wstring_view operator " " sv ( const wchar_t * str , size_t len ) nssv_noexcept // (4)
{
return nonstd : : sv_lite : : wstring_view { str , len } ;
}
# endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
# if nssv_CONFIG_USR_SV_OPERATOR
nssv_constexpr nonstd : : sv_lite : : string_view operator " " _sv ( const char * str , size_t len ) nssv_noexcept // (1)
{
return nonstd : : sv_lite : : string_view { str , len } ;
}
nssv_constexpr nonstd : : sv_lite : : u16string_view operator " " _sv ( const char16_t * str , size_t len ) nssv_noexcept // (2)
{
return nonstd : : sv_lite : : u16string_view { str , len } ;
}
nssv_constexpr nonstd : : sv_lite : : u32string_view operator " " _sv ( const char32_t * str , size_t len ) nssv_noexcept // (3)
{
return nonstd : : sv_lite : : u32string_view { str , len } ;
}
nssv_constexpr nonstd : : sv_lite : : wstring_view operator " " _sv ( const wchar_t * str , size_t len ) nssv_noexcept // (4)
{
return nonstd : : sv_lite : : wstring_view { str , len } ;
}
# endif // nssv_CONFIG_USR_SV_OPERATOR
} } } // namespace nonstd::literals::string_view_literals
# endif
//
// Extensions for std::string:
//
# if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
namespace nonstd {
namespace sv_lite {
// Exclude MSVC 14 (19.00): it yields ambiguous to_string():
# if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140
template < class CharT , class Traits , class Allocator = std : : allocator < CharT > >
std : : basic_string < CharT , Traits , Allocator >
to_string ( basic_string_view < CharT , Traits > v , Allocator const & a = Allocator ( ) )
{
return std : : basic_string < CharT , Traits , Allocator > ( v . begin ( ) , v . end ( ) , a ) ;
}
# else
template < class CharT , class Traits >
std : : basic_string < CharT , Traits >
to_string ( basic_string_view < CharT , Traits > v )
{
return std : : basic_string < CharT , Traits > ( v . begin ( ) , v . end ( ) ) ;
}
template < class CharT , class Traits , class Allocator >
std : : basic_string < CharT , Traits , Allocator >
to_string ( basic_string_view < CharT , Traits > v , Allocator const & a )
{
return std : : basic_string < CharT , Traits , Allocator > ( v . begin ( ) , v . end ( ) , a ) ;
}
# endif // nssv_CPP11_OR_GREATER
template < class CharT , class Traits , class Allocator >
basic_string_view < CharT , Traits >
to_string_view ( std : : basic_string < CharT , Traits , Allocator > const & s )
{
return basic_string_view < CharT , Traits > ( s . data ( ) , s . size ( ) ) ;
}
} } // namespace nonstd::sv_lite
# endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
//
// make types and algorithms available in namespace nonstd:
//
namespace nonstd {
using sv_lite : : basic_string_view ;
using sv_lite : : string_view ;
using sv_lite : : wstring_view ;
# if nssv_HAVE_WCHAR16_T
using sv_lite : : u16string_view ;
# endif
# if nssv_HAVE_WCHAR32_T
using sv_lite : : u32string_view ;
# endif
// literal "sv"
using sv_lite : : operator = = ;
using sv_lite : : operator ! = ;
using sv_lite : : operator < ;
using sv_lite : : operator < = ;
using sv_lite : : operator > ;
using sv_lite : : operator > = ;
using sv_lite : : operator < < ;
# if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
using sv_lite : : to_string ;
using sv_lite : : to_string_view ;
# endif
} // namespace nonstd
// 24.4.5 Hash support (C++11):
// Note: The hash value of a string view object is equal to the hash value of
// the corresponding string object.
# if nssv_HAVE_STD_HASH
# include <functional>
namespace std {
template < >
struct hash < nonstd : : string_view >
{
public :
std : : size_t operator ( ) ( nonstd : : string_view v ) const nssv_noexcept
{
return std : : hash < std : : string > ( ) ( std : : string ( v . data ( ) , v . size ( ) ) ) ;
}
} ;
template < >
struct hash < nonstd : : wstring_view >
{
public :
std : : size_t operator ( ) ( nonstd : : wstring_view v ) const nssv_noexcept
{
return std : : hash < std : : wstring > ( ) ( std : : wstring ( v . data ( ) , v . size ( ) ) ) ;
}
} ;
template < >
struct hash < nonstd : : u16string_view >
{
public :
std : : size_t operator ( ) ( nonstd : : u16string_view v ) const nssv_noexcept
{
return std : : hash < std : : u16string > ( ) ( std : : u16string ( v . data ( ) , v . size ( ) ) ) ;
}
} ;
template < >
struct hash < nonstd : : u32string_view >
{
public :
std : : size_t operator ( ) ( nonstd : : u32string_view v ) const nssv_noexcept
{
return std : : hash < std : : u32string > ( ) ( std : : u32string ( v . data ( ) , v . size ( ) ) ) ;
}
} ;
} // namespace std
# endif // nssv_HAVE_STD_HASH
nssv_RESTORE_WARNINGS ( )
# endif // nssv_HAVE_STD_STRING_VIEW
# endif // NONSTD_SV_LITE_H_INCLUDED
namespace inja {
enum class ElementNotation {
Dot ,
Pointer
} ;
2019-09-30 20:30:42 +02:00
/*!
* \ brief Class for lexer configuration .
*/
2019-04-27 21:50:53 +02:00
struct LexerConfig {
std : : string statement_open { " {% " } ;
std : : string statement_close { " %} " } ;
std : : string line_statement { " ## " } ;
std : : string expression_open { " {{ " } ;
std : : string expression_close { " }} " } ;
std : : string comment_open { " {# " } ;
std : : string comment_close { " #} " } ;
std : : string open_chars { " #{ " } ;
2019-09-30 20:30:42 +02:00
bool trim_blocks { false } ;
bool lstrip_blocks { false } ;
2019-04-27 21:50:53 +02:00
void update_open_chars ( ) {
open_chars = " " ;
if ( open_chars . find ( line_statement [ 0 ] ) = = std : : string : : npos ) {
open_chars + = line_statement [ 0 ] ;
}
if ( open_chars . find ( statement_open [ 0 ] ) = = std : : string : : npos ) {
open_chars + = statement_open [ 0 ] ;
}
if ( open_chars . find ( expression_open [ 0 ] ) = = std : : string : : npos ) {
open_chars + = expression_open [ 0 ] ;
}
if ( open_chars . find ( comment_open [ 0 ] ) = = std : : string : : npos ) {
open_chars + = comment_open [ 0 ] ;
}
}
} ;
2019-09-30 20:30:42 +02:00
/*!
* \ brief Class for parser configuration .
*/
2019-04-27 21:50:53 +02:00
struct ParserConfig {
ElementNotation notation { ElementNotation : : Dot } ;
} ;
}
# endif // PANTOR_INJA_CONFIG_HPP
// #include "function_storage.hpp"
# ifndef PANTOR_INJA_FUNCTION_STORAGE_HPP
# define PANTOR_INJA_FUNCTION_STORAGE_HPP
2019-09-30 20:30:42 +02:00
# include <vector>
2019-04-27 21:50:53 +02:00
// #include "bytecode.hpp"
# ifndef PANTOR_INJA_BYTECODE_HPP
# define PANTOR_INJA_BYTECODE_HPP
2019-09-30 20:30:42 +02:00
# include <string>
2019-04-27 21:50:53 +02:00
# include <utility>
# include <nlohmann/json.hpp>
// #include "string_view.hpp"
namespace inja {
2019-09-30 20:30:42 +02:00
using json = nlohmann : : json ;
2019-04-27 21:50:53 +02:00
struct Bytecode {
enum class Op : uint8_t {
Nop ,
// print StringRef (always immediate)
PrintText ,
// print value
PrintValue ,
// push value onto stack (always immediate)
Push ,
// builtin functions
// result is pushed to stack
// args specify number of arguments
// all functions can take their "last" argument either immediate
// or popped off stack (e.g. if immediate, it's like the immediate was
// just pushed to the stack)
Not ,
And ,
Or ,
In ,
Equal ,
Greater ,
GreaterEqual ,
Less ,
LessEqual ,
2019-09-30 20:30:42 +02:00
At ,
2019-04-27 21:50:53 +02:00
Different ,
DivisibleBy ,
Even ,
First ,
Float ,
Int ,
Last ,
Length ,
Lower ,
Max ,
Min ,
Odd ,
Range ,
Result ,
Round ,
Sort ,
Upper ,
Exists ,
ExistsInObject ,
IsBoolean ,
IsNumber ,
IsInteger ,
IsFloat ,
IsObject ,
IsArray ,
IsString ,
Default ,
// include another template
// value is the template name
Include ,
// callback function
// str is the function name (this means it cannot be a lookup)
// args specify number of arguments
// as with builtin functions, "last" argument can be immediate
Callback ,
// unconditional jump
// args is the index of the bytecode to jump to.
Jump ,
// conditional jump
// value popped off stack is checked for truthyness
// if false, args is the index of the bytecode to jump to.
// if true, no action is taken (falls through)
ConditionalJump ,
// start loop
// value popped off stack is what is iterated over
// args is index of bytecode after end loop (jumped to if iterable is
// empty)
// immediate value is key name (for maps)
// str is value name
StartLoop ,
// end a loop
// args is index of the first bytecode in the loop body
EndLoop ,
} ;
enum Flag {
// location of value for value-taking ops (mask)
ValueMask = 0x03 ,
// pop value off stack
ValuePop = 0x00 ,
// value is immediate rather than on stack
ValueImmediate = 0x01 ,
// lookup immediate str (dot notation)
ValueLookupDot = 0x02 ,
// lookup immediate str (json pointer notation)
ValueLookupPointer = 0x03 ,
} ;
Op op { Op : : Nop } ;
uint32_t args : 30 ;
uint32_t flags : 2 ;
json value ;
std : : string str ;
Bytecode ( ) : args ( 0 ) , flags ( 0 ) { }
explicit Bytecode ( Op op , unsigned int args = 0 ) : op ( op ) , args ( args ) , flags ( 0 ) { }
explicit Bytecode ( Op op , nonstd : : string_view str , unsigned int flags ) : op ( op ) , args ( 0 ) , flags ( flags ) , str ( str ) { }
explicit Bytecode ( Op op , json & & value , unsigned int flags ) : op ( op ) , args ( 0 ) , flags ( flags ) , value ( std : : move ( value ) ) { }
} ;
} // namespace inja
# endif // PANTOR_INJA_BYTECODE_HPP
// #include "string_view.hpp"
namespace inja {
using namespace nlohmann ;
using Arguments = std : : vector < const json * > ;
using CallbackFunction = std : : function < json ( Arguments & args ) > ;
2019-09-30 20:30:42 +02:00
/*!
* \ brief Class for builtin functions and user - defined callbacks .
*/
2019-04-27 21:50:53 +02:00
class FunctionStorage {
public :
void add_builtin ( nonstd : : string_view name , unsigned int num_args , Bytecode : : Op op ) {
auto & data = get_or_new ( name , num_args ) ;
data . op = op ;
}
void add_callback ( nonstd : : string_view name , unsigned int num_args , const CallbackFunction & function ) {
auto & data = get_or_new ( name , num_args ) ;
data . function = function ;
}
Bytecode : : Op find_builtin ( nonstd : : string_view name , unsigned int num_args ) const {
if ( auto ptr = get ( name , num_args ) ) {
return ptr - > op ;
}
return Bytecode : : Op : : Nop ;
}
CallbackFunction find_callback ( nonstd : : string_view name , unsigned int num_args ) const {
if ( auto ptr = get ( name , num_args ) ) {
return ptr - > function ;
}
return nullptr ;
}
private :
struct FunctionData {
unsigned int num_args { 0 } ;
Bytecode : : Op op { Bytecode : : Op : : Nop } ; // for builtins
CallbackFunction function ; // for callbacks
} ;
FunctionData & get_or_new ( nonstd : : string_view name , unsigned int num_args ) {
auto & vec = m_map [ static_cast < std : : string > ( name ) ] ;
for ( auto & i : vec ) {
if ( i . num_args = = num_args ) return i ;
}
vec . emplace_back ( ) ;
vec . back ( ) . num_args = num_args ;
return vec . back ( ) ;
}
const FunctionData * get ( nonstd : : string_view name , unsigned int num_args ) const {
auto it = m_map . find ( static_cast < std : : string > ( name ) ) ;
if ( it = = m_map . end ( ) ) return nullptr ;
for ( auto & & i : it - > second ) {
if ( i . num_args = = num_args ) return & i ;
}
return nullptr ;
}
std : : map < std : : string , std : : vector < FunctionData > > m_map ;
} ;
}
# endif // PANTOR_INJA_FUNCTION_STORAGE_HPP
// #include "parser.hpp"
# ifndef PANTOR_INJA_PARSER_HPP
# define PANTOR_INJA_PARSER_HPP
# include <limits>
2019-09-30 20:30:42 +02:00
# include <string>
# include <utility>
# include <vector>
2019-04-27 21:50:53 +02:00
// #include "bytecode.hpp"
// #include "config.hpp"
// #include "function_storage.hpp"
// #include "lexer.hpp"
# ifndef PANTOR_INJA_LEXER_HPP
# define PANTOR_INJA_LEXER_HPP
# include <cctype>
# include <locale>
// #include "config.hpp"
// #include "token.hpp"
# ifndef PANTOR_INJA_TOKEN_HPP
# define PANTOR_INJA_TOKEN_HPP
2019-09-30 20:30:42 +02:00
# include <string>
2019-04-27 21:50:53 +02:00
// #include "string_view.hpp"
namespace inja {
2019-09-30 20:30:42 +02:00
/*!
* \ brief Helper - class for the inja Parser .
*/
2019-04-27 21:50:53 +02:00
struct Token {
enum class Kind {
Text ,
ExpressionOpen , // {{
ExpressionClose , // }}
LineStatementOpen , // ##
LineStatementClose , // \n
StatementOpen , // {%
StatementClose , // %}
CommentOpen , // {#
CommentClose , // #}
Id , // this, this.foo
Number , // 1, 2, -1, 5.2, -5.3
String , // "this"
Comma , // ,
Colon , // :
LeftParen , // (
RightParen , // )
LeftBracket , // [
RightBracket , // ]
LeftBrace , // {
RightBrace , // }
Equal , // ==
GreaterThan , // >
GreaterEqual , // >=
LessThan , // <
LessEqual , // <=
NotEqual , // !=
Unknown ,
Eof
} kind { Kind : : Unknown } ;
nonstd : : string_view text ;
constexpr Token ( ) = default ;
constexpr Token ( Kind kind , nonstd : : string_view text ) : kind ( kind ) , text ( text ) { }
std : : string describe ( ) const {
switch ( kind ) {
case Kind : : Text :
return " <text> " ;
case Kind : : LineStatementClose :
return " <eol> " ;
case Kind : : Eof :
return " <eof> " ;
default :
return static_cast < std : : string > ( text ) ;
}
}
} ;
}
2019-09-30 20:30:42 +02:00
# endif // PANTOR_INJA_TOKEN_HPP
2019-04-27 21:50:53 +02:00
// #include "utils.hpp"
# ifndef PANTOR_INJA_UTILS_HPP
# define PANTOR_INJA_UTILS_HPP
2019-09-30 20:30:42 +02:00
# include <algorithm>
# include <fstream>
2019-04-27 21:50:53 +02:00
# include <stdexcept>
2019-09-30 20:30:42 +02:00
# include <string>
# include <utility>
2019-04-27 21:50:53 +02:00
// #include "string_view.hpp"
namespace inja {
inline void inja_throw ( const std : : string & type , const std : : string & message ) {
throw std : : runtime_error ( " [inja.exception. " + type + " ] " + message ) ;
}
2019-09-30 20:30:42 +02:00
inline std : : ifstream open_file_or_throw ( const std : : string & path ) {
std : : ifstream file ;
file . exceptions ( std : : ifstream : : failbit | std : : ifstream : : badbit ) ;
try {
file . open ( path ) ;
} catch ( const std : : ios_base : : failure & e ) {
inja_throw ( " file_error " , " failed accessing file at ' " + path + " ' " ) ;
}
return file ;
}
2019-04-27 21:50:53 +02:00
namespace string_view {
inline nonstd : : string_view slice ( nonstd : : string_view view , size_t start , size_t end ) {
start = std : : min ( start , view . size ( ) ) ;
end = std : : min ( std : : max ( start , end ) , view . size ( ) ) ;
2019-09-30 20:30:42 +02:00
return view . substr ( start , end - start ) ; // StringRef(Data + Start, End - Start);
2019-04-27 21:50:53 +02:00
}
inline std : : pair < nonstd : : string_view , nonstd : : string_view > split ( nonstd : : string_view view , char Separator ) {
size_t idx = view . find ( Separator ) ;
if ( idx = = nonstd : : string_view : : npos ) {
return std : : make_pair ( view , nonstd : : string_view ( ) ) ;
}
return std : : make_pair ( slice ( view , 0 , idx ) , slice ( view , idx + 1 , nonstd : : string_view : : npos ) ) ;
}
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 inja
# endif // PANTOR_INJA_UTILS_HPP
namespace inja {
2019-09-30 20:30:42 +02:00
/*!
* \ brief Class for lexing an inja Template .
*/
2019-04-27 21:50:53 +02:00
class Lexer {
enum class State {
Text ,
ExpressionStart ,
ExpressionBody ,
LineStart ,
LineBody ,
StatementStart ,
StatementBody ,
CommentStart ,
CommentBody
} m_state ;
const LexerConfig & m_config ;
nonstd : : string_view m_in ;
size_t m_tok_start ;
size_t m_pos ;
public :
explicit Lexer ( const LexerConfig & config ) : m_config ( config ) { }
void start ( nonstd : : string_view in ) {
m_in = in ;
m_tok_start = 0 ;
m_pos = 0 ;
m_state = State : : Text ;
}
Token scan ( ) {
m_tok_start = m_pos ;
again :
if ( m_tok_start > = m_in . size ( ) ) return make_token ( Token : : Kind : : Eof ) ;
switch ( m_state ) {
default :
case State : : Text : {
// fast-scan to first open character
size_t open_start = m_in . substr ( m_pos ) . find_first_of ( m_config . open_chars ) ;
if ( open_start = = nonstd : : string_view : : npos ) {
// didn't find open, return remaining text as text token
m_pos = m_in . size ( ) ;
return make_token ( Token : : Kind : : Text ) ;
}
m_pos + = open_start ;
// try to match one of the opening sequences, and get the close
nonstd : : string_view open_str = m_in . substr ( m_pos ) ;
2019-09-30 20:30:42 +02:00
bool must_lstrip = false ;
2019-04-27 21:50:53 +02:00
if ( inja : : string_view : : starts_with ( open_str , m_config . expression_open ) ) {
m_state = State : : ExpressionStart ;
} else if ( inja : : string_view : : starts_with ( open_str , m_config . statement_open ) ) {
m_state = State : : StatementStart ;
2019-09-30 20:30:42 +02:00
must_lstrip = m_config . lstrip_blocks ;
2019-04-27 21:50:53 +02:00
} else if ( inja : : string_view : : starts_with ( open_str , m_config . comment_open ) ) {
m_state = State : : CommentStart ;
2019-09-30 20:30:42 +02:00
must_lstrip = m_config . lstrip_blocks ;
2019-04-27 21:50:53 +02:00
} else if ( ( m_pos = = 0 | | m_in [ m_pos - 1 ] = = ' \n ' ) & &
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
goto again ;
}
2019-09-30 20:30:42 +02:00
nonstd : : string_view text = string_view : : slice ( m_in , m_tok_start , m_pos ) ;
if ( must_lstrip )
text = clear_final_line_if_whitespace ( text ) ;
if ( text . empty ( ) ) goto again ; // don't generate empty token
return Token ( Token : : Kind : : Text , text ) ;
2019-04-27 21:50:53 +02:00
}
case State : : ExpressionStart : {
m_state = State : : ExpressionBody ;
m_pos + = m_config . expression_open . size ( ) ;
return make_token ( Token : : Kind : : ExpressionOpen ) ;
}
case State : : LineStart : {
m_state = State : : LineBody ;
m_pos + = m_config . line_statement . size ( ) ;
return make_token ( Token : : Kind : : LineStatementOpen ) ;
}
case State : : StatementStart : {
m_state = State : : StatementBody ;
m_pos + = m_config . statement_open . size ( ) ;
return make_token ( Token : : Kind : : StatementOpen ) ;
}
case State : : CommentStart : {
m_state = State : : CommentBody ;
m_pos + = m_config . comment_open . size ( ) ;
return make_token ( Token : : Kind : : CommentOpen ) ;
}
case State : : ExpressionBody :
return scan_body ( m_config . expression_close , Token : : Kind : : ExpressionClose ) ;
case State : : LineBody :
return scan_body ( " \n " , Token : : Kind : : LineStatementClose ) ;
case State : : StatementBody :
2019-09-30 20:30:42 +02:00
return scan_body ( m_config . statement_close , Token : : Kind : : StatementClose , m_config . trim_blocks ) ;
2019-04-27 21:50:53 +02:00
case State : : CommentBody : {
// fast-scan to comment close
size_t end = m_in . substr ( m_pos ) . find ( m_config . comment_close ) ;
if ( end = = nonstd : : string_view : : npos ) {
m_pos = m_in . size ( ) ;
return make_token ( Token : : Kind : : Eof ) ;
}
// return the entire comment in the close token
m_state = State : : Text ;
m_pos + = end + m_config . comment_close . size ( ) ;
2019-09-30 20:30:42 +02:00
Token tok = make_token ( Token : : Kind : : CommentClose ) ;
if ( m_config . trim_blocks )
skip_newline ( ) ;
return tok ;
2019-04-27 21:50:53 +02:00
}
}
}
const LexerConfig & get_config ( ) const { return m_config ; }
private :
2019-09-30 20:30:42 +02:00
Token scan_body ( nonstd : : string_view close , Token : : Kind closeKind , bool trim = false ) {
2019-04-27 21:50:53 +02:00
again :
// skip whitespace (except for \n as it might be a close)
if ( m_tok_start > = m_in . size ( ) ) return make_token ( Token : : Kind : : Eof ) ;
char ch = m_in [ m_tok_start ] ;
if ( ch = = ' ' | | ch = = ' \t ' | | ch = = ' \r ' ) {
m_tok_start + = 1 ;
goto again ;
}
// check for close
if ( inja : : string_view : : starts_with ( m_in . substr ( m_tok_start ) , close ) ) {
m_state = State : : Text ;
m_pos = m_tok_start + close . size ( ) ;
2019-09-30 20:30:42 +02:00
Token tok = make_token ( closeKind ) ;
if ( trim )
skip_newline ( ) ;
return tok ;
2019-04-27 21:50:53 +02:00
}
// skip \n
if ( ch = = ' \n ' ) {
m_tok_start + = 1 ;
goto again ;
}
m_pos = m_tok_start + 1 ;
if ( std : : isalpha ( ch ) ) return scan_id ( ) ;
switch ( ch ) {
case ' , ' :
return make_token ( Token : : Kind : : Comma ) ;
case ' : ' :
return make_token ( Token : : Kind : : Colon ) ;
case ' ( ' :
return make_token ( Token : : Kind : : LeftParen ) ;
case ' ) ' :
return make_token ( Token : : Kind : : RightParen ) ;
case ' [ ' :
return make_token ( Token : : Kind : : LeftBracket ) ;
case ' ] ' :
return make_token ( Token : : Kind : : RightBracket ) ;
case ' { ' :
return make_token ( Token : : Kind : : LeftBrace ) ;
case ' } ' :
return make_token ( Token : : Kind : : RightBrace ) ;
case ' > ' :
if ( m_pos < m_in . size ( ) & & m_in [ m_pos ] = = ' = ' ) {
m_pos + = 1 ;
return make_token ( Token : : Kind : : GreaterEqual ) ;
}
return make_token ( Token : : Kind : : GreaterThan ) ;
case ' < ' :
if ( m_pos < m_in . size ( ) & & m_in [ m_pos ] = = ' = ' ) {
m_pos + = 1 ;
return make_token ( Token : : Kind : : LessEqual ) ;
}
return make_token ( Token : : Kind : : LessThan ) ;
case ' = ' :
if ( m_pos < m_in . size ( ) & & m_in [ m_pos ] = = ' = ' ) {
m_pos + = 1 ;
return make_token ( Token : : Kind : : Equal ) ;
}
return make_token ( Token : : Kind : : Unknown ) ;
case ' ! ' :
if ( m_pos < m_in . size ( ) & & m_in [ m_pos ] = = ' = ' ) {
m_pos + = 1 ;
return make_token ( Token : : Kind : : NotEqual ) ;
}
return make_token ( Token : : Kind : : Unknown ) ;
case ' \" ' :
return scan_string ( ) ;
case ' 0 ' :
case ' 1 ' :
case ' 2 ' :
case ' 3 ' :
case ' 4 ' :
case ' 5 ' :
case ' 6 ' :
case ' 7 ' :
case ' 8 ' :
case ' 9 ' :
case ' - ' :
return scan_number ( ) ;
case ' _ ' :
return scan_id ( ) ;
default :
return make_token ( Token : : Kind : : Unknown ) ;
}
}
Token scan_id ( ) {
for ( ; ; ) {
if ( m_pos > = m_in . size ( ) ) {
break ;
}
char ch = m_in [ m_pos ] ;
if ( ! std : : isalnum ( ch ) & & ch ! = ' . ' & & ch ! = ' / ' & & ch ! = ' _ ' & & ch ! = ' - ' ) {
break ;
}
m_pos + = 1 ;
}
return make_token ( Token : : Kind : : Id ) ;
}
Token scan_number ( ) {
for ( ; ; ) {
if ( m_pos > = m_in . size ( ) ) {
break ;
}
char ch = m_in [ m_pos ] ;
// be very permissive in lexer (we'll catch errors when conversion happens)
if ( ! std : : isdigit ( ch ) & & ch ! = ' . ' & & ch ! = ' e ' & & ch ! = ' E ' & & ch ! = ' + ' & & ch ! = ' - ' ) {
break ;
}
m_pos + = 1 ;
}
return make_token ( Token : : Kind : : Number ) ;
}
Token scan_string ( ) {
bool escape { false } ;
for ( ; ; ) {
if ( m_pos > = m_in . size ( ) ) break ;
char ch = m_in [ m_pos + + ] ;
if ( ch = = ' \\ ' ) {
escape = true ;
} else if ( ! escape & & ch = = m_in [ m_tok_start ] ) {
break ;
} else {
escape = false ;
}
}
return make_token ( Token : : Kind : : String ) ;
}
Token make_token ( Token : : Kind kind ) const {
return Token ( kind , string_view : : slice ( m_in , m_tok_start , m_pos ) ) ;
}
2019-09-30 20:30:42 +02:00
void skip_newline ( ) {
if ( m_pos < m_in . size ( ) ) {
char ch = m_in [ m_pos ] ;
if ( ch = = ' \n ' )
m_pos + = 1 ;
else if ( ch = = ' \r ' ) {
m_pos + = 1 ;
if ( m_pos < m_in . size ( ) & & m_in [ m_pos ] = = ' \n ' )
m_pos + = 1 ;
}
}
}
static nonstd : : string_view clear_final_line_if_whitespace ( nonstd : : string_view text )
{
nonstd : : string_view result = text ;
while ( ! result . empty ( ) ) {
char ch = result . back ( ) ;
if ( ch = = ' ' | | ch = = ' \t ' )
result . remove_suffix ( 1 ) ;
else if ( ch = = ' \n ' | | ch = = ' \r ' )
break ;
else
return text ;
}
return result ;
}
2019-04-27 21:50:53 +02:00
} ;
}
# endif // PANTOR_INJA_LEXER_HPP
// #include "template.hpp"
# ifndef PANTOR_INJA_TEMPLATE_HPP
# define PANTOR_INJA_TEMPLATE_HPP
2019-09-30 20:30:42 +02:00
# include <map>
2019-04-27 21:50:53 +02:00
# include <string>
# include <vector>
// #include "bytecode.hpp"
namespace inja {
2019-09-30 20:30:42 +02:00
/*!
* \ brief The main inja Template .
*/
2019-04-27 21:50:53 +02:00
struct Template {
std : : vector < Bytecode > bytecodes ;
std : : string content ;
} ;
using TemplateStorage = std : : map < std : : string , Template > ;
}
2019-09-30 20:30:42 +02:00
# endif // PANTOR_INJA_TEMPLATE_HPP
2019-04-27 21:50:53 +02:00
// #include "token.hpp"
// #include "utils.hpp"
# include <nlohmann/json.hpp>
namespace inja {
class ParserStatic {
ParserStatic ( ) {
2019-09-30 20:30:42 +02:00
functions . add_builtin ( " at " , 2 , Bytecode : : Op : : At ) ;
2019-04-27 21:50:53 +02:00
functions . add_builtin ( " default " , 2 , Bytecode : : Op : : Default ) ;
functions . add_builtin ( " divisibleBy " , 2 , Bytecode : : Op : : DivisibleBy ) ;
functions . add_builtin ( " even " , 1 , Bytecode : : Op : : Even ) ;
functions . add_builtin ( " first " , 1 , Bytecode : : Op : : First ) ;
functions . add_builtin ( " float " , 1 , Bytecode : : Op : : Float ) ;
functions . add_builtin ( " int " , 1 , Bytecode : : Op : : Int ) ;
functions . add_builtin ( " last " , 1 , Bytecode : : Op : : Last ) ;
functions . add_builtin ( " length " , 1 , Bytecode : : Op : : Length ) ;
functions . add_builtin ( " lower " , 1 , Bytecode : : Op : : Lower ) ;
functions . add_builtin ( " max " , 1 , Bytecode : : Op : : Max ) ;
functions . add_builtin ( " min " , 1 , Bytecode : : Op : : Min ) ;
functions . add_builtin ( " odd " , 1 , Bytecode : : Op : : Odd ) ;
functions . add_builtin ( " range " , 1 , Bytecode : : Op : : Range ) ;
functions . add_builtin ( " round " , 2 , Bytecode : : Op : : Round ) ;
functions . add_builtin ( " sort " , 1 , Bytecode : : Op : : Sort ) ;
functions . add_builtin ( " upper " , 1 , Bytecode : : Op : : Upper ) ;
functions . add_builtin ( " exists " , 1 , Bytecode : : Op : : Exists ) ;
functions . add_builtin ( " existsIn " , 2 , Bytecode : : Op : : ExistsInObject ) ;
functions . add_builtin ( " isBoolean " , 1 , Bytecode : : Op : : IsBoolean ) ;
functions . add_builtin ( " isNumber " , 1 , Bytecode : : Op : : IsNumber ) ;
functions . add_builtin ( " isInteger " , 1 , Bytecode : : Op : : IsInteger ) ;
functions . add_builtin ( " isFloat " , 1 , Bytecode : : Op : : IsFloat ) ;
functions . add_builtin ( " isObject " , 1 , Bytecode : : Op : : IsObject ) ;
functions . add_builtin ( " isArray " , 1 , Bytecode : : Op : : IsArray ) ;
functions . add_builtin ( " isString " , 1 , Bytecode : : Op : : IsString ) ;
}
public :
ParserStatic ( const ParserStatic & ) = delete ;
ParserStatic & operator = ( const ParserStatic & ) = delete ;
static const ParserStatic & get_instance ( ) {
static ParserStatic inst ;
return inst ;
}
FunctionStorage functions ;
} ;
2019-09-30 20:30:42 +02:00
/*!
* \ brief Class for parsing an inja Template .
*/
2019-04-27 21:50:53 +02:00
class Parser {
public :
explicit Parser ( const ParserConfig & parser_config , const LexerConfig & lexer_config , TemplateStorage & included_templates ) : m_config ( parser_config ) , m_lexer ( lexer_config ) , m_included_templates ( included_templates ) , m_static ( ParserStatic : : get_instance ( ) ) { }
bool parse_expression ( Template & tmpl ) {
if ( ! parse_expression_and ( tmpl ) ) return false ;
2019-09-30 20:30:42 +02:00
if ( m_tok . kind ! = Token : : Kind : : Id | | m_tok . text ! = static_cast < decltype ( m_tok . text ) > ( " or " ) ) return true ;
2019-04-27 21:50:53 +02:00
get_next_token ( ) ;
if ( ! parse_expression_and ( tmpl ) ) return false ;
append_function ( tmpl , Bytecode : : Op : : Or , 2 ) ;
return true ;
}
bool parse_expression_and ( Template & tmpl ) {
if ( ! parse_expression_not ( tmpl ) ) return false ;
2019-09-30 20:30:42 +02:00
if ( m_tok . kind ! = Token : : Kind : : Id | | m_tok . text ! = static_cast < decltype ( m_tok . text ) > ( " and " ) ) return true ;
2019-04-27 21:50:53 +02:00
get_next_token ( ) ;
if ( ! parse_expression_not ( tmpl ) ) return false ;
append_function ( tmpl , Bytecode : : Op : : And , 2 ) ;
return true ;
}
bool parse_expression_not ( Template & tmpl ) {
2019-09-30 20:30:42 +02:00
if ( m_tok . kind = = Token : : Kind : : Id & & m_tok . text = = static_cast < decltype ( m_tok . text ) > ( " not " ) ) {
2019-04-27 21:50:53 +02:00
get_next_token ( ) ;
if ( ! parse_expression_not ( tmpl ) ) return false ;
append_function ( tmpl , Bytecode : : Op : : Not , 1 ) ;
return true ;
} else {
return parse_expression_comparison ( tmpl ) ;
}
}
bool parse_expression_comparison ( Template & tmpl ) {
if ( ! parse_expression_datum ( tmpl ) ) return false ;
Bytecode : : Op op ;
switch ( m_tok . kind ) {
case Token : : Kind : : Id :
2019-09-30 20:30:42 +02:00
if ( m_tok . text = = static_cast < decltype ( m_tok . text ) > ( " in " ) )
2019-04-27 21:50:53 +02:00
op = Bytecode : : Op : : In ;
else
return true ;
break ;
case Token : : Kind : : Equal :
op = Bytecode : : Op : : Equal ;
break ;
case Token : : Kind : : GreaterThan :
op = Bytecode : : Op : : Greater ;
break ;
case Token : : Kind : : LessThan :
op = Bytecode : : Op : : Less ;
break ;
case Token : : Kind : : LessEqual :
op = Bytecode : : Op : : LessEqual ;
break ;
case Token : : Kind : : GreaterEqual :
op = Bytecode : : Op : : GreaterEqual ;
break ;
case Token : : Kind : : NotEqual :
op = Bytecode : : Op : : Different ;
break ;
default :
return true ;
}
get_next_token ( ) ;
if ( ! parse_expression_datum ( tmpl ) ) return false ;
append_function ( tmpl , op , 2 ) ;
return true ;
}
bool parse_expression_datum ( Template & tmpl ) {
nonstd : : string_view json_first ;
size_t bracket_level = 0 ;
size_t brace_level = 0 ;
for ( ; ; ) {
switch ( m_tok . kind ) {
case Token : : Kind : : LeftParen : {
get_next_token ( ) ;
if ( ! parse_expression ( tmpl ) ) return false ;
if ( m_tok . kind ! = Token : : Kind : : RightParen ) {
inja_throw ( " parser_error " , " unmatched '(' " ) ;
}
get_next_token ( ) ;
return true ;
}
case Token : : Kind : : Id :
get_peek_token ( ) ;
if ( m_peek_tok . kind = = Token : : Kind : : LeftParen ) {
// function call, parse arguments
Token func_token = m_tok ;
get_next_token ( ) ; // id
get_next_token ( ) ; // leftParen
unsigned int num_args = 0 ;
if ( m_tok . kind = = Token : : Kind : : RightParen ) {
// no args
get_next_token ( ) ;
} else {
for ( ; ; ) {
if ( ! parse_expression ( tmpl ) ) {
inja_throw ( " parser_error " , " expected expression, got ' " + m_tok . describe ( ) + " ' " ) ;
}
num_args + = 1 ;
if ( m_tok . kind = = Token : : Kind : : RightParen ) {
get_next_token ( ) ;
break ;
}
if ( m_tok . kind ! = Token : : Kind : : Comma ) {
inja_throw ( " parser_error " , " expected ')' or ',', got ' " + m_tok . describe ( ) + " ' " ) ;
}
get_next_token ( ) ;
}
}
auto op = m_static . functions . find_builtin ( func_token . text , num_args ) ;
if ( op ! = Bytecode : : Op : : Nop ) {
// swap arguments for default(); see comment in RenderTo()
if ( op = = Bytecode : : Op : : Default )
std : : swap ( tmpl . bytecodes . back ( ) , * ( tmpl . bytecodes . rbegin ( ) + 1 ) ) ;
append_function ( tmpl , op , num_args ) ;
return true ;
} else {
append_callback ( tmpl , func_token . text , num_args ) ;
return true ;
}
2019-09-30 20:30:42 +02:00
} else if ( m_tok . text = = static_cast < decltype ( m_tok . text ) > ( " true " ) | |
m_tok . text = = static_cast < decltype ( m_tok . text ) > ( " false " ) | |
m_tok . text = = static_cast < decltype ( m_tok . text ) > ( " null " ) ) {
2019-04-27 21:50:53 +02:00
// true, false, null are json literals
if ( brace_level = = 0 & & bracket_level = = 0 ) {
json_first = m_tok . text ;
goto returnJson ;
}
break ;
} else {
// normal literal (json read)
tmpl . bytecodes . emplace_back (
Bytecode : : Op : : Push , m_tok . text ,
m_config . notation = = ElementNotation : : Pointer ? Bytecode : : Flag : : ValueLookupPointer : Bytecode : : Flag : : ValueLookupDot ) ;
get_next_token ( ) ;
return true ;
}
// json passthrough
case Token : : Kind : : Number :
case Token : : Kind : : String :
if ( brace_level = = 0 & & bracket_level = = 0 ) {
json_first = m_tok . text ;
goto returnJson ;
}
break ;
case Token : : Kind : : Comma :
case Token : : Kind : : Colon :
if ( brace_level = = 0 & & bracket_level = = 0 ) {
inja_throw ( " parser_error " , " unexpected token ' " + m_tok . describe ( ) + " ' " ) ;
}
break ;
case Token : : Kind : : LeftBracket :
if ( brace_level = = 0 & & bracket_level = = 0 ) {
json_first = m_tok . text ;
}
bracket_level + = 1 ;
break ;
case Token : : Kind : : LeftBrace :
if ( brace_level = = 0 & & bracket_level = = 0 ) {
json_first = m_tok . text ;
}
brace_level + = 1 ;
break ;
case Token : : Kind : : RightBracket :
if ( bracket_level = = 0 ) {
inja_throw ( " parser_error " , " unexpected ']' " ) ;
}
- - bracket_level ;
if ( brace_level = = 0 & & bracket_level = = 0 ) goto returnJson ;
break ;
case Token : : Kind : : RightBrace :
if ( brace_level = = 0 ) {
inja_throw ( " parser_error " , " unexpected '}' " ) ;
}
- - brace_level ;
if ( brace_level = = 0 & & bracket_level = = 0 ) goto returnJson ;
break ;
default :
if ( brace_level ! = 0 ) {
inja_throw ( " parser_error " , " unmatched '{' " ) ;
}
if ( bracket_level ! = 0 ) {
inja_throw ( " parser_error " , " unmatched '[' " ) ;
}
return false ;
}
get_next_token ( ) ;
}
returnJson :
// bridge across all intermediate tokens
nonstd : : string_view json_text ( json_first . data ( ) , m_tok . text . data ( ) - json_first . data ( ) + m_tok . text . size ( ) ) ;
tmpl . bytecodes . emplace_back ( Bytecode : : Op : : Push , json : : parse ( json_text ) , Bytecode : : Flag : : ValueImmediate ) ;
get_next_token ( ) ;
return true ;
}
bool parse_statement ( Template & tmpl , nonstd : : string_view path ) {
if ( m_tok . kind ! = Token : : Kind : : Id ) return false ;
2019-09-30 20:30:42 +02:00
if ( m_tok . text = = static_cast < decltype ( m_tok . text ) > ( " if " ) ) {
2019-04-27 21:50:53 +02:00
get_next_token ( ) ;
// evaluate expression
if ( ! parse_expression ( tmpl ) ) return false ;
// start a new if block on if stack
m_if_stack . emplace_back ( tmpl . bytecodes . size ( ) ) ;
// conditional jump; destination will be filled in by else or endif
tmpl . bytecodes . emplace_back ( Bytecode : : Op : : ConditionalJump ) ;
2019-09-30 20:30:42 +02:00
} else if ( m_tok . text = = static_cast < decltype ( m_tok . text ) > ( " endif " ) ) {
2019-04-27 21:50:53 +02:00
if ( m_if_stack . empty ( ) ) {
inja_throw ( " parser_error " , " endif without matching if " ) ;
}
auto & if_data = m_if_stack . back ( ) ;
get_next_token ( ) ;
// previous conditional jump jumps here
if ( if_data . prev_cond_jump ! = std : : numeric_limits < unsigned int > : : max ( ) ) {
tmpl . bytecodes [ if_data . prev_cond_jump ] . args = tmpl . bytecodes . size ( ) ;
}
// update all previous unconditional jumps to here
for ( unsigned int i : if_data . uncond_jumps ) {
tmpl . bytecodes [ i ] . args = tmpl . bytecodes . size ( ) ;
}
// pop if stack
m_if_stack . pop_back ( ) ;
2019-09-30 20:30:42 +02:00
} else if ( m_tok . text = = static_cast < decltype ( m_tok . text ) > ( " else " ) ) {
2019-04-27 21:50:53 +02:00
if ( m_if_stack . empty ( ) )
inja_throw ( " parser_error " , " else without matching if " ) ;
auto & if_data = m_if_stack . back ( ) ;
get_next_token ( ) ;
// end previous block with unconditional jump to endif; destination will be
// filled in by endif
if_data . uncond_jumps . push_back ( tmpl . bytecodes . size ( ) ) ;
tmpl . bytecodes . emplace_back ( Bytecode : : Op : : Jump ) ;
// previous conditional jump jumps here
tmpl . bytecodes [ if_data . prev_cond_jump ] . args = tmpl . bytecodes . size ( ) ;
if_data . prev_cond_jump = std : : numeric_limits < unsigned int > : : max ( ) ;
// chained else if
2019-09-30 20:30:42 +02:00
if ( m_tok . kind = = Token : : Kind : : Id & & m_tok . text = = static_cast < decltype ( m_tok . text ) > ( " if " ) ) {
2019-04-27 21:50:53 +02:00
get_next_token ( ) ;
// evaluate expression
if ( ! parse_expression ( tmpl ) ) return false ;
// update "previous jump"
if_data . prev_cond_jump = tmpl . bytecodes . size ( ) ;
// conditional jump; destination will be filled in by else or endif
tmpl . bytecodes . emplace_back ( Bytecode : : Op : : ConditionalJump ) ;
}
2019-09-30 20:30:42 +02:00
} else if ( m_tok . text = = static_cast < decltype ( m_tok . text ) > ( " for " ) ) {
2019-04-27 21:50:53 +02:00
get_next_token ( ) ;
// options: for a in arr; for a, b in obj
if ( m_tok . kind ! = Token : : Kind : : Id )
inja_throw ( " parser_error " , " expected id, got ' " + m_tok . describe ( ) + " ' " ) ;
Token value_token = m_tok ;
get_next_token ( ) ;
Token key_token ;
if ( m_tok . kind = = Token : : Kind : : Comma ) {
get_next_token ( ) ;
if ( m_tok . kind ! = Token : : Kind : : Id )
inja_throw ( " parser_error " , " expected id, got ' " + m_tok . describe ( ) + " ' " ) ;
key_token = std : : move ( value_token ) ;
value_token = m_tok ;
get_next_token ( ) ;
}
2019-09-30 20:30:42 +02:00
if ( m_tok . kind ! = Token : : Kind : : Id | | m_tok . text ! = static_cast < decltype ( m_tok . text ) > ( " in " ) )
2019-04-27 21:50:53 +02:00
inja_throw ( " parser_error " ,
" expected 'in', got ' " + m_tok . describe ( ) + " ' " ) ;
get_next_token ( ) ;
if ( ! parse_expression ( tmpl ) ) return false ;
m_loop_stack . push_back ( tmpl . bytecodes . size ( ) ) ;
tmpl . bytecodes . emplace_back ( Bytecode : : Op : : StartLoop ) ;
if ( ! key_token . text . empty ( ) ) {
tmpl . bytecodes . back ( ) . value = key_token . text ;
}
tmpl . bytecodes . back ( ) . str = static_cast < std : : string > ( value_token . text ) ;
2019-09-30 20:30:42 +02:00
} else if ( m_tok . text = = static_cast < decltype ( m_tok . text ) > ( " endfor " ) ) {
2019-04-27 21:50:53 +02:00
get_next_token ( ) ;
if ( m_loop_stack . empty ( ) ) {
inja_throw ( " parser_error " , " endfor without matching for " ) ;
}
// update loop with EndLoop index (for empty case)
tmpl . bytecodes [ m_loop_stack . back ( ) ] . args = tmpl . bytecodes . size ( ) ;
tmpl . bytecodes . emplace_back ( Bytecode : : Op : : EndLoop ) ;
tmpl . bytecodes . back ( ) . args = m_loop_stack . back ( ) + 1 ; // loop body
m_loop_stack . pop_back ( ) ;
2019-09-30 20:30:42 +02:00
} else if ( m_tok . text = = static_cast < decltype ( m_tok . text ) > ( " include " ) ) {
2019-04-27 21:50:53 +02:00
get_next_token ( ) ;
if ( m_tok . kind ! = Token : : Kind : : String ) {
inja_throw ( " parser_error " , " expected string, got ' " + m_tok . describe ( ) + " ' " ) ;
}
// build the relative path
json json_name = json : : parse ( m_tok . text ) ;
std : : string pathname = static_cast < std : : string > ( path ) ;
pathname + = json_name . get_ref < const std : : string & > ( ) ;
if ( pathname . compare ( 0 , 2 , " ./ " ) = = 0 ) {
pathname . erase ( 0 , 2 ) ;
}
// sys::path::remove_dots(pathname, true, sys::path::Style::posix);
2019-09-30 20:30:42 +02:00
if ( m_included_templates . find ( pathname ) = = m_included_templates . end ( ) ) {
Template include_template = parse_template ( pathname ) ;
m_included_templates . emplace ( pathname , include_template ) ;
}
2019-04-27 21:50:53 +02:00
// generate a reference bytecode
tmpl . bytecodes . emplace_back ( Bytecode : : Op : : Include , json ( pathname ) , Bytecode : : Flag : : ValueImmediate ) ;
get_next_token ( ) ;
} else {
return false ;
}
return true ;
}
void append_function ( Template & tmpl , Bytecode : : Op op , unsigned int num_args ) {
// we can merge with back-to-back push
if ( ! tmpl . bytecodes . empty ( ) ) {
Bytecode & last = tmpl . bytecodes . back ( ) ;
if ( last . op = = Bytecode : : Op : : Push ) {
last . op = op ;
last . args = num_args ;
return ;
}
}
// otherwise just add it to the end
tmpl . bytecodes . emplace_back ( op , num_args ) ;
}
void append_callback ( Template & tmpl , nonstd : : string_view name , unsigned int num_args ) {
// we can merge with back-to-back push value (not lookup)
if ( ! tmpl . bytecodes . empty ( ) ) {
Bytecode & last = tmpl . bytecodes . back ( ) ;
if ( last . op = = Bytecode : : Op : : Push & &
( last . flags & Bytecode : : Flag : : ValueMask ) = = Bytecode : : Flag : : ValueImmediate ) {
last . op = Bytecode : : Op : : Callback ;
last . args = num_args ;
last . str = static_cast < std : : string > ( name ) ;
return ;
}
}
// otherwise just add it to the end
tmpl . bytecodes . emplace_back ( Bytecode : : Op : : Callback , num_args ) ;
tmpl . bytecodes . back ( ) . str = static_cast < std : : string > ( name ) ;
}
void parse_into ( Template & tmpl , nonstd : : string_view path ) {
m_lexer . start ( tmpl . content ) ;
for ( ; ; ) {
get_next_token ( ) ;
switch ( m_tok . kind ) {
case Token : : Kind : : Eof :
if ( ! m_if_stack . empty ( ) ) inja_throw ( " parser_error " , " unmatched if " ) ;
if ( ! m_loop_stack . empty ( ) ) inja_throw ( " parser_error " , " unmatched for " ) ;
return ;
case Token : : Kind : : Text :
tmpl . bytecodes . emplace_back ( Bytecode : : Op : : PrintText , m_tok . text , 0u ) ;
break ;
case Token : : Kind : : StatementOpen :
get_next_token ( ) ;
if ( ! parse_statement ( tmpl , path ) ) {
inja_throw ( " parser_error " , " expected statement, got ' " + m_tok . describe ( ) + " ' " ) ;
}
if ( m_tok . kind ! = Token : : Kind : : StatementClose ) {
inja_throw ( " parser_error " , " expected statement close, got ' " + m_tok . describe ( ) + " ' " ) ;
}
break ;
case Token : : Kind : : LineStatementOpen :
get_next_token ( ) ;
parse_statement ( tmpl , path ) ;
if ( m_tok . kind ! = Token : : Kind : : LineStatementClose & &
m_tok . kind ! = Token : : Kind : : Eof ) {
inja_throw ( " parser_error " , " expected line statement close, got ' " + m_tok . describe ( ) + " ' " ) ;
}
break ;
case Token : : Kind : : ExpressionOpen :
get_next_token ( ) ;
if ( ! parse_expression ( tmpl ) ) {
inja_throw ( " parser_error " , " expected expression, got ' " + m_tok . describe ( ) + " ' " ) ;
}
append_function ( tmpl , Bytecode : : Op : : PrintValue , 1 ) ;
if ( m_tok . kind ! = Token : : Kind : : ExpressionClose ) {
inja_throw ( " parser_error " , " expected expression close, got ' " + m_tok . describe ( ) + " ' " ) ;
}
break ;
case Token : : Kind : : CommentOpen :
get_next_token ( ) ;
if ( m_tok . kind ! = Token : : Kind : : CommentClose ) {
inja_throw ( " parser_error " , " expected comment close, got ' " + m_tok . describe ( ) + " ' " ) ;
}
break ;
default :
inja_throw ( " parser_error " , " unexpected token ' " + m_tok . describe ( ) + " ' " ) ;
break ;
}
}
}
Template parse ( nonstd : : string_view input , nonstd : : string_view path ) {
Template result ;
result . content = static_cast < std : : string > ( input ) ;
parse_into ( result , path ) ;
return result ;
}
Template parse ( nonstd : : string_view input ) {
return parse ( input , " ./ " ) ;
}
Template parse_template ( nonstd : : string_view filename ) {
Template result ;
result . content = load_file ( filename ) ;
nonstd : : string_view path = filename . substr ( 0 , filename . find_last_of ( " / \\ " ) + 1 ) ;
// StringRef path = sys::path::parent_path(filename);
Parser ( m_config , m_lexer . get_config ( ) , m_included_templates ) . parse_into ( result , path ) ;
return result ;
}
std : : string load_file ( nonstd : : string_view filename ) {
2019-09-30 20:30:42 +02:00
std : : ifstream file = open_file_or_throw ( static_cast < std : : string > ( filename ) ) ;
std : : string text ( ( std : : istreambuf_iterator < char > ( file ) ) , std : : istreambuf_iterator < char > ( ) ) ;
return text ;
}
2019-04-27 21:50:53 +02:00
private :
const ParserConfig & m_config ;
Lexer m_lexer ;
Token m_tok ;
Token m_peek_tok ;
bool m_have_peek_tok { false } ;
TemplateStorage & m_included_templates ;
const ParserStatic & m_static ;
struct IfData {
unsigned int prev_cond_jump ;
std : : vector < unsigned int > uncond_jumps ;
explicit IfData ( unsigned int condJump ) : prev_cond_jump ( condJump ) { }
} ;
std : : vector < IfData > m_if_stack ;
std : : vector < unsigned int > m_loop_stack ;
void get_next_token ( ) {
if ( m_have_peek_tok ) {
m_tok = m_peek_tok ;
m_have_peek_tok = false ;
} else {
m_tok = m_lexer . scan ( ) ;
}
}
void get_peek_token ( ) {
if ( ! m_have_peek_tok ) {
m_peek_tok = m_lexer . scan ( ) ;
m_have_peek_tok = true ;
}
}
} ;
} // namespace inja
# endif // PANTOR_INJA_PARSER_HPP
// #include "polyfill.hpp"
# ifndef PANTOR_INJA_POLYFILL_HPP
# define PANTOR_INJA_POLYFILL_HPP
# if __cplusplus < 201402L
# include <cstddef>
2019-09-30 20:30:42 +02:00
# include <memory>
2019-04-27 21:50:53 +02:00
# include <type_traits>
# include <utility>
namespace stdinja {
template < class T > struct _Unique_if {
typedef std : : unique_ptr < T > _Single_object ;
} ;
template < class T > struct _Unique_if < T [ ] > {
typedef std : : unique_ptr < T [ ] > _Unknown_bound ;
} ;
template < class T , size_t N > struct _Unique_if < T [ N ] > {
typedef void _Known_bound ;
} ;
template < class T , class . . . Args >
typename _Unique_if < T > : : _Single_object
make_unique ( Args & & . . . args ) {
return std : : unique_ptr < T > ( new T ( std : : forward < Args > ( args ) . . . ) ) ;
}
template < class T >
typename _Unique_if < T > : : _Unknown_bound
make_unique ( size_t n ) {
typedef typename std : : remove_extent < T > : : type U ;
return std : : unique_ptr < T > ( new U [ n ] ( ) ) ;
}
template < class T , class . . . Args >
typename _Unique_if < T > : : _Known_bound
make_unique ( Args & & . . . ) = delete ;
}
# else
namespace stdinja = std ;
# endif // memory */
# endif // PANTOR_INJA_POLYFILL_HPP
// #include "renderer.hpp"
# ifndef PANTOR_INJA_RENDERER_HPP
# define PANTOR_INJA_RENDERER_HPP
# include <algorithm>
# include <numeric>
2019-09-30 20:30:42 +02:00
# include <string>
# include <utility>
# include <vector>
2019-04-27 21:50:53 +02:00
# include <nlohmann/json.hpp>
// #include "bytecode.hpp"
// #include "template.hpp"
// #include "utils.hpp"
namespace inja {
inline nonstd : : string_view convert_dot_to_json_pointer ( nonstd : : string_view dot , std : : string & out ) {
out . clear ( ) ;
do {
nonstd : : string_view part ;
std : : tie ( part , dot ) = string_view : : split ( dot , ' . ' ) ;
out . push_back ( ' / ' ) ;
out . append ( part . begin ( ) , part . end ( ) ) ;
} while ( ! dot . empty ( ) ) ;
return nonstd : : string_view ( out . data ( ) , out . size ( ) ) ;
}
2019-09-30 20:30:42 +02:00
/*!
* \ brief Class for rendering a Template with data .
*/
2019-04-27 21:50:53 +02:00
class Renderer {
std : : vector < const json * > & get_args ( const Bytecode & bc ) {
m_tmp_args . clear ( ) ;
bool has_imm = ( ( bc . flags & Bytecode : : Flag : : ValueMask ) ! = Bytecode : : Flag : : ValuePop ) ;
// get args from stack
unsigned int pop_args = bc . args ;
if ( has_imm ) {
pop_args - = 1 ;
}
for ( auto i = std : : prev ( m_stack . end ( ) , pop_args ) ; i ! = m_stack . end ( ) ; i + + ) {
m_tmp_args . push_back ( & ( * i ) ) ;
}
// get immediate arg
if ( has_imm ) {
m_tmp_args . push_back ( get_imm ( bc ) ) ;
}
return m_tmp_args ;
}
void pop_args ( const Bytecode & bc ) {
unsigned int popArgs = bc . args ;
if ( ( bc . flags & Bytecode : : Flag : : ValueMask ) ! = Bytecode : : Flag : : ValuePop ) {
popArgs - = 1 ;
}
for ( unsigned int i = 0 ; i < popArgs ; + + i ) {
m_stack . pop_back ( ) ;
}
}
const json * get_imm ( const Bytecode & bc ) {
std : : string ptr_buffer ;
nonstd : : string_view ptr ;
switch ( bc . flags & Bytecode : : Flag : : ValueMask ) {
case Bytecode : : Flag : : ValuePop :
return nullptr ;
case Bytecode : : Flag : : ValueImmediate :
return & bc . value ;
case Bytecode : : Flag : : ValueLookupDot :
ptr = convert_dot_to_json_pointer ( bc . str , ptr_buffer ) ;
break ;
case Bytecode : : Flag : : ValueLookupPointer :
ptr_buffer + = ' / ' ;
ptr_buffer + = bc . str ;
ptr = ptr_buffer ;
break ;
}
try {
return & m_data - > at ( json : : json_pointer ( ptr . data ( ) ) ) ;
} catch ( std : : exception & ) {
// try to evaluate as a no-argument callback
if ( auto callback = m_callbacks . find_callback ( bc . str , 0 ) ) {
std : : vector < const json * > arguments { } ;
m_tmp_val = callback ( arguments ) ;
return & m_tmp_val ;
}
inja_throw ( " render_error " , " variable ' " + static_cast < std : : string > ( bc . str ) + " ' not found " ) ;
return nullptr ;
}
}
bool truthy ( const json & var ) const {
if ( var . empty ( ) ) {
return false ;
} else if ( var . is_number ( ) ) {
return ( var ! = 0 ) ;
} else if ( var . is_string ( ) ) {
return ! var . empty ( ) ;
}
try {
return var . get < bool > ( ) ;
} catch ( json : : type_error & e ) {
inja_throw ( " json_error " , e . what ( ) ) ;
throw ;
}
}
void update_loop_data ( ) {
LoopLevel & level = m_loop_stack . back ( ) ;
if ( level . loop_type = = LoopLevel : : Type : : Array ) {
2019-09-30 20:30:42 +02:00
level . data [ static_cast < std : : string > ( level . value_name ) ] = level . values . at ( level . index ) ; // *level.it;
2019-04-27 21:50:53 +02:00
auto & loopData = level . data [ " loop " ] ;
loopData [ " index " ] = level . index ;
loopData [ " index1 " ] = level . index + 1 ;
loopData [ " is_first " ] = ( level . index = = 0 ) ;
loopData [ " is_last " ] = ( level . index = = level . size - 1 ) ;
} else {
level . data [ static_cast < std : : string > ( level . key_name ) ] = level . map_it - > first ;
level . data [ static_cast < std : : string > ( level . value_name ) ] = * level . map_it - > second ;
}
}
const TemplateStorage & m_included_templates ;
const FunctionStorage & m_callbacks ;
std : : vector < json > m_stack ;
struct LoopLevel {
enum class Type { Map , Array } ;
Type loop_type ;
2019-09-30 20:30:42 +02:00
nonstd : : string_view key_name ; // variable name for keys
nonstd : : string_view value_name ; // variable name for values
2019-04-27 21:50:53 +02:00
json data ; // data with loop info added
json values ; // values to iterate over
// loop over list
size_t index ; // current list index
size_t size ; // length of list
// loop over map
using KeyValue = std : : pair < nonstd : : string_view , json * > ;
using MapValues = std : : vector < KeyValue > ;
2019-09-30 20:30:42 +02:00
MapValues map_values ; // values to iterate over
MapValues : : iterator map_it ; // iterator over values
2019-04-27 21:50:53 +02:00
} ;
std : : vector < LoopLevel > m_loop_stack ;
const json * m_data ;
std : : vector < const json * > m_tmp_args ;
json m_tmp_val ;
public :
Renderer ( const TemplateStorage & included_templates , const FunctionStorage & callbacks ) : m_included_templates ( included_templates ) , m_callbacks ( callbacks ) {
m_stack . reserve ( 16 ) ;
m_tmp_args . reserve ( 4 ) ;
m_loop_stack . reserve ( 16 ) ;
}
void render_to ( std : : ostream & os , const Template & tmpl , const json & data ) {
m_data = & data ;
for ( size_t i = 0 ; i < tmpl . bytecodes . size ( ) ; + + i ) {
const auto & bc = tmpl . bytecodes [ i ] ;
switch ( bc . op ) {
case Bytecode : : Op : : Nop : {
break ;
}
case Bytecode : : Op : : PrintText : {
os < < bc . str ;
break ;
}
case Bytecode : : Op : : PrintValue : {
const json & val = * get_args ( bc ) [ 0 ] ;
2019-09-30 20:30:42 +02:00
if ( val . is_string ( ) ) {
2019-04-27 21:50:53 +02:00
os < < val . get_ref < const std : : string & > ( ) ;
2019-09-30 20:30:42 +02:00
} else {
2019-04-27 21:50:53 +02:00
os < < val . dump ( ) ;
2019-09-30 20:30:42 +02:00
}
2019-04-27 21:50:53 +02:00
pop_args ( bc ) ;
break ;
}
case Bytecode : : Op : : Push : {
m_stack . emplace_back ( * get_imm ( bc ) ) ;
break ;
}
case Bytecode : : Op : : Upper : {
auto result = get_args ( bc ) [ 0 ] - > get < std : : string > ( ) ;
std : : transform ( result . begin ( ) , result . end ( ) , result . begin ( ) , : : toupper ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( std : : move ( result ) ) ;
break ;
}
case Bytecode : : Op : : Lower : {
auto result = get_args ( bc ) [ 0 ] - > get < std : : string > ( ) ;
std : : transform ( result . begin ( ) , result . end ( ) , result . begin ( ) , : : tolower ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( std : : move ( result ) ) ;
break ;
}
case Bytecode : : Op : : Range : {
int number = get_args ( bc ) [ 0 ] - > get < int > ( ) ;
std : : vector < int > result ( number ) ;
std : : iota ( std : : begin ( result ) , std : : end ( result ) , 0 ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( std : : move ( result ) ) ;
break ;
}
case Bytecode : : Op : : Length : {
2019-09-30 20:30:42 +02:00
const json & val = * get_args ( bc ) [ 0 ] ;
int result ;
if ( val . is_string ( ) ) {
result = val . get_ref < const std : : string & > ( ) . length ( ) ;
} else {
result = val . size ( ) ;
}
2019-04-27 21:50:53 +02:00
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : Sort : {
auto result = get_args ( bc ) [ 0 ] - > get < std : : vector < json > > ( ) ;
std : : sort ( result . begin ( ) , result . end ( ) ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( std : : move ( result ) ) ;
break ;
}
2019-09-30 20:30:42 +02:00
case Bytecode : : Op : : At : {
auto args = get_args ( bc ) ;
auto result = args [ 0 ] - > at ( args [ 1 ] - > get < int > ( ) ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
2019-04-27 21:50:53 +02:00
case Bytecode : : Op : : First : {
auto result = get_args ( bc ) [ 0 ] - > front ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : Last : {
auto result = get_args ( bc ) [ 0 ] - > back ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : Round : {
auto args = get_args ( bc ) ;
double number = args [ 0 ] - > get < double > ( ) ;
int precision = args [ 1 ] - > get < int > ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( std : : round ( number * std : : pow ( 10.0 , precision ) ) / std : : pow ( 10.0 , precision ) ) ;
break ;
}
case Bytecode : : Op : : DivisibleBy : {
auto args = get_args ( bc ) ;
int number = args [ 0 ] - > get < int > ( ) ;
int divisor = args [ 1 ] - > get < int > ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( ( divisor ! = 0 ) & & ( number % divisor = = 0 ) ) ;
break ;
}
case Bytecode : : Op : : Odd : {
int number = get_args ( bc ) [ 0 ] - > get < int > ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( number % 2 ! = 0 ) ;
break ;
}
case Bytecode : : Op : : Even : {
int number = get_args ( bc ) [ 0 ] - > get < int > ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( number % 2 = = 0 ) ;
break ;
}
case Bytecode : : Op : : Max : {
auto args = get_args ( bc ) ;
auto result = * std : : max_element ( args [ 0 ] - > begin ( ) , args [ 0 ] - > end ( ) ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( std : : move ( result ) ) ;
break ;
}
case Bytecode : : Op : : Min : {
auto args = get_args ( bc ) ;
auto result = * std : : min_element ( args [ 0 ] - > begin ( ) , args [ 0 ] - > end ( ) ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( std : : move ( result ) ) ;
break ;
}
case Bytecode : : Op : : Not : {
bool result = ! truthy ( * get_args ( bc ) [ 0 ] ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : And : {
auto args = get_args ( bc ) ;
bool result = truthy ( * args [ 0 ] ) & & truthy ( * args [ 1 ] ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : Or : {
auto args = get_args ( bc ) ;
bool result = truthy ( * args [ 0 ] ) | | truthy ( * args [ 1 ] ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : In : {
auto args = get_args ( bc ) ;
bool result = std : : find ( args [ 1 ] - > begin ( ) , args [ 1 ] - > end ( ) , * args [ 0 ] ) ! =
args [ 1 ] - > end ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : Equal : {
auto args = get_args ( bc ) ;
bool result = ( * args [ 0 ] = = * args [ 1 ] ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : Greater : {
auto args = get_args ( bc ) ;
bool result = ( * args [ 0 ] > * args [ 1 ] ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : Less : {
auto args = get_args ( bc ) ;
bool result = ( * args [ 0 ] < * args [ 1 ] ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : GreaterEqual : {
auto args = get_args ( bc ) ;
bool result = ( * args [ 0 ] > = * args [ 1 ] ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : LessEqual : {
auto args = get_args ( bc ) ;
bool result = ( * args [ 0 ] < = * args [ 1 ] ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : Different : {
auto args = get_args ( bc ) ;
bool result = ( * args [ 0 ] ! = * args [ 1 ] ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : Float : {
double result =
std : : stod ( get_args ( bc ) [ 0 ] - > get_ref < const std : : string & > ( ) ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : Int : {
int result = std : : stoi ( get_args ( bc ) [ 0 ] - > get_ref < const std : : string & > ( ) ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : Exists : {
auto & & name = get_args ( bc ) [ 0 ] - > get_ref < const std : : string & > ( ) ;
bool result = ( data . find ( name ) ! = data . end ( ) ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : ExistsInObject : {
auto args = get_args ( bc ) ;
auto & & name = args [ 1 ] - > get_ref < const std : : string & > ( ) ;
bool result = ( args [ 0 ] - > find ( name ) ! = args [ 0 ] - > end ( ) ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : IsBoolean : {
bool result = get_args ( bc ) [ 0 ] - > is_boolean ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : IsNumber : {
bool result = get_args ( bc ) [ 0 ] - > is_number ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : IsInteger : {
bool result = get_args ( bc ) [ 0 ] - > is_number_integer ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : IsFloat : {
bool result = get_args ( bc ) [ 0 ] - > is_number_float ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : IsObject : {
bool result = get_args ( bc ) [ 0 ] - > is_object ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : IsArray : {
bool result = get_args ( bc ) [ 0 ] - > is_array ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : IsString : {
bool result = get_args ( bc ) [ 0 ] - > is_string ( ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( result ) ;
break ;
}
case Bytecode : : Op : : Default : {
// default needs to be a bit "magic"; we can't evaluate the first
// argument during the push operation, so we swap the arguments during
// the parse phase so the second argument is pushed on the stack and
// the first argument is in the immediate
try {
const json * imm = get_imm ( bc ) ;
// if no exception was raised, replace the stack value with it
m_stack . back ( ) = * imm ;
} catch ( std : : exception & ) {
// couldn't read immediate, just leave the stack as is
}
break ;
}
case Bytecode : : Op : : Include :
2019-09-30 20:30:42 +02:00
Renderer ( m_included_templates , m_callbacks ) . render_to ( os , m_included_templates . find ( get_imm ( bc ) - > get_ref < const std : : string & > ( ) ) - > second , * m_data ) ;
2019-04-27 21:50:53 +02:00
break ;
case Bytecode : : Op : : Callback : {
auto callback = m_callbacks . find_callback ( bc . str , bc . args ) ;
if ( ! callback ) {
inja_throw ( " render_error " , " function ' " + static_cast < std : : string > ( bc . str ) + " ' ( " + std : : to_string ( static_cast < unsigned int > ( bc . args ) ) + " ) not found " ) ;
}
json result = callback ( get_args ( bc ) ) ;
pop_args ( bc ) ;
m_stack . emplace_back ( std : : move ( result ) ) ;
break ;
}
case Bytecode : : Op : : Jump : {
i = bc . args - 1 ; // -1 due to ++i in loop
break ;
}
case Bytecode : : Op : : ConditionalJump : {
if ( ! truthy ( m_stack . back ( ) ) ) {
i = bc . args - 1 ; // -1 due to ++i in loop
}
m_stack . pop_back ( ) ;
break ;
}
case Bytecode : : Op : : StartLoop : {
// jump past loop body if empty
if ( m_stack . back ( ) . empty ( ) ) {
m_stack . pop_back ( ) ;
i = bc . args ; // ++i in loop will take it past EndLoop
break ;
}
m_loop_stack . emplace_back ( ) ;
LoopLevel & level = m_loop_stack . back ( ) ;
level . value_name = bc . str ;
level . values = std : : move ( m_stack . back ( ) ) ;
level . data = ( * m_data ) ;
m_stack . pop_back ( ) ;
if ( bc . value . is_string ( ) ) {
// map iterator
if ( ! level . values . is_object ( ) ) {
m_loop_stack . pop_back ( ) ;
inja_throw ( " render_error " , " for key, value requires object " ) ;
}
level . loop_type = LoopLevel : : Type : : Map ;
level . key_name = bc . value . get_ref < const std : : string & > ( ) ;
// sort by key
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 ; } ) ;
level . map_it = level . map_values . begin ( ) ;
} else {
if ( ! level . values . is_array ( ) ) {
m_loop_stack . pop_back ( ) ;
inja_throw ( " render_error " , " type must be array " ) ;
}
// list iterator
level . loop_type = LoopLevel : : Type : : Array ;
level . index = 0 ;
level . size = level . values . size ( ) ;
}
// provide parent access in nested loop
auto parent_loop_it = level . data . find ( " loop " ) ;
if ( parent_loop_it ! = level . data . end ( ) ) {
json loop_copy = * parent_loop_it ;
( * parent_loop_it ) [ " parent " ] = std : : move ( loop_copy ) ;
}
// set "current" data to loop data
m_data = & level . data ;
update_loop_data ( ) ;
break ;
}
case Bytecode : : Op : : EndLoop : {
if ( m_loop_stack . empty ( ) ) {
inja_throw ( " render_error " , " unexpected state in renderer " ) ;
}
LoopLevel & level = m_loop_stack . back ( ) ;
bool done ;
if ( level . loop_type = = LoopLevel : : Type : : Array ) {
level . index + = 1 ;
done = ( level . index = = level . values . size ( ) ) ;
} else {
level . map_it + = 1 ;
done = ( level . map_it = = level . map_values . end ( ) ) ;
}
if ( done ) {
m_loop_stack . pop_back ( ) ;
// set "current" data to outer loop data or main data as appropriate
if ( ! m_loop_stack . empty ( ) ) {
m_data = & m_loop_stack . back ( ) . data ;
} else {
m_data = & data ;
}
break ;
}
update_loop_data ( ) ;
// jump back to start of loop
i = bc . args - 1 ; // -1 due to ++i in loop
break ;
}
default : {
inja_throw ( " render_error " , " unknown op in renderer: " + std : : to_string ( static_cast < unsigned int > ( bc . op ) ) ) ;
}
}
}
}
} ;
} // namespace inja
# endif // PANTOR_INJA_RENDERER_HPP
// #include "string_view.hpp"
// #include "template.hpp"
2019-09-30 20:30:42 +02:00
// #include "utils.hpp"
2019-04-27 21:50:53 +02:00
namespace inja {
using namespace nlohmann ;
2019-09-30 20:30:42 +02:00
/*!
* \ brief Class for changing the configuration .
*/
2019-04-27 21:50:53 +02:00
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 < Impl > m_impl ;
public :
2019-09-30 20:30:42 +02:00
Environment ( ) : Environment ( " " ) { }
2019-04-27 21:50:53 +02:00
explicit Environment ( const std : : string & global_path ) : m_impl ( stdinja : : make_unique < Impl > ( ) ) {
m_impl - > input_path = global_path ;
m_impl - > output_path = global_path ;
}
explicit Environment ( const std : : string & input_path , const std : : string & output_path ) : m_impl ( stdinja : : make_unique < Impl > ( ) ) {
m_impl - > input_path = input_path ;
m_impl - > 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 ( ) ;
}
/// 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 ( ) ;
}
/// 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 ( ) ;
}
/// 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 ( ) ;
}
2019-09-30 20:30:42 +02:00
/// 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 ;
}
/// 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 ;
}
2019-04-27 21:50:53 +02:00
/// Sets the element notation syntax
void set_element_notation ( ElementNotation notation ) {
m_impl - > parser_config . notation = notation ;
}
Template parse ( nonstd : : string_view input ) {
Parser parser ( m_impl - > parser_config , m_impl - > lexer_config , m_impl - > 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 ) ;
2019-09-30 20:30:42 +02:00
return parser . parse_template ( m_impl - > input_path + static_cast < std : : string > ( filename ) ) ;
}
2019-04-27 21:50:53 +02:00
std : : string render ( nonstd : : string_view input , const json & data ) {
return render ( parse ( input ) , data ) ;
}
std : : string render ( const Template & tmpl , const json & data ) {
std : : stringstream os ;
render_to ( os , tmpl , data ) ;
return os . str ( ) ;
}
std : : string render_file ( const std : : string & filename , const json & data ) {
2019-09-30 20:30:42 +02:00
return render ( parse_template ( filename ) , data ) ;
}
2019-04-27 21:50:53 +02:00
std : : string render_file_with_json_file ( const std : : string & filename , const std : : string & filename_data ) {
2019-09-30 20:30:42 +02:00
const json data = load_json ( filename_data ) ;
return render_file ( filename , data ) ;
}
2019-04-27 21:50:53 +02:00
void write ( const std : : string & filename , const json & data , const std : : string & filename_out ) {
2019-09-30 20:30:42 +02:00
std : : ofstream file ( m_impl - > output_path + filename_out ) ;
file < < render_file ( filename , data ) ;
file . close ( ) ;
}
2019-04-27 21:50:53 +02:00
void write ( const Template & temp , const json & data , const std : : string & filename_out ) {
2019-09-30 20:30:42 +02:00
std : : ofstream file ( m_impl - > output_path + filename_out ) ;
file < < render ( temp , data ) ;
file . close ( ) ;
}
2019-04-27 21:50:53 +02:00
2019-09-30 20:30:42 +02:00
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 ) ;
}
2019-04-27 21:50:53 +02:00
2019-09-30 20:30:42 +02:00
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 ) ;
}
2019-04-27 21:50:53 +02:00
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 ) ;
return os ;
}
std : : string load_file ( const std : : string & filename ) {
Parser parser ( m_impl - > parser_config , m_impl - > lexer_config , m_impl - > included_templates ) ;
2019-09-30 20:30:42 +02:00
return parser . load_file ( m_impl - > input_path + filename ) ;
}
2019-04-27 21:50:53 +02:00
json load_json ( const std : : string & filename ) {
2019-09-30 20:30:42 +02:00
std : : ifstream file = open_file_or_throw ( m_impl - > input_path + filename ) ;
json j ;
file > > j ;
return j ;
}
2019-04-27 21:50:53 +02:00
void add_callback ( const std : : string & name , unsigned int numArgs , const CallbackFunction & callback ) {
m_impl - > callbacks . add_callback ( name , numArgs , callback ) ;
}
/** Includes a template with a given name into the environment.
* Then , a template can be rendered in another template using the
* include " <name> " syntax .
*/
void include_template ( const std : : string & name , const Template & tmpl ) {
m_impl - > included_templates [ name ] = tmpl ;
}
} ;
/*!
@ brief render with default settings to a string
*/
inline std : : string render ( nonstd : : string_view input , const json & data ) {
return Environment ( ) . render ( input , data ) ;
}
/*!
@ brief render with default settings to the given output stream
*/
inline void render_to ( std : : ostream & os , nonstd : : string_view input , const json & data ) {
Environment env ;
env . render_to ( os , env . parse ( input ) , data ) ;
}
}
# endif // PANTOR_INJA_ENVIRONMENT_HPP
// #include "string_view.hpp"
// #include "template.hpp"
// #include "parser.hpp"
// #include "renderer.hpp"
# endif // PANTOR_INJA_HPP