4343namespace jinja {
4444
4545using json = nlohmann::json;
46- using UserFunction = std::function<json(const std::vector<json>&)>;
46+ using json = nlohmann::json;
47+ using Argument = std::pair<std::string, json>;
48+ using UserFunction = std::function<json(const std::vector<Argument>&)>;
4749
4850/* *
4951 * @brief A lightweight, C++11 compatible Jinja2 template renderer.
@@ -97,6 +99,8 @@ class Template {
9799private:
98100 struct Impl ;
99101 std::unique_ptr<Impl> m_impl;
102+
103+ void register_builtins ();
100104};
101105
102106} // namespace jinja
@@ -1121,8 +1125,8 @@ struct TestExpr : Expr {
11211125 result = val.is_boolean () && val.get <bool >() == false ;
11221126 }
11231127 // TODO: other tests
1124-
1125- if ( is_not) return ! result;
1128+ if (is_not) result = !result;
1129+ JINJA_LOG ( " TestExpr: " << expr-> dump () << " is " << ( is_not ? " not " : " " ) << test_name << " -> " << ( result ? " TRUE " : " FALSE " )) ;
11261130 return result;
11271131 }
11281132 std::string dump () const override { return " (" + expr->dump () + " is " + (is_not ? " not " : " " ) + test_name + " )" ; }
@@ -1202,9 +1206,9 @@ struct MacroNode : Node {
12021206
12031207inline json CallExpr::evaluate (Context& context) {
12041208 if (auto func = context.get_function (func_name)) {
1205- std::vector<json > arg_vals;
1209+ std::vector<Argument > arg_vals;
12061210 for (auto & arg : args) {
1207- arg_vals.push_back (arg.second ->evaluate (context));
1211+ arg_vals.push_back ({ arg.first , arg. second ->evaluate (context)} );
12081212 }
12091213 return func (arg_vals);
12101214 }
@@ -1229,69 +1233,9 @@ inline json CallExpr::evaluate(Context& context) {
12291233 }
12301234 context.pop_scope ();
12311235 return out;
1232- } else if (func_name == " namespace" ) {
1233- json ns = json::object ();
1234- for (auto & arg : args) {
1235- if (!arg.first .empty ()) {
1236- ns[arg.first ] = arg.second ->evaluate (context);
1237- }
1238- }
1239- return ns;
1240- } else if (func_name == " range" ) {
1241- long start=0 , stop=0 , step=1 ;
1242- if (args.size () == 1 ) {
1243- stop = args[0 ].second ->evaluate (context).get <long >();
1244- } else if (args.size () == 2 ) {
1245- start = args[0 ].second ->evaluate (context).get <long >();
1246- stop = args[1 ].second ->evaluate (context).get <long >();
1247- } else if (args.size () >= 3 ) {
1248- start = args[0 ].second ->evaluate (context).get <long >();
1249- stop = args[1 ].second ->evaluate (context).get <long >();
1250- step = args[2 ].second ->evaluate (context).get <long >();
1251- }
1252- json arr = json::array ();
1253- if (step > 0 ) {
1254- for (long i = start; i < stop; i += step) arr.push_back (i);
1255- } else if (step < 0 ) {
1256- for (long i = start; i > stop; i += step) arr.push_back (i);
1257- }
1258- return arr;
1259- }
1260-
1261- Macro* macro = context.get_macro (func_name);
1262- if (macro) {
1263- json scope = json::object ();
1264- size_t i = 0 ;
1265- for (const auto & call_arg : args) {
1266- if (!call_arg.first .empty ()) {
1267- scope[call_arg.first ] = call_arg.second ->evaluate (context);
1268- } else {
1269- if (i < macro->args .size ()) {
1270- scope[macro->args [i]] = call_arg.second ->evaluate (context);
1271- i++;
1272- }
1273- }
1274- }
1275- context.push_scope (scope);
1276- std::string out;
1277- for (const auto & node : macro->body ) {
1278- node->render (context, out);
1279- }
1280- context.pop_scope ();
1281- return out;
1282- } else if (func_name == " strftime_now" ) {
1283- std::string format = " %Y-%m-%d" ;
1284- if (args.size () > 0 ) {
1285- format = args[0 ].second ->evaluate (context).get <std::string>();
1286- }
1287- std::time_t t = std::time (nullptr );
1288- std::tm tm = *std::localtime (&t);
1289- std::stringstream ss;
1290- ss << std::put_time (&tm, format.c_str ());
1291- return ss.str ();
1292- }
1236+ }
12931237
1294- return " " ;
1238+ return " " ;
12951239}
12961240
12971241struct TextNode : Node {
@@ -1460,7 +1404,7 @@ struct IfNode : Node {
14601404
14611405 void render (Context& context, std::string& out) override {
14621406 bool res = is_truthy (condition->evaluate (context));
1463- JINJA_LOG (" Render If: Condition evaluated to " << (res ? " TRUE" : " FALSE" ));
1407+ JINJA_LOG (" Render If: ( " << condition-> dump () << " ) evaluated to " << (res ? " TRUE" : " FALSE" ));
14641408 if (res) {
14651409 for (const auto & node : true_body) node->render (context, out);
14661410 } else {
@@ -2092,9 +2036,60 @@ inline Template::Template(const std::string& template_str, const json& default_c
20922036 : m_impl(make_unique<Impl>()) {
20932037 m_impl->template_str = template_str;
20942038 m_impl->default_context = default_context;
2039+
2040+ register_builtins ();
2041+
20952042 m_impl->parse ();
20962043}
20972044
2045+ inline void Template::register_builtins () {
2046+ // Built-in: range
2047+ add_function (" range" , [](const std::vector<Argument>& args) -> json {
2048+ long start=0 , stop=0 , step=1 ;
2049+ if (args.size () == 1 ) {
2050+ stop = args[0 ].second .get <long >();
2051+ } else if (args.size () == 2 ) {
2052+ start = args[0 ].second .get <long >();
2053+ stop = args[1 ].second .get <long >();
2054+ } else if (args.size () >= 3 ) {
2055+ start = args[0 ].second .get <long >();
2056+ stop = args[1 ].second .get <long >();
2057+ step = args[2 ].second .get <long >();
2058+ }
2059+ json arr = json::array ();
2060+ if (step > 0 ) {
2061+ for (long i = start; i < stop; i += step) arr.push_back (i);
2062+ } else if (step < 0 ) {
2063+ for (long i = start; i > stop; i += step) arr.push_back (i);
2064+ }
2065+ return arr;
2066+ });
2067+
2068+ // Built-in: namespace
2069+ add_function (" namespace" , [](const std::vector<Argument>& args) -> json {
2070+ json ns = json::object ();
2071+ for (const auto & arg : args) {
2072+ if (!arg.first .empty ()) {
2073+ ns[arg.first ] = arg.second ;
2074+ }
2075+ }
2076+ return ns;
2077+ });
2078+
2079+ // Built-in: strftime_now
2080+ add_function (" strftime_now" , [](const std::vector<Argument>& args) -> json {
2081+ std::string format = " %Y-%m-%d" ;
2082+ if (args.size () > 0 ) {
2083+ format = args[0 ].second .get <std::string>();
2084+ }
2085+ std::time_t t = std::time (nullptr );
2086+ std::tm tm = *std::localtime (&t);
2087+ std::stringstream ss;
2088+ ss << std::put_time (&tm, format.c_str ());
2089+ return ss.str ();
2090+ });
2091+ }
2092+
20982093inline Template::~Template () = default ;
20992094
21002095inline Template::Template (Template&& other) noexcept = default;
@@ -2115,6 +2110,9 @@ inline std::string Template::render(const json& context) const {
21152110
21162111inline void Template::add_function (const std::string& name, UserFunction func) {
21172112 m_impl->functions [name] = std::move (func);
2113+ if (!m_impl->default_context .contains (name)) {
2114+ m_impl->default_context [name] = " <function " + name + " >" ;
2115+ }
21182116}
21192117
21202118inline std::string Template::apply_chat_template (
0 commit comments