@@ -706,7 +706,7 @@ enum SpaceHandling { Keep, Strip, StripSpaces, StripNewline };
706706
707707class TemplateToken {
708708public:
709- enum class Type { Text, Expression, If, Else, Elif, EndIf, For, EndFor, Generation, EndGeneration, Set, EndSet, Comment, Macro, EndMacro, Filter, EndFilter, Break, Continue };
709+ enum class Type { Text, Expression, If, Else, Elif, EndIf, For, EndFor, Generation, EndGeneration, Set, EndSet, Comment, Macro, EndMacro, Filter, EndFilter, Break, Continue, Call, EndCall };
710710
711711 static std::string typeToString (Type t) {
712712 switch (t) {
@@ -729,6 +729,8 @@ class TemplateToken {
729729 case Type::EndGeneration: return " endgeneration" ;
730730 case Type::Break: return " break" ;
731731 case Type::Continue: return " continue" ;
732+ case Type::Call: return " call" ;
733+ case Type::EndCall: return " endcall" ;
732734 }
733735 return " Unknown" ;
734736 }
@@ -846,6 +848,17 @@ struct LoopControlTemplateToken : public TemplateToken {
846848 LoopControlTemplateToken (const Location & loc, SpaceHandling pre , SpaceHandling post , LoopControlType control_type) : TemplateToken(Type::Break, loc, pre , post ), control_type(control_type) {}
847849};
848850
851+ struct CallTemplateToken : public TemplateToken {
852+ std::shared_ptr<Expression> expr;
853+ CallTemplateToken (const Location & loc, SpaceHandling pre , SpaceHandling post , std::shared_ptr<Expression> && e)
854+ : TemplateToken(Type::Call, loc, pre , post ), expr(std::move(e)) {}
855+ };
856+
857+ struct EndCallTemplateToken : public TemplateToken {
858+ EndCallTemplateToken (const Location & loc, SpaceHandling pre , SpaceHandling post )
859+ : TemplateToken(Type::EndCall, loc, pre , post ) {}
860+ };
861+
849862class TemplateNode {
850863 Location location_;
851864protected:
@@ -1050,31 +1063,36 @@ class MacroNode : public TemplateNode {
10501063 void do_render (std::ostringstream &, const std::shared_ptr<Context> & macro_context) const override {
10511064 if (!name) throw std::runtime_error (" MacroNode.name is null" );
10521065 if (!body) throw std::runtime_error (" MacroNode.body is null" );
1053- auto callable = Value::callable ([&](const std::shared_ptr<Context> & context, ArgumentsValue & args) {
1054- auto call_context = macro_context;
1066+ auto callable = Value::callable ([this , macro_context](const std::shared_ptr<Context> & call_context, ArgumentsValue & args) {
1067+ auto execution_context = Context::make (Value::object (), macro_context);
1068+
1069+ if (call_context->contains (" caller" )) {
1070+ execution_context->set (" caller" , call_context->get (" caller" ));
1071+ }
1072+
10551073 std::vector<bool > param_set (params.size (), false );
10561074 for (size_t i = 0 , n = args.args .size (); i < n; i++) {
10571075 auto & arg = args.args [i];
10581076 if (i >= params.size ()) throw std::runtime_error (" Too many positional arguments for macro " + name->get_name ());
10591077 param_set[i] = true ;
10601078 auto & param_name = params[i].first ;
1061- call_context ->set (param_name, arg);
1079+ execution_context ->set (param_name, arg);
10621080 }
10631081 for (auto & [arg_name, value] : args.kwargs ) {
10641082 auto it = named_param_positions.find (arg_name);
10651083 if (it == named_param_positions.end ()) throw std::runtime_error (" Unknown parameter name for macro " + name->get_name () + " : " + arg_name);
10661084
1067- call_context ->set (arg_name, value);
1085+ execution_context ->set (arg_name, value);
10681086 param_set[it->second ] = true ;
10691087 }
10701088 // Set default values for parameters that were not passed
10711089 for (size_t i = 0 , n = params.size (); i < n; i++) {
10721090 if (!param_set[i] && params[i].second != nullptr ) {
1073- auto val = params[i].second ->evaluate (context );
1074- call_context ->set (params[i].first , val);
1091+ auto val = params[i].second ->evaluate (call_context );
1092+ execution_context ->set (params[i].first , val);
10751093 }
10761094 }
1077- return body->render (call_context );
1095+ return body->render (execution_context );
10781096 });
10791097 macro_context->set (name->get_name (), callable);
10801098 }
@@ -1611,6 +1629,40 @@ class CallExpr : public Expression {
16111629 }
16121630};
16131631
1632+ class CallNode : public TemplateNode {
1633+ std::shared_ptr<Expression> expr;
1634+ std::shared_ptr<TemplateNode> body;
1635+
1636+ public:
1637+ CallNode (const Location & loc, std::shared_ptr<Expression> && e, std::shared_ptr<TemplateNode> && b)
1638+ : TemplateNode(loc), expr(std::move(e)), body(std::move(b)) {}
1639+
1640+ void do_render (std::ostringstream & out, const std::shared_ptr<Context> & context) const override {
1641+ if (!expr) throw std::runtime_error (" CallNode.expr is null" );
1642+ if (!body) throw std::runtime_error (" CallNode.body is null" );
1643+
1644+ auto caller = Value::callable ([this , context](const std::shared_ptr<Context> &, ArgumentsValue &) -> Value {
1645+ return Value (body->render (context));
1646+ });
1647+
1648+ context->set (" caller" , caller);
1649+
1650+ auto call_expr = dynamic_cast <CallExpr*>(expr.get ());
1651+ if (!call_expr) {
1652+ throw std::runtime_error (" Invalid call block syntax - expected function call" );
1653+ }
1654+
1655+ Value function = call_expr->object ->evaluate (context);
1656+ if (!function.is_callable ()) {
1657+ throw std::runtime_error (" Call target must be callable: " + function.dump ());
1658+ }
1659+ ArgumentsValue args = call_expr->args .evaluate (context);
1660+
1661+ Value result = function.call (context, args);
1662+ out << result.to_str ();
1663+ }
1664+ };
1665+
16141666class FilterExpr : public Expression {
16151667 std::vector<std::shared_ptr<Expression>> parts;
16161668public:
@@ -2320,7 +2372,7 @@ class Parser {
23202372 static std::regex comment_tok (R"( \{#([-~]?)([\s\S]*?)([-~]?)#\})" );
23212373 static std::regex expr_open_regex (R"( \{\{([-~])?)" );
23222374 static std::regex block_open_regex (R"( ^\{%([-~])?\s*)" );
2323- static std::regex block_keyword_tok (R"( (if|else|elif|endif|for|endfor|generation|endgeneration|set|endset|block|endblock|macro|endmacro|filter|endfilter|break|continue)\b)" );
2375+ static std::regex block_keyword_tok (R"( (if|else|elif|endif|for|endfor|generation|endgeneration|set|endset|block|endblock|macro|endmacro|filter|endfilter|break|continue|call|endcall )\b)" );
23242376 static std::regex non_text_open_regex (R"( \{\{|\{%|\{#)" );
23252377 static std::regex expr_close_regex (R"( \s*([-~])?\}\})" );
23262378 static std::regex block_close_regex (R"( \s*([-~])?%\})" );
@@ -2443,6 +2495,15 @@ class Parser {
24432495 } else if (keyword == " endmacro" ) {
24442496 auto post_space = parseBlockClose ();
24452497 tokens.push_back (std::make_unique<EndMacroTemplateToken>(location, pre_space, post_space));
2498+ } else if (keyword == " call" ) {
2499+ auto expr = parseExpression ();
2500+ if (!expr) throw std::runtime_error (" Expected expression in call block" );
2501+
2502+ auto post_space = parseBlockClose ();
2503+ tokens.push_back (std::make_unique<CallTemplateToken>(location, pre_space, post_space, std::move (expr)));
2504+ } else if (keyword == " endcall" ) {
2505+ auto post_space = parseBlockClose ();
2506+ tokens.push_back (std::make_unique<EndCallTemplateToken>(location, pre_space, post_space));
24462507 } else if (keyword == " filter" ) {
24472508 auto filter = parseExpression ();
24482509 if (!filter) throw std::runtime_error (" Expected expression in filter block" );
@@ -2575,6 +2636,12 @@ class Parser {
25752636 throw unterminated (**start);
25762637 }
25772638 children.emplace_back (std::make_shared<MacroNode>(token->location , std::move (macro_token->name ), std::move (macro_token->params ), std::move (body)));
2639+ } else if (auto call_token = dynamic_cast <CallTemplateToken*>(token.get ())) {
2640+ auto body = parseTemplate (begin, it, end);
2641+ if (it == end || (*(it++))->type != TemplateToken::Type::EndCall) {
2642+ throw unterminated (**start);
2643+ }
2644+ children.emplace_back (std::make_shared<CallNode>(token->location , std::move (call_token->expr ), std::move (body)));
25782645 } else if (auto filter_token = dynamic_cast <FilterTemplateToken*>(token.get ())) {
25792646 auto body = parseTemplate (begin, it, end);
25802647 if (it == end || (*(it++))->type != TemplateToken::Type::EndFilter) {
@@ -2588,6 +2655,7 @@ class Parser {
25882655 } else if (dynamic_cast <EndForTemplateToken*>(token.get ())
25892656 || dynamic_cast <EndSetTemplateToken*>(token.get ())
25902657 || dynamic_cast <EndMacroTemplateToken*>(token.get ())
2658+ || dynamic_cast <EndCallTemplateToken*>(token.get ())
25912659 || dynamic_cast <EndFilterTemplateToken*>(token.get ())
25922660 || dynamic_cast <EndIfTemplateToken*>(token.get ())
25932661 || dynamic_cast <ElseTemplateToken*>(token.get ())
0 commit comments