CoolFace
Modelpublic

Felipe97/llama-cpp-compiled

sourceHugging Faceupdated 3d agoView on Hugging Face
0likes1.1kdownloads
value.cpp1587 linesDownload Raw Back to jinja
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");

Showing the first 1,200 of 1587 lines. Download the file for the rest.