Inja 3.3.0
A Template Engine for Modern C++
Loading...
Searching...
No Matches
renderer.hpp
1#ifndef INCLUDE_INJA_RENDERER_HPP_
2#define INCLUDE_INJA_RENDERER_HPP_
3
4#include <algorithm>
5#include <numeric>
6#include <string>
7#include <utility>
8#include <vector>
9
10#include <nlohmann/json.hpp>
11
12#include "config.hpp"
13#include "exceptions.hpp"
14#include "node.hpp"
15#include "template.hpp"
16#include "utils.hpp"
17
18namespace inja {
19
23class Renderer : public NodeVisitor {
24 using Op = FunctionStorage::Operation;
25
26 const RenderConfig config;
27 const TemplateStorage &template_storage;
28 const FunctionStorage &function_storage;
29
30 const Template *current_template;
31 size_t current_level {0};
32 std::vector<const Template*> template_stack;
33 std::vector<const BlockStatementNode*> block_statement_stack;
34
35 const json *json_input;
36 std::ostream *output_stream;
37
38 json json_additional_data;
39 json* current_loop_data = &json_additional_data["loop"];
40
41 std::vector<std::shared_ptr<json>> json_tmp_stack;
42 std::stack<const json*> json_eval_stack;
43 std::stack<const JsonNode*> not_found_stack;
44
45 bool break_rendering {false};
46
47 bool truthy(const json* data) const {
48 if (data->is_boolean()) {
49 return data->get<bool>();
50 } else if (data->is_number()) {
51 return (*data != 0);
52 } else if (data->is_null()) {
53 return false;
54 }
55 return !data->empty();
56 }
57
58 void print_json(const std::shared_ptr<json> value) {
59 if (value->is_string()) {
60 *output_stream << value->get_ref<const json::string_t&>();
61 } else if (value->is_number_integer()) {
62 *output_stream << value->get<const json::number_integer_t>();
63 } else if (value->is_null()) {
64 } else {
65 *output_stream << value->dump();
66 }
67 }
68
69 const std::shared_ptr<json> eval_expression_list(const ExpressionListNode& expression_list) {
70 if (!expression_list.root) {
71 throw_renderer_error("empty expression", expression_list);
72 }
73
74 expression_list.root->accept(*this);
75
76 if (json_eval_stack.empty()) {
77 throw_renderer_error("empty expression", expression_list);
78 } else if (json_eval_stack.size() != 1) {
79 throw_renderer_error("malformed expression", expression_list);
80 }
81
82 const auto result = json_eval_stack.top();
83 json_eval_stack.pop();
84
85 if (!result) {
86 if (not_found_stack.empty()) {
87 throw_renderer_error("expression could not be evaluated", expression_list);
88 }
89
90 auto node = not_found_stack.top();
91 not_found_stack.pop();
92
93 throw_renderer_error("variable '" + static_cast<std::string>(node->name) + "' not found", *node);
94 }
95 return std::make_shared<json>(*result);
96 }
97
98 void throw_renderer_error(const std::string &message, const AstNode& node) {
99 SourceLocation loc = get_source_location(current_template->content, node.pos);
100 INJA_THROW(RenderError(message, loc));
101 }
102
103 template<size_t N, size_t N_start = 0, bool throw_not_found=true>
104 std::array<const json*, N> get_arguments(const FunctionNode& node) {
105 if (node.arguments.size() < N_start + N) {
106 throw_renderer_error("function needs " + std::to_string(N_start + N) + " variables, but has only found " + std::to_string(node.arguments.size()), node);
107 }
108
109 for (size_t i = N_start; i < N_start + N; i += 1) {
110 node.arguments[i]->accept(*this);
111 }
112
113 if (json_eval_stack.size() < N) {
114 throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(json_eval_stack.size()), node);
115 }
116
117 std::array<const json*, N> result;
118 for (size_t i = 0; i < N; i += 1) {
119 result[N - i - 1] = json_eval_stack.top();
120 json_eval_stack.pop();
121
122 if (!result[N - i - 1]) {
123 const auto json_node = not_found_stack.top();
124 not_found_stack.pop();
125
126 if (throw_not_found) {
127 throw_renderer_error("variable '" + static_cast<std::string>(json_node->name) + "' not found", *json_node);
128 }
129 }
130 }
131 return result;
132 }
133
134 template<bool throw_not_found=true>
135 Arguments get_argument_vector(const FunctionNode& node) {
136 const size_t N = node.arguments.size();
137 for (auto a: node.arguments) {
138 a->accept(*this);
139 }
140
141 if (json_eval_stack.size() < N) {
142 throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(json_eval_stack.size()), node);
143 }
144
145 Arguments result {N};
146 for (size_t i = 0; i < N; i += 1) {
147 result[N - i - 1] = json_eval_stack.top();
148 json_eval_stack.pop();
149
150 if (!result[N - i - 1]) {
151 const auto json_node = not_found_stack.top();
152 not_found_stack.pop();
153
154 if (throw_not_found) {
155 throw_renderer_error("variable '" + static_cast<std::string>(json_node->name) + "' not found", *json_node);
156 }
157 }
158 }
159 return result;
160 }
161
162 void visit(const BlockNode& node) {
163 for (auto& n : node.nodes) {
164 n->accept(*this);
165
166 if (break_rendering) {
167 break;
168 }
169 }
170 }
171
172 void visit(const TextNode& node) {
173 output_stream->write(current_template->content.c_str() + node.pos, node.length);
174 }
175
176 void visit(const ExpressionNode&) { }
177
178 void visit(const LiteralNode& node) {
179 json_eval_stack.push(&node.value);
180 }
181
182 void visit(const JsonNode& node) {
183 if (json_additional_data.contains(node.ptr)) {
184 json_eval_stack.push(&(json_additional_data[node.ptr]));
185
186 } else if (json_input->contains(node.ptr)) {
187 json_eval_stack.push(&(*json_input)[node.ptr]);
188
189 } else {
190 // Try to evaluate as a no-argument callback
191 const auto function_data = function_storage.find_function(node.name, 0);
192 if (function_data.operation == FunctionStorage::Operation::Callback) {
193 Arguments empty_args {};
194 const auto value = std::make_shared<json>(function_data.callback(empty_args));
195 json_tmp_stack.push_back(value);
196 json_eval_stack.push(value.get());
197
198 } else {
199 json_eval_stack.push(nullptr);
200 not_found_stack.emplace(&node);
201 }
202 }
203 }
204
205 void visit(const FunctionNode& node) {
206 std::shared_ptr<json> result_ptr;
207
208 switch (node.operation) {
209 case Op::Not: {
210 const auto args = get_arguments<1>(node);
211 result_ptr = std::make_shared<json>(!truthy(args[0]));
212 json_tmp_stack.push_back(result_ptr);
213 json_eval_stack.push(result_ptr.get());
214 } break;
215 case Op::And: {
216 result_ptr = std::make_shared<json>(truthy(get_arguments<1, 0>(node)[0]) && truthy(get_arguments<1, 1>(node)[0]));
217 json_tmp_stack.push_back(result_ptr);
218 json_eval_stack.push(result_ptr.get());
219 } break;
220 case Op::Or: {
221 result_ptr = std::make_shared<json>(truthy(get_arguments<1, 0>(node)[0]) || truthy(get_arguments<1, 1>(node)[0]));
222 json_tmp_stack.push_back(result_ptr);
223 json_eval_stack.push(result_ptr.get());
224 } break;
225 case Op::In: {
226 const auto args = get_arguments<2>(node);
227 result_ptr = std::make_shared<json>(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end());
228 json_tmp_stack.push_back(result_ptr);
229 json_eval_stack.push(result_ptr.get());
230 } break;
231 case Op::Equal: {
232 const auto args = get_arguments<2>(node);
233 result_ptr = std::make_shared<json>(*args[0] == *args[1]);
234 json_tmp_stack.push_back(result_ptr);
235 json_eval_stack.push(result_ptr.get());
236 } break;
237 case Op::NotEqual: {
238 const auto args = get_arguments<2>(node);
239 result_ptr = std::make_shared<json>(*args[0] != *args[1]);
240 json_tmp_stack.push_back(result_ptr);
241 json_eval_stack.push(result_ptr.get());
242 } break;
243 case Op::Greater: {
244 const auto args = get_arguments<2>(node);
245 result_ptr = std::make_shared<json>(*args[0] > *args[1]);
246 json_tmp_stack.push_back(result_ptr);
247 json_eval_stack.push(result_ptr.get());
248 } break;
249 case Op::GreaterEqual: {
250 const auto args = get_arguments<2>(node);
251 result_ptr = std::make_shared<json>(*args[0] >= *args[1]);
252 json_tmp_stack.push_back(result_ptr);
253 json_eval_stack.push(result_ptr.get());
254 } break;
255 case Op::Less: {
256 const auto args = get_arguments<2>(node);
257 result_ptr = std::make_shared<json>(*args[0] < *args[1]);
258 json_tmp_stack.push_back(result_ptr);
259 json_eval_stack.push(result_ptr.get());
260 } break;
261 case Op::LessEqual: {
262 const auto args = get_arguments<2>(node);
263 result_ptr = std::make_shared<json>(*args[0] <= *args[1]);
264 json_tmp_stack.push_back(result_ptr);
265 json_eval_stack.push(result_ptr.get());
266 } break;
267 case Op::Add: {
268 const auto args = get_arguments<2>(node);
269 if (args[0]->is_string() && args[1]->is_string()) {
270 result_ptr = std::make_shared<json>(args[0]->get_ref<const std::string&>() + args[1]->get_ref<const std::string&>());
271 json_tmp_stack.push_back(result_ptr);
272 } else if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
273 result_ptr = std::make_shared<json>(args[0]->get<int>() + args[1]->get<int>());
274 json_tmp_stack.push_back(result_ptr);
275 } else {
276 result_ptr = std::make_shared<json>(args[0]->get<double>() + args[1]->get<double>());
277 json_tmp_stack.push_back(result_ptr);
278 }
279 json_eval_stack.push(result_ptr.get());
280 } break;
281 case Op::Subtract: {
282 const auto args = get_arguments<2>(node);
283 if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
284 result_ptr = std::make_shared<json>(args[0]->get<int>() - args[1]->get<int>());
285 json_tmp_stack.push_back(result_ptr);
286 } else {
287 result_ptr = std::make_shared<json>(args[0]->get<double>() - args[1]->get<double>());
288 json_tmp_stack.push_back(result_ptr);
289 }
290 json_eval_stack.push(result_ptr.get());
291 } break;
292 case Op::Multiplication: {
293 const auto args = get_arguments<2>(node);
294 if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
295 result_ptr = std::make_shared<json>(args[0]->get<int>() * args[1]->get<int>());
296 json_tmp_stack.push_back(result_ptr);
297 } else {
298 result_ptr = std::make_shared<json>(args[0]->get<double>() * args[1]->get<double>());
299 json_tmp_stack.push_back(result_ptr);
300 }
301 json_eval_stack.push(result_ptr.get());
302 } break;
303 case Op::Division: {
304 const auto args = get_arguments<2>(node);
305 if (args[1]->get<double>() == 0) {
306 throw_renderer_error("division by zero", node);
307 }
308 result_ptr = std::make_shared<json>(args[0]->get<double>() / args[1]->get<double>());
309 json_tmp_stack.push_back(result_ptr);
310 json_eval_stack.push(result_ptr.get());
311 } break;
312 case Op::Power: {
313 const auto args = get_arguments<2>(node);
314 if (args[0]->is_number_integer() && args[1]->get<int>() >= 0) {
315 int result = static_cast<int>(std::pow(args[0]->get<int>(), args[1]->get<int>()));
316 result_ptr = std::make_shared<json>(std::move(result));
317 json_tmp_stack.push_back(result_ptr);
318 } else {
319 double result = std::pow(args[0]->get<double>(), args[1]->get<int>());
320 result_ptr = std::make_shared<json>(std::move(result));
321 json_tmp_stack.push_back(result_ptr);
322 }
323 json_eval_stack.push(result_ptr.get());
324 } break;
325 case Op::Modulo: {
326 const auto args = get_arguments<2>(node);
327 result_ptr = std::make_shared<json>(args[0]->get<int>() % args[1]->get<int>());
328 json_tmp_stack.push_back(result_ptr);
329 json_eval_stack.push(result_ptr.get());
330 } break;
331 case Op::AtId: {
332 const auto container = get_arguments<1, 0, false>(node)[0];
333 node.arguments[1]->accept(*this);
334 if (not_found_stack.empty()) {
335 throw_renderer_error("could not find element with given name", node);
336 }
337 const auto id_node = not_found_stack.top();
338 not_found_stack.pop();
339 json_eval_stack.pop();
340 json_eval_stack.push(&container->at(id_node->name));
341 } break;
342 case Op::At: {
343 const auto args = get_arguments<2>(node);
344 if (args[0]->is_object()) {
345 json_eval_stack.push(&args[0]->at(args[1]->get<std::string>()));
346 } else {
347 json_eval_stack.push(&args[0]->at(args[1]->get<int>()));
348 }
349 } break;
350 case Op::Default: {
351 const auto test_arg = get_arguments<1, 0, false>(node)[0];
352 json_eval_stack.push(test_arg ? test_arg : get_arguments<1, 1>(node)[0]);
353 } break;
354 case Op::DivisibleBy: {
355 const auto args = get_arguments<2>(node);
356 const int divisor = args[1]->get<int>();
357 result_ptr = std::make_shared<json>((divisor != 0) && (args[0]->get<int>() % divisor == 0));
358 json_tmp_stack.push_back(result_ptr);
359 json_eval_stack.push(result_ptr.get());
360 } break;
361 case Op::Even: {
362 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<int>() % 2 == 0);
363 json_tmp_stack.push_back(result_ptr);
364 json_eval_stack.push(result_ptr.get());
365 } break;
366 case Op::Exists: {
367 auto &&name = get_arguments<1>(node)[0]->get_ref<const std::string &>();
368 result_ptr = std::make_shared<json>(json_input->contains(json::json_pointer(JsonNode::convert_dot_to_json_ptr(name))));
369 json_tmp_stack.push_back(result_ptr);
370 json_eval_stack.push(result_ptr.get());
371 } break;
372 case Op::ExistsInObject: {
373 const auto args = get_arguments<2>(node);
374 auto &&name = args[1]->get_ref<const std::string &>();
375 result_ptr = std::make_shared<json>(args[0]->find(name) != args[0]->end());
376 json_tmp_stack.push_back(result_ptr);
377 json_eval_stack.push(result_ptr.get());
378 } break;
379 case Op::First: {
380 const auto result = &get_arguments<1>(node)[0]->front();
381 json_eval_stack.push(result);
382 } break;
383 case Op::Float: {
384 result_ptr = std::make_shared<json>(std::stod(get_arguments<1>(node)[0]->get_ref<const std::string &>()));
385 json_tmp_stack.push_back(result_ptr);
386 json_eval_stack.push(result_ptr.get());
387 } break;
388 case Op::Int: {
389 result_ptr = std::make_shared<json>(std::stoi(get_arguments<1>(node)[0]->get_ref<const std::string &>()));
390 json_tmp_stack.push_back(result_ptr);
391 json_eval_stack.push(result_ptr.get());
392 } break;
393 case Op::Last: {
394 const auto result = &get_arguments<1>(node)[0]->back();
395 json_eval_stack.push(result);
396 } break;
397 case Op::Length: {
398 const auto val = get_arguments<1>(node)[0];
399 if (val->is_string()) {
400 result_ptr = std::make_shared<json>(val->get_ref<const std::string &>().length());
401 } else {
402 result_ptr = std::make_shared<json>(val->size());
403 }
404 json_tmp_stack.push_back(result_ptr);
405 json_eval_stack.push(result_ptr.get());
406 } break;
407 case Op::Lower: {
408 std::string result = get_arguments<1>(node)[0]->get<std::string>();
409 std::transform(result.begin(), result.end(), result.begin(), ::tolower);
410 result_ptr = std::make_shared<json>(std::move(result));
411 json_tmp_stack.push_back(result_ptr);
412 json_eval_stack.push(result_ptr.get());
413 } break;
414 case Op::Max: {
415 const auto args = get_arguments<1>(node);
416 const auto result = std::max_element(args[0]->begin(), args[0]->end());
417 json_eval_stack.push(&(*result));
418 } break;
419 case Op::Min: {
420 const auto args = get_arguments<1>(node);
421 const auto result = std::min_element(args[0]->begin(), args[0]->end());
422 json_eval_stack.push(&(*result));
423 } break;
424 case Op::Odd: {
425 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<int>() % 2 != 0);
426 json_tmp_stack.push_back(result_ptr);
427 json_eval_stack.push(result_ptr.get());
428 } break;
429 case Op::Range: {
430 std::vector<int> result(get_arguments<1>(node)[0]->get<int>());
431 std::iota(result.begin(), result.end(), 0);
432 result_ptr = std::make_shared<json>(std::move(result));
433 json_tmp_stack.push_back(result_ptr);
434 json_eval_stack.push(result_ptr.get());
435 } break;
436 case Op::Round: {
437 const auto args = get_arguments<2>(node);
438 const int precision = args[1]->get<int>();
439 const double result = std::round(args[0]->get<double>() * std::pow(10.0, precision)) / std::pow(10.0, precision);
440 if(0==precision){
441 result_ptr = std::make_shared<json>(int(result));
442 }else{
443 result_ptr = std::make_shared<json>(std::move(result));
444 }
445 json_tmp_stack.push_back(result_ptr);
446 json_eval_stack.push(result_ptr.get());
447 } break;
448 case Op::Sort: {
449 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<std::vector<json>>());
450 std::sort(result_ptr->begin(), result_ptr->end());
451 json_tmp_stack.push_back(result_ptr);
452 json_eval_stack.push(result_ptr.get());
453 } break;
454 case Op::Upper: {
455 std::string result = get_arguments<1>(node)[0]->get<std::string>();
456 std::transform(result.begin(), result.end(), result.begin(), ::toupper);
457 result_ptr = std::make_shared<json>(std::move(result));
458 json_tmp_stack.push_back(result_ptr);
459 json_eval_stack.push(result_ptr.get());
460 } break;
461 case Op::IsBoolean: {
462 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_boolean());
463 json_tmp_stack.push_back(result_ptr);
464 json_eval_stack.push(result_ptr.get());
465 } break;
466 case Op::IsNumber: {
467 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number());
468 json_tmp_stack.push_back(result_ptr);
469 json_eval_stack.push(result_ptr.get());
470 } break;
471 case Op::IsInteger: {
472 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number_integer());
473 json_tmp_stack.push_back(result_ptr);
474 json_eval_stack.push(result_ptr.get());
475 } break;
476 case Op::IsFloat: {
477 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number_float());
478 json_tmp_stack.push_back(result_ptr);
479 json_eval_stack.push(result_ptr.get());
480 } break;
481 case Op::IsObject: {
482 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_object());
483 json_tmp_stack.push_back(result_ptr);
484 json_eval_stack.push(result_ptr.get());
485 } break;
486 case Op::IsArray: {
487 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_array());
488 json_tmp_stack.push_back(result_ptr);
489 json_eval_stack.push(result_ptr.get());
490 } break;
491 case Op::IsString: {
492 result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_string());
493 json_tmp_stack.push_back(result_ptr);
494 json_eval_stack.push(result_ptr.get());
495 } break;
496 case Op::Callback: {
497 auto args = get_argument_vector(node);
498 result_ptr = std::make_shared<json>(node.callback(args));
499 json_tmp_stack.push_back(result_ptr);
500 json_eval_stack.push(result_ptr.get());
501 } break;
502 case Op::Super: {
503 const auto args = get_argument_vector(node);
504 const size_t old_level = current_level;
505 const size_t level_diff = (args.size() == 1) ? args[0]->get<int>() : 1;
506 const size_t level = current_level + level_diff;
507
508 if (block_statement_stack.empty()) {
509 throw_renderer_error("super() call is not within a block", node);
510 }
511
512 if (level < 1 || level > template_stack.size() - 1) {
513 throw_renderer_error("level of super() call does not match parent templates (between 1 and " + std::to_string(template_stack.size() - 1) + ")", node);
514 }
515
516 const auto current_block_statement = block_statement_stack.back();
517 const Template *new_template = template_stack.at(level);
518 const Template *old_template = current_template;
519 const auto block_it = new_template->block_storage.find(current_block_statement->name);
520 if (block_it != new_template->block_storage.end()) {
521 current_template = new_template;
522 current_level = level;
523 block_it->second->block.accept(*this);
524 current_level = old_level;
525 current_template = old_template;
526 } else {
527 throw_renderer_error("could not find block with name '" + current_block_statement->name + "'", node);
528 }
529 result_ptr = std::make_shared<json>(nullptr);
530 json_tmp_stack.push_back(result_ptr);
531 json_eval_stack.push(result_ptr.get());
532 } break;
533 case Op::Join: {
534 const auto args = get_arguments<2>(node);
535 const auto separator = args[1]->get<std::string>();
536 std::ostringstream os;
537 std::string sep;
538 for (const auto& value : *args[0]) {
539 os << sep;
540 if (value.is_string()) {
541 os << value.get<std::string>(); // otherwise the value is surrounded with ""
542 } else {
543 os << value;
544 }
545 sep = separator;
546 }
547 result_ptr = std::make_shared<json>(os.str());
548 json_tmp_stack.push_back(result_ptr);
549 json_eval_stack.push(result_ptr.get());
550 } break;
551 case Op::ParenLeft:
552 case Op::ParenRight:
553 case Op::None:
554 break;
555 }
556 }
557
558 void visit(const ExpressionListNode& node) {
559 print_json(eval_expression_list(node));
560 }
561
562 void visit(const StatementNode&) { }
563
564 void visit(const ForStatementNode&) { }
565
566 void visit(const ForArrayStatementNode& node) {
567 const auto result = eval_expression_list(node.condition);
568 if (!result->is_array()) {
569 throw_renderer_error("object must be an array", node);
570 }
571
572 if (!current_loop_data->empty()) {
573 auto tmp = *current_loop_data; // Because of clang-3
574 (*current_loop_data)["parent"] = std::move(tmp);
575 }
576
577 size_t index = 0;
578 (*current_loop_data)["is_first"] = true;
579 (*current_loop_data)["is_last"] = (result->size() <= 1);
580 for (auto it = result->begin(); it != result->end(); ++it) {
581 json_additional_data[static_cast<std::string>(node.value)] = *it;
582
583 (*current_loop_data)["index"] = index;
584 (*current_loop_data)["index1"] = index + 1;
585 if (index == 1) {
586 (*current_loop_data)["is_first"] = false;
587 }
588 if (index == result->size() - 1) {
589 (*current_loop_data)["is_last"] = true;
590 }
591
592 node.body.accept(*this);
593 ++index;
594 }
595
596 json_additional_data[static_cast<std::string>(node.value)].clear();
597 if (!(*current_loop_data)["parent"].empty()) {
598 const auto tmp = (*current_loop_data)["parent"];
599 *current_loop_data = std::move(tmp);
600 } else {
601 current_loop_data = &json_additional_data["loop"];
602 }
603 }
604
605 void visit(const ForObjectStatementNode& node) {
606 const auto result = eval_expression_list(node.condition);
607 if (!result->is_object()) {
608 throw_renderer_error("object must be an object", node);
609 }
610
611 if (!current_loop_data->empty()) {
612 (*current_loop_data)["parent"] = std::move(*current_loop_data);
613 }
614
615 size_t index = 0;
616 (*current_loop_data)["is_first"] = true;
617 (*current_loop_data)["is_last"] = (result->size() <= 1);
618 for (auto it = result->begin(); it != result->end(); ++it) {
619 json_additional_data[static_cast<std::string>(node.key)] = it.key();
620 json_additional_data[static_cast<std::string>(node.value)] = it.value();
621
622 (*current_loop_data)["index"] = index;
623 (*current_loop_data)["index1"] = index + 1;
624 if (index == 1) {
625 (*current_loop_data)["is_first"] = false;
626 }
627 if (index == result->size() - 1) {
628 (*current_loop_data)["is_last"] = true;
629 }
630
631 node.body.accept(*this);
632 ++index;
633 }
634
635 json_additional_data[static_cast<std::string>(node.key)].clear();
636 json_additional_data[static_cast<std::string>(node.value)].clear();
637 if (!(*current_loop_data)["parent"].empty()) {
638 *current_loop_data = std::move((*current_loop_data)["parent"]);
639 } else {
640 current_loop_data = &json_additional_data["loop"];
641 }
642 }
643
644 void visit(const IfStatementNode& node) {
645 const auto result = eval_expression_list(node.condition);
646 if (truthy(result.get())) {
647 node.true_statement.accept(*this);
648 } else if (node.has_false_statement) {
649 node.false_statement.accept(*this);
650 }
651 }
652
653 void visit(const IncludeStatementNode& node) {
654 auto sub_renderer = Renderer(config, template_storage, function_storage);
655 const auto included_template_it = template_storage.find(node.file);
656 if (included_template_it != template_storage.end()) {
657 sub_renderer.render_to(*output_stream, included_template_it->second, *json_input, &json_additional_data);
658 } else if (config.throw_at_missing_includes) {
659 throw_renderer_error("include '" + node.file + "' not found", node);
660 }
661 }
662
663 void visit(const ExtendsStatementNode& node) {
664 const auto included_template_it = template_storage.find(node.file);
665 if (included_template_it != template_storage.end()) {
666 const Template *parent_template = &included_template_it->second;
667 render_to(*output_stream, *parent_template, *json_input, &json_additional_data);
668 break_rendering = true;
669 } else if (config.throw_at_missing_includes) {
670 throw_renderer_error("extends '" + node.file + "' not found", node);
671 }
672 }
673
674 void visit(const BlockStatementNode& node) {
675 const size_t old_level = current_level;
676 current_level = 0;
677 current_template = template_stack.front();
678 const auto block_it = current_template->block_storage.find(node.name);
679 if (block_it != current_template->block_storage.end()) {
680 block_statement_stack.emplace_back(&node);
681 block_it->second->block.accept(*this);
682 block_statement_stack.pop_back();
683 }
684 current_level = old_level;
685 current_template = template_stack.back();
686 }
687
688 void visit(const SetStatementNode& node) {
689 std::string ptr = node.key;
690 replace_substring(ptr, ".", "/");
691 ptr = "/" + ptr;
692 json_additional_data[nlohmann::json::json_pointer(ptr)] = *eval_expression_list(node.expression);
693 }
694
695public:
696 Renderer(const RenderConfig& config, const TemplateStorage &template_storage, const FunctionStorage &function_storage)
697 : config(config), template_storage(template_storage), function_storage(function_storage) { }
698
699 void render_to(std::ostream &os, const Template &tmpl, const json &data, json *loop_data = nullptr) {
700 output_stream = &os;
701 current_template = &tmpl;
702 json_input = &data;
703 if (loop_data) {
704 json_additional_data = *loop_data;
705 current_loop_data = &json_additional_data["loop"];
706 }
707
708 template_stack.emplace_back(current_template);
709 current_template->root.accept(*this);
710
711 json_tmp_stack.clear();
712 }
713};
714
715} // namespace inja
716
717#endif // INCLUDE_INJA_RENDERER_HPP_
Base node class for the abstract syntax tree (AST).
Definition: node.hpp:59
Definition: node.hpp:70
Definition: node.hpp:347
Definition: node.hpp:254
Definition: node.hpp:92
Definition: node.hpp:336
Definition: node.hpp:284
Definition: node.hpp:295
Definition: node.hpp:273
Definition: node.hpp:135
Class for builtin functions and user-defined callbacks.
Definition: function_storage.hpp:19
Definition: node.hpp:307
Definition: node.hpp:325
Definition: node.hpp:112
Definition: node.hpp:101
Definition: node.hpp:34
Class for rendering a Template with data.
Definition: renderer.hpp:23
Definition: node.hpp:360
Definition: node.hpp:266
Definition: node.hpp:81
Class for render configuration.
Definition: config.hpp:73
Definition: exceptions.hpp:33
Definition: exceptions.hpp:9
The main inja Template.
Definition: template.hpp:18