#ifndef JINJA2CPP_SRC_STATEMENTS_H #define JINJA2CPP_SRC_STATEMENTS_H #include "renderer.h" #include "expression_evaluator.h" #include <string> #include <vector> namespace jinja2 { class Statement : public VisitableRendererBase { public: VISITABLE_STATEMENT(); }; template<typename T = Statement> using StatementPtr = std::shared_ptr<T>; template<typename CharT> class TemplateImpl; struct MacroParam { std::string paramName; ExpressionEvaluatorPtr<> defaultValue; }; inline bool operator==(const MacroParam& lhs, const MacroParam& rhs) { if (lhs.paramName != rhs.paramName) return false; if (lhs.defaultValue != rhs.defaultValue) return false; return true; } using MacroParams = std::vector<MacroParam>; class ForStatement : public Statement { public: VISITABLE_STATEMENT(); ForStatement(std::vector<std::string> vars, ExpressionEvaluatorPtr<> expr, ExpressionEvaluatorPtr<> ifExpr, bool isRecursive) : m_vars(std::move(vars)) , m_value(expr) , m_ifExpr(ifExpr) , m_isRecursive(isRecursive) { } void SetMainBody(RendererPtr renderer) { m_mainBody = std::move(renderer); } void SetElseBody(RendererPtr renderer) { m_elseBody = std::move(renderer); } void Render(OutStream& os, RenderContext& values) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const ForStatement*>(&other); if (!val) return false; if (m_vars != val->m_vars) return false; if (m_value != val->m_value) return false; if (m_ifExpr != val->m_ifExpr) return false; if (m_isRecursive != val->m_isRecursive) return false; if (m_mainBody != val->m_mainBody) return false; if (m_elseBody != val->m_elseBody) return false; return true; } private: void RenderLoop(const InternalValue &loopVal, OutStream &os, RenderContext &values, int level); ListAdapter CreateFilteredAdapter(const ListAdapter& loopItems, RenderContext& values) const; private: std::vector<std::string> m_vars; ExpressionEvaluatorPtr<> m_value; ExpressionEvaluatorPtr<> m_ifExpr; bool m_isRecursive{}; RendererPtr m_mainBody; RendererPtr m_elseBody; }; class ElseBranchStatement; class IfStatement : public Statement { public: VISITABLE_STATEMENT(); IfStatement(ExpressionEvaluatorPtr<> expr) : m_expr(expr) { } void SetMainBody(RendererPtr renderer) { m_mainBody = std::move(renderer); } void AddElseBranch(StatementPtr<ElseBranchStatement> branch) { m_elseBranches.push_back(branch); } void Render(OutStream& os, RenderContext& values) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const IfStatement*>(&other); if (!val) return false; if (m_expr != val->m_expr) return false; if (m_mainBody != val->m_mainBody) return false; if (m_elseBranches != val->m_elseBranches) return false; return true; } private: ExpressionEvaluatorPtr<> m_expr; RendererPtr m_mainBody; std::vector<StatementPtr<ElseBranchStatement>> m_elseBranches; }; class ElseBranchStatement : public Statement { public: VISITABLE_STATEMENT(); ElseBranchStatement(ExpressionEvaluatorPtr<> expr) : m_expr(expr) { } bool ShouldRender(RenderContext& values) const; void SetMainBody(RendererPtr renderer) { m_mainBody = std::move(renderer); } void Render(OutStream& os, RenderContext& values) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const ElseBranchStatement*>(&other); if (!val) return false; if (m_expr != val->m_expr) return false; if (m_mainBody != val->m_mainBody) return false; return true; } private: ExpressionEvaluatorPtr<> m_expr; RendererPtr m_mainBody; }; class SetStatement : public Statement { public: SetStatement(std::vector<std::string> fields) : m_fields(std::move(fields)) { } bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const SetStatement*>(&other); if (!val) return false; if (m_fields != val->m_fields) return false; return true; } protected: void AssignBody(InternalValue, RenderContext&); private: const std::vector<std::string> m_fields; }; class SetLineStatement final : public SetStatement { public: VISITABLE_STATEMENT(); SetLineStatement(std::vector<std::string> fields, ExpressionEvaluatorPtr<> expr) : SetStatement(std::move(fields)), m_expr(std::move(expr)) { } void Render(OutStream& os, RenderContext& values) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const SetLineStatement*>(&other); if (!val) return false; if (m_expr != val->m_expr) return false; return true; } private: const ExpressionEvaluatorPtr<> m_expr; }; class SetBlockStatement : public SetStatement { public: using SetStatement::SetStatement; void SetBody(RendererPtr renderer) { m_body = std::move(renderer); } bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const SetBlockStatement*>(&other); if (!val) return false; if (!SetStatement::IsEqual(*val)) return false; if (m_body != val->m_body) return false; return true; } protected: InternalValue RenderBody(RenderContext&); private: RendererPtr m_body; }; class SetRawBlockStatement final : public SetBlockStatement { public: VISITABLE_STATEMENT(); using SetBlockStatement::SetBlockStatement; void Render(OutStream&, RenderContext&) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const SetRawBlockStatement*>(&other); if (!val) return false; if (!SetBlockStatement::IsEqual(*val)) return false; return true; } }; class SetFilteredBlockStatement final : public SetBlockStatement { public: VISITABLE_STATEMENT(); explicit SetFilteredBlockStatement(std::vector<std::string> fields, ExpressionEvaluatorPtr<ExpressionFilter> expr) : SetBlockStatement(std::move(fields)), m_expr(std::move(expr)) { } void Render(OutStream&, RenderContext&) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const SetFilteredBlockStatement*>(&other); if (!val) return false; if (!SetBlockStatement::IsEqual(*val)) return false; if (m_expr != val->m_expr) return false; return true; } private: const ExpressionEvaluatorPtr<ExpressionFilter> m_expr; }; class ParentBlockStatement : public Statement { public: VISITABLE_STATEMENT(); ParentBlockStatement(std::string name, bool isScoped) : m_name(std::move(name)) , m_isScoped(isScoped) { } void SetMainBody(RendererPtr renderer) { m_mainBody = std::move(renderer); } void Render(OutStream &os, RenderContext &values) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const ParentBlockStatement*>(&other); if (!val) return false; if (m_name != val->m_name) return false; if (m_isScoped != val->m_isScoped) return false; if (m_mainBody != val->m_mainBody) return false; return true; } private: std::string m_name; bool m_isScoped{}; RendererPtr m_mainBody; }; class BlockStatement : public Statement { public: VISITABLE_STATEMENT(); BlockStatement(std::string name) : m_name(std::move(name)) { } auto& GetName() const {return m_name;} void SetMainBody(RendererPtr renderer) { m_mainBody = std::move(renderer); } void Render(OutStream &os, RenderContext &values) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const BlockStatement*>(&other); if (!val) return false; if (m_name != val->m_name) return false; if (m_mainBody != val->m_mainBody) return false; return true; } private: std::string m_name; RendererPtr m_mainBody; }; class ExtendsStatement : public Statement { public: VISITABLE_STATEMENT(); using BlocksCollection = std::unordered_map<std::string, StatementPtr<BlockStatement>>; ExtendsStatement(std::string name, bool isPath) : m_templateName(std::move(name)) , m_isPath(isPath) { } void Render(OutStream &os, RenderContext &values) override; void AddBlock(StatementPtr<BlockStatement> block) { m_blocks[block->GetName()] = block; } bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const ExtendsStatement*>(&other); if (!val) return false; if (m_templateName != val->m_templateName) return false; if (m_isPath != val->m_isPath) return false; if (m_blocks != val->m_blocks) return false; return true; } private: std::string m_templateName; bool m_isPath{}; BlocksCollection m_blocks; void DoRender(OutStream &os, RenderContext &values); }; class IncludeStatement : public Statement { public: VISITABLE_STATEMENT(); IncludeStatement(bool ignoreMissing, bool withContext) : m_ignoreMissing(ignoreMissing) , m_withContext(withContext) {} void SetIncludeNamesExpr(ExpressionEvaluatorPtr<> expr) { m_expr = std::move(expr); } void Render(OutStream& os, RenderContext& values) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const IncludeStatement*>(&other); if (!val) return false; if (m_ignoreMissing != val->m_ignoreMissing) return false; if (m_withContext != val->m_withContext) return false; if (m_expr != val->m_expr) return false; return true; } private: bool m_ignoreMissing{}; bool m_withContext{}; ExpressionEvaluatorPtr<> m_expr; }; class ImportStatement : public Statement { public: VISITABLE_STATEMENT(); explicit ImportStatement(bool withContext) : m_withContext(withContext) {} void SetImportNameExpr(ExpressionEvaluatorPtr<> expr) { m_nameExpr = std::move(expr); } void SetNamespace(std::string name) { m_namespace = std::move(name); } void AddNameToImport(std::string name, std::string alias) { m_namesToImport[std::move(name)] = std::move(alias); } void Render(OutStream& os, RenderContext& values) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const ImportStatement*>(&other); if (!val) return false; if (m_namespace != val->m_namespace) return false; if (m_withContext != val->m_withContext) return false; if (m_namesToImport != val->m_namesToImport) return false; if (m_nameExpr != val->m_nameExpr) return false; if (m_renderer != val->m_renderer) return false; return true; } private: void ImportNames(RenderContext& values, InternalValueMap& importedScope, const std::string& scopeName) const; private: bool m_withContext{}; RendererPtr m_renderer; ExpressionEvaluatorPtr<> m_nameExpr; std::optional<std::string> m_namespace; std::unordered_map<std::string, std::string> m_namesToImport; }; class MacroStatement : public Statement { public: VISITABLE_STATEMENT(); MacroStatement(std::string name, MacroParams params) : m_name(std::move(name)) , m_params(std::move(params)) { } void SetMainBody(RendererPtr renderer) { m_mainBody = std::move(renderer); } void Render(OutStream &os, RenderContext &values) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const MacroStatement*>(&other); if (!val) return false; if (m_name != val->m_name) return false; if (m_params != val->m_params) return false; if (m_mainBody != val->m_mainBody) return false; return true; } protected: void InvokeMacroRenderer(const std::vector<ArgumentInfo>& params, const CallParams& callParams, OutStream& stream, RenderContext& context); void SetupCallArgs(const std::vector<ArgumentInfo>& argsInfo, const CallParams& callParams, RenderContext& context, InternalValueMap& callArgs, InternalValueMap& kwArgs, InternalValueList& varArgs); virtual void SetupMacroScope(InternalValueMap& scope); std::vector<ArgumentInfo> PrepareMacroParams(RenderContext& values); protected: std::string m_name; MacroParams m_params; RendererPtr m_mainBody; }; class MacroCallStatement : public MacroStatement { public: VISITABLE_STATEMENT(); MacroCallStatement(std::string macroName, CallParamsInfo callParams, MacroParams callbackParams) : MacroStatement("$call$", std::move(callbackParams)) , m_macroName(std::move(macroName)) , m_callParams(std::move(callParams)) { } void Render(OutStream &os, RenderContext &values) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const MacroCallStatement*>(&other); if (!val) return false; if (m_macroName != val->m_macroName) return false; if (m_callParams != val->m_callParams) return false; return true; } protected: void SetupMacroScope(InternalValueMap& scope) override; protected: std::string m_macroName; CallParamsInfo m_callParams; }; class DoStatement : public Statement { public: VISITABLE_STATEMENT(); DoStatement(ExpressionEvaluatorPtr<> expr) : m_expr(expr) {} void Render(OutStream &os, RenderContext &values) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const DoStatement*>(&other); if (!val) return false; if (m_expr != val->m_expr) return false; return true; } private: ExpressionEvaluatorPtr<> m_expr; }; class WithStatement : public Statement { public: VISITABLE_STATEMENT(); void SetScopeVars(std::vector<std::pair<std::string, ExpressionEvaluatorPtr<>>> vars) { m_scopeVars = std::move(vars); } void SetMainBody(RendererPtr renderer) { m_mainBody = std::move(renderer); } void Render(OutStream &os, RenderContext &values) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const WithStatement*>(&other); if (!val) return false; if (m_scopeVars != val->m_scopeVars) return false; if (m_mainBody != val->m_mainBody) return false; return true; } private: std::vector<std::pair<std::string, ExpressionEvaluatorPtr<>>> m_scopeVars; RendererPtr m_mainBody; }; class FilterStatement : public Statement { public: VISITABLE_STATEMENT(); explicit FilterStatement(ExpressionEvaluatorPtr<ExpressionFilter> expr) : m_expr(std::move(expr)) {} void SetBody(RendererPtr renderer) { m_body = std::move(renderer); } void Render(OutStream &, RenderContext &) override; bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast<const FilterStatement*>(&other); if (!val) return false; if (m_expr != val->m_expr) return false; if (m_body != val->m_body) return false; return true; } private: ExpressionEvaluatorPtr<ExpressionFilter> m_expr; RendererPtr m_body; }; } // namespace jinja2 #endif // JINJA2CPP_SRC_STATEMENTS_H