MQTTSuite
Loading...
Searching...
No Matches
inja::Renderer Class Reference

Class for rendering a Template with data. More...

#include <inja.hpp>

Inheritance diagram for inja::Renderer:
Collaboration diagram for inja::Renderer:

Public Member Functions

 Renderer (const RenderConfig &config, const TemplateStorage &template_storage, const FunctionStorage &function_storage)
void render_to (std::ostream &os, const Template &tmpl, const json &data, json *loop_data=nullptr)
Public Member Functions inherited from inja::NodeVisitor
virtual ~NodeVisitor ()=default

Private Types

using Op = FunctionStorage::Operation

Private Member Functions

void print_data (const std::shared_ptr< json > &value)
const std::shared_ptr< jsoneval_expression_list (const ExpressionListNode &expression_list)
void throw_renderer_error (const std::string &message, const AstNode &node)
void make_result (const json &&result)
template<size_t N, size_t N_start = 0, bool throw_not_found = true>
std::array< const json *, N > get_arguments (const FunctionNode &node)
template<bool throw_not_found = true>
Arguments get_argument_vector (const FunctionNode &node)
void visit (const BlockNode &node) override
void visit (const TextNode &node) override
void visit (const ExpressionNode &) override
void visit (const LiteralNode &node) override
void visit (const DataNode &node) override
void visit (const FunctionNode &node) override
void visit (const ExpressionListNode &node) override
void visit (const StatementNode &) override
void visit (const ForStatementNode &) override
void visit (const ForArrayStatementNode &node) override
void visit (const ForObjectStatementNode &node) override
void visit (const IfStatementNode &node) override
void visit (const IncludeStatementNode &node) override
void visit (const ExtendsStatementNode &node) override
void visit (const BlockStatementNode &node) override
void visit (const SetStatementNode &node) override

Static Private Member Functions

static bool truthy (const json *data)

Private Attributes

const RenderConfig config
const TemplateStoragetemplate_storage
const FunctionStoragefunction_storage
const Templatecurrent_template
size_t current_level {0}
std::vector< const Template * > template_stack
std::vector< const BlockStatementNode * > block_statement_stack
const jsondata_input
std::ostream * output_stream
json additional_data
jsoncurrent_loop_data = &additional_data["loop"]
std::vector< std::shared_ptr< json > > data_tmp_stack
std::stack< const json * > data_eval_stack
std::stack< const DataNode * > not_found_stack
bool break_rendering {false}

Detailed Description

Class for rendering a Template with data.

Definition at line 2329 of file inja.hpp.

Member Typedef Documentation

◆ Op

Definition at line 2330 of file inja.hpp.

Constructor & Destructor Documentation

◆ Renderer()

inja::Renderer::Renderer ( const RenderConfig & config,
const TemplateStorage & template_storage,
const FunctionStorage & function_storage )
inlineexplicit

Definition at line 2958 of file inja.hpp.

2959 : config(config)
2962 }
const RenderConfig config
Definition inja.hpp:2332
const FunctionStorage & function_storage
Definition inja.hpp:2334
const TemplateStorage & template_storage
Definition inja.hpp:2333

References config, function_storage, and template_storage.

Referenced by inja::Environment::render_to(), and visit().

Here is the caller graph for this function:

Member Function Documentation

◆ eval_expression_list()

const std::shared_ptr< json > inja::Renderer::eval_expression_list ( const ExpressionListNode & expression_list)
inlineprivate

Definition at line 2381 of file inja.hpp.

2381 {
2382 if (!expression_list.root) {
2383 throw_renderer_error("empty expression", expression_list);
2384 }
2385
2386 expression_list.root->accept(*this);
2387
2388 if (data_eval_stack.empty()) {
2389 throw_renderer_error("empty expression", expression_list);
2390 } else if (data_eval_stack.size() != 1) {
2391 throw_renderer_error("malformed expression", expression_list);
2392 }
2393
2394 const auto result = data_eval_stack.top();
2395 data_eval_stack.pop();
2396
2397 if (result == nullptr) {
2398 if (not_found_stack.empty()) {
2399 throw_renderer_error("expression could not be evaluated", expression_list);
2400 }
2401
2402 const auto node = not_found_stack.top();
2403 not_found_stack.pop();
2404
2405 throw_renderer_error("variable '" + static_cast<std::string>(node->name) + "' not found", *node);
2406 }
2407 return std::make_shared<json>(*result);
2408 }
void throw_renderer_error(const std::string &message, const AstNode &node)
Definition inja.hpp:2410
std::stack< const json * > data_eval_stack
Definition inja.hpp:2348
std::stack< const DataNode * > not_found_stack
Definition inja.hpp:2349

References inja::ExpressionNode::accept(), data_eval_stack, inja::DataNode::name, not_found_stack, inja::ExpressionListNode::root, and throw_renderer_error().

Referenced by visit(), visit(), visit(), visit(), and visit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_argument_vector()

template<bool throw_not_found = true>
Arguments inja::Renderer::get_argument_vector ( const FunctionNode & node)
inlineprivate

Definition at line 2457 of file inja.hpp.

2457 {
2458 const size_t N = node.arguments.size();
2459 for (const auto& a : node.arguments) {
2460 a->accept(*this);
2461 }
2462
2463 if (data_eval_stack.size() < N) {
2464 throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " +
2465 std::to_string(data_eval_stack.size()),
2466 node);
2467 }
2468
2469 Arguments result{N};
2470 for (size_t i = 0; i < N; i += 1) {
2471 result[N - i - 1] = data_eval_stack.top();
2472 data_eval_stack.pop();
2473
2474 if (!result[N - i - 1]) {
2475 const auto data_node = not_found_stack.top();
2476 not_found_stack.pop();
2477
2478 if (throw_not_found) {
2479 throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
2480 }
2481 }
2482 }
2483 return result;
2484 }
std::vector< const nlohmann::json * > Arguments

References inja::ExpressionNode::accept(), inja::FunctionNode::arguments, data_eval_stack, inja::DataNode::name, not_found_stack, and throw_renderer_error().

Referenced by visit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_arguments()

template<size_t N, size_t N_start = 0, bool throw_not_found = true>
std::array< const json *, N > inja::Renderer::get_arguments ( const FunctionNode & node)
inlineprivate

Definition at line 2422 of file inja.hpp.

2422 {
2423 if (node.arguments.size() < N_start + N) {
2424 throw_renderer_error("function needs " + std::to_string(N_start + N) + " variables, but has only found " +
2425 std::to_string(node.arguments.size()),
2426 node);
2427 }
2428
2429 for (size_t i = N_start; i < N_start + N; i += 1) {
2430 node.arguments[i]->accept(*this);
2431 }
2432
2433 if (data_eval_stack.size() < N) {
2434 throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " +
2435 std::to_string(data_eval_stack.size()),
2436 node);
2437 }
2438
2439 std::array<const json*, N> result;
2440 for (size_t i = 0; i < N; i += 1) {
2441 result[N - i - 1] = data_eval_stack.top();
2442 data_eval_stack.pop();
2443
2444 if (!result[N - i - 1]) {
2445 const auto data_node = not_found_stack.top();
2446 not_found_stack.pop();
2447
2448 if (throw_not_found) {
2449 throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
2450 }
2451 }
2452 }
2453 return result;
2454 }

References inja::ExpressionNode::accept(), inja::FunctionNode::arguments, data_eval_stack, inja::DataNode::name, not_found_stack, and throw_renderer_error().

Referenced by visit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ make_result()

void inja::Renderer::make_result ( const json && result)
inlineprivate

Definition at line 2415 of file inja.hpp.

2415 {
2416 auto result_ptr = std::make_shared<json>(result);
2417 data_tmp_stack.push_back(result_ptr);
2418 data_eval_stack.push(result_ptr.get());
2419 }
std::vector< std::shared_ptr< json > > data_tmp_stack
Definition inja.hpp:2347

References data_eval_stack, and data_tmp_stack.

Referenced by visit().

Here is the caller graph for this function:

◆ print_data()

void inja::Renderer::print_data ( const std::shared_ptr< json > & value)
inlineprivate

Definition at line 2364 of file inja.hpp.

2364 {
2365 if (value->is_string()) {
2366 if (config.html_autoescape) {
2367 *output_stream << htmlescape(value->get_ref<const json::string_t&>());
2368 } else {
2369 *output_stream << value->get_ref<const json::string_t&>();
2370 }
2371 } else if (value->is_number_unsigned()) {
2372 *output_stream << value->get<const json::number_unsigned_t>();
2373 } else if (value->is_number_integer()) {
2374 *output_stream << value->get<const json::number_integer_t>();
2375 } else if (value->is_null()) {
2376 } else {
2377 *output_stream << value->dump();
2378 }
2379 }
std::ostream * output_stream
Definition inja.hpp:2342
std::string htmlescape(const std::string &data)
Escapes HTML.
Definition inja.hpp:2298

References config, inja::RenderConfig::html_autoescape, inja::htmlescape(), and output_stream.

Referenced by visit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ render_to()

void inja::Renderer::render_to ( std::ostream & os,
const Template & tmpl,
const json & data,
json * loop_data = nullptr )
inline

Definition at line 2964 of file inja.hpp.

2964 {
2965 output_stream = &os;
2966 current_template = &tmpl;
2967 data_input = &data;
2968 if (loop_data != nullptr) {
2969 additional_data = *loop_data;
2971 }
2972
2973 template_stack.emplace_back(current_template);
2974 current_template->root.accept(*this);
2975
2976 data_tmp_stack.clear();
2977 }
const json * data_input
Definition inja.hpp:2341
json additional_data
Definition inja.hpp:2344
const Template * current_template
Definition inja.hpp:2336
std::vector< const Template * > template_stack
Definition inja.hpp:2338
json * current_loop_data
Definition inja.hpp:2345

References inja::BlockNode::accept(), additional_data, current_loop_data, current_template, data_input, data_tmp_stack, output_stream, inja::Template::root, and template_stack.

Referenced by inja::Environment::render_to(), visit(), and visit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ throw_renderer_error()

void inja::Renderer::throw_renderer_error ( const std::string & message,
const AstNode & node )
inlineprivate

Definition at line 2410 of file inja.hpp.

2410 {
2411 const SourceLocation loc = get_source_location(current_template->content, node.pos);
2412 INJA_THROW(RenderError(message, loc));
2413 }
#define INJA_THROW(exception)
Definition inja.hpp:55
SourceLocation get_source_location(std::string_view content, size_t pos)
Definition inja.hpp:357

References inja::Template::content, current_template, inja::get_source_location(), inja::AstNode::pos, and inja::RenderError::RenderError().

Referenced by eval_expression_list(), get_argument_vector(), get_arguments(), visit(), visit(), visit(), visit(), and visit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ truthy()

bool inja::Renderer::truthy ( const json * data)
inlinestaticprivate

Definition at line 2353 of file inja.hpp.

2353 {
2354 if (data->is_boolean()) {
2355 return data->get<bool>();
2356 } else if (data->is_number()) {
2357 return (*data != 0);
2358 } else if (data->is_null()) {
2359 return false;
2360 }
2361 return !data->empty();
2362 }

Referenced by visit(), and visit().

Here is the caller graph for this function:

◆ visit() [1/16]

void inja::Renderer::visit ( const BlockNode & node)
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2486 of file inja.hpp.

2486 {
2487 for (const auto& n : node.nodes) {
2488 n->accept(*this);
2489
2490 if (break_rendering) {
2491 break;
2492 }
2493 }
2494 }
bool break_rendering
Definition inja.hpp:2351

References inja::AstNode::accept(), break_rendering, and inja::BlockNode::nodes.

Here is the call graph for this function:

◆ visit() [2/16]

void inja::Renderer::visit ( const BlockStatementNode & node)
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2936 of file inja.hpp.

2936 {
2937 const size_t old_level = current_level;
2938 current_level = 0;
2940 const auto block_it = current_template->block_storage.find(node.name);
2941 if (block_it != current_template->block_storage.end()) {
2942 block_statement_stack.emplace_back(&node);
2943 block_it->second->block.accept(*this);
2944 block_statement_stack.pop_back();
2945 }
2946 current_level = old_level;
2948 }
size_t current_level
Definition inja.hpp:2337
std::vector< const BlockStatementNode * > block_statement_stack
Definition inja.hpp:2339

References inja::BlockNode::accept(), inja::BlockStatementNode::block, block_statement_stack, inja::Template::block_storage, current_level, current_template, inja::BlockStatementNode::name, and template_stack.

Here is the call graph for this function:

◆ visit() [3/16]

void inja::Renderer::visit ( const DataNode & node)
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2507 of file inja.hpp.

2507 {
2508 if (additional_data.contains(node.ptr)) {
2509 data_eval_stack.push(&(additional_data[node.ptr]));
2510 } else if (data_input->contains(node.ptr)) {
2511 data_eval_stack.push(&(*data_input)[node.ptr]);
2512 } else {
2513 // Try to evaluate as a no-argument callback
2514 const auto function_data = function_storage.find_function(node.name, 0);
2515 if (function_data.operation == FunctionStorage::Operation::Callback) {
2516 Arguments empty_args{};
2517 const auto value = std::make_shared<json>(function_data.callback(empty_args));
2518 data_tmp_stack.push_back(value);
2519 data_eval_stack.push(value.get());
2520 } else {
2521 data_eval_stack.push(nullptr);
2522 not_found_stack.emplace(&node);
2523 }
2524 }
2525 }

References additional_data, inja::FunctionStorage::Callback, inja::FunctionStorage::FunctionData::callback, data_eval_stack, data_input, data_tmp_stack, inja::FunctionStorage::find_function(), function_storage, inja::DataNode::name, not_found_stack, inja::FunctionStorage::FunctionData::operation, and inja::DataNode::ptr.

Here is the call graph for this function:

◆ visit() [4/16]

void inja::Renderer::visit ( const ExpressionListNode & node)
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2818 of file inja.hpp.

2818 {
2820 }
const std::shared_ptr< json > eval_expression_list(const ExpressionListNode &expression_list)
Definition inja.hpp:2381
void print_data(const std::shared_ptr< json > &value)
Definition inja.hpp:2364

References eval_expression_list(), and print_data().

Here is the call graph for this function:

◆ visit() [5/16]

void inja::Renderer::visit ( const ExpressionNode & )
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2500 of file inja.hpp.

2500 {
2501 }

◆ visit() [6/16]

void inja::Renderer::visit ( const ExtendsStatementNode & node)
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2925 of file inja.hpp.

2925 {
2926 const auto included_template_it = template_storage.find(node.file);
2927 if (included_template_it != template_storage.end()) {
2928 const Template* parent_template = &included_template_it->second;
2929 render_to(*output_stream, *parent_template, *data_input, &additional_data);
2930 break_rendering = true;
2931 } else if (config.throw_at_missing_includes) {
2932 throw_renderer_error("extends '" + node.file + "' not found", node);
2933 }
2934 }
void render_to(std::ostream &os, const Template &tmpl, const json &data, json *loop_data=nullptr)
Definition inja.hpp:2964

References additional_data, break_rendering, config, data_input, inja::ExtendsStatementNode::file, output_stream, render_to(), template_storage, inja::RenderConfig::throw_at_missing_includes, and throw_renderer_error().

Here is the call graph for this function:

◆ visit() [7/16]

void inja::Renderer::visit ( const ForArrayStatementNode & node)
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2828 of file inja.hpp.

2828 {
2829 const auto result = eval_expression_list(node.condition);
2830 if (!result->is_array()) {
2831 throw_renderer_error("object must be an array", node);
2832 }
2833
2834 if (!current_loop_data->empty()) {
2835 auto tmp = *current_loop_data; // Because of clang-3
2836 (*current_loop_data)["parent"] = std::move(tmp);
2837 }
2838
2839 size_t index = 0;
2840 (*current_loop_data)["is_first"] = true;
2841 (*current_loop_data)["is_last"] = (result->size() <= 1);
2842 for (auto it = result->begin(); it != result->end(); ++it) {
2843 additional_data[static_cast<std::string>(node.value)] = *it;
2844
2845 (*current_loop_data)["index"] = index;
2846 (*current_loop_data)["index1"] = index + 1;
2847 if (index == 1) {
2848 (*current_loop_data)["is_first"] = false;
2849 }
2850 if (index == result->size() - 1) {
2851 (*current_loop_data)["is_last"] = true;
2852 }
2853
2854 node.body.accept(*this);
2855 ++index;
2856 }
2857
2858 additional_data[static_cast<std::string>(node.value)].clear();
2859 if (!(*current_loop_data)["parent"].empty()) {
2860 const auto tmp = (*current_loop_data)["parent"];
2861 *current_loop_data = tmp;
2862 } else {
2864 }
2865 }

References inja::BlockNode::accept(), additional_data, inja::ForStatementNode::body, inja::ForStatementNode::condition, current_loop_data, eval_expression_list(), throw_renderer_error(), and inja::ForArrayStatementNode::value.

Here is the call graph for this function:

◆ visit() [8/16]

void inja::Renderer::visit ( const ForObjectStatementNode & node)
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2867 of file inja.hpp.

2867 {
2868 const auto result = eval_expression_list(node.condition);
2869 if (!result->is_object()) {
2870 throw_renderer_error("object must be an object", node);
2871 }
2872
2873 if (!current_loop_data->empty()) {
2874 (*current_loop_data)["parent"] = std::move(*current_loop_data);
2875 }
2876
2877 size_t index = 0;
2878 (*current_loop_data)["is_first"] = true;
2879 (*current_loop_data)["is_last"] = (result->size() <= 1);
2880 for (auto it = result->begin(); it != result->end(); ++it) {
2881 additional_data[static_cast<std::string>(node.key)] = it.key();
2882 additional_data[static_cast<std::string>(node.value)] = it.value();
2883
2884 (*current_loop_data)["index"] = index;
2885 (*current_loop_data)["index1"] = index + 1;
2886 if (index == 1) {
2887 (*current_loop_data)["is_first"] = false;
2888 }
2889 if (index == result->size() - 1) {
2890 (*current_loop_data)["is_last"] = true;
2891 }
2892
2893 node.body.accept(*this);
2894 ++index;
2895 }
2896
2897 additional_data[static_cast<std::string>(node.key)].clear();
2898 additional_data[static_cast<std::string>(node.value)].clear();
2899 if (!(*current_loop_data)["parent"].empty()) {
2900 *current_loop_data = std::move((*current_loop_data)["parent"]);
2901 } else {
2903 }
2904 }

References inja::BlockNode::accept(), additional_data, inja::ForStatementNode::body, inja::ForStatementNode::condition, current_loop_data, eval_expression_list(), inja::ForObjectStatementNode::key, throw_renderer_error(), and inja::ForObjectStatementNode::value.

Here is the call graph for this function:

◆ visit() [9/16]

void inja::Renderer::visit ( const ForStatementNode & )
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2825 of file inja.hpp.

2825 {
2826 }

◆ visit() [10/16]

void inja::Renderer::visit ( const FunctionNode & node)
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2527 of file inja.hpp.

2527 {
2528 switch (node.operation) {
2529 case Op::Not: {
2530 const auto args = get_arguments<1>(node);
2531 make_result(!truthy(args[0]));
2532 } break;
2533 case Op::And: {
2535 } break;
2536 case Op::Or: {
2538 } break;
2539 case Op::In: {
2540 const auto args = get_arguments<2>(node);
2541 make_result(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end());
2542 } break;
2543 case Op::Equal: {
2544 const auto args = get_arguments<2>(node);
2545 make_result(*args[0] == *args[1]);
2546 } break;
2547 case Op::NotEqual: {
2548 const auto args = get_arguments<2>(node);
2549 make_result(*args[0] != *args[1]);
2550 } break;
2551 case Op::Greater: {
2552 const auto args = get_arguments<2>(node);
2553 make_result(*args[0] > *args[1]);
2554 } break;
2555 case Op::GreaterEqual: {
2556 const auto args = get_arguments<2>(node);
2557 make_result(*args[0] >= *args[1]);
2558 } break;
2559 case Op::Less: {
2560 const auto args = get_arguments<2>(node);
2561 make_result(*args[0] < *args[1]);
2562 } break;
2563 case Op::LessEqual: {
2564 const auto args = get_arguments<2>(node);
2565 make_result(*args[0] <= *args[1]);
2566 } break;
2567 case Op::Add: {
2568 const auto args = get_arguments<2>(node);
2569 if (args[0]->is_string() && args[1]->is_string()) {
2570 make_result(args[0]->get_ref<const json::string_t&>() + args[1]->get_ref<const json::string_t&>());
2571 } else if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
2572 make_result(args[0]->get<const json::number_integer_t>() + args[1]->get<const json::number_integer_t>());
2573 } else {
2574 make_result(args[0]->get<const json::number_float_t>() + args[1]->get<const json::number_float_t>());
2575 }
2576 } break;
2577 case Op::Subtract: {
2578 const auto args = get_arguments<2>(node);
2579 if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
2580 make_result(args[0]->get<const json::number_integer_t>() - args[1]->get<const json::number_integer_t>());
2581 } else {
2582 make_result(args[0]->get<const json::number_float_t>() - args[1]->get<const json::number_float_t>());
2583 }
2584 } break;
2585 case Op::Multiplication: {
2586 const auto args = get_arguments<2>(node);
2587 if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
2588 make_result(args[0]->get<const json::number_integer_t>() * args[1]->get<const json::number_integer_t>());
2589 } else {
2590 make_result(args[0]->get<const json::number_float_t>() * args[1]->get<const json::number_float_t>());
2591 }
2592 } break;
2593 case Op::Division: {
2594 const auto args = get_arguments<2>(node);
2595 if (args[1]->get<const json::number_float_t>() == 0) {
2596 throw_renderer_error("division by zero", node);
2597 }
2598 make_result(args[0]->get<const json::number_float_t>() / args[1]->get<const json::number_float_t>());
2599 } break;
2600 case Op::Power: {
2601 const auto args = get_arguments<2>(node);
2602 if (args[0]->is_number_integer() && args[1]->get<const json::number_integer_t>() >= 0) {
2603 const auto result = static_cast<json::number_integer_t>(
2604 std::pow(args[0]->get<const json::number_integer_t>(), args[1]->get<const json::number_integer_t>()));
2605 make_result(result);
2606 } else {
2607 const auto result =
2608 std::pow(args[0]->get<const json::number_float_t>(), args[1]->get<const json::number_integer_t>());
2609 make_result(result);
2610 }
2611 } break;
2612 case Op::Modulo: {
2613 const auto args = get_arguments<2>(node);
2614 make_result(args[0]->get<const json::number_integer_t>() % args[1]->get<const json::number_integer_t>());
2615 } break;
2616 case Op::AtId: {
2617 const auto container = get_arguments<1, 0, false>(node)[0];
2618 node.arguments[1]->accept(*this);
2619 if (not_found_stack.empty()) {
2620 throw_renderer_error("could not find element with given name", node);
2621 }
2622 const auto id_node = not_found_stack.top();
2623 not_found_stack.pop();
2624 data_eval_stack.pop();
2625 data_eval_stack.push(&container->at(id_node->name));
2626 } break;
2627 case Op::At: {
2628 const auto args = get_arguments<2>(node);
2629 if (args[0]->is_object()) {
2630 data_eval_stack.push(&args[0]->at(args[1]->get<std::string>()));
2631 } else {
2632 data_eval_stack.push(&args[0]->at(args[1]->get<std::size_t>()));
2633 }
2634 } break;
2635 case Op::Capitalize: {
2636 auto result = get_arguments<1>(node)[0]->get<json::string_t>();
2637 result[0] = static_cast<char>(::toupper(result[0]));
2638 std::transform(result.begin() + 1, result.end(), result.begin() + 1, [](char c) {
2639 return static_cast<char>(::tolower(c));
2640 });
2641 make_result(std::move(result));
2642 } break;
2643 case Op::Default: {
2644 const auto test_arg = get_arguments<1, 0, false>(node)[0];
2645 data_eval_stack.push((test_arg != nullptr) ? test_arg : get_arguments<1, 1>(node)[0]);
2646 } break;
2647 case Op::DivisibleBy: {
2648 const auto args = get_arguments<2>(node);
2649 const auto divisor = args[1]->get<const json::number_integer_t>();
2650 make_result((divisor != 0) && (args[0]->get<const json::number_integer_t>() % divisor == 0));
2651 } break;
2652 case Op::Even: {
2653 make_result(get_arguments<1>(node)[0]->get<const json::number_integer_t>() % 2 == 0);
2654 } break;
2655 case Op::Exists: {
2656 auto&& name = get_arguments<1>(node)[0]->get_ref<const json::string_t&>();
2657 make_result(data_input->contains(json::json_pointer(DataNode::convert_dot_to_ptr(name))));
2658 } break;
2659 case Op::ExistsInObject: {
2660 const auto args = get_arguments<2>(node);
2661 auto&& name = args[1]->get_ref<const json::string_t&>();
2662 make_result(args[0]->find(name) != args[0]->end());
2663 } break;
2664 case Op::First: {
2665 const auto result = &get_arguments<1>(node)[0]->front();
2666 data_eval_stack.push(result);
2667 } break;
2668 case Op::Float: {
2669 make_result(std::stod(get_arguments<1>(node)[0]->get_ref<const json::string_t&>()));
2670 } break;
2671 case Op::Int: {
2672 make_result(std::stoi(get_arguments<1>(node)[0]->get_ref<const json::string_t&>()));
2673 } break;
2674 case Op::Last: {
2675 const auto result = &get_arguments<1>(node)[0]->back();
2676 data_eval_stack.push(result);
2677 } break;
2678 case Op::Length: {
2679 const auto val = get_arguments<1>(node)[0];
2680 if (val->is_string()) {
2681 make_result(val->get_ref<const json::string_t&>().length());
2682 } else {
2683 make_result(val->size());
2684 }
2685 } break;
2686 case Op::Lower: {
2687 auto result = get_arguments<1>(node)[0]->get<json::string_t>();
2688 std::transform(result.begin(), result.end(), result.begin(), [](char c) {
2689 return static_cast<char>(::tolower(c));
2690 });
2691 make_result(std::move(result));
2692 } break;
2693 case Op::Max: {
2694 const auto args = get_arguments<1>(node);
2695 const auto result = std::max_element(args[0]->begin(), args[0]->end());
2696 data_eval_stack.push(&(*result));
2697 } break;
2698 case Op::Min: {
2699 const auto args = get_arguments<1>(node);
2700 const auto result = std::min_element(args[0]->begin(), args[0]->end());
2701 data_eval_stack.push(&(*result));
2702 } break;
2703 case Op::Odd: {
2704 make_result(get_arguments<1>(node)[0]->get<const json::number_integer_t>() % 2 != 0);
2705 } break;
2706 case Op::Range: {
2707 std::vector<int> result(get_arguments<1>(node)[0]->get<const std::size_t>());
2708 std::iota(result.begin(), result.end(), 0);
2709 make_result(std::move(result));
2710 } break;
2711 case Op::Replace: {
2712 const auto args = get_arguments<3>(node);
2713 auto result = args[0]->get<std::string>();
2714 replace_substring(result, args[1]->get<std::string>(), args[2]->get<std::string>());
2715 make_result(std::move(result));
2716 } break;
2717 case Op::Round: {
2718 const auto args = get_arguments<2>(node);
2719 const auto precision = args[1]->get<const json::number_integer_t>();
2720 const double result =
2721 std::round(args[0]->get<const json::number_float_t>() * std::pow(10.0, precision)) / std::pow(10.0, precision);
2722 if (precision == 0) {
2723 make_result(static_cast<int>(result));
2724 } else {
2725 make_result(result);
2726 }
2727 } break;
2728 case Op::Sort: {
2729 auto result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<std::vector<json>>());
2730 std::sort(result_ptr->begin(), result_ptr->end());
2731 data_tmp_stack.push_back(result_ptr);
2732 data_eval_stack.push(result_ptr.get());
2733 } break;
2734 case Op::Upper: {
2735 auto result = get_arguments<1>(node)[0]->get<json::string_t>();
2736 std::transform(result.begin(), result.end(), result.begin(), [](char c) {
2737 return static_cast<char>(::toupper(c));
2738 });
2739 make_result(std::move(result));
2740 } break;
2741 case Op::IsBoolean: {
2742 make_result(get_arguments<1>(node)[0]->is_boolean());
2743 } break;
2744 case Op::IsNumber: {
2745 make_result(get_arguments<1>(node)[0]->is_number());
2746 } break;
2747 case Op::IsInteger: {
2748 make_result(get_arguments<1>(node)[0]->is_number_integer());
2749 } break;
2750 case Op::IsFloat: {
2751 make_result(get_arguments<1>(node)[0]->is_number_float());
2752 } break;
2753 case Op::IsObject: {
2754 make_result(get_arguments<1>(node)[0]->is_object());
2755 } break;
2756 case Op::IsArray: {
2757 make_result(get_arguments<1>(node)[0]->is_array());
2758 } break;
2759 case Op::IsString: {
2760 make_result(get_arguments<1>(node)[0]->is_string());
2761 } break;
2762 case Op::Callback: {
2763 auto args = get_argument_vector(node);
2764 make_result(node.callback(args));
2765 } break;
2766 case Op::Super: {
2767 const auto args = get_argument_vector(node);
2768 const size_t old_level = current_level;
2769 const size_t level_diff = (args.size() == 1) ? args[0]->get<std::size_t>() : 1;
2770 const size_t level = current_level + level_diff;
2771
2772 if (block_statement_stack.empty()) {
2773 throw_renderer_error("super() call is not within a block", node);
2774 }
2775
2776 if (level < 1 || level > template_stack.size() - 1) {
2777 throw_renderer_error("level of super() call does not match parent templates (between 1 and " +
2778 std::to_string(template_stack.size() - 1) + ")",
2779 node);
2780 }
2781
2782 const auto current_block_statement = block_statement_stack.back();
2783 const Template* new_template = template_stack.at(level);
2784 const Template* old_template = current_template;
2785 const auto block_it = new_template->block_storage.find(current_block_statement->name);
2786 if (block_it != new_template->block_storage.end()) {
2787 current_template = new_template;
2788 current_level = level;
2789 block_it->second->block.accept(*this);
2790 current_level = old_level;
2791 current_template = old_template;
2792 } else {
2793 throw_renderer_error("could not find block with name '" + current_block_statement->name + "'", node);
2794 }
2795 make_result(nullptr);
2796 } break;
2797 case Op::Join: {
2798 const auto args = get_arguments<2>(node);
2799 const auto separator = args[1]->get<json::string_t>();
2800 std::ostringstream os;
2801 std::string sep;
2802 for (const auto& value : *args[0]) {
2803 os << sep;
2804 if (value.is_string()) {
2805 os << value.get<std::string>(); // otherwise the value is surrounded with ""
2806 } else {
2807 os << value.dump();
2808 }
2809 sep = separator;
2810 }
2811 make_result(os.str());
2812 } break;
2813 case Op::None:
2814 break;
2815 }
2816 }
static std::string convert_dot_to_ptr(std::string_view ptr_name)
Definition inja.hpp:513
std::array< const json *, N > get_arguments(const FunctionNode &node)
Definition inja.hpp:2422
Arguments get_argument_vector(const FunctionNode &node)
Definition inja.hpp:2457
static bool truthy(const json *data)
Definition inja.hpp:2353
void make_result(const json &&result)
Definition inja.hpp:2415
void replace_substring(std::string &s, const std::string &f, const std::string &t)
Definition inja.hpp:380

References inja::BlockNode::accept(), inja::ExpressionNode::accept(), inja::FunctionStorage::Add, inja::FunctionStorage::And, inja::FunctionNode::arguments, inja::FunctionStorage::At, inja::FunctionStorage::AtId, inja::BlockStatementNode::block, block_statement_stack, inja::Template::block_storage, inja::FunctionStorage::Callback, inja::FunctionNode::callback, inja::FunctionStorage::Capitalize, inja::DataNode::convert_dot_to_ptr(), current_level, current_template, data_eval_stack, data_input, data_tmp_stack, inja::FunctionStorage::Default, inja::FunctionStorage::DivisibleBy, inja::FunctionStorage::Division, inja::FunctionStorage::Equal, inja::FunctionStorage::Even, inja::FunctionStorage::Exists, inja::FunctionStorage::ExistsInObject, inja::FunctionStorage::First, inja::FunctionStorage::Float, get_argument_vector(), get_arguments(), inja::FunctionStorage::Greater, inja::FunctionStorage::GreaterEqual, inja::FunctionStorage::In, inja::FunctionStorage::Int, inja::FunctionStorage::IsArray, inja::FunctionStorage::IsBoolean, inja::FunctionStorage::IsFloat, inja::FunctionStorage::IsInteger, inja::FunctionStorage::IsNumber, inja::FunctionStorage::IsObject, inja::FunctionStorage::IsString, inja::FunctionStorage::Join, inja::FunctionStorage::Last, inja::FunctionStorage::Length, inja::FunctionStorage::Less, inja::FunctionStorage::LessEqual, inja::FunctionStorage::Lower, make_result(), inja::FunctionStorage::Max, inja::FunctionStorage::Min, inja::FunctionStorage::Modulo, inja::FunctionStorage::Multiplication, inja::BlockStatementNode::name, inja::DataNode::name, inja::FunctionStorage::None, inja::FunctionStorage::Not, not_found_stack, inja::FunctionStorage::NotEqual, inja::FunctionStorage::Odd, inja::FunctionNode::operation, inja::FunctionStorage::Or, inja::FunctionStorage::Power, inja::FunctionStorage::Range, inja::FunctionStorage::Replace, inja::replace_substring(), inja::FunctionStorage::Round, inja::FunctionStorage::Sort, inja::FunctionStorage::Subtract, inja::FunctionStorage::Super, template_stack, throw_renderer_error(), truthy(), and inja::FunctionStorage::Upper.

Here is the call graph for this function:

◆ visit() [11/16]

void inja::Renderer::visit ( const IfStatementNode & node)
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2906 of file inja.hpp.

2906 {
2907 const auto result = eval_expression_list(node.condition);
2908 if (truthy(result.get())) {
2909 node.true_statement.accept(*this);
2910 } else if (node.has_false_statement) {
2911 node.false_statement.accept(*this);
2912 }
2913 }

References inja::BlockNode::accept(), inja::IfStatementNode::condition, eval_expression_list(), inja::IfStatementNode::false_statement, inja::IfStatementNode::has_false_statement, inja::IfStatementNode::true_statement, and truthy().

Here is the call graph for this function:

◆ visit() [12/16]

void inja::Renderer::visit ( const IncludeStatementNode & node)
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2915 of file inja.hpp.

2915 {
2916 auto sub_renderer = Renderer(config, template_storage, function_storage);
2917 const auto included_template_it = template_storage.find(node.file);
2918 if (included_template_it != template_storage.end()) {
2919 sub_renderer.render_to(*output_stream, included_template_it->second, *data_input, &additional_data);
2920 } else if (config.throw_at_missing_includes) {
2921 throw_renderer_error("include '" + node.file + "' not found", node);
2922 }
2923 }
Renderer(const RenderConfig &config, const TemplateStorage &template_storage, const FunctionStorage &function_storage)
Definition inja.hpp:2958

References additional_data, config, data_input, inja::IncludeStatementNode::file, function_storage, output_stream, render_to(), Renderer(), template_storage, inja::RenderConfig::throw_at_missing_includes, and throw_renderer_error().

Here is the call graph for this function:

◆ visit() [13/16]

void inja::Renderer::visit ( const LiteralNode & node)
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2503 of file inja.hpp.

2503 {
2504 data_eval_stack.push(&node.value);
2505 }

References data_eval_stack, and inja::LiteralNode::value.

◆ visit() [14/16]

void inja::Renderer::visit ( const SetStatementNode & node)
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2950 of file inja.hpp.

2950 {
2951 std::string ptr = node.key;
2952 replace_substring(ptr, ".", "/");
2953 ptr = "/" + ptr;
2954 additional_data[json::json_pointer(ptr)] = *eval_expression_list(node.expression);
2955 }

References additional_data, eval_expression_list(), inja::SetStatementNode::expression, inja::SetStatementNode::key, and inja::replace_substring().

Here is the call graph for this function:

◆ visit() [15/16]

void inja::Renderer::visit ( const StatementNode & )
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2822 of file inja.hpp.

2822 {
2823 }

◆ visit() [16/16]

void inja::Renderer::visit ( const TextNode & node)
inlineoverrideprivatevirtual

Implements inja::NodeVisitor.

Definition at line 2496 of file inja.hpp.

2496 {
2497 output_stream->write(current_template->content.c_str() + node.pos, static_cast<std::streamsize>(node.length));
2498 }

References inja::Template::content, current_template, inja::TextNode::length, output_stream, and inja::AstNode::pos.

Member Data Documentation

◆ additional_data

json inja::Renderer::additional_data
private

Definition at line 2344 of file inja.hpp.

Referenced by render_to(), visit(), visit(), visit(), visit(), visit(), and visit().

◆ block_statement_stack

std::vector<const BlockStatementNode*> inja::Renderer::block_statement_stack
private

Definition at line 2339 of file inja.hpp.

Referenced by visit(), and visit().

◆ break_rendering

bool inja::Renderer::break_rendering {false}
private

Definition at line 2351 of file inja.hpp.

2351{false};

Referenced by visit(), and visit().

◆ config

const RenderConfig inja::Renderer::config
private

Definition at line 2332 of file inja.hpp.

Referenced by print_data(), Renderer(), visit(), and visit().

◆ current_level

size_t inja::Renderer::current_level {0}
private

Definition at line 2337 of file inja.hpp.

2337{0};

Referenced by visit(), and visit().

◆ current_loop_data

json* inja::Renderer::current_loop_data = &additional_data["loop"]
private

Definition at line 2345 of file inja.hpp.

Referenced by render_to(), visit(), and visit().

◆ current_template

const Template* inja::Renderer::current_template
private

Definition at line 2336 of file inja.hpp.

Referenced by render_to(), throw_renderer_error(), visit(), visit(), and visit().

◆ data_eval_stack

std::stack<const json*> inja::Renderer::data_eval_stack
private

◆ data_input

const json* inja::Renderer::data_input
private

Definition at line 2341 of file inja.hpp.

Referenced by render_to(), visit(), visit(), visit(), and visit().

◆ data_tmp_stack

std::vector<std::shared_ptr<json> > inja::Renderer::data_tmp_stack
private

Definition at line 2347 of file inja.hpp.

Referenced by make_result(), render_to(), visit(), and visit().

◆ function_storage

const FunctionStorage& inja::Renderer::function_storage
private

Definition at line 2334 of file inja.hpp.

Referenced by Renderer(), visit(), and visit().

◆ not_found_stack

std::stack<const DataNode*> inja::Renderer::not_found_stack
private

Definition at line 2349 of file inja.hpp.

Referenced by eval_expression_list(), get_argument_vector(), get_arguments(), visit(), and visit().

◆ output_stream

std::ostream* inja::Renderer::output_stream
private

Definition at line 2342 of file inja.hpp.

Referenced by print_data(), render_to(), visit(), visit(), and visit().

◆ template_stack

std::vector<const Template*> inja::Renderer::template_stack
private

Definition at line 2338 of file inja.hpp.

Referenced by render_to(), visit(), and visit().

◆ template_storage

const TemplateStorage& inja::Renderer::template_storage
private

Definition at line 2333 of file inja.hpp.

Referenced by Renderer(), visit(), and visit().


The documentation for this class was generated from the following file: