Felipe97/llama-cpp-compiled
01.1k
1#include "runtime.h"2#include "unicode.h"3#include "value.h"4 5// for converting from JSON to jinja values6#include "json.h"7 8#include <sstream>9#include <string>10#include <cctype>11#include <vector>12#include <optional>13#include <algorithm>14 15#define FILENAME "jinja-value"16 17namespace jinja {18 19// func_args method implementations20 21value func_args::get_kwarg(const std::string & key, value default_val) const {22 for (const auto & arg : args) {23 if (is_val<value_kwarg>(arg)) {24 auto * kwarg = cast_val<value_kwarg>(arg);25 if (kwarg->key == key) {26 return kwarg->val;27 }28 }29 }30 return default_val;31}32 33value func_args::get_kwarg_or_pos(const std::string & key, size_t pos) const {34 value val = get_kwarg(key, mk_val<value_undefined>());35 36 if (val->is_undefined() && pos < count() && !is_val<value_kwarg>(args[pos])) {37 return args[pos];38 }39 40 return val;41}42 43value func_args::get_pos(size_t pos) const {44 if (count() > pos) {45 return args[pos];46 }47 throw raised_exception("Function '" + func_name + "' expected at least " + std::to_string(pos + 1) + " arguments, got " + std::to_string(count()));48}49 50value func_args::get_pos(size_t pos, value default_val) const {51 if (count() > pos) {52 return args[pos];53 }54 return default_val;55}56 57void func_args::push_back(const value & val) {58 args.push_back(val);59}60 61void func_args::push_front(const value & val) {62 args.insert(args.begin(), val);63}64 65const std::vector<value> & func_args::get_args() const {66 return args;67}68 69/**70 * Function that mimics Python's array slicing.71 */72template<typename T>73static T slice(const T & array, int64_t start, int64_t stop, int64_t step = 1) {74 int64_t len = static_cast<int64_t>(array.size());75 int64_t direction = (step > 0) ? 1 : ((step < 0) ? -1 : 0);76 int64_t start_val = 0;77 int64_t stop_val = 0;78 if (direction >= 0) {79 start_val = start;80 if (start_val < 0) {81 start_val = std::max(len + start_val, (int64_t)0);82 } else {83 start_val = std::min(start_val, len);84 }85 86 stop_val = stop;87 if (stop_val < 0) {88 stop_val = std::max(len + stop_val, (int64_t)0);89 } else {90 stop_val = std::min(stop_val, len);91 }92 } else {93 start_val = start;94 if (start_val < 0) {95 start_val = std::max(len + start_val, (int64_t)0);96 } else {97 start_val = std::min(start_val, len - 1);98 }99 100 stop_val = stop;101 if (stop_val < -1) {102 stop_val = std::max(len + stop_val, (int64_t)-1);103 } else {104 stop_val = std::min(stop_val, len - 1);105 }106 }107 T result;108 if (direction == 0) {109 return result;110 }111 for (int64_t i = start_val; direction * i < direction * stop_val; i += step) {112 if (i >= 0 && i < len) {113 result.push_back(array[static_cast<size_t>(i)]);114 }115 }116 return result;117}118 119template<typename T>120static value empty_value_fn(const func_args &) {121 if constexpr (std::is_same_v<T, value_int>) {122 return mk_val<T>(0);123 } else if constexpr (std::is_same_v<T, value_float>) {124 return mk_val<T>(0.0);125 } else if constexpr (std::is_same_v<T, value_bool>) {126 return mk_val<T>(false);127 } else {128 return mk_val<T>();129 }130}131template<typename T>132static value test_type_fn(const func_args & args) {133 args.ensure_count(1);134 bool is_type = is_val<T>(args.get_pos(0));135 JJ_DEBUG("test_type_fn: type=%s result=%d", typeid(T).name(), is_type ? 1 : 0);136 return mk_val<value_bool>(is_type);137}138template<typename T, typename U>139static value test_type_fn(const func_args & args) {140 args.ensure_count(1);141 bool is_type = is_val<T>(args.get_pos(0)) || is_val<U>(args.get_pos(0));142 JJ_DEBUG("test_type_fn: type=%s or %s result=%d", typeid(T).name(), typeid(U).name(), is_type ? 1 : 0);143 return mk_val<value_bool>(is_type);144}145template<typename T, typename U, typename V>146static value test_type_fn(const func_args & args) {147 args.ensure_count(1);148 bool is_type = is_val<T>(args.get_pos(0)) || is_val<U>(args.get_pos(0)) || is_val<V>(args.get_pos(0));149 JJ_DEBUG("test_type_fn: type=%s, %s or %s result=%d", typeid(T).name(), typeid(U).name(), typeid(V).name(), is_type ? 1 : 0);150 return mk_val<value_bool>(is_type);151}152template<value_compare_op op>153static value test_compare_fn(const func_args & args) {154 args.ensure_count(2, 2);155 return mk_val<value_bool>(value_compare(args.get_pos(0), args.get_pos(1), op));156}157 158static void append_codepoint_as_ascii_json_escape(std::string & out, uint32_t codepoint) {159 auto append_u16 = [&out](uint32_t value) {160 char buf[8];161 snprintf(buf, sizeof(buf), "\\u%04x", static_cast<unsigned int>(value));162 out += buf;163 };164 165 if (codepoint <= 0xFFFF) {166 append_u16(codepoint);167 return;168 }169 170 codepoint -= 0x10000;171 append_u16(0xD800 + ((codepoint >> 10) & 0x3FF));172 append_u16(0xDC00 + (codepoint & 0x3FF));173}174 175static std::string json_ensure_ascii_preserving_format(const std::string & json_str) {176 std::string output;177 output.reserve(json_str.size());178 179 bool in_string = false;180 bool escaped = false;181 182 for (size_t pos = 0; pos < json_str.size();) {183 const char ch = json_str[pos];184 if (!in_string) {185 output.push_back(ch);186 if (ch == '"') {187 in_string = true;188 }189 ++pos;190 continue;191 }192 193 if (escaped) {194 output.push_back(ch);195 escaped = false;196 ++pos;197 continue;198 }199 200 if (ch == '\\') {201 output.push_back(ch);202 escaped = true;203 ++pos;204 continue;205 }206 207 if (ch == '"') {208 output.push_back(ch);209 in_string = false;210 ++pos;211 continue;212 }213 214 const unsigned char uch = static_cast<unsigned char>(ch);215 if (uch < 0x80) {216 output.push_back(ch);217 ++pos;218 continue;219 }220 221 auto parsed = common_parse_utf8_codepoint(json_str, pos);222 if (parsed.status != utf8_parse_result::SUCCESS) {223 output += "\\ufffd";224 ++pos;225 continue;226 }227 228 append_codepoint_as_ascii_json_escape(output, parsed.codepoint);229 pos += parsed.bytes_consumed;230 }231 232 return output;233}234 235static value tojson(const func_args & args) {236 args.ensure_count(1, 5);237 value val_ascii = args.get_kwarg_or_pos("ensure_ascii", 1);238 value val_indent = args.get_kwarg_or_pos("indent", 2);239 value val_separators = args.get_kwarg_or_pos("separators", 3);240 value val_sort = args.get_kwarg_or_pos("sort_keys", 4);241 int indent = -1;242 if (args.ctx.is_get_stats) {243 // mark as used (recursively) for stats244 auto val_input = args.get_pos(0);245 value_t::stats_t::mark_used(const_cast<value&>(val_input), true);246 }247 if (is_val<value_int>(val_indent)) {248 indent = static_cast<int>(val_indent->as_int());249 }250 if (val_sort->as_bool()) { // undefined == false251 throw not_implemented_exception("tojson sort_keys=true not implemented");252 }253 const bool ensure_ascii = val_ascii->as_bool(); // undefined == false254 auto separators = (is_val<value_array>(val_separators) ? val_separators : mk_val<value_array>())->as_array();255 std::string item_sep = separators.size() > 0 ? separators[0]->as_string().str() : (indent < 0 ? ", " : ",");256 std::string key_sep = separators.size() > 1 ? separators[1]->as_string().str() : ": ";257 std::string json_str = value_to_json(args.get_pos(0), indent, item_sep, key_sep);258 if (ensure_ascii) {259 json_str = json_ensure_ascii_preserving_format(json_str);260 }261 return mk_val<value_string>(json_str);262}263 264template<bool is_reject>265static value selectattr(const func_args & args) {266 args.ensure_count(2, 4);267 args.ensure_vals<value_array, value_string, value_string, value_string>(true, true, false, false);268 269 auto arr = args.get_pos(0)->as_array();270 auto attribute = args.get_pos(1);271 auto out = mk_val<value_array>();272 value val_default = mk_val<value_undefined>();273 274 if (args.count() == 2) {275 // example: array | selectattr("active")276 for (const auto & item : arr) {277 if (!is_val<value_object>(item)) {278 throw raised_exception("selectattr: item is not an object");279 }280 value attr_val = item->at(attribute, val_default);281 bool is_selected = attr_val->as_bool();282 if constexpr (is_reject) is_selected = !is_selected;283 if (is_selected) out->push_back(item);284 }285 return out;286 287 } else if (args.count() == 3) {288 // example: array | selectattr("equalto", "text")289 // translated to: test_is_equalto(item, "text")290 std::string test_name = args.get_pos(1)->as_string().str();291 value test_val = args.get_pos(2);292 auto & builtins = global_builtins();293 auto it = builtins.find("test_is_" + test_name);294 if (it == builtins.end()) {295 throw raised_exception("selectattr: unknown test '" + test_name + "'");296 }297 auto test_fn = it->second;298 for (const auto & item : arr) {299 func_args test_args(args.ctx);300 test_args.push_back(item); // current object301 test_args.push_back(test_val); // extra argument302 value test_result = test_fn(test_args);303 bool is_selected = test_result->as_bool();304 if constexpr (is_reject) is_selected = !is_selected;305 if (is_selected) out->push_back(item);306 }307 return out;308 309 } else if (args.count() == 4) {310 // example: array | selectattr("status", "equalto", "active")311 // translated to: test_is_equalto(item.status, "active")312 std::string test_name = args.get_pos(2)->as_string().str();313 auto extra_arg = args.get_pos(3);314 auto & builtins = global_builtins();315 auto it = builtins.find("test_is_" + test_name);316 if (it == builtins.end()) {317 throw raised_exception("selectattr: unknown test '" + test_name + "'");318 }319 auto test_fn = it->second;320 for (const auto & item : arr) {321 if (!is_val<value_object>(item)) {322 throw raised_exception("selectattr: item is not an object");323 }324 value attr_val = item->at(attribute, val_default);325 func_args test_args(args.ctx);326 test_args.push_back(attr_val); // attribute value327 test_args.push_back(extra_arg); // extra argument328 value test_result = test_fn(test_args);329 bool is_selected = test_result->as_bool();330 if constexpr (is_reject) is_selected = !is_selected;331 if (is_selected) out->push_back(item);332 }333 return out;334 } else {335 throw raised_exception("selectattr: invalid number of arguments");336 }337 338 return out;339}340 341static value default_value(const func_args & args) {342 args.ensure_count(2, 3);343 value val_check = args.get_kwarg_or_pos("boolean", 2);344 bool check_bool = val_check->as_bool(); // undefined == false345 bool no_value = check_bool346 ? (!args.get_pos(0)->as_bool())347 : (args.get_pos(0)->is_undefined() || args.get_pos(0)->is_none());348 return no_value ? args.get_pos(1) : args.get_pos(0);349}350 351const func_builtins & global_builtins() {352 static const func_builtins builtins = {353 {"raise_exception", [](const func_args & args) -> value {354 args.ensure_vals<value_string>();355 std::string msg = args.get_pos(0)->as_string().str();356 throw raised_exception("Jinja Exception: " + msg);357 }},358 {"namespace", [](const func_args & args) -> value {359 auto out = mk_val<value_object>();360 for (const auto & arg : args.get_args()) {361 if (!is_val<value_kwarg>(arg)) {362 throw raised_exception("namespace() arguments must be kwargs");363 }364 auto kwarg = cast_val<value_kwarg>(arg);365 JJ_DEBUG("namespace: adding key '%s'", kwarg->key.c_str());366 out->insert(kwarg->key, kwarg->val);367 }368 return out;369 }},370 {"strftime_now", [](const func_args & args) -> value {371 args.ensure_vals<value_string>();372 std::string format = args.get_pos(0)->as_string().str();373 // get current time374 // TODO: make sure this is the same behavior as Python's strftime375 char buf[100];376 if (std::strftime(buf, sizeof(buf), format.c_str(), std::localtime(&args.ctx.current_time))) {377 return mk_val<value_string>(std::string(buf));378 } else {379 throw raised_exception("strftime_now: failed to format time");380 }381 }},382 {"range", [](const func_args & args) -> value {383 args.ensure_count(1, 3);384 args.ensure_vals<value_int, value_int, value_int>(true, false, false);385 386 auto arg0 = args.get_pos(0);387 auto arg1 = args.get_pos(1, mk_val<value_undefined>());388 auto arg2 = args.get_pos(2, mk_val<value_undefined>());389 390 int64_t start, stop, step;391 if (args.count() == 1) {392 start = 0;393 stop = arg0->as_int();394 step = 1;395 } else if (args.count() == 2) {396 start = arg0->as_int();397 stop = arg1->as_int();398 step = 1;399 } else {400 start = arg0->as_int();401 stop = arg1->as_int();402 step = arg2->as_int();403 }404 405 auto out = mk_val<value_array>();406 if (step == 0) {407 throw raised_exception("range() step argument must not be zero");408 }409 if (step > 0) {410 for (int64_t i = start; i < stop; i += step) {411 out->push_back(mk_val<value_int>(i));412 }413 } else {414 for (int64_t i = start; i > stop; i += step) {415 out->push_back(mk_val<value_int>(i));416 }417 }418 return out;419 }},420 {"tojson", tojson},421 422 // tests423 {"test_is_boolean", test_type_fn<value_bool>},424 {"test_is_callable", test_type_fn<value_func>},425 {"test_is_odd", [](const func_args & args) -> value {426 args.ensure_vals<value_int>();427 int64_t val = args.get_pos(0)->as_int();428 return mk_val<value_bool>(val % 2 != 0);429 }},430 {"test_is_even", [](const func_args & args) -> value {431 args.ensure_vals<value_int>();432 int64_t val = args.get_pos(0)->as_int();433 return mk_val<value_bool>(val % 2 == 0);434 }},435 {"test_is_false", [](const func_args & args) -> value {436 args.ensure_count(1);437 bool val = is_val<value_bool>(args.get_pos(0)) && !args.get_pos(0)->as_bool();438 return mk_val<value_bool>(val);439 }},440 {"test_is_true", [](const func_args & args) -> value {441 args.ensure_count(1);442 bool val = is_val<value_bool>(args.get_pos(0)) && args.get_pos(0)->as_bool();443 return mk_val<value_bool>(val);444 }},445 {"test_is_divisibleby", [](const func_args & args) -> value {446 args.ensure_vals<value_int, value_int>();447 bool res = args.get_pos(0)->val_int % args.get_pos(1)->val_int == 0;448 return mk_val<value_bool>(res);449 }},450 {"test_is_string", test_type_fn<value_string>},451 {"test_is_integer", test_type_fn<value_int>},452 {"test_is_float", test_type_fn<value_float>},453 {"test_is_number", test_type_fn<value_int, value_float>},454 {"test_is_iterable", test_type_fn<value_array, value_string, value_undefined>},455 {"test_is_sequence", test_type_fn<value_array, value_string, value_undefined>},456 {"test_is_mapping", test_type_fn<value_object>},457 {"test_is_lower", [](const func_args & args) -> value {458 args.ensure_vals<value_string>();459 return mk_val<value_bool>(args.get_pos(0)->val_str.is_lowercase());460 }},461 {"test_is_upper", [](const func_args & args) -> value {462 args.ensure_vals<value_string>();463 return mk_val<value_bool>(args.get_pos(0)->val_str.is_uppercase());464 }},465 {"test_is_none", test_type_fn<value_none>},466 {"test_is_defined", [](const func_args & args) -> value {467 args.ensure_count(1);468 bool res = !args.get_pos(0)->is_undefined();469 JJ_DEBUG("test_is_defined: result=%d", res ? 1 : 0);470 return mk_val<value_bool>(res);471 }},472 {"test_is_undefined", test_type_fn<value_undefined>},473 {"test_is_eq", test_compare_fn<value_compare_op::eq>},474 {"test_is_equalto", test_compare_fn<value_compare_op::eq>},475 {"test_is_ge", test_compare_fn<value_compare_op::ge>},476 {"test_is_gt", test_compare_fn<value_compare_op::gt>},477 {"test_is_greaterthan", test_compare_fn<value_compare_op::gt>},478 {"test_is_lt", test_compare_fn<value_compare_op::lt>},479 {"test_is_lessthan", test_compare_fn<value_compare_op::lt>},480 {"test_is_ne", test_compare_fn<value_compare_op::ne>},481 {"test_is_in", [](const func_args & args) -> value {482 args.ensure_count(2);483 auto needle = args.get_pos(0);484 auto haystack = args.get_pos(1);485 if (is_val<value_undefined>(haystack)) {486 return mk_val<value_bool>(false);487 }488 if (is_val<value_array>(haystack)) {489 for (const auto & item : haystack->as_array()) {490 if (*needle == *item) {491 return mk_val<value_bool>(true);492 }493 }494 return mk_val<value_bool>(false);495 }496 if (is_val<value_string>(haystack)) {497 if (!is_val<value_string>(needle)) {498 throw raised_exception("'in' test expects args[1] as string when args[0] is string, got args[1] as " + needle->type());499 }500 return mk_val<value_bool>(501 haystack->as_string().str().find(needle->as_string().str()) != std::string::npos);502 }503 if (is_val<value_object>(haystack)) {504 return mk_val<value_bool>(haystack->has_key(needle));505 }506 throw raised_exception("'in' test expects iterable as first argument, got " + haystack->type());507 }},508 {"test_is_test", [](const func_args & args) -> value {509 args.ensure_vals<value_string>();510 auto & builtins = global_builtins();511 std::string test_name = args.get_pos(0)->val_str.str();512 auto it = builtins.find("test_is_" + test_name);513 bool res = it != builtins.end();514 return mk_val<value_bool>(res);515 }},516 {"test_is_sameas", [](const func_args & args) -> value {517 // Check if an object points to the same memory address as another object518 (void)args;519 throw not_implemented_exception("sameas test not implemented");520 }},521 {"test_is_escaped", [](const func_args & args) -> value {522 (void)args;523 throw not_implemented_exception("escaped test not implemented");524 }},525 {"test_is_filter", [](const func_args & args) -> value {526 (void)args;527 throw not_implemented_exception("filter test not implemented");528 }},529 };530 return builtins;531}532 533 534const func_builtins & value_int_t::get_builtins() const {535 static const func_builtins builtins = {536 {"default", default_value},537 {"abs", [](const func_args & args) -> value {538 args.ensure_vals<value_int>();539 int64_t val = args.get_pos(0)->as_int();540 return mk_val<value_int>(val < 0 ? -val : val);541 }},542 {"int", [](const func_args & args) -> value {543 args.ensure_vals<value_int>();544 return mk_val<value_int>(args.get_pos(0)->as_int());545 }},546 {"float", [](const func_args & args) -> value {547 args.ensure_vals<value_int>();548 double val = static_cast<double>(args.get_pos(0)->as_int());549 return mk_val<value_float>(val);550 }},551 {"safe", tojson},552 {"string", tojson},553 {"tojson", tojson},554 };555 return builtins;556}557 558 559const func_builtins & value_float_t::get_builtins() const {560 static const func_builtins builtins = {561 {"default", default_value},562 {"abs", [](const func_args & args) -> value {563 args.ensure_vals<value_float>();564 double val = args.get_pos(0)->as_float();565 return mk_val<value_float>(val < 0.0 ? -val : val);566 }},567 {"int", [](const func_args & args) -> value {568 args.ensure_vals<value_float>();569 int64_t val = static_cast<int64_t>(args.get_pos(0)->as_float());570 return mk_val<value_int>(val);571 }},572 {"float", [](const func_args & args) -> value {573 args.ensure_vals<value_float>();574 return mk_val<value_float>(args.get_pos(0)->as_float());575 }},576 {"safe", tojson},577 {"string", tojson},578 {"tojson", tojson},579 };580 return builtins;581}582 583static bool string_startswith(const std::string & str, const std::string & prefix) {584 if (str.length() < prefix.length()) return false;585 return str.compare(0, prefix.length(), prefix) == 0;586}587 588static bool string_endswith(const std::string & str, const std::string & suffix) {589 if (str.length() < suffix.length()) return false;590 return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;591}592 593[[noreturn]] static value string_join_not_implemented(const func_args &) {594 throw not_implemented_exception("String join builtin not implemented");595}596 597const func_builtins & value_string_t::get_builtins() const {598 static const func_builtins builtins = {599 {"default", default_value},600 {"upper", [](const func_args & args) -> value {601 args.ensure_vals<value_string>();602 jinja::string str = args.get_pos(0)->as_string().uppercase();603 return mk_val<value_string>(str);604 }},605 {"lower", [](const func_args & args) -> value {606 args.ensure_vals<value_string>();607 jinja::string str = args.get_pos(0)->as_string().lowercase();608 return mk_val<value_string>(str);609 }},610 {"strip", [](const func_args & args) -> value {611 value val_input = args.get_pos(0);612 if (!is_val<value_string>(val_input)) {613 throw raised_exception("strip() first argument must be a string");614 }615 value val_chars = args.get_kwarg_or_pos("chars", 1);616 if (val_chars->is_undefined()) {617 return mk_val<value_string>(args.get_pos(0)->as_string().strip(true, true));618 } else {619 return mk_val<value_string>(args.get_pos(0)->as_string().strip(true, true, val_chars->as_string().str()));620 }621 }},622 {"rstrip", [](const func_args & args) -> value {623 args.ensure_vals<value_string>();624 value val_chars = args.get_kwarg_or_pos("chars", 1);625 if (val_chars->is_undefined()) {626 return mk_val<value_string>(args.get_pos(0)->as_string().strip(false, true));627 } else {628 return mk_val<value_string>(args.get_pos(0)->as_string().strip(false, true, val_chars->as_string().str()));629 }630 }},631 {"lstrip", [](const func_args & args) -> value {632 args.ensure_vals<value_string>();633 value val_chars = args.get_kwarg_or_pos("chars", 1);634 if (val_chars->is_undefined()) {635 return mk_val<value_string>(args.get_pos(0)->as_string().strip(true, false));636 } else {637 return mk_val<value_string>(args.get_pos(0)->as_string().strip(true, false, val_chars->as_string().str()));638 }639 }},640 {"title", [](const func_args & args) -> value {641 args.ensure_vals<value_string>();642 jinja::string str = args.get_pos(0)->as_string().titlecase();643 return mk_val<value_string>(str);644 }},645 {"capitalize", [](const func_args & args) -> value {646 args.ensure_vals<value_string>();647 jinja::string str = args.get_pos(0)->as_string().capitalize();648 return mk_val<value_string>(str);649 }},650 {"length", [](const func_args & args) -> value {651 args.ensure_vals<value_string>();652 jinja::string str = args.get_pos(0)->as_string();653 return mk_val<value_int>(str.length());654 }},655 {"startswith", [](const func_args & args) -> value {656 args.ensure_vals<value_string, value_string>();657 std::string str = args.get_pos(0)->as_string().str();658 std::string prefix = args.get_pos(1)->as_string().str();659 return mk_val<value_bool>(string_startswith(str, prefix));660 }},661 {"endswith", [](const func_args & args) -> value {662 args.ensure_vals<value_string, value_string>();663 std::string str = args.get_pos(0)->as_string().str();664 std::string suffix = args.get_pos(1)->as_string().str();665 return mk_val<value_bool>(string_endswith(str, suffix));666 }},667 {"split", [](const func_args & args) -> value {668 args.ensure_count(1, 3);669 value val_input = args.get_pos(0);670 if (!is_val<value_string>(val_input)) {671 throw raised_exception("split() first argument must be a string");672 }673 std::string str = val_input->as_string().str();674 // FIXME: Support non-specified delimiter (split on consecutive (no leading or trailing) whitespace)675 std::string delim = (args.count() > 1) ? args.get_pos(1)->as_string().str() : " ";676 if (delim.empty()) {677 throw raised_exception("empty separator");678 }679 int64_t maxsplit = (args.count() > 2) ? args.get_pos(2)->as_int() : -1;680 auto result = mk_val<value_array>();681 size_t pos = 0;682 std::string token;683 while ((pos = str.find(delim)) != std::string::npos && maxsplit != 0) {684 token = str.substr(0, pos);685 result->push_back(mk_val<value_string>(token));686 str.erase(0, pos + delim.length());687 --maxsplit;688 }689 auto res = mk_val<value_string>(str);690 res->val_str.mark_input_based_on(args.get_pos(0)->val_str);691 result->push_back(std::move(res));692 return result;693 }},694 {"rsplit", [](const func_args & args) -> value {695 args.ensure_count(1, 3);696 value val_input = args.get_pos(0);697 if (!is_val<value_string>(val_input)) {698 throw raised_exception("rsplit() first argument must be a string");699 }700 std::string str = val_input->as_string().str();701 // FIXME: Support non-specified delimiter (split on consecutive (no leading or trailing) whitespace)702 std::string delim = (args.count() > 1) ? args.get_pos(1)->as_string().str() : " ";703 if (delim.empty()) {704 throw raised_exception("empty separator");705 }706 int64_t maxsplit = (args.count() > 2) ? args.get_pos(2)->as_int() : -1;707 auto result = mk_val<value_array>();708 size_t pos = 0;709 std::string token;710 while ((pos = str.rfind(delim)) != std::string::npos && maxsplit != 0) {711 token = str.substr(pos + delim.length());712 result->push_back(mk_val<value_string>(token));713 str.erase(pos);714 --maxsplit;715 }716 auto res = mk_val<value_string>(str);717 res->val_str.mark_input_based_on(args.get_pos(0)->val_str);718 result->push_back(std::move(res));719 result->reverse();720 return result;721 }},722 {"replace", [](const func_args & args) -> value {723 args.ensure_vals<value_string, value_string, value_string, value_int>(true, true, true, false);724 std::string str = args.get_pos(0)->as_string().str();725 std::string old_str = args.get_pos(1)->as_string().str();726 std::string new_str = args.get_pos(2)->as_string().str();727 int64_t count = args.count() > 3 ? args.get_pos(3)->as_int() : -1;728 if (count > 0) {729 throw not_implemented_exception("String replace with count argument not implemented");730 }731 if (old_str != new_str) {732 size_t pos = 0;733 if (old_str.empty()) {734 std::string new_res;735 new_res.reserve(str.length() + new_str.length() * (str.length() + 1));736 new_res += new_str;737 for (const char c : str) {738 new_res.push_back(c);739 new_res += new_str;740 }741 str = new_res;742 } else {743 while ((pos = str.find(old_str, pos)) != std::string::npos) {744 str.replace(pos, old_str.length(), new_str);745 pos += new_str.length();746 }747 }748 }749 auto res = mk_val<value_string>(str);750 res->val_str.mark_input_based_on(args.get_pos(0)->val_str);751 return res;752 }},753 {"format", [](const func_args & args) -> value {754 value val_input = args.get_pos(0);755 if (!is_val<value_string>(val_input)) {756 throw raised_exception("format() first argument must be a string");757 }758 const jinja::string & fmt = val_input->as_string();759 const bool fmt_is_input = fmt.all_parts_are_input();760 761 const std::string str = fmt.str();762 jinja::string result;763 std::string literal;764 auto flush_literal = [&]() {765 if (!literal.empty()) {766 result.parts.push_back({fmt_is_input, literal});767 literal.clear();768 }769 };770 771 size_t arg_idx = 1; // positional args follow the format string772 for (size_t i = 0; i < str.size(); ++i) {773 if (str[i] != '{') {774 literal += str[i];775 continue;776 }777 if (i + 1 >= str.size() || str[i + 1] != '}') {778 throw not_implemented_exception("format() only supports simple '{}' placeholders");779 }780 ++i;781 flush_literal();782 const jinja::string arg_str = args.get_pos(arg_idx++)->as_string();783 result.parts.insert(result.parts.end(), arg_str.parts.begin(), arg_str.parts.end());784 }785 flush_literal();786 return mk_val<value_string>(result);787 }},788 {"int", [](const func_args & args) -> value {789 value val_input = args.get_pos(0);790 value val_default = args.get_kwarg_or_pos("default", 1);791 value val_base = args.get_kwarg_or_pos("base", 2);792 const int base = val_base->is_undefined() ? 10 : val_base->as_int();793 if (base != 0 && (base < 2 || base > 36)) {794 // an out-of-range base makes std::stoi fail fast on the MSVC CRT instead of throwing795 throw raised_exception("int() base must be 0 or between 2 and 36");796 }797 if (is_val<value_string>(val_input) == false) {798 throw raised_exception("int() first argument must be a string");799 }800 std::string str = val_input->as_string().str();801 try {802 return mk_val<value_int>(std::stoi(str, nullptr, base));803 } catch (...) {804 return mk_val<value_int>(val_default->is_undefined() ? 0 : val_default->as_int());805 }806 }},807 {"float", [](const func_args & args) -> value {808 args.ensure_vals<value_string>();809 value val_default = args.get_kwarg_or_pos("default", 1);810 std::string str = args.get_pos(0)->as_string().str();811 try {812 return mk_val<value_float>(std::stod(str));813 } catch (...) {814 return mk_val<value_float>(val_default->is_undefined() ? 0.0 : val_default->as_float());815 }816 }},817 {"string", [](const func_args & args) -> value {818 // no-op819 args.ensure_vals<value_string>();820 return mk_val<value_string>(args.get_pos(0)->as_string());821 }},822 {"default", [](const func_args & args) -> value {823 value input = args.get_pos(0);824 if (!is_val<value_string>(input)) {825 throw raised_exception("default() first argument must be a string");826 }827 value default_val = mk_val<value_string>("");828 if (args.count() > 1 && !args.get_pos(1)->is_undefined()) {829 default_val = args.get_pos(1);830 }831 value boolean_val = args.get_kwarg_or_pos("boolean", 2); // undefined == false832 if (input->is_undefined() || (boolean_val->as_bool() && !input->as_bool())) {833 return default_val;834 } else {835 return input;836 }837 }},838 {"slice", [](const func_args & args) -> value {839 args.ensure_count(1, 4);840 args.ensure_vals<value_string, value_int, value_int, value_int>(true, true, false, false);841 842 auto arg0 = args.get_pos(1);843 auto arg1 = args.get_pos(2, mk_val<value_undefined>());844 auto arg2 = args.get_pos(3, mk_val<value_undefined>());845 846 int64_t start, stop, step;847 if (args.count() == 1) {848 start = 0;849 stop = arg0->as_int();850 step = 1;851 } else if (args.count() == 2) {852 start = arg0->as_int();853 stop = arg1->as_int();854 step = 1;855 } else {856 start = arg0->as_int();857 stop = arg1->as_int();858 step = arg2->as_int();859 }860 if (step == 0) {861 throw raised_exception("slice step cannot be zero");862 }863 auto input = args.get_pos(0);864 auto sliced = slice(input->as_string().str(), start, stop, step);865 auto res = mk_val<value_string>(sliced);866 res->val_str.mark_input_based_on(input->as_string());867 return res;868 }},869 {"safe", [](const func_args & args) -> value {870 // no-op for now871 args.ensure_vals<value_string>();872 return args.get_pos(0);873 }},874 {"tojson", tojson},875 {"indent", [](const func_args &args) -> value {876 args.ensure_count(1, 4);877 value val_input = args.get_pos(0);878 value val_width = args.get_kwarg_or_pos("width", 1);879 const bool first = args.get_kwarg_or_pos("first", 2)->as_bool(); // undefined == false880 const bool blank = args.get_kwarg_or_pos("blank", 3)->as_bool(); // undefined == false881 if (!is_val<value_string>(val_input)) {882 throw raised_exception("indent() first argument must be a string");883 }884 std::string indent;885 if (is_val<value_int>(val_width)) {886 indent.assign(val_width->as_int(), ' ');887 } else if (is_val<value_string>(val_width)) {888 indent = val_width->as_string().str();889 } else {890 indent = " ";891 }892 std::string indented;893 std::string input = val_input->as_string().str();894 std::istringstream iss = std::istringstream(input);895 std::string line;896 while (std::getline(iss, line)) {897 if (!indented.empty()) {898 indented.push_back('\n');899 }900 if ((indented.empty() ? first : (!line.empty() || blank))) {901 indented += indent;902 }903 indented += line;904 }905 if (!input.empty() && input.back() == '\n') {906 indented.push_back('\n');907 if (blank) {908 indented += indent;909 }910 }911 912 auto res = mk_val<value_string>(indented);913 res->val_str.mark_input_based_on(val_input->as_string());914 return res;915 }},916 {"join", string_join_not_implemented},917 };918 return builtins;919}920 921 922const func_builtins & value_bool_t::get_builtins() const {923 static const func_handler tostring = [](const func_args & args) -> value {924 args.ensure_vals<value_bool>();925 bool val = args.get_pos(0)->as_bool();926 return mk_val<value_string>(val ? "True" : "False");927 };928 static const func_builtins builtins = {929 {"default", default_value},930 {"int", [](const func_args & args) -> value {931 args.ensure_vals<value_bool>();932 bool val = args.get_pos(0)->as_bool();933 return mk_val<value_int>(val ? 1 : 0);934 }},935 {"float", [](const func_args & args) -> value {936 args.ensure_vals<value_bool>();937 bool val = args.get_pos(0)->as_bool();938 return mk_val<value_float>(val ? 1.0 : 0.0);939 }},940 {"safe", tostring},941 {"string", tostring},942 {"tojson", tojson},943 };944 return builtins;945}946 947[[noreturn]] static value array_unique_not_implemented(const func_args &) {948 throw not_implemented_exception("Array unique builtin not implemented");949}950 951const func_builtins & value_array_t::get_builtins() const {952 static const func_builtins builtins = {953 {"default", default_value},954 {"list", [](const func_args & args) -> value {955 args.ensure_vals<value_array>();956 const auto & arr = args.get_pos(0)->as_array();957 auto result = mk_val<value_array>();958 for (const auto& v : arr) {959 result->push_back(v);960 }961 return result;962 }},963 {"first", [](const func_args & args) -> value {964 args.ensure_vals<value_array>();965 const auto & arr = args.get_pos(0)->as_array();966 if (arr.empty()) {967 return mk_val<value_undefined>();968 }969 return arr[0];970 }},971 {"last", [](const func_args & args) -> value {972 args.ensure_vals<value_array>();973 const auto & arr = args.get_pos(0)->as_array();974 if (arr.empty()) {975 return mk_val<value_undefined>();976 }977 return arr[arr.size() - 1];978 }},979 {"length", [](const func_args & args) -> value {980 args.ensure_vals<value_array>();981 const auto & arr = args.get_pos(0)->as_array();982 return mk_val<value_int>(static_cast<int64_t>(arr.size()));983 }},984 {"slice", [](const func_args & args) -> value {985 args.ensure_count(1, 4);986 args.ensure_vals<value_array, value_int, value_int, value_int>(true, true, false, false);987 988 auto val = args.get_pos(0);989 auto arg0 = args.get_pos(1);990 auto arg1 = args.get_pos(2, mk_val<value_undefined>());991 auto arg2 = args.get_pos(3, mk_val<value_undefined>());992 993 int64_t start, stop, step;994 if (args.count() == 1) {995 start = 0;996 stop = arg0->as_int();997 step = 1;998 } else if (args.count() == 2) {999 start = arg0->as_int();1000 stop = arg1->as_int();1001 step = 1;1002 } else {1003 start = arg0->as_int();1004 stop = arg1->as_int();1005 step = arg2->as_int();1006 }1007 if (step == 0) {1008 throw raised_exception("slice step cannot be zero");1009 }1010 auto arr = slice(val->as_array(), start, stop, step);1011 return is_val<value_tuple>(val) ? mk_val<value_tuple>(std::move(arr)) : mk_val<value_array>(std::move(arr));1012 }},1013 {"selectattr", selectattr<false>},1014 {"select", selectattr<false>},1015 {"rejectattr", selectattr<true>},1016 {"reject", selectattr<true>},1017 {"join", [](const func_args & args) -> value {1018 args.ensure_count(1, 3);1019 if (!is_val<value_array>(args.get_pos(0))) {1020 throw raised_exception("join() first argument must be an array");1021 }1022 value val_delim = args.get_kwarg_or_pos("d", 1);1023 value attribute = args.get_kwarg_or_pos("attribute", 2);1024 const auto & arr = args.get_pos(0)->as_array();1025 const bool attr_is_int = is_val<value_int>(attribute);1026 if (!attribute->is_undefined() && !is_val<value_string>(attribute) && !attr_is_int) {1027 throw raised_exception("join() attribute must be string or integer");1028 }1029 const int64_t attr_int = attr_is_int ? attribute->as_int() : 0;1030 const std::string delim = val_delim->is_undefined() ? "" : val_delim->as_string().str();1031 std::string result;1032 for (size_t i = 0; i < arr.size(); ++i) {1033 value val_arr = arr[i];1034 if (!attribute->is_undefined()) {1035 if (attr_is_int && is_val<value_array>(val_arr)) {1036 val_arr = val_arr->at(attr_int);1037 } else if (!attr_is_int && is_val<value_object>(val_arr)) {1038 val_arr = val_arr->at(attribute);1039 }1040 }1041 if (!is_val<value_string>(val_arr) && !is_val<value_int>(val_arr) && !is_val<value_float>(val_arr)) {1042 throw raised_exception("join() can only join arrays of strings or numerics");1043 }1044 result += val_arr->as_string().str();1045 if (i < arr.size() - 1) {1046 result += delim;1047 }1048 }1049 return mk_val<value_string>(result);1050 }},1051 {"string", [](const func_args & args) -> value {1052 args.ensure_vals<value_array>();1053 if (args.ctx.is_get_stats) {1054 // mark as used (recursively) for stats1055 auto val_input = args.get_pos(0);1056 value_t::stats_t::mark_used(const_cast<value&>(val_input), true);1057 }1058 return mk_val<value_string>(args.get_pos(0)->as_string());1059 }},1060 {"tojson", tojson},1061 {"map", [](const func_args & args) -> value {1062 args.ensure_count(2);1063 if (!is_val<value_array>(args.get_pos(0))) {1064 throw raised_exception("map: first argument must be an array");1065 }1066 if (!is_val<value_kwarg>(args.get_args().at(1))) {1067 throw not_implemented_exception("map: filter-mapping not implemented");1068 }1069 value val = args.get_pos(0);1070 value attribute = args.get_kwarg_or_pos("attribute", 1);1071 const bool attr_is_int = is_val<value_int>(attribute);1072 if (!is_val<value_string>(attribute) && !attr_is_int) {1073 throw raised_exception("map: attribute must be string or integer");1074 }1075 const int64_t attr_int = attr_is_int ? attribute->as_int() : 0;1076 value default_val = args.get_kwarg("default", mk_val<value_undefined>());1077 auto out = mk_val<value_array>();1078 auto arr = val->as_array();1079 for (const auto & item : arr) {1080 value attr_val;1081 if (attr_is_int) {1082 attr_val = is_val<value_array>(item) ? item->at(attr_int, default_val) : default_val;1083 } else {1084 attr_val = is_val<value_object>(item) ? item->at(attribute, default_val) : default_val;1085 }1086 out->push_back(attr_val);1087 }1088 return is_val<value_tuple>(val) ? mk_val<value_tuple>(std::move(out->as_array())) : out;1089 }},1090 {"append", [](const func_args & args) -> value {1091 args.ensure_count(2);1092 if (!is_val<value_array>(args.get_pos(0))) {1093 throw raised_exception("append: first argument must be an array");1094 }1095 const value_array_t * arr = cast_val<value_array>(args.get_pos(0));1096 // need to use const_cast here to modify the array1097 value_array_t * arr_editable = const_cast<value_array_t *>(arr);1098 arr_editable->push_back(args.get_pos(1));1099 return args.get_pos(0);1100 }},1101 {"pop", [](const func_args & args) -> value {1102 args.ensure_count(1, 2);1103 args.ensure_vals<value_array, value_int>(true, false);1104 int64_t index = args.count() == 2 ? args.get_pos(1)->as_int() : -1;1105 const value_array_t * arr = cast_val<value_array>(args.get_pos(0));1106 // need to use const_cast here to modify the array1107 value_array_t * arr_editable = const_cast<value_array_t *>(arr);1108 return arr_editable->pop_at(index);1109 }},1110 {"sort", [](const func_args & args) -> value {1111 args.ensure_count(1, 4);1112 if (!is_val<value_array>(args.get_pos(0))) {1113 throw raised_exception("sort: first argument must be an array");1114 }1115 value val = args.get_pos(0);1116 value val_reverse = args.get_kwarg_or_pos("reverse", 1);1117 value val_case = args.get_kwarg_or_pos("case_sensitive", 2);1118 value attribute = args.get_kwarg_or_pos("attribute", 3);1119 // FIXME: sorting is currently always case sensitive1120 //const bool case_sensitive = val_case->as_bool(); // undefined == false1121 const bool reverse = val_reverse->as_bool(); // undefined == false1122 const bool attr_is_int = is_val<value_int>(attribute);1123 const int64_t attr_int = attr_is_int ? attribute->as_int() : 0;1124 std::vector<value> arr = val->as_array(); // copy1125 std::sort(arr.begin(), arr.end(),[&](const value & a, const value & b) {1126 value val_a = a;1127 value val_b = b;1128 if (!attribute->is_undefined()) {1129 if (attr_is_int && is_val<value_array>(a) && is_val<value_array>(b)) {1130 val_a = a->at(attr_int);1131 val_b = b->at(attr_int);1132 } else if (!attr_is_int && is_val<value_object>(a) && is_val<value_object>(b)) {1133 val_a = a->at(attribute);1134 val_b = b->at(attribute);1135 } else {1136 throw raised_exception("sort: unsupported object attribute comparison between " + a->type() + " and " + b->type());1137 }1138 }1139 return value_compare(val_a, val_b, reverse ? value_compare_op::gt : value_compare_op::lt);1140 });1141 return is_val<value_tuple>(val) ? mk_val<value_tuple>(std::move(arr)) : mk_val<value_array>(std::move(arr));1142 }},1143 {"reverse", [](const func_args & args) -> value {1144 args.ensure_vals<value_array>();1145 value val = args.get_pos(0);1146 std::vector<value> arr = val->as_array(); // copy1147 std::reverse(arr.begin(), arr.end());1148 return is_val<value_tuple>(val) ? mk_val<value_tuple>(std::move(arr)) : mk_val<value_array>(std::move(arr));1149 }},1150 {"min", [](const func_args & args) -> value {1151 args.ensure_count(1, 4);1152 args.ensure_vals<value_array>();1153 value val_case = args.get_kwarg_or_pos("case_sensitive", 1);1154 value attribute = args.get_kwarg_or_pos("attribute", 2);1155 if (!attribute->is_undefined()) {1156 throw not_implemented_exception("min: attribute not implemented");1157 }1158 // FIXME: min is currently always case sensitive1159 (void) val_case;1160 const auto & arr = args.get_pos(0)->as_array();1161 if (arr.empty()) {1162 return mk_val<value_undefined>();1163 }1164 value result = arr[0];1165 for (size_t i = 1; i < arr.size(); ++i) {1166 if (value_compare(arr[i], result, value_compare_op::lt)) {1167 result = arr[i];1168 }1169 }1170 return result;1171 }},1172 {"max", [](const func_args & args) -> value {1173 args.ensure_count(1, 4);1174 args.ensure_vals<value_array>();1175 value val_case = args.get_kwarg_or_pos("case_sensitive", 1);1176 value attribute = args.get_kwarg_or_pos("attribute", 2);1177 if (!attribute->is_undefined()) {1178 throw not_implemented_exception("max: attribute not implemented");1179 }1180 // FIXME: max is currently always case sensitive1181 (void) val_case;1182 const auto & arr = args.get_pos(0)->as_array();1183 if (arr.empty()) {1184 return mk_val<value_undefined>();1185 }1186 value result = arr[0];1187 for (size_t i = 1; i < arr.size(); ++i) {1188 if (value_compare(arr[i], result, value_compare_op::gt)) {1189 result = arr[i];1190 }1191 }1192 return result;1193 }},1194 {"unique", array_unique_not_implemented},1195 };1196 return builtins;1197}1198 1199[[noreturn]] static value object_join_not_implemented(const func_args &) {1200 throw not_implemented_exception("object join not implemented");