├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── example.cpp ├── external ├── inja │ └── inja.hpp └── json │ └── nlohmann │ └── json.hpp ├── include ├── cuda_jit.h └── map.h └── src └── cuda_jit.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .idea/ 3 | 4 | cmake-build-debug/ 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(cuda_jit) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | find_package(CUDA REQUIRED) 7 | find_library(CUDA_NVRTC_LIB libnvrtc nvrtc HINTS "${CUDA_TOOLKIT_ROOT_DIR}/lib64" "${LIBNVRTC_LIBRARY_DIR}" "${CUDA_TOOLKIT_ROOT_DIR}/lib/x64" /usr/lib64 /usr/local/cuda/lib64) 8 | 9 | add_library(cuda_jit) 10 | target_sources(cuda_jit PUBLIC include/cuda_jit.h include/map.h PRIVATE src/cuda_jit.cpp) 11 | target_include_directories(cuda_jit PUBLIC ${CUDA_INCLUDE_DIRS} include external/json PRIVATE external/inja) 12 | target_link_libraries(cuda_jit PUBLIC ${CUDA_LIBRARIES} PRIVATE ${CUDA_NVRTC_LIB} ${CUDA_CUDA_LIBRARY}) 13 | 14 | add_executable(example) 15 | target_sources(example PRIVATE example.cpp) 16 | target_link_libraries(example cuda_jit) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Evtushenko Georgy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by egi on 1/18/20. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | #include "map.h" 9 | 10 | int main () 11 | { 12 | jit (saxpy, 13 | { 14 | const size_t tid = threadIdx.x; 15 | 16 | if (tid < {{ N }}) 17 | { 18 | // Test comment 19 | out[tid] = a * x[tid] + y[tid]; 20 | } 21 | }, (int, a), (const int *, x), (const int *, y), (int *, out)); 22 | 23 | size_t n = 1024; 24 | 25 | nlohmann::json data; 26 | data["N"] = n; 27 | 28 | int a = 2; 29 | int *x {}; 30 | int *y {}; 31 | int *out {}; 32 | 33 | cudaMalloc (&x, n * sizeof (int)); 34 | cudaMalloc (&y, n * sizeof (int)); 35 | cudaMalloc (&out, n * sizeof (int)); 36 | 37 | std::unique_ptr h_x (new int[n]); 38 | std::unique_ptr h_y (new int[n]); 39 | std::unique_ptr h_out (new int[n]); 40 | 41 | for (size_t i = 0; i < n; i++) 42 | { 43 | h_x[i] = 1; 44 | h_y[i] = 2; 45 | } 46 | 47 | cudaMemcpy (x, h_x.get (), n * sizeof (float), cudaMemcpyHostToDevice); 48 | cudaMemcpy (y, h_y.get (), n * sizeof (float), cudaMemcpyHostToDevice); 49 | 50 | auto saxpy_kernel = saxpy.compile (data); 51 | saxpy_kernel.launch (1, 1024, a, x, y, out); 52 | 53 | cudaMemcpy (h_out.get (), out, n * sizeof (float), cudaMemcpyDeviceToHost); 54 | 55 | for (size_t i = 0; i < n; i++) 56 | { 57 | int target_value = a * h_x[i] + h_y[i]; 58 | if (target_value != h_out[i]) 59 | std::cerr << "Error in out[" << i << "] = " << h_out[i] << " != " << target_value << "\n"; 60 | } 61 | 62 | cudaFree (x); 63 | cudaFree (y); 64 | cudaFree (out); 65 | } -------------------------------------------------------------------------------- /external/inja/inja.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Pantor. All rights reserved. 2 | 3 | #ifndef INCLUDE_INJA_INJA_HPP_ 4 | #define INCLUDE_INJA_INJA_HPP_ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | // #include "environment.hpp" 17 | // Copyright (c) 2019 Pantor. All rights reserved. 18 | 19 | #ifndef INCLUDE_INJA_ENVIRONMENT_HPP_ 20 | #define INCLUDE_INJA_ENVIRONMENT_HPP_ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | // #include "config.hpp" 30 | // Copyright (c) 2019 Pantor. All rights reserved. 31 | 32 | #ifndef INCLUDE_INJA_CONFIG_HPP_ 33 | #define INCLUDE_INJA_CONFIG_HPP_ 34 | 35 | #include 36 | #include 37 | 38 | // #include "string_view.hpp" 39 | // Copyright 2017-2019 by Martin Moene 40 | // 41 | // string-view lite, a C++17-like string_view for C++98 and later. 42 | // For more information see https://github.com/martinmoene/string-view-lite 43 | // 44 | // Distributed under the Boost Software License, Version 1.0. 45 | // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 46 | 47 | 48 | 49 | #ifndef NONSTD_SV_LITE_H_INCLUDED 50 | #define NONSTD_SV_LITE_H_INCLUDED 51 | 52 | #define string_view_lite_MAJOR 1 53 | #define string_view_lite_MINOR 4 54 | #define string_view_lite_PATCH 0 55 | 56 | #define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH) 57 | 58 | #define nssv_STRINGIFY( x ) nssv_STRINGIFY_( x ) 59 | #define nssv_STRINGIFY_( x ) #x 60 | 61 | // string-view lite configuration: 62 | 63 | #define nssv_STRING_VIEW_DEFAULT 0 64 | #define nssv_STRING_VIEW_NONSTD 1 65 | #define nssv_STRING_VIEW_STD 2 66 | 67 | #if !defined( nssv_CONFIG_SELECT_STRING_VIEW ) 68 | # define nssv_CONFIG_SELECT_STRING_VIEW ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD ) 69 | #endif 70 | 71 | #if defined( nssv_CONFIG_SELECT_STD_STRING_VIEW ) || defined( nssv_CONFIG_SELECT_NONSTD_STRING_VIEW ) 72 | # 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_... 73 | #endif 74 | 75 | #ifndef nssv_CONFIG_STD_SV_OPERATOR 76 | # define nssv_CONFIG_STD_SV_OPERATOR 0 77 | #endif 78 | 79 | #ifndef nssv_CONFIG_USR_SV_OPERATOR 80 | # define nssv_CONFIG_USR_SV_OPERATOR 1 81 | #endif 82 | 83 | #ifdef nssv_CONFIG_CONVERSION_STD_STRING 84 | # define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING 85 | # define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING 86 | #endif 87 | 88 | #ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 89 | # define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1 90 | #endif 91 | 92 | #ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 93 | # define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1 94 | #endif 95 | 96 | // Control presence of exception handling (try and auto discover): 97 | 98 | #ifndef nssv_CONFIG_NO_EXCEPTIONS 99 | # if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) 100 | # define nssv_CONFIG_NO_EXCEPTIONS 0 101 | # else 102 | # define nssv_CONFIG_NO_EXCEPTIONS 1 103 | # endif 104 | #endif 105 | 106 | // C++ language version detection (C++20 is speculative): 107 | // Note: VC14.0/1900 (VS2015) lacks too much from C++14. 108 | 109 | #ifndef nssv_CPLUSPLUS 110 | # if defined(_MSVC_LANG ) && !defined(__clang__) 111 | # define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) 112 | # else 113 | # define nssv_CPLUSPLUS __cplusplus 114 | # endif 115 | #endif 116 | 117 | #define nssv_CPP98_OR_GREATER ( nssv_CPLUSPLUS >= 199711L ) 118 | #define nssv_CPP11_OR_GREATER ( nssv_CPLUSPLUS >= 201103L ) 119 | #define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L ) 120 | #define nssv_CPP14_OR_GREATER ( nssv_CPLUSPLUS >= 201402L ) 121 | #define nssv_CPP17_OR_GREATER ( nssv_CPLUSPLUS >= 201703L ) 122 | #define nssv_CPP20_OR_GREATER ( nssv_CPLUSPLUS >= 202000L ) 123 | 124 | // use C++17 std::string_view if available and requested: 125 | 126 | #if nssv_CPP17_OR_GREATER && defined(__has_include ) 127 | # if __has_include( ) 128 | # define nssv_HAVE_STD_STRING_VIEW 1 129 | # else 130 | # define nssv_HAVE_STD_STRING_VIEW 0 131 | # endif 132 | #else 133 | # define nssv_HAVE_STD_STRING_VIEW 0 134 | #endif 135 | 136 | #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) ) 137 | 138 | #define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW ) 139 | #define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH 140 | 141 | // 142 | // Use C++17 std::string_view: 143 | // 144 | 145 | #if nssv_USES_STD_STRING_VIEW 146 | 147 | #include 148 | 149 | // Extensions for std::string: 150 | 151 | #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 152 | 153 | namespace nonstd { 154 | 155 | template< class CharT, class Traits, class Allocator = std::allocator > 156 | std::basic_string 157 | to_string( std::basic_string_view v, Allocator const & a = Allocator() ) 158 | { 159 | return std::basic_string( v.begin(), v.end(), a ); 160 | } 161 | 162 | template< class CharT, class Traits, class Allocator > 163 | std::basic_string_view 164 | to_string_view( std::basic_string const & s ) 165 | { 166 | return std::basic_string_view( s.data(), s.size() ); 167 | } 168 | 169 | // Literal operators sv and _sv: 170 | 171 | #if nssv_CONFIG_STD_SV_OPERATOR 172 | 173 | using namespace std::literals::string_view_literals; 174 | 175 | #endif 176 | 177 | #if nssv_CONFIG_USR_SV_OPERATOR 178 | 179 | inline namespace literals { 180 | inline namespace string_view_literals { 181 | 182 | 183 | constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept // (1) 184 | { 185 | return std::string_view{ str, len }; 186 | } 187 | 188 | constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept // (2) 189 | { 190 | return std::u16string_view{ str, len }; 191 | } 192 | 193 | constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept // (3) 194 | { 195 | return std::u32string_view{ str, len }; 196 | } 197 | 198 | constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept // (4) 199 | { 200 | return std::wstring_view{ str, len }; 201 | } 202 | 203 | }} // namespace literals::string_view_literals 204 | 205 | #endif // nssv_CONFIG_USR_SV_OPERATOR 206 | 207 | } // namespace nonstd 208 | 209 | #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 210 | 211 | namespace nonstd { 212 | 213 | using std::string_view; 214 | using std::wstring_view; 215 | using std::u16string_view; 216 | using std::u32string_view; 217 | using std::basic_string_view; 218 | 219 | // literal "sv" and "_sv", see above 220 | 221 | using std::operator==; 222 | using std::operator!=; 223 | using std::operator<; 224 | using std::operator<=; 225 | using std::operator>; 226 | using std::operator>=; 227 | 228 | using std::operator<<; 229 | 230 | } // namespace nonstd 231 | 232 | #else // nssv_HAVE_STD_STRING_VIEW 233 | 234 | // 235 | // Before C++17: use string_view lite: 236 | // 237 | 238 | // Compiler versions: 239 | // 240 | // MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0) 241 | // MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002) 242 | // MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003) 243 | // MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) 244 | // MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) 245 | // MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) 246 | // MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) 247 | // MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) 248 | // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) 249 | // MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017) 250 | 251 | #if defined(_MSC_VER ) && !defined(__clang__) 252 | # define nssv_COMPILER_MSVC_VER (_MSC_VER ) 253 | # define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) 254 | #else 255 | # define nssv_COMPILER_MSVC_VER 0 256 | # define nssv_COMPILER_MSVC_VERSION 0 257 | #endif 258 | 259 | #define nssv_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) 260 | 261 | #if defined(__clang__) 262 | # define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) 263 | #else 264 | # define nssv_COMPILER_CLANG_VERSION 0 265 | #endif 266 | 267 | #if defined(__GNUC__) && !defined(__clang__) 268 | # define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) 269 | #else 270 | # define nssv_COMPILER_GNUC_VERSION 0 271 | #endif 272 | 273 | // half-open range [lo..hi): 274 | #define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) 275 | 276 | // Presence of language and library features: 277 | 278 | #ifdef _HAS_CPP0X 279 | # define nssv_HAS_CPP0X _HAS_CPP0X 280 | #else 281 | # define nssv_HAS_CPP0X 0 282 | #endif 283 | 284 | // Unless defined otherwise below, consider VC14 as C++11 for variant-lite: 285 | 286 | #if nssv_COMPILER_MSVC_VER >= 1900 287 | # undef nssv_CPP11_OR_GREATER 288 | # define nssv_CPP11_OR_GREATER 1 289 | #endif 290 | 291 | #define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500) 292 | #define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600) 293 | #define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700) 294 | #define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800) 295 | #define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900) 296 | #define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910) 297 | 298 | #define nssv_CPP14_000 (nssv_CPP14_OR_GREATER) 299 | #define nssv_CPP17_000 (nssv_CPP17_OR_GREATER) 300 | 301 | // Presence of C++11 language features: 302 | 303 | #define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140 304 | #define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140 305 | #define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140 306 | #define nssv_HAVE_NOEXCEPT nssv_CPP11_140 307 | #define nssv_HAVE_NULLPTR nssv_CPP11_100 308 | #define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140 309 | #define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140 310 | #define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140 311 | #define nssv_HAVE_WCHAR16_T nssv_CPP11_100 312 | #define nssv_HAVE_WCHAR32_T nssv_CPP11_100 313 | 314 | #if ! ( ( nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) ) 315 | # define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140 316 | #else 317 | # define nssv_HAVE_STD_DEFINED_LITERALS 0 318 | #endif 319 | 320 | // Presence of C++14 language features: 321 | 322 | #define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000 323 | 324 | // Presence of C++17 language features: 325 | 326 | #define nssv_HAVE_NODISCARD nssv_CPP17_000 327 | 328 | // Presence of C++ library features: 329 | 330 | #define nssv_HAVE_STD_HASH nssv_CPP11_120 331 | 332 | // C++ feature usage: 333 | 334 | #if nssv_HAVE_CONSTEXPR_11 335 | # define nssv_constexpr constexpr 336 | #else 337 | # define nssv_constexpr /*constexpr*/ 338 | #endif 339 | 340 | #if nssv_HAVE_CONSTEXPR_14 341 | # define nssv_constexpr14 constexpr 342 | #else 343 | # define nssv_constexpr14 /*constexpr*/ 344 | #endif 345 | 346 | #if nssv_HAVE_EXPLICIT_CONVERSION 347 | # define nssv_explicit explicit 348 | #else 349 | # define nssv_explicit /*explicit*/ 350 | #endif 351 | 352 | #if nssv_HAVE_INLINE_NAMESPACE 353 | # define nssv_inline_ns inline 354 | #else 355 | # define nssv_inline_ns /*inline*/ 356 | #endif 357 | 358 | #if nssv_HAVE_NOEXCEPT 359 | # define nssv_noexcept noexcept 360 | #else 361 | # define nssv_noexcept /*noexcept*/ 362 | #endif 363 | 364 | //#if nssv_HAVE_REF_QUALIFIER 365 | //# define nssv_ref_qual & 366 | //# define nssv_refref_qual && 367 | //#else 368 | //# define nssv_ref_qual /*&*/ 369 | //# define nssv_refref_qual /*&&*/ 370 | //#endif 371 | 372 | #if nssv_HAVE_NULLPTR 373 | # define nssv_nullptr nullptr 374 | #else 375 | # define nssv_nullptr NULL 376 | #endif 377 | 378 | #if nssv_HAVE_NODISCARD 379 | # define nssv_nodiscard [[nodiscard]] 380 | #else 381 | # define nssv_nodiscard /*[[nodiscard]]*/ 382 | #endif 383 | 384 | // Additional includes: 385 | 386 | #include 387 | #include 388 | #include 389 | #include 390 | #include 391 | #include // std::char_traits<> 392 | 393 | #if ! nssv_CONFIG_NO_EXCEPTIONS 394 | # include 395 | #endif 396 | 397 | #if nssv_CPP11_OR_GREATER 398 | # include 399 | #endif 400 | 401 | // Clang, GNUC, MSVC warning suppression macros: 402 | 403 | #if defined(__clang__) 404 | # pragma clang diagnostic ignored "-Wreserved-user-defined-literal" 405 | # pragma clang diagnostic push 406 | # pragma clang diagnostic ignored "-Wuser-defined-literals" 407 | #elif defined(__GNUC__) 408 | # pragma GCC diagnostic push 409 | # pragma GCC diagnostic ignored "-Wliteral-suffix" 410 | #endif // __clang__ 411 | 412 | #if nssv_COMPILER_MSVC_VERSION >= 140 413 | # define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]] 414 | # define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) ) 415 | # define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) 416 | #else 417 | # define nssv_SUPPRESS_MSGSL_WARNING(expr) 418 | # define nssv_SUPPRESS_MSVC_WARNING(code, descr) 419 | # define nssv_DISABLE_MSVC_WARNINGS(codes) 420 | #endif 421 | 422 | #if defined(__clang__) 423 | # define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop") 424 | #elif defined(__GNUC__) 425 | # define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop") 426 | #elif nssv_COMPILER_MSVC_VERSION >= 140 427 | # define nssv_RESTORE_WARNINGS() __pragma(warning(pop )) 428 | #else 429 | # define nssv_RESTORE_WARNINGS() 430 | #endif 431 | 432 | // Suppress the following MSVC (GSL) warnings: 433 | // - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not 434 | // start with an underscore are reserved 435 | // - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; 436 | // use brace initialization, gsl::narrow_cast or gsl::narow 437 | // - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead 438 | 439 | nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 ) 440 | //nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" ) 441 | //nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix ) 442 | 443 | namespace nonstd { namespace sv_lite { 444 | 445 | #if nssv_CPP11_OR_GREATER 446 | 447 | namespace detail { 448 | 449 | // Expect tail call optimization to make length() non-recursive: 450 | 451 | template< typename CharT > 452 | inline constexpr std::size_t length( CharT * s, std::size_t result = 0 ) 453 | { 454 | return *s == '\0' ? result : length( s + 1, result + 1 ); 455 | } 456 | 457 | } // namespace detail 458 | 459 | #endif // nssv_CPP11_OR_GREATER 460 | 461 | template 462 | < 463 | class CharT, 464 | class Traits = std::char_traits 465 | > 466 | class basic_string_view; 467 | 468 | // 469 | // basic_string_view: 470 | // 471 | 472 | template 473 | < 474 | class CharT, 475 | class Traits /* = std::char_traits */ 476 | > 477 | class basic_string_view 478 | { 479 | public: 480 | // Member types: 481 | 482 | typedef Traits traits_type; 483 | typedef CharT value_type; 484 | 485 | typedef CharT * pointer; 486 | typedef CharT const * const_pointer; 487 | typedef CharT & reference; 488 | typedef CharT const & const_reference; 489 | 490 | typedef const_pointer iterator; 491 | typedef const_pointer const_iterator; 492 | typedef std::reverse_iterator< const_iterator > reverse_iterator; 493 | typedef std::reverse_iterator< const_iterator > const_reverse_iterator; 494 | 495 | typedef std::size_t size_type; 496 | typedef std::ptrdiff_t difference_type; 497 | 498 | // 24.4.2.1 Construction and assignment: 499 | 500 | nssv_constexpr basic_string_view() nssv_noexcept 501 | : data_( nssv_nullptr ) 502 | , size_( 0 ) 503 | {} 504 | 505 | #if nssv_CPP11_OR_GREATER 506 | nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default; 507 | #else 508 | nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept 509 | : data_( other.data_) 510 | , size_( other.size_) 511 | {} 512 | #endif 513 | 514 | nssv_constexpr basic_string_view( CharT const * s, size_type count ) nssv_noexcept // non-standard noexcept 515 | : data_( s ) 516 | , size_( count ) 517 | {} 518 | 519 | nssv_constexpr basic_string_view( CharT const * s) nssv_noexcept // non-standard noexcept 520 | : data_( s ) 521 | #if nssv_CPP17_OR_GREATER 522 | , size_( Traits::length(s) ) 523 | #elif nssv_CPP11_OR_GREATER 524 | , size_( detail::length(s) ) 525 | #else 526 | , size_( Traits::length(s) ) 527 | #endif 528 | {} 529 | 530 | // Assignment: 531 | 532 | #if nssv_CPP11_OR_GREATER 533 | nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default; 534 | #else 535 | nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept 536 | { 537 | data_ = other.data_; 538 | size_ = other.size_; 539 | return *this; 540 | } 541 | #endif 542 | 543 | // 24.4.2.2 Iterator support: 544 | 545 | nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; } 546 | nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; } 547 | 548 | nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); } 549 | nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); } 550 | 551 | nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator( end() ); } 552 | nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator( begin() ); } 553 | 554 | nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); } 555 | nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); } 556 | 557 | // 24.4.2.3 Capacity: 558 | 559 | nssv_constexpr size_type size() const nssv_noexcept { return size_; } 560 | nssv_constexpr size_type length() const nssv_noexcept { return size_; } 561 | nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); } 562 | 563 | // since C++20 564 | nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept 565 | { 566 | return 0 == size_; 567 | } 568 | 569 | // 24.4.2.4 Element access: 570 | 571 | nssv_constexpr const_reference operator[]( size_type pos ) const 572 | { 573 | return data_at( pos ); 574 | } 575 | 576 | nssv_constexpr14 const_reference at( size_type pos ) const 577 | { 578 | #if nssv_CONFIG_NO_EXCEPTIONS 579 | assert( pos < size() ); 580 | #else 581 | if ( pos >= size() ) 582 | { 583 | throw std::out_of_range("nonstd::string_view::at()"); 584 | } 585 | #endif 586 | return data_at( pos ); 587 | } 588 | 589 | nssv_constexpr const_reference front() const { return data_at( 0 ); } 590 | nssv_constexpr const_reference back() const { return data_at( size() - 1 ); } 591 | 592 | nssv_constexpr const_pointer data() const nssv_noexcept { return data_; } 593 | 594 | // 24.4.2.5 Modifiers: 595 | 596 | nssv_constexpr14 void remove_prefix( size_type n ) 597 | { 598 | assert( n <= size() ); 599 | data_ += n; 600 | size_ -= n; 601 | } 602 | 603 | nssv_constexpr14 void remove_suffix( size_type n ) 604 | { 605 | assert( n <= size() ); 606 | size_ -= n; 607 | } 608 | 609 | nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept 610 | { 611 | using std::swap; 612 | swap( data_, other.data_ ); 613 | swap( size_, other.size_ ); 614 | } 615 | 616 | // 24.4.2.6 String operations: 617 | 618 | size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const 619 | { 620 | #if nssv_CONFIG_NO_EXCEPTIONS 621 | assert( pos <= size() ); 622 | #else 623 | if ( pos > size() ) 624 | { 625 | throw std::out_of_range("nonstd::string_view::copy()"); 626 | } 627 | #endif 628 | const size_type rlen = (std::min)( n, size() - pos ); 629 | 630 | (void) Traits::copy( dest, data() + pos, rlen ); 631 | 632 | return rlen; 633 | } 634 | 635 | nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const 636 | { 637 | #if nssv_CONFIG_NO_EXCEPTIONS 638 | assert( pos <= size() ); 639 | #else 640 | if ( pos > size() ) 641 | { 642 | throw std::out_of_range("nonstd::string_view::substr()"); 643 | } 644 | #endif 645 | return basic_string_view( data() + pos, (std::min)( n, size() - pos ) ); 646 | } 647 | 648 | // compare(), 6x: 649 | 650 | nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1) 651 | { 652 | if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) 653 | { 654 | return result; 655 | } 656 | 657 | return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; 658 | } 659 | 660 | nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2) 661 | { 662 | return substr( pos1, n1 ).compare( other ); 663 | } 664 | 665 | nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3) 666 | { 667 | return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) ); 668 | } 669 | 670 | nssv_constexpr int compare( CharT const * s ) const // (4) 671 | { 672 | return compare( basic_string_view( s ) ); 673 | } 674 | 675 | nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5) 676 | { 677 | return substr( pos1, n1 ).compare( basic_string_view( s ) ); 678 | } 679 | 680 | nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6) 681 | { 682 | return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) ); 683 | } 684 | 685 | // 24.4.2.7 Searching: 686 | 687 | // starts_with(), 3x, since C++20: 688 | 689 | nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept // (1) 690 | { 691 | return size() >= v.size() && compare( 0, v.size(), v ) == 0; 692 | } 693 | 694 | nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept // (2) 695 | { 696 | return starts_with( basic_string_view( &c, 1 ) ); 697 | } 698 | 699 | nssv_constexpr bool starts_with( CharT const * s ) const // (3) 700 | { 701 | return starts_with( basic_string_view( s ) ); 702 | } 703 | 704 | // ends_with(), 3x, since C++20: 705 | 706 | nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept // (1) 707 | { 708 | return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0; 709 | } 710 | 711 | nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept // (2) 712 | { 713 | return ends_with( basic_string_view( &c, 1 ) ); 714 | } 715 | 716 | nssv_constexpr bool ends_with( CharT const * s ) const // (3) 717 | { 718 | return ends_with( basic_string_view( s ) ); 719 | } 720 | 721 | // find(), 4x: 722 | 723 | nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) 724 | { 725 | return assert( v.size() == 0 || v.data() != nssv_nullptr ) 726 | , pos >= size() 727 | ? npos 728 | : to_pos( std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) ); 729 | } 730 | 731 | nssv_constexpr14 size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) 732 | { 733 | return find( basic_string_view( &c, 1 ), pos ); 734 | } 735 | 736 | nssv_constexpr14 size_type find( CharT const * s, size_type pos, size_type n ) const // (3) 737 | { 738 | return find( basic_string_view( s, n ), pos ); 739 | } 740 | 741 | nssv_constexpr14 size_type find( CharT const * s, size_type pos = 0 ) const // (4) 742 | { 743 | return find( basic_string_view( s ), pos ); 744 | } 745 | 746 | // rfind(), 4x: 747 | 748 | nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) 749 | { 750 | if ( size() < v.size() ) 751 | { 752 | return npos; 753 | } 754 | 755 | if ( v.empty() ) 756 | { 757 | return (std::min)( size(), pos ); 758 | } 759 | 760 | const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size(); 761 | const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq ); 762 | 763 | return result != last ? size_type( result - cbegin() ) : npos; 764 | } 765 | 766 | nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept // (2) 767 | { 768 | return rfind( basic_string_view( &c, 1 ), pos ); 769 | } 770 | 771 | nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const // (3) 772 | { 773 | return rfind( basic_string_view( s, n ), pos ); 774 | } 775 | 776 | nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const // (4) 777 | { 778 | return rfind( basic_string_view( s ), pos ); 779 | } 780 | 781 | // find_first_of(), 4x: 782 | 783 | nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) 784 | { 785 | return pos >= size() 786 | ? npos 787 | : to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) ); 788 | } 789 | 790 | nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) 791 | { 792 | return find_first_of( basic_string_view( &c, 1 ), pos ); 793 | } 794 | 795 | nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const // (3) 796 | { 797 | return find_first_of( basic_string_view( s, n ), pos ); 798 | } 799 | 800 | nssv_constexpr size_type find_first_of( CharT const * s, size_type pos = 0 ) const // (4) 801 | { 802 | return find_first_of( basic_string_view( s ), pos ); 803 | } 804 | 805 | // find_last_of(), 4x: 806 | 807 | nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) 808 | { 809 | return empty() 810 | ? npos 811 | : pos >= size() 812 | ? find_last_of( v, size() - 1 ) 813 | : to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) ); 814 | } 815 | 816 | nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) 817 | { 818 | return find_last_of( basic_string_view( &c, 1 ), pos ); 819 | } 820 | 821 | nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const // (3) 822 | { 823 | return find_last_of( basic_string_view( s, count ), pos ); 824 | } 825 | 826 | nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const // (4) 827 | { 828 | return find_last_of( basic_string_view( s ), pos ); 829 | } 830 | 831 | // find_first_not_of(), 4x: 832 | 833 | nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) 834 | { 835 | return pos >= size() 836 | ? npos 837 | : to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) ); 838 | } 839 | 840 | nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) 841 | { 842 | return find_first_not_of( basic_string_view( &c, 1 ), pos ); 843 | } 844 | 845 | nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const // (3) 846 | { 847 | return find_first_not_of( basic_string_view( s, count ), pos ); 848 | } 849 | 850 | nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const // (4) 851 | { 852 | return find_first_not_of( basic_string_view( s ), pos ); 853 | } 854 | 855 | // find_last_not_of(), 4x: 856 | 857 | nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) 858 | { 859 | return empty() 860 | ? npos 861 | : pos >= size() 862 | ? find_last_not_of( v, size() - 1 ) 863 | : to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) ); 864 | } 865 | 866 | nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) 867 | { 868 | return find_last_not_of( basic_string_view( &c, 1 ), pos ); 869 | } 870 | 871 | nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const // (3) 872 | { 873 | return find_last_not_of( basic_string_view( s, count ), pos ); 874 | } 875 | 876 | nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const // (4) 877 | { 878 | return find_last_not_of( basic_string_view( s ), pos ); 879 | } 880 | 881 | // Constants: 882 | 883 | #if nssv_CPP17_OR_GREATER 884 | static nssv_constexpr size_type npos = size_type(-1); 885 | #elif nssv_CPP11_OR_GREATER 886 | enum : size_type { npos = size_type(-1) }; 887 | #else 888 | enum { npos = size_type(-1) }; 889 | #endif 890 | 891 | private: 892 | struct not_in_view 893 | { 894 | const basic_string_view v; 895 | 896 | nssv_constexpr explicit not_in_view( basic_string_view v ) : v( v ) {} 897 | 898 | nssv_constexpr bool operator()( CharT c ) const 899 | { 900 | return npos == v.find_first_of( c ); 901 | } 902 | }; 903 | 904 | nssv_constexpr size_type to_pos( const_iterator it ) const 905 | { 906 | return it == cend() ? npos : size_type( it - cbegin() ); 907 | } 908 | 909 | nssv_constexpr size_type to_pos( const_reverse_iterator it ) const 910 | { 911 | return it == crend() ? npos : size_type( crend() - it - 1 ); 912 | } 913 | 914 | nssv_constexpr const_reference data_at( size_type pos ) const 915 | { 916 | #if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 ) 917 | return data_[pos]; 918 | #else 919 | return assert( pos < size() ), data_[pos]; 920 | #endif 921 | } 922 | 923 | private: 924 | const_pointer data_; 925 | size_type size_; 926 | 927 | public: 928 | #if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 929 | 930 | template< class Allocator > 931 | basic_string_view( std::basic_string const & s ) nssv_noexcept 932 | : data_( s.data() ) 933 | , size_( s.size() ) 934 | {} 935 | 936 | #if nssv_HAVE_EXPLICIT_CONVERSION 937 | 938 | template< class Allocator > 939 | explicit operator std::basic_string() const 940 | { 941 | return to_string( Allocator() ); 942 | } 943 | 944 | #endif // nssv_HAVE_EXPLICIT_CONVERSION 945 | 946 | #if nssv_CPP11_OR_GREATER 947 | 948 | template< class Allocator = std::allocator > 949 | std::basic_string 950 | to_string( Allocator const & a = Allocator() ) const 951 | { 952 | return std::basic_string( begin(), end(), a ); 953 | } 954 | 955 | #else 956 | 957 | std::basic_string 958 | to_string() const 959 | { 960 | return std::basic_string( begin(), end() ); 961 | } 962 | 963 | template< class Allocator > 964 | std::basic_string 965 | to_string( Allocator const & a ) const 966 | { 967 | return std::basic_string( begin(), end(), a ); 968 | } 969 | 970 | #endif // nssv_CPP11_OR_GREATER 971 | 972 | #endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 973 | }; 974 | 975 | // 976 | // Non-member functions: 977 | // 978 | 979 | // 24.4.3 Non-member comparison functions: 980 | // lexicographically compare two string views (function template): 981 | 982 | template< class CharT, class Traits > 983 | nssv_constexpr bool operator== ( 984 | basic_string_view lhs, 985 | basic_string_view rhs ) nssv_noexcept 986 | { return lhs.compare( rhs ) == 0 ; } 987 | 988 | template< class CharT, class Traits > 989 | nssv_constexpr bool operator!= ( 990 | basic_string_view lhs, 991 | basic_string_view rhs ) nssv_noexcept 992 | { return lhs.compare( rhs ) != 0 ; } 993 | 994 | template< class CharT, class Traits > 995 | nssv_constexpr bool operator< ( 996 | basic_string_view lhs, 997 | basic_string_view rhs ) nssv_noexcept 998 | { return lhs.compare( rhs ) < 0 ; } 999 | 1000 | template< class CharT, class Traits > 1001 | nssv_constexpr bool operator<= ( 1002 | basic_string_view lhs, 1003 | basic_string_view rhs ) nssv_noexcept 1004 | { return lhs.compare( rhs ) <= 0 ; } 1005 | 1006 | template< class CharT, class Traits > 1007 | nssv_constexpr bool operator> ( 1008 | basic_string_view lhs, 1009 | basic_string_view rhs ) nssv_noexcept 1010 | { return lhs.compare( rhs ) > 0 ; } 1011 | 1012 | template< class CharT, class Traits > 1013 | nssv_constexpr bool operator>= ( 1014 | basic_string_view lhs, 1015 | basic_string_view rhs ) nssv_noexcept 1016 | { return lhs.compare( rhs ) >= 0 ; } 1017 | 1018 | // Let S be basic_string_view, and sv be an instance of S. 1019 | // Implementations shall provide sufficient additional overloads marked 1020 | // constexpr and noexcept so that an object t with an implicit conversion 1021 | // to S can be compared according to Table 67. 1022 | 1023 | #if ! nssv_CPP11_OR_GREATER || nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 ) 1024 | 1025 | // accomodate for older compilers: 1026 | 1027 | // == 1028 | 1029 | template< class CharT, class Traits> 1030 | nssv_constexpr bool operator==( 1031 | basic_string_view lhs, 1032 | char const * rhs ) nssv_noexcept 1033 | { return lhs.compare( rhs ) == 0; } 1034 | 1035 | template< class CharT, class Traits> 1036 | nssv_constexpr bool operator==( 1037 | char const * lhs, 1038 | basic_string_view rhs ) nssv_noexcept 1039 | { return rhs.compare( lhs ) == 0; } 1040 | 1041 | template< class CharT, class Traits> 1042 | nssv_constexpr bool operator==( 1043 | basic_string_view lhs, 1044 | std::basic_string rhs ) nssv_noexcept 1045 | { return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } 1046 | 1047 | template< class CharT, class Traits> 1048 | nssv_constexpr bool operator==( 1049 | std::basic_string rhs, 1050 | basic_string_view lhs ) nssv_noexcept 1051 | { return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } 1052 | 1053 | // != 1054 | 1055 | template< class CharT, class Traits> 1056 | nssv_constexpr bool operator!=( 1057 | basic_string_view lhs, 1058 | char const * rhs ) nssv_noexcept 1059 | { return lhs.compare( rhs ) != 0; } 1060 | 1061 | template< class CharT, class Traits> 1062 | nssv_constexpr bool operator!=( 1063 | char const * lhs, 1064 | basic_string_view rhs ) nssv_noexcept 1065 | { return rhs.compare( lhs ) != 0; } 1066 | 1067 | template< class CharT, class Traits> 1068 | nssv_constexpr bool operator!=( 1069 | basic_string_view lhs, 1070 | std::basic_string rhs ) nssv_noexcept 1071 | { return lhs.size() != rhs.size() && lhs.compare( rhs ) != 0; } 1072 | 1073 | template< class CharT, class Traits> 1074 | nssv_constexpr bool operator!=( 1075 | std::basic_string rhs, 1076 | basic_string_view lhs ) nssv_noexcept 1077 | { return lhs.size() != rhs.size() || rhs.compare( lhs ) != 0; } 1078 | 1079 | // < 1080 | 1081 | template< class CharT, class Traits> 1082 | nssv_constexpr bool operator<( 1083 | basic_string_view lhs, 1084 | char const * rhs ) nssv_noexcept 1085 | { return lhs.compare( rhs ) < 0; } 1086 | 1087 | template< class CharT, class Traits> 1088 | nssv_constexpr bool operator<( 1089 | char const * lhs, 1090 | basic_string_view rhs ) nssv_noexcept 1091 | { return rhs.compare( lhs ) > 0; } 1092 | 1093 | template< class CharT, class Traits> 1094 | nssv_constexpr bool operator<( 1095 | basic_string_view lhs, 1096 | std::basic_string rhs ) nssv_noexcept 1097 | { return lhs.compare( rhs ) < 0; } 1098 | 1099 | template< class CharT, class Traits> 1100 | nssv_constexpr bool operator<( 1101 | std::basic_string rhs, 1102 | basic_string_view lhs ) nssv_noexcept 1103 | { return rhs.compare( lhs ) > 0; } 1104 | 1105 | // <= 1106 | 1107 | template< class CharT, class Traits> 1108 | nssv_constexpr bool operator<=( 1109 | basic_string_view lhs, 1110 | char const * rhs ) nssv_noexcept 1111 | { return lhs.compare( rhs ) <= 0; } 1112 | 1113 | template< class CharT, class Traits> 1114 | nssv_constexpr bool operator<=( 1115 | char const * lhs, 1116 | basic_string_view rhs ) nssv_noexcept 1117 | { return rhs.compare( lhs ) >= 0; } 1118 | 1119 | template< class CharT, class Traits> 1120 | nssv_constexpr bool operator<=( 1121 | basic_string_view lhs, 1122 | std::basic_string rhs ) nssv_noexcept 1123 | { return lhs.compare( rhs ) <= 0; } 1124 | 1125 | template< class CharT, class Traits> 1126 | nssv_constexpr bool operator<=( 1127 | std::basic_string rhs, 1128 | basic_string_view lhs ) nssv_noexcept 1129 | { return rhs.compare( lhs ) >= 0; } 1130 | 1131 | // > 1132 | 1133 | template< class CharT, class Traits> 1134 | nssv_constexpr bool operator>( 1135 | basic_string_view lhs, 1136 | char const * rhs ) nssv_noexcept 1137 | { return lhs.compare( rhs ) > 0; } 1138 | 1139 | template< class CharT, class Traits> 1140 | nssv_constexpr bool operator>( 1141 | char const * lhs, 1142 | basic_string_view rhs ) nssv_noexcept 1143 | { return rhs.compare( lhs ) < 0; } 1144 | 1145 | template< class CharT, class Traits> 1146 | nssv_constexpr bool operator>( 1147 | basic_string_view lhs, 1148 | std::basic_string rhs ) nssv_noexcept 1149 | { return lhs.compare( rhs ) > 0; } 1150 | 1151 | template< class CharT, class Traits> 1152 | nssv_constexpr bool operator>( 1153 | std::basic_string rhs, 1154 | basic_string_view lhs ) nssv_noexcept 1155 | { return rhs.compare( lhs ) < 0; } 1156 | 1157 | // >= 1158 | 1159 | template< class CharT, class Traits> 1160 | nssv_constexpr bool operator>=( 1161 | basic_string_view lhs, 1162 | char const * rhs ) nssv_noexcept 1163 | { return lhs.compare( rhs ) >= 0; } 1164 | 1165 | template< class CharT, class Traits> 1166 | nssv_constexpr bool operator>=( 1167 | char const * lhs, 1168 | basic_string_view rhs ) nssv_noexcept 1169 | { return rhs.compare( lhs ) <= 0; } 1170 | 1171 | template< class CharT, class Traits> 1172 | nssv_constexpr bool operator>=( 1173 | basic_string_view lhs, 1174 | std::basic_string rhs ) nssv_noexcept 1175 | { return lhs.compare( rhs ) >= 0; } 1176 | 1177 | template< class CharT, class Traits> 1178 | nssv_constexpr bool operator>=( 1179 | std::basic_string rhs, 1180 | basic_string_view lhs ) nssv_noexcept 1181 | { return rhs.compare( lhs ) <= 0; } 1182 | 1183 | #else // newer compilers: 1184 | 1185 | #define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view >::type 1186 | 1187 | #if nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 140, 150 ) 1188 | # define nssv_MSVC_ORDER(x) , int=x 1189 | #else 1190 | # define nssv_MSVC_ORDER(x) /*, int=x*/ 1191 | #endif 1192 | 1193 | // == 1194 | 1195 | template< class CharT, class Traits nssv_MSVC_ORDER(1) > 1196 | nssv_constexpr bool operator==( 1197 | basic_string_view lhs, 1198 | nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs ) nssv_noexcept 1199 | { return lhs.compare( rhs ) == 0; } 1200 | 1201 | template< class CharT, class Traits nssv_MSVC_ORDER(2) > 1202 | nssv_constexpr bool operator==( 1203 | nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, 1204 | basic_string_view rhs ) nssv_noexcept 1205 | { return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } 1206 | 1207 | // != 1208 | 1209 | template< class CharT, class Traits nssv_MSVC_ORDER(1) > 1210 | nssv_constexpr bool operator!= ( 1211 | basic_string_view < CharT, Traits > lhs, 1212 | nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept 1213 | { return lhs.size() != rhs.size() || lhs.compare( rhs ) != 0 ; } 1214 | 1215 | template< class CharT, class Traits nssv_MSVC_ORDER(2) > 1216 | nssv_constexpr bool operator!= ( 1217 | nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, 1218 | basic_string_view < CharT, Traits > rhs ) nssv_noexcept 1219 | { return lhs.compare( rhs ) != 0 ; } 1220 | 1221 | // < 1222 | 1223 | template< class CharT, class Traits nssv_MSVC_ORDER(1) > 1224 | nssv_constexpr bool operator< ( 1225 | basic_string_view < CharT, Traits > lhs, 1226 | nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept 1227 | { return lhs.compare( rhs ) < 0 ; } 1228 | 1229 | template< class CharT, class Traits nssv_MSVC_ORDER(2) > 1230 | nssv_constexpr bool operator< ( 1231 | nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, 1232 | basic_string_view < CharT, Traits > rhs ) nssv_noexcept 1233 | { return lhs.compare( rhs ) < 0 ; } 1234 | 1235 | // <= 1236 | 1237 | template< class CharT, class Traits nssv_MSVC_ORDER(1) > 1238 | nssv_constexpr bool operator<= ( 1239 | basic_string_view < CharT, Traits > lhs, 1240 | nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept 1241 | { return lhs.compare( rhs ) <= 0 ; } 1242 | 1243 | template< class CharT, class Traits nssv_MSVC_ORDER(2) > 1244 | nssv_constexpr bool operator<= ( 1245 | nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, 1246 | basic_string_view < CharT, Traits > rhs ) nssv_noexcept 1247 | { return lhs.compare( rhs ) <= 0 ; } 1248 | 1249 | // > 1250 | 1251 | template< class CharT, class Traits nssv_MSVC_ORDER(1) > 1252 | nssv_constexpr bool operator> ( 1253 | basic_string_view < CharT, Traits > lhs, 1254 | nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept 1255 | { return lhs.compare( rhs ) > 0 ; } 1256 | 1257 | template< class CharT, class Traits nssv_MSVC_ORDER(2) > 1258 | nssv_constexpr bool operator> ( 1259 | nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, 1260 | basic_string_view < CharT, Traits > rhs ) nssv_noexcept 1261 | { return lhs.compare( rhs ) > 0 ; } 1262 | 1263 | // >= 1264 | 1265 | template< class CharT, class Traits nssv_MSVC_ORDER(1) > 1266 | nssv_constexpr bool operator>= ( 1267 | basic_string_view < CharT, Traits > lhs, 1268 | nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept 1269 | { return lhs.compare( rhs ) >= 0 ; } 1270 | 1271 | template< class CharT, class Traits nssv_MSVC_ORDER(2) > 1272 | nssv_constexpr bool operator>= ( 1273 | nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, 1274 | basic_string_view < CharT, Traits > rhs ) nssv_noexcept 1275 | { return lhs.compare( rhs ) >= 0 ; } 1276 | 1277 | #undef nssv_MSVC_ORDER 1278 | #undef nssv_BASIC_STRING_VIEW_I 1279 | 1280 | #endif // compiler-dependent approach to comparisons 1281 | 1282 | // 24.4.4 Inserters and extractors: 1283 | 1284 | namespace detail { 1285 | 1286 | template< class Stream > 1287 | void write_padding( Stream & os, std::streamsize n ) 1288 | { 1289 | for ( std::streamsize i = 0; i < n; ++i ) 1290 | os.rdbuf()->sputc( os.fill() ); 1291 | } 1292 | 1293 | template< class Stream, class View > 1294 | Stream & write_to_stream( Stream & os, View const & sv ) 1295 | { 1296 | typename Stream::sentry sentry( os ); 1297 | 1298 | if ( !os ) 1299 | return os; 1300 | 1301 | const std::streamsize length = static_cast( sv.length() ); 1302 | 1303 | // Whether, and how, to pad: 1304 | const bool pad = ( length < os.width() ); 1305 | const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right; 1306 | 1307 | if ( left_pad ) 1308 | write_padding( os, os.width() - length ); 1309 | 1310 | // Write span characters: 1311 | os.rdbuf()->sputn( sv.begin(), length ); 1312 | 1313 | if ( pad && !left_pad ) 1314 | write_padding( os, os.width() - length ); 1315 | 1316 | // Reset output stream width: 1317 | os.width( 0 ); 1318 | 1319 | return os; 1320 | } 1321 | 1322 | } // namespace detail 1323 | 1324 | template< class CharT, class Traits > 1325 | std::basic_ostream & 1326 | operator<<( 1327 | std::basic_ostream& os, 1328 | basic_string_view sv ) 1329 | { 1330 | return detail::write_to_stream( os, sv ); 1331 | } 1332 | 1333 | // Several typedefs for common character types are provided: 1334 | 1335 | typedef basic_string_view string_view; 1336 | typedef basic_string_view wstring_view; 1337 | #if nssv_HAVE_WCHAR16_T 1338 | typedef basic_string_view u16string_view; 1339 | typedef basic_string_view u32string_view; 1340 | #endif 1341 | 1342 | }} // namespace nonstd::sv_lite 1343 | 1344 | // 1345 | // 24.4.6 Suffix for basic_string_view literals: 1346 | // 1347 | 1348 | #if nssv_HAVE_USER_DEFINED_LITERALS 1349 | 1350 | namespace nonstd { 1351 | nssv_inline_ns namespace literals { 1352 | nssv_inline_ns namespace string_view_literals { 1353 | 1354 | #if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS 1355 | 1356 | nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept // (1) 1357 | { 1358 | return nonstd::sv_lite::string_view{ str, len }; 1359 | } 1360 | 1361 | nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept // (2) 1362 | { 1363 | return nonstd::sv_lite::u16string_view{ str, len }; 1364 | } 1365 | 1366 | nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept // (3) 1367 | { 1368 | return nonstd::sv_lite::u32string_view{ str, len }; 1369 | } 1370 | 1371 | nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) 1372 | { 1373 | return nonstd::sv_lite::wstring_view{ str, len }; 1374 | } 1375 | 1376 | #endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS 1377 | 1378 | #if nssv_CONFIG_USR_SV_OPERATOR 1379 | 1380 | nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept // (1) 1381 | { 1382 | return nonstd::sv_lite::string_view{ str, len }; 1383 | } 1384 | 1385 | nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept // (2) 1386 | { 1387 | return nonstd::sv_lite::u16string_view{ str, len }; 1388 | } 1389 | 1390 | nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept // (3) 1391 | { 1392 | return nonstd::sv_lite::u32string_view{ str, len }; 1393 | } 1394 | 1395 | nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) 1396 | { 1397 | return nonstd::sv_lite::wstring_view{ str, len }; 1398 | } 1399 | 1400 | #endif // nssv_CONFIG_USR_SV_OPERATOR 1401 | 1402 | }}} // namespace nonstd::literals::string_view_literals 1403 | 1404 | #endif 1405 | 1406 | // 1407 | // Extensions for std::string: 1408 | // 1409 | 1410 | #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1411 | 1412 | namespace nonstd { 1413 | namespace sv_lite { 1414 | 1415 | // Exclude MSVC 14 (19.00): it yields ambiguous to_string(): 1416 | 1417 | #if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140 1418 | 1419 | template< class CharT, class Traits, class Allocator = std::allocator > 1420 | std::basic_string 1421 | to_string( basic_string_view v, Allocator const & a = Allocator() ) 1422 | { 1423 | return std::basic_string( v.begin(), v.end(), a ); 1424 | } 1425 | 1426 | #else 1427 | 1428 | template< class CharT, class Traits > 1429 | std::basic_string 1430 | to_string( basic_string_view v ) 1431 | { 1432 | return std::basic_string( v.begin(), v.end() ); 1433 | } 1434 | 1435 | template< class CharT, class Traits, class Allocator > 1436 | std::basic_string 1437 | to_string( basic_string_view v, Allocator const & a ) 1438 | { 1439 | return std::basic_string( v.begin(), v.end(), a ); 1440 | } 1441 | 1442 | #endif // nssv_CPP11_OR_GREATER 1443 | 1444 | template< class CharT, class Traits, class Allocator > 1445 | basic_string_view 1446 | to_string_view( std::basic_string const & s ) 1447 | { 1448 | return basic_string_view( s.data(), s.size() ); 1449 | } 1450 | 1451 | }} // namespace nonstd::sv_lite 1452 | 1453 | #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1454 | 1455 | // 1456 | // make types and algorithms available in namespace nonstd: 1457 | // 1458 | 1459 | namespace nonstd { 1460 | 1461 | using sv_lite::basic_string_view; 1462 | using sv_lite::string_view; 1463 | using sv_lite::wstring_view; 1464 | 1465 | #if nssv_HAVE_WCHAR16_T 1466 | using sv_lite::u16string_view; 1467 | #endif 1468 | #if nssv_HAVE_WCHAR32_T 1469 | using sv_lite::u32string_view; 1470 | #endif 1471 | 1472 | // literal "sv" 1473 | 1474 | using sv_lite::operator==; 1475 | using sv_lite::operator!=; 1476 | using sv_lite::operator<; 1477 | using sv_lite::operator<=; 1478 | using sv_lite::operator>; 1479 | using sv_lite::operator>=; 1480 | 1481 | using sv_lite::operator<<; 1482 | 1483 | #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1484 | using sv_lite::to_string; 1485 | using sv_lite::to_string_view; 1486 | #endif 1487 | 1488 | } // namespace nonstd 1489 | 1490 | // 24.4.5 Hash support (C++11): 1491 | 1492 | // Note: The hash value of a string view object is equal to the hash value of 1493 | // the corresponding string object. 1494 | 1495 | #if nssv_HAVE_STD_HASH 1496 | 1497 | #include 1498 | 1499 | namespace std { 1500 | 1501 | template<> 1502 | struct hash< nonstd::string_view > 1503 | { 1504 | public: 1505 | std::size_t operator()( nonstd::string_view v ) const nssv_noexcept 1506 | { 1507 | return std::hash()( std::string( v.data(), v.size() ) ); 1508 | } 1509 | }; 1510 | 1511 | template<> 1512 | struct hash< nonstd::wstring_view > 1513 | { 1514 | public: 1515 | std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept 1516 | { 1517 | return std::hash()( std::wstring( v.data(), v.size() ) ); 1518 | } 1519 | }; 1520 | 1521 | template<> 1522 | struct hash< nonstd::u16string_view > 1523 | { 1524 | public: 1525 | std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept 1526 | { 1527 | return std::hash()( std::u16string( v.data(), v.size() ) ); 1528 | } 1529 | }; 1530 | 1531 | template<> 1532 | struct hash< nonstd::u32string_view > 1533 | { 1534 | public: 1535 | std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept 1536 | { 1537 | return std::hash()( std::u32string( v.data(), v.size() ) ); 1538 | } 1539 | }; 1540 | 1541 | } // namespace std 1542 | 1543 | #endif // nssv_HAVE_STD_HASH 1544 | 1545 | nssv_RESTORE_WARNINGS() 1546 | 1547 | #endif // nssv_HAVE_STD_STRING_VIEW 1548 | #endif // NONSTD_SV_LITE_H_INCLUDED 1549 | 1550 | 1551 | 1552 | namespace inja { 1553 | 1554 | enum class ElementNotation { 1555 | Dot, 1556 | Pointer 1557 | }; 1558 | 1559 | /*! 1560 | * \brief Class for lexer configuration. 1561 | */ 1562 | struct LexerConfig { 1563 | std::string statement_open {"{%"}; 1564 | std::string statement_close {"%}"}; 1565 | std::string line_statement {"##"}; 1566 | std::string expression_open {"{{"}; 1567 | std::string expression_close {"}}"}; 1568 | std::string comment_open {"{#"}; 1569 | std::string comment_close {"#}"}; 1570 | std::string open_chars {"#{"}; 1571 | 1572 | bool trim_blocks {false}; 1573 | bool lstrip_blocks {false}; 1574 | 1575 | void update_open_chars() { 1576 | open_chars = ""; 1577 | if (open_chars.find(line_statement[0]) == std::string::npos) { 1578 | open_chars += line_statement[0]; 1579 | } 1580 | if (open_chars.find(statement_open[0]) == std::string::npos) { 1581 | open_chars += statement_open[0]; 1582 | } 1583 | if (open_chars.find(expression_open[0]) == std::string::npos) { 1584 | open_chars += expression_open[0]; 1585 | } 1586 | if (open_chars.find(comment_open[0]) == std::string::npos) { 1587 | open_chars += comment_open[0]; 1588 | } 1589 | } 1590 | }; 1591 | 1592 | /*! 1593 | * \brief Class for parser configuration. 1594 | */ 1595 | struct ParserConfig { 1596 | ElementNotation notation {ElementNotation::Dot}; 1597 | }; 1598 | 1599 | } // namespace inja 1600 | 1601 | #endif // INCLUDE_INJA_CONFIG_HPP_ 1602 | 1603 | // #include "function_storage.hpp" 1604 | // Copyright (c) 2019 Pantor. All rights reserved. 1605 | 1606 | #ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_ 1607 | #define INCLUDE_INJA_FUNCTION_STORAGE_HPP_ 1608 | 1609 | #include 1610 | 1611 | // #include "bytecode.hpp" 1612 | // Copyright (c) 2019 Pantor. All rights reserved. 1613 | 1614 | #ifndef INCLUDE_INJA_BYTECODE_HPP_ 1615 | #define INCLUDE_INJA_BYTECODE_HPP_ 1616 | 1617 | #include 1618 | #include 1619 | 1620 | #include 1621 | 1622 | // #include "string_view.hpp" 1623 | 1624 | 1625 | 1626 | namespace inja { 1627 | 1628 | using json = nlohmann::json; 1629 | 1630 | 1631 | struct Bytecode { 1632 | enum class Op : uint8_t { 1633 | Nop, 1634 | // print StringRef (always immediate) 1635 | PrintText, 1636 | // print value 1637 | PrintValue, 1638 | // push value onto stack (always immediate) 1639 | Push, 1640 | 1641 | // builtin functions 1642 | // result is pushed to stack 1643 | // args specify number of arguments 1644 | // all functions can take their "last" argument either immediate 1645 | // or popped off stack (e.g. if immediate, it's like the immediate was 1646 | // just pushed to the stack) 1647 | Not, 1648 | And, 1649 | Or, 1650 | In, 1651 | Equal, 1652 | Greater, 1653 | GreaterEqual, 1654 | Less, 1655 | LessEqual, 1656 | At, 1657 | Different, 1658 | DivisibleBy, 1659 | Even, 1660 | First, 1661 | Float, 1662 | Int, 1663 | Last, 1664 | Length, 1665 | Lower, 1666 | Max, 1667 | Min, 1668 | Odd, 1669 | Range, 1670 | Result, 1671 | Round, 1672 | Sort, 1673 | Upper, 1674 | Exists, 1675 | ExistsInObject, 1676 | IsBoolean, 1677 | IsNumber, 1678 | IsInteger, 1679 | IsFloat, 1680 | IsObject, 1681 | IsArray, 1682 | IsString, 1683 | Default, 1684 | 1685 | // include another template 1686 | // value is the template name 1687 | Include, 1688 | 1689 | // callback function 1690 | // str is the function name (this means it cannot be a lookup) 1691 | // args specify number of arguments 1692 | // as with builtin functions, "last" argument can be immediate 1693 | Callback, 1694 | 1695 | // unconditional jump 1696 | // args is the index of the bytecode to jump to. 1697 | Jump, 1698 | 1699 | // conditional jump 1700 | // value popped off stack is checked for truthyness 1701 | // if false, args is the index of the bytecode to jump to. 1702 | // if true, no action is taken (falls through) 1703 | ConditionalJump, 1704 | 1705 | // start loop 1706 | // value popped off stack is what is iterated over 1707 | // args is index of bytecode after end loop (jumped to if iterable is 1708 | // empty) 1709 | // immediate value is key name (for maps) 1710 | // str is value name 1711 | StartLoop, 1712 | 1713 | // end a loop 1714 | // args is index of the first bytecode in the loop body 1715 | EndLoop, 1716 | }; 1717 | 1718 | enum Flag { 1719 | // location of value for value-taking ops (mask) 1720 | ValueMask = 0x03, 1721 | // pop value off stack 1722 | ValuePop = 0x00, 1723 | // value is immediate rather than on stack 1724 | ValueImmediate = 0x01, 1725 | // lookup immediate str (dot notation) 1726 | ValueLookupDot = 0x02, 1727 | // lookup immediate str (json pointer notation) 1728 | ValueLookupPointer = 0x03, 1729 | }; 1730 | 1731 | Op op {Op::Nop}; 1732 | uint32_t args: 30; 1733 | uint32_t flags: 2; 1734 | 1735 | json value; 1736 | std::string str; 1737 | 1738 | Bytecode(): args(0), flags(0) {} 1739 | explicit Bytecode(Op op, unsigned int args = 0): op(op), args(args), flags(0) {} 1740 | explicit Bytecode(Op op, nonstd::string_view str, unsigned int flags): op(op), args(0), flags(flags), str(str) {} 1741 | explicit Bytecode(Op op, json&& value, unsigned int flags): op(op), args(0), flags(flags), value(std::move(value)) {} 1742 | }; 1743 | 1744 | } // namespace inja 1745 | 1746 | #endif // INCLUDE_INJA_BYTECODE_HPP_ 1747 | 1748 | // #include "string_view.hpp" 1749 | 1750 | 1751 | 1752 | namespace inja { 1753 | 1754 | using json = nlohmann::json; 1755 | 1756 | using Arguments = std::vector; 1757 | using CallbackFunction = std::function; 1758 | 1759 | /*! 1760 | * \brief Class for builtin functions and user-defined callbacks. 1761 | */ 1762 | class FunctionStorage { 1763 | public: 1764 | void add_builtin(nonstd::string_view name, unsigned int num_args, Bytecode::Op op) { 1765 | auto& data = get_or_new(name, num_args); 1766 | data.op = op; 1767 | } 1768 | 1769 | void add_callback(nonstd::string_view name, unsigned int num_args, const CallbackFunction& function) { 1770 | auto& data = get_or_new(name, num_args); 1771 | data.function = function; 1772 | } 1773 | 1774 | Bytecode::Op find_builtin(nonstd::string_view name, unsigned int num_args) const { 1775 | if (auto ptr = get(name, num_args)) { 1776 | return ptr->op; 1777 | } 1778 | return Bytecode::Op::Nop; 1779 | } 1780 | 1781 | CallbackFunction find_callback(nonstd::string_view name, unsigned int num_args) const { 1782 | if (auto ptr = get(name, num_args)) { 1783 | return ptr->function; 1784 | } 1785 | return nullptr; 1786 | } 1787 | 1788 | private: 1789 | struct FunctionData { 1790 | unsigned int num_args {0}; 1791 | Bytecode::Op op {Bytecode::Op::Nop}; // for builtins 1792 | CallbackFunction function; // for callbacks 1793 | }; 1794 | 1795 | FunctionData& get_or_new(nonstd::string_view name, unsigned int num_args) { 1796 | auto &vec = m_map[static_cast(name)]; 1797 | for (auto &i: vec) { 1798 | if (i.num_args == num_args) return i; 1799 | } 1800 | vec.emplace_back(); 1801 | vec.back().num_args = num_args; 1802 | return vec.back(); 1803 | } 1804 | 1805 | const FunctionData* get(nonstd::string_view name, unsigned int num_args) const { 1806 | auto it = m_map.find(static_cast(name)); 1807 | if (it == m_map.end()) return nullptr; 1808 | for (auto &&i: it->second) { 1809 | if (i.num_args == num_args) return &i; 1810 | } 1811 | return nullptr; 1812 | } 1813 | 1814 | std::map> m_map; 1815 | }; 1816 | 1817 | } 1818 | 1819 | #endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_ 1820 | 1821 | // #include "parser.hpp" 1822 | // Copyright (c) 2019 Pantor. All rights reserved. 1823 | 1824 | #ifndef INCLUDE_INJA_PARSER_HPP_ 1825 | #define INCLUDE_INJA_PARSER_HPP_ 1826 | 1827 | #include 1828 | #include 1829 | #include 1830 | #include 1831 | 1832 | // #include "bytecode.hpp" 1833 | 1834 | // #include "config.hpp" 1835 | 1836 | // #include "function_storage.hpp" 1837 | 1838 | // #include "lexer.hpp" 1839 | // Copyright (c) 2019 Pantor. All rights reserved. 1840 | 1841 | #ifndef INCLUDE_INJA_LEXER_HPP_ 1842 | #define INCLUDE_INJA_LEXER_HPP_ 1843 | 1844 | #include 1845 | #include 1846 | 1847 | // #include "config.hpp" 1848 | 1849 | // #include "token.hpp" 1850 | // Copyright (c) 2019 Pantor. All rights reserved. 1851 | 1852 | #ifndef INCLUDE_INJA_TOKEN_HPP_ 1853 | #define INCLUDE_INJA_TOKEN_HPP_ 1854 | 1855 | #include 1856 | 1857 | // #include "string_view.hpp" 1858 | 1859 | 1860 | 1861 | namespace inja { 1862 | 1863 | /*! 1864 | * \brief Helper-class for the inja Parser. 1865 | */ 1866 | struct Token { 1867 | enum class Kind { 1868 | Text, 1869 | ExpressionOpen, // {{ 1870 | ExpressionClose, // }} 1871 | LineStatementOpen, // ## 1872 | LineStatementClose, // \n 1873 | StatementOpen, // {% 1874 | StatementClose, // %} 1875 | CommentOpen, // {# 1876 | CommentClose, // #} 1877 | Id, // this, this.foo 1878 | Number, // 1, 2, -1, 5.2, -5.3 1879 | String, // "this" 1880 | Comma, // , 1881 | Colon, // : 1882 | LeftParen, // ( 1883 | RightParen, // ) 1884 | LeftBracket, // [ 1885 | RightBracket, // ] 1886 | LeftBrace, // { 1887 | RightBrace, // } 1888 | Equal, // == 1889 | GreaterThan, // > 1890 | GreaterEqual, // >= 1891 | LessThan, // < 1892 | LessEqual, // <= 1893 | NotEqual, // != 1894 | Unknown, 1895 | Eof 1896 | } kind {Kind::Unknown}; 1897 | 1898 | nonstd::string_view text; 1899 | 1900 | constexpr Token() = default; 1901 | constexpr Token(Kind kind, nonstd::string_view text): kind(kind), text(text) {} 1902 | 1903 | std::string describe() const { 1904 | switch (kind) { 1905 | case Kind::Text: 1906 | return ""; 1907 | case Kind::LineStatementClose: 1908 | return ""; 1909 | case Kind::Eof: 1910 | return ""; 1911 | default: 1912 | return static_cast(text); 1913 | } 1914 | } 1915 | }; 1916 | 1917 | } 1918 | 1919 | #endif // INCLUDE_INJA_TOKEN_HPP_ 1920 | 1921 | // #include "utils.hpp" 1922 | // Copyright (c) 2019 Pantor. All rights reserved. 1923 | 1924 | #ifndef INCLUDE_INJA_UTILS_HPP_ 1925 | #define INCLUDE_INJA_UTILS_HPP_ 1926 | 1927 | #include 1928 | #include 1929 | #include 1930 | #include 1931 | #include 1932 | 1933 | // #include "string_view.hpp" 1934 | 1935 | 1936 | 1937 | namespace inja { 1938 | 1939 | inline void inja_throw(const std::string& type, const std::string& message) { 1940 | throw std::runtime_error("[inja.exception." + type + "] " + message); 1941 | } 1942 | 1943 | inline std::ifstream open_file_or_throw(const std::string& path) { 1944 | std::ifstream file; 1945 | file.exceptions(std::ifstream::failbit | std::ifstream::badbit); 1946 | try { 1947 | file.open(path); 1948 | } catch(const std::ios_base::failure& e) { 1949 | inja_throw("file_error", "failed accessing file at '" + path + "'"); 1950 | } 1951 | return file; 1952 | } 1953 | 1954 | namespace string_view { 1955 | inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) { 1956 | start = std::min(start, view.size()); 1957 | end = std::min(std::max(start, end), view.size()); 1958 | return view.substr(start, end - start); // StringRef(Data + Start, End - Start); 1959 | } 1960 | 1961 | inline std::pair split(nonstd::string_view view, char Separator) { 1962 | size_t idx = view.find(Separator); 1963 | if (idx == nonstd::string_view::npos) { 1964 | return std::make_pair(view, nonstd::string_view()); 1965 | } 1966 | return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, nonstd::string_view::npos)); 1967 | } 1968 | 1969 | inline bool starts_with(nonstd::string_view view, nonstd::string_view prefix) { 1970 | return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0); 1971 | } 1972 | } // namespace string_view 1973 | 1974 | } // namespace inja 1975 | 1976 | #endif // INCLUDE_INJA_UTILS_HPP_ 1977 | 1978 | 1979 | 1980 | namespace inja { 1981 | 1982 | /*! 1983 | * \brief Class for lexing an inja Template. 1984 | */ 1985 | class Lexer { 1986 | enum class State { 1987 | Text, 1988 | ExpressionStart, 1989 | ExpressionBody, 1990 | LineStart, 1991 | LineBody, 1992 | StatementStart, 1993 | StatementBody, 1994 | CommentStart, 1995 | CommentBody 1996 | } m_state; 1997 | 1998 | const LexerConfig& m_config; 1999 | nonstd::string_view m_in; 2000 | size_t m_tok_start; 2001 | size_t m_pos; 2002 | 2003 | public: 2004 | explicit Lexer(const LexerConfig& config) : m_config(config) {} 2005 | 2006 | void start(nonstd::string_view in) { 2007 | m_in = in; 2008 | m_tok_start = 0; 2009 | m_pos = 0; 2010 | m_state = State::Text; 2011 | } 2012 | 2013 | Token scan() { 2014 | m_tok_start = m_pos; 2015 | 2016 | again: 2017 | if (m_tok_start >= m_in.size()) return make_token(Token::Kind::Eof); 2018 | 2019 | switch (m_state) { 2020 | default: 2021 | case State::Text: { 2022 | // fast-scan to first open character 2023 | size_t open_start = m_in.substr(m_pos).find_first_of(m_config.open_chars); 2024 | if (open_start == nonstd::string_view::npos) { 2025 | // didn't find open, return remaining text as text token 2026 | m_pos = m_in.size(); 2027 | return make_token(Token::Kind::Text); 2028 | } 2029 | m_pos += open_start; 2030 | 2031 | // try to match one of the opening sequences, and get the close 2032 | nonstd::string_view open_str = m_in.substr(m_pos); 2033 | bool must_lstrip = false; 2034 | if (inja::string_view::starts_with(open_str, m_config.expression_open)) { 2035 | m_state = State::ExpressionStart; 2036 | } else if (inja::string_view::starts_with(open_str, m_config.statement_open)) { 2037 | m_state = State::StatementStart; 2038 | must_lstrip = m_config.lstrip_blocks; 2039 | } else if (inja::string_view::starts_with(open_str, m_config.comment_open)) { 2040 | m_state = State::CommentStart; 2041 | must_lstrip = m_config.lstrip_blocks; 2042 | } else if ((m_pos == 0 || m_in[m_pos - 1] == '\n') && 2043 | inja::string_view::starts_with(open_str, m_config.line_statement)) { 2044 | m_state = State::LineStart; 2045 | } else { 2046 | m_pos += 1; // wasn't actually an opening sequence 2047 | goto again; 2048 | } 2049 | 2050 | nonstd::string_view text = string_view::slice(m_in, m_tok_start, m_pos); 2051 | if (must_lstrip) 2052 | text = clear_final_line_if_whitespace(text); 2053 | 2054 | if (text.empty()) goto again; // don't generate empty token 2055 | return Token(Token::Kind::Text, text); 2056 | } 2057 | case State::ExpressionStart: { 2058 | m_state = State::ExpressionBody; 2059 | m_pos += m_config.expression_open.size(); 2060 | return make_token(Token::Kind::ExpressionOpen); 2061 | } 2062 | case State::LineStart: { 2063 | m_state = State::LineBody; 2064 | m_pos += m_config.line_statement.size(); 2065 | return make_token(Token::Kind::LineStatementOpen); 2066 | } 2067 | case State::StatementStart: { 2068 | m_state = State::StatementBody; 2069 | m_pos += m_config.statement_open.size(); 2070 | return make_token(Token::Kind::StatementOpen); 2071 | } 2072 | case State::CommentStart: { 2073 | m_state = State::CommentBody; 2074 | m_pos += m_config.comment_open.size(); 2075 | return make_token(Token::Kind::CommentOpen); 2076 | } 2077 | case State::ExpressionBody: 2078 | return scan_body(m_config.expression_close, Token::Kind::ExpressionClose); 2079 | case State::LineBody: 2080 | return scan_body("\n", Token::Kind::LineStatementClose); 2081 | case State::StatementBody: 2082 | return scan_body(m_config.statement_close, Token::Kind::StatementClose, m_config.trim_blocks); 2083 | case State::CommentBody: { 2084 | // fast-scan to comment close 2085 | size_t end = m_in.substr(m_pos).find(m_config.comment_close); 2086 | if (end == nonstd::string_view::npos) { 2087 | m_pos = m_in.size(); 2088 | return make_token(Token::Kind::Eof); 2089 | } 2090 | // return the entire comment in the close token 2091 | m_state = State::Text; 2092 | m_pos += end + m_config.comment_close.size(); 2093 | Token tok = make_token(Token::Kind::CommentClose); 2094 | if (m_config.trim_blocks) 2095 | skip_newline(); 2096 | return tok; 2097 | } 2098 | } 2099 | } 2100 | 2101 | const LexerConfig& get_config() const { return m_config; } 2102 | 2103 | private: 2104 | Token scan_body(nonstd::string_view close, Token::Kind closeKind, bool trim = false) { 2105 | again: 2106 | // skip whitespace (except for \n as it might be a close) 2107 | if (m_tok_start >= m_in.size()) return make_token(Token::Kind::Eof); 2108 | char ch = m_in[m_tok_start]; 2109 | if (ch == ' ' || ch == '\t' || ch == '\r') { 2110 | m_tok_start += 1; 2111 | goto again; 2112 | } 2113 | 2114 | // check for close 2115 | if (inja::string_view::starts_with(m_in.substr(m_tok_start), close)) { 2116 | m_state = State::Text; 2117 | m_pos = m_tok_start + close.size(); 2118 | Token tok = make_token(closeKind); 2119 | if (trim) 2120 | skip_newline(); 2121 | return tok; 2122 | } 2123 | 2124 | // skip \n 2125 | if (ch == '\n') { 2126 | m_tok_start += 1; 2127 | goto again; 2128 | } 2129 | 2130 | m_pos = m_tok_start + 1; 2131 | if (std::isalpha(ch)) return scan_id(); 2132 | switch (ch) { 2133 | case ',': 2134 | return make_token(Token::Kind::Comma); 2135 | case ':': 2136 | return make_token(Token::Kind::Colon); 2137 | case '(': 2138 | return make_token(Token::Kind::LeftParen); 2139 | case ')': 2140 | return make_token(Token::Kind::RightParen); 2141 | case '[': 2142 | return make_token(Token::Kind::LeftBracket); 2143 | case ']': 2144 | return make_token(Token::Kind::RightBracket); 2145 | case '{': 2146 | return make_token(Token::Kind::LeftBrace); 2147 | case '}': 2148 | return make_token(Token::Kind::RightBrace); 2149 | case '>': 2150 | if (m_pos < m_in.size() && m_in[m_pos] == '=') { 2151 | m_pos += 1; 2152 | return make_token(Token::Kind::GreaterEqual); 2153 | } 2154 | return make_token(Token::Kind::GreaterThan); 2155 | case '<': 2156 | if (m_pos < m_in.size() && m_in[m_pos] == '=') { 2157 | m_pos += 1; 2158 | return make_token(Token::Kind::LessEqual); 2159 | } 2160 | return make_token(Token::Kind::LessThan); 2161 | case '=': 2162 | if (m_pos < m_in.size() && m_in[m_pos] == '=') { 2163 | m_pos += 1; 2164 | return make_token(Token::Kind::Equal); 2165 | } 2166 | return make_token(Token::Kind::Unknown); 2167 | case '!': 2168 | if (m_pos < m_in.size() && m_in[m_pos] == '=') { 2169 | m_pos += 1; 2170 | return make_token(Token::Kind::NotEqual); 2171 | } 2172 | return make_token(Token::Kind::Unknown); 2173 | case '\"': 2174 | return scan_string(); 2175 | case '0': 2176 | case '1': 2177 | case '2': 2178 | case '3': 2179 | case '4': 2180 | case '5': 2181 | case '6': 2182 | case '7': 2183 | case '8': 2184 | case '9': 2185 | case '-': 2186 | return scan_number(); 2187 | case '_': 2188 | return scan_id(); 2189 | default: 2190 | return make_token(Token::Kind::Unknown); 2191 | } 2192 | } 2193 | 2194 | Token scan_id() { 2195 | for (;;) { 2196 | if (m_pos >= m_in.size()) { 2197 | break; 2198 | } 2199 | char ch = m_in[m_pos]; 2200 | if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') { 2201 | break; 2202 | } 2203 | m_pos += 1; 2204 | } 2205 | return make_token(Token::Kind::Id); 2206 | } 2207 | 2208 | Token scan_number() { 2209 | for (;;) { 2210 | if (m_pos >= m_in.size()) { 2211 | break; 2212 | } 2213 | char ch = m_in[m_pos]; 2214 | // be very permissive in lexer (we'll catch errors when conversion happens) 2215 | if (!std::isdigit(ch) && ch != '.' && ch != 'e' && ch != 'E' && ch != '+' && ch != '-') { 2216 | break; 2217 | } 2218 | m_pos += 1; 2219 | } 2220 | return make_token(Token::Kind::Number); 2221 | } 2222 | 2223 | Token scan_string() { 2224 | bool escape {false}; 2225 | for (;;) { 2226 | if (m_pos >= m_in.size()) break; 2227 | char ch = m_in[m_pos++]; 2228 | if (ch == '\\') { 2229 | escape = true; 2230 | } else if (!escape && ch == m_in[m_tok_start]) { 2231 | break; 2232 | } else { 2233 | escape = false; 2234 | } 2235 | } 2236 | return make_token(Token::Kind::String); 2237 | } 2238 | 2239 | Token make_token(Token::Kind kind) const { 2240 | return Token(kind, string_view::slice(m_in, m_tok_start, m_pos)); 2241 | } 2242 | 2243 | void skip_newline() { 2244 | if (m_pos < m_in.size()) { 2245 | char ch = m_in[m_pos]; 2246 | if (ch == '\n') 2247 | m_pos += 1; 2248 | else if (ch == '\r') { 2249 | m_pos += 1; 2250 | if (m_pos < m_in.size() && m_in[m_pos] == '\n') 2251 | m_pos += 1; 2252 | } 2253 | } 2254 | } 2255 | 2256 | static nonstd::string_view clear_final_line_if_whitespace(nonstd::string_view text) 2257 | { 2258 | nonstd::string_view result = text; 2259 | while (!result.empty()) { 2260 | char ch = result.back(); 2261 | if (ch == ' ' || ch == '\t') 2262 | result.remove_suffix(1); 2263 | else if (ch == '\n' || ch == '\r') 2264 | break; 2265 | else 2266 | return text; 2267 | } 2268 | return result; 2269 | } 2270 | }; 2271 | 2272 | } 2273 | 2274 | #endif // INCLUDE_INJA_LEXER_HPP_ 2275 | 2276 | // #include "template.hpp" 2277 | // Copyright (c) 2019 Pantor. All rights reserved. 2278 | 2279 | #ifndef INCLUDE_INJA_TEMPLATE_HPP_ 2280 | #define INCLUDE_INJA_TEMPLATE_HPP_ 2281 | 2282 | #include 2283 | #include 2284 | #include 2285 | 2286 | // #include "bytecode.hpp" 2287 | 2288 | 2289 | 2290 | namespace inja { 2291 | 2292 | /*! 2293 | * \brief The main inja Template. 2294 | */ 2295 | struct Template { 2296 | std::vector bytecodes; 2297 | std::string content; 2298 | }; 2299 | 2300 | using TemplateStorage = std::map; 2301 | 2302 | } // namespace inja 2303 | 2304 | #endif // INCLUDE_INJA_TEMPLATE_HPP_ 2305 | 2306 | // #include "token.hpp" 2307 | 2308 | // #include "utils.hpp" 2309 | 2310 | 2311 | #include 2312 | 2313 | 2314 | namespace inja { 2315 | 2316 | class ParserStatic { 2317 | ParserStatic() { 2318 | functions.add_builtin("at", 2, Bytecode::Op::At); 2319 | functions.add_builtin("default", 2, Bytecode::Op::Default); 2320 | functions.add_builtin("divisibleBy", 2, Bytecode::Op::DivisibleBy); 2321 | functions.add_builtin("even", 1, Bytecode::Op::Even); 2322 | functions.add_builtin("first", 1, Bytecode::Op::First); 2323 | functions.add_builtin("float", 1, Bytecode::Op::Float); 2324 | functions.add_builtin("int", 1, Bytecode::Op::Int); 2325 | functions.add_builtin("last", 1, Bytecode::Op::Last); 2326 | functions.add_builtin("length", 1, Bytecode::Op::Length); 2327 | functions.add_builtin("lower", 1, Bytecode::Op::Lower); 2328 | functions.add_builtin("max", 1, Bytecode::Op::Max); 2329 | functions.add_builtin("min", 1, Bytecode::Op::Min); 2330 | functions.add_builtin("odd", 1, Bytecode::Op::Odd); 2331 | functions.add_builtin("range", 1, Bytecode::Op::Range); 2332 | functions.add_builtin("round", 2, Bytecode::Op::Round); 2333 | functions.add_builtin("sort", 1, Bytecode::Op::Sort); 2334 | functions.add_builtin("upper", 1, Bytecode::Op::Upper); 2335 | functions.add_builtin("exists", 1, Bytecode::Op::Exists); 2336 | functions.add_builtin("existsIn", 2, Bytecode::Op::ExistsInObject); 2337 | functions.add_builtin("isBoolean", 1, Bytecode::Op::IsBoolean); 2338 | functions.add_builtin("isNumber", 1, Bytecode::Op::IsNumber); 2339 | functions.add_builtin("isInteger", 1, Bytecode::Op::IsInteger); 2340 | functions.add_builtin("isFloat", 1, Bytecode::Op::IsFloat); 2341 | functions.add_builtin("isObject", 1, Bytecode::Op::IsObject); 2342 | functions.add_builtin("isArray", 1, Bytecode::Op::IsArray); 2343 | functions.add_builtin("isString", 1, Bytecode::Op::IsString); 2344 | } 2345 | 2346 | public: 2347 | ParserStatic(const ParserStatic&) = delete; 2348 | ParserStatic& operator=(const ParserStatic&) = delete; 2349 | 2350 | static const ParserStatic& get_instance() { 2351 | static ParserStatic inst; 2352 | return inst; 2353 | } 2354 | 2355 | FunctionStorage functions; 2356 | }; 2357 | 2358 | /*! 2359 | * \brief Class for parsing an inja Template. 2360 | */ 2361 | class Parser { 2362 | public: 2363 | explicit Parser(const ParserConfig& parser_config, const LexerConfig& lexer_config, TemplateStorage& included_templates): m_config(parser_config), m_lexer(lexer_config), m_included_templates(included_templates), m_static(ParserStatic::get_instance()) { } 2364 | 2365 | bool parse_expression(Template& tmpl) { 2366 | if (!parse_expression_and(tmpl)) return false; 2367 | if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast("or")) return true; 2368 | get_next_token(); 2369 | if (!parse_expression_and(tmpl)) return false; 2370 | append_function(tmpl, Bytecode::Op::Or, 2); 2371 | return true; 2372 | } 2373 | 2374 | bool parse_expression_and(Template& tmpl) { 2375 | if (!parse_expression_not(tmpl)) return false; 2376 | if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast("and")) return true; 2377 | get_next_token(); 2378 | if (!parse_expression_not(tmpl)) return false; 2379 | append_function(tmpl, Bytecode::Op::And, 2); 2380 | return true; 2381 | } 2382 | 2383 | bool parse_expression_not(Template& tmpl) { 2384 | if (m_tok.kind == Token::Kind::Id && m_tok.text == static_cast("not")) { 2385 | get_next_token(); 2386 | if (!parse_expression_not(tmpl)) return false; 2387 | append_function(tmpl, Bytecode::Op::Not, 1); 2388 | return true; 2389 | } else { 2390 | return parse_expression_comparison(tmpl); 2391 | } 2392 | } 2393 | 2394 | bool parse_expression_comparison(Template& tmpl) { 2395 | if (!parse_expression_datum(tmpl)) return false; 2396 | Bytecode::Op op; 2397 | switch (m_tok.kind) { 2398 | case Token::Kind::Id: 2399 | if (m_tok.text == static_cast("in")) 2400 | op = Bytecode::Op::In; 2401 | else 2402 | return true; 2403 | break; 2404 | case Token::Kind::Equal: 2405 | op = Bytecode::Op::Equal; 2406 | break; 2407 | case Token::Kind::GreaterThan: 2408 | op = Bytecode::Op::Greater; 2409 | break; 2410 | case Token::Kind::LessThan: 2411 | op = Bytecode::Op::Less; 2412 | break; 2413 | case Token::Kind::LessEqual: 2414 | op = Bytecode::Op::LessEqual; 2415 | break; 2416 | case Token::Kind::GreaterEqual: 2417 | op = Bytecode::Op::GreaterEqual; 2418 | break; 2419 | case Token::Kind::NotEqual: 2420 | op = Bytecode::Op::Different; 2421 | break; 2422 | default: 2423 | return true; 2424 | } 2425 | get_next_token(); 2426 | if (!parse_expression_datum(tmpl)) return false; 2427 | append_function(tmpl, op, 2); 2428 | return true; 2429 | } 2430 | 2431 | bool parse_expression_datum(Template& tmpl) { 2432 | nonstd::string_view json_first; 2433 | size_t bracket_level = 0; 2434 | size_t brace_level = 0; 2435 | 2436 | for (;;) { 2437 | switch (m_tok.kind) { 2438 | case Token::Kind::LeftParen: { 2439 | get_next_token(); 2440 | if (!parse_expression(tmpl)) return false; 2441 | if (m_tok.kind != Token::Kind::RightParen) { 2442 | inja_throw("parser_error", "unmatched '('"); 2443 | } 2444 | get_next_token(); 2445 | return true; 2446 | } 2447 | case Token::Kind::Id: 2448 | get_peek_token(); 2449 | if (m_peek_tok.kind == Token::Kind::LeftParen) { 2450 | // function call, parse arguments 2451 | Token func_token = m_tok; 2452 | get_next_token(); // id 2453 | get_next_token(); // leftParen 2454 | unsigned int num_args = 0; 2455 | if (m_tok.kind == Token::Kind::RightParen) { 2456 | // no args 2457 | get_next_token(); 2458 | } else { 2459 | for (;;) { 2460 | if (!parse_expression(tmpl)) { 2461 | inja_throw("parser_error", "expected expression, got '" + m_tok.describe() + "'"); 2462 | } 2463 | num_args += 1; 2464 | if (m_tok.kind == Token::Kind::RightParen) { 2465 | get_next_token(); 2466 | break; 2467 | } 2468 | if (m_tok.kind != Token::Kind::Comma) { 2469 | inja_throw("parser_error", "expected ')' or ',', got '" + m_tok.describe() + "'"); 2470 | } 2471 | get_next_token(); 2472 | } 2473 | } 2474 | 2475 | auto op = m_static.functions.find_builtin(func_token.text, num_args); 2476 | 2477 | if (op != Bytecode::Op::Nop) { 2478 | // swap arguments for default(); see comment in RenderTo() 2479 | if (op == Bytecode::Op::Default) 2480 | std::swap(tmpl.bytecodes.back(), *(tmpl.bytecodes.rbegin() + 1)); 2481 | append_function(tmpl, op, num_args); 2482 | return true; 2483 | } else { 2484 | append_callback(tmpl, func_token.text, num_args); 2485 | return true; 2486 | } 2487 | } else if (m_tok.text == static_cast("true") || 2488 | m_tok.text == static_cast("false") || 2489 | m_tok.text == static_cast("null")) { 2490 | // true, false, null are json literals 2491 | if (brace_level == 0 && bracket_level == 0) { 2492 | json_first = m_tok.text; 2493 | goto returnJson; 2494 | } 2495 | break; 2496 | } else { 2497 | // normal literal (json read) 2498 | tmpl.bytecodes.emplace_back( 2499 | Bytecode::Op::Push, m_tok.text, 2500 | m_config.notation == ElementNotation::Pointer ? Bytecode::Flag::ValueLookupPointer : Bytecode::Flag::ValueLookupDot); 2501 | get_next_token(); 2502 | return true; 2503 | } 2504 | // json passthrough 2505 | case Token::Kind::Number: 2506 | case Token::Kind::String: 2507 | if (brace_level == 0 && bracket_level == 0) { 2508 | json_first = m_tok.text; 2509 | goto returnJson; 2510 | } 2511 | break; 2512 | case Token::Kind::Comma: 2513 | case Token::Kind::Colon: 2514 | if (brace_level == 0 && bracket_level == 0) { 2515 | inja_throw("parser_error", "unexpected token '" + m_tok.describe() + "'"); 2516 | } 2517 | break; 2518 | case Token::Kind::LeftBracket: 2519 | if (brace_level == 0 && bracket_level == 0) { 2520 | json_first = m_tok.text; 2521 | } 2522 | bracket_level += 1; 2523 | break; 2524 | case Token::Kind::LeftBrace: 2525 | if (brace_level == 0 && bracket_level == 0) { 2526 | json_first = m_tok.text; 2527 | } 2528 | brace_level += 1; 2529 | break; 2530 | case Token::Kind::RightBracket: 2531 | if (bracket_level == 0) { 2532 | inja_throw("parser_error", "unexpected ']'"); 2533 | } 2534 | --bracket_level; 2535 | if (brace_level == 0 && bracket_level == 0) goto returnJson; 2536 | break; 2537 | case Token::Kind::RightBrace: 2538 | if (brace_level == 0) { 2539 | inja_throw("parser_error", "unexpected '}'"); 2540 | } 2541 | --brace_level; 2542 | if (brace_level == 0 && bracket_level == 0) goto returnJson; 2543 | break; 2544 | default: 2545 | if (brace_level != 0) { 2546 | inja_throw("parser_error", "unmatched '{'"); 2547 | } 2548 | if (bracket_level != 0) { 2549 | inja_throw("parser_error", "unmatched '['"); 2550 | } 2551 | return false; 2552 | } 2553 | 2554 | get_next_token(); 2555 | } 2556 | 2557 | returnJson: 2558 | // bridge across all intermediate tokens 2559 | nonstd::string_view json_text(json_first.data(), m_tok.text.data() - json_first.data() + m_tok.text.size()); 2560 | tmpl.bytecodes.emplace_back(Bytecode::Op::Push, json::parse(json_text), Bytecode::Flag::ValueImmediate); 2561 | get_next_token(); 2562 | return true; 2563 | } 2564 | 2565 | bool parse_statement(Template& tmpl, nonstd::string_view path) { 2566 | if (m_tok.kind != Token::Kind::Id) return false; 2567 | 2568 | if (m_tok.text == static_cast("if")) { 2569 | get_next_token(); 2570 | 2571 | // evaluate expression 2572 | if (!parse_expression(tmpl)) return false; 2573 | 2574 | // start a new if block on if stack 2575 | m_if_stack.emplace_back(static_cast(tmpl.bytecodes.size())); 2576 | 2577 | // conditional jump; destination will be filled in by else or endif 2578 | tmpl.bytecodes.emplace_back(Bytecode::Op::ConditionalJump); 2579 | } else if (m_tok.text == static_cast("endif")) { 2580 | if (m_if_stack.empty()) { 2581 | inja_throw("parser_error", "endif without matching if"); 2582 | } 2583 | auto& if_data = m_if_stack.back(); 2584 | get_next_token(); 2585 | 2586 | // previous conditional jump jumps here 2587 | if (if_data.prev_cond_jump != std::numeric_limits::max()) { 2588 | tmpl.bytecodes[if_data.prev_cond_jump].args = tmpl.bytecodes.size(); 2589 | } 2590 | 2591 | // update all previous unconditional jumps to here 2592 | for (unsigned int i: if_data.uncond_jumps) { 2593 | tmpl.bytecodes[i].args = tmpl.bytecodes.size(); 2594 | } 2595 | 2596 | // pop if stack 2597 | m_if_stack.pop_back(); 2598 | } else if (m_tok.text == static_cast("else")) { 2599 | if (m_if_stack.empty()) 2600 | inja_throw("parser_error", "else without matching if"); 2601 | auto& if_data = m_if_stack.back(); 2602 | get_next_token(); 2603 | 2604 | // end previous block with unconditional jump to endif; destination will be 2605 | // filled in by endif 2606 | if_data.uncond_jumps.push_back(tmpl.bytecodes.size()); 2607 | tmpl.bytecodes.emplace_back(Bytecode::Op::Jump); 2608 | 2609 | // previous conditional jump jumps here 2610 | tmpl.bytecodes[if_data.prev_cond_jump].args = tmpl.bytecodes.size(); 2611 | if_data.prev_cond_jump = std::numeric_limits::max(); 2612 | 2613 | // chained else if 2614 | if (m_tok.kind == Token::Kind::Id && m_tok.text == static_cast("if")) { 2615 | get_next_token(); 2616 | 2617 | // evaluate expression 2618 | if (!parse_expression(tmpl)) return false; 2619 | 2620 | // update "previous jump" 2621 | if_data.prev_cond_jump = tmpl.bytecodes.size(); 2622 | 2623 | // conditional jump; destination will be filled in by else or endif 2624 | tmpl.bytecodes.emplace_back(Bytecode::Op::ConditionalJump); 2625 | } 2626 | } else if (m_tok.text == static_cast("for")) { 2627 | get_next_token(); 2628 | 2629 | // options: for a in arr; for a, b in obj 2630 | if (m_tok.kind != Token::Kind::Id) 2631 | inja_throw("parser_error", "expected id, got '" + m_tok.describe() + "'"); 2632 | Token value_token = m_tok; 2633 | get_next_token(); 2634 | 2635 | Token key_token; 2636 | if (m_tok.kind == Token::Kind::Comma) { 2637 | get_next_token(); 2638 | if (m_tok.kind != Token::Kind::Id) 2639 | inja_throw("parser_error", "expected id, got '" + m_tok.describe() + "'"); 2640 | key_token = std::move(value_token); 2641 | value_token = m_tok; 2642 | get_next_token(); 2643 | } 2644 | 2645 | if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast("in")) 2646 | inja_throw("parser_error", 2647 | "expected 'in', got '" + m_tok.describe() + "'"); 2648 | get_next_token(); 2649 | 2650 | if (!parse_expression(tmpl)) return false; 2651 | 2652 | m_loop_stack.push_back(tmpl.bytecodes.size()); 2653 | 2654 | tmpl.bytecodes.emplace_back(Bytecode::Op::StartLoop); 2655 | if (!key_token.text.empty()) { 2656 | tmpl.bytecodes.back().value = key_token.text; 2657 | } 2658 | tmpl.bytecodes.back().str = static_cast(value_token.text); 2659 | } else if (m_tok.text == static_cast("endfor")) { 2660 | get_next_token(); 2661 | if (m_loop_stack.empty()) { 2662 | inja_throw("parser_error", "endfor without matching for"); 2663 | } 2664 | 2665 | // update loop with EndLoop index (for empty case) 2666 | tmpl.bytecodes[m_loop_stack.back()].args = tmpl.bytecodes.size(); 2667 | 2668 | tmpl.bytecodes.emplace_back(Bytecode::Op::EndLoop); 2669 | tmpl.bytecodes.back().args = m_loop_stack.back() + 1; // loop body 2670 | m_loop_stack.pop_back(); 2671 | } else if (m_tok.text == static_cast("include")) { 2672 | get_next_token(); 2673 | 2674 | if (m_tok.kind != Token::Kind::String) { 2675 | inja_throw("parser_error", "expected string, got '" + m_tok.describe() + "'"); 2676 | } 2677 | 2678 | // build the relative path 2679 | json json_name = json::parse(m_tok.text); 2680 | std::string pathname = static_cast(path); 2681 | pathname += json_name.get_ref(); 2682 | if (pathname.compare(0, 2, "./") == 0) { 2683 | pathname.erase(0, 2); 2684 | } 2685 | // sys::path::remove_dots(pathname, true, sys::path::Style::posix); 2686 | 2687 | if (m_included_templates.find(pathname) == m_included_templates.end()) { 2688 | Template include_template = parse_template(pathname); 2689 | m_included_templates.emplace(pathname, include_template); 2690 | } 2691 | 2692 | // generate a reference bytecode 2693 | tmpl.bytecodes.emplace_back(Bytecode::Op::Include, json(pathname), Bytecode::Flag::ValueImmediate); 2694 | 2695 | get_next_token(); 2696 | } else { 2697 | return false; 2698 | } 2699 | return true; 2700 | } 2701 | 2702 | void append_function(Template& tmpl, Bytecode::Op op, unsigned int num_args) { 2703 | // we can merge with back-to-back push 2704 | if (!tmpl.bytecodes.empty()) { 2705 | Bytecode& last = tmpl.bytecodes.back(); 2706 | if (last.op == Bytecode::Op::Push) { 2707 | last.op = op; 2708 | last.args = num_args; 2709 | return; 2710 | } 2711 | } 2712 | 2713 | // otherwise just add it to the end 2714 | tmpl.bytecodes.emplace_back(op, num_args); 2715 | } 2716 | 2717 | void append_callback(Template& tmpl, nonstd::string_view name, unsigned int num_args) { 2718 | // we can merge with back-to-back push value (not lookup) 2719 | if (!tmpl.bytecodes.empty()) { 2720 | Bytecode& last = tmpl.bytecodes.back(); 2721 | if (last.op == Bytecode::Op::Push && 2722 | (last.flags & Bytecode::Flag::ValueMask) == Bytecode::Flag::ValueImmediate) { 2723 | last.op = Bytecode::Op::Callback; 2724 | last.args = num_args; 2725 | last.str = static_cast(name); 2726 | return; 2727 | } 2728 | } 2729 | 2730 | // otherwise just add it to the end 2731 | tmpl.bytecodes.emplace_back(Bytecode::Op::Callback, num_args); 2732 | tmpl.bytecodes.back().str = static_cast(name); 2733 | } 2734 | 2735 | void parse_into(Template& tmpl, nonstd::string_view path) { 2736 | m_lexer.start(tmpl.content); 2737 | 2738 | for (;;) { 2739 | get_next_token(); 2740 | switch (m_tok.kind) { 2741 | case Token::Kind::Eof: 2742 | if (!m_if_stack.empty()) inja_throw("parser_error", "unmatched if"); 2743 | if (!m_loop_stack.empty()) inja_throw("parser_error", "unmatched for"); 2744 | return; 2745 | case Token::Kind::Text: 2746 | tmpl.bytecodes.emplace_back(Bytecode::Op::PrintText, m_tok.text, 0u); 2747 | break; 2748 | case Token::Kind::StatementOpen: 2749 | get_next_token(); 2750 | if (!parse_statement(tmpl, path)) { 2751 | inja_throw("parser_error", "expected statement, got '" + m_tok.describe() + "'"); 2752 | } 2753 | if (m_tok.kind != Token::Kind::StatementClose) { 2754 | inja_throw("parser_error", "expected statement close, got '" + m_tok.describe() + "'"); 2755 | } 2756 | break; 2757 | case Token::Kind::LineStatementOpen: 2758 | get_next_token(); 2759 | parse_statement(tmpl, path); 2760 | if (m_tok.kind != Token::Kind::LineStatementClose && 2761 | m_tok.kind != Token::Kind::Eof) { 2762 | inja_throw("parser_error", "expected line statement close, got '" + m_tok.describe() + "'"); 2763 | } 2764 | break; 2765 | case Token::Kind::ExpressionOpen: 2766 | get_next_token(); 2767 | if (!parse_expression(tmpl)) { 2768 | inja_throw("parser_error", "expected expression, got '" + m_tok.describe() + "'"); 2769 | } 2770 | append_function(tmpl, Bytecode::Op::PrintValue, 1); 2771 | if (m_tok.kind != Token::Kind::ExpressionClose) { 2772 | inja_throw("parser_error", "expected expression close, got '" + m_tok.describe() + "'"); 2773 | } 2774 | break; 2775 | case Token::Kind::CommentOpen: 2776 | get_next_token(); 2777 | if (m_tok.kind != Token::Kind::CommentClose) { 2778 | inja_throw("parser_error", "expected comment close, got '" + m_tok.describe() + "'"); 2779 | } 2780 | break; 2781 | default: 2782 | inja_throw("parser_error", "unexpected token '" + m_tok.describe() + "'"); 2783 | break; 2784 | } 2785 | } 2786 | } 2787 | 2788 | Template parse(nonstd::string_view input, nonstd::string_view path) { 2789 | Template result; 2790 | result.content = static_cast(input); 2791 | parse_into(result, path); 2792 | return result; 2793 | } 2794 | 2795 | Template parse(nonstd::string_view input) { 2796 | return parse(input, "./"); 2797 | } 2798 | 2799 | Template parse_template(nonstd::string_view filename) { 2800 | Template result; 2801 | result.content = load_file(filename); 2802 | 2803 | nonstd::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1); 2804 | // StringRef path = sys::path::parent_path(filename); 2805 | Parser(m_config, m_lexer.get_config(), m_included_templates).parse_into(result, path); 2806 | return result; 2807 | } 2808 | 2809 | std::string load_file(nonstd::string_view filename) { 2810 | std::ifstream file = open_file_or_throw(static_cast(filename)); 2811 | std::string text((std::istreambuf_iterator(file)), std::istreambuf_iterator()); 2812 | return text; 2813 | } 2814 | 2815 | private: 2816 | const ParserConfig& m_config; 2817 | Lexer m_lexer; 2818 | Token m_tok; 2819 | Token m_peek_tok; 2820 | bool m_have_peek_tok {false}; 2821 | TemplateStorage& m_included_templates; 2822 | const ParserStatic& m_static; 2823 | 2824 | struct IfData { 2825 | using jump_t = unsigned int; 2826 | jump_t prev_cond_jump; 2827 | std::vector uncond_jumps; 2828 | 2829 | explicit IfData(jump_t condJump) 2830 | : prev_cond_jump(condJump) 2831 | { 2832 | } 2833 | }; 2834 | 2835 | std::vector m_if_stack; 2836 | std::vector m_loop_stack; 2837 | 2838 | void get_next_token() { 2839 | if (m_have_peek_tok) { 2840 | m_tok = m_peek_tok; 2841 | m_have_peek_tok = false; 2842 | } else { 2843 | m_tok = m_lexer.scan(); 2844 | } 2845 | } 2846 | 2847 | void get_peek_token() { 2848 | if (!m_have_peek_tok) { 2849 | m_peek_tok = m_lexer.scan(); 2850 | m_have_peek_tok = true; 2851 | } 2852 | } 2853 | }; 2854 | 2855 | } // namespace inja 2856 | 2857 | #endif // INCLUDE_INJA_PARSER_HPP_ 2858 | 2859 | // #include "polyfill.hpp" 2860 | // Copyright (c) 2019 Pantor. All rights reserved. 2861 | 2862 | #ifndef INCLUDE_INJA_POLYFILL_HPP_ 2863 | #define INCLUDE_INJA_POLYFILL_HPP_ 2864 | 2865 | 2866 | #if __cplusplus < 201402L 2867 | 2868 | #include 2869 | #include 2870 | #include 2871 | #include 2872 | 2873 | 2874 | namespace stdinja { 2875 | 2876 | template struct _Unique_if { 2877 | typedef std::unique_ptr _Single_object; 2878 | }; 2879 | 2880 | template struct _Unique_if { 2881 | typedef std::unique_ptr _Unknown_bound; 2882 | }; 2883 | 2884 | template struct _Unique_if { 2885 | typedef void _Known_bound; 2886 | }; 2887 | 2888 | template 2889 | typename _Unique_if::_Single_object 2890 | make_unique(Args&&... args) { 2891 | return std::unique_ptr(new T(std::forward(args)...)); 2892 | } 2893 | 2894 | template 2895 | typename _Unique_if::_Unknown_bound 2896 | make_unique(size_t n) { 2897 | typedef typename std::remove_extent::type U; 2898 | return std::unique_ptr(new U[n]()); 2899 | } 2900 | 2901 | template 2902 | typename _Unique_if::_Known_bound 2903 | make_unique(Args&&...) = delete; 2904 | 2905 | } // namespace stdinja 2906 | 2907 | #else 2908 | 2909 | namespace stdinja = std; 2910 | 2911 | #endif // memory */ 2912 | 2913 | #endif // INCLUDE_INJA_POLYFILL_HPP_ 2914 | 2915 | // #include "renderer.hpp" 2916 | // Copyright (c) 2019 Pantor. All rights reserved. 2917 | 2918 | #ifndef INCLUDE_INJA_RENDERER_HPP_ 2919 | #define INCLUDE_INJA_RENDERER_HPP_ 2920 | 2921 | #include 2922 | #include 2923 | #include 2924 | #include 2925 | #include 2926 | 2927 | #include 2928 | 2929 | // #include "bytecode.hpp" 2930 | 2931 | // #include "template.hpp" 2932 | 2933 | // #include "utils.hpp" 2934 | 2935 | 2936 | 2937 | namespace inja { 2938 | 2939 | inline nonstd::string_view convert_dot_to_json_pointer(nonstd::string_view dot, std::string& out) { 2940 | out.clear(); 2941 | do { 2942 | nonstd::string_view part; 2943 | std::tie(part, dot) = string_view::split(dot, '.'); 2944 | out.push_back('/'); 2945 | out.append(part.begin(), part.end()); 2946 | } while (!dot.empty()); 2947 | return nonstd::string_view(out.data(), out.size()); 2948 | } 2949 | 2950 | /*! 2951 | * \brief Class for rendering a Template with data. 2952 | */ 2953 | class Renderer { 2954 | std::vector& get_args(const Bytecode& bc) { 2955 | m_tmp_args.clear(); 2956 | 2957 | bool has_imm = ((bc.flags & Bytecode::Flag::ValueMask) != Bytecode::Flag::ValuePop); 2958 | 2959 | // get args from stack 2960 | unsigned int pop_args = bc.args; 2961 | if (has_imm) { 2962 | pop_args -= 1; 2963 | } 2964 | 2965 | for (auto i = std::prev(m_stack.end(), pop_args); i != m_stack.end(); i++) { 2966 | m_tmp_args.push_back(&(*i)); 2967 | } 2968 | 2969 | // get immediate arg 2970 | if (has_imm) { 2971 | m_tmp_args.push_back(get_imm(bc)); 2972 | } 2973 | 2974 | return m_tmp_args; 2975 | } 2976 | 2977 | void pop_args(const Bytecode& bc) { 2978 | unsigned int popArgs = bc.args; 2979 | if ((bc.flags & Bytecode::Flag::ValueMask) != Bytecode::Flag::ValuePop) { 2980 | popArgs -= 1; 2981 | } 2982 | for (unsigned int i = 0; i < popArgs; ++i) { 2983 | m_stack.pop_back(); 2984 | } 2985 | } 2986 | 2987 | const json* get_imm(const Bytecode& bc) { 2988 | std::string ptr_buffer; 2989 | nonstd::string_view ptr; 2990 | switch (bc.flags & Bytecode::Flag::ValueMask) { 2991 | case Bytecode::Flag::ValuePop: 2992 | return nullptr; 2993 | case Bytecode::Flag::ValueImmediate: 2994 | return &bc.value; 2995 | case Bytecode::Flag::ValueLookupDot: 2996 | ptr = convert_dot_to_json_pointer(bc.str, ptr_buffer); 2997 | break; 2998 | case Bytecode::Flag::ValueLookupPointer: 2999 | ptr_buffer += '/'; 3000 | ptr_buffer += bc.str; 3001 | ptr = ptr_buffer; 3002 | break; 3003 | } 3004 | try { 3005 | return &m_data->at(json::json_pointer(ptr.data())); 3006 | } catch (std::exception&) { 3007 | // try to evaluate as a no-argument callback 3008 | if (auto callback = m_callbacks.find_callback(bc.str, 0)) { 3009 | std::vector arguments {}; 3010 | m_tmp_val = callback(arguments); 3011 | return &m_tmp_val; 3012 | } 3013 | inja_throw("render_error", "variable '" + static_cast(bc.str) + "' not found"); 3014 | return nullptr; 3015 | } 3016 | } 3017 | 3018 | bool truthy(const json& var) const { 3019 | if (var.empty()) { 3020 | return false; 3021 | } else if (var.is_number()) { 3022 | return (var != 0); 3023 | } else if (var.is_string()) { 3024 | return !var.empty(); 3025 | } 3026 | 3027 | try { 3028 | return var.get(); 3029 | } catch (json::type_error& e) { 3030 | inja_throw("json_error", e.what()); 3031 | throw; 3032 | } 3033 | } 3034 | 3035 | void update_loop_data() { 3036 | LoopLevel& level = m_loop_stack.back(); 3037 | 3038 | if (level.loop_type == LoopLevel::Type::Array) { 3039 | level.data[static_cast(level.value_name)] = level.values.at(level.index); // *level.it; 3040 | auto& loopData = level.data["loop"]; 3041 | loopData["index"] = level.index; 3042 | loopData["index1"] = level.index + 1; 3043 | loopData["is_first"] = (level.index == 0); 3044 | loopData["is_last"] = (level.index == level.size - 1); 3045 | } else { 3046 | level.data[static_cast(level.key_name)] = level.map_it->first; 3047 | level.data[static_cast(level.value_name)] = *level.map_it->second; 3048 | } 3049 | } 3050 | 3051 | const TemplateStorage& m_included_templates; 3052 | const FunctionStorage& m_callbacks; 3053 | 3054 | std::vector m_stack; 3055 | 3056 | 3057 | struct LoopLevel { 3058 | enum class Type { Map, Array }; 3059 | 3060 | Type loop_type; 3061 | nonstd::string_view key_name; // variable name for keys 3062 | nonstd::string_view value_name; // variable name for values 3063 | json data; // data with loop info added 3064 | 3065 | json values; // values to iterate over 3066 | 3067 | // loop over list 3068 | size_t index; // current list index 3069 | size_t size; // length of list 3070 | 3071 | // loop over map 3072 | using KeyValue = std::pair; 3073 | using MapValues = std::vector; 3074 | MapValues map_values; // values to iterate over 3075 | MapValues::iterator map_it; // iterator over values 3076 | }; 3077 | 3078 | std::vector m_loop_stack; 3079 | const json* m_data; 3080 | 3081 | std::vector m_tmp_args; 3082 | json m_tmp_val; 3083 | 3084 | 3085 | public: 3086 | Renderer(const TemplateStorage& included_templates, const FunctionStorage& callbacks): m_included_templates(included_templates), m_callbacks(callbacks) { 3087 | m_stack.reserve(16); 3088 | m_tmp_args.reserve(4); 3089 | m_loop_stack.reserve(16); 3090 | } 3091 | 3092 | void render_to(std::ostream& os, const Template& tmpl, const json& data) { 3093 | m_data = &data; 3094 | 3095 | for (size_t i = 0; i < tmpl.bytecodes.size(); ++i) { 3096 | const auto& bc = tmpl.bytecodes[i]; 3097 | 3098 | switch (bc.op) { 3099 | case Bytecode::Op::Nop: { 3100 | break; 3101 | } 3102 | case Bytecode::Op::PrintText: { 3103 | os << bc.str; 3104 | break; 3105 | } 3106 | case Bytecode::Op::PrintValue: { 3107 | const json& val = *get_args(bc)[0]; 3108 | if (val.is_string()) { 3109 | os << val.get_ref(); 3110 | } else { 3111 | os << val.dump(); 3112 | } 3113 | pop_args(bc); 3114 | break; 3115 | } 3116 | case Bytecode::Op::Push: { 3117 | m_stack.emplace_back(*get_imm(bc)); 3118 | break; 3119 | } 3120 | case Bytecode::Op::Upper: { 3121 | auto result = get_args(bc)[0]->get(); 3122 | std::transform(result.begin(), result.end(), result.begin(), ::toupper); 3123 | pop_args(bc); 3124 | m_stack.emplace_back(std::move(result)); 3125 | break; 3126 | } 3127 | case Bytecode::Op::Lower: { 3128 | auto result = get_args(bc)[0]->get(); 3129 | std::transform(result.begin(), result.end(), result.begin(), ::tolower); 3130 | pop_args(bc); 3131 | m_stack.emplace_back(std::move(result)); 3132 | break; 3133 | } 3134 | case Bytecode::Op::Range: { 3135 | int number = get_args(bc)[0]->get(); 3136 | std::vector result(number); 3137 | std::iota(std::begin(result), std::end(result), 0); 3138 | pop_args(bc); 3139 | m_stack.emplace_back(std::move(result)); 3140 | break; 3141 | } 3142 | case Bytecode::Op::Length: { 3143 | const json& val = *get_args(bc)[0]; 3144 | 3145 | int result; 3146 | if (val.is_string()) { 3147 | result = val.get_ref().length(); 3148 | } else { 3149 | result = val.size(); 3150 | } 3151 | 3152 | pop_args(bc); 3153 | m_stack.emplace_back(result); 3154 | break; 3155 | } 3156 | case Bytecode::Op::Sort: { 3157 | auto result = get_args(bc)[0]->get>(); 3158 | std::sort(result.begin(), result.end()); 3159 | pop_args(bc); 3160 | m_stack.emplace_back(std::move(result)); 3161 | break; 3162 | } 3163 | case Bytecode::Op::At: { 3164 | auto args = get_args(bc); 3165 | auto result = args[0]->at(args[1]->get()); 3166 | pop_args(bc); 3167 | m_stack.emplace_back(result); 3168 | break; 3169 | } 3170 | case Bytecode::Op::First: { 3171 | auto result = get_args(bc)[0]->front(); 3172 | pop_args(bc); 3173 | m_stack.emplace_back(result); 3174 | break; 3175 | } 3176 | case Bytecode::Op::Last: { 3177 | auto result = get_args(bc)[0]->back(); 3178 | pop_args(bc); 3179 | m_stack.emplace_back(result); 3180 | break; 3181 | } 3182 | case Bytecode::Op::Round: { 3183 | auto args = get_args(bc); 3184 | double number = args[0]->get(); 3185 | int precision = args[1]->get(); 3186 | pop_args(bc); 3187 | m_stack.emplace_back(std::round(number * std::pow(10.0, precision)) / std::pow(10.0, precision)); 3188 | break; 3189 | } 3190 | case Bytecode::Op::DivisibleBy: { 3191 | auto args = get_args(bc); 3192 | int number = args[0]->get(); 3193 | int divisor = args[1]->get(); 3194 | pop_args(bc); 3195 | m_stack.emplace_back((divisor != 0) && (number % divisor == 0)); 3196 | break; 3197 | } 3198 | case Bytecode::Op::Odd: { 3199 | int number = get_args(bc)[0]->get(); 3200 | pop_args(bc); 3201 | m_stack.emplace_back(number % 2 != 0); 3202 | break; 3203 | } 3204 | case Bytecode::Op::Even: { 3205 | int number = get_args(bc)[0]->get(); 3206 | pop_args(bc); 3207 | m_stack.emplace_back(number % 2 == 0); 3208 | break; 3209 | } 3210 | case Bytecode::Op::Max: { 3211 | auto args = get_args(bc); 3212 | auto result = *std::max_element(args[0]->begin(), args[0]->end()); 3213 | pop_args(bc); 3214 | m_stack.emplace_back(std::move(result)); 3215 | break; 3216 | } 3217 | case Bytecode::Op::Min: { 3218 | auto args = get_args(bc); 3219 | auto result = *std::min_element(args[0]->begin(), args[0]->end()); 3220 | pop_args(bc); 3221 | m_stack.emplace_back(std::move(result)); 3222 | break; 3223 | } 3224 | case Bytecode::Op::Not: { 3225 | bool result = !truthy(*get_args(bc)[0]); 3226 | pop_args(bc); 3227 | m_stack.emplace_back(result); 3228 | break; 3229 | } 3230 | case Bytecode::Op::And: { 3231 | auto args = get_args(bc); 3232 | bool result = truthy(*args[0]) && truthy(*args[1]); 3233 | pop_args(bc); 3234 | m_stack.emplace_back(result); 3235 | break; 3236 | } 3237 | case Bytecode::Op::Or: { 3238 | auto args = get_args(bc); 3239 | bool result = truthy(*args[0]) || truthy(*args[1]); 3240 | pop_args(bc); 3241 | m_stack.emplace_back(result); 3242 | break; 3243 | } 3244 | case Bytecode::Op::In: { 3245 | auto args = get_args(bc); 3246 | bool result = std::find(args[1]->begin(), args[1]->end(), *args[0]) != 3247 | args[1]->end(); 3248 | pop_args(bc); 3249 | m_stack.emplace_back(result); 3250 | break; 3251 | } 3252 | case Bytecode::Op::Equal: { 3253 | auto args = get_args(bc); 3254 | bool result = (*args[0] == *args[1]); 3255 | pop_args(bc); 3256 | m_stack.emplace_back(result); 3257 | break; 3258 | } 3259 | case Bytecode::Op::Greater: { 3260 | auto args = get_args(bc); 3261 | bool result = (*args[0] > *args[1]); 3262 | pop_args(bc); 3263 | m_stack.emplace_back(result); 3264 | break; 3265 | } 3266 | case Bytecode::Op::Less: { 3267 | auto args = get_args(bc); 3268 | bool result = (*args[0] < *args[1]); 3269 | pop_args(bc); 3270 | m_stack.emplace_back(result); 3271 | break; 3272 | } 3273 | case Bytecode::Op::GreaterEqual: { 3274 | auto args = get_args(bc); 3275 | bool result = (*args[0] >= *args[1]); 3276 | pop_args(bc); 3277 | m_stack.emplace_back(result); 3278 | break; 3279 | } 3280 | case Bytecode::Op::LessEqual: { 3281 | auto args = get_args(bc); 3282 | bool result = (*args[0] <= *args[1]); 3283 | pop_args(bc); 3284 | m_stack.emplace_back(result); 3285 | break; 3286 | } 3287 | case Bytecode::Op::Different: { 3288 | auto args = get_args(bc); 3289 | bool result = (*args[0] != *args[1]); 3290 | pop_args(bc); 3291 | m_stack.emplace_back(result); 3292 | break; 3293 | } 3294 | case Bytecode::Op::Float: { 3295 | double result = 3296 | std::stod(get_args(bc)[0]->get_ref()); 3297 | pop_args(bc); 3298 | m_stack.emplace_back(result); 3299 | break; 3300 | } 3301 | case Bytecode::Op::Int: { 3302 | int result = std::stoi(get_args(bc)[0]->get_ref()); 3303 | pop_args(bc); 3304 | m_stack.emplace_back(result); 3305 | break; 3306 | } 3307 | case Bytecode::Op::Exists: { 3308 | auto&& name = get_args(bc)[0]->get_ref(); 3309 | bool result = (data.find(name) != data.end()); 3310 | pop_args(bc); 3311 | m_stack.emplace_back(result); 3312 | break; 3313 | } 3314 | case Bytecode::Op::ExistsInObject: { 3315 | auto args = get_args(bc); 3316 | auto&& name = args[1]->get_ref(); 3317 | bool result = (args[0]->find(name) != args[0]->end()); 3318 | pop_args(bc); 3319 | m_stack.emplace_back(result); 3320 | break; 3321 | } 3322 | case Bytecode::Op::IsBoolean: { 3323 | bool result = get_args(bc)[0]->is_boolean(); 3324 | pop_args(bc); 3325 | m_stack.emplace_back(result); 3326 | break; 3327 | } 3328 | case Bytecode::Op::IsNumber: { 3329 | bool result = get_args(bc)[0]->is_number(); 3330 | pop_args(bc); 3331 | m_stack.emplace_back(result); 3332 | break; 3333 | } 3334 | case Bytecode::Op::IsInteger: { 3335 | bool result = get_args(bc)[0]->is_number_integer(); 3336 | pop_args(bc); 3337 | m_stack.emplace_back(result); 3338 | break; 3339 | } 3340 | case Bytecode::Op::IsFloat: { 3341 | bool result = get_args(bc)[0]->is_number_float(); 3342 | pop_args(bc); 3343 | m_stack.emplace_back(result); 3344 | break; 3345 | } 3346 | case Bytecode::Op::IsObject: { 3347 | bool result = get_args(bc)[0]->is_object(); 3348 | pop_args(bc); 3349 | m_stack.emplace_back(result); 3350 | break; 3351 | } 3352 | case Bytecode::Op::IsArray: { 3353 | bool result = get_args(bc)[0]->is_array(); 3354 | pop_args(bc); 3355 | m_stack.emplace_back(result); 3356 | break; 3357 | } 3358 | case Bytecode::Op::IsString: { 3359 | bool result = get_args(bc)[0]->is_string(); 3360 | pop_args(bc); 3361 | m_stack.emplace_back(result); 3362 | break; 3363 | } 3364 | case Bytecode::Op::Default: { 3365 | // default needs to be a bit "magic"; we can't evaluate the first 3366 | // argument during the push operation, so we swap the arguments during 3367 | // the parse phase so the second argument is pushed on the stack and 3368 | // the first argument is in the immediate 3369 | try { 3370 | const json* imm = get_imm(bc); 3371 | // if no exception was raised, replace the stack value with it 3372 | m_stack.back() = *imm; 3373 | } catch (std::exception&) { 3374 | // couldn't read immediate, just leave the stack as is 3375 | } 3376 | break; 3377 | } 3378 | case Bytecode::Op::Include: 3379 | Renderer(m_included_templates, m_callbacks).render_to(os, m_included_templates.find(get_imm(bc)->get_ref())->second, *m_data); 3380 | break; 3381 | case Bytecode::Op::Callback: { 3382 | auto callback = m_callbacks.find_callback(bc.str, bc.args); 3383 | if (!callback) { 3384 | inja_throw("render_error", "function '" + static_cast(bc.str) + "' (" + std::to_string(static_cast(bc.args)) + ") not found"); 3385 | } 3386 | json result = callback(get_args(bc)); 3387 | pop_args(bc); 3388 | m_stack.emplace_back(std::move(result)); 3389 | break; 3390 | } 3391 | case Bytecode::Op::Jump: { 3392 | i = bc.args - 1; // -1 due to ++i in loop 3393 | break; 3394 | } 3395 | case Bytecode::Op::ConditionalJump: { 3396 | if (!truthy(m_stack.back())) { 3397 | i = bc.args - 1; // -1 due to ++i in loop 3398 | } 3399 | m_stack.pop_back(); 3400 | break; 3401 | } 3402 | case Bytecode::Op::StartLoop: { 3403 | // jump past loop body if empty 3404 | if (m_stack.back().empty()) { 3405 | m_stack.pop_back(); 3406 | i = bc.args; // ++i in loop will take it past EndLoop 3407 | break; 3408 | } 3409 | 3410 | m_loop_stack.emplace_back(); 3411 | LoopLevel& level = m_loop_stack.back(); 3412 | level.value_name = bc.str; 3413 | level.values = std::move(m_stack.back()); 3414 | level.data = (*m_data); 3415 | m_stack.pop_back(); 3416 | 3417 | if (bc.value.is_string()) { 3418 | // map iterator 3419 | if (!level.values.is_object()) { 3420 | m_loop_stack.pop_back(); 3421 | inja_throw("render_error", "for key, value requires object"); 3422 | } 3423 | level.loop_type = LoopLevel::Type::Map; 3424 | level.key_name = bc.value.get_ref(); 3425 | 3426 | // sort by key 3427 | for (auto it = level.values.begin(), end = level.values.end(); it != end; ++it) { 3428 | level.map_values.emplace_back(it.key(), &it.value()); 3429 | } 3430 | auto sort_lambda = [](const LoopLevel::KeyValue& a, const LoopLevel::KeyValue& b) { return a.first < b.first; }; 3431 | std::sort(level.map_values.begin(), level.map_values.end(), sort_lambda); 3432 | level.map_it = level.map_values.begin(); 3433 | } else { 3434 | if (!level.values.is_array()) { 3435 | m_loop_stack.pop_back(); 3436 | inja_throw("render_error", "type must be array"); 3437 | } 3438 | 3439 | // list iterator 3440 | level.loop_type = LoopLevel::Type::Array; 3441 | level.index = 0; 3442 | level.size = level.values.size(); 3443 | } 3444 | 3445 | // provide parent access in nested loop 3446 | auto parent_loop_it = level.data.find("loop"); 3447 | if (parent_loop_it != level.data.end()) { 3448 | json loop_copy = *parent_loop_it; 3449 | (*parent_loop_it)["parent"] = std::move(loop_copy); 3450 | } 3451 | 3452 | // set "current" data to loop data 3453 | m_data = &level.data; 3454 | update_loop_data(); 3455 | break; 3456 | } 3457 | case Bytecode::Op::EndLoop: { 3458 | if (m_loop_stack.empty()) { 3459 | inja_throw("render_error", "unexpected state in renderer"); 3460 | } 3461 | LoopLevel& level = m_loop_stack.back(); 3462 | 3463 | bool done; 3464 | if (level.loop_type == LoopLevel::Type::Array) { 3465 | level.index += 1; 3466 | done = (level.index == level.values.size()); 3467 | } else { 3468 | level.map_it += 1; 3469 | done = (level.map_it == level.map_values.end()); 3470 | } 3471 | 3472 | if (done) { 3473 | m_loop_stack.pop_back(); 3474 | // set "current" data to outer loop data or main data as appropriate 3475 | if (!m_loop_stack.empty()) { 3476 | m_data = &m_loop_stack.back().data; 3477 | } else { 3478 | m_data = &data; 3479 | } 3480 | break; 3481 | } 3482 | 3483 | update_loop_data(); 3484 | 3485 | // jump back to start of loop 3486 | i = bc.args - 1; // -1 due to ++i in loop 3487 | break; 3488 | } 3489 | default: { 3490 | inja_throw("render_error", "unknown op in renderer: " + std::to_string(static_cast(bc.op))); 3491 | } 3492 | } 3493 | } 3494 | } 3495 | }; 3496 | 3497 | } // namespace inja 3498 | 3499 | #endif // INCLUDE_INJA_RENDERER_HPP_ 3500 | 3501 | // #include "string_view.hpp" 3502 | 3503 | // #include "template.hpp" 3504 | 3505 | // #include "utils.hpp" 3506 | 3507 | 3508 | 3509 | namespace inja { 3510 | 3511 | using json = nlohmann::json; 3512 | 3513 | /*! 3514 | * \brief Class for changing the configuration. 3515 | */ 3516 | class Environment { 3517 | public: 3518 | Environment(): Environment("") { } 3519 | 3520 | explicit Environment(const std::string& global_path): m_input_path(global_path), m_output_path(global_path) {} 3521 | 3522 | Environment(const std::string& input_path, const std::string& output_path): m_input_path(input_path), m_output_path(output_path) {} 3523 | 3524 | /// Sets the opener and closer for template statements 3525 | void set_statement(const std::string& open, const std::string& close) { 3526 | m_lexer_config.statement_open = open; 3527 | m_lexer_config.statement_close = close; 3528 | m_lexer_config.update_open_chars(); 3529 | } 3530 | 3531 | /// Sets the opener for template line statements 3532 | void set_line_statement(const std::string& open) { 3533 | m_lexer_config.line_statement = open; 3534 | m_lexer_config.update_open_chars(); 3535 | } 3536 | 3537 | /// Sets the opener and closer for template expressions 3538 | void set_expression(const std::string& open, const std::string& close) { 3539 | m_lexer_config.expression_open = open; 3540 | m_lexer_config.expression_close = close; 3541 | m_lexer_config.update_open_chars(); 3542 | } 3543 | 3544 | /// Sets the opener and closer for template comments 3545 | void set_comment(const std::string& open, const std::string& close) { 3546 | m_lexer_config.comment_open = open; 3547 | m_lexer_config.comment_close = close; 3548 | m_lexer_config.update_open_chars(); 3549 | } 3550 | 3551 | /// Sets whether to remove the first newline after a block 3552 | void set_trim_blocks(bool trim_blocks) { 3553 | m_lexer_config.trim_blocks = trim_blocks; 3554 | } 3555 | 3556 | /// Sets whether to strip the spaces and tabs from the start of a line to a block 3557 | void set_lstrip_blocks(bool lstrip_blocks) { 3558 | m_lexer_config.lstrip_blocks = lstrip_blocks; 3559 | } 3560 | 3561 | /// Sets the element notation syntax 3562 | void set_element_notation(ElementNotation notation) { 3563 | m_parser_config.notation = notation; 3564 | } 3565 | 3566 | 3567 | Template parse(nonstd::string_view input) { 3568 | Parser parser(m_parser_config, m_lexer_config, m_included_templates); 3569 | return parser.parse(input); 3570 | } 3571 | 3572 | Template parse_template(const std::string& filename) { 3573 | Parser parser(m_parser_config, m_lexer_config, m_included_templates); 3574 | return parser.parse_template(m_input_path + static_cast(filename)); 3575 | } 3576 | 3577 | std::string render(nonstd::string_view input, const json& data) { 3578 | return render(parse(input), data); 3579 | } 3580 | 3581 | std::string render(const Template& tmpl, const json& data) { 3582 | std::stringstream os; 3583 | render_to(os, tmpl, data); 3584 | return os.str(); 3585 | } 3586 | 3587 | std::string render_file(const std::string& filename, const json& data) { 3588 | return render(parse_template(filename), data); 3589 | } 3590 | 3591 | std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) { 3592 | const json data = load_json(filename_data); 3593 | return render_file(filename, data); 3594 | } 3595 | 3596 | void write(const std::string& filename, const json& data, const std::string& filename_out) { 3597 | std::ofstream file(m_output_path + filename_out); 3598 | file << render_file(filename, data); 3599 | file.close(); 3600 | } 3601 | 3602 | void write(const Template& temp, const json& data, const std::string& filename_out) { 3603 | std::ofstream file(m_output_path + filename_out); 3604 | file << render(temp, data); 3605 | file.close(); 3606 | } 3607 | 3608 | void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) { 3609 | const json data = load_json(filename_data); 3610 | write(filename, data, filename_out); 3611 | } 3612 | 3613 | void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) { 3614 | const json data = load_json(filename_data); 3615 | write(temp, data, filename_out); 3616 | } 3617 | 3618 | std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) { 3619 | Renderer(m_included_templates, m_callbacks).render_to(os, tmpl, data); 3620 | return os; 3621 | } 3622 | 3623 | std::string load_file(const std::string& filename) { 3624 | Parser parser(m_parser_config, m_lexer_config, m_included_templates); 3625 | return parser.load_file(m_input_path + filename); 3626 | } 3627 | 3628 | json load_json(const std::string& filename) { 3629 | std::ifstream file = open_file_or_throw(m_input_path + filename); 3630 | json j; 3631 | file >> j; 3632 | return j; 3633 | } 3634 | 3635 | void add_callback(const std::string& name, unsigned int numArgs, const CallbackFunction& callback) { 3636 | m_callbacks.add_callback(name, numArgs, callback); 3637 | } 3638 | 3639 | /** Includes a template with a given name into the environment. 3640 | * Then, a template can be rendered in another template using the 3641 | * include "" syntax. 3642 | */ 3643 | void include_template(const std::string& name, const Template& tmpl) { 3644 | m_included_templates[name] = tmpl; 3645 | } 3646 | 3647 | private: 3648 | std::string m_input_path; 3649 | std::string m_output_path; 3650 | 3651 | LexerConfig m_lexer_config; 3652 | ParserConfig m_parser_config; 3653 | 3654 | FunctionStorage m_callbacks; 3655 | TemplateStorage m_included_templates; 3656 | }; 3657 | 3658 | /*! 3659 | @brief render with default settings to a string 3660 | */ 3661 | inline std::string render(nonstd::string_view input, const json& data) { 3662 | return Environment().render(input, data); 3663 | } 3664 | 3665 | /*! 3666 | @brief render with default settings to the given output stream 3667 | */ 3668 | inline void render_to(std::ostream& os, nonstd::string_view input, const json& data) { 3669 | Environment env; 3670 | env.render_to(os, env.parse(input), data); 3671 | } 3672 | 3673 | } 3674 | 3675 | #endif // INCLUDE_INJA_ENVIRONMENT_HPP_ 3676 | 3677 | // #include "string_view.hpp" 3678 | 3679 | // #include "template.hpp" 3680 | 3681 | // #include "parser.hpp" 3682 | 3683 | // #include "renderer.hpp" 3684 | 3685 | 3686 | 3687 | #endif // INCLUDE_INJA_INJA_HPP_ 3688 | -------------------------------------------------------------------------------- /include/cuda_jit.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by egi on 1/18/20. 3 | // 4 | 5 | #ifndef CUDA_JIT_H 6 | #define CUDA_JIT_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "nlohmann/json.hpp" 13 | 14 | #include /// For dim3 15 | 16 | #include "map.h" 17 | 18 | namespace cuda_jit 19 | { 20 | 21 | class kernel_base 22 | { 23 | class kernel_impl; 24 | 25 | public: 26 | kernel_base (kernel_base &&); 27 | kernel_base (const std::string &kernel_name, std::unique_ptr ptx_arg); 28 | ~kernel_base (); 29 | 30 | protected: 31 | void launch_base (dim3 grid_size, dim3 block_size, void **params); 32 | 33 | private: 34 | std::unique_ptr ptx; 35 | std::unique_ptr impl; 36 | }; 37 | 38 | template 39 | class kernel : public kernel_base 40 | { 41 | std::vector params; 42 | 43 | public: 44 | kernel (kernel &&) = default; 45 | explicit kernel (const std::string &kernel_name, std::unique_ptr ptx_arg) 46 | : kernel_base (kernel_name, std::move (ptx_arg)) 47 | { } 48 | 49 | void launch (dim3 grid_size, dim3 block_size, args_types... args) 50 | { 51 | params = { &args... }; 52 | kernel_base::launch_base (grid_size, block_size, params.data ()); 53 | } 54 | }; 55 | 56 | class cuda_jit_base 57 | { 58 | protected: 59 | const std::string name; 60 | const std::string params; 61 | const std::string body_template; 62 | 63 | public: 64 | cuda_jit_base ( 65 | const std::string &kernel_name, 66 | const std::string &kernel_params, 67 | const std::string &kernel_body) 68 | : name (kernel_name), params (kernel_params), body_template (kernel_body) 69 | { 70 | } 71 | 72 | protected: 73 | std::unique_ptr compile_base (const nlohmann::json &json); 74 | 75 | private: 76 | std::string gen_kernel (const nlohmann::json &json) const; 77 | }; 78 | 79 | template 80 | class cuda_jit : public cuda_jit_base 81 | { 82 | public: 83 | cuda_jit ( 84 | const std::string &kernel_name, 85 | const std::string &kernel_params, 86 | const std::string &kernel_body) 87 | : cuda_jit_base (kernel_name, kernel_params, kernel_body) 88 | {} 89 | 90 | kernel compile (const nlohmann::json &json) 91 | { 92 | return kernel (name, cuda_jit_base::compile_base (json)); 93 | } 94 | }; 95 | 96 | } 97 | 98 | #define GET_FIRST(f, s) f 99 | #define APPLY_TO_PAIR(function, pair) function pair 100 | #define GET_FIRST_FROM_PAIR(pair) APPLY_TO_PAIR(GET_FIRST, pair) 101 | 102 | #define CONCAT_PAIR_IMPL(f, s) f s 103 | #define CONCAT_PAIR(pair) CONCAT_PAIR_IMPL pair 104 | 105 | #define DEFER_STRINGIFY(args...) STRINGIFY(args) 106 | #define STRINGIFY(args...) #args 107 | 108 | #define jit(name, body, args...) cuda_jit::cuda_jit name (#name, DEFER_STRINGIFY(MAP_LIST(CONCAT_PAIR, args)), #body) 109 | 110 | #endif // CUDA_JIT_H 111 | -------------------------------------------------------------------------------- /include/map.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 William Swanson 3 | * 4 | * Permission is hereby granted, free of charge, to any person 5 | * obtaining a copy of this software and associated documentation 6 | * files (the "Software"), to deal in the Software without 7 | * restriction, including without limitation the rights to use, copy, 8 | * modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | * 23 | * Except as contained in this notice, the names of the authors or 24 | * their institutions shall not be used in advertising or otherwise to 25 | * promote the sale, use or other dealings in this Software without 26 | * prior written authorization from the authors. 27 | */ 28 | 29 | #ifndef MAP_H_INCLUDED 30 | #define MAP_H_INCLUDED 31 | 32 | #define EVAL0(...) __VA_ARGS__ 33 | #define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__))) 34 | #define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) 35 | #define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) 36 | #define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) 37 | #define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) 38 | 39 | #define MAP_END(...) 40 | #define MAP_OUT 41 | #define MAP_COMMA , 42 | 43 | #define MAP_GET_END2() 0, MAP_END 44 | #define MAP_GET_END1(...) MAP_GET_END2 45 | #define MAP_GET_END(...) MAP_GET_END1 46 | #define MAP_NEXT0(test, next, ...) next MAP_OUT 47 | #define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0) 48 | #define MAP_NEXT(test, next) MAP_NEXT1(MAP_GET_END test, next) 49 | 50 | #define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__) 51 | #define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__) 52 | 53 | #define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0) 54 | #define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next) 55 | 56 | #define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__) 57 | #define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__) 58 | 59 | /** 60 | * Applies the function macro `f` to each of the remaining parameters. 61 | */ 62 | #define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) 63 | 64 | /** 65 | * Applies the function macro `f` to each of the remaining parameters and 66 | * inserts commas between the results. 67 | */ 68 | #define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /src/cuda_jit.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by egi on 1/18/20. 3 | // 4 | 5 | #include "cuda_jit.h" 6 | #include "inja.hpp" 7 | 8 | #include 9 | #include 10 | 11 | namespace cuda_jit 12 | { 13 | 14 | void throw_on_error (nvrtcResult result) 15 | { 16 | if (result != NVRTC_SUCCESS) 17 | throw std::runtime_error (std::string ("NVRTC ERRROR: ") + nvrtcGetErrorString (result)); 18 | } 19 | 20 | void throw_on_error (CUresult result) 21 | { 22 | if (result != CUDA_SUCCESS) 23 | { 24 | const char *msg; 25 | cuGetErrorName (result, &msg); 26 | throw std::runtime_error (std::string ("CUDA ERRROR: ") + std::string (msg)); 27 | } 28 | } 29 | 30 | class kernel_base::kernel_impl 31 | { 32 | public: 33 | CUmodule kernel_module; 34 | CUfunction kernel_fn; 35 | }; 36 | 37 | kernel_base::kernel_base (kernel_base &&) = default; 38 | kernel_base::kernel_base (const std::string &kernel_name, std::unique_ptr ptx_arg) 39 | : ptx (std::move (ptx_arg)) 40 | , impl (new kernel_base::kernel_impl ()) 41 | { 42 | throw_on_error (cuModuleLoadDataEx (&impl->kernel_module, ptx.get (), 0, nullptr, nullptr)); 43 | throw_on_error (cuModuleGetFunction (&impl->kernel_fn, impl->kernel_module, kernel_name.c_str ())); 44 | } 45 | 46 | kernel_base::~kernel_base () 47 | { 48 | cuModuleUnload (impl->kernel_module); 49 | } 50 | 51 | 52 | std::string cuda_jit_base::gen_kernel (const nlohmann::json &json) const 53 | { 54 | const std::string body = inja::render (body_template, json); 55 | return "extern \"C\" __global__ void " + name + " (" + params + ")" + body; 56 | } 57 | 58 | std::unique_ptr cuda_jit_base::compile_base (const nlohmann::json &json) 59 | { 60 | cudaFree (0); 61 | const std::string kernel_source = gen_kernel (json); 62 | 63 | nvrtcProgram prog; 64 | throw_on_error (nvrtcCreateProgram (&prog, kernel_source.c_str (), name.c_str (), 0, nullptr, nullptr)); 65 | 66 | try 67 | { 68 | const char *options[] = { "--gpu-architecture=compute_75", "-fmad=true" }; 69 | throw_on_error (nvrtcCompileProgram (prog, 2, options)); 70 | } 71 | catch (...) 72 | { 73 | size_t log_size {}; 74 | throw_on_error (nvrtcGetProgramLogSize (prog, &log_size)); 75 | 76 | std::unique_ptr log (new char[log_size]); 77 | throw_on_error (nvrtcGetProgramLog (prog, log.get ())); 78 | 79 | nvrtcDestroyProgram (&prog); 80 | throw std::runtime_error ("Compilation fail: " + std::string (log.get ())); 81 | } 82 | 83 | size_t ptx_size {}; 84 | throw_on_error (nvrtcGetPTXSize (prog, &ptx_size)); 85 | 86 | std::unique_ptr ptx (new char[ptx_size]); 87 | throw_on_error (nvrtcGetPTX (prog, ptx.get ())); 88 | throw_on_error (nvrtcDestroyProgram (&prog)); 89 | 90 | return std::move (ptx); 91 | } 92 | 93 | void kernel_base::launch_base (dim3 grid_size, dim3 block_size, void **params) 94 | { 95 | throw_on_error (cuLaunchKernel (impl->kernel_fn, grid_size.x, grid_size.y, grid_size.z, block_size.x, block_size.y, block_size.z, 0, 0, params, nullptr)); 96 | } 97 | 98 | } 99 | --------------------------------------------------------------------------------