add more builtins

This commit is contained in:
Xuan Son Nguyen 2025-12-27 22:10:45 +01:00
parent 15b3dbab05
commit 7ed11f78f9
3 changed files with 220 additions and 25 deletions

View File

@ -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<double>(val_int); }
virtual std::string as_string() const override { return std::to_string(val_int); }
virtual value clone() const override { return std::make_unique<value_int_t>(*this); }
virtual const func_builtins & get_builtins() const override;
};
using value_int = std::unique_ptr<value_int_t>;
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<int64_t>(val_flt); }
virtual std::string as_string() const override { return std::to_string(val_flt); }
virtual value clone() const override { return std::make_unique<value_float_t>(*this); }
virtual const func_builtins & get_builtins() const override;
};
using value_float = std::unique_ptr<value_float_t>;
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<value_string_t>(*this); }
const func_builtins & get_builtins() const override;
virtual const func_builtins & get_builtins() const override;
};
using value_string = std::unique_ptr<value_string_t>;
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<value_bool_t>(*this); }
virtual const func_builtins & get_builtins() const override;
};
using value_bool = std::unique_ptr<value_bool_t>;
struct value_array_t : public value_t {
value_array_t() {
val_arr = std::make_shared<std::vector<value>>();
@ -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<value_array_t>;
struct value_object_t : public value_t {
value_object_t() {
val_obj = std::make_shared<std::map<std::string, value>>();
@ -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<value_object_t>;
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<value_func_t>;
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<value_null_t>;
struct value_undefined_t : public value_t {
virtual std::string type() const override { return "Undefined"; }
virtual bool is_undefined() const override { return true; }

View File

@ -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<value_int>();
int64_t val = args.args[0]->as_int();
return std::make_unique<value_int_t>(val < 0 ? -val : val);
}},
{"float", [](const func_args & args) -> value {
args.ensure_vals<value_int>();
double val = static_cast<double>(args.args[0]->as_int());
return std::make_unique<value_float_t>(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<value_float>();
double val = args.args[0]->as_float();
return std::make_unique<value_float_t>(val < 0.0 ? -val : val);
}},
{"int", [](const func_args & args) -> value {
args.ensure_vals<value_float>();
int64_t val = static_cast<int64_t>(args.args[0]->as_float());
return std::make_unique<value_int_t>(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<value_string_t>(str);
}},
{"int", [](const func_args & args) -> value {
args.ensure_vals<value_string>();
std::string str = args.args[0]->as_string();
try {
return std::make_unique<value_int_t>(std::stoi(str));
} catch (...) {
throw std::runtime_error("Cannot convert string '" + str + "' to int");
}
}},
{"float", [](const func_args & args) -> value {
args.ensure_vals<value_string>();
std::string str = args.args[0]->as_string();
try {
return std::make_unique<value_float_t>(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<value_string>();
return std::make_unique<value_string_t>(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<value_bool>();
bool val = args.args[0]->as_bool();
return std::make_unique<value_int_t>(val ? 1 : 0);
}},
{"float", [](const func_args & args) -> value {
args.ensure_vals<value_bool>();
bool val = args.args[0]->as_bool();
return std::make_unique<value_float_t>(val ? 1.0 : 0.0);
}},
{"string", [](const func_args & args) -> value {
args.ensure_vals<value_bool>();
bool val = args.args[0]->as_bool();
return std::make_unique<value_string_t>(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<value_array>();
const auto & arr = args.args[0]->as_array();
auto result = std::make_unique<value_array_t>();
for (const auto& v : arr) {
result->val_arr->push_back(v->clone());
}
return result;
}},
{"first", [](const func_args & args) -> value {
args.ensure_vals<value_array>();
const auto & arr = args.args[0]->as_array();
if (arr.empty()) {
return std::make_unique<value_undefined_t>();
}
return arr[0]->clone();
}},
{"last", [](const func_args & args) -> value {
args.ensure_vals<value_array>();
const auto & arr = args.args[0]->as_array();
if (arr.empty()) {
return std::make_unique<value_undefined_t>();
}
return arr[arr.size() - 1]->clone();
}},
{"length", [](const func_args & args) -> value {
args.ensure_vals<value_array>();
const auto & arr = args.args[0]->as_array();
return std::make_unique<value_int_t>(static_cast<int64_t>(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<value_object, value_string>(); // 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<value_undefined_t>();
}
}},
{"keys", [](const func_args & args) -> value {
args.ensure_vals<value_object>();
const auto & obj = args.args[0]->as_object();
auto result = std::make_unique<value_array_t>();
for (const auto & pair : obj) {
result->val_arr->push_back(std::make_unique<value_string_t>(pair.first));
}
return result;
}},
{"values", [](const func_args & args) -> value {
args.ensure_vals<value_object>();
const auto & obj = args.args[0]->as_object();
auto result = std::make_unique<value_array_t>();
for (const auto & pair : obj) {
result->val_arr->push_back(pair.second->clone());
}
return result;
}},
{"items", [](const func_args & args) -> value {
args.ensure_vals<value_object>();
const auto & obj = args.args[0]->as_object();
auto result = std::make_unique<value_array_t>();
for (const auto & pair : obj) {
auto item = std::make_unique<value_array_t>();
item->val_arr->push_back(std::make_unique<value_string_t>(pair.first));
item->val_arr->push_back(pair.second->clone());
result->val_arr->push_back(std::move(item));
}
return result;
}},
};
return builtins;
}
} // namespace jinja

View File

@ -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<identifier>(filter)) {
auto filter_val = dynamic_cast<identifier*>(filter.get())->value;
@ -154,43 +164,42 @@ value filter_expression::execute(context & ctx) {
if (is_val<value_array>(input)) {
auto & arr = input->as_array();
if (filter_val == "list") {
return std::make_unique<value_array_t>(input);
} else if (filter_val == "first") {
if (arr.empty()) {
return std::make_unique<value_undefined_t>();
}
return arr[0]->clone();
} else if (filter_val == "last") {
if (arr.empty()) {
return std::make_unique<value_undefined_t>();
}
return arr[arr.size() - 1]->clone();
} else if (filter_val == "length") {
return std::make_unique<value_int_t>(static_cast<int64_t>(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<value_string>(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<value_int>(input) || is_val<value_float>(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<call_expression>(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");
}
}