#include "jinja-lexer.h" #include "jinja-vm.h" #include "jinja-parser.h" #include "jinja-value.h" #include #include 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 mk_val(val < 0 ? -val : val); }}, {"float", [](const func_args & args) -> value { args.ensure_vals(); double val = static_cast(args.args[0]->as_int()); return mk_val(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 mk_val(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 mk_val(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(); if (left) { while (start < end && isspace(static_cast(str[start]))) { ++start; } } if (right) { while (end > start && isspace(static_cast(str[end - 1]))) { --end; } } return str.substr(start, end - start); } static bool string_startswith(const std::string & str, const std::string & prefix) { if (str.length() < prefix.length()) return false; return str.compare(0, prefix.length(), prefix) == 0; } static bool string_endswith(const std::string & str, const std::string & suffix) { if (str.length() < suffix.length()) return false; return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0; } const func_builtins & value_string_t::get_builtins() const { static const func_builtins builtins = { {"upper", [](const func_args & args) -> value { args.ensure_vals(); std::string str = args.args[0]->as_string(); std::transform(str.begin(), str.end(), str.begin(), ::toupper); return mk_val(str); }}, {"lower", [](const func_args & args) -> value { args.ensure_vals(); std::string str = args.args[0]->as_string(); std::transform(str.begin(), str.end(), str.begin(), ::tolower); return mk_val(str); }}, {"strip", [](const func_args & args) -> value { args.ensure_vals(); std::string str = args.args[0]->as_string(); return mk_val(string_strip(str, true, true)); }}, {"rstrip", [](const func_args & args) -> value { args.ensure_vals(); std::string str = args.args[0]->as_string(); return mk_val(string_strip(str, false, true)); }}, {"lstrip", [](const func_args & args) -> value { args.ensure_vals(); std::string str = args.args[0]->as_string(); return mk_val(string_strip(str, true, false)); }}, {"title", [](const func_args & args) -> value { args.ensure_vals(); std::string str = args.args[0]->as_string(); bool capitalize_next = true; for (char &c : str) { if (isspace(static_cast(c))) { capitalize_next = true; } else if (capitalize_next) { c = ::toupper(static_cast(c)); capitalize_next = false; } else { c = ::tolower(static_cast(c)); } } return mk_val(str); }}, {"capitalize", [](const func_args & args) -> value { args.ensure_vals(); std::string str = args.args[0]->as_string(); if (!str.empty()) { str[0] = ::toupper(static_cast(str[0])); std::transform(str.begin() + 1, str.end(), str.begin() + 1, ::tolower); } return mk_val(str); }}, {"length", [](const func_args & args) -> value { args.ensure_vals(); std::string str = args.args[0]->as_string(); return mk_val(str.length()); }}, {"startswith", [](const func_args & args) -> value { args.ensure_vals(); std::string str = args.args[0]->as_string(); std::string prefix = args.args[1]->as_string(); return mk_val(string_startswith(str, prefix)); }}, {"endswith", [](const func_args & args) -> value { args.ensure_vals(); std::string str = args.args[0]->as_string(); std::string suffix = args.args[1]->as_string(); return mk_val(string_endswith(str, suffix)); }}, {"split", [](const func_args & args) -> value { args.ensure_vals(); std::string str = args.args[0]->as_string(); std::string delim = (args.args.size() > 1) ? args.args[1]->as_string() : " "; auto result = mk_val(); size_t pos = 0; std::string token; while ((pos = str.find(delim)) != std::string::npos) { token = str.substr(0, pos); result->val_arr->push_back(mk_val(token)); str.erase(0, pos + delim.length()); } result->val_arr->push_back(mk_val(str)); return std::move(result); }}, {"replace", [](const func_args & args) -> value { args.ensure_vals(); std::string str = args.args[0]->as_string(); std::string old_str = args.args[1]->as_string(); std::string new_str = args.args[2]->as_string(); size_t pos = 0; while ((pos = str.find(old_str, pos)) != std::string::npos) { str.replace(pos, old_str.length(), new_str); pos += new_str.length(); } return mk_val(str); }}, {"int", [](const func_args & args) -> value { args.ensure_vals(); std::string str = args.args[0]->as_string(); try { return mk_val(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 mk_val(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 mk_val(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 mk_val(val ? 1 : 0); }}, {"float", [](const func_args & args) -> value { args.ensure_vals(); bool val = args.args[0]->as_bool(); return mk_val(val ? 1.0 : 0.0); }}, {"string", [](const func_args & args) -> value { args.ensure_vals(); bool val = args.args[0]->as_bool(); return mk_val(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 = mk_val(); 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 mk_val(); } 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 mk_val(); } return arr[arr.size() - 1]->clone(); }}, {"length", [](const func_args & args) -> value { args.ensure_vals(); const auto & arr = args.args[0]->as_array(); return mk_val(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 mk_val(); } }}, {"keys", [](const func_args & args) -> value { args.ensure_vals(); const auto & obj = args.args[0]->as_object(); auto result = mk_val(); for (const auto & pair : obj) { result->val_arr->push_back(mk_val(pair.first)); } return result; }}, {"values", [](const func_args & args) -> value { args.ensure_vals(); const auto & obj = args.args[0]->as_object(); auto result = mk_val(); 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 = mk_val(); for (const auto & pair : obj) { auto item = mk_val(); item->val_arr->push_back(mk_val(pair.first)); item->val_arr->push_back(pair.second->clone()); result->val_arr->push_back(std::move(item)); } return result; }}, }; return builtins; } } // namespace jinja