2024-03-12 14:12:04 +08:00
/*
___ _ Version 3.3
| _ _ | _ __ ( _ ) __ _ https : //github.com/pantor/inja
| | | ' _ \ | | / _ ` | Licensed under the MIT License < http : //opensource.org/licenses/MIT>.
| | | | | | | | ( _ | |
| ___ | _ | | _ | / | \ __ , _ | Copyright ( c ) 2018 - 2021 Lars Berscheid
| __ /
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 .
*/
# ifndef INCLUDE_INJA_INJA_HPP_
# define INCLUDE_INJA_INJA_HPP_
# include <nlohmann/json.hpp>
2024-04-09 21:24:07 +08:00
# if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(INJA_NOEXCEPTION)
2024-03-12 14:12:04 +08:00
# ifndef INJA_THROW
# define INJA_THROW(exception) throw exception
# endif
# else
# include <cstdlib>
# ifndef INJA_THROW
2024-04-09 21:24:07 +08:00
# define INJA_THROW(exception) \
std : : abort ( ) ; \
2024-03-12 14:12:04 +08:00
std : : ignore = exception
# endif
# ifndef INJA_NOEXCEPTION
# define INJA_NOEXCEPTION
# endif
# endif
// #include "environment.hpp"
# ifndef INCLUDE_INJA_ENVIRONMENT_HPP_
# define INCLUDE_INJA_ENVIRONMENT_HPP_
# include <fstream>
# include <iostream>
# include <memory>
# include <sstream>
# include <string>
# include <nlohmann/json.hpp>
// #include "config.hpp"
# ifndef INCLUDE_INJA_CONFIG_HPP_
# define INCLUDE_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 4
# define string_view_lite_PATCH 0
2024-04-09 21:24:07 +08:00
# define string_view_lite_VERSION \
nssv_STRINGIFY ( string_view_lite_MAJOR ) " . " nssv_STRINGIFY ( string_view_lite_MINOR ) " . " nssv_STRINGIFY ( \
string_view_lite_PATCH )
2024-03-12 14:12:04 +08:00
# 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)
2024-04-09 21:24:07 +08:00
# define nssv_CONFIG_SELECT_STRING_VIEW (nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD)
2024-03-12 14:12:04 +08:00
# 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
2024-04-09 21:24:07 +08:00
# 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 ) )
2024-03-12 14:12:04 +08:00
# 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 string_view_literals
} // namespace literals
# endif // nssv_CONFIG_USR_SV_OPERATOR
} // namespace nonstd
# endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
namespace nonstd {
using std : : basic_string_view ;
using std : : string_view ;
using std : : u16string_view ;
using std : : u32string_view ;
using std : : wstring_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__)
2024-04-09 21:24:07 +08:00
# define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
2024-03-12 14:12:04 +08:00
# else
# define nssv_COMPILER_CLANG_VERSION 0
# endif
# if defined(__GNUC__) && !defined(__clang__)
2024-04-09 21:24:07 +08:00
# define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
2024-03-12 14:12:04 +08:00
# 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
2024-04-09 21:24:07 +08:00
# if !((nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION) || nssv_BETWEEN(nssv_COMPILER_CLANG_VERSION, 300, 400))
2024-03-12 14:12:04 +08:00
# define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140
# else
# define nssv_HAVE_STD_DEFINED_LITERALS 0
# 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 {
# if nssv_CPP11_OR_GREATER
namespace detail {
// Expect tail call optimization to make length() non-recursive:
template < typename CharT >
inline constexpr std : : size_t length ( CharT * s , std : : size_t result = 0 )
{
return * s = = ' \0 ' ? result : length ( s + 1 , result + 1 ) ;
}
} // namespace detail
# endif // nssv_CPP11_OR_GREATER
template < class CharT , 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 ;
typedef std : : reverse_iterator < const_iterator > const_reverse_iterator ;
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 ) nssv_noexcept // non-standard noexcept
: data_ ( s ) ,
size_ ( count )
{ }
nssv_constexpr basic_string_view ( CharT const * s ) nssv_noexcept // non-standard noexcept
: data_ ( s )
# if nssv_CPP17_OR_GREATER
,
size_ ( Traits : : length ( s ) )
# elif nssv_CPP11_OR_GREATER
,
size_ ( detail : : length ( s ) )
# else
,
size_ ( Traits : : length ( s ) )
# endif
{ }
// Assignment:
# if nssv_CPP11_OR_GREATER
2024-04-09 21:24:07 +08:00
nssv_constexpr14 basic_string_view & operator = ( basic_string_view const & other ) nssv_noexcept = default ;
2024-03-12 14:12:04 +08:00
# 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 ( ) ; }
2024-04-09 21:24:07 +08:00
nssv_constexpr const_reverse_iterator rbegin ( ) const nssv_noexcept { return const_reverse_iterator ( end ( ) ) ; }
2024-03-12 14:12:04 +08:00
2024-04-09 21:24:07 +08:00
nssv_constexpr const_reverse_iterator rend ( ) const nssv_noexcept { return const_reverse_iterator ( begin ( ) ) ; }
2024-03-12 14:12:04 +08:00
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_ ; }
2024-04-09 21:24:07 +08:00
nssv_constexpr size_type max_size ( ) const nssv_noexcept { return ( std : : numeric_limits < size_type > : : max ) ( ) ; }
2024-03-12 14:12:04 +08:00
// 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 ( " nonstd::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 ( " nonstd::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 ( " nonstd::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)
{
2024-04-09 21:24:07 +08:00
if ( const int result = Traits : : compare ( data ( ) , other . data ( ) , ( std : : min ) ( size ( ) , other . size ( ) ) ) ) {
2024-03-12 14:12:04 +08:00
return result ;
}
return size ( ) = = other . size ( ) ? 0 : size ( ) < other . size ( ) ? - 1 : 1 ;
}
2024-04-09 21:24:07 +08:00
nssv_constexpr int compare ( size_type pos1 , size_type n1 , basic_string_view other ) const // (2)
2024-03-12 14:12:04 +08:00
{
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 ) ) ;
}
2024-04-09 21:24:07 +08:00
nssv_constexpr int compare ( size_type pos1 , size_type n1 , CharT const * s , size_type n2 ) const // (6)
2024-03-12 14:12:04 +08:00
{
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 ) ,
2024-04-09 21:24:07 +08:00
pos > = size ( ) ? npos : to_pos ( std : : search ( cbegin ( ) + pos , cend ( ) , v . cbegin ( ) , v . cend ( ) , Traits : : eq ) ) ;
2024-03-12 14:12:04 +08:00
}
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 ) ; }
2024-04-09 21:24:07 +08:00
const_iterator last = cbegin ( ) + ( std : : min ) ( size ( ) - v . size ( ) , pos ) + v . size ( ) ;
2024-03-12 14:12:04 +08:00
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)
{
2024-04-09 21:24:07 +08:00
return pos > = size ( ) ? npos
: to_pos ( std : : find_first_of ( cbegin ( ) + pos , cend ( ) , v . cbegin ( ) , v . cend ( ) , Traits : : eq ) ) ;
2024-03-12 14:12:04 +08:00
}
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 ) ;
}
2024-04-09 21:24:07 +08:00
nssv_constexpr size_type find_first_of ( CharT const * s , size_type pos ,
2024-03-12 14:12:04 +08:00
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
2024-04-09 21:24:07 +08:00
: 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 ) ) ;
2024-03-12 14:12:04 +08:00
}
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 ) ;
}
2024-04-09 21:24:07 +08:00
nssv_constexpr size_type find_last_of ( CharT const * s , size_type pos ,
2024-03-12 14:12:04 +08:00
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)
{
2024-04-09 21:24:07 +08:00
return pos > = size ( ) ? npos : to_pos ( std : : find_if ( cbegin ( ) + pos , cend ( ) , not_in_view ( v ) ) ) ;
2024-03-12 14:12:04 +08:00
}
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 ) ;
}
2024-04-09 21:24:07 +08:00
nssv_constexpr size_type find_first_not_of ( CharT const * s , size_type pos ,
2024-03-12 14:12:04 +08:00
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)
{
2024-04-09 21:24:07 +08:00
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 ) ) ) ;
2024-03-12 14:12:04 +08:00
}
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 ) ;
}
2024-04-09 21:24:07 +08:00
nssv_constexpr size_type find_last_not_of ( CharT const * s , size_type pos ,
2024-03-12 14:12:04 +08:00
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 explicit 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 > >
2024-04-09 21:24:07 +08:00
std : : basic_string < CharT , Traits , Allocator > to_string ( Allocator const & a = Allocator ( ) ) const
2024-03-12 14:12:04 +08:00
{
return std : : basic_string < CharT , Traits , Allocator > ( begin ( ) , end ( ) , a ) ;
}
# else
2024-04-09 21:24:07 +08:00
std : : basic_string < CharT , Traits > to_string ( ) const { return std : : basic_string < CharT , Traits > ( begin ( ) , end ( ) ) ; }
2024-03-12 14:12:04 +08:00
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 >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator = = ( basic_string_view < CharT , Traits > lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) = = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator ! = ( basic_string_view < CharT , Traits > lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) ! = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator < ( basic_string_view < CharT , Traits > lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) < 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator < = ( basic_string_view < CharT , Traits > lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) < = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator > ( basic_string_view < CharT , Traits > lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) > 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator > = ( basic_string_view < CharT , Traits > lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
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)
// accomodate for older compilers:
// ==
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool operator = = ( basic_string_view < CharT , Traits > lhs , char const * rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) = = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool operator = = ( char const * lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return rhs . compare ( lhs ) = = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator = = ( basic_string_view < CharT , Traits > lhs , std : : basic_string < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . size ( ) = = rhs . size ( ) & & lhs . compare ( rhs ) = = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator = = ( std : : basic_string < CharT , Traits > rhs , basic_string_view < CharT , Traits > lhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . size ( ) = = rhs . size ( ) & & lhs . compare ( rhs ) = = 0 ;
}
// !=
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool operator ! = ( basic_string_view < CharT , Traits > lhs , char const * rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) ! = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool operator ! = ( char const * lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return rhs . compare ( lhs ) ! = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator ! = ( basic_string_view < CharT , Traits > lhs , std : : basic_string < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . size ( ) ! = rhs . size ( ) & & lhs . compare ( rhs ) ! = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator ! = ( std : : basic_string < CharT , Traits > rhs , basic_string_view < CharT , Traits > lhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . size ( ) ! = rhs . size ( ) | | rhs . compare ( lhs ) ! = 0 ;
}
// <
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool operator < ( basic_string_view < CharT , Traits > lhs , char const * rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) < 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool operator < ( char const * lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return rhs . compare ( lhs ) > 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator < ( basic_string_view < CharT , Traits > lhs , std : : basic_string < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) < 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator < ( std : : basic_string < CharT , Traits > rhs , basic_string_view < CharT , Traits > lhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return rhs . compare ( lhs ) > 0 ;
}
// <=
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool operator < = ( basic_string_view < CharT , Traits > lhs , char const * rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) < = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool operator < = ( char const * lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return rhs . compare ( lhs ) > = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator < = ( basic_string_view < CharT , Traits > lhs , std : : basic_string < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) < = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator < = ( std : : basic_string < CharT , Traits > rhs , basic_string_view < CharT , Traits > lhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return rhs . compare ( lhs ) > = 0 ;
}
// >
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool operator > ( basic_string_view < CharT , Traits > lhs , char const * rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) > 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool operator > ( char const * lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return rhs . compare ( lhs ) < 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator > ( basic_string_view < CharT , Traits > lhs , std : : basic_string < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) > 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator > ( std : : basic_string < CharT , Traits > rhs , basic_string_view < CharT , Traits > lhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return rhs . compare ( lhs ) < 0 ;
}
// >=
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool operator > = ( basic_string_view < CharT , Traits > lhs , char const * rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) > = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool operator > = ( char const * lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return rhs . compare ( lhs ) < = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator > = ( basic_string_view < CharT , Traits > lhs , std : : basic_string < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) > = 0 ;
}
template < class CharT , class Traits >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator > = ( std : : basic_string < CharT , Traits > rhs , basic_string_view < CharT , Traits > lhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return rhs . compare ( lhs ) < = 0 ;
}
# else // newer compilers:
# 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 ) >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator = = ( basic_string_view < CharT , Traits > lhs , nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) = = 0 ;
}
template < class CharT , class Traits nssv_MSVC_ORDER ( 2 ) >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator = = ( nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . size ( ) = = rhs . size ( ) & & lhs . compare ( rhs ) = = 0 ;
}
// !=
template < class CharT , class Traits nssv_MSVC_ORDER ( 1 ) >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator ! = ( basic_string_view < CharT , Traits > lhs , nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . size ( ) ! = rhs . size ( ) | | lhs . compare ( rhs ) ! = 0 ;
}
template < class CharT , class Traits nssv_MSVC_ORDER ( 2 ) >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator ! = ( nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) ! = 0 ;
}
// <
template < class CharT , class Traits nssv_MSVC_ORDER ( 1 ) >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator < ( basic_string_view < CharT , Traits > lhs , nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) < 0 ;
}
template < class CharT , class Traits nssv_MSVC_ORDER ( 2 ) >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator < ( nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) < 0 ;
}
// <=
template < class CharT , class Traits nssv_MSVC_ORDER ( 1 ) >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator < = ( basic_string_view < CharT , Traits > lhs , nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) < = 0 ;
}
template < class CharT , class Traits nssv_MSVC_ORDER ( 2 ) >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator < = ( nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) < = 0 ;
}
// >
template < class CharT , class Traits nssv_MSVC_ORDER ( 1 ) >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator > ( basic_string_view < CharT , Traits > lhs , nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) > 0 ;
}
template < class CharT , class Traits nssv_MSVC_ORDER ( 2 ) >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator > ( nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) > 0 ;
}
// >=
template < class CharT , class Traits nssv_MSVC_ORDER ( 1 ) >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator > = ( basic_string_view < CharT , Traits > lhs , nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) > = 0 ;
}
template < class CharT , class Traits nssv_MSVC_ORDER ( 2 ) >
2024-04-09 21:24:07 +08:00
nssv_constexpr bool
operator > = ( nssv_BASIC_STRING_VIEW_I ( CharT , Traits ) lhs , basic_string_view < CharT , Traits > rhs ) nssv_noexcept
2024-03-12 14:12:04 +08:00
{
return lhs . compare ( rhs ) > = 0 ;
}
# undef nssv_MSVC_ORDER
# undef nssv_BASIC_STRING_VIEW_I
# endif // compiler-dependent approach to comparisons
// 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:
2024-04-09 21:24:07 +08:00
const bool pad = ( length < os . width ( ) ) ;
const bool left_pad = pad & & ( os . flags ( ) & std : : ios_base : : adjustfield ) = = std : : ios_base : : right ;
2024-03-12 14:12:04 +08:00
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 >
2024-04-09 21:24:07 +08:00
std : : basic_ostream < CharT , Traits > &
operator < < ( std : : basic_ostream < CharT , Traits > & os , basic_string_view < CharT , Traits > sv )
2024-03-12 14:12:04 +08:00
{
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 sv_lite
} // 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
2024-04-09 21:24:07 +08:00
nssv_constexpr nonstd : : sv_lite : : string_view operator " " sv ( const char * str , size_t len ) nssv_noexcept // (1)
2024-03-12 14:12:04 +08:00
{
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 } ;
}
2024-04-09 21:24:07 +08:00
nssv_constexpr nonstd : : sv_lite : : wstring_view operator " " sv ( const wchar_t * str , size_t len ) nssv_noexcept // (4)
2024-03-12 14:12:04 +08:00
{
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
2024-04-09 21:24:07 +08:00
nssv_constexpr nonstd : : sv_lite : : string_view operator " " _sv ( const char * str , size_t len ) nssv_noexcept // (1)
2024-03-12 14:12:04 +08:00
{
return nonstd : : sv_lite : : string_view { str , len } ;
}
nssv_constexpr nonstd : : sv_lite : : u16string_view operator " " _sv ( const char16_t * str ,
2024-04-09 21:24:07 +08:00
size_t len ) nssv_noexcept // (2)
2024-03-12 14:12:04 +08:00
{
return nonstd : : sv_lite : : u16string_view { str , len } ;
}
nssv_constexpr nonstd : : sv_lite : : u32string_view operator " " _sv ( const char32_t * str ,
2024-04-09 21:24:07 +08:00
size_t len ) nssv_noexcept // (3)
2024-03-12 14:12:04 +08:00
{
return nonstd : : sv_lite : : u32string_view { str , len } ;
}
2024-04-09 21:24:07 +08:00
nssv_constexpr nonstd : : sv_lite : : wstring_view operator " " _sv ( const wchar_t * str , size_t len ) nssv_noexcept // (4)
2024-03-12 14:12:04 +08:00
{
return nonstd : : sv_lite : : wstring_view { str , len } ;
}
# endif // nssv_CONFIG_USR_SV_OPERATOR
}
}
} // namespace nonstd
# 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 sv_lite
} // namespace nonstd
# 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
2024-04-09 21:24:07 +08:00
namespace sled {
using namespace nonstd ;
}
2024-03-12 14:12:04 +08:00
# endif // NONSTD_SV_LITE_H_INCLUDED
namespace inja {
/*!
* \ brief Class for lexer configuration .
*/
struct LexerConfig {
std : : string statement_open { " {% " } ;
std : : string statement_open_no_lstrip { " {%+ " } ;
std : : string statement_open_force_lstrip { " {%- " } ;
std : : string statement_close { " %} " } ;
std : : string statement_close_force_rstrip { " -%} " } ;
std : : string line_statement { " ## " } ;
std : : string expression_open { " {{ " } ;
std : : string expression_open_force_lstrip { " {{- " } ;
std : : string expression_close { " }} " } ;
std : : string expression_close_force_rstrip { " -}} " } ;
std : : string comment_open { " {# " } ;
std : : string comment_open_force_lstrip { " {#- " } ;
std : : string comment_close { " #} " } ;
std : : string comment_close_force_rstrip { " -#} " } ;
std : : string open_chars { " #{ " } ;
bool trim_blocks { false } ;
bool lstrip_blocks { false } ;
void update_open_chars ( )
{
open_chars = " " ;
2024-04-09 21:24:07 +08:00
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 ] ; }
2024-03-12 14:12:04 +08:00
if ( open_chars . find ( statement_open_no_lstrip [ 0 ] ) = = std : : string : : npos ) {
open_chars + = statement_open_no_lstrip [ 0 ] ;
}
if ( open_chars . find ( statement_open_force_lstrip [ 0 ] ) = = std : : string : : npos ) {
open_chars + = statement_open_force_lstrip [ 0 ] ;
}
2024-04-09 21:24:07 +08:00
if ( open_chars . find ( expression_open [ 0 ] ) = = std : : string : : npos ) { open_chars + = expression_open [ 0 ] ; }
2024-03-12 14:12:04 +08:00
if ( open_chars . find ( expression_open_force_lstrip [ 0 ] ) = = std : : string : : npos ) {
open_chars + = expression_open_force_lstrip [ 0 ] ;
}
2024-04-09 21:24:07 +08:00
if ( open_chars . find ( comment_open [ 0 ] ) = = std : : string : : npos ) { open_chars + = comment_open [ 0 ] ; }
2024-03-12 14:12:04 +08:00
if ( open_chars . find ( comment_open_force_lstrip [ 0 ] ) = = std : : string : : npos ) {
open_chars + = comment_open_force_lstrip [ 0 ] ;
}
}
} ;
/*!
* \ brief Class for parser configuration .
*/
struct ParserConfig {
bool search_included_templates_in_files { true } ;
} ;
/*!
* \ brief Class for render configuration .
*/
struct RenderConfig {
bool throw_at_missing_includes { true } ;
} ;
} // namespace inja
# endif // INCLUDE_INJA_CONFIG_HPP_
// #include "function_storage.hpp"
# ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_
# define INCLUDE_INJA_FUNCTION_STORAGE_HPP_
# include <vector>
// #include "string_view.hpp"
namespace inja {
using json = nlohmann : : json ;
2024-04-09 21:24:07 +08:00
using Arguments = std : : vector < const json * > ;
using CallbackFunction = std : : function < json ( Arguments & args ) > ;
2024-03-12 14:12:04 +08:00
using VoidCallbackFunction = std : : function < void ( Arguments & args ) > ;
/*!
* \ brief Class for builtin functions and user - defined callbacks .
*/
class FunctionStorage {
public :
enum class Operation {
Not ,
And ,
Or ,
In ,
Equal ,
NotEqual ,
Greater ,
GreaterEqual ,
Less ,
LessEqual ,
Add ,
Subtract ,
Multiplication ,
Division ,
Power ,
Modulo ,
AtId ,
At ,
Default ,
DivisibleBy ,
Even ,
Exists ,
ExistsInObject ,
First ,
Float ,
Int ,
IsArray ,
IsBoolean ,
IsFloat ,
IsInteger ,
IsNumber ,
IsObject ,
IsString ,
Last ,
Length ,
Lower ,
Max ,
Min ,
Odd ,
Range ,
Round ,
Sort ,
Upper ,
Super ,
Join ,
Callback ,
ParenLeft ,
ParenRight ,
None ,
} ;
struct FunctionData {
explicit FunctionData ( const Operation & op , const CallbackFunction & cb = CallbackFunction { } )
: operation ( op ) ,
callback ( cb )
{ }
const Operation operation ;
const CallbackFunction callback ;
} ;
private :
const int VARIADIC { - 1 } ;
std : : map < std : : pair < std : : string , int > , FunctionData > function_storage = {
2024-04-09 21:24:07 +08:00
{ std : : make_pair ( " at " , 2 ) , FunctionData { Operation : : At } } ,
{ std : : make_pair ( " default " , 2 ) , FunctionData { Operation : : Default } } ,
{ std : : make_pair ( " divisibleBy " , 2 ) , FunctionData { Operation : : DivisibleBy } } ,
{ std : : make_pair ( " even " , 1 ) , FunctionData { Operation : : Even } } ,
{ std : : make_pair ( " exists " , 1 ) , FunctionData { Operation : : Exists } } ,
{ std : : make_pair ( " existsIn " , 2 ) , FunctionData { Operation : : ExistsInObject } } ,
{ std : : make_pair ( " first " , 1 ) , FunctionData { Operation : : First } } ,
{ std : : make_pair ( " float " , 1 ) , FunctionData { Operation : : Float } } ,
{ std : : make_pair ( " int " , 1 ) , FunctionData { Operation : : Int } } ,
{ std : : make_pair ( " isArray " , 1 ) , FunctionData { Operation : : IsArray } } ,
{ std : : make_pair ( " isBoolean " , 1 ) , FunctionData { Operation : : IsBoolean } } ,
{ std : : make_pair ( " isFloat " , 1 ) , FunctionData { Operation : : IsFloat } } ,
{ std : : make_pair ( " isInteger " , 1 ) , FunctionData { Operation : : IsInteger } } ,
{ std : : make_pair ( " isNumber " , 1 ) , FunctionData { Operation : : IsNumber } } ,
{ std : : make_pair ( " isObject " , 1 ) , FunctionData { Operation : : IsObject } } ,
{ std : : make_pair ( " isString " , 1 ) , FunctionData { Operation : : IsString } } ,
{ std : : make_pair ( " last " , 1 ) , FunctionData { Operation : : Last } } ,
{ std : : make_pair ( " length " , 1 ) , FunctionData { Operation : : Length } } ,
{ std : : make_pair ( " lower " , 1 ) , FunctionData { Operation : : Lower } } ,
{ std : : make_pair ( " max " , 1 ) , FunctionData { Operation : : Max } } ,
{ std : : make_pair ( " min " , 1 ) , FunctionData { Operation : : Min } } ,
{ std : : make_pair ( " odd " , 1 ) , FunctionData { Operation : : Odd } } ,
{ std : : make_pair ( " range " , 1 ) , FunctionData { Operation : : Range } } ,
{ std : : make_pair ( " round " , 2 ) , FunctionData { Operation : : Round } } ,
{ std : : make_pair ( " sort " , 1 ) , FunctionData { Operation : : Sort } } ,
{ std : : make_pair ( " upper " , 1 ) , FunctionData { Operation : : Upper } } ,
{ std : : make_pair ( " super " , 0 ) , FunctionData { Operation : : Super } } ,
{ std : : make_pair ( " super " , 1 ) , FunctionData { Operation : : Super } } ,
{ std : : make_pair ( " join " , 2 ) , FunctionData { Operation : : Join } } ,
2024-03-12 14:12:04 +08:00
} ;
public :
void add_builtin ( nonstd : : string_view name , int num_args , Operation op )
{
2024-04-09 21:24:07 +08:00
function_storage . emplace ( std : : make_pair ( static_cast < std : : string > ( name ) , num_args ) , FunctionData { op } ) ;
2024-03-12 14:12:04 +08:00
}
void add_callback ( nonstd : : string_view name , int num_args , const CallbackFunction & callback )
{
function_storage . emplace ( std : : make_pair ( static_cast < std : : string > ( name ) , num_args ) ,
FunctionData { Operation : : Callback , callback } ) ;
}
FunctionData find_function ( nonstd : : string_view name , int num_args ) const
{
auto it = function_storage . find ( std : : make_pair ( static_cast < std : : string > ( name ) , num_args ) ) ;
if ( it ! = function_storage . end ( ) ) {
return it - > second ;
// Find variadic function
} else if ( num_args > 0 ) {
it = function_storage . find ( std : : make_pair ( static_cast < std : : string > ( name ) , VARIADIC ) ) ;
if ( it ! = function_storage . end ( ) ) { return it - > second ; }
}
return FunctionData { Operation : : None } ;
}
} ;
} // namespace inja
# endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_
// #include "parser.hpp"
# ifndef INCLUDE_INJA_PARSER_HPP_
# define INCLUDE_INJA_PARSER_HPP_
# include <limits>
# include <queue>
# include <stack>
# include <string>
# include <utility>
# include <vector>
// #include "config.hpp"
// #include "exceptions.hpp"
# ifndef INCLUDE_INJA_EXCEPTIONS_HPP_
# define INCLUDE_INJA_EXCEPTIONS_HPP_
# include <stdexcept>
# include <string>
namespace inja {
struct SourceLocation {
size_t line ;
size_t column ;
} ;
struct InjaError : public std : : runtime_error {
const std : : string type ;
const std : : string message ;
const SourceLocation location ;
explicit InjaError ( const std : : string & type , const std : : string & message )
: std : : runtime_error ( " [inja.exception. " + type + " ] " + message ) ,
type ( type ) ,
message ( message ) ,
location ( { 0 , 0 } )
{ }
explicit InjaError ( const std : : string & type , const std : : string & message , SourceLocation location )
2024-04-09 21:24:07 +08:00
: std : : runtime_error ( " [inja.exception. " + type + " ] (at " + std : : to_string ( location . line ) + " : "
+ std : : to_string ( location . column ) + " ) " + message ) ,
2024-03-12 14:12:04 +08:00
type ( type ) ,
message ( message ) ,
location ( location )
{ }
} ;
struct ParserError : public InjaError {
explicit ParserError ( const std : : string & message , SourceLocation location )
: InjaError ( " parser_error " , message , location )
{ }
} ;
struct RenderError : public InjaError {
explicit RenderError ( const std : : string & message , SourceLocation location )
: InjaError ( " render_error " , message , location )
{ }
} ;
struct FileError : public InjaError {
explicit FileError ( const std : : string & message ) : InjaError ( " file_error " , message ) { }
2024-04-09 21:24:07 +08:00
explicit FileError ( const std : : string & message , SourceLocation location ) : InjaError ( " file_error " , message , location )
2024-03-12 14:12:04 +08:00
{ }
} ;
struct JsonError : public InjaError {
2024-04-09 21:24:07 +08:00
explicit JsonError ( const std : : string & message , SourceLocation location ) : InjaError ( " json_error " , message , location )
2024-03-12 14:12:04 +08:00
{ }
} ;
} // namespace inja
# endif // INCLUDE_INJA_EXCEPTIONS_HPP_
// #include "function_storage.hpp"
// #include "lexer.hpp"
# ifndef INCLUDE_INJA_LEXER_HPP_
# define INCLUDE_INJA_LEXER_HPP_
# include <cctype>
# include <locale>
// #include "config.hpp"
// #include "token.hpp"
# ifndef INCLUDE_INJA_TOKEN_HPP_
# define INCLUDE_INJA_TOKEN_HPP_
# include <string>
// #include "string_view.hpp"
namespace inja {
/*!
* \ brief Helper - class for the inja Lexer .
*/
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"
Plus , // +
Minus , // -
Times , // *
Slash , // /
Percent , // %
Power , // ^
Comma , // ,
Dot , // .
Colon , // :
LeftParen , // (
RightParen , // )
LeftBracket , // [
RightBracket , // ]
LeftBrace , // {
RightBrace , // }
Equal , // ==
NotEqual , // !=
GreaterThan , // >
GreaterEqual , // >=
LessThan , // <
LessEqual , // <=
Unknown ,
Eof ,
} ;
Kind kind { Kind : : Unknown } ;
nonstd : : string_view text ;
explicit constexpr Token ( ) = default ;
explicit 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 ) ;
}
}
} ;
} // namespace inja
# endif // INCLUDE_INJA_TOKEN_HPP_
// #include "utils.hpp"
# ifndef INCLUDE_INJA_UTILS_HPP_
# define INCLUDE_INJA_UTILS_HPP_
# include <algorithm>
# include <fstream>
# include <string>
# include <utility>
// #include "exceptions.hpp"
// #include "string_view.hpp"
namespace inja {
inline void
open_file_or_throw ( const std : : string & path , std : : ifstream & file )
{
file . exceptions ( std : : ifstream : : failbit | std : : ifstream : : badbit ) ;
# ifndef INJA_NOEXCEPTION
try {
file . open ( path ) ;
} catch ( const std : : ios_base : : failure & /*e*/ ) {
INJA_THROW ( FileError ( " failed accessing file at ' " + path + " ' " ) ) ;
}
# else
file . open ( path ) ;
# endif
}
namespace string_view {
inline nonstd : : string_view
slice ( nonstd : : string_view view , size_t start , size_t end )
{
start = std : : min ( start , view . size ( ) ) ;
2024-04-09 21:24:07 +08:00
end = std : : min ( std : : max ( start , end ) , view . size ( ) ) ;
2024-03-12 14:12:04 +08:00
return view . substr ( start , end - start ) ;
}
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_view
inline SourceLocation
get_source_location ( nonstd : : string_view content , size_t pos )
{
// Get line and offset position (starts at 1:1)
2024-04-09 21:24:07 +08:00
auto sliced = string_view : : slice ( content , 0 , pos ) ;
2024-03-12 14:12:04 +08:00
std : : size_t last_newline = sliced . rfind ( " \n " ) ;
if ( last_newline = = nonstd : : string_view : : npos ) { return { 1 , sliced . length ( ) + 1 } ; }
// Count newlines
2024-04-09 21:24:07 +08:00
size_t count_lines = 0 ;
2024-03-12 14:12:04 +08:00
size_t search_start = 0 ;
while ( search_start < = sliced . size ( ) ) {
search_start = sliced . find ( " \n " , search_start ) + 1 ;
if ( search_start = = 0 ) { break ; }
count_lines + = 1 ;
}
return { count_lines + 1 , sliced . length ( ) - last_newline } ;
}
inline void
replace_substring ( std : : string & s , const std : : string & f , const std : : string & t )
{
if ( f . empty ( ) ) return ;
for ( auto pos = s . find ( f ) ; // find first occurrence of f
pos ! = std : : string : : npos ; // make sure f was found
s . replace ( pos , f . size ( ) , t ) , // replace with t, and
pos = s . find ( f , pos + t . size ( ) ) ) // find next occurrence of f
{ }
}
} // namespace inja
# endif // INCLUDE_INJA_UTILS_HPP_
namespace inja {
/*!
* \ brief Class for lexing an inja Template .
*/
class Lexer {
enum class State {
Text ,
ExpressionStart ,
ExpressionStartForceLstrip ,
ExpressionBody ,
LineStart ,
LineBody ,
StatementStart ,
StatementStartNoLstrip ,
StatementStartForceLstrip ,
StatementBody ,
CommentStart ,
CommentStartForceLstrip ,
CommentBody ,
} ;
enum class MinusState {
Operator ,
Number ,
} ;
const LexerConfig & config ;
State state ;
MinusState minus_state ;
nonstd : : string_view m_in ;
size_t tok_start ;
size_t pos ;
Token scan_body ( nonstd : : string_view close ,
Token : : Kind closeKind ,
nonstd : : string_view close_trim = nonstd : : string_view ( ) ,
2024-04-09 21:24:07 +08:00
bool trim = false )
2024-03-12 14:12:04 +08:00
{
again :
// skip whitespace (except for \n as it might be a close)
if ( tok_start > = m_in . size ( ) ) { return make_token ( Token : : Kind : : Eof ) ; }
const char ch = m_in [ tok_start ] ;
if ( ch = = ' ' | | ch = = ' \t ' | | ch = = ' \r ' ) {
tok_start + = 1 ;
goto again ;
}
// check for close
2024-04-09 21:24:07 +08:00
if ( ! close_trim . empty ( ) & & inja : : string_view : : starts_with ( m_in . substr ( tok_start ) , close_trim ) ) {
state = State : : Text ;
pos = tok_start + close_trim . size ( ) ;
2024-03-12 14:12:04 +08:00
const Token tok = make_token ( closeKind ) ;
skip_whitespaces_and_newlines ( ) ;
return tok ;
}
if ( inja : : string_view : : starts_with ( m_in . substr ( tok_start ) , close ) ) {
2024-04-09 21:24:07 +08:00
state = State : : Text ;
pos = tok_start + close . size ( ) ;
2024-03-12 14:12:04 +08:00
const Token tok = make_token ( closeKind ) ;
if ( trim ) { skip_whitespaces_and_first_newline ( ) ; }
return tok ;
}
// skip \n
if ( ch = = ' \n ' ) {
tok_start + = 1 ;
goto again ;
}
pos = tok_start + 1 ;
if ( std : : isalpha ( ch ) ) {
minus_state = MinusState : : Operator ;
return scan_id ( ) ;
}
const MinusState current_minus_state = minus_state ;
if ( minus_state = = MinusState : : Operator ) { minus_state = MinusState : : Number ; }
switch ( ch ) {
case ' + ' :
return make_token ( Token : : Kind : : Plus ) ;
case ' - ' :
2024-04-09 21:24:07 +08:00
if ( current_minus_state = = MinusState : : Operator ) { return make_token ( Token : : Kind : : Minus ) ; }
2024-03-12 14:12:04 +08:00
return scan_number ( ) ;
case ' * ' :
return make_token ( Token : : Kind : : Times ) ;
case ' / ' :
return make_token ( Token : : Kind : : Slash ) ;
case ' ^ ' :
return make_token ( Token : : Kind : : Power ) ;
case ' % ' :
return make_token ( Token : : Kind : : Percent ) ;
case ' . ' :
return make_token ( Token : : Kind : : Dot ) ;
case ' , ' :
return make_token ( Token : : Kind : : Comma ) ;
case ' : ' :
return make_token ( Token : : Kind : : Colon ) ;
case ' ( ' :
return make_token ( Token : : Kind : : LeftParen ) ;
case ' ) ' :
minus_state = MinusState : : Operator ;
return make_token ( Token : : Kind : : RightParen ) ;
case ' [ ' :
return make_token ( Token : : Kind : : LeftBracket ) ;
case ' ] ' :
minus_state = MinusState : : Operator ;
return make_token ( Token : : Kind : : RightBracket ) ;
case ' { ' :
return make_token ( Token : : Kind : : LeftBrace ) ;
case ' } ' :
minus_state = MinusState : : Operator ;
return make_token ( Token : : Kind : : RightBrace ) ;
case ' > ' :
if ( pos < m_in . size ( ) & & m_in [ pos ] = = ' = ' ) {
pos + = 1 ;
return make_token ( Token : : Kind : : GreaterEqual ) ;
}
return make_token ( Token : : Kind : : GreaterThan ) ;
case ' < ' :
if ( pos < m_in . size ( ) & & m_in [ pos ] = = ' = ' ) {
pos + = 1 ;
return make_token ( Token : : Kind : : LessEqual ) ;
}
return make_token ( Token : : Kind : : LessThan ) ;
case ' = ' :
if ( pos < m_in . size ( ) & & m_in [ pos ] = = ' = ' ) {
pos + = 1 ;
return make_token ( Token : : Kind : : Equal ) ;
}
return make_token ( Token : : Kind : : Unknown ) ;
case ' ! ' :
if ( pos < m_in . size ( ) & & m_in [ pos ] = = ' = ' ) {
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 ' :
minus_state = MinusState : : Operator ;
return scan_number ( ) ;
case ' _ ' :
case ' @ ' :
case ' $ ' :
minus_state = MinusState : : Operator ;
return scan_id ( ) ;
default :
return make_token ( Token : : Kind : : Unknown ) ;
}
}
Token scan_id ( )
{
for ( ; ; ) {
if ( pos > = m_in . size ( ) ) { break ; }
const char ch = m_in [ pos ] ;
if ( ! std : : isalnum ( ch ) & & ch ! = ' . ' & & ch ! = ' / ' & & ch ! = ' _ ' & & ch ! = ' - ' ) { break ; }
pos + = 1 ;
}
return make_token ( Token : : Kind : : Id ) ;
}
Token scan_number ( )
{
for ( ; ; ) {
if ( pos > = m_in . size ( ) ) { break ; }
const char ch = m_in [ pos ] ;
// be very permissive in lexer (we'll catch errors when conversion happens)
2024-04-09 21:24:07 +08:00
if ( ! std : : isdigit ( ch ) & & ch ! = ' . ' & & ch ! = ' e ' & & ch ! = ' E ' & & ch ! = ' + ' & & ch ! = ' - ' ) { break ; }
2024-03-12 14:12:04 +08:00
pos + = 1 ;
}
return make_token ( Token : : Kind : : Number ) ;
}
Token scan_string ( )
{
bool escape { false } ;
for ( ; ; ) {
if ( pos > = m_in . size ( ) ) { break ; }
const char ch = m_in [ pos + + ] ;
if ( ch = = ' \\ ' ) {
escape = true ;
} else if ( ! escape & & ch = = m_in [ tok_start ] ) {
break ;
} else {
escape = false ;
}
}
return make_token ( Token : : Kind : : String ) ;
}
2024-04-09 21:24:07 +08:00
Token make_token ( Token : : Kind kind ) const { return Token ( kind , string_view : : slice ( m_in , tok_start , pos ) ) ; }
2024-03-12 14:12:04 +08:00
void skip_whitespaces_and_newlines ( )
{
if ( pos < m_in . size ( ) ) {
while ( pos < m_in . size ( )
2024-04-09 21:24:07 +08:00
& & ( m_in [ pos ] = = ' ' | | m_in [ pos ] = = ' \t ' | | m_in [ pos ] = = ' \n ' | | m_in [ pos ] = = ' \r ' ) ) {
2024-03-12 14:12:04 +08:00
pos + = 1 ;
}
}
}
void skip_whitespaces_and_first_newline ( )
{
if ( pos < m_in . size ( ) ) {
while ( pos < m_in . size ( ) & & ( m_in [ pos ] = = ' ' | | m_in [ pos ] = = ' \t ' ) ) { pos + = 1 ; }
}
if ( pos < m_in . size ( ) ) {
const char ch = m_in [ pos ] ;
if ( ch = = ' \n ' ) {
pos + = 1 ;
} else if ( ch = = ' \r ' ) {
pos + = 1 ;
if ( pos < m_in . size ( ) & & m_in [ pos ] = = ' \n ' ) { pos + = 1 ; }
}
}
}
static nonstd : : string_view clear_final_line_if_whitespace ( nonstd : : string_view text )
{
nonstd : : string_view result = text ;
while ( ! result . empty ( ) ) {
const char ch = result . back ( ) ;
if ( ch = = ' ' | | ch = = ' \t ' ) {
result . remove_suffix ( 1 ) ;
} else if ( ch = = ' \n ' | | ch = = ' \r ' ) {
break ;
} else {
return text ;
}
}
return result ;
}
public :
2024-04-09 21:24:07 +08:00
explicit Lexer ( const LexerConfig & config ) : config ( config ) , state ( State : : Text ) , minus_state ( MinusState : : Number ) { }
2024-03-12 14:12:04 +08:00
SourceLocation current_position ( ) const { return get_source_location ( m_in , tok_start ) ; }
void start ( nonstd : : string_view input )
{
2024-04-09 21:24:07 +08:00
m_in = input ;
tok_start = 0 ;
pos = 0 ;
state = State : : Text ;
2024-03-12 14:12:04 +08:00
minus_state = MinusState : : Number ;
// Consume byte order mark (BOM) for UTF-8
if ( inja : : string_view : : starts_with ( m_in , " \xEF \xBB \xBF " ) ) { m_in = m_in . substr ( 3 ) ; }
}
Token scan ( )
{
tok_start = pos ;
again :
if ( tok_start > = m_in . size ( ) ) { return make_token ( Token : : Kind : : Eof ) ; }
switch ( state ) {
default :
case State : : Text : {
// fast-scan to first open character
const size_t open_start = m_in . substr ( pos ) . find_first_of ( config . open_chars ) ;
if ( open_start = = nonstd : : string_view : : npos ) {
// didn't find open, return remaining text as text token
pos = m_in . size ( ) ;
return make_token ( Token : : Kind : : Text ) ;
}
pos + = open_start ;
// try to match one of the opening sequences, and get the close
nonstd : : string_view open_str = m_in . substr ( pos ) ;
2024-04-09 21:24:07 +08:00
bool must_lstrip = false ;
2024-03-12 14:12:04 +08:00
if ( inja : : string_view : : starts_with ( open_str , config . expression_open ) ) {
if ( inja : : string_view : : starts_with ( open_str , config . expression_open_force_lstrip ) ) {
2024-04-09 21:24:07 +08:00
state = State : : ExpressionStartForceLstrip ;
2024-03-12 14:12:04 +08:00
must_lstrip = true ;
} else {
state = State : : ExpressionStart ;
}
} else if ( inja : : string_view : : starts_with ( open_str , config . statement_open ) ) {
if ( inja : : string_view : : starts_with ( open_str , config . statement_open_no_lstrip ) ) {
state = State : : StatementStartNoLstrip ;
2024-04-09 21:24:07 +08:00
} else if ( inja : : string_view : : starts_with ( open_str , config . statement_open_force_lstrip ) ) {
state = State : : StatementStartForceLstrip ;
2024-03-12 14:12:04 +08:00
must_lstrip = true ;
} else {
2024-04-09 21:24:07 +08:00
state = State : : StatementStart ;
2024-03-12 14:12:04 +08:00
must_lstrip = config . lstrip_blocks ;
}
} else if ( inja : : string_view : : starts_with ( open_str , config . comment_open ) ) {
if ( inja : : string_view : : starts_with ( open_str , config . comment_open_force_lstrip ) ) {
2024-04-09 21:24:07 +08:00
state = State : : CommentStartForceLstrip ;
2024-03-12 14:12:04 +08:00
must_lstrip = true ;
} else {
2024-04-09 21:24:07 +08:00
state = State : : CommentStart ;
2024-03-12 14:12:04 +08:00
must_lstrip = config . lstrip_blocks ;
}
} else if ( ( pos = = 0 | | m_in [ pos - 1 ] = = ' \n ' )
& & inja : : string_view : : starts_with ( open_str , config . line_statement ) ) {
state = State : : LineStart ;
} else {
pos + = 1 ; // wasn't actually an opening sequence
goto again ;
}
nonstd : : string_view text = string_view : : slice ( m_in , tok_start , 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 ) ;
}
case State : : ExpressionStart : {
state = State : : ExpressionBody ;
pos + = config . expression_open . size ( ) ;
return make_token ( Token : : Kind : : ExpressionOpen ) ;
}
case State : : ExpressionStartForceLstrip : {
state = State : : ExpressionBody ;
pos + = config . expression_open_force_lstrip . size ( ) ;
return make_token ( Token : : Kind : : ExpressionOpen ) ;
}
case State : : LineStart : {
state = State : : LineBody ;
pos + = config . line_statement . size ( ) ;
return make_token ( Token : : Kind : : LineStatementOpen ) ;
}
case State : : StatementStart : {
state = State : : StatementBody ;
pos + = config . statement_open . size ( ) ;
return make_token ( Token : : Kind : : StatementOpen ) ;
}
case State : : StatementStartNoLstrip : {
state = State : : StatementBody ;
pos + = config . statement_open_no_lstrip . size ( ) ;
return make_token ( Token : : Kind : : StatementOpen ) ;
}
case State : : StatementStartForceLstrip : {
state = State : : StatementBody ;
pos + = config . statement_open_force_lstrip . size ( ) ;
return make_token ( Token : : Kind : : StatementOpen ) ;
}
case State : : CommentStart : {
state = State : : CommentBody ;
pos + = config . comment_open . size ( ) ;
return make_token ( Token : : Kind : : CommentOpen ) ;
}
case State : : CommentStartForceLstrip : {
state = State : : CommentBody ;
pos + = config . comment_open_force_lstrip . size ( ) ;
return make_token ( Token : : Kind : : CommentOpen ) ;
}
case State : : ExpressionBody :
return scan_body ( config . expression_close ,
Token : : Kind : : ExpressionClose ,
config . expression_close_force_rstrip ) ;
case State : : LineBody :
return scan_body ( " \n " , Token : : Kind : : LineStatementClose ) ;
case State : : StatementBody :
return scan_body ( config . statement_close ,
Token : : Kind : : StatementClose ,
config . statement_close_force_rstrip ,
config . trim_blocks ) ;
case State : : CommentBody : {
// fast-scan to comment close
const size_t end = m_in . substr ( pos ) . find ( config . comment_close ) ;
if ( end = = nonstd : : string_view : : npos ) {
pos = m_in . size ( ) ;
return make_token ( Token : : Kind : : Eof ) ;
}
// Check for trim pattern
const bool must_rstrip
2024-04-09 21:24:07 +08:00
= inja : : string_view : : starts_with ( m_in . substr ( pos + end - 1 ) , config . comment_close_force_rstrip ) ;
2024-03-12 14:12:04 +08:00
// return the entire comment in the close token
state = State : : Text ;
pos + = end + config . comment_close . size ( ) ;
Token tok = make_token ( Token : : Kind : : CommentClose ) ;
if ( must_rstrip | | config . trim_blocks ) { skip_whitespaces_and_first_newline ( ) ; }
return tok ;
}
}
}
const LexerConfig & get_config ( ) const { return config ; }
} ;
} // namespace inja
# endif // INCLUDE_INJA_LEXER_HPP_
// #include "node.hpp"
# ifndef INCLUDE_INJA_NODE_HPP_
# define INCLUDE_INJA_NODE_HPP_
# include <string>
# include <utility>
# include <nlohmann/json.hpp>
// #include "function_storage.hpp"
// #include "string_view.hpp"
namespace inja {
class NodeVisitor ;
class BlockNode ;
class TextNode ;
class ExpressionNode ;
class LiteralNode ;
class JsonNode ;
class FunctionNode ;
class ExpressionListNode ;
class StatementNode ;
class ForStatementNode ;
class ForArrayStatementNode ;
class ForObjectStatementNode ;
class IfStatementNode ;
class IncludeStatementNode ;
class ExtendsStatementNode ;
class BlockStatementNode ;
class SetStatementNode ;
class NodeVisitor {
public :
virtual ~ NodeVisitor ( ) = default ;
2024-04-09 21:24:07 +08:00
virtual void visit ( const BlockNode & node ) = 0 ;
virtual void visit ( const TextNode & node ) = 0 ;
virtual void visit ( const ExpressionNode & node ) = 0 ;
virtual void visit ( const LiteralNode & node ) = 0 ;
virtual void visit ( const JsonNode & node ) = 0 ;
virtual void visit ( const FunctionNode & node ) = 0 ;
virtual void visit ( const ExpressionListNode & node ) = 0 ;
virtual void visit ( const StatementNode & node ) = 0 ;
virtual void visit ( const ForStatementNode & node ) = 0 ;
virtual void visit ( const ForArrayStatementNode & node ) = 0 ;
2024-03-12 14:12:04 +08:00
virtual void visit ( const ForObjectStatementNode & node ) = 0 ;
2024-04-09 21:24:07 +08:00
virtual void visit ( const IfStatementNode & node ) = 0 ;
virtual void visit ( const IncludeStatementNode & node ) = 0 ;
virtual void visit ( const ExtendsStatementNode & node ) = 0 ;
virtual void visit ( const BlockStatementNode & node ) = 0 ;
virtual void visit ( const SetStatementNode & node ) = 0 ;
2024-03-12 14:12:04 +08:00
} ;
/*!
* \ brief Base node class for the abstract syntax tree ( AST ) .
*/
class AstNode {
public :
virtual void accept ( NodeVisitor & v ) const = 0 ;
size_t pos ;
AstNode ( size_t pos ) : pos ( pos ) { }
virtual ~ AstNode ( ) { }
} ;
class BlockNode : public AstNode {
public :
std : : vector < std : : shared_ptr < AstNode > > nodes ;
explicit BlockNode ( ) : AstNode ( 0 ) { }
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; }
} ;
class TextNode : public AstNode {
public :
const size_t length ;
explicit TextNode ( size_t pos , size_t length ) : AstNode ( pos ) , length ( length ) { }
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; }
} ;
class ExpressionNode : public AstNode {
public :
explicit ExpressionNode ( size_t pos ) : AstNode ( pos ) { }
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; }
} ;
class LiteralNode : public ExpressionNode {
public :
const nlohmann : : json value ;
2024-04-09 21:24:07 +08:00
explicit LiteralNode ( const nlohmann : : json & value , size_t pos ) : ExpressionNode ( pos ) , value ( value ) { }
2024-03-12 14:12:04 +08:00
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; }
} ;
class JsonNode : public ExpressionNode {
public :
const std : : string name ;
const json : : json_pointer ptr ;
static std : : string convert_dot_to_json_ptr ( nonstd : : string_view ptr_name )
{
std : : string result ;
do {
nonstd : : string_view part ;
std : : tie ( part , ptr_name ) = string_view : : split ( ptr_name , ' . ' ) ;
result . push_back ( ' / ' ) ;
result . append ( part . begin ( ) , part . end ( ) ) ;
} while ( ! ptr_name . empty ( ) ) ;
return result ;
}
explicit JsonNode ( nonstd : : string_view ptr_name , size_t pos )
: ExpressionNode ( pos ) ,
name ( ptr_name ) ,
ptr ( json : : json_pointer ( convert_dot_to_json_ptr ( ptr_name ) ) )
{ }
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; }
} ;
class FunctionNode : public ExpressionNode {
using Op = FunctionStorage : : Operation ;
public :
enum class Associativity {
Left ,
Right ,
} ;
unsigned int precedence ;
Associativity associativity ;
Op operation ;
std : : string name ;
int number_args ; // Should also be negative -> -1 for unknown number
std : : vector < std : : shared_ptr < ExpressionNode > > arguments ;
CallbackFunction callback ;
explicit FunctionNode ( nonstd : : string_view name , size_t pos )
: ExpressionNode ( pos ) ,
precedence ( 8 ) ,
associativity ( Associativity : : Left ) ,
operation ( Op : : Callback ) ,
name ( name ) ,
number_args ( 1 )
{ }
2024-04-09 21:24:07 +08:00
explicit FunctionNode ( Op operation , size_t pos ) : ExpressionNode ( pos ) , operation ( operation ) , number_args ( 1 )
2024-03-12 14:12:04 +08:00
{
switch ( operation ) {
case Op : : Not : {
2024-04-09 21:24:07 +08:00
number_args = 1 ;
precedence = 4 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : And : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 1 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : Or : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 1 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : In : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 2 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : Equal : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 2 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : NotEqual : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 2 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : Greater : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 2 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : GreaterEqual : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 2 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : Less : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 2 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : LessEqual : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 2 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : Add : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 3 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : Subtract : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 3 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : Multiplication : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 4 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : Division : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 4 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : Power : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 5 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Right ;
} break ;
case Op : : Modulo : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 4 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
case Op : : AtId : {
2024-04-09 21:24:07 +08:00
number_args = 2 ;
precedence = 8 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
} break ;
default : {
2024-04-09 21:24:07 +08:00
precedence = 1 ;
2024-03-12 14:12:04 +08:00
associativity = Associativity : : Left ;
}
}
}
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; }
} ;
class ExpressionListNode : public AstNode {
public :
std : : shared_ptr < ExpressionNode > root ;
explicit ExpressionListNode ( ) : AstNode ( 0 ) { }
explicit ExpressionListNode ( size_t pos ) : AstNode ( pos ) { }
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; }
} ;
class StatementNode : public AstNode {
public :
StatementNode ( size_t pos ) : AstNode ( pos ) { }
virtual void accept ( NodeVisitor & v ) const = 0 ;
} ;
class ForStatementNode : public StatementNode {
public :
ExpressionListNode condition ;
BlockNode body ;
BlockNode * const parent ;
ForStatementNode ( BlockNode * const parent , size_t pos ) : StatementNode ( pos ) , parent ( parent ) { }
virtual void accept ( NodeVisitor & v ) const = 0 ;
} ;
class ForArrayStatementNode : public ForStatementNode {
public :
const std : : string value ;
explicit ForArrayStatementNode ( const std : : string & value , BlockNode * const parent , size_t pos )
: ForStatementNode ( parent , pos ) ,
value ( value )
{ }
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; }
} ;
class ForObjectStatementNode : public ForStatementNode {
public :
const std : : string key ;
const std : : string value ;
explicit ForObjectStatementNode ( const std : : string & key ,
const std : : string & value ,
BlockNode * const parent ,
size_t pos )
: ForStatementNode ( parent , pos ) ,
key ( key ) ,
value ( value )
{ }
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; }
} ;
class IfStatementNode : public StatementNode {
public :
ExpressionListNode condition ;
BlockNode true_statement ;
BlockNode false_statement ;
BlockNode * const parent ;
const bool is_nested ;
bool has_false_statement { false } ;
2024-04-09 21:24:07 +08:00
explicit IfStatementNode ( BlockNode * const parent , size_t pos ) : StatementNode ( pos ) , parent ( parent ) , is_nested ( false )
2024-03-12 14:12:04 +08:00
{ }
explicit IfStatementNode ( bool is_nested , BlockNode * const parent , size_t pos )
: StatementNode ( pos ) ,
parent ( parent ) ,
is_nested ( is_nested )
{ }
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; }
} ;
class IncludeStatementNode : public StatementNode {
public :
const std : : string file ;
2024-04-09 21:24:07 +08:00
explicit IncludeStatementNode ( const std : : string & file , size_t pos ) : StatementNode ( pos ) , file ( file ) { }
2024-03-12 14:12:04 +08:00
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; }
} ;
class ExtendsStatementNode : public StatementNode {
public :
const std : : string file ;
2024-04-09 21:24:07 +08:00
explicit ExtendsStatementNode ( const std : : string & file , size_t pos ) : StatementNode ( pos ) , file ( file ) { }
2024-03-12 14:12:04 +08:00
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; } ;
} ;
class BlockStatementNode : public StatementNode {
public :
const std : : string name ;
BlockNode block ;
BlockNode * const parent ;
explicit BlockStatementNode ( BlockNode * const parent , const std : : string & name , size_t pos )
: StatementNode ( pos ) ,
name ( name ) ,
parent ( parent )
{ }
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; } ;
} ;
class SetStatementNode : public StatementNode {
public :
const std : : string key ;
ExpressionListNode expression ;
explicit SetStatementNode ( const std : : string & key , size_t pos ) : StatementNode ( pos ) , key ( key ) { }
void accept ( NodeVisitor & v ) const { v . visit ( * this ) ; }
} ;
} // namespace inja
# endif // INCLUDE_INJA_NODE_HPP_
// #include "template.hpp"
# ifndef INCLUDE_INJA_TEMPLATE_HPP_
# define INCLUDE_INJA_TEMPLATE_HPP_
# include <map>
# include <memory>
# include <string>
# include <vector>
// #include "node.hpp"
// #include "statistics.hpp"
# ifndef INCLUDE_INJA_STATISTICS_HPP_
# define INCLUDE_INJA_STATISTICS_HPP_
// #include "node.hpp"
namespace inja {
/*!
* \ brief A class for counting statistics on a Template .
*/
class StatisticsVisitor : public NodeVisitor {
void visit ( const BlockNode & node )
{
for ( auto & n : node . nodes ) { n - > accept ( * this ) ; }
}
void visit ( const TextNode & ) { }
void visit ( const ExpressionNode & ) { }
void visit ( const LiteralNode & ) { }
void visit ( const JsonNode & ) { variable_counter + = 1 ; }
void visit ( const FunctionNode & node )
{
for ( auto & n : node . arguments ) { n - > accept ( * this ) ; }
}
void visit ( const ExpressionListNode & node ) { node . root - > accept ( * this ) ; }
void visit ( const StatementNode & ) { }
void visit ( const ForStatementNode & ) { }
void visit ( const ForArrayStatementNode & node )
{
node . condition . accept ( * this ) ;
node . body . accept ( * this ) ;
}
void visit ( const ForObjectStatementNode & node )
{
node . condition . accept ( * this ) ;
node . body . accept ( * this ) ;
}
void visit ( const IfStatementNode & node )
{
node . condition . accept ( * this ) ;
node . true_statement . accept ( * this ) ;
node . false_statement . accept ( * this ) ;
}
void visit ( const IncludeStatementNode & ) { }
void visit ( const ExtendsStatementNode & ) { }
void visit ( const BlockStatementNode & node ) { node . block . accept ( * this ) ; }
void visit ( const SetStatementNode & ) { }
public :
unsigned int variable_counter ;
explicit StatisticsVisitor ( ) : variable_counter ( 0 ) { }
} ;
} // namespace inja
# endif // INCLUDE_INJA_STATISTICS_HPP_
namespace inja {
/*!
* \ brief The main inja Template .
*/
struct Template {
BlockNode root ;
std : : string content ;
std : : map < std : : string , std : : shared_ptr < BlockStatementNode > > block_storage ;
explicit Template ( ) { }
explicit Template ( const std : : string & content ) : content ( content ) { }
/// Return number of variables (total number, not distinct ones) in the template
int count_variables ( )
{
auto statistic_visitor = StatisticsVisitor ( ) ;
root . accept ( statistic_visitor ) ;
return statistic_visitor . variable_counter ;
}
} ;
using TemplateStorage = std : : map < std : : string , Template > ;
} // namespace inja
# endif // INCLUDE_INJA_TEMPLATE_HPP_
// #include "token.hpp"
// #include "utils.hpp"
# include <nlohmann/json.hpp>
namespace inja {
/*!
* \ brief Class for parsing an inja Template .
*/
class Parser {
const ParserConfig & config ;
Lexer lexer ;
TemplateStorage & template_storage ;
const FunctionStorage & function_storage ;
Token tok , peek_tok ;
bool have_peek_tok { false } ;
size_t current_paren_level { 0 } ;
size_t current_bracket_level { 0 } ;
size_t current_brace_level { 0 } ;
nonstd : : string_view json_literal_start ;
BlockNode * current_block { nullptr } ;
ExpressionListNode * current_expression_list { nullptr } ;
std : : stack < std : : pair < FunctionNode * , size_t > > function_stack ;
std : : vector < std : : shared_ptr < ExpressionNode > > arguments ;
std : : stack < std : : shared_ptr < FunctionNode > > operator_stack ;
std : : stack < IfStatementNode * > if_statement_stack ;
std : : stack < ForStatementNode * > for_statement_stack ;
std : : stack < BlockStatementNode * > block_statement_stack ;
inline void throw_parser_error ( const std : : string & message )
{
INJA_THROW ( ParserError ( message , lexer . current_position ( ) ) ) ;
}
inline void get_next_token ( )
{
if ( have_peek_tok ) {
2024-04-09 21:24:07 +08:00
tok = peek_tok ;
2024-03-12 14:12:04 +08:00
have_peek_tok = false ;
} else {
tok = lexer . scan ( ) ;
}
}
inline void get_peek_token ( )
{
if ( ! have_peek_tok ) {
2024-04-09 21:24:07 +08:00
peek_tok = lexer . scan ( ) ;
2024-03-12 14:12:04 +08:00
have_peek_tok = true ;
}
}
inline void add_json_literal ( const char * content_ptr )
{
nonstd : : string_view json_text ( json_literal_start . data ( ) ,
2024-04-09 21:24:07 +08:00
tok . text . data ( ) - json_literal_start . data ( ) + tok . text . size ( ) ) ;
arguments . emplace_back ( std : : make_shared < LiteralNode > ( json : : parse ( json_text ) , json_text . data ( ) - content_ptr ) ) ;
2024-03-12 14:12:04 +08:00
}
inline void add_operator ( )
{
auto function = operator_stack . top ( ) ;
operator_stack . pop ( ) ;
for ( int i = 0 ; i < function - > number_args ; + + i ) {
function - > arguments . insert ( function - > arguments . begin ( ) , arguments . back ( ) ) ;
arguments . pop_back ( ) ;
}
arguments . emplace_back ( function ) ;
}
void add_to_template_storage ( nonstd : : string_view path , std : : string & template_name )
{
if ( config . search_included_templates_in_files
& & template_storage . find ( template_name ) = = template_storage . end ( ) ) {
// Build the relative path
template_name = static_cast < std : : string > ( path ) + template_name ;
if ( template_name . compare ( 0 , 2 , " ./ " ) = = 0 ) { template_name . erase ( 0 , 2 ) ; }
if ( template_storage . find ( template_name ) = = template_storage . end ( ) ) {
auto include_template = Template ( load_file ( template_name ) ) ;
template_storage . emplace ( template_name , include_template ) ;
parse_into_template ( template_storage [ template_name ] , template_name ) ;
}
}
}
bool parse_expression ( Template & tmpl , Token : : Kind closing )
{
while ( tok . kind ! = closing & & tok . kind ! = Token : : Kind : : Eof ) {
// Literals
switch ( tok . kind ) {
case Token : : Kind : : String : {
if ( current_brace_level = = 0 & & current_bracket_level = = 0 ) {
json_literal_start = tok . text ;
add_json_literal ( tmpl . content . c_str ( ) ) ;
}
} break ;
case Token : : Kind : : Number : {
if ( current_brace_level = = 0 & & current_bracket_level = = 0 ) {
json_literal_start = tok . text ;
add_json_literal ( tmpl . content . c_str ( ) ) ;
}
} break ;
case Token : : Kind : : LeftBracket : {
2024-04-09 21:24:07 +08:00
if ( current_brace_level = = 0 & & current_bracket_level = = 0 ) { json_literal_start = tok . text ; }
2024-03-12 14:12:04 +08:00
current_bracket_level + = 1 ;
} break ;
case Token : : Kind : : LeftBrace : {
2024-04-09 21:24:07 +08:00
if ( current_brace_level = = 0 & & current_bracket_level = = 0 ) { json_literal_start = tok . text ; }
2024-03-12 14:12:04 +08:00
current_brace_level + = 1 ;
} break ;
case Token : : Kind : : RightBracket : {
if ( current_bracket_level = = 0 ) { throw_parser_error ( " unexpected ']' " ) ; }
current_bracket_level - = 1 ;
2024-04-09 21:24:07 +08:00
if ( current_brace_level = = 0 & & current_bracket_level = = 0 ) { add_json_literal ( tmpl . content . c_str ( ) ) ; }
2024-03-12 14:12:04 +08:00
} break ;
case Token : : Kind : : RightBrace : {
if ( current_brace_level = = 0 ) { throw_parser_error ( " unexpected '}' " ) ; }
current_brace_level - = 1 ;
2024-04-09 21:24:07 +08:00
if ( current_brace_level = = 0 & & current_bracket_level = = 0 ) { add_json_literal ( tmpl . content . c_str ( ) ) ; }
2024-03-12 14:12:04 +08:00
} break ;
case Token : : Kind : : Id : {
get_peek_token ( ) ;
// Json Literal
if ( tok . text = = static_cast < decltype ( tok . text ) > ( " true " )
| | tok . text = = static_cast < decltype ( tok . text ) > ( " false " )
| | tok . text = = static_cast < decltype ( tok . text ) > ( " null " ) ) {
if ( current_brace_level = = 0 & & current_bracket_level = = 0 ) {
json_literal_start = tok . text ;
add_json_literal ( tmpl . content . c_str ( ) ) ;
}
// Operator
2024-04-09 21:24:07 +08:00
} else if ( tok . text = = " and " | | tok . text = = " or " | | tok . text = = " in " | | tok . text = = " not " ) {
2024-03-12 14:12:04 +08:00
goto parse_operator ;
// Functions
} else if ( peek_tok . kind = = Token : : Kind : : LeftParen ) {
2024-04-09 21:24:07 +08:00
operator_stack . emplace ( std : : make_shared < FunctionNode > ( static_cast < std : : string > ( tok . text ) ,
tok . text . data ( ) - tmpl . content . c_str ( ) ) ) ;
2024-03-12 14:12:04 +08:00
function_stack . emplace ( operator_stack . top ( ) . get ( ) , current_paren_level ) ;
// Variables
} else {
2024-04-09 21:24:07 +08:00
arguments . emplace_back ( std : : make_shared < JsonNode > ( static_cast < std : : string > ( tok . text ) ,
tok . text . data ( ) - tmpl . content . c_str ( ) ) ) ;
2024-03-12 14:12:04 +08:00
}
// Operators
} break ;
case Token : : Kind : : Equal :
case Token : : Kind : : NotEqual :
case Token : : Kind : : GreaterThan :
case Token : : Kind : : GreaterEqual :
case Token : : Kind : : LessThan :
case Token : : Kind : : LessEqual :
case Token : : Kind : : Plus :
case Token : : Kind : : Minus :
case Token : : Kind : : Times :
case Token : : Kind : : Slash :
case Token : : Kind : : Power :
case Token : : Kind : : Percent :
case Token : : Kind : : Dot : {
parse_operator :
FunctionStorage : : Operation operation ;
switch ( tok . kind ) {
case Token : : Kind : : Id : {
if ( tok . text = = " and " ) {
operation = FunctionStorage : : Operation : : And ;
} else if ( tok . text = = " or " ) {
operation = FunctionStorage : : Operation : : Or ;
} else if ( tok . text = = " in " ) {
operation = FunctionStorage : : Operation : : In ;
} else if ( tok . text = = " not " ) {
operation = FunctionStorage : : Operation : : Not ;
} else {
throw_parser_error ( " unknown operator in parser. " ) ;
}
} break ;
case Token : : Kind : : Equal : {
operation = FunctionStorage : : Operation : : Equal ;
} break ;
case Token : : Kind : : NotEqual : {
operation = FunctionStorage : : Operation : : NotEqual ;
} break ;
case Token : : Kind : : GreaterThan : {
operation = FunctionStorage : : Operation : : Greater ;
} break ;
case Token : : Kind : : GreaterEqual : {
operation = FunctionStorage : : Operation : : GreaterEqual ;
} break ;
case Token : : Kind : : LessThan : {
operation = FunctionStorage : : Operation : : Less ;
} break ;
case Token : : Kind : : LessEqual : {
operation = FunctionStorage : : Operation : : LessEqual ;
} break ;
case Token : : Kind : : Plus : {
operation = FunctionStorage : : Operation : : Add ;
} break ;
case Token : : Kind : : Minus : {
operation = FunctionStorage : : Operation : : Subtract ;
} break ;
case Token : : Kind : : Times : {
operation = FunctionStorage : : Operation : : Multiplication ;
} break ;
case Token : : Kind : : Slash : {
operation = FunctionStorage : : Operation : : Division ;
} break ;
case Token : : Kind : : Power : {
operation = FunctionStorage : : Operation : : Power ;
} break ;
case Token : : Kind : : Percent : {
operation = FunctionStorage : : Operation : : Modulo ;
} break ;
case Token : : Kind : : Dot : {
operation = FunctionStorage : : Operation : : AtId ;
} break ;
default : {
throw_parser_error ( " unknown operator in parser. " ) ;
}
}
2024-04-09 21:24:07 +08:00
auto function_node = std : : make_shared < FunctionNode > ( operation , tok . text . data ( ) - tmpl . content . c_str ( ) ) ;
while ( ! operator_stack . empty ( )
& & ( ( operator_stack . top ( ) - > precedence > function_node - > precedence )
| | ( operator_stack . top ( ) - > precedence = = function_node - > precedence
& & function_node - > associativity = = FunctionNode : : Associativity : : Left ) )
& & ( operator_stack . top ( ) - > operation ! = FunctionStorage : : Operation : : ParenLeft ) ) {
2024-03-12 14:12:04 +08:00
add_operator ( ) ;
}
operator_stack . emplace ( function_node ) ;
} break ;
case Token : : Kind : : Comma : {
if ( current_brace_level = = 0 & & current_bracket_level = = 0 ) {
if ( function_stack . empty ( ) ) { throw_parser_error ( " unexpected ',' " ) ; }
function_stack . top ( ) . first - > number_args + = 1 ;
}
} break ;
case Token : : Kind : : Colon : {
2024-04-09 21:24:07 +08:00
if ( current_brace_level = = 0 & & current_bracket_level = = 0 ) { throw_parser_error ( " unexpected ':' " ) ; }
2024-03-12 14:12:04 +08:00
} break ;
case Token : : Kind : : LeftParen : {
current_paren_level + = 1 ;
2024-04-09 21:24:07 +08:00
operator_stack . emplace ( std : : make_shared < FunctionNode > ( FunctionStorage : : Operation : : ParenLeft ,
tok . text . data ( ) - tmpl . content . c_str ( ) ) ) ;
2024-03-12 14:12:04 +08:00
get_peek_token ( ) ;
if ( peek_tok . kind = = Token : : Kind : : RightParen ) {
2024-04-09 21:24:07 +08:00
if ( ! function_stack . empty ( ) & & function_stack . top ( ) . second = = current_paren_level - 1 ) {
2024-03-12 14:12:04 +08:00
function_stack . top ( ) . first - > number_args = 0 ;
}
}
} break ;
case Token : : Kind : : RightParen : {
current_paren_level - = 1 ;
while ( ! operator_stack . empty ( )
2024-04-09 21:24:07 +08:00
& & operator_stack . top ( ) - > operation ! = FunctionStorage : : Operation : : ParenLeft ) {
2024-03-12 14:12:04 +08:00
add_operator ( ) ;
}
if ( ! operator_stack . empty ( )
& & operator_stack . top ( ) - > operation = = FunctionStorage : : Operation : : ParenLeft ) {
operator_stack . pop ( ) ;
}
if ( ! function_stack . empty ( ) & & function_stack . top ( ) . second = = current_paren_level ) {
2024-04-09 21:24:07 +08:00
auto func = function_stack . top ( ) . first ;
auto function_data = function_storage . find_function ( func - > name , func - > number_args ) ;
2024-03-12 14:12:04 +08:00
if ( function_data . operation = = FunctionStorage : : Operation : : None ) {
throw_parser_error ( " unknown function " + func - > name ) ;
}
func - > operation = function_data . operation ;
if ( function_data . operation = = FunctionStorage : : Operation : : Callback ) {
func - > callback = function_data . callback ;
}
2024-04-09 21:24:07 +08:00
if ( operator_stack . empty ( ) ) { throw_parser_error ( " internal error at function " + func - > name ) ; }
2024-03-12 14:12:04 +08:00
add_operator ( ) ;
function_stack . pop ( ) ;
}
}
default :
break ;
}
get_next_token ( ) ;
}
while ( ! operator_stack . empty ( ) ) { add_operator ( ) ; }
if ( arguments . size ( ) = = 1 ) {
current_expression_list - > root = arguments [ 0 ] ;
2024-04-09 21:24:07 +08:00
arguments = { } ;
2024-03-12 14:12:04 +08:00
} else if ( arguments . size ( ) > 1 ) {
throw_parser_error ( " malformed expression " ) ;
}
return true ;
}
bool parse_statement ( Template & tmpl , Token : : Kind closing , nonstd : : string_view path )
{
if ( tok . kind ! = Token : : Kind : : Id ) { return false ; }
if ( tok . text = = static_cast < decltype ( tok . text ) > ( " if " ) ) {
get_next_token ( ) ;
auto if_statement_node
2024-04-09 21:24:07 +08:00
= std : : make_shared < IfStatementNode > ( current_block , tok . text . data ( ) - tmpl . content . c_str ( ) ) ;
2024-03-12 14:12:04 +08:00
current_block - > nodes . emplace_back ( if_statement_node ) ;
if_statement_stack . emplace ( if_statement_node . get ( ) ) ;
2024-04-09 21:24:07 +08:00
current_block = & if_statement_node - > true_statement ;
2024-03-12 14:12:04 +08:00
current_expression_list = & if_statement_node - > condition ;
if ( ! parse_expression ( tmpl , closing ) ) { return false ; }
} else if ( tok . text = = static_cast < decltype ( tok . text ) > ( " else " ) ) {
if ( if_statement_stack . empty ( ) ) { throw_parser_error ( " else without matching if " ) ; }
auto & if_statement_data = if_statement_stack . top ( ) ;
get_next_token ( ) ;
if_statement_data - > has_false_statement = true ;
2024-04-09 21:24:07 +08:00
current_block = & if_statement_data - > false_statement ;
2024-03-12 14:12:04 +08:00
// Chained else if
if ( tok . kind = = Token : : Kind : : Id & & tok . text = = static_cast < decltype ( tok . text ) > ( " if " ) ) {
get_next_token ( ) ;
auto if_statement_node
2024-04-09 21:24:07 +08:00
= std : : make_shared < IfStatementNode > ( true , current_block , tok . text . data ( ) - tmpl . content . c_str ( ) ) ;
2024-03-12 14:12:04 +08:00
current_block - > nodes . emplace_back ( if_statement_node ) ;
if_statement_stack . emplace ( if_statement_node . get ( ) ) ;
2024-04-09 21:24:07 +08:00
current_block = & if_statement_node - > true_statement ;
2024-03-12 14:12:04 +08:00
current_expression_list = & if_statement_node - > condition ;
if ( ! parse_expression ( tmpl , closing ) ) { return false ; }
}
} else if ( tok . text = = static_cast < decltype ( tok . text ) > ( " endif " ) ) {
if ( if_statement_stack . empty ( ) ) { throw_parser_error ( " endif without matching if " ) ; }
// Nested if statements
while ( if_statement_stack . top ( ) - > is_nested ) { if_statement_stack . pop ( ) ; }
auto & if_statement_data = if_statement_stack . top ( ) ;
get_next_token ( ) ;
current_block = if_statement_data - > parent ;
if_statement_stack . pop ( ) ;
} else if ( tok . text = = static_cast < decltype ( tok . text ) > ( " block " ) ) {
get_next_token ( ) ;
if ( tok . kind ! = Token : : Kind : : Id ) {
throw_parser_error ( " expected block name, got ' " + tok . describe ( ) + " ' " ) ;
}
const std : : string block_name = static_cast < std : : string > ( tok . text ) ;
2024-04-09 21:24:07 +08:00
auto block_statement_node = std : : make_shared < BlockStatementNode > (
current_block ,
block_name ,
tok . text . data ( ) - tmpl . content . c_str ( ) ) ;
2024-03-12 14:12:04 +08:00
current_block - > nodes . emplace_back ( block_statement_node ) ;
block_statement_stack . emplace ( block_statement_node . get ( ) ) ;
current_block = & block_statement_node - > block ;
2024-04-09 21:24:07 +08:00
auto success = tmpl . block_storage . emplace ( block_name , block_statement_node ) ;
if ( ! success . second ) { throw_parser_error ( " block with the name ' " + block_name + " ' does already exist " ) ; }
2024-03-12 14:12:04 +08:00
get_next_token ( ) ;
} else if ( tok . text = = static_cast < decltype ( tok . text ) > ( " endblock " ) ) {
2024-04-09 21:24:07 +08:00
if ( block_statement_stack . empty ( ) ) { throw_parser_error ( " endblock without matching block " ) ; }
2024-03-12 14:12:04 +08:00
auto & block_statement_data = block_statement_stack . top ( ) ;
get_next_token ( ) ;
current_block = block_statement_data - > parent ;
block_statement_stack . pop ( ) ;
} else if ( tok . text = = static_cast < decltype ( tok . text ) > ( " for " ) ) {
get_next_token ( ) ;
// options: for a in arr; for a, b in obj
2024-04-09 21:24:07 +08:00
if ( tok . kind ! = Token : : Kind : : Id ) { throw_parser_error ( " expected id, got ' " + tok . describe ( ) + " ' " ) ; }
2024-03-12 14:12:04 +08:00
Token value_token = tok ;
get_next_token ( ) ;
// Object type
std : : shared_ptr < ForStatementNode > for_statement_node ;
if ( tok . kind = = Token : : Kind : : Comma ) {
get_next_token ( ) ;
2024-04-09 21:24:07 +08:00
if ( tok . kind ! = Token : : Kind : : Id ) { throw_parser_error ( " expected id, got ' " + tok . describe ( ) + " ' " ) ; }
2024-03-12 14:12:04 +08:00
Token key_token = std : : move ( value_token ) ;
2024-04-09 21:24:07 +08:00
value_token = tok ;
2024-03-12 14:12:04 +08:00
get_next_token ( ) ;
for_statement_node = std : : make_shared < ForObjectStatementNode > (
static_cast < std : : string > ( key_token . text ) ,
static_cast < std : : string > ( value_token . text ) ,
current_block ,
tok . text . data ( ) - tmpl . content . c_str ( ) ) ;
// Array type
} else {
for_statement_node = std : : make_shared < ForArrayStatementNode > (
static_cast < std : : string > ( value_token . text ) ,
current_block ,
tok . text . data ( ) - tmpl . content . c_str ( ) ) ;
}
current_block - > nodes . emplace_back ( for_statement_node ) ;
for_statement_stack . emplace ( for_statement_node . get ( ) ) ;
2024-04-09 21:24:07 +08:00
current_block = & for_statement_node - > body ;
2024-03-12 14:12:04 +08:00
current_expression_list = & for_statement_node - > condition ;
if ( tok . kind ! = Token : : Kind : : Id | | tok . text ! = static_cast < decltype ( tok . text ) > ( " in " ) ) {
throw_parser_error ( " expected 'in', got ' " + tok . describe ( ) + " ' " ) ;
}
get_next_token ( ) ;
if ( ! parse_expression ( tmpl , closing ) ) { return false ; }
} else if ( tok . text = = static_cast < decltype ( tok . text ) > ( " endfor " ) ) {
if ( for_statement_stack . empty ( ) ) { throw_parser_error ( " endfor without matching for " ) ; }
auto & for_statement_data = for_statement_stack . top ( ) ;
get_next_token ( ) ;
current_block = for_statement_data - > parent ;
for_statement_stack . pop ( ) ;
} else if ( tok . text = = static_cast < decltype ( tok . text ) > ( " include " ) ) {
get_next_token ( ) ;
if ( tok . kind ! = Token : : Kind : : String ) {
throw_parser_error ( " expected string, got ' " + tok . describe ( ) + " ' " ) ;
}
std : : string template_name = json : : parse ( tok . text ) . get_ref < const std : : string & > ( ) ;
add_to_template_storage ( path , template_name ) ;
current_block - > nodes . emplace_back (
2024-04-09 21:24:07 +08:00
std : : make_shared < IncludeStatementNode > ( template_name , tok . text . data ( ) - tmpl . content . c_str ( ) ) ) ;
2024-03-12 14:12:04 +08:00
get_next_token ( ) ;
} else if ( tok . text = = static_cast < decltype ( tok . text ) > ( " extends " ) ) {
get_next_token ( ) ;
if ( tok . kind ! = Token : : Kind : : String ) {
throw_parser_error ( " expected string, got ' " + tok . describe ( ) + " ' " ) ;
}
std : : string template_name = json : : parse ( tok . text ) . get_ref < const std : : string & > ( ) ;
add_to_template_storage ( path , template_name ) ;
current_block - > nodes . emplace_back (
2024-04-09 21:24:07 +08:00
std : : make_shared < ExtendsStatementNode > ( template_name , tok . text . data ( ) - tmpl . content . c_str ( ) ) ) ;
2024-03-12 14:12:04 +08:00
get_next_token ( ) ;
} else if ( tok . text = = static_cast < decltype ( tok . text ) > ( " set " ) ) {
get_next_token ( ) ;
if ( tok . kind ! = Token : : Kind : : Id ) {
throw_parser_error ( " expected variable name, got ' " + tok . describe ( ) + " ' " ) ;
}
std : : string key = static_cast < std : : string > ( tok . text ) ;
get_next_token ( ) ;
2024-04-09 21:24:07 +08:00
auto set_statement_node = std : : make_shared < SetStatementNode > ( key , tok . text . data ( ) - tmpl . content . c_str ( ) ) ;
2024-03-12 14:12:04 +08:00
current_block - > nodes . emplace_back ( set_statement_node ) ;
current_expression_list = & set_statement_node - > expression ;
if ( tok . text ! = static_cast < decltype ( tok . text ) > ( " = " ) ) {
throw_parser_error ( " expected '=', got ' " + tok . describe ( ) + " ' " ) ;
}
get_next_token ( ) ;
if ( ! parse_expression ( tmpl , closing ) ) { return false ; }
} else {
return false ;
}
return true ;
}
void parse_into ( Template & tmpl , nonstd : : string_view path )
{
lexer . start ( tmpl . content ) ;
current_block = & tmpl . root ;
for ( ; ; ) {
get_next_token ( ) ;
switch ( tok . kind ) {
case Token : : Kind : : Eof : {
if ( ! if_statement_stack . empty ( ) ) { throw_parser_error ( " unmatched if " ) ; }
if ( ! for_statement_stack . empty ( ) ) { throw_parser_error ( " unmatched for " ) ; }
}
return ;
case Token : : Kind : : Text : {
current_block - > nodes . emplace_back (
2024-04-09 21:24:07 +08:00
std : : make_shared < TextNode > ( tok . text . data ( ) - tmpl . content . c_str ( ) , tok . text . size ( ) ) ) ;
2024-03-12 14:12:04 +08:00
} break ;
case Token : : Kind : : StatementOpen : {
get_next_token ( ) ;
if ( ! parse_statement ( tmpl , Token : : Kind : : StatementClose , path ) ) {
throw_parser_error ( " expected statement, got ' " + tok . describe ( ) + " ' " ) ;
}
if ( tok . kind ! = Token : : Kind : : StatementClose ) {
throw_parser_error ( " expected statement close, got ' " + tok . describe ( ) + " ' " ) ;
}
} break ;
case Token : : Kind : : LineStatementOpen : {
get_next_token ( ) ;
if ( ! parse_statement ( tmpl , Token : : Kind : : LineStatementClose , path ) ) {
throw_parser_error ( " expected statement, got ' " + tok . describe ( ) + " ' " ) ;
}
if ( tok . kind ! = Token : : Kind : : LineStatementClose & & tok . kind ! = Token : : Kind : : Eof ) {
2024-04-09 21:24:07 +08:00
throw_parser_error ( " expected line statement close, got ' " + tok . describe ( ) + " ' " ) ;
2024-03-12 14:12:04 +08:00
}
} break ;
case Token : : Kind : : ExpressionOpen : {
get_next_token ( ) ;
auto expression_list_node
= std : : make_shared < ExpressionListNode > ( tok . text . data ( ) - tmpl . content . c_str ( ) ) ;
current_block - > nodes . emplace_back ( expression_list_node ) ;
current_expression_list = expression_list_node . get ( ) ;
if ( ! parse_expression ( tmpl , Token : : Kind : : ExpressionClose ) ) {
throw_parser_error ( " expected expression, got ' " + tok . describe ( ) + " ' " ) ;
}
if ( tok . kind ! = Token : : Kind : : ExpressionClose ) {
throw_parser_error ( " expected expression close, got ' " + tok . describe ( ) + " ' " ) ;
}
} break ;
case Token : : Kind : : CommentOpen : {
get_next_token ( ) ;
if ( tok . kind ! = Token : : Kind : : CommentClose ) {
throw_parser_error ( " expected comment close, got ' " + tok . describe ( ) + " ' " ) ;
}
} break ;
default : {
throw_parser_error ( " unexpected token ' " + tok . describe ( ) + " ' " ) ;
} break ;
}
}
}
public :
explicit Parser ( const ParserConfig & parser_config ,
const LexerConfig & lexer_config ,
TemplateStorage & template_storage ,
const FunctionStorage & function_storage )
: config ( parser_config ) ,
lexer ( lexer_config ) ,
template_storage ( template_storage ) ,
function_storage ( function_storage )
{ }
Template parse ( nonstd : : string_view input , nonstd : : string_view path )
{
auto result = Template ( static_cast < std : : string > ( input ) ) ;
parse_into ( result , path ) ;
return result ;
}
Template parse ( nonstd : : string_view input ) { return parse ( input , " ./ " ) ; }
void parse_into_template ( Template & tmpl , nonstd : : string_view filename )
{
nonstd : : string_view path = filename . substr ( 0 , filename . find_last_of ( " / \\ " ) + 1 ) ;
// StringRef path = sys::path::parent_path(filename);
auto sub_parser = Parser ( config , lexer . get_config ( ) , template_storage , function_storage ) ;
sub_parser . parse_into ( tmpl , path ) ;
}
std : : string load_file ( nonstd : : string_view filename )
{
std : : ifstream file ;
open_file_or_throw ( static_cast < std : : string > ( filename ) , file ) ;
std : : string text ( ( std : : istreambuf_iterator < char > ( file ) ) , std : : istreambuf_iterator < char > ( ) ) ;
return text ;
}
} ;
} // namespace inja
# endif // INCLUDE_INJA_PARSER_HPP_
// #include "renderer.hpp"
# ifndef INCLUDE_INJA_RENDERER_HPP_
# define INCLUDE_INJA_RENDERER_HPP_
# include <algorithm>
# include <numeric>
# include <string>
# include <utility>
# include <vector>
# include <nlohmann/json.hpp>
// #include "config.hpp"
// #include "exceptions.hpp"
// #include "node.hpp"
// #include "template.hpp"
// #include "utils.hpp"
namespace inja {
/*!
* \ brief Class for rendering a Template with data .
*/
class Renderer : public NodeVisitor {
using Op = FunctionStorage : : Operation ;
const RenderConfig config ;
const TemplateStorage & template_storage ;
const FunctionStorage & function_storage ;
const Template * current_template ;
size_t current_level { 0 } ;
std : : vector < const Template * > template_stack ;
std : : vector < const BlockStatementNode * > block_statement_stack ;
const json * json_input ;
std : : ostream * output_stream ;
json json_additional_data ;
json * current_loop_data = & json_additional_data [ " loop " ] ;
std : : vector < std : : shared_ptr < json > > json_tmp_stack ;
std : : stack < const json * > json_eval_stack ;
std : : stack < const JsonNode * > not_found_stack ;
bool break_rendering { false } ;
bool truthy ( const json * data ) const
{
if ( data - > is_boolean ( ) ) {
return data - > get < bool > ( ) ;
} else if ( data - > is_number ( ) ) {
return ( * data ! = 0 ) ;
} else if ( data - > is_null ( ) ) {
return false ;
}
return ! data - > empty ( ) ;
}
void print_json ( const std : : shared_ptr < json > value )
{
if ( value - > is_string ( ) ) {
* output_stream < < value - > get_ref < const json : : string_t & > ( ) ;
} else if ( value - > is_number_integer ( ) ) {
* output_stream < < value - > get < const json : : number_integer_t > ( ) ;
} else if ( value - > is_null ( ) ) {
} else {
* output_stream < < value - > dump ( ) ;
}
}
const std : : shared_ptr < json > eval_expression_list ( const ExpressionListNode & expression_list )
{
if ( ! expression_list . root ) { throw_renderer_error ( " empty expression " , expression_list ) ; }
expression_list . root - > accept ( * this ) ;
if ( json_eval_stack . empty ( ) ) {
throw_renderer_error ( " empty expression " , expression_list ) ;
} else if ( json_eval_stack . size ( ) ! = 1 ) {
throw_renderer_error ( " malformed expression " , expression_list ) ;
}
const auto result = json_eval_stack . top ( ) ;
json_eval_stack . pop ( ) ;
if ( ! result ) {
2024-04-09 21:24:07 +08:00
if ( not_found_stack . empty ( ) ) { throw_renderer_error ( " expression could not be evaluated " , expression_list ) ; }
2024-03-12 14:12:04 +08:00
auto node = not_found_stack . top ( ) ;
not_found_stack . pop ( ) ;
2024-04-09 21:24:07 +08:00
throw_renderer_error ( " variable ' " + static_cast < std : : string > ( node - > name ) + " ' not found " , * node ) ;
2024-03-12 14:12:04 +08:00
}
return std : : make_shared < json > ( * result ) ;
}
void throw_renderer_error ( const std : : string & message , const AstNode & node )
{
SourceLocation loc = get_source_location ( current_template - > content , node . pos ) ;
INJA_THROW ( RenderError ( message , loc ) ) ;
}
template < size_t N , size_t N_start = 0 , bool throw_not_found = true >
std : : array < const json * , N > get_arguments ( const FunctionNode & node )
{
if ( node . arguments . size ( ) < N_start + N ) {
2024-04-09 21:24:07 +08:00
throw_renderer_error ( " function needs " + std : : to_string ( N_start + N ) + " variables, but has only found "
2024-03-12 14:12:04 +08:00
+ std : : to_string ( node . arguments . size ( ) ) ,
node ) ;
}
for ( size_t i = N_start ; i < N_start + N ; i + = 1 ) { node . arguments [ i ] - > accept ( * this ) ; }
if ( json_eval_stack . size ( ) < N ) {
2024-04-09 21:24:07 +08:00
throw_renderer_error ( " function needs " + std : : to_string ( N ) + " variables, but has only found "
2024-03-12 14:12:04 +08:00
+ std : : to_string ( json_eval_stack . size ( ) ) ,
node ) ;
}
std : : array < const json * , N > result ;
for ( size_t i = 0 ; i < N ; i + = 1 ) {
result [ N - i - 1 ] = json_eval_stack . top ( ) ;
json_eval_stack . pop ( ) ;
if ( ! result [ N - i - 1 ] ) {
const auto json_node = not_found_stack . top ( ) ;
not_found_stack . pop ( ) ;
if ( throw_not_found ) {
2024-04-09 21:24:07 +08:00
throw_renderer_error ( " variable ' " + static_cast < std : : string > ( json_node - > name ) + " ' not found " ,
2024-03-12 14:12:04 +08:00
* json_node ) ;
}
}
}
return result ;
}
template < bool throw_not_found = true >
Arguments get_argument_vector ( const FunctionNode & node )
{
const size_t N = node . arguments . size ( ) ;
for ( auto a : node . arguments ) { a - > accept ( * this ) ; }
if ( json_eval_stack . size ( ) < N ) {
2024-04-09 21:24:07 +08:00
throw_renderer_error ( " function needs " + std : : to_string ( N ) + " variables, but has only found "
2024-03-12 14:12:04 +08:00
+ std : : to_string ( json_eval_stack . size ( ) ) ,
node ) ;
}
Arguments result { N } ;
for ( size_t i = 0 ; i < N ; i + = 1 ) {
result [ N - i - 1 ] = json_eval_stack . top ( ) ;
json_eval_stack . pop ( ) ;
if ( ! result [ N - i - 1 ] ) {
const auto json_node = not_found_stack . top ( ) ;
not_found_stack . pop ( ) ;
if ( throw_not_found ) {
2024-04-09 21:24:07 +08:00
throw_renderer_error ( " variable ' " + static_cast < std : : string > ( json_node - > name ) + " ' not found " ,
2024-03-12 14:12:04 +08:00
* json_node ) ;
}
}
}
return result ;
}
void visit ( const BlockNode & node )
{
for ( auto & n : node . nodes ) {
n - > accept ( * this ) ;
if ( break_rendering ) { break ; }
}
}
void visit ( const TextNode & node )
{
output_stream - > write ( current_template - > content . c_str ( ) + node . pos , node . length ) ;
}
void visit ( const ExpressionNode & ) { }
void visit ( const LiteralNode & node ) { json_eval_stack . push ( & node . value ) ; }
void visit ( const JsonNode & node )
{
if ( json_additional_data . contains ( node . ptr ) ) {
json_eval_stack . push ( & ( json_additional_data [ node . ptr ] ) ) ;
} else if ( json_input - > contains ( node . ptr ) ) {
json_eval_stack . push ( & ( * json_input ) [ node . ptr ] ) ;
} else {
// Try to evaluate as a no-argument callback
const auto function_data = function_storage . find_function ( node . name , 0 ) ;
if ( function_data . operation = = FunctionStorage : : Operation : : Callback ) {
Arguments empty_args { } ;
const auto value = std : : make_shared < json > ( function_data . callback ( empty_args ) ) ;
json_tmp_stack . push_back ( value ) ;
json_eval_stack . push ( value . get ( ) ) ;
} else {
json_eval_stack . push ( nullptr ) ;
not_found_stack . emplace ( & node ) ;
}
}
}
void visit ( const FunctionNode & node )
{
std : : shared_ptr < json > result_ptr ;
switch ( node . operation ) {
case Op : : Not : {
const auto args = get_arguments < 1 > ( node ) ;
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( ! truthy ( args [ 0 ] ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : And : {
2024-04-09 21:24:07 +08:00
result_ptr
= std : : make_shared < json > ( truthy ( get_arguments < 1 , 0 > ( node ) [ 0 ] ) & & truthy ( get_arguments < 1 , 1 > ( node ) [ 0 ] ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Or : {
2024-04-09 21:24:07 +08:00
result_ptr
= std : : make_shared < json > ( truthy ( get_arguments < 1 , 0 > ( node ) [ 0 ] ) | | truthy ( get_arguments < 1 , 1 > ( node ) [ 0 ] ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : In : {
const auto args = get_arguments < 2 > ( node ) ;
2024-04-09 21:24:07 +08:00
result_ptr
= std : : make_shared < json > ( std : : find ( args [ 1 ] - > begin ( ) , args [ 1 ] - > end ( ) , * args [ 0 ] ) ! = args [ 1 ] - > end ( ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Equal : {
const auto args = get_arguments < 2 > ( node ) ;
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( * args [ 0 ] = = * args [ 1 ] ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : NotEqual : {
const auto args = get_arguments < 2 > ( node ) ;
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( * args [ 0 ] ! = * args [ 1 ] ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Greater : {
const auto args = get_arguments < 2 > ( node ) ;
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( * args [ 0 ] > * args [ 1 ] ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : GreaterEqual : {
const auto args = get_arguments < 2 > ( node ) ;
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( * args [ 0 ] > = * args [ 1 ] ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Less : {
const auto args = get_arguments < 2 > ( node ) ;
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( * args [ 0 ] < * args [ 1 ] ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : LessEqual : {
const auto args = get_arguments < 2 > ( node ) ;
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( * args [ 0 ] < = * args [ 1 ] ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Add : {
const auto args = get_arguments < 2 > ( node ) ;
if ( args [ 0 ] - > is_string ( ) & & args [ 1 ] - > is_string ( ) ) {
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > (
args [ 0 ] - > get_ref < const std : : string & > ( ) + args [ 1 ] - > get_ref < const std : : string & > ( ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
} else if ( args [ 0 ] - > is_number_integer ( ) & & args [ 1 ] - > is_number_integer ( ) ) {
result_ptr = std : : make_shared < json > ( args [ 0 ] - > get < int > ( ) + args [ 1 ] - > get < int > ( ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
} else {
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( args [ 0 ] - > get < double > ( ) + args [ 1 ] - > get < double > ( ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
}
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Subtract : {
const auto args = get_arguments < 2 > ( node ) ;
if ( args [ 0 ] - > is_number_integer ( ) & & args [ 1 ] - > is_number_integer ( ) ) {
result_ptr = std : : make_shared < json > ( args [ 0 ] - > get < int > ( ) - args [ 1 ] - > get < int > ( ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
} else {
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( args [ 0 ] - > get < double > ( ) - args [ 1 ] - > get < double > ( ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
}
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Multiplication : {
const auto args = get_arguments < 2 > ( node ) ;
if ( args [ 0 ] - > is_number_integer ( ) & & args [ 1 ] - > is_number_integer ( ) ) {
result_ptr = std : : make_shared < json > ( args [ 0 ] - > get < int > ( ) * args [ 1 ] - > get < int > ( ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
} else {
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( args [ 0 ] - > get < double > ( ) * args [ 1 ] - > get < double > ( ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
}
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Division : {
const auto args = get_arguments < 2 > ( node ) ;
if ( args [ 1 ] - > get < double > ( ) = = 0 ) { throw_renderer_error ( " division by zero " , node ) ; }
result_ptr = std : : make_shared < json > ( args [ 0 ] - > get < double > ( ) / args [ 1 ] - > get < double > ( ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Power : {
const auto args = get_arguments < 2 > ( node ) ;
if ( args [ 0 ] - > is_number_integer ( ) & & args [ 1 ] - > get < int > ( ) > = 0 ) {
int result = static_cast < int > ( std : : pow ( args [ 0 ] - > get < int > ( ) , args [ 1 ] - > get < int > ( ) ) ) ;
result_ptr = std : : make_shared < json > ( std : : move ( result ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
} else {
double result = std : : pow ( args [ 0 ] - > get < double > ( ) , args [ 1 ] - > get < int > ( ) ) ;
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( std : : move ( result ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
}
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Modulo : {
const auto args = get_arguments < 2 > ( node ) ;
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( args [ 0 ] - > get < int > ( ) % args [ 1 ] - > get < int > ( ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : AtId : {
const auto container = get_arguments < 1 , 0 , false > ( node ) [ 0 ] ;
node . arguments [ 1 ] - > accept ( * this ) ;
2024-04-09 21:24:07 +08:00
if ( not_found_stack . empty ( ) ) { throw_renderer_error ( " could not find element with given name " , node ) ; }
2024-03-12 14:12:04 +08:00
const auto id_node = not_found_stack . top ( ) ;
not_found_stack . pop ( ) ;
json_eval_stack . pop ( ) ;
json_eval_stack . push ( & container - > at ( id_node - > name ) ) ;
} break ;
case Op : : At : {
const auto args = get_arguments < 2 > ( node ) ;
if ( args [ 0 ] - > is_object ( ) ) {
json_eval_stack . push ( & args [ 0 ] - > at ( args [ 1 ] - > get < std : : string > ( ) ) ) ;
} else {
json_eval_stack . push ( & args [ 0 ] - > at ( args [ 1 ] - > get < int > ( ) ) ) ;
}
} break ;
case Op : : Default : {
const auto test_arg = get_arguments < 1 , 0 , false > ( node ) [ 0 ] ;
json_eval_stack . push ( test_arg ? test_arg : get_arguments < 1 , 1 > ( node ) [ 0 ] ) ;
} break ;
case Op : : DivisibleBy : {
2024-04-09 21:24:07 +08:00
const auto args = get_arguments < 2 > ( node ) ;
2024-03-12 14:12:04 +08:00
const int divisor = args [ 1 ] - > get < int > ( ) ;
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( ( divisor ! = 0 ) & & ( args [ 0 ] - > get < int > ( ) % divisor = = 0 ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Even : {
result_ptr = std : : make_shared < json > ( get_arguments < 1 > ( node ) [ 0 ] - > get < int > ( ) % 2 = = 0 ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Exists : {
auto & & name = get_arguments < 1 > ( node ) [ 0 ] - > get_ref < const std : : string & > ( ) ;
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > (
2024-03-12 14:12:04 +08:00
json_input - > contains ( json : : json_pointer ( JsonNode : : convert_dot_to_json_ptr ( name ) ) ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : ExistsInObject : {
const auto args = get_arguments < 2 > ( node ) ;
2024-04-09 21:24:07 +08:00
auto & & name = args [ 1 ] - > get_ref < const std : : string & > ( ) ;
result_ptr = std : : make_shared < json > ( args [ 0 ] - > find ( name ) ! = args [ 0 ] - > end ( ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : First : {
const auto result = & get_arguments < 1 > ( node ) [ 0 ] - > front ( ) ;
json_eval_stack . push ( result ) ;
} break ;
case Op : : Float : {
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( std : : stod ( get_arguments < 1 > ( node ) [ 0 ] - > get_ref < const std : : string & > ( ) ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Int : {
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( std : : stoi ( get_arguments < 1 > ( node ) [ 0 ] - > get_ref < const std : : string & > ( ) ) ) ;
2024-03-12 14:12:04 +08:00
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Last : {
const auto result = & get_arguments < 1 > ( node ) [ 0 ] - > back ( ) ;
json_eval_stack . push ( result ) ;
} break ;
case Op : : Length : {
const auto val = get_arguments < 1 > ( node ) [ 0 ] ;
if ( val - > is_string ( ) ) {
result_ptr = std : : make_shared < json > ( val - > get_ref < const std : : string & > ( ) . length ( ) ) ;
} else {
result_ptr = std : : make_shared < json > ( val - > size ( ) ) ;
}
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Lower : {
std : : string result = get_arguments < 1 > ( node ) [ 0 ] - > get < std : : string > ( ) ;
std : : transform ( result . begin ( ) , result . end ( ) , result . begin ( ) , : : tolower ) ;
result_ptr = std : : make_shared < json > ( std : : move ( result ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Max : {
2024-04-09 21:24:07 +08:00
const auto args = get_arguments < 1 > ( node ) ;
2024-03-12 14:12:04 +08:00
const auto result = std : : max_element ( args [ 0 ] - > begin ( ) , args [ 0 ] - > end ( ) ) ;
json_eval_stack . push ( & ( * result ) ) ;
} break ;
case Op : : Min : {
2024-04-09 21:24:07 +08:00
const auto args = get_arguments < 1 > ( node ) ;
2024-03-12 14:12:04 +08:00
const auto result = std : : min_element ( args [ 0 ] - > begin ( ) , args [ 0 ] - > end ( ) ) ;
json_eval_stack . push ( & ( * result ) ) ;
} break ;
case Op : : Odd : {
result_ptr = std : : make_shared < json > ( get_arguments < 1 > ( node ) [ 0 ] - > get < int > ( ) % 2 ! = 0 ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Range : {
std : : vector < int > result ( get_arguments < 1 > ( node ) [ 0 ] - > get < int > ( ) ) ;
std : : iota ( result . begin ( ) , result . end ( ) , 0 ) ;
result_ptr = std : : make_shared < json > ( std : : move ( result ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Round : {
2024-04-09 21:24:07 +08:00
const auto args = get_arguments < 2 > ( node ) ;
2024-03-12 14:12:04 +08:00
const int precision = args [ 1 ] - > get < int > ( ) ;
2024-04-09 21:24:07 +08:00
const double result
= std : : round ( args [ 0 ] - > get < double > ( ) * std : : pow ( 10.0 , precision ) ) / std : : pow ( 10.0 , precision ) ;
2024-03-12 14:12:04 +08:00
if ( 0 = = precision ) {
result_ptr = std : : make_shared < json > ( int ( result ) ) ;
} else {
result_ptr = std : : make_shared < json > ( std : : move ( result ) ) ;
}
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Sort : {
2024-04-09 21:24:07 +08:00
result_ptr = std : : make_shared < json > ( get_arguments < 1 > ( node ) [ 0 ] - > get < std : : vector < json > > ( ) ) ;
2024-03-12 14:12:04 +08:00
std : : sort ( result_ptr - > begin ( ) , result_ptr - > end ( ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Upper : {
std : : string result = get_arguments < 1 > ( node ) [ 0 ] - > get < std : : string > ( ) ;
std : : transform ( result . begin ( ) , result . end ( ) , result . begin ( ) , : : toupper ) ;
result_ptr = std : : make_shared < json > ( std : : move ( result ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : IsBoolean : {
result_ptr = std : : make_shared < json > ( get_arguments < 1 > ( node ) [ 0 ] - > is_boolean ( ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : IsNumber : {
result_ptr = std : : make_shared < json > ( get_arguments < 1 > ( node ) [ 0 ] - > is_number ( ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : IsInteger : {
result_ptr = std : : make_shared < json > ( get_arguments < 1 > ( node ) [ 0 ] - > is_number_integer ( ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : IsFloat : {
result_ptr = std : : make_shared < json > ( get_arguments < 1 > ( node ) [ 0 ] - > is_number_float ( ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : IsObject : {
result_ptr = std : : make_shared < json > ( get_arguments < 1 > ( node ) [ 0 ] - > is_object ( ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : IsArray : {
result_ptr = std : : make_shared < json > ( get_arguments < 1 > ( node ) [ 0 ] - > is_array ( ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : IsString : {
result_ptr = std : : make_shared < json > ( get_arguments < 1 > ( node ) [ 0 ] - > is_string ( ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Callback : {
2024-04-09 21:24:07 +08:00
auto args = get_argument_vector ( node ) ;
2024-03-12 14:12:04 +08:00
result_ptr = std : : make_shared < json > ( node . callback ( args ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Super : {
2024-04-09 21:24:07 +08:00
const auto args = get_argument_vector ( node ) ;
const size_t old_level = current_level ;
2024-03-12 14:12:04 +08:00
const size_t level_diff = ( args . size ( ) = = 1 ) ? args [ 0 ] - > get < int > ( ) : 1 ;
2024-04-09 21:24:07 +08:00
const size_t level = current_level + level_diff ;
2024-03-12 14:12:04 +08:00
2024-04-09 21:24:07 +08:00
if ( block_statement_stack . empty ( ) ) { throw_renderer_error ( " super() call is not within a block " , node ) ; }
2024-03-12 14:12:04 +08:00
if ( level < 1 | | level > template_stack . size ( ) - 1 ) {
2024-04-09 21:24:07 +08:00
throw_renderer_error ( " level of super() call does not match parent templates (between 1 and "
+ std : : to_string ( template_stack . size ( ) - 1 ) + " ) " ,
node ) ;
2024-03-12 14:12:04 +08:00
}
const auto current_block_statement = block_statement_stack . back ( ) ;
2024-04-09 21:24:07 +08:00
const Template * new_template = template_stack . at ( level ) ;
const Template * old_template = current_template ;
const auto block_it = new_template - > block_storage . find ( current_block_statement - > name ) ;
2024-03-12 14:12:04 +08:00
if ( block_it ! = new_template - > block_storage . end ( ) ) {
current_template = new_template ;
2024-04-09 21:24:07 +08:00
current_level = level ;
2024-03-12 14:12:04 +08:00
block_it - > second - > block . accept ( * this ) ;
2024-04-09 21:24:07 +08:00
current_level = old_level ;
2024-03-12 14:12:04 +08:00
current_template = old_template ;
} else {
2024-04-09 21:24:07 +08:00
throw_renderer_error ( " could not find block with name ' " + current_block_statement - > name + " ' " , node ) ;
2024-03-12 14:12:04 +08:00
}
result_ptr = std : : make_shared < json > ( nullptr ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : Join : {
2024-04-09 21:24:07 +08:00
const auto args = get_arguments < 2 > ( node ) ;
2024-03-12 14:12:04 +08:00
const auto separator = args [ 1 ] - > get < std : : string > ( ) ;
std : : ostringstream os ;
std : : string sep ;
for ( const auto & value : * args [ 0 ] ) {
os < < sep ;
if ( value . is_string ( ) ) {
os < < value . get < std : : string > ( ) ; // otherwise the value is surrounded with ""
} else {
os < < value ;
}
sep = separator ;
}
result_ptr = std : : make_shared < json > ( os . str ( ) ) ;
json_tmp_stack . push_back ( result_ptr ) ;
json_eval_stack . push ( result_ptr . get ( ) ) ;
} break ;
case Op : : ParenLeft :
case Op : : ParenRight :
case Op : : None :
break ;
}
}
void visit ( const ExpressionListNode & node ) { print_json ( eval_expression_list ( node ) ) ; }
void visit ( const StatementNode & ) { }
void visit ( const ForStatementNode & ) { }
void visit ( const ForArrayStatementNode & node )
{
const auto result = eval_expression_list ( node . condition ) ;
if ( ! result - > is_array ( ) ) { throw_renderer_error ( " object must be an array " , node ) ; }
if ( ! current_loop_data - > empty ( ) ) {
2024-04-09 21:24:07 +08:00
auto tmp = * current_loop_data ; // Because of clang-3
2024-03-12 14:12:04 +08:00
( * current_loop_data ) [ " parent " ] = std : : move ( tmp ) ;
}
2024-04-09 21:24:07 +08:00
size_t index = 0 ;
2024-03-12 14:12:04 +08:00
( * current_loop_data ) [ " is_first " ] = true ;
2024-04-09 21:24:07 +08:00
( * current_loop_data ) [ " is_last " ] = ( result - > size ( ) < = 1 ) ;
2024-03-12 14:12:04 +08:00
for ( auto it = result - > begin ( ) ; it ! = result - > end ( ) ; + + it ) {
json_additional_data [ static_cast < std : : string > ( node . value ) ] = * it ;
2024-04-09 21:24:07 +08:00
( * current_loop_data ) [ " index " ] = index ;
2024-03-12 14:12:04 +08:00
( * current_loop_data ) [ " index1 " ] = index + 1 ;
if ( index = = 1 ) { ( * current_loop_data ) [ " is_first " ] = false ; }
if ( index = = result - > size ( ) - 1 ) { ( * current_loop_data ) [ " is_last " ] = true ; }
node . body . accept ( * this ) ;
+ + index ;
}
json_additional_data [ static_cast < std : : string > ( node . value ) ] . clear ( ) ;
if ( ! ( * current_loop_data ) [ " parent " ] . empty ( ) ) {
2024-04-09 21:24:07 +08:00
const auto tmp = ( * current_loop_data ) [ " parent " ] ;
2024-03-12 14:12:04 +08:00
* current_loop_data = std : : move ( tmp ) ;
} else {
current_loop_data = & json_additional_data [ " loop " ] ;
}
}
void visit ( const ForObjectStatementNode & node )
{
const auto result = eval_expression_list ( node . condition ) ;
if ( ! result - > is_object ( ) ) { throw_renderer_error ( " object must be an object " , node ) ; }
2024-04-09 21:24:07 +08:00
if ( ! current_loop_data - > empty ( ) ) { ( * current_loop_data ) [ " parent " ] = std : : move ( * current_loop_data ) ; }
2024-03-12 14:12:04 +08:00
2024-04-09 21:24:07 +08:00
size_t index = 0 ;
2024-03-12 14:12:04 +08:00
( * current_loop_data ) [ " is_first " ] = true ;
2024-04-09 21:24:07 +08:00
( * current_loop_data ) [ " is_last " ] = ( result - > size ( ) < = 1 ) ;
2024-03-12 14:12:04 +08:00
for ( auto it = result - > begin ( ) ; it ! = result - > end ( ) ; + + it ) {
2024-04-09 21:24:07 +08:00
json_additional_data [ static_cast < std : : string > ( node . key ) ] = it . key ( ) ;
2024-03-12 14:12:04 +08:00
json_additional_data [ static_cast < std : : string > ( node . value ) ] = it . value ( ) ;
2024-04-09 21:24:07 +08:00
( * current_loop_data ) [ " index " ] = index ;
2024-03-12 14:12:04 +08:00
( * current_loop_data ) [ " index1 " ] = index + 1 ;
if ( index = = 1 ) { ( * current_loop_data ) [ " is_first " ] = false ; }
if ( index = = result - > size ( ) - 1 ) { ( * current_loop_data ) [ " is_last " ] = true ; }
node . body . accept ( * this ) ;
+ + index ;
}
json_additional_data [ static_cast < std : : string > ( node . key ) ] . clear ( ) ;
json_additional_data [ static_cast < std : : string > ( node . value ) ] . clear ( ) ;
if ( ! ( * current_loop_data ) [ " parent " ] . empty ( ) ) {
* current_loop_data = std : : move ( ( * current_loop_data ) [ " parent " ] ) ;
} else {
current_loop_data = & json_additional_data [ " loop " ] ;
}
}
void visit ( const IfStatementNode & node )
{
const auto result = eval_expression_list ( node . condition ) ;
if ( truthy ( result . get ( ) ) ) {
node . true_statement . accept ( * this ) ;
} else if ( node . has_false_statement ) {
node . false_statement . accept ( * this ) ;
}
}
void visit ( const IncludeStatementNode & node )
{
2024-04-09 21:24:07 +08:00
auto sub_renderer = Renderer ( config , template_storage , function_storage ) ;
2024-03-12 14:12:04 +08:00
const auto included_template_it = template_storage . find ( node . file ) ;
if ( included_template_it ! = template_storage . end ( ) ) {
2024-04-09 21:24:07 +08:00
sub_renderer . render_to ( * output_stream , included_template_it - > second , * json_input , & json_additional_data ) ;
2024-03-12 14:12:04 +08:00
} else if ( config . throw_at_missing_includes ) {
throw_renderer_error ( " include ' " + node . file + " ' not found " , node ) ;
}
}
void visit ( const ExtendsStatementNode & node )
{
const auto included_template_it = template_storage . find ( node . file ) ;
if ( included_template_it ! = template_storage . end ( ) ) {
const Template * parent_template = & included_template_it - > second ;
render_to ( * output_stream , * parent_template , * json_input , & json_additional_data ) ;
break_rendering = true ;
} else if ( config . throw_at_missing_includes ) {
throw_renderer_error ( " extends ' " + node . file + " ' not found " , node ) ;
}
}
void visit ( const BlockStatementNode & node )
{
const size_t old_level = current_level ;
2024-04-09 21:24:07 +08:00
current_level = 0 ;
current_template = template_stack . front ( ) ;
const auto block_it = current_template - > block_storage . find ( node . name ) ;
2024-03-12 14:12:04 +08:00
if ( block_it ! = current_template - > block_storage . end ( ) ) {
block_statement_stack . emplace_back ( & node ) ;
block_it - > second - > block . accept ( * this ) ;
block_statement_stack . pop_back ( ) ;
}
2024-04-09 21:24:07 +08:00
current_level = old_level ;
2024-03-12 14:12:04 +08:00
current_template = template_stack . back ( ) ;
}
void visit ( const SetStatementNode & node )
{
std : : string ptr = node . key ;
replace_substring ( ptr , " . " , " / " ) ;
2024-04-09 21:24:07 +08:00
ptr = " / " + ptr ;
json_additional_data [ nlohmann : : json : : json_pointer ( ptr ) ] = * eval_expression_list ( node . expression ) ;
2024-03-12 14:12:04 +08:00
}
public :
Renderer ( const RenderConfig & config ,
const TemplateStorage & template_storage ,
const FunctionStorage & function_storage )
: config ( config ) ,
template_storage ( template_storage ) ,
function_storage ( function_storage )
{ }
2024-04-09 21:24:07 +08:00
void render_to ( std : : ostream & os , const Template & tmpl , const json & data , json * loop_data = nullptr )
2024-03-12 14:12:04 +08:00
{
2024-04-09 21:24:07 +08:00
output_stream = & os ;
2024-03-12 14:12:04 +08:00
current_template = & tmpl ;
2024-04-09 21:24:07 +08:00
json_input = & data ;
2024-03-12 14:12:04 +08:00
if ( loop_data ) {
json_additional_data = * loop_data ;
2024-04-09 21:24:07 +08:00
current_loop_data = & json_additional_data [ " loop " ] ;
2024-03-12 14:12:04 +08:00
}
template_stack . emplace_back ( current_template ) ;
current_template - > root . accept ( * this ) ;
json_tmp_stack . clear ( ) ;
}
} ;
} // namespace inja
# endif // INCLUDE_INJA_RENDERER_HPP_
// #include "string_view.hpp"
// #include "template.hpp"
// #include "utils.hpp"
namespace inja {
using json = nlohmann : : json ;
/*!
* \ brief Class for changing the configuration .
*/
class Environment {
std : : string input_path ;
std : : string output_path ;
LexerConfig lexer_config ;
ParserConfig parser_config ;
RenderConfig render_config ;
FunctionStorage function_storage ;
TemplateStorage template_storage ;
public :
Environment ( ) : Environment ( " " ) { }
2024-04-09 21:24:07 +08:00
explicit Environment ( const std : : string & global_path ) : input_path ( global_path ) , output_path ( global_path ) { }
2024-03-12 14:12:04 +08:00
Environment ( const std : : string & input_path , const std : : string & output_path )
: input_path ( input_path ) ,
output_path ( output_path )
{ }
/// Sets the opener and closer for template statements
void set_statement ( const std : : string & open , const std : : string & close )
{
2024-04-09 21:24:07 +08:00
lexer_config . statement_open = open ;
lexer_config . statement_open_no_lstrip = open + " + " ;
lexer_config . statement_open_force_lstrip = open + " - " ;
lexer_config . statement_close = close ;
2024-03-12 14:12:04 +08:00
lexer_config . statement_close_force_rstrip = " - " + close ;
lexer_config . update_open_chars ( ) ;
}
/// Sets the opener for template line statements
void set_line_statement ( const std : : string & open )
{
lexer_config . line_statement = open ;
lexer_config . update_open_chars ( ) ;
}
/// Sets the opener and closer for template expressions
void set_expression ( const std : : string & open , const std : : string & close )
{
2024-04-09 21:24:07 +08:00
lexer_config . expression_open = open ;
lexer_config . expression_open_force_lstrip = open + " - " ;
lexer_config . expression_close = close ;
2024-03-12 14:12:04 +08:00
lexer_config . expression_close_force_rstrip = " - " + close ;
lexer_config . update_open_chars ( ) ;
}
/// Sets the opener and closer for template comments
void set_comment ( const std : : string & open , const std : : string & close )
{
2024-04-09 21:24:07 +08:00
lexer_config . comment_open = open ;
lexer_config . comment_open_force_lstrip = open + " - " ;
lexer_config . comment_close = close ;
2024-03-12 14:12:04 +08:00
lexer_config . comment_close_force_rstrip = " - " + close ;
lexer_config . update_open_chars ( ) ;
}
/// Sets whether to remove the first newline after a block
void set_trim_blocks ( bool trim_blocks ) { 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 ) { lexer_config . lstrip_blocks = lstrip_blocks ; }
/// Sets the element notation syntax
void set_search_included_templates_in_files ( bool search_in_files )
{
parser_config . search_included_templates_in_files = search_in_files ;
}
/// Sets whether a missing include will throw an error
2024-04-09 21:24:07 +08:00
void set_throw_at_missing_includes ( bool will_throw ) { render_config . throw_at_missing_includes = will_throw ; }
2024-03-12 14:12:04 +08:00
Template parse ( nonstd : : string_view input )
{
Parser parser ( parser_config , lexer_config , template_storage , function_storage ) ;
return parser . parse ( input ) ;
}
Template parse_template ( const std : : string & filename )
{
Parser parser ( parser_config , lexer_config , template_storage , function_storage ) ;
auto result = Template ( parser . load_file ( input_path + static_cast < std : : string > ( filename ) ) ) ;
parser . parse_into_template ( result , input_path + static_cast < std : : string > ( filename ) ) ;
return result ;
}
Template parse_file ( const std : : string & filename ) { return parse_template ( filename ) ; }
2024-04-09 21:24:07 +08:00
std : : string render ( nonstd : : string_view input , const json & data ) { return render ( parse ( input ) , data ) ; }
2024-03-12 14:12:04 +08:00
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 )
{
return render ( parse_template ( filename ) , data ) ;
}
2024-04-09 21:24:07 +08:00
std : : string render_file_with_json_file ( const std : : string & filename , const std : : string & filename_data )
2024-03-12 14:12:04 +08:00
{
const json data = load_json ( filename_data ) ;
return render_file ( filename , data ) ;
}
void write ( const std : : string & filename , const json & data , const std : : string & filename_out )
{
std : : ofstream file ( output_path + filename_out ) ;
file < < render_file ( filename , data ) ;
file . close ( ) ;
}
void write ( const Template & temp , const json & data , const std : : string & filename_out )
{
std : : ofstream file ( output_path + filename_out ) ;
file < < render ( temp , data ) ;
file . close ( ) ;
}
2024-04-09 21:24:07 +08:00
void
write_with_json_file ( const std : : string & filename , const std : : string & filename_data , const std : : string & filename_out )
2024-03-12 14:12:04 +08:00
{
const json data = load_json ( filename_data ) ;
write ( filename , data , filename_out ) ;
}
2024-04-09 21:24:07 +08:00
void write_with_json_file ( const Template & temp , const std : : string & filename_data , const std : : string & filename_out )
2024-03-12 14:12:04 +08:00
{
const json data = load_json ( filename_data ) ;
write ( temp , data , filename_out ) ;
}
std : : ostream & render_to ( std : : ostream & os , const Template & tmpl , const json & data )
{
Renderer ( render_config , template_storage , function_storage ) . render_to ( os , tmpl , data ) ;
return os ;
}
std : : string load_file ( const std : : string & filename )
{
Parser parser ( parser_config , lexer_config , template_storage , function_storage ) ;
return parser . load_file ( input_path + filename ) ;
}
json load_json ( const std : : string & filename )
{
std : : ifstream file ;
open_file_or_throw ( input_path + filename , file ) ;
json j ;
file > > j ;
return j ;
}
/*!
@ brief Adds a variadic callback
*/
2024-04-09 21:24:07 +08:00
void add_callback ( const std : : string & name , const CallbackFunction & callback ) { add_callback ( name , - 1 , callback ) ; }
2024-03-12 14:12:04 +08:00
/*!
@ brief Adds a variadic void callback
*/
void add_void_callback ( const std : : string & name , const VoidCallbackFunction & callback )
{
add_void_callback ( name , - 1 , callback ) ;
}
/*!
@ brief Adds a callback with given number or arguments
*/
void add_callback ( const std : : string & name , int num_args , const CallbackFunction & callback )
{
function_storage . add_callback ( name , num_args , callback ) ;
}
/*!
@ brief Adds a void callback with given number or arguments
*/
2024-04-09 21:24:07 +08:00
void add_void_callback ( const std : : string & name , int num_args , const VoidCallbackFunction & callback )
2024-03-12 14:12:04 +08:00
{
function_storage . add_callback ( name , num_args , [ callback ] ( Arguments & args ) {
callback ( args ) ;
return json ( ) ;
} ) ;
}
/** Includes a template with a given name into the environment.
* Then , a template can be rendered in another template using the
* include " <name> " syntax .
*/
2024-04-09 21:24:07 +08:00
void include_template ( const std : : string & name , const Template & tmpl ) { template_storage [ name ] = tmpl ; }
2024-03-12 14:12:04 +08:00
} ;
/*!
@ 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 ) ;
}
} // namespace inja
# endif // INCLUDE_INJA_ENVIRONMENT_HPP_
// #include "exceptions.hpp"
// #include "parser.hpp"
// #include "renderer.hpp"
// #include "string_view.hpp"
// #include "template.hpp"
# endif // INCLUDE_INJA_INJA_HPP_