cactus-react-native 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/LICENSE.txt +20 -0
  2. package/README.md +3 -1
  3. package/android/src/main/CMakeLists.txt +58 -23
  4. package/android/src/main/java/com/cactus/Cactus.java +484 -16
  5. package/android/src/main/java/com/cactus/LlamaContext.java +199 -0
  6. package/android/src/main/jni.cpp +325 -10
  7. package/android/src/main/jniLibs/arm64-v8a/libcactus.so +0 -0
  8. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8.so +0 -0
  9. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2.so +0 -0
  10. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2_dotprod.so +0 -0
  11. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2_dotprod_i8mm.so +0 -0
  12. package/android/src/main/jniLibs/arm64-v8a/libcactus_v8_2_i8mm.so +0 -0
  13. package/android/src/main/jniLibs/x86_64/libcactus.so +0 -0
  14. package/android/src/main/jniLibs/x86_64/libcactus_x86_64.so +0 -0
  15. package/android/src/newarch/java/com/cactus/CactusModule.java +79 -7
  16. package/android/src/oldarch/java/com/cactus/CactusModule.java +70 -0
  17. package/cactus-react-native.podspec +0 -3
  18. package/ios/CMakeLists.txt +58 -36
  19. package/ios/Cactus.mm +243 -2
  20. package/ios/CactusContext.h +22 -0
  21. package/ios/CactusContext.mm +176 -1
  22. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus.h +92 -5
  23. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_ffi.h +268 -0
  24. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/chat.h +2 -0
  25. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/common.h +42 -51
  26. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ggml-backend.h +4 -4
  27. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ggml-common.h +12 -6
  28. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ggml-cpp.h +1 -1
  29. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ggml-cpu.h +5 -0
  30. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ggml-impl.h +52 -18
  31. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ggml-metal-impl.h +106 -14
  32. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ggml-opt.h +49 -28
  33. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ggml.h +87 -106
  34. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama-arch.h +16 -0
  35. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama-batch.h +2 -1
  36. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama-chat.h +7 -2
  37. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama-context.h +44 -33
  38. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama-cparams.h +1 -0
  39. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama-graph.h +83 -17
  40. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama-hparams.h +44 -2
  41. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama-kv-cache.h +407 -179
  42. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama-memory.h +13 -2
  43. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama-model-loader.h +5 -3
  44. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama-model-saver.h +37 -0
  45. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama-model.h +24 -2
  46. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama-vocab.h +6 -0
  47. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/llama.h +102 -142
  48. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/minja/chat-template.hpp +23 -11
  49. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/minja/minja.hpp +186 -127
  50. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Info.plist +0 -0
  51. package/ios/cactus.xcframework/ios-arm64/cactus.framework/cactus +0 -0
  52. package/ios/cactus.xcframework/ios-arm64/cactus.framework/ggml-llama.metallib +0 -0
  53. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/cactus.h +92 -5
  54. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/cactus_ffi.h +268 -0
  55. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/chat.h +2 -0
  56. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/common.h +42 -51
  57. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/ggml-backend.h +4 -4
  58. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/ggml-common.h +12 -6
  59. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/ggml-cpp.h +1 -1
  60. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/ggml-cpu.h +5 -0
  61. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/ggml-impl.h +52 -18
  62. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/ggml-metal-impl.h +106 -14
  63. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/ggml-opt.h +49 -28
  64. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/ggml.h +87 -106
  65. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama-arch.h +16 -0
  66. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama-batch.h +2 -1
  67. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama-chat.h +7 -2
  68. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama-context.h +44 -33
  69. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama-cparams.h +1 -0
  70. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama-graph.h +83 -17
  71. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama-hparams.h +44 -2
  72. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama-kv-cache.h +407 -179
  73. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama-memory.h +13 -2
  74. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama-model-loader.h +5 -3
  75. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama-model-saver.h +37 -0
  76. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama-model.h +24 -2
  77. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama-vocab.h +6 -0
  78. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/llama.h +102 -142
  79. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/minja/chat-template.hpp +23 -11
  80. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/minja/minja.hpp +186 -127
  81. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Info.plist +0 -0
  82. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/_CodeSignature/CodeResources +1 -1
  83. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/cactus +0 -0
  84. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/ggml-llama-sim.metallib +0 -0
  85. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/cactus.h +92 -5
  86. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/cactus_ffi.h +268 -0
  87. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/chat.h +2 -0
  88. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/common.h +42 -51
  89. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/ggml-backend.h +4 -4
  90. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/ggml-common.h +12 -6
  91. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/ggml-cpp.h +1 -1
  92. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/ggml-cpu.h +5 -0
  93. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/ggml-impl.h +52 -18
  94. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/ggml-metal-impl.h +106 -14
  95. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/ggml-opt.h +49 -28
  96. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/ggml.h +87 -106
  97. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama-arch.h +16 -0
  98. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama-batch.h +2 -1
  99. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama-chat.h +7 -2
  100. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama-context.h +44 -33
  101. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama-cparams.h +1 -0
  102. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama-graph.h +83 -17
  103. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama-hparams.h +44 -2
  104. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama-kv-cache.h +407 -179
  105. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama-memory.h +13 -2
  106. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama-model-loader.h +5 -3
  107. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama-model-saver.h +37 -0
  108. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama-model.h +24 -2
  109. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama-vocab.h +6 -0
  110. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/llama.h +102 -142
  111. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/minja/chat-template.hpp +23 -11
  112. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/minja/minja.hpp +186 -127
  113. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Info.plist +0 -0
  114. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/cactus +0 -0
  115. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/ggml-llama.metallib +0 -0
  116. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/cactus.h +92 -5
  117. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/cactus_ffi.h +268 -0
  118. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/chat.h +2 -0
  119. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/common.h +42 -51
  120. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/ggml-backend.h +4 -4
  121. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/ggml-common.h +12 -6
  122. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/ggml-cpp.h +1 -1
  123. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/ggml-cpu.h +5 -0
  124. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/ggml-impl.h +52 -18
  125. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/ggml-metal-impl.h +106 -14
  126. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/ggml-opt.h +49 -28
  127. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/ggml.h +87 -106
  128. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama-arch.h +16 -0
  129. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama-batch.h +2 -1
  130. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama-chat.h +7 -2
  131. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama-context.h +44 -33
  132. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama-cparams.h +1 -0
  133. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama-graph.h +83 -17
  134. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama-hparams.h +44 -2
  135. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama-kv-cache.h +407 -179
  136. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama-memory.h +13 -2
  137. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama-model-loader.h +5 -3
  138. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama-model-saver.h +37 -0
  139. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama-model.h +24 -2
  140. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama-vocab.h +6 -0
  141. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/llama.h +102 -142
  142. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/minja/chat-template.hpp +23 -11
  143. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/minja/minja.hpp +186 -127
  144. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Info.plist +0 -0
  145. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/_CodeSignature/CodeResources +1 -1
  146. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/cactus +0 -0
  147. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/ggml-llama-sim.metallib +0 -0
  148. package/lib/commonjs/NativeCactus.js +1 -0
  149. package/lib/commonjs/NativeCactus.js.map +1 -1
  150. package/lib/commonjs/index.js +112 -0
  151. package/lib/commonjs/index.js.map +1 -1
  152. package/lib/commonjs/tools.js +118 -0
  153. package/lib/commonjs/tools.js.map +1 -0
  154. package/lib/module/NativeCactus.js +3 -0
  155. package/lib/module/NativeCactus.js.map +1 -1
  156. package/lib/module/index.js +87 -1
  157. package/lib/module/index.js.map +1 -1
  158. package/lib/module/tools.js +110 -0
  159. package/lib/module/tools.js.map +1 -0
  160. package/lib/typescript/NativeCactus.d.ts +30 -1
  161. package/lib/typescript/NativeCactus.d.ts.map +1 -1
  162. package/lib/typescript/index.d.ts +21 -2
  163. package/lib/typescript/index.d.ts.map +1 -1
  164. package/lib/typescript/tools.d.ts +38 -0
  165. package/lib/typescript/tools.d.ts.map +1 -0
  166. package/package.json +6 -3
  167. package/src/NativeCactus.ts +62 -1
  168. package/src/index.ts +113 -2
  169. package/src/tools.ts +127 -0
  170. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ggml-cpu-aarch64.h +0 -8
  171. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ggml-cpu-impl.h +0 -531
  172. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ggml-cpu-quants.h +0 -63
  173. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ggml-cpu-traits.h +0 -38
  174. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/sgemm.h +0 -14
  175. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/ggml-cpu-aarch64.h +0 -8
  176. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/ggml-cpu-impl.h +0 -531
  177. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/ggml-cpu-quants.h +0 -63
  178. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/ggml-cpu-traits.h +0 -38
  179. package/ios/cactus.xcframework/ios-arm64_x86_64-simulator/cactus.framework/Headers/sgemm.h +0 -14
  180. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/ggml-cpu-aarch64.h +0 -8
  181. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/ggml-cpu-impl.h +0 -531
  182. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/ggml-cpu-quants.h +0 -63
  183. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/ggml-cpu-traits.h +0 -38
  184. package/ios/cactus.xcframework/tvos-arm64/cactus.framework/Headers/sgemm.h +0 -14
  185. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/ggml-cpu-aarch64.h +0 -8
  186. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/ggml-cpu-impl.h +0 -531
  187. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/ggml-cpu-quants.h +0 -63
  188. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/ggml-cpu-traits.h +0 -38
  189. package/ios/cactus.xcframework/tvos-arm64_x86_64-simulator/cactus.framework/Headers/sgemm.h +0 -14
@@ -8,14 +8,27 @@
8
8
  // SPDX-License-Identifier: MIT
9
9
  #pragma once
10
10
 
11
+ #include <algorithm>
12
+ #include <cctype>
13
+ #include <cstddef>
14
+ #include <cstdint>
15
+ #include <cmath>
16
+ #include <exception>
17
+ #include <functional>
11
18
  #include <iostream>
12
- #include <string>
13
- #include <vector>
14
- #include <regex>
19
+ #include <iterator>
20
+ #include <limits>
21
+ #include <map>
15
22
  #include <memory>
16
- #include <stdexcept>
23
+ #include <regex>
17
24
  #include <sstream>
25
+ #include <string>
26
+ #include <stdexcept>
27
+ #include <unordered_map>
18
28
  #include <unordered_set>
29
+ #include <utility>
30
+ #include <vector>
31
+
19
32
  #include "../json.hpp"
20
33
 
21
34
  using json = nlohmann::ordered_json;
@@ -221,7 +234,7 @@ public:
221
234
  }
222
235
  } else if (is_object()) {
223
236
  if (!index.is_hashable())
224
- throw std::runtime_error("Unashable type: " + index.dump());
237
+ throw std::runtime_error("Unhashable type: " + index.dump());
225
238
  auto it = object_->find(index.primitive_);
226
239
  if (it == object_->end())
227
240
  throw std::runtime_error("Key not found: " + index.dump());
@@ -240,7 +253,7 @@ public:
240
253
  auto index = key.get<int>();
241
254
  return array_->at(index < 0 ? array_->size() + index : index);
242
255
  } else if (object_) {
243
- if (!key.is_hashable()) throw std::runtime_error("Unashable type: " + dump());
256
+ if (!key.is_hashable()) throw std::runtime_error("Unhashable type: " + dump());
244
257
  auto it = object_->find(key.primitive_);
245
258
  if (it == object_->end()) return Value();
246
259
  return it->second;
@@ -249,7 +262,7 @@ public:
249
262
  }
250
263
  void set(const Value& key, const Value& value) {
251
264
  if (!object_) throw std::runtime_error("Value is not an object: " + dump());
252
- if (!key.is_hashable()) throw std::runtime_error("Unashable type: " + dump());
265
+ if (!key.is_hashable()) throw std::runtime_error("Unhashable type: " + dump());
253
266
  (*object_)[key.primitive_] = value;
254
267
  }
255
268
  Value call(const std::shared_ptr<Context> & context, ArgumentsValue & args) const {
@@ -386,7 +399,7 @@ public:
386
399
  }
387
400
  return false;
388
401
  } else if (object_) {
389
- if (!value.is_hashable()) throw std::runtime_error("Unashable type: " + value.dump());
402
+ if (!value.is_hashable()) throw std::runtime_error("Unhashable type: " + value.dump());
390
403
  return object_->find(value.primitive_) != object_->end();
391
404
  } else {
392
405
  throw std::runtime_error("contains can only be called on arrays and objects: " + dump());
@@ -404,7 +417,7 @@ public:
404
417
  return const_cast<Value*>(this)->at(index);
405
418
  }
406
419
  Value& at(const Value & index) {
407
- if (!index.is_hashable()) throw std::runtime_error("Unashable type: " + dump());
420
+ if (!index.is_hashable()) throw std::runtime_error("Unhashable type: " + dump());
408
421
  if (is_array()) return array_->at(index.get<int>());
409
422
  if (is_object()) return object_->at(index.primitive_);
410
423
  throw std::runtime_error("Value is not an array or object: " + dump());
@@ -664,8 +677,8 @@ public:
664
677
  class VariableExpr : public Expression {
665
678
  std::string name;
666
679
  public:
667
- VariableExpr(const Location & location, const std::string& n)
668
- : Expression(location), name(n) {}
680
+ VariableExpr(const Location & loc, const std::string& n)
681
+ : Expression(loc), name(n) {}
669
682
  std::string get_name() const { return name; }
670
683
  Value do_evaluate(const std::shared_ptr<Context> & context) const override {
671
684
  if (!context->contains(name)) {
@@ -731,51 +744,51 @@ public:
731
744
 
732
745
  struct TextTemplateToken : public TemplateToken {
733
746
  std::string text;
734
- TextTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post, const std::string& t) : TemplateToken(Type::Text, location, pre, post), text(t) {}
747
+ TextTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post, const std::string& t) : TemplateToken(Type::Text, loc, pre, post), text(t) {}
735
748
  };
736
749
 
737
750
  struct ExpressionTemplateToken : public TemplateToken {
738
751
  std::shared_ptr<Expression> expr;
739
- ExpressionTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post, std::shared_ptr<Expression> && e) : TemplateToken(Type::Expression, location, pre, post), expr(std::move(e)) {}
752
+ ExpressionTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post, std::shared_ptr<Expression> && e) : TemplateToken(Type::Expression, loc, pre, post), expr(std::move(e)) {}
740
753
  };
741
754
 
742
755
  struct IfTemplateToken : public TemplateToken {
743
756
  std::shared_ptr<Expression> condition;
744
- IfTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post, std::shared_ptr<Expression> && c) : TemplateToken(Type::If, location, pre, post), condition(std::move(c)) {}
757
+ IfTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post, std::shared_ptr<Expression> && c) : TemplateToken(Type::If, loc, pre, post), condition(std::move(c)) {}
745
758
  };
746
759
 
747
760
  struct ElifTemplateToken : public TemplateToken {
748
761
  std::shared_ptr<Expression> condition;
749
- ElifTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post, std::shared_ptr<Expression> && c) : TemplateToken(Type::Elif, location, pre, post), condition(std::move(c)) {}
762
+ ElifTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post, std::shared_ptr<Expression> && c) : TemplateToken(Type::Elif, loc, pre, post), condition(std::move(c)) {}
750
763
  };
751
764
 
752
765
  struct ElseTemplateToken : public TemplateToken {
753
- ElseTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::Else, location, pre, post) {}
766
+ ElseTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::Else, loc, pre, post) {}
754
767
  };
755
768
 
756
769
  struct EndIfTemplateToken : public TemplateToken {
757
- EndIfTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::EndIf, location, pre, post) {}
770
+ EndIfTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::EndIf, loc, pre, post) {}
758
771
  };
759
772
 
760
773
  struct MacroTemplateToken : public TemplateToken {
761
774
  std::shared_ptr<VariableExpr> name;
762
775
  Expression::Parameters params;
763
- MacroTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post, std::shared_ptr<VariableExpr> && n, Expression::Parameters && p)
764
- : TemplateToken(Type::Macro, location, pre, post), name(std::move(n)), params(std::move(p)) {}
776
+ MacroTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post, std::shared_ptr<VariableExpr> && n, Expression::Parameters && p)
777
+ : TemplateToken(Type::Macro, loc, pre, post), name(std::move(n)), params(std::move(p)) {}
765
778
  };
766
779
 
767
780
  struct EndMacroTemplateToken : public TemplateToken {
768
- EndMacroTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::EndMacro, location, pre, post) {}
781
+ EndMacroTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::EndMacro, loc, pre, post) {}
769
782
  };
770
783
 
771
784
  struct FilterTemplateToken : public TemplateToken {
772
785
  std::shared_ptr<Expression> filter;
773
- FilterTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post, std::shared_ptr<Expression> && filter)
774
- : TemplateToken(Type::Filter, location, pre, post), filter(std::move(filter)) {}
786
+ FilterTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post, std::shared_ptr<Expression> && filter)
787
+ : TemplateToken(Type::Filter, loc, pre, post), filter(std::move(filter)) {}
775
788
  };
776
789
 
777
790
  struct EndFilterTemplateToken : public TemplateToken {
778
- EndFilterTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::EndFilter, location, pre, post) {}
791
+ EndFilterTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::EndFilter, loc, pre, post) {}
779
792
  };
780
793
 
781
794
  struct ForTemplateToken : public TemplateToken {
@@ -783,38 +796,38 @@ struct ForTemplateToken : public TemplateToken {
783
796
  std::shared_ptr<Expression> iterable;
784
797
  std::shared_ptr<Expression> condition;
785
798
  bool recursive;
786
- ForTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post, const std::vector<std::string> & vns, std::shared_ptr<Expression> && iter,
799
+ ForTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post, const std::vector<std::string> & vns, std::shared_ptr<Expression> && iter,
787
800
  std::shared_ptr<Expression> && c, bool r)
788
- : TemplateToken(Type::For, location, pre, post), var_names(vns), iterable(std::move(iter)), condition(std::move(c)), recursive(r) {}
801
+ : TemplateToken(Type::For, loc, pre, post), var_names(vns), iterable(std::move(iter)), condition(std::move(c)), recursive(r) {}
789
802
  };
790
803
 
791
804
  struct EndForTemplateToken : public TemplateToken {
792
- EndForTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::EndFor, location, pre, post) {}
805
+ EndForTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::EndFor, loc, pre, post) {}
793
806
  };
794
807
 
795
808
  struct GenerationTemplateToken : public TemplateToken {
796
- GenerationTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::Generation, location, pre, post) {}
809
+ GenerationTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::Generation, loc, pre, post) {}
797
810
  };
798
811
 
799
812
  struct EndGenerationTemplateToken : public TemplateToken {
800
- EndGenerationTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::EndGeneration, location, pre, post) {}
813
+ EndGenerationTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::EndGeneration, loc, pre, post) {}
801
814
  };
802
815
 
803
816
  struct SetTemplateToken : public TemplateToken {
804
817
  std::string ns;
805
818
  std::vector<std::string> var_names;
806
819
  std::shared_ptr<Expression> value;
807
- SetTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post, const std::string & ns, const std::vector<std::string> & vns, std::shared_ptr<Expression> && v)
808
- : TemplateToken(Type::Set, location, pre, post), ns(ns), var_names(vns), value(std::move(v)) {}
820
+ SetTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post, const std::string & ns, const std::vector<std::string> & vns, std::shared_ptr<Expression> && v)
821
+ : TemplateToken(Type::Set, loc, pre, post), ns(ns), var_names(vns), value(std::move(v)) {}
809
822
  };
810
823
 
811
824
  struct EndSetTemplateToken : public TemplateToken {
812
- EndSetTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::EndSet, location, pre, post) {}
825
+ EndSetTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post) : TemplateToken(Type::EndSet, loc, pre, post) {}
813
826
  };
814
827
 
815
828
  struct CommentTemplateToken : public TemplateToken {
816
829
  std::string text;
817
- CommentTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post, const std::string& t) : TemplateToken(Type::Comment, location, pre, post), text(t) {}
830
+ CommentTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post, const std::string& t) : TemplateToken(Type::Comment, loc, pre, post), text(t) {}
818
831
  };
819
832
 
820
833
  enum class LoopControlType { Break, Continue };
@@ -830,7 +843,7 @@ public:
830
843
 
831
844
  struct LoopControlTemplateToken : public TemplateToken {
832
845
  LoopControlType control_type;
833
- LoopControlTemplateToken(const Location & location, SpaceHandling pre, SpaceHandling post, LoopControlType control_type) : TemplateToken(Type::Break, location, pre, post), control_type(control_type) {}
846
+ LoopControlTemplateToken(const Location & loc, SpaceHandling pre, SpaceHandling post, LoopControlType control_type) : TemplateToken(Type::Break, loc, pre, post), control_type(control_type) {}
834
847
  };
835
848
 
836
849
  class TemplateNode {
@@ -868,8 +881,8 @@ public:
868
881
  class SequenceNode : public TemplateNode {
869
882
  std::vector<std::shared_ptr<TemplateNode>> children;
870
883
  public:
871
- SequenceNode(const Location & location, std::vector<std::shared_ptr<TemplateNode>> && c)
872
- : TemplateNode(location), children(std::move(c)) {}
884
+ SequenceNode(const Location & loc, std::vector<std::shared_ptr<TemplateNode>> && c)
885
+ : TemplateNode(loc), children(std::move(c)) {}
873
886
  void do_render(std::ostringstream & out, const std::shared_ptr<Context> & context) const override {
874
887
  for (const auto& child : children) child->render(out, context);
875
888
  }
@@ -878,7 +891,7 @@ public:
878
891
  class TextNode : public TemplateNode {
879
892
  std::string text;
880
893
  public:
881
- TextNode(const Location & location, const std::string& t) : TemplateNode(location), text(t) {}
894
+ TextNode(const Location & loc, const std::string& t) : TemplateNode(loc), text(t) {}
882
895
  void do_render(std::ostringstream & out, const std::shared_ptr<Context> &) const override {
883
896
  out << text;
884
897
  }
@@ -887,7 +900,7 @@ public:
887
900
  class ExpressionNode : public TemplateNode {
888
901
  std::shared_ptr<Expression> expr;
889
902
  public:
890
- ExpressionNode(const Location & location, std::shared_ptr<Expression> && e) : TemplateNode(location), expr(std::move(e)) {}
903
+ ExpressionNode(const Location & loc, std::shared_ptr<Expression> && e) : TemplateNode(loc), expr(std::move(e)) {}
891
904
  void do_render(std::ostringstream & out, const std::shared_ptr<Context> & context) const override {
892
905
  if (!expr) throw std::runtime_error("ExpressionNode.expr is null");
893
906
  auto result = expr->evaluate(context);
@@ -904,8 +917,8 @@ public:
904
917
  class IfNode : public TemplateNode {
905
918
  std::vector<std::pair<std::shared_ptr<Expression>, std::shared_ptr<TemplateNode>>> cascade;
906
919
  public:
907
- IfNode(const Location & location, std::vector<std::pair<std::shared_ptr<Expression>, std::shared_ptr<TemplateNode>>> && c)
908
- : TemplateNode(location), cascade(std::move(c)) {}
920
+ IfNode(const Location & loc, std::vector<std::pair<std::shared_ptr<Expression>, std::shared_ptr<TemplateNode>>> && c)
921
+ : TemplateNode(loc), cascade(std::move(c)) {}
909
922
  void do_render(std::ostringstream & out, const std::shared_ptr<Context> & context) const override {
910
923
  for (const auto& branch : cascade) {
911
924
  auto enter_branch = true;
@@ -924,7 +937,7 @@ public:
924
937
  class LoopControlNode : public TemplateNode {
925
938
  LoopControlType control_type_;
926
939
  public:
927
- LoopControlNode(const Location & location, LoopControlType control_type) : TemplateNode(location), control_type_(control_type) {}
940
+ LoopControlNode(const Location & loc, LoopControlType control_type) : TemplateNode(loc), control_type_(control_type) {}
928
941
  void do_render(std::ostringstream &, const std::shared_ptr<Context> &) const override {
929
942
  throw LoopControlException(control_type_);
930
943
  }
@@ -938,9 +951,9 @@ class ForNode : public TemplateNode {
938
951
  bool recursive;
939
952
  std::shared_ptr<TemplateNode> else_body;
940
953
  public:
941
- ForNode(const Location & location, std::vector<std::string> && var_names, std::shared_ptr<Expression> && iterable,
954
+ ForNode(const Location & loc, std::vector<std::string> && var_names, std::shared_ptr<Expression> && iterable,
942
955
  std::shared_ptr<Expression> && condition, std::shared_ptr<TemplateNode> && body, bool recursive, std::shared_ptr<TemplateNode> && else_body)
943
- : TemplateNode(location), var_names(var_names), iterable(std::move(iterable)), condition(std::move(condition)), body(std::move(body)), recursive(recursive), else_body(std::move(else_body)) {}
956
+ : TemplateNode(loc), var_names(var_names), iterable(std::move(iterable)), condition(std::move(condition)), body(std::move(body)), recursive(recursive), else_body(std::move(else_body)) {}
944
957
 
945
958
  void do_render(std::ostringstream & out, const std::shared_ptr<Context> & context) const override {
946
959
  // https://jinja.palletsprojects.com/en/3.0.x/templates/#for
@@ -1025,8 +1038,8 @@ class MacroNode : public TemplateNode {
1025
1038
  std::shared_ptr<TemplateNode> body;
1026
1039
  std::unordered_map<std::string, size_t> named_param_positions;
1027
1040
  public:
1028
- MacroNode(const Location & location, std::shared_ptr<VariableExpr> && n, Expression::Parameters && p, std::shared_ptr<TemplateNode> && b)
1029
- : TemplateNode(location), name(std::move(n)), params(std::move(p)), body(std::move(b)) {
1041
+ MacroNode(const Location & loc, std::shared_ptr<VariableExpr> && n, Expression::Parameters && p, std::shared_ptr<TemplateNode> && b)
1042
+ : TemplateNode(loc), name(std::move(n)), params(std::move(p)), body(std::move(b)) {
1030
1043
  for (size_t i = 0; i < params.size(); ++i) {
1031
1044
  const auto & name = params[i].first;
1032
1045
  if (!name.empty()) {
@@ -1072,8 +1085,8 @@ class FilterNode : public TemplateNode {
1072
1085
  std::shared_ptr<TemplateNode> body;
1073
1086
 
1074
1087
  public:
1075
- FilterNode(const Location & location, std::shared_ptr<Expression> && f, std::shared_ptr<TemplateNode> && b)
1076
- : TemplateNode(location), filter(std::move(f)), body(std::move(b)) {}
1088
+ FilterNode(const Location & loc, std::shared_ptr<Expression> && f, std::shared_ptr<TemplateNode> && b)
1089
+ : TemplateNode(loc), filter(std::move(f)), body(std::move(b)) {}
1077
1090
 
1078
1091
  void do_render(std::ostringstream & out, const std::shared_ptr<Context> & context) const override {
1079
1092
  if (!filter) throw std::runtime_error("FilterNode.filter is null");
@@ -1095,8 +1108,8 @@ class SetNode : public TemplateNode {
1095
1108
  std::vector<std::string> var_names;
1096
1109
  std::shared_ptr<Expression> value;
1097
1110
  public:
1098
- SetNode(const Location & location, const std::string & ns, const std::vector<std::string> & vns, std::shared_ptr<Expression> && v)
1099
- : TemplateNode(location), ns(ns), var_names(vns), value(std::move(v)) {}
1111
+ SetNode(const Location & loc, const std::string & ns, const std::vector<std::string> & vns, std::shared_ptr<Expression> && v)
1112
+ : TemplateNode(loc), ns(ns), var_names(vns), value(std::move(v)) {}
1100
1113
  void do_render(std::ostringstream &, const std::shared_ptr<Context> & context) const override {
1101
1114
  if (!value) throw std::runtime_error("SetNode.value is null");
1102
1115
  if (!ns.empty()) {
@@ -1118,8 +1131,8 @@ class SetTemplateNode : public TemplateNode {
1118
1131
  std::string name;
1119
1132
  std::shared_ptr<TemplateNode> template_value;
1120
1133
  public:
1121
- SetTemplateNode(const Location & location, const std::string & name, std::shared_ptr<TemplateNode> && tv)
1122
- : TemplateNode(location), name(name), template_value(std::move(tv)) {}
1134
+ SetTemplateNode(const Location & loc, const std::string & name, std::shared_ptr<TemplateNode> && tv)
1135
+ : TemplateNode(loc), name(name), template_value(std::move(tv)) {}
1123
1136
  void do_render(std::ostringstream &, const std::shared_ptr<Context> & context) const override {
1124
1137
  if (!template_value) throw std::runtime_error("SetTemplateNode.template_value is null");
1125
1138
  Value value { template_value->render(context) };
@@ -1132,8 +1145,8 @@ class IfExpr : public Expression {
1132
1145
  std::shared_ptr<Expression> then_expr;
1133
1146
  std::shared_ptr<Expression> else_expr;
1134
1147
  public:
1135
- IfExpr(const Location & location, std::shared_ptr<Expression> && c, std::shared_ptr<Expression> && t, std::shared_ptr<Expression> && e)
1136
- : Expression(location), condition(std::move(c)), then_expr(std::move(t)), else_expr(std::move(e)) {}
1148
+ IfExpr(const Location & loc, std::shared_ptr<Expression> && c, std::shared_ptr<Expression> && t, std::shared_ptr<Expression> && e)
1149
+ : Expression(loc), condition(std::move(c)), then_expr(std::move(t)), else_expr(std::move(e)) {}
1137
1150
  Value do_evaluate(const std::shared_ptr<Context> & context) const override {
1138
1151
  if (!condition) throw std::runtime_error("IfExpr.condition is null");
1139
1152
  if (!then_expr) throw std::runtime_error("IfExpr.then_expr is null");
@@ -1150,16 +1163,16 @@ public:
1150
1163
  class LiteralExpr : public Expression {
1151
1164
  Value value;
1152
1165
  public:
1153
- LiteralExpr(const Location & location, const Value& v)
1154
- : Expression(location), value(v) {}
1166
+ LiteralExpr(const Location & loc, const Value& v)
1167
+ : Expression(loc), value(v) {}
1155
1168
  Value do_evaluate(const std::shared_ptr<Context> &) const override { return value; }
1156
1169
  };
1157
1170
 
1158
1171
  class ArrayExpr : public Expression {
1159
1172
  std::vector<std::shared_ptr<Expression>> elements;
1160
1173
  public:
1161
- ArrayExpr(const Location & location, std::vector<std::shared_ptr<Expression>> && e)
1162
- : Expression(location), elements(std::move(e)) {}
1174
+ ArrayExpr(const Location & loc, std::vector<std::shared_ptr<Expression>> && e)
1175
+ : Expression(loc), elements(std::move(e)) {}
1163
1176
  Value do_evaluate(const std::shared_ptr<Context> & context) const override {
1164
1177
  auto result = Value::array();
1165
1178
  for (const auto& e : elements) {
@@ -1173,8 +1186,8 @@ public:
1173
1186
  class DictExpr : public Expression {
1174
1187
  std::vector<std::pair<std::shared_ptr<Expression>, std::shared_ptr<Expression>>> elements;
1175
1188
  public:
1176
- DictExpr(const Location & location, std::vector<std::pair<std::shared_ptr<Expression>, std::shared_ptr<Expression>>> && e)
1177
- : Expression(location), elements(std::move(e)) {}
1189
+ DictExpr(const Location & loc, std::vector<std::pair<std::shared_ptr<Expression>, std::shared_ptr<Expression>>> && e)
1190
+ : Expression(loc), elements(std::move(e)) {}
1178
1191
  Value do_evaluate(const std::shared_ptr<Context> & context) const override {
1179
1192
  auto result = Value::object();
1180
1193
  for (const auto& [key, value] : elements) {
@@ -1188,9 +1201,9 @@ public:
1188
1201
 
1189
1202
  class SliceExpr : public Expression {
1190
1203
  public:
1191
- std::shared_ptr<Expression> start, end;
1192
- SliceExpr(const Location & location, std::shared_ptr<Expression> && s, std::shared_ptr<Expression> && e)
1193
- : Expression(location), start(std::move(s)), end(std::move(e)) {}
1204
+ std::shared_ptr<Expression> start, end, step;
1205
+ SliceExpr(const Location & loc, std::shared_ptr<Expression> && s, std::shared_ptr<Expression> && e, std::shared_ptr<Expression> && st = nullptr)
1206
+ : Expression(loc), start(std::move(s)), end(std::move(e)), step(std::move(st)) {}
1194
1207
  Value do_evaluate(const std::shared_ptr<Context> &) const override {
1195
1208
  throw std::runtime_error("SliceExpr not implemented");
1196
1209
  }
@@ -1200,25 +1213,42 @@ class SubscriptExpr : public Expression {
1200
1213
  std::shared_ptr<Expression> base;
1201
1214
  std::shared_ptr<Expression> index;
1202
1215
  public:
1203
- SubscriptExpr(const Location & location, std::shared_ptr<Expression> && b, std::shared_ptr<Expression> && i)
1204
- : Expression(location), base(std::move(b)), index(std::move(i)) {}
1216
+ SubscriptExpr(const Location & loc, std::shared_ptr<Expression> && b, std::shared_ptr<Expression> && i)
1217
+ : Expression(loc), base(std::move(b)), index(std::move(i)) {}
1205
1218
  Value do_evaluate(const std::shared_ptr<Context> & context) const override {
1206
1219
  if (!base) throw std::runtime_error("SubscriptExpr.base is null");
1207
1220
  if (!index) throw std::runtime_error("SubscriptExpr.index is null");
1208
1221
  auto target_value = base->evaluate(context);
1209
1222
  if (auto slice = dynamic_cast<SliceExpr*>(index.get())) {
1210
- auto start = slice->start ? slice->start->evaluate(context).get<int64_t>() : 0;
1211
- auto end = slice->end ? slice->end->evaluate(context).get<int64_t>() : (int64_t) target_value.size();
1223
+ auto len = target_value.size();
1224
+ auto wrap = [len](int64_t i) -> int64_t {
1225
+ if (i < 0) {
1226
+ return i + len;
1227
+ }
1228
+ return i;
1229
+ };
1230
+ int64_t step = slice->step ? slice->step->evaluate(context).get<int64_t>() : 1;
1231
+ if (!step) {
1232
+ throw std::runtime_error("slice step cannot be zero");
1233
+ }
1234
+ int64_t start = slice->start ? wrap(slice->start->evaluate(context).get<int64_t>()) : (step < 0 ? len - 1 : 0);
1235
+ int64_t end = slice->end ? wrap(slice->end->evaluate(context).get<int64_t>()) : (step < 0 ? -1 : len);
1212
1236
  if (target_value.is_string()) {
1213
1237
  std::string s = target_value.get<std::string>();
1214
- if (start < 0) start = s.size() + start;
1215
- if (end < 0) end = s.size() + end;
1216
- return s.substr(start, end - start);
1238
+
1239
+ std::string result;
1240
+ if (start < end && step == 1) {
1241
+ result = s.substr(start, end - start);
1242
+ } else {
1243
+ for (int64_t i = start; step > 0 ? i < end : i > end; i += step) {
1244
+ result += s[i];
1245
+ }
1246
+ }
1247
+ return result;
1248
+
1217
1249
  } else if (target_value.is_array()) {
1218
- if (start < 0) start = target_value.size() + start;
1219
- if (end < 0) end = target_value.size() + end;
1220
1250
  auto result = Value::array();
1221
- for (auto i = start; i < end; ++i) {
1251
+ for (int64_t i = start; step > 0 ? i < end : i > end; i += step) {
1222
1252
  result.push_back(target_value.at(i));
1223
1253
  }
1224
1254
  return result;
@@ -1243,8 +1273,8 @@ public:
1243
1273
  enum class Op { Plus, Minus, LogicalNot, Expansion, ExpansionDict };
1244
1274
  std::shared_ptr<Expression> expr;
1245
1275
  Op op;
1246
- UnaryOpExpr(const Location & location, std::shared_ptr<Expression> && e, Op o)
1247
- : Expression(location), expr(std::move(e)), op(o) {}
1276
+ UnaryOpExpr(const Location & loc, std::shared_ptr<Expression> && e, Op o)
1277
+ : Expression(loc), expr(std::move(e)), op(o) {}
1248
1278
  Value do_evaluate(const std::shared_ptr<Context> & context) const override {
1249
1279
  if (!expr) throw std::runtime_error("UnaryOpExpr.expr is null");
1250
1280
  auto e = expr->evaluate(context);
@@ -1269,8 +1299,8 @@ private:
1269
1299
  std::shared_ptr<Expression> right;
1270
1300
  Op op;
1271
1301
  public:
1272
- BinaryOpExpr(const Location & location, std::shared_ptr<Expression> && l, std::shared_ptr<Expression> && r, Op o)
1273
- : Expression(location), left(std::move(l)), right(std::move(r)), op(o) {}
1302
+ BinaryOpExpr(const Location & loc, std::shared_ptr<Expression> && l, std::shared_ptr<Expression> && r, Op o)
1303
+ : Expression(loc), left(std::move(l)), right(std::move(r)), op(o) {}
1274
1304
  Value do_evaluate(const std::shared_ptr<Context> & context) const override {
1275
1305
  if (!left) throw std::runtime_error("BinaryOpExpr.left is null");
1276
1306
  if (!right) throw std::runtime_error("BinaryOpExpr.right is null");
@@ -1293,6 +1323,8 @@ public:
1293
1323
  if (name == "iterable") return l.is_iterable();
1294
1324
  if (name == "sequence") return l.is_array();
1295
1325
  if (name == "defined") return !l.is_null();
1326
+ if (name == "true") return l.to_bool();
1327
+ if (name == "false") return !l.to_bool();
1296
1328
  throw std::runtime_error("Unknown type for 'is' operator: " + name);
1297
1329
  };
1298
1330
  auto value = eval();
@@ -1427,8 +1459,8 @@ class MethodCallExpr : public Expression {
1427
1459
  std::shared_ptr<VariableExpr> method;
1428
1460
  ArgumentsExpression args;
1429
1461
  public:
1430
- MethodCallExpr(const Location & location, std::shared_ptr<Expression> && obj, std::shared_ptr<VariableExpr> && m, ArgumentsExpression && a)
1431
- : Expression(location), object(std::move(obj)), method(std::move(m)), args(std::move(a)) {}
1462
+ MethodCallExpr(const Location & loc, std::shared_ptr<Expression> && obj, std::shared_ptr<VariableExpr> && m, ArgumentsExpression && a)
1463
+ : Expression(loc), object(std::move(obj)), method(std::move(m)), args(std::move(a)) {}
1432
1464
  Value do_evaluate(const std::shared_ptr<Context> & context) const override {
1433
1465
  if (!object) throw std::runtime_error("MethodCallExpr.object is null");
1434
1466
  if (!method) throw std::runtime_error("MethodCallExpr.method is null");
@@ -1508,6 +1540,10 @@ public:
1508
1540
  vargs.expectArgs("endswith method", {1, 1}, {0, 0});
1509
1541
  auto suffix = vargs.args[0].get<std::string>();
1510
1542
  return suffix.length() <= str.length() && std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
1543
+ } else if (method->get_name() == "startswith") {
1544
+ vargs.expectArgs("startswith method", {1, 1}, {0, 0});
1545
+ auto prefix = vargs.args[0].get<std::string>();
1546
+ return prefix.length() <= str.length() && std::equal(prefix.begin(), prefix.end(), str.begin());
1511
1547
  } else if (method->get_name() == "title") {
1512
1548
  vargs.expectArgs("title method", {0, 0}, {0, 0});
1513
1549
  auto res = str;
@@ -1526,8 +1562,8 @@ class CallExpr : public Expression {
1526
1562
  public:
1527
1563
  std::shared_ptr<Expression> object;
1528
1564
  ArgumentsExpression args;
1529
- CallExpr(const Location & location, std::shared_ptr<Expression> && obj, ArgumentsExpression && a)
1530
- : Expression(location), object(std::move(obj)), args(std::move(a)) {}
1565
+ CallExpr(const Location & loc, std::shared_ptr<Expression> && obj, ArgumentsExpression && a)
1566
+ : Expression(loc), object(std::move(obj)), args(std::move(a)) {}
1531
1567
  Value do_evaluate(const std::shared_ptr<Context> & context) const override {
1532
1568
  if (!object) throw std::runtime_error("CallExpr.object is null");
1533
1569
  auto obj = object->evaluate(context);
@@ -1542,8 +1578,8 @@ public:
1542
1578
  class FilterExpr : public Expression {
1543
1579
  std::vector<std::shared_ptr<Expression>> parts;
1544
1580
  public:
1545
- FilterExpr(const Location & location, std::vector<std::shared_ptr<Expression>> && p)
1546
- : Expression(location), parts(std::move(p)) {}
1581
+ FilterExpr(const Location & loc, std::vector<std::shared_ptr<Expression>> && p)
1582
+ : Expression(loc), parts(std::move(p)) {}
1547
1583
  Value do_evaluate(const std::shared_ptr<Context> & context) const override {
1548
1584
  Value result;
1549
1585
  bool first = true;
@@ -2070,28 +2106,37 @@ private:
2070
2106
 
2071
2107
  while (it != end && consumeSpaces() && peekSymbols({ "[", "." })) {
2072
2108
  if (!consumeToken("[").empty()) {
2073
- std::shared_ptr<Expression> index;
2109
+ std::shared_ptr<Expression> index;
2110
+ auto slice_loc = get_location();
2111
+ std::shared_ptr<Expression> start, end, step;
2112
+ bool has_first_colon = false, has_second_colon = false;
2113
+
2114
+ if (!peekSymbols({ ":" })) {
2115
+ start = parseExpression();
2116
+ }
2117
+
2118
+ if (!consumeToken(":").empty()) {
2119
+ has_first_colon = true;
2120
+ if (!peekSymbols({ ":", "]" })) {
2121
+ end = parseExpression();
2122
+ }
2074
2123
  if (!consumeToken(":").empty()) {
2075
- auto slice_end = parseExpression();
2076
- index = std::make_shared<SliceExpr>(slice_end->location, nullptr, std::move(slice_end));
2077
- } else {
2078
- auto slice_start = parseExpression();
2079
- if (!consumeToken(":").empty()) {
2080
- consumeSpaces();
2081
- if (peekSymbols({ "]" })) {
2082
- index = std::make_shared<SliceExpr>(slice_start->location, std::move(slice_start), nullptr);
2083
- } else {
2084
- auto slice_end = parseExpression();
2085
- index = std::make_shared<SliceExpr>(slice_start->location, std::move(slice_start), std::move(slice_end));
2086
- }
2087
- } else {
2088
- index = std::move(slice_start);
2124
+ has_second_colon = true;
2125
+ if (!peekSymbols({ "]" })) {
2126
+ step = parseExpression();
2089
2127
  }
2090
2128
  }
2091
- if (!index) throw std::runtime_error("Empty index in subscript");
2092
- if (consumeToken("]").empty()) throw std::runtime_error("Expected closing bracket in subscript");
2129
+ }
2130
+
2131
+ if ((has_first_colon || has_second_colon) && (start || end || step)) {
2132
+ index = std::make_shared<SliceExpr>(slice_loc, std::move(start), std::move(end), std::move(step));
2133
+ } else {
2134
+ index = std::move(start);
2135
+ }
2136
+ if (!index) throw std::runtime_error("Empty index in subscript");
2137
+ if (consumeToken("]").empty()) throw std::runtime_error("Expected closing bracket in subscript");
2093
2138
 
2094
- value = std::make_shared<SubscriptExpr>(value->location, std::move(value), std::move(index));
2139
+ value = std::make_shared<SubscriptExpr>(value->location, std::move(value), std::move(index));
2095
2140
  } else if (!consumeToken(".").empty()) {
2096
2141
  auto identifier = parseIdentifier();
2097
2142
  if (!identifier) throw std::runtime_error("Expected identifier in subscript");
@@ -2460,7 +2505,7 @@ private:
2460
2505
  static std::regex leading_space_regex(R"(^\s+)");
2461
2506
  text = std::regex_replace(text, leading_space_regex, "");
2462
2507
  } else if (options.trim_blocks && (it - 1) != begin && !dynamic_cast<ExpressionTemplateToken*>((*(it - 2)).get())) {
2463
- if (text.length() > 0 && text[0] == '\n') {
2508
+ if (!text.empty() && text[0] == '\n') {
2464
2509
  text.erase(0, 1);
2465
2510
  }
2466
2511
  }
@@ -2538,7 +2583,7 @@ public:
2538
2583
  TemplateTokenIterator begin = tokens.begin();
2539
2584
  auto it = begin;
2540
2585
  TemplateTokenIterator end = tokens.end();
2541
- return parser.parseTemplate(begin, it, end, /* full= */ true);
2586
+ return parser.parseTemplate(begin, it, end, /* fully= */ true);
2542
2587
  }
2543
2588
  };
2544
2589
 
@@ -2577,7 +2622,7 @@ inline std::shared_ptr<Context> Context::builtins() {
2577
2622
  throw std::runtime_error(args.at("message").get<std::string>());
2578
2623
  }));
2579
2624
  globals.set("tojson", simple_function("tojson", { "value", "indent" }, [](const std::shared_ptr<Context> &, Value & args) {
2580
- return Value(args.at("value").dump(args.get<int64_t>("indent", -1), /* tojson= */ true));
2625
+ return Value(args.at("value").dump(args.get<int64_t>("indent", -1), /* to_json= */ true));
2581
2626
  }));
2582
2627
  globals.set("items", simple_function("items", { "object" }, [](const std::shared_ptr<Context> &, Value & args) {
2583
2628
  auto items = Value::array();
@@ -2599,21 +2644,25 @@ inline std::shared_ptr<Context> Context::builtins() {
2599
2644
  globals.set("last", simple_function("last", { "items" }, [](const std::shared_ptr<Context> &, Value & args) {
2600
2645
  auto items = args.at("items");
2601
2646
  if (!items.is_array()) throw std::runtime_error("object is not a list");
2602
- if (items.size() == 0) return Value();
2647
+ if (items.empty()) return Value();
2603
2648
  return items.at(items.size() - 1);
2604
2649
  }));
2605
2650
  globals.set("trim", simple_function("trim", { "text" }, [](const std::shared_ptr<Context> &, Value & args) {
2606
2651
  auto & text = args.at("text");
2607
2652
  return text.is_null() ? text : Value(strip(text.get<std::string>()));
2608
2653
  }));
2609
- globals.set("lower", simple_function("lower", { "text" }, [](const std::shared_ptr<Context> &, Value & args) {
2610
- auto text = args.at("text");
2611
- if (text.is_null()) return text;
2612
- std::string res;
2613
- auto str = text.get<std::string>();
2614
- std::transform(str.begin(), str.end(), std::back_inserter(res), ::tolower);
2615
- return Value(res);
2616
- }));
2654
+ auto char_transform_function = [](const std::string & name, const std::function<char(char)> & fn) {
2655
+ return simple_function(name, { "text" }, [=](const std::shared_ptr<Context> &, Value & args) {
2656
+ auto text = args.at("text");
2657
+ if (text.is_null()) return text;
2658
+ std::string res;
2659
+ auto str = text.get<std::string>();
2660
+ std::transform(str.begin(), str.end(), std::back_inserter(res), fn);
2661
+ return Value(res);
2662
+ });
2663
+ };
2664
+ globals.set("lower", char_transform_function("lower", ::tolower));
2665
+ globals.set("upper", char_transform_function("upper", ::toupper));
2617
2666
  globals.set("default", Value::callable([=](const std::shared_ptr<Context> &, ArgumentsValue & args) {
2618
2667
  args.expectArgs("default", {2, 3}, {0, 1});
2619
2668
  auto & value = args.args[0];
@@ -2743,12 +2792,17 @@ inline std::shared_ptr<Context> Context::builtins() {
2743
2792
  return Value::callable([=](const std::shared_ptr<Context> & context, ArgumentsValue & args) {
2744
2793
  args.expectArgs(is_select ? "select" : "reject", {2, (std::numeric_limits<size_t>::max)()}, {0, 0});
2745
2794
  auto & items = args.args[0];
2746
- if (items.is_null())
2795
+ if (items.is_null()) {
2747
2796
  return Value::array();
2748
- if (!items.is_array()) throw std::runtime_error("object is not iterable: " + items.dump());
2797
+ }
2798
+ if (!items.is_array()) {
2799
+ throw std::runtime_error("object is not iterable: " + items.dump());
2800
+ }
2749
2801
 
2750
2802
  auto filter_fn = context->get(args.args[1]);
2751
- if (filter_fn.is_null()) throw std::runtime_error("Undefined filter: " + args.args[1].dump());
2803
+ if (filter_fn.is_null()) {
2804
+ throw std::runtime_error("Undefined filter: " + args.args[1].dump());
2805
+ }
2752
2806
 
2753
2807
  auto filter_args = Value::array();
2754
2808
  for (size_t i = 2, n = args.args.size(); i < n; i++) {
@@ -2870,20 +2924,25 @@ inline std::shared_ptr<Context> Context::builtins() {
2870
2924
  auto v = arg.get<int64_t>();
2871
2925
  startEndStep[i] = v;
2872
2926
  param_set[i] = true;
2873
- }
2874
2927
  }
2875
- for (auto & [name, value] : args.kwargs) {
2876
- size_t i;
2877
- if (name == "start") i = 0;
2878
- else if (name == "end") i = 1;
2879
- else if (name == "step") i = 2;
2880
- else throw std::runtime_error("Unknown argument " + name + " for function range");
2881
-
2882
- if (param_set[i]) {
2883
- throw std::runtime_error("Duplicate argument " + name + " for function range");
2884
- }
2885
- startEndStep[i] = value.get<int64_t>();
2886
- param_set[i] = true;
2928
+ }
2929
+ for (auto & [name, value] : args.kwargs) {
2930
+ size_t i;
2931
+ if (name == "start") {
2932
+ i = 0;
2933
+ } else if (name == "end") {
2934
+ i = 1;
2935
+ } else if (name == "step") {
2936
+ i = 2;
2937
+ } else {
2938
+ throw std::runtime_error("Unknown argument " + name + " for function range");
2939
+ }
2940
+
2941
+ if (param_set[i]) {
2942
+ throw std::runtime_error("Duplicate argument " + name + " for function range");
2943
+ }
2944
+ startEndStep[i] = value.get<int64_t>();
2945
+ param_set[i] = true;
2887
2946
  }
2888
2947
  if (!param_set[1]) {
2889
2948
  throw std::runtime_error("Missing required argument 'end' for function range");