25 #ifndef INCLUDE_INJA_INJA_HPP_
26 #define INCLUDE_INJA_INJA_HPP_
30 #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(INJA_NOEXCEPTION)
32 #define INJA_THROW(exception) throw exception
37 #define INJA_THROW(exception) std::abort(); std::ignore = exception
39 #ifndef INJA_NOEXCEPTION
40 #define INJA_NOEXCEPTION
45 #ifndef INCLUDE_INJA_ENVIRONMENT_HPP_
46 #define INCLUDE_INJA_ENVIRONMENT_HPP_
57 #ifndef INCLUDE_INJA_CONFIG_HPP_
58 #define INCLUDE_INJA_CONFIG_HPP_
74 #ifndef NONSTD_SV_LITE_H_INCLUDED
75 #define NONSTD_SV_LITE_H_INCLUDED
77 #define string_view_lite_MAJOR 1
78 #define string_view_lite_MINOR 4
79 #define string_view_lite_PATCH 0
81 #define string_view_lite_VERSION \
82 nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY( \
83 string_view_lite_PATCH)
85 #define nssv_STRINGIFY(x) nssv_STRINGIFY_(x)
86 #define nssv_STRINGIFY_(x) #x
90 #define nssv_STRING_VIEW_DEFAULT 0
91 #define nssv_STRING_VIEW_NONSTD 1
92 #define nssv_STRING_VIEW_STD 2
94 #if !defined(nssv_CONFIG_SELECT_STRING_VIEW)
95 #define nssv_CONFIG_SELECT_STRING_VIEW (nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD)
98 #if defined(nssv_CONFIG_SELECT_STD_STRING_VIEW) || defined(nssv_CONFIG_SELECT_NONSTD_STRING_VIEW)
99 #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_...
102 #ifndef nssv_CONFIG_STD_SV_OPERATOR
103 #define nssv_CONFIG_STD_SV_OPERATOR 0
106 #ifndef nssv_CONFIG_USR_SV_OPERATOR
107 #define nssv_CONFIG_USR_SV_OPERATOR 1
110 #ifdef nssv_CONFIG_CONVERSION_STD_STRING
111 #define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING
112 #define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING
115 #ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
116 #define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1
119 #ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
120 #define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1
125 #ifndef nssv_CONFIG_NO_EXCEPTIONS
126 #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
127 #define nssv_CONFIG_NO_EXCEPTIONS 0
129 #define nssv_CONFIG_NO_EXCEPTIONS 1
136 #ifndef nssv_CPLUSPLUS
137 #if defined(_MSVC_LANG) && !defined(__clang__)
138 #define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG)
140 #define nssv_CPLUSPLUS __cplusplus
144 #define nssv_CPP98_OR_GREATER (nssv_CPLUSPLUS >= 199711L)
145 #define nssv_CPP11_OR_GREATER (nssv_CPLUSPLUS >= 201103L)
146 #define nssv_CPP11_OR_GREATER_ (nssv_CPLUSPLUS >= 201103L)
147 #define nssv_CPP14_OR_GREATER (nssv_CPLUSPLUS >= 201402L)
148 #define nssv_CPP17_OR_GREATER (nssv_CPLUSPLUS >= 201703L)
149 #define nssv_CPP20_OR_GREATER (nssv_CPLUSPLUS >= 202000L)
153 #if nssv_CPP17_OR_GREATER && defined(__has_include)
154 #if __has_include(<string_view> )
155 #define nssv_HAVE_STD_STRING_VIEW 1
157 #define nssv_HAVE_STD_STRING_VIEW 0
160 #define nssv_HAVE_STD_STRING_VIEW 0
163 #define nssv_USES_STD_STRING_VIEW \
164 ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || \
165 ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW))
167 #define nssv_HAVE_STARTS_WITH (nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW)
168 #define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH
174 #if nssv_USES_STD_STRING_VIEW
176 #include <string_view>
180 #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
184 template <
class CharT,
class Traits,
class Allocator = std::allocator<CharT>>
185 std::basic_string<CharT, Traits, Allocator>
to_string(std::basic_string_view<CharT, Traits> v,
186 Allocator
const &
a = Allocator()) {
187 return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(),
a);
190 template <
class CharT,
class Traits,
class Allocator>
191 std::basic_string_view<CharT, Traits>
to_string_view(std::basic_string<CharT, Traits, Allocator>
const &s) {
192 return std::basic_string_view<CharT, Traits>(s.data(), s.size());
197 #if nssv_CONFIG_STD_SV_OPERATOR
199 using namespace std::literals::string_view_literals;
203 #if nssv_CONFIG_USR_SV_OPERATOR
205 inline namespace literals {
206 inline namespace string_view_literals {
208 constexpr std::string_view
operator"" _sv(
const char *
str,
size_t len) noexcept
210 return std::string_view {
str, len};
213 constexpr std::u16string_view
operator"" _sv(
const char16_t *
str,
size_t len) noexcept
215 return std::u16string_view {
str, len};
218 constexpr std::u32string_view
operator"" _sv(
const char32_t *
str,
size_t len) noexcept
220 return std::u32string_view {
str, len};
223 constexpr std::wstring_view
operator"" _sv(
const wchar_t *
str,
size_t len) noexcept
225 return std::wstring_view {
str, len};
231 #endif // nssv_CONFIG_USR_SV_OPERATOR
235 #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
239 using std::basic_string_view;
240 using std::string_view;
241 using std::u16string_view;
242 using std::u32string_view;
243 using std::wstring_view;
247 using std::operator==;
248 using std::operator!=;
249 using std::operator<;
250 using std::operator<=;
251 using std::operator>;
252 using std::operator>=;
254 using std::operator<<;
258 #else // nssv_HAVE_STD_STRING_VIEW
277 #if defined(_MSC_VER) && !defined(__clang__)
278 #define nssv_COMPILER_MSVC_VER (_MSC_VER)
279 #define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * (5 + (_MSC_VER < 1900)))
281 #define nssv_COMPILER_MSVC_VER 0
282 #define nssv_COMPILER_MSVC_VERSION 0
285 #define nssv_COMPILER_VERSION(major, minor, patch) (10 * (10 * (major) + (minor)) + (patch))
287 #if defined(__clang__)
288 #define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
290 #define nssv_COMPILER_CLANG_VERSION 0
293 #if defined(__GNUC__) && !defined(__clang__)
294 #define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
296 #define nssv_COMPILER_GNUC_VERSION 0
300 #define nssv_BETWEEN(v, lo, hi) ((lo) <= (v) && (v) < (hi))
305 #define nssv_HAS_CPP0X _HAS_CPP0X
307 #define nssv_HAS_CPP0X 0
312 #if nssv_COMPILER_MSVC_VER >= 1900
313 #undef nssv_CPP11_OR_GREATER
314 #define nssv_CPP11_OR_GREATER 1
317 #define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500)
318 #define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600)
319 #define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700)
320 #define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800)
321 #define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900)
322 #define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910)
324 #define nssv_CPP14_000 (nssv_CPP14_OR_GREATER)
325 #define nssv_CPP17_000 (nssv_CPP17_OR_GREATER)
329 #define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140
330 #define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140
331 #define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140
332 #define nssv_HAVE_NOEXCEPT nssv_CPP11_140
333 #define nssv_HAVE_NULLPTR nssv_CPP11_100
334 #define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140
335 #define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140
336 #define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140
337 #define nssv_HAVE_WCHAR16_T nssv_CPP11_100
338 #define nssv_HAVE_WCHAR32_T nssv_CPP11_100
340 #if !((nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION) || nssv_BETWEEN(nssv_COMPILER_CLANG_VERSION, 300, 400))
341 #define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140
343 #define nssv_HAVE_STD_DEFINED_LITERALS 0
348 #define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000
352 #define nssv_HAVE_NODISCARD nssv_CPP17_000
356 #define nssv_HAVE_STD_HASH nssv_CPP11_120
360 #if nssv_HAVE_CONSTEXPR_11
361 #define nssv_constexpr constexpr
363 #define nssv_constexpr
366 #if nssv_HAVE_CONSTEXPR_14
367 #define nssv_constexpr14 constexpr
369 #define nssv_constexpr14
372 #if nssv_HAVE_EXPLICIT_CONVERSION
373 #define nssv_explicit explicit
375 #define nssv_explicit
378 #if nssv_HAVE_INLINE_NAMESPACE
379 #define nssv_inline_ns inline
381 #define nssv_inline_ns
384 #if nssv_HAVE_NOEXCEPT
385 #define nssv_noexcept noexcept
387 #define nssv_noexcept
398 #if nssv_HAVE_NULLPTR
399 #define nssv_nullptr nullptr
401 #define nssv_nullptr NULL
404 #if nssv_HAVE_NODISCARD
405 #define nssv_nodiscard [[nodiscard]]
407 #define nssv_nodiscard
419 #if !nssv_CONFIG_NO_EXCEPTIONS
423 #if nssv_CPP11_OR_GREATER
424 #include <type_traits>
429 #if defined(__clang__)
430 #pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
431 #pragma clang diagnostic push
432 #pragma clang diagnostic ignored "-Wuser-defined-literals"
433 #elif defined(__GNUC__)
434 #pragma GCC diagnostic push
435 #pragma GCC diagnostic ignored "-Wliteral-suffix"
438 #if nssv_COMPILER_MSVC_VERSION >= 140
439 #define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]]
440 #define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress : code))
441 #define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable : codes))
443 #define nssv_SUPPRESS_MSGSL_WARNING(expr)
444 #define nssv_SUPPRESS_MSVC_WARNING(code, descr)
445 #define nssv_DISABLE_MSVC_WARNINGS(codes)
448 #if defined(__clang__)
449 #define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop")
450 #elif defined(__GNUC__)
451 #define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop")
452 #elif nssv_COMPILER_MSVC_VERSION >= 140
453 #define nssv_RESTORE_WARNINGS() __pragma(warning(pop))
455 #define nssv_RESTORE_WARNINGS()
472 #if nssv_CPP11_OR_GREATER
478 template <
typename CharT>
inline constexpr std::size_t length(CharT *s, std::size_t
result = 0) {
484 #endif // nssv_CPP11_OR_GREATER
486 template <
class CharT,
class Traits = std::
char_traits<CharT>>
class basic_string_view;
492 template <
class CharT,
class Traits
494 class basic_string_view {
498 typedef Traits traits_type;
499 typedef CharT value_type;
501 typedef CharT *pointer;
502 typedef CharT
const *const_pointer;
503 typedef CharT &reference;
504 typedef CharT
const &const_reference;
506 typedef const_pointer iterator;
507 typedef const_pointer const_iterator;
508 typedef std::reverse_iterator<const_iterator> reverse_iterator;
509 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
511 typedef std::size_t size_type;
512 typedef std::ptrdiff_t difference_type;
518 #if nssv_CPP11_OR_GREATER
522 size_(other.size_) {}
531 #if nssv_CPP17_OR_GREATER
533 size_(Traits::length(s))
534 #elif nssv_CPP11_OR_GREATER
536 size_(detail::length(s))
539 size_(Traits::length(s))
546 #if nssv_CPP11_OR_GREATER
581 nssv_constexpr const_reference operator[](size_type pos)
const {
return data_at(pos); }
584 #if nssv_CONFIG_NO_EXCEPTIONS
585 assert(pos < size());
588 throw std::out_of_range(
"nonstd::string_view::at()");
594 nssv_constexpr const_reference front()
const {
return data_at(0); }
595 nssv_constexpr const_reference back()
const {
return data_at(size() - 1); }
614 swap(data_, other.data_);
615 swap(size_, other.size_);
620 size_type copy(CharT *
dest, size_type n, size_type pos = 0)
const {
621 #if nssv_CONFIG_NO_EXCEPTIONS
622 assert(pos <= size());
625 throw std::out_of_range(
"nonstd::string_view::copy()");
628 const size_type rlen = (std::min)(n, size() - pos);
635 nssv_constexpr14 basic_string_view substr(size_type pos = 0, size_type n = npos)
const {
636 #if nssv_CONFIG_NO_EXCEPTIONS
637 assert(pos <= size());
640 throw std::out_of_range(
"nonstd::string_view::substr()");
643 return basic_string_view(
data() + pos, (std::min)(n, size() - pos));
650 if (
const int result = Traits::compare(
data(), other.data(), (std::min)(size(), other.size()))) {
654 return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
657 nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other)
const
659 return substr(pos1, n1).compare(other);
662 nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other, size_type pos2,
665 return substr(pos1, n1).compare(other.substr(pos2, n2));
670 return compare(basic_string_view(s));
673 nssv_constexpr int compare(size_type pos1, size_type n1, CharT
const *s)
const
675 return substr(pos1, n1).compare(basic_string_view(s));
678 nssv_constexpr int compare(size_type pos1, size_type n1, CharT
const *s, size_type n2)
const
680 return substr(pos1, n1).compare(basic_string_view(s, n2));
689 return size() >= v.size() && compare(0, v.size(), v) == 0;
706 return size() >= v.size() && compare(size() - v.size(), npos, v) == 0;
711 return ends_with(basic_string_view(&
c, 1));
716 return ends_with(basic_string_view(s));
723 return assert(v.size() == 0 || v.data() !=
nssv_nullptr),
724 pos >= size() ? npos : to_pos(std::search(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq));
729 return find(basic_string_view(&
c, 1), pos);
732 nssv_constexpr14 size_type find(CharT
const *s, size_type pos, size_type n)
const
734 return find(basic_string_view(s, n), pos);
739 return find(basic_string_view(s), pos);
746 if (size() < v.size()) {
751 return (std::min)(size(), pos);
754 const_iterator last = cbegin() + (std::min)(size() - v.size(), pos) + v.size();
755 const_iterator
result = std::find_end(cbegin(), last, v.cbegin(), v.cend(), Traits::eq);
757 return result != last ? size_type(
result - cbegin()) : npos;
762 return rfind(basic_string_view(&
c, 1), pos);
765 nssv_constexpr14 size_type rfind(CharT
const *s, size_type pos, size_type n)
const
767 return rfind(basic_string_view(s, n), pos);
770 nssv_constexpr14 size_type rfind(CharT
const *s, size_type pos = npos)
const
772 return rfind(basic_string_view(s), pos);
779 return pos >= size() ? npos
780 : to_pos(std::find_first_of(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq));
785 return find_first_of(basic_string_view(&
c, 1), pos);
788 nssv_constexpr size_type find_first_of(CharT
const *s, size_type pos, size_type n)
const
790 return find_first_of(basic_string_view(s, n), pos);
793 nssv_constexpr size_type find_first_of(CharT
const *s, size_type pos = 0)
const
795 return find_first_of(basic_string_view(s), pos);
802 return empty() ? npos
803 : pos >= size() ? find_last_of(v, size() - 1)
804 : to_pos(std::find_first_of(const_reverse_iterator(cbegin() + pos + 1), crend(),
805 v.cbegin(), v.cend(), Traits::eq));
810 return find_last_of(basic_string_view(&
c, 1), pos);
813 nssv_constexpr size_type find_last_of(CharT
const *s, size_type pos, size_type
count)
const
815 return find_last_of(basic_string_view(s,
count), pos);
818 nssv_constexpr size_type find_last_of(CharT
const *s, size_type pos = npos)
const
820 return find_last_of(basic_string_view(s), pos);
827 return pos >= size() ? npos : to_pos(std::find_if(cbegin() + pos, cend(), not_in_view(v)));
832 return find_first_not_of(basic_string_view(&
c, 1), pos);
835 nssv_constexpr size_type find_first_not_of(CharT
const *s, size_type pos, size_type
count)
const
837 return find_first_not_of(basic_string_view(s,
count), pos);
840 nssv_constexpr size_type find_first_not_of(CharT
const *s, size_type pos = 0)
const
842 return find_first_not_of(basic_string_view(s), pos);
849 return empty() ? npos
851 ? find_last_not_of(v, size() - 1)
852 : to_pos(std::find_if(const_reverse_iterator(cbegin() + pos + 1), crend(), not_in_view(v)));
857 return find_last_not_of(basic_string_view(&
c, 1), pos);
860 nssv_constexpr size_type find_last_not_of(CharT
const *s, size_type pos, size_type
count)
const
862 return find_last_not_of(basic_string_view(s,
count), pos);
865 nssv_constexpr size_type find_last_not_of(CharT
const *s, size_type pos = npos)
const
867 return find_last_not_of(basic_string_view(s), pos);
872 #if nssv_CPP17_OR_GREATER
874 #elif nssv_CPP11_OR_GREATER
875 enum : size_type { npos = size_type(-1) };
877 enum { npos = size_type(-1) };
882 const basic_string_view v;
884 nssv_constexpr explicit not_in_view(basic_string_view v) : v(v) {}
886 nssv_constexpr bool operator()(CharT
c)
const {
return npos == v.find_first_of(
c); }
889 nssv_constexpr size_type to_pos(const_iterator it)
const {
return it == cend() ? npos : size_type(it - cbegin()); }
891 nssv_constexpr size_type to_pos(const_reverse_iterator it)
const {
892 return it == crend() ? npos : size_type(crend() - it - 1);
896 #if nssv_BETWEEN(nssv_COMPILER_GNUC_VERSION, 1, 500)
899 return assert(pos < size()), data_[pos];
908 #if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
910 template <
class Allocator>
911 basic_string_view(std::basic_string<CharT, Traits, Allocator>
const &s)
nssv_noexcept : data_(s.data()),
914 #if nssv_HAVE_EXPLICIT_CONVERSION
916 template <
class Allocator>
explicit operator std::basic_string<CharT, Traits, Allocator>()
const {
920 #endif // nssv_HAVE_EXPLICIT_CONVERSION
922 #if nssv_CPP11_OR_GREATER
924 template <
class Allocator = std::allocator<CharT>>
925 std::basic_string<CharT, Traits, Allocator>
to_string(Allocator
const &
a = Allocator())
const {
926 return std::basic_string<CharT, Traits, Allocator>(begin(), end(),
a);
931 std::basic_string<CharT, Traits>
to_string()
const {
return std::basic_string<CharT, Traits>(begin(), end()); }
933 template <
class Allocator> std::basic_string<CharT, Traits, Allocator>
to_string(Allocator
const &
a)
const {
934 return std::basic_string<CharT, Traits, Allocator>(begin(), end(),
a);
937 #endif // nssv_CPP11_OR_GREATER
939 #endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
949 template <
class CharT,
class Traits>
950 nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
952 return lhs.compare(rhs) == 0;
955 template <
class CharT,
class Traits>
956 nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
958 return lhs.compare(rhs) != 0;
961 template <
class CharT,
class Traits>
964 return lhs.compare(rhs) < 0;
967 template <
class CharT,
class Traits>
968 nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
970 return lhs.compare(rhs) <= 0;
973 template <
class CharT,
class Traits>
974 nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
976 return lhs.compare(rhs) > 0;
979 template <
class CharT,
class Traits>
980 nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
982 return lhs.compare(rhs) >= 0;
990 #if !nssv_CPP11_OR_GREATER || nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 100, 141)
996 template <
class CharT,
class Traits>
998 return lhs.compare(rhs) == 0;
1001 template <
class CharT,
class Traits>
1003 return rhs.compare(lhs) == 0;
1006 template <
class CharT,
class Traits>
1007 nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
1009 return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
1012 template <
class CharT,
class Traits>
1013 nssv_constexpr bool operator==(std::basic_string<CharT, Traits> rhs,
1015 return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
1020 template <
class CharT,
class Traits>
1022 return lhs.compare(rhs) != 0;
1025 template <
class CharT,
class Traits>
1027 return rhs.compare(lhs) != 0;
1030 template <
class CharT,
class Traits>
1031 nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
1033 return lhs.size() != rhs.size() && lhs.compare(rhs) != 0;
1036 template <
class CharT,
class Traits>
1037 nssv_constexpr bool operator!=(std::basic_string<CharT, Traits> rhs,
1039 return lhs.size() != rhs.size() || rhs.compare(lhs) != 0;
1044 template <
class CharT,
class Traits>
1046 return lhs.compare(rhs) < 0;
1049 template <
class CharT,
class Traits>
1051 return rhs.compare(lhs) > 0;
1054 template <
class CharT,
class Traits>
1057 return lhs.compare(rhs) < 0;
1060 template <
class CharT,
class Traits>
1063 return rhs.compare(lhs) > 0;
1068 template <
class CharT,
class Traits>
1070 return lhs.compare(rhs) <= 0;
1073 template <
class CharT,
class Traits>
1075 return rhs.compare(lhs) >= 0;
1078 template <
class CharT,
class Traits>
1079 nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
1081 return lhs.compare(rhs) <= 0;
1084 template <
class CharT,
class Traits>
1085 nssv_constexpr bool operator<=(std::basic_string<CharT, Traits> rhs,
1087 return rhs.compare(lhs) >= 0;
1092 template <
class CharT,
class Traits>
1094 return lhs.compare(rhs) > 0;
1097 template <
class CharT,
class Traits>
1099 return rhs.compare(lhs) < 0;
1102 template <
class CharT,
class Traits>
1103 nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
1105 return lhs.compare(rhs) > 0;
1108 template <
class CharT,
class Traits>
1109 nssv_constexpr bool operator>(std::basic_string<CharT, Traits> rhs,
1111 return rhs.compare(lhs) < 0;
1116 template <
class CharT,
class Traits>
1118 return lhs.compare(rhs) >= 0;
1121 template <
class CharT,
class Traits>
1123 return rhs.compare(lhs) <= 0;
1126 template <
class CharT,
class Traits>
1127 nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
1129 return lhs.compare(rhs) >= 0;
1132 template <
class CharT,
class Traits>
1133 nssv_constexpr bool operator>=(std::basic_string<CharT, Traits> rhs,
1135 return rhs.compare(lhs) <= 0;
1138 #else // newer compilers:
1140 #define nssv_BASIC_STRING_VIEW_I(T, U) typename std::decay<basic_string_view<T, U>>::type
1142 #if nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 140, 150)
1143 #define nssv_MSVC_ORDER(x) , int = x
1145 #define nssv_MSVC_ORDER(x)
1150 template <
class CharT,
class Traits nssv_MSVC_ORDER(1)>
1151 nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
1152 nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs)
nssv_noexcept {
1153 return lhs.compare(rhs) == 0;
1156 template <
class CharT,
class Traits nssv_MSVC_ORDER(2)>
1157 nssv_constexpr bool operator==(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
1159 return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
1164 template <
class CharT,
class Traits nssv_MSVC_ORDER(1)>
1165 nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
1166 nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs)
nssv_noexcept {
1167 return lhs.size() != rhs.size() || lhs.compare(rhs) != 0;
1170 template <
class CharT,
class Traits nssv_MSVC_ORDER(2)>
1171 nssv_constexpr bool operator!=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
1173 return lhs.compare(rhs) != 0;
1178 template <
class CharT,
class Traits nssv_MSVC_ORDER(1)>
1180 nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs)
nssv_noexcept {
1181 return lhs.compare(rhs) < 0;
1184 template <
class CharT,
class Traits nssv_MSVC_ORDER(2)>
1187 return lhs.compare(rhs) < 0;
1192 template <
class CharT,
class Traits nssv_MSVC_ORDER(1)>
1193 nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
1194 nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs)
nssv_noexcept {
1195 return lhs.compare(rhs) <= 0;
1198 template <
class CharT,
class Traits nssv_MSVC_ORDER(2)>
1199 nssv_constexpr bool operator<=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
1201 return lhs.compare(rhs) <= 0;
1206 template <
class CharT,
class Traits nssv_MSVC_ORDER(1)>
1207 nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
1208 nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs)
nssv_noexcept {
1209 return lhs.compare(rhs) > 0;
1212 template <
class CharT,
class Traits nssv_MSVC_ORDER(2)>
1213 nssv_constexpr bool operator>(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
1215 return lhs.compare(rhs) > 0;
1220 template <
class CharT,
class Traits nssv_MSVC_ORDER(1)>
1221 nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
1222 nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs)
nssv_noexcept {
1223 return lhs.compare(rhs) >= 0;
1226 template <
class CharT,
class Traits nssv_MSVC_ORDER(2)>
1227 nssv_constexpr bool operator>=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
1229 return lhs.compare(rhs) >= 0;
1232 #undef nssv_MSVC_ORDER
1233 #undef nssv_BASIC_STRING_VIEW_I
1235 #endif // compiler-dependent approach to comparisons
1241 template <
class Stream>
void write_padding(Stream &os, std::streamsize n) {
1242 for (std::streamsize i = 0; i < n; ++i)
1243 os.rdbuf()->sputc(os.fill());
1246 template <
class Stream,
class View> Stream &write_to_stream(Stream &os, View
const &sv) {
1247 typename Stream::sentry sentry(os);
1252 const std::streamsize length =
static_cast<std::streamsize
>(sv.length());
1255 const bool pad = (length < os.width());
1256 const bool left_pad = pad && (os.flags() & std::ios_base::adjustfield) == std::ios_base::right;
1259 write_padding(os, os.width() - length);
1262 os.rdbuf()->sputn(sv.begin(), length);
1264 if (pad && !left_pad)
1265 write_padding(os, os.width() - length);
1275 template <
class CharT,
class Traits>
1276 std::basic_ostream<CharT, Traits> &
operator<<(std::basic_ostream<CharT, Traits> &os,
1277 basic_string_view<CharT, Traits> sv) {
1278 return detail::write_to_stream(os, sv);
1283 typedef basic_string_view<char> string_view;
1284 typedef basic_string_view<wchar_t> wstring_view;
1285 #if nssv_HAVE_WCHAR16_T
1286 typedef basic_string_view<char16_t> u16string_view;
1287 typedef basic_string_view<char32_t> u32string_view;
1297 #if nssv_HAVE_USER_DEFINED_LITERALS
1303 #if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
1307 return nonstd::sv_lite::string_view {
str, len};
1312 return nonstd::sv_lite::u16string_view {
str, len};
1317 return nonstd::sv_lite::u32string_view {
str, len};
1322 return nonstd::sv_lite::wstring_view {
str, len};
1325 #endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
1327 #if nssv_CONFIG_USR_SV_OPERATOR
1331 return nonstd::sv_lite::string_view {
str, len};
1336 return nonstd::sv_lite::u16string_view {
str, len};
1341 return nonstd::sv_lite::u32string_view {
str, len};
1346 return nonstd::sv_lite::wstring_view {
str, len};
1349 #endif // nssv_CONFIG_USR_SV_OPERATOR
1360 #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
1367 #if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140
1369 template <
class CharT,
class Traits,
class Allocator = std::allocator<CharT>>
1370 std::basic_string<CharT, Traits, Allocator>
to_string(basic_string_view<CharT, Traits> v,
1371 Allocator
const &
a = Allocator()) {
1372 return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(),
a);
1377 template <
class CharT,
class Traits> std::basic_string<CharT, Traits>
to_string(basic_string_view<CharT, Traits> v) {
1378 return std::basic_string<CharT, Traits>(v.begin(), v.end());
1381 template <
class CharT,
class Traits,
class Allocator>
1382 std::basic_string<CharT, Traits, Allocator>
to_string(basic_string_view<CharT, Traits> v, Allocator
const &
a) {
1383 return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(),
a);
1386 #endif // nssv_CPP11_OR_GREATER
1388 template <
class CharT,
class Traits,
class Allocator>
1389 basic_string_view<CharT, Traits>
to_string_view(std::basic_string<CharT, Traits, Allocator>
const &s) {
1390 return basic_string_view<CharT, Traits>(s.data(), s.size());
1396 #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
1404 using sv_lite::basic_string_view;
1405 using sv_lite::string_view;
1406 using sv_lite::wstring_view;
1408 #if nssv_HAVE_WCHAR16_T
1409 using sv_lite::u16string_view;
1411 #if nssv_HAVE_WCHAR32_T
1412 using sv_lite::u32string_view;
1417 using sv_lite::operator==;
1418 using sv_lite::operator!=;
1419 using sv_lite::operator<;
1420 using sv_lite::operator<=;
1421 using sv_lite::operator>;
1422 using sv_lite::operator>=;
1424 using sv_lite::operator<<;
1426 #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
1438 #if nssv_HAVE_STD_HASH
1440 #include <functional>
1444 template <>
struct hash<
nonstd::string_view> {
1446 std::size_t operator()(nonstd::string_view v)
const nssv_noexcept {
1447 return std::hash<std::string>()(std::string(v.data(), v.size()));
1451 template <>
struct hash<
nonstd::wstring_view> {
1453 std::size_t operator()(nonstd::wstring_view v)
const nssv_noexcept {
1454 return std::hash<std::wstring>()(std::wstring(v.data(), v.size()));
1458 template <>
struct hash<
nonstd::u16string_view> {
1460 std::size_t operator()(nonstd::u16string_view v)
const nssv_noexcept {
1461 return std::hash<std::u16string>()(std::u16string(v.data(), v.size()));
1465 template <>
struct hash<
nonstd::u32string_view> {
1467 std::size_t operator()(nonstd::u32string_view v)
const nssv_noexcept {
1468 return std::hash<std::u32string>()(std::u32string(v.data(), v.size()));
1474 #endif // nssv_HAVE_STD_HASH
1478 #endif // nssv_HAVE_STD_STRING_VIEW
1479 #endif // NONSTD_SV_LITE_H_INCLUDED
1487 struct LexerConfig {
1488 std::string statement_open {
"{%"};
1489 std::string statement_open_no_lstrip {
"{%+"};
1490 std::string statement_open_force_lstrip {
"{%-"};
1491 std::string statement_close {
"%}"};
1492 std::string statement_close_force_rstrip {
"-%}"};
1493 std::string line_statement {
"##"};
1494 std::string expression_open {
"{{"};
1495 std::string expression_open_force_lstrip {
"{{-"};
1496 std::string expression_close {
"}}"};
1497 std::string expression_close_force_rstrip {
"-}}"};
1498 std::string comment_open {
"{#"};
1499 std::string comment_open_force_lstrip {
"{#-"};
1500 std::string comment_close {
"#}"};
1501 std::string comment_close_force_rstrip {
"-#}"};
1502 std::string open_chars {
"#{"};
1504 bool trim_blocks {
false};
1505 bool lstrip_blocks {
false};
1507 void update_open_chars() {
1509 if (open_chars.find(line_statement[0]) == std::string::npos) {
1510 open_chars += line_statement[0];
1512 if (open_chars.find(statement_open[0]) == std::string::npos) {
1513 open_chars += statement_open[0];
1515 if (open_chars.find(statement_open_no_lstrip[0]) == std::string::npos) {
1516 open_chars += statement_open_no_lstrip[0];
1518 if (open_chars.find(statement_open_force_lstrip[0]) == std::string::npos) {
1519 open_chars += statement_open_force_lstrip[0];
1521 if (open_chars.find(expression_open[0]) == std::string::npos) {
1522 open_chars += expression_open[0];
1524 if (open_chars.find(expression_open_force_lstrip[0]) == std::string::npos) {
1525 open_chars += expression_open_force_lstrip[0];
1527 if (open_chars.find(comment_open[0]) == std::string::npos) {
1528 open_chars += comment_open[0];
1530 if (open_chars.find(comment_open_force_lstrip[0]) == std::string::npos) {
1531 open_chars += comment_open_force_lstrip[0];
1539 struct ParserConfig {
1540 bool search_included_templates_in_files {
true};
1546 struct RenderConfig {
1547 bool throw_at_missing_includes {
true};
1552 #endif // INCLUDE_INJA_CONFIG_HPP_
1555 #ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_
1556 #define INCLUDE_INJA_FUNCTION_STORAGE_HPP_
1635 const int VARIADIC {-1};
1638 {std::make_pair(
"at", 2),
FunctionData { Operation::At }},
1639 {std::make_pair(
"default", 2),
FunctionData { Operation::Default }},
1640 {std::make_pair(
"divisibleBy", 2),
FunctionData { Operation::DivisibleBy }},
1641 {std::make_pair(
"even", 1),
FunctionData { Operation::Even }},
1642 {std::make_pair(
"exists", 1),
FunctionData { Operation::Exists }},
1643 {std::make_pair(
"existsIn", 2),
FunctionData { Operation::ExistsInObject }},
1644 {std::make_pair(
"first", 1),
FunctionData { Operation::First }},
1645 {std::make_pair(
"float", 1),
FunctionData { Operation::Float }},
1647 {std::make_pair(
"isArray", 1),
FunctionData { Operation::IsArray }},
1648 {std::make_pair(
"isBoolean", 1),
FunctionData { Operation::IsBoolean }},
1649 {std::make_pair(
"isFloat", 1),
FunctionData { Operation::IsFloat }},
1650 {std::make_pair(
"isInteger", 1),
FunctionData { Operation::IsInteger }},
1651 {std::make_pair(
"isNumber", 1),
FunctionData { Operation::IsNumber }},
1652 {std::make_pair(
"isObject", 1),
FunctionData { Operation::IsObject }},
1653 {std::make_pair(
"isString", 1),
FunctionData { Operation::IsString }},
1654 {std::make_pair(
"last", 1),
FunctionData { Operation::Last }},
1655 {std::make_pair(
"length", 1),
FunctionData { Operation::Length }},
1656 {std::make_pair(
"lower", 1),
FunctionData { Operation::Lower }},
1657 {std::make_pair(
"max", 1),
FunctionData { Operation::Max }},
1658 {std::make_pair(
"min", 1),
FunctionData { Operation::Min }},
1659 {std::make_pair(
"odd", 1),
FunctionData { Operation::Odd }},
1660 {std::make_pair(
"range", 1),
FunctionData { Operation::Range }},
1661 {std::make_pair(
"round", 2),
FunctionData { Operation::Round }},
1662 {std::make_pair(
"sort", 1),
FunctionData { Operation::Sort }},
1663 {std::make_pair(
"upper", 1),
FunctionData { Operation::Upper }},
1664 {std::make_pair(
"super", 0),
FunctionData { Operation::Super }},
1665 {std::make_pair(
"super", 1),
FunctionData { Operation::Super }},
1666 {std::make_pair(
"join", 2),
FunctionData { Operation::Join }},
1671 function_storage.emplace(std::make_pair(
static_cast<std::string
>(
name), num_args),
FunctionData {
op });
1675 function_storage.emplace(std::make_pair(
static_cast<std::string
>(
name), num_args),
FunctionData { Operation::Callback, callback });
1679 auto it = function_storage.find(std::make_pair(
static_cast<std::string
>(
name), num_args));
1680 if (it != function_storage.end()) {
1684 }
else if (num_args > 0) {
1685 it = function_storage.find(std::make_pair(
static_cast<std::string
>(
name), VARIADIC));
1686 if (it != function_storage.end()) {
1697 #endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_
1700 #ifndef INCLUDE_INJA_PARSER_HPP_
1701 #define INCLUDE_INJA_PARSER_HPP_
1713 #ifndef INCLUDE_INJA_EXCEPTIONS_HPP_
1714 #define INCLUDE_INJA_EXCEPTIONS_HPP_
1716 #include <stdexcept>
1760 #endif // INCLUDE_INJA_EXCEPTIONS_HPP_
1765 #ifndef INCLUDE_INJA_LEXER_HPP_
1766 #define INCLUDE_INJA_LEXER_HPP_
1774 #ifndef INCLUDE_INJA_TOKEN_HPP_
1775 #define INCLUDE_INJA_TOKEN_HPP_
1829 explicit constexpr
Token() =
default;
1836 case Kind::LineStatementClose:
1841 return static_cast<std::string
>(
text);
1848 #endif // INCLUDE_INJA_TOKEN_HPP_
1851 #ifndef INCLUDE_INJA_UTILS_HPP_
1852 #define INCLUDE_INJA_UTILS_HPP_
1854 #include <algorithm>
1867 file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
1868 #ifndef INJA_NOEXCEPTION
1871 }
catch (
const std::ios_base::failure & ) {
1879 namespace string_view {
1880 inline nonstd::string_view
slice(nonstd::string_view view,
size_t start,
size_t end) {
1881 start = std::min(start, view.size());
1882 end = std::min(
std::max(start, end), view.size());
1883 return view.substr(start, end - start);
1886 inline std::pair<nonstd::string_view, nonstd::string_view>
split(nonstd::string_view view,
char Separator) {
1887 size_t idx = view.find(Separator);
1888 if (idx == nonstd::string_view::npos) {
1889 return std::make_pair(view, nonstd::string_view());
1891 return std::make_pair(
slice(view, 0, idx),
slice(view, idx + 1, nonstd::string_view::npos));
1895 return (view.size() >=
prefix.size() && view.compare(0,
prefix.size(),
prefix) == 0);
1902 std::size_t last_newline = sliced.rfind(
"\n");
1904 if (last_newline == nonstd::string_view::npos) {
1905 return {1, sliced.length() + 1};
1909 size_t count_lines = 0;
1910 size_t search_start = 0;
1911 while (search_start <= sliced.size()) {
1912 search_start = sliced.find(
"\n", search_start) + 1;
1913 if (search_start == 0) {
1919 return {count_lines + 1, sliced.length() - last_newline};
1923 const std::string&
t)
1925 if (f.empty())
return;
1926 for (
auto pos = s.find(f);
1927 pos != std::string::npos;
1928 s.replace(pos, f.size(),
t),
1929 pos = s.find(f, pos +
t.size()))
1935 #endif // INCLUDE_INJA_UTILS_HPP_
1947 ExpressionStartForceLstrip,
1952 StatementStartNoLstrip,
1953 StatementStartForceLstrip,
1956 CommentStartForceLstrip,
1977 if (tok_start >= m_in.size()) {
1978 return make_token(Token::Kind::Eof);
1980 const char ch = m_in[tok_start];
1981 if (ch ==
' ' || ch ==
'\t' || ch ==
'\r') {
1988 state = State::Text;
1989 pos = tok_start + close_trim.size();
1990 const Token tok = make_token(closeKind);
1991 skip_whitespaces_and_newlines();
1996 state = State::Text;
1997 pos = tok_start + close.size();
1998 const Token tok = make_token(closeKind);
2000 skip_whitespaces_and_first_newline();
2011 pos = tok_start + 1;
2012 if (std::isalpha(ch)) {
2013 minus_state = MinusState::Operator;
2017 const MinusState current_minus_state = minus_state;
2018 if (minus_state == MinusState::Operator) {
2019 minus_state = MinusState::Number;
2024 return make_token(Token::Kind::Plus);
2026 if (current_minus_state == MinusState::Operator) {
2027 return make_token(Token::Kind::Minus);
2029 return scan_number();
2031 return make_token(Token::Kind::Times);
2033 return make_token(Token::Kind::Slash);
2035 return make_token(Token::Kind::Power);
2037 return make_token(Token::Kind::Percent);
2039 return make_token(Token::Kind::Dot);
2041 return make_token(Token::Kind::Comma);
2043 return make_token(Token::Kind::Colon);
2045 return make_token(Token::Kind::LeftParen);
2047 minus_state = MinusState::Operator;
2048 return make_token(Token::Kind::RightParen);
2050 return make_token(Token::Kind::LeftBracket);
2052 minus_state = MinusState::Operator;
2053 return make_token(Token::Kind::RightBracket);
2055 return make_token(Token::Kind::LeftBrace);
2057 minus_state = MinusState::Operator;
2058 return make_token(Token::Kind::RightBrace);
2060 if (pos < m_in.size() && m_in[pos] ==
'=') {
2062 return make_token(Token::Kind::GreaterEqual);
2064 return make_token(Token::Kind::GreaterThan);
2066 if (pos < m_in.size() && m_in[pos] ==
'=') {
2068 return make_token(Token::Kind::LessEqual);
2070 return make_token(Token::Kind::LessThan);
2072 if (pos < m_in.size() && m_in[pos] ==
'=') {
2074 return make_token(Token::Kind::Equal);
2076 return make_token(Token::Kind::Unknown);
2078 if (pos < m_in.size() && m_in[pos] ==
'=') {
2080 return make_token(Token::Kind::NotEqual);
2082 return make_token(Token::Kind::Unknown);
2084 return scan_string();
2095 minus_state = MinusState::Operator;
2096 return scan_number();
2100 minus_state = MinusState::Operator;
2103 return make_token(Token::Kind::Unknown);
2109 if (pos >= m_in.size()) {
2112 const char ch = m_in[pos];
2113 if (!std::isalnum(ch) && ch !=
'.' && ch !=
'/' && ch !=
'_' && ch !=
'-') {
2118 return make_token(Token::Kind::Id);
2123 if (pos >= m_in.size()) {
2126 const char ch = m_in[pos];
2128 if (!std::isdigit(ch) && ch !=
'.' && ch !=
'e' && ch !=
'E' && ch !=
'+' && ch !=
'-') {
2133 return make_token(Token::Kind::Number);
2139 if (pos >= m_in.size()) {
2142 const char ch = m_in[pos++];
2145 }
else if (!
escape && ch == m_in[tok_start]) {
2151 return make_token(Token::Kind::String);
2157 if (pos < m_in.size()) {
2158 while (pos < m_in.size() && (m_in[pos] ==
' ' || m_in[pos] ==
'\t' || m_in[pos] ==
'\n' || m_in[pos] ==
'\r')) {
2165 if (pos < m_in.size()) {
2166 while (pos < m_in.size() && (m_in[pos] ==
' ' || m_in[pos] ==
'\t')) {
2171 if (pos < m_in.size()) {
2172 const char ch = m_in[pos];
2175 }
else if (ch ==
'\r') {
2177 if (pos < m_in.size() && m_in[pos] ==
'\n') {
2186 while (!
result.empty()) {
2187 const char ch =
result.back();
2188 if (ch ==
' ' || ch ==
'\t') {
2190 }
else if (ch ==
'\n' || ch ==
'\r') {
2210 state = State::Text;
2211 minus_state = MinusState::Number;
2215 m_in = m_in.substr(3);
2223 if (tok_start >= m_in.size()) {
2224 return make_token(Token::Kind::Eof);
2231 const size_t open_start = m_in.substr(pos).find_first_of(config.open_chars);
2232 if (open_start == nonstd::string_view::npos) {
2235 return make_token(Token::Kind::Text);
2240 nonstd::string_view open_str = m_in.substr(pos);
2241 bool must_lstrip =
false;
2244 state = State::ExpressionStartForceLstrip;
2247 state = State::ExpressionStart;
2251 state = State::StatementStartNoLstrip;
2253 state = State::StatementStartForceLstrip;
2256 state = State::StatementStart;
2257 must_lstrip = config.lstrip_blocks;
2261 state = State::CommentStartForceLstrip;
2264 state = State::CommentStart;
2265 must_lstrip = config.lstrip_blocks;
2268 state = State::LineStart;
2276 text = clear_final_line_if_whitespace(
text);
2284 case State::ExpressionStart: {
2285 state = State::ExpressionBody;
2286 pos += config.expression_open.size();
2287 return make_token(Token::Kind::ExpressionOpen);
2289 case State::ExpressionStartForceLstrip: {
2290 state = State::ExpressionBody;
2291 pos += config.expression_open_force_lstrip.size();
2292 return make_token(Token::Kind::ExpressionOpen);
2294 case State::LineStart: {
2295 state = State::LineBody;
2296 pos += config.line_statement.size();
2297 return make_token(Token::Kind::LineStatementOpen);
2299 case State::StatementStart: {
2300 state = State::StatementBody;
2301 pos += config.statement_open.size();
2302 return make_token(Token::Kind::StatementOpen);
2304 case State::StatementStartNoLstrip: {
2305 state = State::StatementBody;
2306 pos += config.statement_open_no_lstrip.size();
2307 return make_token(Token::Kind::StatementOpen);
2309 case State::StatementStartForceLstrip: {
2310 state = State::StatementBody;
2311 pos += config.statement_open_force_lstrip.size();
2312 return make_token(Token::Kind::StatementOpen);
2314 case State::CommentStart: {
2315 state = State::CommentBody;
2316 pos += config.comment_open.size();
2317 return make_token(Token::Kind::CommentOpen);
2319 case State::CommentStartForceLstrip: {
2320 state = State::CommentBody;
2321 pos += config.comment_open_force_lstrip.size();
2322 return make_token(Token::Kind::CommentOpen);
2324 case State::ExpressionBody:
2325 return scan_body(config.expression_close, Token::Kind::ExpressionClose, config.expression_close_force_rstrip);
2326 case State::LineBody:
2327 return scan_body(
"\n", Token::Kind::LineStatementClose);
2328 case State::StatementBody:
2329 return scan_body(config.statement_close, Token::Kind::StatementClose, config.statement_close_force_rstrip, config.trim_blocks);
2330 case State::CommentBody: {
2332 const size_t end = m_in.substr(pos).find(config.comment_close);
2333 if (end == nonstd::string_view::npos) {
2335 return make_token(Token::Kind::Eof);
2342 state = State::Text;
2343 pos += end + config.comment_close.size();
2344 Token tok = make_token(Token::Kind::CommentClose);
2346 if (must_rstrip || config.trim_blocks) {
2347 skip_whitespaces_and_first_newline();
2361 #endif // INCLUDE_INJA_LEXER_HPP_
2364 #ifndef INCLUDE_INJA_NODE_HPP_
2365 #define INCLUDE_INJA_NODE_HPP_
2383 class ExpressionNode;
2387 class ExpressionListNode;
2388 class StatementNode;
2389 class ForStatementNode;
2390 class ForArrayStatementNode;
2391 class ForObjectStatementNode;
2392 class IfStatementNode;
2393 class IncludeStatementNode;
2394 class ExtendsStatementNode;
2395 class BlockStatementNode;
2396 class SetStatementNode;
2403 virtual void visit(
const BlockNode& node) = 0;
2404 virtual void visit(
const TextNode& node) = 0;
2407 virtual void visit(
const JsonNode& node) = 0;
2437 std::vector<std::shared_ptr<AstNode>>
nodes;
2485 nonstd::string_view part;
2488 result.append(part.begin(), part.end());
2489 }
while (!ptr_name.empty());
2521 switch (operation) {
2525 associativity = Associativity::Left;
2530 associativity = Associativity::Left;
2535 associativity = Associativity::Left;
2540 associativity = Associativity::Left;
2545 associativity = Associativity::Left;
2547 case Op::NotEqual: {
2550 associativity = Associativity::Left;
2555 associativity = Associativity::Left;
2557 case Op::GreaterEqual: {
2560 associativity = Associativity::Left;
2565 associativity = Associativity::Left;
2567 case Op::LessEqual: {
2570 associativity = Associativity::Left;
2575 associativity = Associativity::Left;
2577 case Op::Subtract: {
2580 associativity = Associativity::Left;
2582 case Op::Multiplication: {
2585 associativity = Associativity::Left;
2587 case Op::Division: {
2590 associativity = Associativity::Left;
2595 associativity = Associativity::Right;
2600 associativity = Associativity::Left;
2605 associativity = Associativity::Left;
2609 associativity = Associativity::Left;
2621 std::shared_ptr<ExpressionNode>
root;
2680 bool has_false_statement {
false};
2739 #endif // INCLUDE_INJA_NODE_HPP_
2742 #ifndef INCLUDE_INJA_TEMPLATE_HPP_
2743 #define INCLUDE_INJA_TEMPLATE_HPP_
2753 #ifndef INCLUDE_INJA_STATISTICS_HPP_
2754 #define INCLUDE_INJA_STATISTICS_HPP_
2767 for (
auto& n : node.
nodes) {
2777 variable_counter += 1;
2787 node.
root->accept(*
this);
2827 #endif // INCLUDE_INJA_STATISTICS_HPP_
2842 explicit Template(
const std::string& content): content(content) { }
2847 root.accept(statistic_visitor);
2848 return statistic_visitor.variable_counter;
2856 #endif // INCLUDE_INJA_TEMPLATE_HPP_
2878 bool have_peek_tok {
false};
2880 size_t current_paren_level {0};
2881 size_t current_bracket_level {0};
2882 size_t current_brace_level {0};
2901 if (have_peek_tok) {
2903 have_peek_tok =
false;
2910 if (!have_peek_tok) {
2911 peek_tok = lexer.
scan();
2912 have_peek_tok =
true;
2917 nonstd::string_view json_text(json_literal_start.data(), tok.
text.data() - json_literal_start.data() + tok.
text.size());
2918 arguments.emplace_back(std::make_shared<LiteralNode>(json::parse(json_text), json_text.data() - content_ptr));
2922 auto function = operator_stack.top();
2923 operator_stack.pop();
2925 for (
int i = 0; i <
function->number_args; ++i) {
2926 function->arguments.insert(function->arguments.begin(), arguments.back());
2927 arguments.pop_back();
2929 arguments.emplace_back(
function);
2933 if (config.search_included_templates_in_files && template_storage.find(template_name) == template_storage.end()) {
2935 template_name =
static_cast<std::string
>(
path) + template_name;
2936 if (template_name.compare(0, 2,
"./") == 0) {
2937 template_name.erase(0, 2);
2940 if (template_storage.find(template_name) == template_storage.end()) {
2941 auto include_template =
Template(load_file(template_name));
2942 template_storage.emplace(template_name, include_template);
2943 parse_into_template(template_storage[template_name], template_name);
2949 while (tok.
kind != closing && tok.
kind != Token::Kind::Eof) {
2952 case Token::Kind::String: {
2953 if (current_brace_level == 0 && current_bracket_level == 0) {
2954 json_literal_start = tok.
text;
2955 add_json_literal(tmpl.
content.c_str());
2959 case Token::Kind::Number: {
2960 if (current_brace_level == 0 && current_bracket_level == 0) {
2961 json_literal_start = tok.
text;
2962 add_json_literal(tmpl.
content.c_str());
2966 case Token::Kind::LeftBracket: {
2967 if (current_brace_level == 0 && current_bracket_level == 0) {
2968 json_literal_start = tok.
text;
2970 current_bracket_level += 1;
2973 case Token::Kind::LeftBrace: {
2974 if (current_brace_level == 0 && current_bracket_level == 0) {
2975 json_literal_start = tok.
text;
2977 current_brace_level += 1;
2980 case Token::Kind::RightBracket: {
2981 if (current_bracket_level == 0) {
2982 throw_parser_error(
"unexpected ']'");
2985 current_bracket_level -= 1;
2986 if (current_brace_level == 0 && current_bracket_level == 0) {
2987 add_json_literal(tmpl.
content.c_str());
2991 case Token::Kind::RightBrace: {
2992 if (current_brace_level == 0) {
2993 throw_parser_error(
"unexpected '}'");
2996 current_brace_level -= 1;
2997 if (current_brace_level == 0 && current_bracket_level == 0) {
2998 add_json_literal(tmpl.
content.c_str());
3002 case Token::Kind::Id: {
3006 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")) {
3007 if (current_brace_level == 0 && current_bracket_level == 0) {
3008 json_literal_start = tok.
text;
3009 add_json_literal(tmpl.
content.c_str());
3013 }
else if (tok.
text ==
"and" || tok.
text ==
"or" || tok.
text ==
"in" || tok.
text ==
"not") {
3014 goto parse_operator;
3017 }
else if (peek_tok.
kind == Token::Kind::LeftParen) {
3018 operator_stack.emplace(std::make_shared<FunctionNode>(
static_cast<std::string
>(tok.
text), tok.
text.data() - tmpl.
content.c_str()));
3019 function_stack.emplace(operator_stack.top().get(), current_paren_level);
3023 arguments.emplace_back(std::make_shared<JsonNode>(
static_cast<std::string
>(tok.
text), tok.
text.data() - tmpl.
content.c_str()));
3028 case Token::Kind::Equal:
3029 case Token::Kind::NotEqual:
3030 case Token::Kind::GreaterThan:
3031 case Token::Kind::GreaterEqual:
3032 case Token::Kind::LessThan:
3033 case Token::Kind::LessEqual:
3034 case Token::Kind::Plus:
3035 case Token::Kind::Minus:
3036 case Token::Kind::Times:
3037 case Token::Kind::Slash:
3038 case Token::Kind::Power:
3039 case Token::Kind::Percent:
3040 case Token::Kind::Dot: {
3045 case Token::Kind::Id: {
3046 if (tok.
text ==
"and") {
3047 operation = FunctionStorage::Operation::And;
3048 }
else if (tok.
text ==
"or") {
3049 operation = FunctionStorage::Operation::Or;
3050 }
else if (tok.
text ==
"in") {
3051 operation = FunctionStorage::Operation::In;
3052 }
else if (tok.
text ==
"not") {
3053 operation = FunctionStorage::Operation::Not;
3055 throw_parser_error(
"unknown operator in parser.");
3058 case Token::Kind::Equal: {
3059 operation = FunctionStorage::Operation::Equal;
3061 case Token::Kind::NotEqual: {
3062 operation = FunctionStorage::Operation::NotEqual;
3064 case Token::Kind::GreaterThan: {
3065 operation = FunctionStorage::Operation::Greater;
3067 case Token::Kind::GreaterEqual: {
3068 operation = FunctionStorage::Operation::GreaterEqual;
3070 case Token::Kind::LessThan: {
3071 operation = FunctionStorage::Operation::Less;
3073 case Token::Kind::LessEqual: {
3074 operation = FunctionStorage::Operation::LessEqual;
3076 case Token::Kind::Plus: {
3077 operation = FunctionStorage::Operation::Add;
3079 case Token::Kind::Minus: {
3080 operation = FunctionStorage::Operation::Subtract;
3082 case Token::Kind::Times: {
3083 operation = FunctionStorage::Operation::Multiplication;
3085 case Token::Kind::Slash: {
3086 operation = FunctionStorage::Operation::Division;
3088 case Token::Kind::Power: {
3089 operation = FunctionStorage::Operation::Power;
3091 case Token::Kind::Percent: {
3092 operation = FunctionStorage::Operation::Modulo;
3094 case Token::Kind::Dot: {
3095 operation = FunctionStorage::Operation::AtId;
3098 throw_parser_error(
"unknown operator in parser.");
3101 auto function_node = std::make_shared<FunctionNode>(operation, tok.
text.data() - tmpl.
content.c_str());
3103 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)) {
3107 operator_stack.emplace(function_node);
3110 case Token::Kind::Comma: {
3111 if (current_brace_level == 0 && current_bracket_level == 0) {
3112 if (function_stack.empty()) {
3113 throw_parser_error(
"unexpected ','");
3116 function_stack.top().first->number_args += 1;
3120 case Token::Kind::Colon: {
3121 if (current_brace_level == 0 && current_bracket_level == 0) {
3122 throw_parser_error(
"unexpected ':'");
3126 case Token::Kind::LeftParen: {
3127 current_paren_level += 1;
3128 operator_stack.emplace(std::make_shared<FunctionNode>(FunctionStorage::Operation::ParenLeft, tok.
text.data() - tmpl.
content.c_str()));
3131 if (peek_tok.
kind == Token::Kind::RightParen) {
3132 if (!function_stack.empty() && function_stack.top().second == current_paren_level - 1) {
3133 function_stack.top().first->number_args = 0;
3138 case Token::Kind::RightParen: {
3139 current_paren_level -= 1;
3140 while (!operator_stack.empty() && operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft) {
3144 if (!operator_stack.empty() && operator_stack.top()->operation == FunctionStorage::Operation::ParenLeft) {
3145 operator_stack.pop();
3148 if (!function_stack.empty() && function_stack.top().second == current_paren_level) {
3149 auto func = function_stack.top().first;
3150 auto function_data = function_storage.
find_function(func->name, func->number_args);
3152 throw_parser_error(
"unknown function " + func->name);
3154 func->operation = function_data.operation;
3155 if (function_data.operation == FunctionStorage::Operation::Callback) {
3156 func->callback = function_data.callback;
3159 if (operator_stack.empty()) {
3160 throw_parser_error(
"internal error at function " + func->name);
3164 function_stack.pop();
3174 while (!operator_stack.empty()) {
3178 if (arguments.size() == 1) {
3179 current_expression_list->root = arguments[0];
3182 }
else if (arguments.size() > 1) {
3183 throw_parser_error(
"malformed expression");
3190 if (tok.
kind != Token::Kind::Id) {
3194 if (tok.
text ==
static_cast<decltype(tok.
text)
>(
"if")) {
3197 auto if_statement_node = std::make_shared<IfStatementNode>(current_block, tok.
text.data() - tmpl.
content.c_str());
3198 current_block->nodes.emplace_back(if_statement_node);
3199 if_statement_stack.emplace(if_statement_node.get());
3200 current_block = &if_statement_node->true_statement;
3201 current_expression_list = &if_statement_node->condition;
3203 if (!parse_expression(tmpl, closing)) {
3207 }
else if (tok.
text ==
static_cast<decltype(tok.
text)
>(
"else")) {
3208 if (if_statement_stack.empty()) {
3209 throw_parser_error(
"else without matching if");
3211 auto &if_statement_data = if_statement_stack.top();
3214 if_statement_data->has_false_statement =
true;
3215 current_block = &if_statement_data->false_statement;
3218 if (tok.
kind == Token::Kind::Id && tok.
text ==
static_cast<decltype(tok.
text)
>(
"if")) {
3221 auto if_statement_node = std::make_shared<IfStatementNode>(
true, current_block, tok.
text.data() - tmpl.
content.c_str());
3222 current_block->nodes.emplace_back(if_statement_node);
3223 if_statement_stack.emplace(if_statement_node.get());
3224 current_block = &if_statement_node->true_statement;
3225 current_expression_list = &if_statement_node->condition;
3227 if (!parse_expression(tmpl, closing)) {
3232 }
else if (tok.
text ==
static_cast<decltype(tok.
text)
>(
"endif")) {
3233 if (if_statement_stack.empty()) {
3234 throw_parser_error(
"endif without matching if");
3238 while (if_statement_stack.top()->is_nested) {
3239 if_statement_stack.pop();
3242 auto &if_statement_data = if_statement_stack.top();
3245 current_block = if_statement_data->parent;
3246 if_statement_stack.pop();
3248 }
else if (tok.
text ==
static_cast<decltype(tok.
text)
>(
"block")) {
3251 if (tok.
kind != Token::Kind::Id) {
3252 throw_parser_error(
"expected block name, got '" + tok.
describe() +
"'");
3255 const std::string block_name =
static_cast<std::string
>(tok.
text);
3257 auto block_statement_node = std::make_shared<BlockStatementNode>(current_block, block_name, tok.
text.data() - tmpl.
content.c_str());
3258 current_block->nodes.emplace_back(block_statement_node);
3259 block_statement_stack.emplace(block_statement_node.get());
3260 current_block = &block_statement_node->block;
3261 auto success = tmpl.
block_storage.emplace(block_name, block_statement_node);
3262 if (!success.second) {
3263 throw_parser_error(
"block with the name '" + block_name +
"' does already exist");
3268 }
else if (tok.
text ==
static_cast<decltype(tok.
text)
>(
"endblock")) {
3269 if (block_statement_stack.empty()) {
3270 throw_parser_error(
"endblock without matching block");
3273 auto &block_statement_data = block_statement_stack.top();
3276 current_block = block_statement_data->parent;
3277 block_statement_stack.pop();
3279 }
else if (tok.
text ==
static_cast<decltype(tok.
text)
>(
"for")) {
3283 if (tok.
kind != Token::Kind::Id) {
3284 throw_parser_error(
"expected id, got '" + tok.
describe() +
"'");
3287 Token value_token = tok;
3291 std::shared_ptr<ForStatementNode> for_statement_node;
3292 if (tok.
kind == Token::Kind::Comma) {
3294 if (tok.
kind != Token::Kind::Id) {
3295 throw_parser_error(
"expected id, got '" + tok.
describe() +
"'");
3298 Token key_token = std::move(value_token);
3302 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());
3306 for_statement_node = std::make_shared<ForArrayStatementNode>(
static_cast<std::string
>(value_token.
text), current_block, tok.
text.data() - tmpl.
content.c_str());
3309 current_block->nodes.emplace_back(for_statement_node);
3310 for_statement_stack.emplace(for_statement_node.get());
3311 current_block = &for_statement_node->body;
3312 current_expression_list = &for_statement_node->condition;
3314 if (tok.
kind != Token::Kind::Id || tok.
text !=
static_cast<decltype(tok.
text)
>(
"in")) {
3315 throw_parser_error(
"expected 'in', got '" + tok.
describe() +
"'");
3319 if (!parse_expression(tmpl, closing)) {
3323 }
else if (tok.
text ==
static_cast<decltype(tok.
text)
>(
"endfor")) {
3324 if (for_statement_stack.empty()) {
3325 throw_parser_error(
"endfor without matching for");
3328 auto &for_statement_data = for_statement_stack.top();
3331 current_block = for_statement_data->parent;
3332 for_statement_stack.pop();
3334 }
else if (tok.
text ==
static_cast<decltype(tok.
text)
>(
"include")) {
3337 if (tok.
kind != Token::Kind::String) {
3338 throw_parser_error(
"expected string, got '" + tok.
describe() +
"'");
3341 std::string template_name = json::parse(tok.
text).get_ref<
const std::string &>();
3342 add_to_template_storage(
path, template_name);
3344 current_block->nodes.emplace_back(std::make_shared<IncludeStatementNode>(template_name, tok.
text.data() - tmpl.
content.c_str()));
3348 }
else if (tok.
text ==
static_cast<decltype(tok.
text)
>(
"extends")) {
3351 if (tok.
kind != Token::Kind::String) {
3352 throw_parser_error(
"expected string, got '" + tok.
describe() +
"'");
3355 std::string template_name = json::parse(tok.
text).get_ref<
const std::string &>();
3356 add_to_template_storage(
path, template_name);
3358 current_block->nodes.emplace_back(std::make_shared<ExtendsStatementNode>(template_name, tok.
text.data() - tmpl.
content.c_str()));
3362 }
else if (tok.
text ==
static_cast<decltype(tok.
text)
>(
"set")) {
3365 if (tok.
kind != Token::Kind::Id) {
3366 throw_parser_error(
"expected variable name, got '" + tok.
describe() +
"'");
3369 std::string key =
static_cast<std::string
>(tok.
text);
3372 auto set_statement_node = std::make_shared<SetStatementNode>(key, tok.
text.data() - tmpl.
content.c_str());
3373 current_block->nodes.emplace_back(set_statement_node);
3374 current_expression_list = &set_statement_node->expression;
3376 if (tok.
text !=
static_cast<decltype(tok.
text)
>(
"=")) {
3377 throw_parser_error(
"expected '=', got '" + tok.
describe() +
"'");
3381 if (!parse_expression(tmpl, closing)) {
3393 current_block = &tmpl.
root;
3398 case Token::Kind::Eof: {
3399 if (!if_statement_stack.empty()) {
3400 throw_parser_error(
"unmatched if");
3402 if (!for_statement_stack.empty()) {
3403 throw_parser_error(
"unmatched for");
3406 case Token::Kind::Text: {
3407 current_block->nodes.emplace_back(std::make_shared<TextNode>(tok.
text.data() - tmpl.
content.c_str(), tok.
text.size()));
3409 case Token::Kind::StatementOpen: {
3411 if (!parse_statement(tmpl, Token::Kind::StatementClose,
path)) {
3412 throw_parser_error(
"expected statement, got '" + tok.
describe() +
"'");
3414 if (tok.
kind != Token::Kind::StatementClose) {
3415 throw_parser_error(
"expected statement close, got '" + tok.
describe() +
"'");
3418 case Token::Kind::LineStatementOpen: {
3420 if (!parse_statement(tmpl, Token::Kind::LineStatementClose,
path)) {
3421 throw_parser_error(
"expected statement, got '" + tok.
describe() +
"'");
3423 if (tok.
kind != Token::Kind::LineStatementClose && tok.
kind != Token::Kind::Eof) {
3424 throw_parser_error(
"expected line statement close, got '" + tok.
describe() +
"'");
3427 case Token::Kind::ExpressionOpen: {
3430 auto expression_list_node = std::make_shared<ExpressionListNode>(tok.
text.data() - tmpl.
content.c_str());
3431 current_block->nodes.emplace_back(expression_list_node);
3432 current_expression_list = expression_list_node.get();
3434 if (!parse_expression(tmpl, Token::Kind::ExpressionClose)) {
3435 throw_parser_error(
"expected expression, got '" + tok.
describe() +
"'");
3438 if (tok.
kind != Token::Kind::ExpressionClose) {
3439 throw_parser_error(
"expected expression close, got '" + tok.
describe() +
"'");
3442 case Token::Kind::CommentOpen: {
3444 if (tok.
kind != Token::Kind::CommentClose) {
3445 throw_parser_error(
"expected comment close, got '" + tok.
describe() +
"'");
3449 throw_parser_error(
"unexpected token '" + tok.
describe() +
"'");
3457 explicit Parser(
const ParserConfig &parser_config,
const LexerConfig &lexer_config,
3459 : config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) { }
3468 return parse(input,
"./");
3475 auto sub_parser =
Parser(config, lexer.
get_config(), template_storage, function_storage);
3476 sub_parser.parse_into(tmpl,
path);
3482 std::string
text((std::istreambuf_iterator<char>(
file)), std::istreambuf_iterator<char>());
3489 #endif // INCLUDE_INJA_PARSER_HPP_
3492 #ifndef INCLUDE_INJA_RENDERER_HPP_
3493 #define INCLUDE_INJA_RENDERER_HPP_
3495 #include <algorithm>
3527 size_t current_level {0};
3535 json* current_loop_data = &json_additional_data[
"loop"];
3541 bool break_rendering {
false};
3544 if (
data->is_boolean()) {
3545 return data->get<
bool>();
3546 }
else if (
data->is_number()) {
3547 return (*
data != 0);
3548 }
else if (
data->is_null()) {
3551 return !
data->empty();
3555 if (value->is_string()) {
3557 }
else if (value->is_number_integer()) {
3559 }
else if (value->is_null()) {
3561 *output_stream << value->dump();
3566 if (!expression_list.
root) {
3567 throw_renderer_error(
"empty expression", expression_list);
3570 expression_list.
root->accept(*
this);
3572 if (json_eval_stack.empty()) {
3573 throw_renderer_error(
"empty expression", expression_list);
3574 }
else if (json_eval_stack.size() != 1) {
3575 throw_renderer_error(
"malformed expression", expression_list);
3578 const auto result = json_eval_stack.top();
3579 json_eval_stack.pop();
3582 if (not_found_stack.empty()) {
3583 throw_renderer_error(
"expression could not be evaluated", expression_list);
3586 auto node = not_found_stack.top();
3587 not_found_stack.pop();
3589 throw_renderer_error(
"variable '" +
static_cast<std::string
>(node->name) +
"' not found", *node);
3591 return std::make_shared<json>(*
result);
3599 template<
size_t N,
size_t N_start = 0,
bool throw_not_found=true>
3601 if (node.
arguments.size() < N_start + N) {
3605 for (
size_t i = N_start; i < N_start + N; i += 1) {
3609 if (json_eval_stack.size() < N) {
3610 throw_renderer_error(
"function needs " +
std::to_string(N) +
" variables, but has only found " +
std::to_string(json_eval_stack.size()), node);
3613 std::array<const json*, N>
result;
3614 for (
size_t i = 0; i < N; i += 1) {
3615 result[N - i - 1] = json_eval_stack.top();
3616 json_eval_stack.pop();
3618 if (!
result[N - i - 1]) {
3619 const auto json_node = not_found_stack.top();
3620 not_found_stack.pop();
3622 if (throw_not_found) {
3623 throw_renderer_error(
"variable '" +
static_cast<std::string
>(json_node->name) +
"' not found", *json_node);
3630 template<
bool throw_not_found=true>
3637 if (json_eval_stack.size() < N) {
3638 throw_renderer_error(
"function needs " +
std::to_string(N) +
" variables, but has only found " +
std::to_string(json_eval_stack.size()), node);
3642 for (
size_t i = 0; i < N; i += 1) {
3643 result[N - i - 1] = json_eval_stack.top();
3644 json_eval_stack.pop();
3646 if (!
result[N - i - 1]) {
3647 const auto json_node = not_found_stack.top();
3648 not_found_stack.pop();
3650 if (throw_not_found) {
3651 throw_renderer_error(
"variable '" +
static_cast<std::string
>(json_node->name) +
"' not found", *json_node);
3659 for (
auto& n : node.
nodes) {
3662 if (break_rendering) {
3669 output_stream->write(current_template->
content.c_str() + node.
pos, node.
length);
3675 json_eval_stack.push(&node.
value);
3680 json_eval_stack.push(&(json_additional_data[node.
ptr]));
3683 json_eval_stack.push(&(*json_input)[node.
ptr]);
3688 if (function_data.operation == FunctionStorage::Operation::Callback) {
3690 const auto value = std::make_shared<json>(function_data.callback(empty_args));
3691 json_tmp_stack.push_back(value);
3692 json_eval_stack.push(value.get());
3695 json_eval_stack.push(
nullptr);
3696 not_found_stack.emplace(&node);
3702 std::shared_ptr<json> result_ptr;
3706 const auto args = get_arguments<1>(node);
3707 result_ptr = std::make_shared<json>(!truthy(
args[0]));
3708 json_tmp_stack.push_back(result_ptr);
3709 json_eval_stack.push(result_ptr.get());
3712 result_ptr = std::make_shared<json>(truthy(get_arguments<1, 0>(node)[0]) && truthy(get_arguments<1, 1>(node)[0]));
3713 json_tmp_stack.push_back(result_ptr);
3714 json_eval_stack.push(result_ptr.get());
3717 result_ptr = std::make_shared<json>(truthy(get_arguments<1, 0>(node)[0]) || truthy(get_arguments<1, 1>(node)[0]));
3718 json_tmp_stack.push_back(result_ptr);
3719 json_eval_stack.push(result_ptr.get());
3722 const auto args = get_arguments<2>(node);
3723 result_ptr = std::make_shared<json>(std::find(
args[1]->begin(),
args[1]->end(), *
args[0]) !=
args[1]->end());
3724 json_tmp_stack.push_back(result_ptr);
3725 json_eval_stack.push(result_ptr.get());
3728 const auto args = get_arguments<2>(node);
3729 result_ptr = std::make_shared<json>(*
args[0] == *
args[1]);
3730 json_tmp_stack.push_back(result_ptr);
3731 json_eval_stack.push(result_ptr.get());
3733 case Op::NotEqual: {
3734 const auto args = get_arguments<2>(node);
3735 result_ptr = std::make_shared<json>(*
args[0] != *
args[1]);
3736 json_tmp_stack.push_back(result_ptr);
3737 json_eval_stack.push(result_ptr.get());
3740 const auto args = get_arguments<2>(node);
3741 result_ptr = std::make_shared<json>(*
args[0] > *
args[1]);
3742 json_tmp_stack.push_back(result_ptr);
3743 json_eval_stack.push(result_ptr.get());
3745 case Op::GreaterEqual: {
3746 const auto args = get_arguments<2>(node);
3747 result_ptr = std::make_shared<json>(*
args[0] >= *
args[1]);
3748 json_tmp_stack.push_back(result_ptr);
3749 json_eval_stack.push(result_ptr.get());
3752 const auto args = get_arguments<2>(node);
3753 result_ptr = std::make_shared<json>(*
args[0] < *
args[1]);
3754 json_tmp_stack.push_back(result_ptr);
3755 json_eval_stack.push(result_ptr.get());
3757 case Op::LessEqual: {
3758 const auto args = get_arguments<2>(node);
3759 result_ptr = std::make_shared<json>(*
args[0] <= *
args[1]);
3760 json_tmp_stack.push_back(result_ptr);
3761 json_eval_stack.push(result_ptr.get());
3764 const auto args = get_arguments<2>(node);
3765 if (
args[0]->is_string() &&
args[1]->is_string()) {
3766 result_ptr = std::make_shared<json>(
args[0]->get_ref<const std::string&>() +
args[1]->get_ref<const std::string&>());
3767 json_tmp_stack.push_back(result_ptr);
3768 }
else if (
args[0]->is_number_integer() &&
args[1]->is_number_integer()) {
3769 result_ptr = std::make_shared<json>(
args[0]->get<int>() +
args[1]->get<int>());
3770 json_tmp_stack.push_back(result_ptr);
3772 result_ptr = std::make_shared<json>(
args[0]->get<double>() +
args[1]->get<double>());
3773 json_tmp_stack.push_back(result_ptr);
3775 json_eval_stack.push(result_ptr.get());
3777 case Op::Subtract: {
3778 const auto args = get_arguments<2>(node);
3779 if (
args[0]->is_number_integer() &&
args[1]->is_number_integer()) {
3780 result_ptr = std::make_shared<json>(
args[0]->get<int>() -
args[1]->get<int>());
3781 json_tmp_stack.push_back(result_ptr);
3783 result_ptr = std::make_shared<json>(
args[0]->get<double>() -
args[1]->get<double>());
3784 json_tmp_stack.push_back(result_ptr);
3786 json_eval_stack.push(result_ptr.get());
3788 case Op::Multiplication: {
3789 const auto args = get_arguments<2>(node);
3790 if (
args[0]->is_number_integer() &&
args[1]->is_number_integer()) {
3791 result_ptr = std::make_shared<json>(
args[0]->get<int>() *
args[1]->get<int>());
3792 json_tmp_stack.push_back(result_ptr);
3794 result_ptr = std::make_shared<json>(
args[0]->get<double>() *
args[1]->get<double>());
3795 json_tmp_stack.push_back(result_ptr);
3797 json_eval_stack.push(result_ptr.get());
3799 case Op::Division: {
3800 const auto args = get_arguments<2>(node);
3801 if (
args[1]->get<double>() == 0) {
3802 throw_renderer_error(
"division by zero", node);
3804 result_ptr = std::make_shared<json>(
args[0]->get<double>() /
args[1]->get<double>());
3805 json_tmp_stack.push_back(result_ptr);
3806 json_eval_stack.push(result_ptr.get());
3809 const auto args = get_arguments<2>(node);
3810 if (
args[0]->is_number_integer() &&
args[1]->get<int>() >= 0) {
3811 int result =
static_cast<int>(std::pow(
args[0]->get<int>(),
args[1]->get<int>()));
3812 result_ptr = std::make_shared<json>(std::move(
result));
3813 json_tmp_stack.push_back(result_ptr);
3815 double result = std::pow(
args[0]->get<double>(),
args[1]->get<int>());
3816 result_ptr = std::make_shared<json>(std::move(
result));
3817 json_tmp_stack.push_back(result_ptr);
3819 json_eval_stack.push(result_ptr.get());
3822 const auto args = get_arguments<2>(node);
3823 result_ptr = std::make_shared<json>(
args[0]->get<int>() %
args[1]->get<int>());
3824 json_tmp_stack.push_back(result_ptr);
3825 json_eval_stack.push(result_ptr.get());
3828 const auto container = get_arguments<1, 0, false>(node)[0];
3830 if (not_found_stack.empty()) {
3831 throw_renderer_error(
"could not find element with given name", node);
3833 const auto id_node = not_found_stack.top();
3834 not_found_stack.pop();
3835 json_eval_stack.pop();
3836 json_eval_stack.push(&container->at(id_node->name));
3839 const auto args = get_arguments<2>(node);
3840 if (
args[0]->is_object()) {
3841 json_eval_stack.push(&
args[0]->at(
args[1]->get<std::string>()));
3843 json_eval_stack.push(&
args[0]->at(
args[1]->get<int>()));
3847 const auto test_arg = get_arguments<1, 0, false>(node)[0];
3848 json_eval_stack.push(test_arg ? test_arg : get_arguments<1, 1>(node)[0]);
3850 case Op::DivisibleBy: {
3851 const auto args = get_arguments<2>(node);
3852 const int divisor =
args[1]->get<
int>();
3853 result_ptr = std::make_shared<json>((divisor != 0) && (
args[0]->get<int>() % divisor == 0));
3854 json_tmp_stack.push_back(result_ptr);
3855 json_eval_stack.push(result_ptr.get());
3858 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<int>() % 2 == 0);
3859 json_tmp_stack.push_back(result_ptr);
3860 json_eval_stack.push(result_ptr.get());
3863 auto &&
name = get_arguments<1>(node)[0]->get_ref<
const std::string &>();
3865 json_tmp_stack.push_back(result_ptr);
3866 json_eval_stack.push(result_ptr.get());
3868 case Op::ExistsInObject: {
3869 const auto args = get_arguments<2>(node);
3870 auto &&
name =
args[1]->get_ref<
const std::string &>();
3871 result_ptr = std::make_shared<json>(
args[0]->find(
name) !=
args[0]->end());
3872 json_tmp_stack.push_back(result_ptr);
3873 json_eval_stack.push(result_ptr.get());
3876 const auto result = &get_arguments<1>(node)[0]->front();
3877 json_eval_stack.push(
result);
3880 result_ptr = std::make_shared<json>(std::stod(get_arguments<1>(node)[0]->get_ref<const std::string &>()));
3881 json_tmp_stack.push_back(result_ptr);
3882 json_eval_stack.push(result_ptr.get());
3885 result_ptr = std::make_shared<json>(std::stoi(get_arguments<1>(node)[0]->get_ref<const std::string &>()));
3886 json_tmp_stack.push_back(result_ptr);
3887 json_eval_stack.push(result_ptr.get());
3890 const auto result = &get_arguments<1>(node)[0]->back();
3891 json_eval_stack.push(
result);
3894 const auto val = get_arguments<1>(node)[0];
3895 if (val->is_string()) {
3896 result_ptr = std::make_shared<json>(val->get_ref<
const std::string &>().length());
3898 result_ptr = std::make_shared<json>(val->size());
3900 json_tmp_stack.push_back(result_ptr);
3901 json_eval_stack.push(result_ptr.get());
3904 std::string
result = get_arguments<1>(node)[0]->get<std::string>();
3906 result_ptr = std::make_shared<json>(std::move(
result));
3907 json_tmp_stack.push_back(result_ptr);
3908 json_eval_stack.push(result_ptr.get());
3911 const auto args = get_arguments<1>(node);
3912 const auto result = std::max_element(
args[0]->begin(),
args[0]->end());
3913 json_eval_stack.push(&(*
result));
3916 const auto args = get_arguments<1>(node);
3917 const auto result = std::min_element(
args[0]->begin(),
args[0]->end());
3918 json_eval_stack.push(&(*
result));
3921 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<int>() % 2 != 0);
3922 json_tmp_stack.push_back(result_ptr);
3923 json_eval_stack.push(result_ptr.get());
3926 std::vector<int>
result(get_arguments<1>(node)[0]->get<int>());
3928 result_ptr = std::make_shared<json>(std::move(
result));
3929 json_tmp_stack.push_back(result_ptr);
3930 json_eval_stack.push(result_ptr.get());
3933 const auto args = get_arguments<2>(node);
3934 const int precision =
args[1]->get<
int>();
3935 const double result = std::round(
args[0]->get<double>() * std::pow(10.0, precision)) / std::pow(10.0, precision);
3937 result_ptr = std::make_shared<json>(
int(
result));
3939 result_ptr = std::make_shared<json>(std::move(
result));
3941 json_tmp_stack.push_back(result_ptr);
3942 json_eval_stack.push(result_ptr.get());
3945 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->
get<std::vector<json>>());
3946 std::sort(result_ptr->begin(), result_ptr->end());
3947 json_tmp_stack.push_back(result_ptr);
3948 json_eval_stack.push(result_ptr.get());
3951 std::string
result = get_arguments<1>(node)[0]->get<std::string>();
3953 result_ptr = std::make_shared<json>(std::move(
result));
3954 json_tmp_stack.push_back(result_ptr);
3955 json_eval_stack.push(result_ptr.get());
3957 case Op::IsBoolean: {
3958 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_boolean());
3959 json_tmp_stack.push_back(result_ptr);
3960 json_eval_stack.push(result_ptr.get());
3962 case Op::IsNumber: {
3963 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number());
3964 json_tmp_stack.push_back(result_ptr);
3965 json_eval_stack.push(result_ptr.get());
3967 case Op::IsInteger: {
3968 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number_integer());
3969 json_tmp_stack.push_back(result_ptr);
3970 json_eval_stack.push(result_ptr.get());
3973 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number_float());
3974 json_tmp_stack.push_back(result_ptr);
3975 json_eval_stack.push(result_ptr.get());
3977 case Op::IsObject: {
3978 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_object());
3979 json_tmp_stack.push_back(result_ptr);
3980 json_eval_stack.push(result_ptr.get());
3983 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_array());
3984 json_tmp_stack.push_back(result_ptr);
3985 json_eval_stack.push(result_ptr.get());
3987 case Op::IsString: {
3988 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_string());
3989 json_tmp_stack.push_back(result_ptr);
3990 json_eval_stack.push(result_ptr.get());
3992 case Op::Callback: {
3993 auto args = get_argument_vector(node);
3994 result_ptr = std::make_shared<json>(node.
callback(
args));
3995 json_tmp_stack.push_back(result_ptr);
3996 json_eval_stack.push(result_ptr.get());
3999 const auto args = get_argument_vector(node);
4001 const size_t level_diff = (
args.size() == 1) ?
args[0]->get<int>() : 1;
4002 const size_t level = current_level + level_diff;
4004 if (block_statement_stack.empty()) {
4005 throw_renderer_error(
"super() call is not within a block", node);
4008 if (level < 1 || level > template_stack.size() - 1) {
4009 throw_renderer_error(
"level of super() call does not match parent templates (between 1 and " +
std::to_string(template_stack.size() - 1) +
")", node);
4012 const auto current_block_statement = block_statement_stack.back();
4014 const Template *old_template = current_template;
4015 const auto block_it = new_template->
block_storage.find(current_block_statement->name);
4017 current_template = new_template;
4018 current_level =
level;
4019 block_it->second->block.accept(*
this);
4021 current_template = old_template;
4023 throw_renderer_error(
"could not find block with name '" + current_block_statement->name +
"'", node);
4025 result_ptr = std::make_shared<json>(
nullptr);
4026 json_tmp_stack.push_back(result_ptr);
4027 json_eval_stack.push(result_ptr.get());
4030 const auto args = get_arguments<2>(node);
4031 const auto separator =
args[1]->get<std::string>();
4032 std::ostringstream os;
4034 for (
const auto& value : *
args[0]) {
4036 if (value.is_string()) {
4037 os << value.get<std::string>();
4043 result_ptr = std::make_shared<json>(os.str());
4044 json_tmp_stack.push_back(result_ptr);
4045 json_eval_stack.push(result_ptr.get());
4048 case Op::ParenRight:
4055 print_json(eval_expression_list(node));
4064 if (!
result->is_array()) {
4065 throw_renderer_error(
"object must be an array", node);
4068 if (!current_loop_data->
empty()) {
4069 auto tmp = *current_loop_data;
4070 (*current_loop_data)[
"parent"] = std::move(
tmp);
4074 (*current_loop_data)[
"is_first"] =
true;
4075 (*current_loop_data)[
"is_last"] = (
result->size() <= 1);
4076 for (
auto it =
result->begin(); it !=
result->end(); ++it) {
4077 json_additional_data[
static_cast<std::string
>(node.
value)] = *it;
4079 (*current_loop_data)[
"index"] =
index;
4080 (*current_loop_data)[
"index1"] =
index + 1;
4082 (*current_loop_data)[
"is_first"] =
false;
4085 (*current_loop_data)[
"is_last"] =
true;
4092 json_additional_data[
static_cast<std::string
>(node.
value)].clear();
4093 if (!(*current_loop_data)[
"parent"].
empty()) {
4094 const auto tmp = (*current_loop_data)[
"parent"];
4095 *current_loop_data = std::move(
tmp);
4097 current_loop_data = &json_additional_data[
"loop"];
4103 if (!
result->is_object()) {
4104 throw_renderer_error(
"object must be an object", node);
4107 if (!current_loop_data->
empty()) {
4108 (*current_loop_data)[
"parent"] = std::move(*current_loop_data);
4112 (*current_loop_data)[
"is_first"] =
true;
4113 (*current_loop_data)[
"is_last"] = (
result->size() <= 1);
4114 for (
auto it =
result->begin(); it !=
result->end(); ++it) {
4115 json_additional_data[
static_cast<std::string
>(node.
key)] = it.key();
4116 json_additional_data[
static_cast<std::string
>(node.
value)] = it.
value();
4118 (*current_loop_data)[
"index"] =
index;
4119 (*current_loop_data)[
"index1"] =
index + 1;
4121 (*current_loop_data)[
"is_first"] =
false;
4124 (*current_loop_data)[
"is_last"] =
true;
4131 json_additional_data[
static_cast<std::string
>(node.
key)].clear();
4132 json_additional_data[
static_cast<std::string
>(node.
value)].clear();
4133 if (!(*current_loop_data)[
"parent"].
empty()) {
4134 *current_loop_data = std::move((*current_loop_data)[
"parent"]);
4136 current_loop_data = &json_additional_data[
"loop"];
4142 if (truthy(
result.get())) {
4150 auto sub_renderer =
Renderer(config, template_storage, function_storage);
4151 const auto included_template_it = template_storage.find(node.
file);
4152 if (included_template_it != template_storage.end()) {
4153 sub_renderer.render_to(*output_stream, included_template_it->second, *json_input, &json_additional_data);
4154 }
else if (config.throw_at_missing_includes) {
4155 throw_renderer_error(
"include '" + node.
file +
"' not found", node);
4160 const auto included_template_it = template_storage.find(node.
file);
4161 if (included_template_it != template_storage.end()) {
4162 const Template *parent_template = &included_template_it->second;
4163 render_to(*output_stream, *parent_template, *json_input, &json_additional_data);
4164 break_rendering =
true;
4165 }
else if (config.throw_at_missing_includes) {
4166 throw_renderer_error(
"extends '" + node.
file +
"' not found", node);
4173 current_template = template_stack.front();
4176 block_statement_stack.emplace_back(&node);
4177 block_it->second->block.accept(*
this);
4178 block_statement_stack.pop_back();
4181 current_template = template_stack.back();
4185 std::string ptr = node.
key;
4193 : config(config), template_storage(template_storage), function_storage(function_storage) { }
4196 output_stream = &os;
4197 current_template = &tmpl;
4200 json_additional_data = *loop_data;
4201 current_loop_data = &json_additional_data[
"loop"];
4207 json_tmp_stack.clear();
4213 #endif // INCLUDE_INJA_RENDERER_HPP_
4243 explicit Environment(
const std::string &global_path) : input_path(global_path), output_path(global_path) {}
4245 Environment(
const std::string &input_path,
const std::string &output_path)
4246 : input_path(input_path), output_path(output_path) {}
4250 lexer_config.statement_open =
open;
4251 lexer_config.statement_open_no_lstrip =
open +
"+";
4252 lexer_config.statement_open_force_lstrip =
open +
"-";
4253 lexer_config.statement_close = close;
4254 lexer_config.statement_close_force_rstrip =
"-" + close;
4255 lexer_config.update_open_chars();
4260 lexer_config.line_statement =
open;
4261 lexer_config.update_open_chars();
4266 lexer_config.expression_open =
open;
4267 lexer_config.expression_open_force_lstrip =
open +
"-";
4268 lexer_config.expression_close = close;
4269 lexer_config.expression_close_force_rstrip =
"-" + close;
4270 lexer_config.update_open_chars();
4275 lexer_config.comment_open =
open;
4276 lexer_config.comment_open_force_lstrip =
open +
"-";
4277 lexer_config.comment_close = close;
4278 lexer_config.comment_close_force_rstrip =
"-" + close;
4279 lexer_config.update_open_chars();
4284 lexer_config.trim_blocks = trim_blocks;
4289 lexer_config.lstrip_blocks = lstrip_blocks;
4294 parser_config.search_included_templates_in_files = search_in_files;
4299 render_config.throw_at_missing_includes = will_throw;
4303 Parser parser(parser_config, lexer_config, template_storage, function_storage);
4304 return parser.parse(input);
4308 Parser parser(parser_config, lexer_config, template_storage, function_storage);
4321 std::stringstream os;
4331 const json data = load_json(filename_data);
4336 std::ofstream
file(output_path + filename_out);
4342 std::ofstream
file(output_path + filename_out);
4348 const std::string &filename_out) {
4349 const json data = load_json(filename_data);
4354 const json data = load_json(filename_data);
4364 Parser parser(parser_config, lexer_config, template_storage, function_storage);
4380 add_callback(
name, -1, callback);
4387 add_void_callback(
name, -1, callback);
4409 template_storage[
name] = tmpl;
4430 #endif // INCLUDE_INJA_ENVIRONMENT_HPP_
4443 #endif // INCLUDE_INJA_INJA_HPP_