From 7ed11f78f94d57f618223b7cabbe9dc8f75930fd Mon Sep 17 00:00:00 2001 From: Xuan Son Nguyen Date: Sat, 27 Dec 2025 22:10:45 +0100 Subject: [PATCH] add more builtins --- common/jinja/jinja-value.h | 16 ++- common/jinja/jinja-vm-builtins.cpp | 172 +++++++++++++++++++++++++++++ common/jinja/jinja-vm.cpp | 57 ++++++---- 3 files changed, 220 insertions(+), 25 deletions(-) diff --git a/common/jinja/jinja-value.h b/common/jinja/jinja-value.h index 289acb1c7d..ac742b2f44 100644 --- a/common/jinja/jinja-value.h +++ b/common/jinja/jinja-value.h @@ -118,6 +118,7 @@ struct value_t { } }; + struct value_int_t : public value_t { value_int_t(int64_t v) { val_int = v; } virtual std::string type() const override { return "Integer"; } @@ -125,9 +126,11 @@ struct value_int_t : public value_t { virtual double as_float() const override { return static_cast(val_int); } virtual std::string as_string() const override { return std::to_string(val_int); } virtual value clone() const override { return std::make_unique(*this); } + virtual const func_builtins & get_builtins() const override; }; using value_int = std::unique_ptr; + struct value_float_t : public value_t { value_float_t(double v) { val_flt = v; } virtual std::string type() const override { return "Float"; } @@ -135,27 +138,32 @@ struct value_float_t : public value_t { virtual int64_t as_int() const override { return static_cast(val_flt); } virtual std::string as_string() const override { return std::to_string(val_flt); } virtual value clone() const override { return std::make_unique(*this); } + virtual const func_builtins & get_builtins() const override; }; using value_float = std::unique_ptr; + struct value_string_t : public value_t { value_string_t(const std::string & v) { val_str = v; } virtual std::string type() const override { return "String"; } virtual std::string as_string() const override { return val_str; } virtual value clone() const override { return std::make_unique(*this); } - const func_builtins & get_builtins() const override; + virtual const func_builtins & get_builtins() const override; }; using value_string = std::unique_ptr; + struct value_bool_t : public value_t { value_bool_t(bool v) { val_bool = v; } virtual std::string type() const override { return "Boolean"; } virtual bool as_bool() const override { return val_bool; } virtual std::string as_string() const override { return val_bool ? "True" : "False"; } virtual value clone() const override { return std::make_unique(*this); } + virtual const func_builtins & get_builtins() const override; }; using value_bool = std::unique_ptr; + struct value_array_t : public value_t { value_array_t() { val_arr = std::make_shared>(); @@ -184,9 +192,11 @@ struct value_array_t : public value_t { tmp->val_arr = this->val_arr; return tmp; } + virtual const func_builtins & get_builtins() const override; }; using value_array = std::unique_ptr; + struct value_object_t : public value_t { value_object_t() { val_obj = std::make_shared>(); @@ -208,9 +218,11 @@ struct value_object_t : public value_t { tmp->val_obj = this->val_obj; return tmp; } + virtual const func_builtins & get_builtins() const override; }; using value_object = std::unique_ptr; + struct value_func_t : public value_t { value_func_t(func_handler & func) { val_func = func; @@ -223,6 +235,7 @@ struct value_func_t : public value_t { }; using value_func = std::unique_ptr; + struct value_null_t : public value_t { virtual std::string type() const override { return "Null"; } virtual bool is_null() const override { return true; } @@ -230,6 +243,7 @@ struct value_null_t : public value_t { }; using value_null = std::unique_ptr; + struct value_undefined_t : public value_t { virtual std::string type() const override { return "Undefined"; } virtual bool is_undefined() const override { return true; } diff --git a/common/jinja/jinja-vm-builtins.cpp b/common/jinja/jinja-vm-builtins.cpp index 85d0681867..c369455fde 100644 --- a/common/jinja/jinja-vm-builtins.cpp +++ b/common/jinja/jinja-vm-builtins.cpp @@ -8,6 +8,40 @@ namespace jinja { +const func_builtins & value_int_t::get_builtins() const { + static const func_builtins builtins = { + {"abs", [](const func_args & args) -> value { + args.ensure_vals(); + int64_t val = args.args[0]->as_int(); + return std::make_unique(val < 0 ? -val : val); + }}, + {"float", [](const func_args & args) -> value { + args.ensure_vals(); + double val = static_cast(args.args[0]->as_int()); + return std::make_unique(val); + }}, + }; + return builtins; +} + + +const func_builtins & value_float_t::get_builtins() const { + static const func_builtins builtins = { + {"abs", [](const func_args & args) -> value { + args.ensure_vals(); + double val = args.args[0]->as_float(); + return std::make_unique(val < 0.0 ? -val : val); + }}, + {"int", [](const func_args & args) -> value { + args.ensure_vals(); + int64_t val = static_cast(args.args[0]->as_float()); + return std::make_unique(val); + }}, + }; + return builtins; +} + + static std::string string_strip(const std::string & str, bool left, bool right) { size_t start = 0; size_t end = str.length(); @@ -132,8 +166,146 @@ const func_builtins & value_string_t::get_builtins() const { } return std::make_unique(str); }}, + {"int", [](const func_args & args) -> value { + args.ensure_vals(); + std::string str = args.args[0]->as_string(); + try { + return std::make_unique(std::stoi(str)); + } catch (...) { + throw std::runtime_error("Cannot convert string '" + str + "' to int"); + } + }}, + {"float", [](const func_args & args) -> value { + args.ensure_vals(); + std::string str = args.args[0]->as_string(); + try { + return std::make_unique(std::stod(str)); + } catch (...) { + throw std::runtime_error("Cannot convert string '" + str + "' to float"); + } + }}, + {"string", [](const func_args & args) -> value { + // no-op + args.ensure_vals(); + return std::make_unique(args.args[0]->as_string()); + }}, + {"indent", [](const func_args & args) -> value { + throw std::runtime_error("indent builtin not implemented"); + }}, + {"join", [](const func_args & args) -> value { + throw std::runtime_error("join builtin not implemented"); + }}, }; return builtins; }; + +const func_builtins & value_bool_t::get_builtins() const { + static const func_builtins builtins = { + {"int", [](const func_args & args) -> value { + args.ensure_vals(); + bool val = args.args[0]->as_bool(); + return std::make_unique(val ? 1 : 0); + }}, + {"float", [](const func_args & args) -> value { + args.ensure_vals(); + bool val = args.args[0]->as_bool(); + return std::make_unique(val ? 1.0 : 0.0); + }}, + {"string", [](const func_args & args) -> value { + args.ensure_vals(); + bool val = args.args[0]->as_bool(); + return std::make_unique(val ? "True" : "False"); + }}, + }; + return builtins; +} + + +const func_builtins & value_array_t::get_builtins() const { + static const func_builtins builtins = { + {"list", [](const func_args & args) -> value { + args.ensure_vals(); + const auto & arr = args.args[0]->as_array(); + auto result = std::make_unique(); + for (const auto& v : arr) { + result->val_arr->push_back(v->clone()); + } + return result; + }}, + {"first", [](const func_args & args) -> value { + args.ensure_vals(); + const auto & arr = args.args[0]->as_array(); + if (arr.empty()) { + return std::make_unique(); + } + return arr[0]->clone(); + }}, + {"last", [](const func_args & args) -> value { + args.ensure_vals(); + const auto & arr = args.args[0]->as_array(); + if (arr.empty()) { + return std::make_unique(); + } + return arr[arr.size() - 1]->clone(); + }}, + {"length", [](const func_args & args) -> value { + args.ensure_vals(); + const auto & arr = args.args[0]->as_array(); + return std::make_unique(static_cast(arr.size())); + }}, + // TODO: reverse, sort, join, string, unique + }; + return builtins; +} + + +const func_builtins & value_object_t::get_builtins() const { + static const func_builtins builtins = { + {"get", [](const func_args & args) -> value { + args.ensure_vals(); // TODO: add default value + const auto & obj = args.args[0]->as_object(); + std::string key = args.args[1]->as_string(); + auto it = obj.find(key); + if (it != obj.end()) { + return it->second->clone(); + } else { + return std::make_unique(); + } + }}, + {"keys", [](const func_args & args) -> value { + args.ensure_vals(); + const auto & obj = args.args[0]->as_object(); + auto result = std::make_unique(); + for (const auto & pair : obj) { + result->val_arr->push_back(std::make_unique(pair.first)); + } + return result; + }}, + {"values", [](const func_args & args) -> value { + args.ensure_vals(); + const auto & obj = args.args[0]->as_object(); + auto result = std::make_unique(); + for (const auto & pair : obj) { + result->val_arr->push_back(pair.second->clone()); + } + return result; + }}, + {"items", [](const func_args & args) -> value { + args.ensure_vals(); + const auto & obj = args.args[0]->as_object(); + auto result = std::make_unique(); + for (const auto & pair : obj) { + auto item = std::make_unique(); + item->val_arr->push_back(std::make_unique(pair.first)); + item->val_arr->push_back(pair.second->clone()); + result->val_arr->push_back(std::move(item)); + } + return result; + }}, + }; + return builtins; +} + + } // namespace jinja diff --git a/common/jinja/jinja-vm.cpp b/common/jinja/jinja-vm.cpp index 25106f1e4a..bd1017f5db 100644 --- a/common/jinja/jinja-vm.cpp +++ b/common/jinja/jinja-vm.cpp @@ -142,7 +142,17 @@ value binary_expression::execute(context & ctx) { value filter_expression::execute(context & ctx) { value input = operand->execute(ctx); - // value filter_func = filter->execute(ctx); + + auto try_builtin = [&](const std::string & name) -> value { + auto builtins = input->get_builtins(); + auto it = builtins.find(name); + if (it != builtins.end()) { + func_args args; + args.args.push_back(input->clone()); + return it->second(args); + } + return nullptr; + }; if (is_stmt(filter)) { auto filter_val = dynamic_cast(filter.get())->value; @@ -154,43 +164,42 @@ value filter_expression::execute(context & ctx) { if (is_val(input)) { auto & arr = input->as_array(); - if (filter_val == "list") { - return std::make_unique(input); - } else if (filter_val == "first") { - if (arr.empty()) { - return std::make_unique(); - } - return arr[0]->clone(); - } else if (filter_val == "last") { - if (arr.empty()) { - return std::make_unique(); - } - return arr[arr.size() - 1]->clone(); - } else if (filter_val == "length") { - return std::make_unique(static_cast(arr.size())); - } else { - // TODO: reverse, sort, join, string, unique - throw std::runtime_error("Unknown filter '" + filter_val + "' for array"); + auto res = try_builtin(filter_val); + if (res) { + return res; } + throw std::runtime_error("Unknown filter '" + filter_val + "' for array"); } else if (is_val(input)) { auto str = input->as_string(); auto builtins = input->get_builtins(); - auto it = builtins.find(filter_val); - if (it != builtins.end()) { - func_args args; - args.args.push_back(input->clone()); - return it->second(args); + if (filter_val == "trim") { + filter_val = "strip"; // alias + } + auto res = try_builtin(filter_val); + if (res) { + return res; } throw std::runtime_error("Unknown filter '" + filter_val + "' for string"); } else if (is_val(input) || is_val(input)) { - // TODO + auto res = try_builtin(filter_val); + if (res) { + return res; + } throw std::runtime_error("Unknown filter '" + filter_val + "' for number"); } else { throw std::runtime_error("Filters not supported for type " + input->type()); } + + } else if (is_stmt(filter)) { + // TODO + // value filter_func = filter->execute(ctx); + throw std::runtime_error("Filter with arguments not implemented"); + + } else { + throw std::runtime_error("Invalid filter expression"); } }