MQTTSuite
Loading...
Searching...
No Matches
inja.hpp
Go to the documentation of this file.
1/*
2 ___ _ Version 3.4.0
3 |_ _|_ __ (_) __ _ https://github.com/pantor/inja
4 | || '_ \ | |/ _` | Licensed under the MIT License <http://opensource.org/licenses/MIT>.
5 | || | | || | (_| |
6 |___|_| |_|/ |\__,_| Copyright (c) 2018-2023 Lars Berscheid
7 |__/
8Permission is hereby granted, free of charge, to any person obtaining a copy
9of this software and associated documentation files (the "Software"), to deal
10in the Software without restriction, including without limitation the rights
11to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12copies of the Software, and to permit persons to whom the Software is
13furnished to do so, subject to the following conditions:
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#ifndef INCLUDE_INJA_INJA_HPP_
26#define INCLUDE_INJA_INJA_HPP_
27
28#include <nlohmann/json.hpp>
29
30namespace inja {
31#ifndef INJA_DATA_TYPE
32 using json = nlohmann::json;
33#else
34 using json = INJA_DATA_TYPE;
35#endif
36} // namespace inja
37
38#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(INJA_NOEXCEPTION)
39#ifndef INJA_THROW
40#define INJA_THROW(exception) throw exception
41#endif
42#else
43#include <cstdlib>
44#ifndef INJA_THROW
45#define INJA_THROW(exception)
46 std::abort();
47 std::ignore = exception
48#endif
49#ifndef INJA_NOEXCEPTION
50#define INJA_NOEXCEPTION
51#endif
52#endif
53
54// #include "environment.hpp"
55#ifndef INCLUDE_INJA_ENVIRONMENT_HPP_
56#define INCLUDE_INJA_ENVIRONMENT_HPP_
57
58#include <fstream>
59#include <iostream>
60#include <memory>
61#include <sstream>
62#include <string>
63#include <string_view>
64
65// #include "config.hpp"
66#ifndef INCLUDE_INJA_CONFIG_HPP_
67#define INCLUDE_INJA_CONFIG_HPP_
68
69#include <functional>
70#include <string>
71
72// #include "template.hpp"
73#ifndef INCLUDE_INJA_TEMPLATE_HPP_
74#define INCLUDE_INJA_TEMPLATE_HPP_
75
76#include <map>
77#include <memory>
78#include <string>
79#include <vector>
80
81// #include "node.hpp"
82#ifndef INCLUDE_INJA_NODE_HPP_
83#define INCLUDE_INJA_NODE_HPP_
84
85#include <string>
86#include <string_view>
87#include <utility>
88
89// #include "function_storage.hpp"
90#ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_
91#define INCLUDE_INJA_FUNCTION_STORAGE_HPP_
92
93#include <string_view>
94#include <vector>
95
96namespace inja {
97
98 using Arguments = std::vector<const json*>;
99 using CallbackFunction = std::function<json(Arguments& args)>;
100 using VoidCallbackFunction = std::function<void(Arguments& args)>;
101
102 /*!
103 * \brief Class for builtin functions and user-defined callbacks.
104 */
106 public:
156
158 explicit FunctionData(const Operation& op, const CallbackFunction& cb = CallbackFunction{})
159 : operation(op)
160 , callback(cb) {
161 }
163 const CallbackFunction callback;
164 };
165
166 private:
167 const int VARIADIC{-1};
168
169 std::map<std::pair<std::string, int>, FunctionData> function_storage = {
170 {std::make_pair("at", 2), FunctionData{Operation::At}},
171 {std::make_pair("default", 2), FunctionData{Operation::Default}},
172 {std::make_pair("divisibleBy", 2), FunctionData{Operation::DivisibleBy}},
173 {std::make_pair("even", 1), FunctionData{Operation::Even}},
174 {std::make_pair("exists", 1), FunctionData{Operation::Exists}},
175 {std::make_pair("existsIn", 2), FunctionData{Operation::ExistsInObject}},
176 {std::make_pair("first", 1), FunctionData{Operation::First}},
177 {std::make_pair("float", 1), FunctionData{Operation::Float}},
178 {std::make_pair("int", 1), FunctionData{Operation::Int}},
179 {std::make_pair("isArray", 1), FunctionData{Operation::IsArray}},
180 {std::make_pair("isBoolean", 1), FunctionData{Operation::IsBoolean}},
181 {std::make_pair("isFloat", 1), FunctionData{Operation::IsFloat}},
182 {std::make_pair("isInteger", 1), FunctionData{Operation::IsInteger}},
183 {std::make_pair("isNumber", 1), FunctionData{Operation::IsNumber}},
184 {std::make_pair("isObject", 1), FunctionData{Operation::IsObject}},
185 {std::make_pair("isString", 1), FunctionData{Operation::IsString}},
186 {std::make_pair("last", 1), FunctionData{Operation::Last}},
187 {std::make_pair("length", 1), FunctionData{Operation::Length}},
188 {std::make_pair("lower", 1), FunctionData{Operation::Lower}},
189 {std::make_pair("max", 1), FunctionData{Operation::Max}},
190 {std::make_pair("min", 1), FunctionData{Operation::Min}},
191 {std::make_pair("odd", 1), FunctionData{Operation::Odd}},
192 {std::make_pair("range", 1), FunctionData{Operation::Range}},
193 {std::make_pair("round", 2), FunctionData{Operation::Round}},
194 {std::make_pair("sort", 1), FunctionData{Operation::Sort}},
195 {std::make_pair("upper", 1), FunctionData{Operation::Upper}},
196 {std::make_pair("super", 0), FunctionData{Operation::Super}},
197 {std::make_pair("super", 1), FunctionData{Operation::Super}},
198 {std::make_pair("join", 2), FunctionData{Operation::Join}},
199 };
200
201 public:
202 void add_builtin(std::string_view name, int num_args, Operation op) {
203 function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData{op});
204 }
205
206 void add_callback(std::string_view name, int num_args, const CallbackFunction& callback) {
207 function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData{Operation::Callback, callback});
208 }
209
210 FunctionData find_function(std::string_view name, int num_args) const {
211 auto it = function_storage.find(std::make_pair(static_cast<std::string>(name), num_args));
212 if (it != function_storage.end()) {
213 return it->second;
214
215 // Find variadic function
216 } else if (num_args > 0) {
217 it = function_storage.find(std::make_pair(static_cast<std::string>(name), VARIADIC));
218 if (it != function_storage.end()) {
219 return it->second;
220 }
221 }
222
224 }
225 };
226
227} // namespace inja
228
229#endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_
230
231// #include "utils.hpp"
232#ifndef INCLUDE_INJA_UTILS_HPP_
233#define INCLUDE_INJA_UTILS_HPP_
234
235#include <algorithm>
236#include <fstream>
237#include <string>
238#include <string_view>
239#include <utility>
240
241// #include "exceptions.hpp"
242#ifndef INCLUDE_INJA_EXCEPTIONS_HPP_
243#define INCLUDE_INJA_EXCEPTIONS_HPP_
244
245#include <stdexcept>
246#include <string>
247
248namespace inja {
249
251 size_t line;
252 size_t column;
253 };
254
255 struct InjaError : public std::runtime_error {
256 const std::string type;
257 const std::string message;
258
260
261 explicit InjaError(const std::string& type, const std::string& message)
262 : std::runtime_error("[inja.exception." + type + "] " + message)
263 , type(type)
264 , message(message)
265 , location({0, 0}) {
266 }
267
268 explicit InjaError(const std::string& type, const std::string& message, SourceLocation location)
269 : std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" +
270 std::to_string(location.column) + ") " + message)
271 , type(type)
272 , message(message)
273 , location(location) {
274 }
275 };
276
277 struct ParserError : public InjaError {
278 explicit ParserError(const std::string& message, SourceLocation location)
279 : InjaError("parser_error", message, location) {
280 }
281 };
282
283 struct RenderError : public InjaError {
284 explicit RenderError(const std::string& message, SourceLocation location)
285 : InjaError("render_error", message, location) {
286 }
287 };
288
289 struct FileError : public InjaError {
290 explicit FileError(const std::string& message)
291 : InjaError("file_error", message) {
292 }
293 explicit FileError(const std::string& message, SourceLocation location)
294 : InjaError("file_error", message, location) {
295 }
296 };
297
298 struct DataError : public InjaError {
299 explicit DataError(const std::string& message, SourceLocation location)
300 : InjaError("data_error", message, location) {
301 }
302 };
303
304} // namespace inja
305
306#endif // INCLUDE_INJA_EXCEPTIONS_HPP_
307
308namespace inja {
309
310 namespace string_view {
311 inline std::string_view slice(std::string_view view, size_t start, size_t end) {
312 start = std::min(start, view.size());
313 end = std::min(std::max(start, end), view.size());
314 return view.substr(start, end - start);
315 }
316
317 inline std::pair<std::string_view, std::string_view> split(std::string_view view, char Separator) {
318 size_t idx = view.find(Separator);
319 if (idx == std::string_view::npos) {
320 return std::make_pair(view, std::string_view());
321 }
322 return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, std::string_view::npos));
323 }
324
325 inline bool starts_with(std::string_view view, std::string_view prefix) {
326 return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0);
327 }
328 } // namespace string_view
329
330 inline SourceLocation get_source_location(std::string_view content, size_t pos) {
331 // Get line and offset position (starts at 1:1)
332 auto sliced = string_view::slice(content, 0, pos);
333 std::size_t last_newline = sliced.rfind("\n");
334
335 if (last_newline == std::string_view::npos) {
336 return {1, sliced.length() + 1};
337 }
338
339 // Count newlines
340 size_t count_lines = 0;
341 size_t search_start = 0;
342 while (search_start <= sliced.size()) {
343 search_start = sliced.find("\n", search_start) + 1;
344 if (search_start == 0) {
345 break;
346 }
347 count_lines += 1;
348 }
349
350 return {count_lines + 1, sliced.length() - last_newline};
351 }
352
353 inline void replace_substring(std::string& s, const std::string& f, const std::string& t) {
354 if (f.empty()) {
355 return;
356 }
357 for (auto pos = s.find(f); // find first occurrence of f
358 pos != std::string::npos; // make sure f was found
359 s.replace(pos, f.size(), t), // replace with t, and
360 pos = s.find(f, pos + t.size())) // find next occurrence of f
361 {
362 }
363 }
364
365} // namespace inja
366
367#endif // INCLUDE_INJA_UTILS_HPP_
368
369namespace inja {
370
371 class NodeVisitor;
372 class BlockNode;
373 class TextNode;
374 class ExpressionNode;
375 class LiteralNode;
376 class DataNode;
377 class FunctionNode;
378 class ExpressionListNode;
379 class StatementNode;
380 class ForStatementNode;
383 class IfStatementNode;
386 class BlockStatementNode;
387 class SetStatementNode;
388
390 public:
391 virtual ~NodeVisitor() = default;
392
393 virtual void visit(const BlockNode& node) = 0;
394 virtual void visit(const TextNode& node) = 0;
395 virtual void visit(const ExpressionNode& node) = 0;
396 virtual void visit(const LiteralNode& node) = 0;
397 virtual void visit(const DataNode& node) = 0;
398 virtual void visit(const FunctionNode& node) = 0;
399 virtual void visit(const ExpressionListNode& node) = 0;
400 virtual void visit(const StatementNode& node) = 0;
401 virtual void visit(const ForStatementNode& node) = 0;
402 virtual void visit(const ForArrayStatementNode& node) = 0;
403 virtual void visit(const ForObjectStatementNode& node) = 0;
404 virtual void visit(const IfStatementNode& node) = 0;
405 virtual void visit(const IncludeStatementNode& node) = 0;
406 virtual void visit(const ExtendsStatementNode& node) = 0;
407 virtual void visit(const BlockStatementNode& node) = 0;
408 virtual void visit(const SetStatementNode& node) = 0;
409 };
410
411 /*!
412 * \brief Base node class for the abstract syntax tree (AST).
413 */
414 class AstNode {
415 public:
416 virtual void accept(NodeVisitor& v) const = 0;
417
418 size_t pos;
419
420 AstNode(size_t pos)
421 : pos(pos) {
422 }
423 AstNode(const AstNode&) = default;
424 AstNode& operator=(const AstNode&) = default;
425 virtual ~AstNode() {
426 }
427 };
428
429 class BlockNode : public AstNode {
430 public:
431 std::vector<std::shared_ptr<AstNode>> nodes;
432
433 explicit BlockNode()
434 : AstNode(0) {
435 }
436
437 void accept(NodeVisitor& v) const override {
438 v.visit(*this);
439 }
440 };
441
442 class TextNode : public AstNode {
443 public:
444 const size_t length;
445
446 explicit TextNode(size_t pos, size_t length)
447 : AstNode(pos)
448 , length(length) {
449 }
450
451 void accept(NodeVisitor& v) const override {
452 v.visit(*this);
453 }
454 };
455
456 class ExpressionNode : public AstNode {
457 public:
458 explicit ExpressionNode(size_t pos)
459 : AstNode(pos) {
460 }
461
462 void accept(NodeVisitor& v) const override {
463 v.visit(*this);
464 }
465 };
466
468 public:
469 const json value;
470
471 explicit LiteralNode(std::string_view data_text, size_t pos)
473 , value(json::parse(data_text)) {
474 }
475
476 void accept(NodeVisitor& v) const override {
477 v.visit(*this);
478 }
479 };
480
481 class DataNode : public ExpressionNode {
482 public:
483 const std::string name;
484 const json::json_pointer ptr;
485
486 static std::string convert_dot_to_ptr(std::string_view ptr_name) {
487 std::string result;
488 do {
489 std::string_view part;
490 std::tie(part, ptr_name) = string_view::split(ptr_name, '.');
491 result.push_back('/');
492 result.append(part.begin(), part.end());
493 } while (!ptr_name.empty());
494 return result;
495 }
496
497 explicit DataNode(std::string_view ptr_name, size_t pos)
499 , name(ptr_name)
500 , ptr(json::json_pointer(convert_dot_to_ptr(ptr_name))) {
501 }
502
503 void accept(NodeVisitor& v) const override {
504 v.visit(*this);
505 }
506 };
507
510
511 public:
512 enum class Associativity {
515 };
516
517 unsigned int precedence;
519
521
522 std::string name;
523 int number_args; // Can also be negative -> -1 for unknown number
524 std::vector<std::shared_ptr<ExpressionNode>> arguments;
525 CallbackFunction callback;
526
527 explicit FunctionNode(std::string_view name, size_t pos)
529 , precedence(8)
532 , name(name)
533 , number_args(0) {
534 }
535 explicit FunctionNode(Op operation, size_t pos)
537 , operation(operation)
538 , number_args(1) {
539 switch (operation) {
540 case Op::Not: {
541 number_args = 1;
542 precedence = 4;
544 } break;
545 case Op::And: {
546 number_args = 2;
547 precedence = 1;
549 } break;
550 case Op::Or: {
551 number_args = 2;
552 precedence = 1;
554 } break;
555 case Op::In: {
556 number_args = 2;
557 precedence = 2;
559 } break;
560 case Op::Equal: {
561 number_args = 2;
562 precedence = 2;
564 } break;
565 case Op::NotEqual: {
566 number_args = 2;
567 precedence = 2;
569 } break;
570 case Op::Greater: {
571 number_args = 2;
572 precedence = 2;
574 } break;
575 case Op::GreaterEqual: {
576 number_args = 2;
577 precedence = 2;
579 } break;
580 case Op::Less: {
581 number_args = 2;
582 precedence = 2;
584 } break;
585 case Op::LessEqual: {
586 number_args = 2;
587 precedence = 2;
589 } break;
590 case Op::Add: {
591 number_args = 2;
592 precedence = 3;
594 } break;
595 case Op::Subtract: {
596 number_args = 2;
597 precedence = 3;
599 } break;
600 case Op::Multiplication: {
601 number_args = 2;
602 precedence = 4;
604 } break;
605 case Op::Division: {
606 number_args = 2;
607 precedence = 4;
609 } break;
610 case Op::Power: {
611 number_args = 2;
612 precedence = 5;
614 } break;
615 case Op::Modulo: {
616 number_args = 2;
617 precedence = 4;
619 } break;
620 case Op::AtId: {
621 number_args = 2;
622 precedence = 8;
624 } break;
625 default: {
626 precedence = 1;
628 }
629 }
630 }
631
632 void accept(NodeVisitor& v) const override {
633 v.visit(*this);
634 }
635 };
636
638 public:
639 std::shared_ptr<ExpressionNode> root;
640
642 : AstNode(0) {
643 }
644 explicit ExpressionListNode(size_t pos)
645 : AstNode(pos) {
646 }
647
648 void accept(NodeVisitor& v) const override {
649 v.visit(*this);
650 }
651 };
652
653 class StatementNode : public AstNode {
654 public:
655 StatementNode(size_t pos)
656 : AstNode(pos) {
657 }
658
659 virtual void accept(NodeVisitor& v) const override = 0;
660 };
661
663 public:
667
668 ForStatementNode(BlockNode* const parent, size_t pos)
669 : StatementNode(pos)
670 , parent(parent) {
671 }
672
673 virtual void accept(NodeVisitor& v) const override = 0;
674 };
675
677 public:
678 const std::string value;
679
680 explicit ForArrayStatementNode(const std::string& value, BlockNode* const parent, size_t pos)
681 : ForStatementNode(parent, pos)
682 , value(value) {
683 }
684
685 void accept(NodeVisitor& v) const override {
686 v.visit(*this);
687 }
688 };
689
691 public:
692 const std::string key;
693 const std::string value;
694
695 explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode* const parent, size_t pos)
696 : ForStatementNode(parent, pos)
697 , key(key)
698 , value(value) {
699 }
700
701 void accept(NodeVisitor& v) const override {
702 v.visit(*this);
703 }
704 };
705
707 public:
712
713 const bool is_nested;
715
716 explicit IfStatementNode(BlockNode* const parent, size_t pos)
717 : StatementNode(pos)
718 , parent(parent)
719 , is_nested(false) {
720 }
721 explicit IfStatementNode(bool is_nested, BlockNode* const parent, size_t pos)
722 : StatementNode(pos)
723 , parent(parent)
724 , is_nested(is_nested) {
725 }
726
727 void accept(NodeVisitor& v) const override {
728 v.visit(*this);
729 }
730 };
731
733 public:
734 const std::string file;
735
736 explicit IncludeStatementNode(const std::string& file, size_t pos)
737 : StatementNode(pos)
738 , file(file) {
739 }
740
741 void accept(NodeVisitor& v) const override {
742 v.visit(*this);
743 }
744 };
745
747 public:
748 const std::string file;
749
750 explicit ExtendsStatementNode(const std::string& file, size_t pos)
751 : StatementNode(pos)
752 , file(file) {
753 }
754
755 void accept(NodeVisitor& v) const override {
756 v.visit(*this);
757 }
758 };
759
761 public:
762 const std::string name;
765
766 explicit BlockStatementNode(BlockNode* const parent, const std::string& name, size_t pos)
767 : StatementNode(pos)
768 , name(name)
769 , parent(parent) {
770 }
771
772 void accept(NodeVisitor& v) const override {
773 v.visit(*this);
774 }
775 };
776
778 public:
779 const std::string key;
781
782 explicit SetStatementNode(const std::string& key, size_t pos)
783 : StatementNode(pos)
784 , key(key) {
785 }
786
787 void accept(NodeVisitor& v) const override {
788 v.visit(*this);
789 }
790 };
791
792} // namespace inja
793
794#endif // INCLUDE_INJA_NODE_HPP_
795
796// #include "statistics.hpp"
797#ifndef INCLUDE_INJA_STATISTICS_HPP_
798#define INCLUDE_INJA_STATISTICS_HPP_
799
800// #include "node.hpp"
801
802namespace inja {
803
804 /*!
805 * \brief A class for counting statistics on a Template.
806 */
808 void visit(const BlockNode& node) override {
809 for (auto& n : node.nodes) {
810 n->accept(*this);
811 }
812 }
813
814 void visit(const TextNode&) override {
815 }
816 void visit(const ExpressionNode&) override {
817 }
818 void visit(const LiteralNode&) override {
819 }
820
821 void visit(const DataNode&) override {
822 variable_counter += 1;
823 }
824
825 void visit(const FunctionNode& node) override {
826 for (auto& n : node.arguments) {
827 n->accept(*this);
828 }
829 }
830
831 void visit(const ExpressionListNode& node) override {
832 node.root->accept(*this);
833 }
834
835 void visit(const StatementNode&) override {
836 }
837 void visit(const ForStatementNode&) override {
838 }
839
840 void visit(const ForArrayStatementNode& node) override {
841 node.condition.accept(*this);
842 node.body.accept(*this);
843 }
844
845 void visit(const ForObjectStatementNode& node) override {
846 node.condition.accept(*this);
847 node.body.accept(*this);
848 }
849
850 void visit(const IfStatementNode& node) override {
851 node.condition.accept(*this);
854 }
855
856 void visit(const IncludeStatementNode&) override {
857 }
858
859 void visit(const ExtendsStatementNode&) override {
860 }
861
862 void visit(const BlockStatementNode& node) override {
863 node.block.accept(*this);
864 }
865
866 void visit(const SetStatementNode&) override {
867 }
868
869 public:
870 unsigned int variable_counter;
871
873 : variable_counter(0) {
874 }
875 };
876
877} // namespace inja
878
879#endif // INCLUDE_INJA_STATISTICS_HPP_
880
881namespace inja {
882
883 /*!
884 * \brief The main inja Template.
885 */
886 struct Template {
888 std::string content;
889 std::map<std::string, std::shared_ptr<BlockStatementNode>> block_storage;
890
891 explicit Template() {
892 }
893 explicit Template(const std::string& content)
894 : content(content) {
895 }
896
897 /// Return number of variables (total number, not distinct ones) in the template
898 unsigned int count_variables() {
899 auto statistic_visitor = StatisticsVisitor();
900 root.accept(statistic_visitor);
901 return statistic_visitor.variable_counter;
902 }
903 };
904
905 using TemplateStorage = std::map<std::string, Template>;
906
907} // namespace inja
908
909#endif // INCLUDE_INJA_TEMPLATE_HPP_
910
911namespace inja {
912
913 /*!
914 * \brief Class for lexer configuration.
915 */
916 struct LexerConfig {
917 std::string statement_open{"{%"};
918 std::string statement_open_no_lstrip{"{%+"};
919 std::string statement_open_force_lstrip{"{%-"};
920 std::string statement_close{"%}"};
922 std::string line_statement{"##"};
923 std::string expression_open{"{{"};
925 std::string expression_close{"}}"};
927 std::string comment_open{"{#"};
928 std::string comment_open_force_lstrip{"{#-"};
929 std::string comment_close{"#}"};
930 std::string comment_close_force_rstrip{"-#}"};
931 std::string open_chars{"#{"};
932
933 bool trim_blocks{false};
934 bool lstrip_blocks{false};
935
937 open_chars = "";
938 if (open_chars.find(line_statement[0]) == std::string::npos) {
940 }
941 if (open_chars.find(statement_open[0]) == std::string::npos) {
943 }
944 if (open_chars.find(statement_open_no_lstrip[0]) == std::string::npos) {
946 }
947 if (open_chars.find(statement_open_force_lstrip[0]) == std::string::npos) {
949 }
950 if (open_chars.find(expression_open[0]) == std::string::npos) {
952 }
953 if (open_chars.find(expression_open_force_lstrip[0]) == std::string::npos) {
955 }
956 if (open_chars.find(comment_open[0]) == std::string::npos) {
958 }
959 if (open_chars.find(comment_open_force_lstrip[0]) == std::string::npos) {
961 }
962 }
963 };
964
965 /*!
966 * \brief Class for parser configuration.
967 */
970
971 std::function<Template(const std::string&, const std::string&)> include_callback;
972 };
973
974 /*!
975 * \brief Class for render configuration.
976 */
979 };
980
981} // namespace inja
982
983#endif // INCLUDE_INJA_CONFIG_HPP_
984
985// #include "function_storage.hpp"
986
987// #include "parser.hpp"
988#ifndef INCLUDE_INJA_PARSER_HPP_
989#define INCLUDE_INJA_PARSER_HPP_
990
991#include <limits>
992#include <stack>
993#include <string>
994#include <utility>
995#include <vector>
996
997// #include "config.hpp"
998
999// #include "exceptions.hpp"
1000
1001// #include "function_storage.hpp"
1002
1003// #include "lexer.hpp"
1004#ifndef INCLUDE_INJA_LEXER_HPP_
1005#define INCLUDE_INJA_LEXER_HPP_
1006
1007#include <cctype>
1008#include <locale>
1009
1010// #include "config.hpp"
1011
1012// #include "token.hpp"
1013#ifndef INCLUDE_INJA_TOKEN_HPP_
1014#define INCLUDE_INJA_TOKEN_HPP_
1015
1016#include <string>
1017#include <string_view>
1018
1019namespace inja {
1020
1021 /*!
1022 * \brief Helper-class for the inja Lexer.
1023 */
1024 struct Token {
1062
1064 std::string_view text;
1065
1066 explicit constexpr Token() = default;
1067 explicit constexpr Token(Kind kind, std::string_view text)
1068 : kind(kind)
1069 , text(text) {
1070 }
1071
1072 std::string describe() const {
1073 switch (kind) {
1074 case Kind::Text:
1075 return "<text>";
1077 return "<eol>";
1078 case Kind::Eof:
1079 return "<eof>";
1080 default:
1081 return static_cast<std::string>(text);
1082 }
1083 }
1084 };
1085
1086} // namespace inja
1087
1088#endif // INCLUDE_INJA_TOKEN_HPP_
1089
1090// #include "utils.hpp"
1091
1092namespace inja {
1093
1094 /*!
1095 * \brief Class for lexing an inja Template.
1096 */
1097 class Lexer {
1113
1114 enum class MinusState {
1117 };
1118
1120
1123 std::string_view m_in;
1125 size_t pos;
1126
1127 Token
1128 scan_body(std::string_view close, Token::Kind closeKind, std::string_view close_trim = std::string_view(), bool trim = false) {
1129 again:
1130 // skip whitespace (except for \n as it might be a close)
1131 if (tok_start >= m_in.size()) {
1133 }
1134 const char ch = m_in[tok_start];
1135 if (ch == ' ' || ch == '\t' || ch == '\r') {
1136 tok_start += 1;
1137 goto again;
1138 }
1139
1140 // check for close
1141 if (!close_trim.empty() && inja::string_view::starts_with(m_in.substr(tok_start), close_trim)) {
1143 pos = tok_start + close_trim.size();
1144 const Token tok = make_token(closeKind);
1146 return tok;
1147 }
1148
1151 pos = tok_start + close.size();
1152 const Token tok = make_token(closeKind);
1153 if (trim) {
1155 }
1156 return tok;
1157 }
1158
1159 // skip \n
1160 if (ch == '\n') {
1161 tok_start += 1;
1162 goto again;
1163 }
1164
1165 pos = tok_start + 1;
1166 if (std::isalpha(ch)) {
1168 return scan_id();
1169 }
1170
1171 const MinusState current_minus_state = minus_state;
1174 }
1175
1176 switch (ch) {
1177 case '+':
1179 case '-':
1180 if (current_minus_state == MinusState::Operator) {
1182 }
1183 return scan_number();
1184 case '*':
1186 case '/':
1188 case '^':
1190 case '%':
1192 case '.':
1194 case ',':
1196 case ':':
1198 case '(':
1200 case ')':
1203 case '[':
1205 case ']':
1208 case '{':
1210 case '}':
1213 case '>':
1214 if (pos < m_in.size() && m_in[pos] == '=') {
1215 pos += 1;
1217 }
1219 case '<':
1220 if (pos < m_in.size() && m_in[pos] == '=') {
1221 pos += 1;
1223 }
1225 case '=':
1226 if (pos < m_in.size() && m_in[pos] == '=') {
1227 pos += 1;
1229 }
1231 case '!':
1232 if (pos < m_in.size() && m_in[pos] == '=') {
1233 pos += 1;
1235 }
1237 case '\"':
1238 return scan_string();
1239 case '0':
1240 case '1':
1241 case '2':
1242 case '3':
1243 case '4':
1244 case '5':
1245 case '6':
1246 case '7':
1247 case '8':
1248 case '9':
1250 return scan_number();
1251 case '_':
1252 case '@':
1253 case '$':
1255 return scan_id();
1256 default:
1258 }
1259 }
1260
1262 for (;;) {
1263 if (pos >= m_in.size()) {
1264 break;
1265 }
1266 const char ch = m_in[pos];
1267 if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') {
1268 break;
1269 }
1270 pos += 1;
1271 }
1273 }
1274
1276 for (;;) {
1277 if (pos >= m_in.size()) {
1278 break;
1279 }
1280 const char ch = m_in[pos];
1281 // be very permissive in lexer (we'll catch errors when conversion happens)
1282 if (!(std::isdigit(ch) || ch == '.' || ch == 'e' || ch == 'E' ||
1283 (ch == '+' && (pos == 0 || m_in[pos - 1] == 'e' || m_in[pos - 1] == 'E')) ||
1284 (ch == '-' && (pos == 0 || m_in[pos - 1] == 'e' || m_in[pos - 1] == 'E')))) {
1285 break;
1286 }
1287 pos += 1;
1288 }
1290 }
1291
1293 bool escape{false};
1294 for (;;) {
1295 if (pos >= m_in.size()) {
1296 break;
1297 }
1298 const char ch = m_in[pos++];
1299 if (ch == '\\') {
1300 escape = true;
1301 } else if (!escape && ch == m_in[tok_start]) {
1302 break;
1303 } else {
1304 escape = false;
1305 }
1306 }
1308 }
1309
1313
1315 if (pos < m_in.size()) {
1316 while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t' || m_in[pos] == '\n' || m_in[pos] == '\r')) {
1317 pos += 1;
1318 }
1319 }
1320 }
1321
1323 if (pos < m_in.size()) {
1324 while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t')) {
1325 pos += 1;
1326 }
1327 }
1328
1329 if (pos < m_in.size()) {
1330 const char ch = m_in[pos];
1331 if (ch == '\n') {
1332 pos += 1;
1333 } else if (ch == '\r') {
1334 pos += 1;
1335 if (pos < m_in.size() && m_in[pos] == '\n') {
1336 pos += 1;
1337 }
1338 }
1339 }
1340 }
1341
1342 static std::string_view clear_final_line_if_whitespace(std::string_view text) {
1343 std::string_view result = text;
1344 while (!result.empty()) {
1345 const char ch = result.back();
1346 if (ch == ' ' || ch == '\t') {
1347 result.remove_suffix(1);
1348 } else if (ch == '\n' || ch == '\r') {
1349 break;
1350 } else {
1351 return text;
1352 }
1353 }
1354 return result;
1355 }
1356
1357 public:
1358 explicit Lexer(const LexerConfig& config)
1359 : config(config)
1362 }
1363
1367
1368 void start(std::string_view input) {
1369 m_in = input;
1370 tok_start = 0;
1371 pos = 0;
1374
1375 // Consume byte order mark (BOM) for UTF-8
1376 if (inja::string_view::starts_with(m_in, "\xEF\xBB\xBF")) {
1377 m_in = m_in.substr(3);
1378 }
1379 }
1380
1382 tok_start = pos;
1383
1384 again:
1385 if (tok_start >= m_in.size()) {
1387 }
1388
1389 switch (state) {
1390 default:
1391 case State::Text: {
1392 // fast-scan to first open character
1393 const size_t open_start = m_in.substr(pos).find_first_of(config.open_chars);
1394 if (open_start == std::string_view::npos) {
1395 // didn't find open, return remaining text as text token
1396 pos = m_in.size();
1398 }
1399 pos += open_start;
1400
1401 // try to match one of the opening sequences, and get the close
1402 std::string_view open_str = m_in.substr(pos);
1403 bool must_lstrip = false;
1407 must_lstrip = true;
1408 } else {
1410 }
1416 must_lstrip = true;
1417 } else {
1419 must_lstrip = config.lstrip_blocks;
1420 }
1424 must_lstrip = true;
1425 } else {
1427 must_lstrip = config.lstrip_blocks;
1428 }
1429 } else if ((pos == 0 || m_in[pos - 1] == '\n') && inja::string_view::starts_with(open_str, config.line_statement)) {
1431 } else {
1432 pos += 1; // wasn't actually an opening sequence
1433 goto again;
1434 }
1435
1436 std::string_view text = string_view::slice(m_in, tok_start, pos);
1437 if (must_lstrip) {
1439 }
1440
1441 if (text.empty()) {
1442 goto again; // don't generate empty token
1443 }
1444 return Token(Token::Kind::Text, text);
1445 }
1448 pos += config.expression_open.size();
1450 }
1455 }
1456 case State::LineStart: {
1458 pos += config.line_statement.size();
1460 }
1461 case State::StatementStart: {
1463 pos += config.statement_open.size();
1465 }
1470 }
1475 }
1476 case State::CommentStart: {
1478 pos += config.comment_open.size();
1480 }
1485 }
1488 case State::LineBody:
1491 return scan_body(
1493 case State::CommentBody: {
1494 // fast-scan to comment close
1495 const size_t end = m_in.substr(pos).find(config.comment_close);
1496 if (end == std::string_view::npos) {
1497 pos = m_in.size();
1499 }
1500
1501 // Check for trim pattern
1502 const bool must_rstrip = inja::string_view::starts_with(m_in.substr(pos + end - 1), config.comment_close_force_rstrip);
1503
1504 // return the entire comment in the close token
1506 pos += end + config.comment_close.size();
1508
1509 if (must_rstrip || config.trim_blocks) {
1511 }
1512 return tok;
1513 }
1514 }
1515 }
1516
1517 const LexerConfig& get_config() const {
1518 return config;
1519 }
1520 };
1521
1522} // namespace inja
1523
1524#endif // INCLUDE_INJA_LEXER_HPP_
1525
1526// #include "node.hpp"
1527
1528// #include "template.hpp"
1529
1530// #include "token.hpp"
1531
1532// #include "utils.hpp"
1533
1534namespace inja {
1535
1536 /*!
1537 * \brief Class for parsing an inja Template.
1538 */
1539 class Parser {
1540 using Arguments = std::vector<std::shared_ptr<ExpressionNode>>;
1541 using OperatorStack = std::stack<std::shared_ptr<FunctionNode>>;
1542
1544
1546 TemplateStorage& template_storage;
1548
1550 bool have_peek_tok{false};
1551
1552 std::string_view literal_start;
1553
1556
1560
1561 [[noreturn]] inline void throw_parser_error(const std::string& message) const {
1563 }
1564
1565 inline void get_next_token() {
1566 if (have_peek_tok) {
1567 tok = peek_tok;
1568 have_peek_tok = false;
1569 } else {
1570 tok = lexer.scan();
1571 }
1572 }
1573
1574 inline void get_peek_token() {
1575 if (!have_peek_tok) {
1577 have_peek_tok = true;
1578 }
1579 }
1580
1581 inline void add_literal(Arguments& arguments, const char* content_ptr) {
1582 std::string_view data_text(literal_start.data(),
1583 static_cast<std::string_view::size_type>(tok.text.data() - literal_start.data()) + tok.text.size());
1584 arguments.emplace_back(std::make_shared<LiteralNode>(data_text, data_text.data() - content_ptr));
1585 }
1586
1587 inline void add_operator(Arguments& arguments, OperatorStack& operator_stack) {
1588 auto function = operator_stack.top();
1589 operator_stack.pop();
1590
1591 if (static_cast<int>(arguments.size()) < function->number_args) {
1592 throw_parser_error("too few arguments");
1593 }
1594
1595 for (int i = 0; i < function->number_args; ++i) {
1596 function->arguments.insert(function->arguments.begin(), arguments.back());
1597 arguments.pop_back();
1598 }
1599 arguments.emplace_back(function);
1600 }
1601
1602 void add_to_template_storage(std::string_view path, std::string& template_name) {
1603 if (template_storage.find(template_name) != template_storage.end()) {
1604 return;
1605 }
1606
1607 std::string original_path = static_cast<std::string>(path);
1608 std::string original_name = template_name;
1609
1611 // Build the relative path
1612 template_name = original_path + original_name;
1613 if (template_name.compare(0, 2, "./") == 0) {
1614 template_name.erase(0, 2);
1615 }
1616
1617 if (template_storage.find(template_name) == template_storage.end()) {
1618 // Load file
1619 std::ifstream file;
1620 file.open(template_name);
1621 if (!file.fail()) {
1622 std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
1623
1624 auto include_template = Template(text);
1625 template_storage.emplace(template_name, include_template);
1626 parse_into_template(template_storage[template_name], template_name);
1627 return;
1628 } else if (!config.include_callback) {
1629 INJA_THROW(FileError("failed accessing file at '" + template_name + "'"));
1630 }
1631 }
1632 }
1633
1634 // Try include callback
1636 auto include_template = config.include_callback(original_path, original_name);
1637 template_storage.emplace(template_name, include_template);
1638 }
1639 }
1640
1641 std::string parse_filename() const {
1643 throw_parser_error("expected string, got '" + tok.describe() + "'");
1644 }
1645
1646 if (tok.text.length() < 2) {
1647 throw_parser_error("expected filename, got '" + static_cast<std::string>(tok.text) + "'");
1648 }
1649
1650 // Remove first and last character ""
1651 return std::string{tok.text.substr(1, tok.text.length() - 2)};
1652 }
1653
1654 bool parse_expression(Template& tmpl, Token::Kind closing) {
1656 return tok.kind == closing;
1657 }
1658
1659 std::shared_ptr<ExpressionNode> parse_expression(Template& tmpl) {
1660 size_t current_bracket_level{0};
1661 size_t current_brace_level{0};
1662 Arguments arguments;
1663 OperatorStack operator_stack;
1664
1665 while (tok.kind != Token::Kind::Eof) {
1666 // Literals
1667 switch (tok.kind) {
1668 case Token::Kind::String: {
1669 if (current_brace_level == 0 && current_bracket_level == 0) {
1671 add_literal(arguments, tmpl.content.c_str());
1672 }
1673 } break;
1674 case Token::Kind::Number: {
1675 if (current_brace_level == 0 && current_bracket_level == 0) {
1677 add_literal(arguments, tmpl.content.c_str());
1678 }
1679 } break;
1681 if (current_brace_level == 0 && current_bracket_level == 0) {
1683 }
1684 current_bracket_level += 1;
1685 } break;
1687 if (current_brace_level == 0 && current_bracket_level == 0) {
1689 }
1690 current_brace_level += 1;
1691 } break;
1693 if (current_bracket_level == 0) {
1694 throw_parser_error("unexpected ']'");
1695 }
1696
1697 current_bracket_level -= 1;
1698 if (current_brace_level == 0 && current_bracket_level == 0) {
1699 add_literal(arguments, tmpl.content.c_str());
1700 }
1701 } break;
1703 if (current_brace_level == 0) {
1704 throw_parser_error("unexpected '}'");
1705 }
1706
1707 current_brace_level -= 1;
1708 if (current_brace_level == 0 && current_bracket_level == 0) {
1709 add_literal(arguments, tmpl.content.c_str());
1710 }
1711 } break;
1712 case Token::Kind::Id: {
1714
1715 // Data Literal
1716 if (tok.text == static_cast<decltype(tok.text)>("true") || tok.text == static_cast<decltype(tok.text)>("false") ||
1717 tok.text == static_cast<decltype(tok.text)>("null")) {
1718 if (current_brace_level == 0 && current_bracket_level == 0) {
1720 add_literal(arguments, tmpl.content.c_str());
1721 }
1722
1723 // Operator
1724 } else if (tok.text == "and" || tok.text == "or" || tok.text == "in" || tok.text == "not") {
1725 goto parse_operator;
1726
1727 // Functions
1728 } else if (peek_tok.kind == Token::Kind::LeftParen) {
1729 auto func = std::make_shared<FunctionNode>(tok.text, tok.text.data() - tmpl.content.c_str());
1731 do {
1733 auto expr = parse_expression(tmpl);
1734 if (!expr) {
1735 break;
1736 }
1737 func->number_args += 1;
1738 func->arguments.emplace_back(expr);
1739 } while (tok.kind == Token::Kind::Comma);
1741 throw_parser_error("expected right parenthesis, got '" + tok.describe() + "'");
1742 }
1743
1744 auto function_data = function_storage.find_function(func->name, func->number_args);
1745 if (function_data.operation == FunctionStorage::Operation::None) {
1746 throw_parser_error("unknown function " + func->name);
1747 }
1748 func->operation = function_data.operation;
1750 func->callback = function_data.callback;
1751 }
1752 arguments.emplace_back(func);
1753
1754 // Variables
1755 } else {
1756 arguments.emplace_back(
1757 std::make_shared<DataNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
1758 }
1759
1760 // Operators
1761 } break;
1762 case Token::Kind::Equal:
1768 case Token::Kind::Plus:
1769 case Token::Kind::Minus:
1770 case Token::Kind::Times:
1771 case Token::Kind::Slash:
1772 case Token::Kind::Power:
1774 case Token::Kind::Dot: {
1775 parse_operator:
1776 FunctionStorage::Operation operation;
1777 switch (tok.kind) {
1778 case Token::Kind::Id: {
1779 if (tok.text == "and") {
1781 } else if (tok.text == "or") {
1783 } else if (tok.text == "in") {
1785 } else if (tok.text == "not") {
1787 } else {
1788 throw_parser_error("unknown operator in parser.");
1789 }
1790 } break;
1791 case Token::Kind::Equal: {
1793 } break;
1794 case Token::Kind::NotEqual: {
1796 } break;
1799 } break;
1802 } break;
1803 case Token::Kind::LessThan: {
1805 } break;
1808 } break;
1809 case Token::Kind::Plus: {
1811 } break;
1812 case Token::Kind::Minus: {
1814 } break;
1815 case Token::Kind::Times: {
1817 } break;
1818 case Token::Kind::Slash: {
1820 } break;
1821 case Token::Kind::Power: {
1823 } break;
1824 case Token::Kind::Percent: {
1826 } break;
1827 case Token::Kind::Dot: {
1829 } break;
1830 default: {
1831 throw_parser_error("unknown operator in parser.");
1832 }
1833 }
1834 auto function_node = std::make_shared<FunctionNode>(operation, tok.text.data() - tmpl.content.c_str());
1835
1836 while (!operator_stack.empty() && ((operator_stack.top()->precedence > function_node->precedence) ||
1837 (operator_stack.top()->precedence == function_node->precedence &&
1839 add_operator(arguments, operator_stack);
1840 }
1841
1842 operator_stack.emplace(function_node);
1843 } break;
1844 case Token::Kind::Comma: {
1845 if (current_brace_level == 0 && current_bracket_level == 0) {
1846 goto break_loop;
1847 }
1848 } break;
1849 case Token::Kind::Colon: {
1850 if (current_brace_level == 0 && current_bracket_level == 0) {
1851 throw_parser_error("unexpected ':'");
1852 }
1853 } break;
1856 auto expr = parse_expression(tmpl);
1858 throw_parser_error("expected right parenthesis, got '" + tok.describe() + "'");
1859 }
1860 if (!expr) {
1861 throw_parser_error("empty expression in parentheses");
1862 }
1863 arguments.emplace_back(expr);
1864 } break;
1865 default:
1866 goto break_loop;
1867 }
1868
1870 }
1871
1872 break_loop:
1873 while (!operator_stack.empty()) {
1874 add_operator(arguments, operator_stack);
1875 }
1876
1877 std::shared_ptr<ExpressionNode> expr;
1878 if (arguments.size() == 1) {
1879 expr = arguments[0];
1880 arguments = {};
1881 } else if (arguments.size() > 1) {
1882 throw_parser_error("malformed expression");
1883 }
1884 return expr;
1885 }
1886
1887 bool parse_statement(Template& tmpl, Token::Kind closing, std::string_view path) {
1888 if (tok.kind != Token::Kind::Id) {
1889 return false;
1890 }
1891
1892 if (tok.text == static_cast<decltype(tok.text)>("if")) {
1894
1895 auto if_statement_node = std::make_shared<IfStatementNode>(current_block, tok.text.data() - tmpl.content.c_str());
1896 current_block->nodes.emplace_back(if_statement_node);
1897 if_statement_stack.emplace(if_statement_node.get());
1898 current_block = &if_statement_node->true_statement;
1899 current_expression_list = &if_statement_node->condition;
1900
1901 if (!parse_expression(tmpl, closing)) {
1902 return false;
1903 }
1904 } else if (tok.text == static_cast<decltype(tok.text)>("else")) {
1905 if (if_statement_stack.empty()) {
1906 throw_parser_error("else without matching if");
1907 }
1908 auto& if_statement_data = if_statement_stack.top();
1910
1911 if_statement_data->has_false_statement = true;
1912 current_block = &if_statement_data->false_statement;
1913
1914 // Chained else if
1915 if (tok.kind == Token::Kind::Id && tok.text == static_cast<decltype(tok.text)>("if")) {
1917
1918 auto if_statement_node = std::make_shared<IfStatementNode>(true, current_block, tok.text.data() - tmpl.content.c_str());
1919 current_block->nodes.emplace_back(if_statement_node);
1920 if_statement_stack.emplace(if_statement_node.get());
1921 current_block = &if_statement_node->true_statement;
1922 current_expression_list = &if_statement_node->condition;
1923
1924 if (!parse_expression(tmpl, closing)) {
1925 return false;
1926 }
1927 }
1928 } else if (tok.text == static_cast<decltype(tok.text)>("endif")) {
1929 if (if_statement_stack.empty()) {
1930 throw_parser_error("endif without matching if");
1931 }
1932
1933 // Nested if statements
1934 while (if_statement_stack.top()->is_nested) {
1935 if_statement_stack.pop();
1936 }
1937
1938 auto& if_statement_data = if_statement_stack.top();
1940
1941 current_block = if_statement_data->parent;
1942 if_statement_stack.pop();
1943 } else if (tok.text == static_cast<decltype(tok.text)>("block")) {
1945
1946 if (tok.kind != Token::Kind::Id) {
1947 throw_parser_error("expected block name, got '" + tok.describe() + "'");
1948 }
1949
1950 const std::string block_name = static_cast<std::string>(tok.text);
1951
1952 auto block_statement_node =
1953 std::make_shared<BlockStatementNode>(current_block, block_name, tok.text.data() - tmpl.content.c_str());
1954 current_block->nodes.emplace_back(block_statement_node);
1955 block_statement_stack.emplace(block_statement_node.get());
1956 current_block = &block_statement_node->block;
1957 auto success = tmpl.block_storage.emplace(block_name, block_statement_node);
1958 if (!success.second) {
1959 throw_parser_error("block with the name '" + block_name + "' does already exist");
1960 }
1961
1963 } else if (tok.text == static_cast<decltype(tok.text)>("endblock")) {
1964 if (block_statement_stack.empty()) {
1965 throw_parser_error("endblock without matching block");
1966 }
1967
1968 auto& block_statement_data = block_statement_stack.top();
1970
1971 current_block = block_statement_data->parent;
1973 } else if (tok.text == static_cast<decltype(tok.text)>("for")) {
1975
1976 // options: for a in arr; for a, b in obj
1977 if (tok.kind != Token::Kind::Id) {
1978 throw_parser_error("expected id, got '" + tok.describe() + "'");
1979 }
1980
1981 Token value_token = tok;
1983
1984 // Object type
1985 std::shared_ptr<ForStatementNode> for_statement_node;
1988 if (tok.kind != Token::Kind::Id) {
1989 throw_parser_error("expected id, got '" + tok.describe() + "'");
1990 }
1991
1992 Token key_token = std::move(value_token);
1993 value_token = tok;
1995
1996 for_statement_node = std::make_shared<ForObjectStatementNode>(static_cast<std::string>(key_token.text),
1997 static_cast<std::string>(value_token.text),
1999 tok.text.data() - tmpl.content.c_str());
2000
2001 // Array type
2002 } else {
2003 for_statement_node = std::make_shared<ForArrayStatementNode>(
2004 static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
2005 }
2006
2007 current_block->nodes.emplace_back(for_statement_node);
2008 for_statement_stack.emplace(for_statement_node.get());
2009 current_block = &for_statement_node->body;
2010 current_expression_list = &for_statement_node->condition;
2011
2012 if (tok.kind != Token::Kind::Id || tok.text != static_cast<decltype(tok.text)>("in")) {
2013 throw_parser_error("expected 'in', got '" + tok.describe() + "'");
2014 }
2016
2017 if (!parse_expression(tmpl, closing)) {
2018 return false;
2019 }
2020 } else if (tok.text == static_cast<decltype(tok.text)>("endfor")) {
2021 if (for_statement_stack.empty()) {
2022 throw_parser_error("endfor without matching for");
2023 }
2024
2025 auto& for_statement_data = for_statement_stack.top();
2027
2028 current_block = for_statement_data->parent;
2029 for_statement_stack.pop();
2030 } else if (tok.text == static_cast<decltype(tok.text)>("include")) {
2032
2033 std::string template_name = parse_filename();
2034 add_to_template_storage(path, template_name);
2035
2036 current_block->nodes.emplace_back(
2037 std::make_shared<IncludeStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
2038
2040 } else if (tok.text == static_cast<decltype(tok.text)>("extends")) {
2042
2043 std::string template_name = parse_filename();
2044 add_to_template_storage(path, template_name);
2045
2046 current_block->nodes.emplace_back(
2047 std::make_shared<ExtendsStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
2048
2050 } else if (tok.text == static_cast<decltype(tok.text)>("set")) {
2052
2053 if (tok.kind != Token::Kind::Id) {
2054 throw_parser_error("expected variable name, got '" + tok.describe() + "'");
2055 }
2056
2057 std::string key = static_cast<std::string>(tok.text);
2059
2060 auto set_statement_node = std::make_shared<SetStatementNode>(key, tok.text.data() - tmpl.content.c_str());
2061 current_block->nodes.emplace_back(set_statement_node);
2062 current_expression_list = &set_statement_node->expression;
2063
2064 if (tok.text != static_cast<decltype(tok.text)>("=")) {
2065 throw_parser_error("expected '=', got '" + tok.describe() + "'");
2066 }
2068
2069 if (!parse_expression(tmpl, closing)) {
2070 return false;
2071 }
2072 } else {
2073 return false;
2074 }
2075 return true;
2076 }
2077
2078 void parse_into(Template& tmpl, std::string_view path) {
2080 current_block = &tmpl.root;
2081
2082 for (;;) {
2084 switch (tok.kind) {
2085 case Token::Kind::Eof: {
2086 if (!if_statement_stack.empty()) {
2087 throw_parser_error("unmatched if");
2088 }
2089 if (!for_statement_stack.empty()) {
2090 throw_parser_error("unmatched for");
2091 }
2092 }
2093 return;
2094 case Token::Kind::Text: {
2095 current_block->nodes.emplace_back(
2096 std::make_shared<TextNode>(tok.text.data() - tmpl.content.c_str(), tok.text.size()));
2097 } break;
2101 throw_parser_error("expected statement, got '" + tok.describe() + "'");
2102 }
2104 throw_parser_error("expected statement close, got '" + tok.describe() + "'");
2105 }
2106 } break;
2110 throw_parser_error("expected statement, got '" + tok.describe() + "'");
2111 }
2113 throw_parser_error("expected line statement close, got '" + tok.describe() + "'");
2114 }
2115 } break;
2118
2119 auto expression_list_node = std::make_shared<ExpressionListNode>(tok.text.data() - tmpl.content.c_str());
2120 current_block->nodes.emplace_back(expression_list_node);
2121 current_expression_list = expression_list_node.get();
2122
2124 throw_parser_error("expected expression close, got '" + tok.describe() + "'");
2125 }
2126 } break;
2130 throw_parser_error("expected comment close, got '" + tok.describe() + "'");
2131 }
2132 } break;
2133 default: {
2134 throw_parser_error("unexpected token '" + tok.describe() + "'");
2135 } break;
2136 }
2137 }
2138 }
2139
2140 public:
2141 explicit Parser(const ParserConfig& parser_config,
2142 const LexerConfig& lexer_config,
2143 TemplateStorage& template_storage,
2144 const FunctionStorage& function_storage)
2145 : config(parser_config)
2146 , lexer(lexer_config)
2147 , template_storage(template_storage)
2148 , function_storage(function_storage) {
2149 }
2150
2151 Template parse(std::string_view input, std::string_view path) {
2152 auto result = Template(static_cast<std::string>(input));
2153 parse_into(result, path);
2154 return result;
2155 }
2156
2157 void parse_into_template(Template& tmpl, std::string_view filename) {
2158 std::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
2159
2160 // StringRef path = sys::path::parent_path(filename);
2162 sub_parser.parse_into(tmpl, path);
2163 }
2164
2165 std::string load_file(const std::string& filename) {
2166 std::ifstream file;
2167 file.open(filename);
2168 if (file.fail()) {
2169 INJA_THROW(FileError("failed accessing file at '" + filename + "'"));
2170 }
2171 std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
2172 return text;
2173 }
2174 };
2175
2176} // namespace inja
2177
2178#endif // INCLUDE_INJA_PARSER_HPP_
2179
2180// #include "renderer.hpp"
2181#ifndef INCLUDE_INJA_RENDERER_HPP_
2182#define INCLUDE_INJA_RENDERER_HPP_
2183
2184#include <algorithm>
2185#include <numeric>
2186#include <string>
2187#include <utility>
2188#include <vector>
2189
2190// #include "config.hpp"
2191
2192// #include "exceptions.hpp"
2193
2194// #include "node.hpp"
2195
2196// #include "template.hpp"
2197
2198// #include "utils.hpp"
2199
2200namespace inja {
2201
2202 /*!
2203 * \brief Class for rendering a Template with data.
2204 */
2205 class Renderer : public NodeVisitor {
2207
2209 const TemplateStorage& template_storage;
2211
2213 size_t current_level{0};
2214 std::vector<const Template*> template_stack;
2216
2218 std::ostream* output_stream;
2219
2222
2223 std::vector<std::shared_ptr<json>> data_tmp_stack;
2224 std::stack<const json*> data_eval_stack;
2225 std::stack<const DataNode*> not_found_stack;
2226
2227 bool break_rendering{false};
2228
2229 static bool truthy(const json* data) {
2230 if (data->is_boolean()) {
2231 return data->get<bool>();
2232 } else if (data->is_number()) {
2233 return (*data != 0);
2234 } else if (data->is_null()) {
2235 return false;
2236 }
2237 return !data->empty();
2238 }
2239
2240 void print_data(const std::shared_ptr<json> value) {
2241 if (value->is_string()) {
2242 *output_stream << value->get_ref<const json::string_t&>();
2243 } else if (value->is_number_unsigned()) {
2244 *output_stream << value->get<const json::number_unsigned_t>();
2245 } else if (value->is_number_integer()) {
2246 *output_stream << value->get<const json::number_integer_t>();
2247 } else if (value->is_null()) {
2248 } else {
2249 *output_stream << value->dump();
2250 }
2251 }
2252
2253 const std::shared_ptr<json> eval_expression_list(const ExpressionListNode& expression_list) {
2254 if (!expression_list.root) {
2255 throw_renderer_error("empty expression", expression_list);
2256 }
2257
2258 expression_list.root->accept(*this);
2259
2260 if (data_eval_stack.empty()) {
2261 throw_renderer_error("empty expression", expression_list);
2262 } else if (data_eval_stack.size() != 1) {
2263 throw_renderer_error("malformed expression", expression_list);
2264 }
2265
2266 const auto result = data_eval_stack.top();
2267 data_eval_stack.pop();
2268
2269 if (!result) {
2270 if (not_found_stack.empty()) {
2271 throw_renderer_error("expression could not be evaluated", expression_list);
2272 }
2273
2274 auto node = not_found_stack.top();
2275 not_found_stack.pop();
2276
2277 throw_renderer_error("variable '" + static_cast<std::string>(node->name) + "' not found", *node);
2278 }
2279 return std::make_shared<json>(*result);
2280 }
2281
2282 [[noreturn]] void throw_renderer_error(const std::string& message, const AstNode& node) {
2284 INJA_THROW(RenderError(message, loc));
2285 }
2286
2287 void make_result(const json&& result) {
2288 auto result_ptr = std::make_shared<json>(result);
2289 data_tmp_stack.push_back(result_ptr);
2290 data_eval_stack.push(result_ptr.get());
2291 }
2292
2293 template <size_t N, size_t N_start = 0, bool throw_not_found = true>
2294 std::array<const json*, N> get_arguments(const FunctionNode& node) {
2295 if (node.arguments.size() < N_start + N) {
2296 throw_renderer_error("function needs " + std::to_string(N_start + N) + " variables, but has only found " +
2297 std::to_string(node.arguments.size()),
2298 node);
2299 }
2300
2301 for (size_t i = N_start; i < N_start + N; i += 1) {
2302 node.arguments[i]->accept(*this);
2303 }
2304
2305 if (data_eval_stack.size() < N) {
2306 throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " +
2307 std::to_string(data_eval_stack.size()),
2308 node);
2309 }
2310
2311 std::array<const json*, N> result;
2312 for (size_t i = 0; i < N; i += 1) {
2313 result[N - i - 1] = data_eval_stack.top();
2314 data_eval_stack.pop();
2315
2316 if (!result[N - i - 1]) {
2317 const auto data_node = not_found_stack.top();
2318 not_found_stack.pop();
2319
2320 if (throw_not_found) {
2321 throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
2322 }
2323 }
2324 }
2325 return result;
2326 }
2327
2328 template <bool throw_not_found = true>
2329 Arguments get_argument_vector(const FunctionNode& node) {
2330 const size_t N = node.arguments.size();
2331 for (auto a : node.arguments) {
2332 a->accept(*this);
2333 }
2334
2335 if (data_eval_stack.size() < N) {
2336 throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " +
2337 std::to_string(data_eval_stack.size()),
2338 node);
2339 }
2340
2341 Arguments result{N};
2342 for (size_t i = 0; i < N; i += 1) {
2343 result[N - i - 1] = data_eval_stack.top();
2344 data_eval_stack.pop();
2345
2346 if (!result[N - i - 1]) {
2347 const auto data_node = not_found_stack.top();
2348 not_found_stack.pop();
2349
2350 if (throw_not_found) {
2351 throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
2352 }
2353 }
2354 }
2355 return result;
2356 }
2357
2358 void visit(const BlockNode& node) override {
2359 for (auto& n : node.nodes) {
2360 n->accept(*this);
2361
2362 if (break_rendering) {
2363 break;
2364 }
2365 }
2366 }
2367
2368 void visit(const TextNode& node) override {
2369 output_stream->write(current_template->content.c_str() + node.pos, static_cast<std::streamsize>(node.length));
2370 }
2371
2372 void visit(const ExpressionNode&) override {
2373 }
2374
2375 void visit(const LiteralNode& node) override {
2376 data_eval_stack.push(&node.value);
2377 }
2378
2379 void visit(const DataNode& node) override {
2380 if (additional_data.contains(node.ptr)) {
2381 data_eval_stack.push(&(additional_data[node.ptr]));
2382 } else if (data_input->contains(node.ptr)) {
2383 data_eval_stack.push(&(*data_input)[node.ptr]);
2384 } else {
2385 // Try to evaluate as a no-argument callback
2386 const auto function_data = function_storage.find_function(node.name, 0);
2388 Arguments empty_args{};
2389 const auto value = std::make_shared<json>(function_data.callback(empty_args));
2390 data_tmp_stack.push_back(value);
2391 data_eval_stack.push(value.get());
2392 } else {
2393 data_eval_stack.push(nullptr);
2394 not_found_stack.emplace(&node);
2395 }
2396 }
2397 }
2398
2399 void visit(const FunctionNode& node) override {
2400 switch (node.operation) {
2401 case Op::Not: {
2402 const auto args = get_arguments<1>(node);
2403 make_result(!truthy(args[0]));
2404 } break;
2405 case Op::And: {
2406 make_result(truthy(get_arguments<1, 0>(node)[0]) && truthy(get_arguments<1, 1>(node)[0]));
2407 } break;
2408 case Op::Or: {
2409 make_result(truthy(get_arguments<1, 0>(node)[0]) || truthy(get_arguments<1, 1>(node)[0]));
2410 } break;
2411 case Op::In: {
2412 const auto args = get_arguments<2>(node);
2413 make_result(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end());
2414 } break;
2415 case Op::Equal: {
2416 const auto args = get_arguments<2>(node);
2417 make_result(*args[0] == *args[1]);
2418 } break;
2419 case Op::NotEqual: {
2420 const auto args = get_arguments<2>(node);
2421 make_result(*args[0] != *args[1]);
2422 } break;
2423 case Op::Greater: {
2424 const auto args = get_arguments<2>(node);
2425 make_result(*args[0] > *args[1]);
2426 } break;
2427 case Op::GreaterEqual: {
2428 const auto args = get_arguments<2>(node);
2429 make_result(*args[0] >= *args[1]);
2430 } break;
2431 case Op::Less: {
2432 const auto args = get_arguments<2>(node);
2433 make_result(*args[0] < *args[1]);
2434 } break;
2435 case Op::LessEqual: {
2436 const auto args = get_arguments<2>(node);
2437 make_result(*args[0] <= *args[1]);
2438 } break;
2439 case Op::Add: {
2440 const auto args = get_arguments<2>(node);
2441 if (args[0]->is_string() && args[1]->is_string()) {
2442 make_result(args[0]->get_ref<const json::string_t&>() + args[1]->get_ref<const json::string_t&>());
2443 } else if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
2444 make_result(args[0]->get<const json::number_integer_t>() + args[1]->get<const json::number_integer_t>());
2445 } else {
2446 make_result(args[0]->get<const json::number_float_t>() + args[1]->get<const json::number_float_t>());
2447 }
2448 } break;
2449 case Op::Subtract: {
2450 const auto args = get_arguments<2>(node);
2451 if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
2452 make_result(args[0]->get<const json::number_integer_t>() - args[1]->get<const json::number_integer_t>());
2453 } else {
2454 make_result(args[0]->get<const json::number_float_t>() - args[1]->get<const json::number_float_t>());
2455 }
2456 } break;
2457 case Op::Multiplication: {
2458 const auto args = get_arguments<2>(node);
2459 if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
2460 make_result(args[0]->get<const json::number_integer_t>() * args[1]->get<const json::number_integer_t>());
2461 } else {
2462 make_result(args[0]->get<const json::number_float_t>() * args[1]->get<const json::number_float_t>());
2463 }
2464 } break;
2465 case Op::Division: {
2466 const auto args = get_arguments<2>(node);
2467 if (args[1]->get<const json::number_float_t>() == 0) {
2468 throw_renderer_error("division by zero", node);
2469 }
2470 make_result(args[0]->get<const json::number_float_t>() / args[1]->get<const json::number_float_t>());
2471 } break;
2472 case Op::Power: {
2473 const auto args = get_arguments<2>(node);
2474 if (args[0]->is_number_integer() && args[1]->get<const json::number_integer_t>() >= 0) {
2475 const auto result = static_cast<json::number_integer_t>(
2476 std::pow(args[0]->get<const json::number_integer_t>(), args[1]->get<const json::number_integer_t>()));
2477 make_result(result);
2478 } else {
2479 const auto result =
2480 std::pow(args[0]->get<const json::number_float_t>(), args[1]->get<const json::number_integer_t>());
2481 make_result(result);
2482 }
2483 } break;
2484 case Op::Modulo: {
2485 const auto args = get_arguments<2>(node);
2486 make_result(args[0]->get<const json::number_integer_t>() % args[1]->get<const json::number_integer_t>());
2487 } break;
2488 case Op::AtId: {
2489 const auto container = get_arguments<1, 0, false>(node)[0];
2490 node.arguments[1]->accept(*this);
2491 if (not_found_stack.empty()) {
2492 throw_renderer_error("could not find element with given name", node);
2493 }
2494 const auto id_node = not_found_stack.top();
2495 not_found_stack.pop();
2496 data_eval_stack.pop();
2497 data_eval_stack.push(&container->at(id_node->name));
2498 } break;
2499 case Op::At: {
2500 const auto args = get_arguments<2>(node);
2501 if (args[0]->is_object()) {
2502 data_eval_stack.push(&args[0]->at(args[1]->get<std::string>()));
2503 } else {
2504 data_eval_stack.push(&args[0]->at(args[1]->get<std::stack<const json*>::size_type>()));
2505 }
2506 } break;
2507 case Op::Default: {
2508 const auto test_arg = get_arguments<1, 0, false>(node)[0];
2509 data_eval_stack.push(test_arg ? test_arg : get_arguments<1, 1>(node)[0]);
2510 } break;
2511 case Op::DivisibleBy: {
2512 const auto args = get_arguments<2>(node);
2513 const auto divisor = args[1]->get<const json::number_integer_t>();
2514 make_result((divisor != 0) && (args[0]->get<const json::number_integer_t>() % divisor == 0));
2515 } break;
2516 case Op::Even: {
2517 make_result(get_arguments<1>(node)[0]->get<const json::number_integer_t>() % 2 == 0);
2518 } break;
2519 case Op::Exists: {
2520 auto&& name = get_arguments<1>(node)[0]->get_ref<const json::string_t&>();
2521 make_result(data_input->contains(json::json_pointer(DataNode::convert_dot_to_ptr(name))));
2522 } break;
2523 case Op::ExistsInObject: {
2524 const auto args = get_arguments<2>(node);
2525 auto&& name = args[1]->get_ref<const json::string_t&>();
2526 make_result(args[0]->find(name) != args[0]->end());
2527 } break;
2528 case Op::First: {
2529 const auto result = &get_arguments<1>(node)[0]->front();
2530 data_eval_stack.push(result);
2531 } break;
2532 case Op::Float: {
2533 make_result(std::stod(get_arguments<1>(node)[0]->get_ref<const json::string_t&>()));
2534 } break;
2535 case Op::Int: {
2536 make_result(std::stoi(get_arguments<1>(node)[0]->get_ref<const json::string_t&>()));
2537 } break;
2538 case Op::Last: {
2539 const auto result = &get_arguments<1>(node)[0]->back();
2540 data_eval_stack.push(result);
2541 } break;
2542 case Op::Length: {
2543 const auto val = get_arguments<1>(node)[0];
2544 if (val->is_string()) {
2545 make_result(val->get_ref<const json::string_t&>().length());
2546 } else {
2547 make_result(val->size());
2548 }
2549 } break;
2550 case Op::Lower: {
2551 auto result = get_arguments<1>(node)[0]->get<json::string_t>();
2552 std::transform(result.begin(), result.end(), result.begin(), [](char c) {
2553 return static_cast<char>(::tolower(c));
2554 });
2555 make_result(std::move(result));
2556 } break;
2557 case Op::Max: {
2558 const auto args = get_arguments<1>(node);
2559 const auto result = std::max_element(args[0]->begin(), args[0]->end());
2560 data_eval_stack.push(&(*result));
2561 } break;
2562 case Op::Min: {
2563 const auto args = get_arguments<1>(node);
2564 const auto result = std::min_element(args[0]->begin(), args[0]->end());
2565 data_eval_stack.push(&(*result));
2566 } break;
2567 case Op::Odd: {
2568 make_result(get_arguments<1>(node)[0]->get<const json::number_integer_t>() % 2 != 0);
2569 } break;
2570 case Op::Range: {
2571 std::vector<int> result(get_arguments<1>(node)[0]->get<std::vector<int>::size_type>());
2572 std::iota(result.begin(), result.end(), 0);
2573 make_result(std::move(result));
2574 } break;
2575 case Op::Round: {
2576 const auto args = get_arguments<2>(node);
2577 const int precision = args[1]->get<int>();
2578 const double result =
2579 std::round(args[0]->get<const json::number_float_t>() * std::pow(10.0, precision)) / std::pow(10.0, precision);
2580 if (precision == 0) {
2581 make_result(int(result));
2582 } else {
2583 make_result(result);
2584 }
2585 } break;
2586 case Op::Sort: {
2587 auto result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<std::vector<json>>());
2588 std::sort(result_ptr->begin(), result_ptr->end());
2589 data_tmp_stack.push_back(result_ptr);
2590 data_eval_stack.push(result_ptr.get());
2591 } break;
2592 case Op::Upper: {
2593 auto result = get_arguments<1>(node)[0]->get<json::string_t>();
2594 std::transform(result.begin(), result.end(), result.begin(), [](char c) {
2595 return static_cast<char>(::toupper(c));
2596 });
2597 make_result(std::move(result));
2598 } break;
2599 case Op::IsBoolean: {
2600 make_result(get_arguments<1>(node)[0]->is_boolean());
2601 } break;
2602 case Op::IsNumber: {
2603 make_result(get_arguments<1>(node)[0]->is_number());
2604 } break;
2605 case Op::IsInteger: {
2606 make_result(get_arguments<1>(node)[0]->is_number_integer());
2607 } break;
2608 case Op::IsFloat: {
2609 make_result(get_arguments<1>(node)[0]->is_number_float());
2610 } break;
2611 case Op::IsObject: {
2612 make_result(get_arguments<1>(node)[0]->is_object());
2613 } break;
2614 case Op::IsArray: {
2615 make_result(get_arguments<1>(node)[0]->is_array());
2616 } break;
2617 case Op::IsString: {
2618 make_result(get_arguments<1>(node)[0]->is_string());
2619 } break;
2620 case Op::Callback: {
2621 auto args = get_argument_vector(node);
2622 make_result(node.callback(args));
2623 } break;
2624 case Op::Super: {
2625 const auto args = get_argument_vector(node);
2626 const size_t old_level = current_level;
2627 const size_t level_diff = (args.size() == 1) ? args[0]->get<size_t>() : 1;
2628 const size_t level = current_level + level_diff;
2629
2630 if (block_statement_stack.empty()) {
2631 throw_renderer_error("super() call is not within a block", node);
2632 }
2633
2634 if (level < 1 || level > template_stack.size() - 1) {
2635 throw_renderer_error("level of super() call does not match parent templates (between 1 and " +
2636 std::to_string(template_stack.size() - 1) + ")",
2637 node);
2638 }
2639
2640 const auto current_block_statement = block_statement_stack.back();
2641 const Template* new_template = template_stack.at(level);
2642 const Template* old_template = current_template;
2643 const auto block_it = new_template->block_storage.find(current_block_statement->name);
2644 if (block_it != new_template->block_storage.end()) {
2645 current_template = new_template;
2646 current_level = level;
2647 block_it->second->block.accept(*this);
2648 current_level = old_level;
2649 current_template = old_template;
2650 } else {
2651 throw_renderer_error("could not find block with name '" + current_block_statement->name + "'", node);
2652 }
2653 make_result(nullptr);
2654 } break;
2655 case Op::Join: {
2656 const auto args = get_arguments<2>(node);
2657 const auto separator = args[1]->get<json::string_t>();
2658 std::ostringstream os;
2659 std::string sep;
2660 for (const auto& value : *args[0]) {
2661 os << sep;
2662 if (value.is_string()) {
2663 os << value.get<std::string>(); // otherwise the value is surrounded with ""
2664 } else {
2665 os << value.dump();
2666 }
2667 sep = separator;
2668 }
2669 make_result(os.str());
2670 } break;
2671 case Op::None:
2672 break;
2673 }
2674 }
2675
2676 void visit(const ExpressionListNode& node) override {
2677 print_data(eval_expression_list(node));
2678 }
2679
2680 void visit(const StatementNode&) override {
2681 }
2682
2683 void visit(const ForStatementNode&) override {
2684 }
2685
2686 void visit(const ForArrayStatementNode& node) override {
2687 const auto result = eval_expression_list(node.condition);
2688 if (!result->is_array()) {
2689 throw_renderer_error("object must be an array", node);
2690 }
2691
2692 if (!current_loop_data->empty()) {
2693 auto tmp = *current_loop_data; // Because of clang-3
2694 (*current_loop_data)["parent"] = std::move(tmp);
2695 }
2696
2697 size_t index = 0;
2698 (*current_loop_data)["is_first"] = true;
2699 (*current_loop_data)["is_last"] = (result->size() <= 1);
2700 for (auto it = result->begin(); it != result->end(); ++it) {
2701 additional_data[static_cast<std::string>(node.value)] = *it;
2702
2703 (*current_loop_data)["index"] = index;
2704 (*current_loop_data)["index1"] = index + 1;
2705 if (index == 1) {
2706 (*current_loop_data)["is_first"] = false;
2707 }
2708 if (index == result->size() - 1) {
2709 (*current_loop_data)["is_last"] = true;
2710 }
2711
2712 node.body.accept(*this);
2713 ++index;
2714 }
2715
2716 additional_data[static_cast<std::string>(node.value)].clear();
2717 if (!(*current_loop_data)["parent"].empty()) {
2718 const auto tmp = (*current_loop_data)["parent"];
2719 *current_loop_data = std::move(tmp);
2720 } else {
2722 }
2723 }
2724
2725 void visit(const ForObjectStatementNode& node) override {
2726 const auto result = eval_expression_list(node.condition);
2727 if (!result->is_object()) {
2728 throw_renderer_error("object must be an object", node);
2729 }
2730
2731 if (!current_loop_data->empty()) {
2732 (*current_loop_data)["parent"] = std::move(*current_loop_data);
2733 }
2734
2735 size_t index = 0;
2736 (*current_loop_data)["is_first"] = true;
2737 (*current_loop_data)["is_last"] = (result->size() <= 1);
2738 for (auto it = result->begin(); it != result->end(); ++it) {
2739 additional_data[static_cast<std::string>(node.key)] = it.key();
2740 additional_data[static_cast<std::string>(node.value)] = it.value();
2741
2742 (*current_loop_data)["index"] = index;
2743 (*current_loop_data)["index1"] = index + 1;
2744 if (index == 1) {
2745 (*current_loop_data)["is_first"] = false;
2746 }
2747 if (index == result->size() - 1) {
2748 (*current_loop_data)["is_last"] = true;
2749 }
2750
2751 node.body.accept(*this);
2752 ++index;
2753 }
2754
2755 additional_data[static_cast<std::string>(node.key)].clear();
2756 additional_data[static_cast<std::string>(node.value)].clear();
2757 if (!(*current_loop_data)["parent"].empty()) {
2758 *current_loop_data = std::move((*current_loop_data)["parent"]);
2759 } else {
2761 }
2762 }
2763
2764 void visit(const IfStatementNode& node) override {
2765 const auto result = eval_expression_list(node.condition);
2766 if (truthy(result.get())) {
2767 node.true_statement.accept(*this);
2768 } else if (node.has_false_statement) {
2770 }
2771 }
2772
2773 void visit(const IncludeStatementNode& node) override {
2774 auto sub_renderer = Renderer(config, template_storage, function_storage);
2775 const auto included_template_it = template_storage.find(node.file);
2776 if (included_template_it != template_storage.end()) {
2777 sub_renderer.render_to(*output_stream, included_template_it->second, *data_input, &additional_data);
2779 throw_renderer_error("include '" + node.file + "' not found", node);
2780 }
2781 }
2782
2783 void visit(const ExtendsStatementNode& node) override {
2784 const auto included_template_it = template_storage.find(node.file);
2785 if (included_template_it != template_storage.end()) {
2786 const Template* parent_template = &included_template_it->second;
2787 render_to(*output_stream, *parent_template, *data_input, &additional_data);
2788 break_rendering = true;
2790 throw_renderer_error("extends '" + node.file + "' not found", node);
2791 }
2792 }
2793
2794 void visit(const BlockStatementNode& node) override {
2795 const size_t old_level = current_level;
2796 current_level = 0;
2798 const auto block_it = current_template->block_storage.find(node.name);
2799 if (block_it != current_template->block_storage.end()) {
2800 block_statement_stack.emplace_back(&node);
2801 block_it->second->block.accept(*this);
2802 block_statement_stack.pop_back();
2803 }
2804 current_level = old_level;
2806 }
2807
2808 void visit(const SetStatementNode& node) override {
2809 std::string ptr = node.key;
2810 replace_substring(ptr, ".", "/");
2811 ptr = "/" + ptr;
2812 additional_data[json::json_pointer(ptr)] = *eval_expression_list(node.expression);
2813 }
2814
2815 public:
2816 Renderer(const RenderConfig& config, const TemplateStorage& template_storage, const FunctionStorage& function_storage)
2817 : config(config)
2818 , template_storage(template_storage)
2819 , function_storage(function_storage) {
2820 }
2821
2822 void render_to(std::ostream& os, const Template& tmpl, const json& data, json* loop_data = nullptr) {
2823 output_stream = &os;
2824 current_template = &tmpl;
2825 data_input = &data;
2826 if (loop_data) {
2827 additional_data = *loop_data;
2829 }
2830
2831 template_stack.emplace_back(current_template);
2833
2834 data_tmp_stack.clear();
2835 }
2836 };
2837
2838} // namespace inja
2839
2840#endif // INCLUDE_INJA_RENDERER_HPP_
2841
2842// #include "template.hpp"
2843
2844// #include "utils.hpp"
2845
2846namespace inja {
2847
2848 /*!
2849 * \brief Class for changing the configuration.
2850 */
2855
2857 TemplateStorage template_storage;
2858
2859 protected:
2860 std::string input_path;
2861 std::string output_path;
2862
2863 public:
2865 : Environment("") {
2866 }
2867
2868 explicit Environment(const std::string& global_path)
2869 : input_path(global_path)
2870 , output_path(global_path) {
2871 }
2872
2873 Environment(const std::string& input_path, const std::string& output_path)
2874 : input_path(input_path)
2875 , output_path(output_path) {
2876 }
2877
2878 /// Sets the opener and closer for template statements
2879 void set_statement(const std::string& open, const std::string& close) {
2886 }
2887
2888 /// Sets the opener for template line statements
2889 void set_line_statement(const std::string& open) {
2892 }
2893
2894 /// Sets the opener and closer for template expressions
2895 void set_expression(const std::string& open, const std::string& close) {
2901 }
2902
2903 /// Sets the opener and closer for template comments
2904 void set_comment(const std::string& open, const std::string& close) {
2910 }
2911
2912 /// Sets whether to remove the first newline after a block
2913 void set_trim_blocks(bool trim_blocks) {
2914 lexer_config.trim_blocks = trim_blocks;
2915 }
2916
2917 /// Sets whether to strip the spaces and tabs from the start of a line to a block
2918 void set_lstrip_blocks(bool lstrip_blocks) {
2919 lexer_config.lstrip_blocks = lstrip_blocks;
2920 }
2921
2922 /// Sets the element notation syntax
2923 void set_search_included_templates_in_files(bool search_in_files) {
2925 }
2926
2927 /// Sets whether a missing include will throw an error
2928 void set_throw_at_missing_includes(bool will_throw) {
2930 }
2931
2932 Template parse(std::string_view input) {
2934 return parser.parse(input, input_path);
2935 }
2936
2937 Template parse_template(const std::string& filename) {
2939 auto result = Template(parser.load_file(input_path + static_cast<std::string>(filename)));
2940 parser.parse_into_template(result, input_path + static_cast<std::string>(filename));
2941 return result;
2942 }
2943
2944 Template parse_file(const std::string& filename) {
2945 return parse_template(filename);
2946 }
2947
2948 std::string render(std::string_view input, const json& data) {
2949 return render(parse(input), data);
2950 }
2951
2952 std::string render(const Template& tmpl, const json& data) {
2953 std::stringstream os;
2954 render_to(os, tmpl, data);
2955 return os.str();
2956 }
2957
2958 std::string render_file(const std::string& filename, const json& data) {
2959 return render(parse_template(filename), data);
2960 }
2961
2962 std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) {
2963 const json data = load_json(filename_data);
2964 return render_file(filename, data);
2965 }
2966
2967 void write(const std::string& filename, const json& data, const std::string& filename_out) {
2968 std::ofstream file(output_path + filename_out);
2969 file << render_file(filename, data);
2970 file.close();
2971 }
2972
2973 void write(const Template& temp, const json& data, const std::string& filename_out) {
2974 std::ofstream file(output_path + filename_out);
2975 file << render(temp, data);
2976 file.close();
2977 }
2978
2979 void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) {
2980 const json data = load_json(filename_data);
2981 write(filename, data, filename_out);
2982 }
2983
2984 void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) {
2985 const json data = load_json(filename_data);
2986 write(temp, data, filename_out);
2987 }
2988
2989 std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) {
2990 Renderer(render_config, template_storage, function_storage).render_to(os, tmpl, data);
2991 return os;
2992 }
2993
2994 std::string load_file(const std::string& filename) {
2996 return parser.load_file(input_path + filename);
2997 }
2998
2999 json load_json(const std::string& filename) {
3000 std::ifstream file;
3001 file.open(input_path + filename);
3002 if (file.fail()) {
3003 INJA_THROW(FileError("failed accessing file at '" + input_path + filename + "'"));
3004 }
3005
3006 return json::parse(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
3007 }
3008
3009 /*!
3010 @brief Adds a variadic callback
3011 */
3012 void add_callback(const std::string& name, const CallbackFunction& callback) {
3013 add_callback(name, -1, callback);
3014 }
3015
3016 /*!
3017 @brief Adds a variadic void callback
3018 */
3019 void add_void_callback(const std::string& name, const VoidCallbackFunction& callback) {
3020 add_void_callback(name, -1, callback);
3021 }
3022
3023 /*!
3024 @brief Adds a callback with given number or arguments
3025 */
3026 void add_callback(const std::string& name, int num_args, const CallbackFunction& callback) {
3027 function_storage.add_callback(name, num_args, callback);
3028 }
3029
3030 /*!
3031 @brief Adds a void callback with given number or arguments
3032 */
3033 void add_void_callback(const std::string& name, int num_args, const VoidCallbackFunction& callback) {
3034 function_storage.add_callback(name, num_args, [callback](Arguments& args) {
3035 callback(args);
3036 return json();
3037 });
3038 }
3039
3040 /** Includes a template with a given name into the environment.
3041 * Then, a template can be rendered in another template using the
3042 * include "<name>" syntax.
3043 */
3044 void include_template(const std::string& name, const Template& tmpl) {
3045 template_storage[name] = tmpl;
3046 }
3047
3048 /*!
3049 @brief Sets a function that is called when an included file is not found
3050 */
3051 void set_include_callback(const std::function<Template(const std::string&, const std::string&)>& callback) {
3053 }
3054 };
3055
3056 /*!
3057 @brief render with default settings to a string
3058 */
3059 inline std::string render(std::string_view input, const json& data) {
3060 return Environment().render(input, data);
3061 }
3062
3063 /*!
3064 @brief render with default settings to the given output stream
3065 */
3066 inline void render_to(std::ostream& os, std::string_view input, const json& data) {
3067 Environment env;
3068 env.render_to(os, env.parse(input), data);
3069 }
3070
3071} // namespace inja
3072
3073#endif // INCLUDE_INJA_ENVIRONMENT_HPP_
3074
3075// #include "exceptions.hpp"
3076
3077// #include "parser.hpp"
3078
3079// #include "renderer.hpp"
3080
3081// #include "template.hpp"
3082
3083#endif // INCLUDE_INJA_INJA_HPP_
std::vector< mqtt::lib::Function > functions
Definition Double.cpp:80
std::vector< mqtt::lib::VoidFunction > voidFunctions
Base node class for the abstract syntax tree (AST).
Definition inja.hpp:414
AstNode(size_t pos)
Definition inja.hpp:420
AstNode & operator=(const AstNode &)=default
size_t pos
Definition inja.hpp:418
virtual void accept(NodeVisitor &v) const =0
AstNode(const AstNode &)=default
virtual ~AstNode()
Definition inja.hpp:425
std::vector< std::shared_ptr< AstNode > > nodes
Definition inja.hpp:431
void accept(NodeVisitor &v) const override
Definition inja.hpp:437
void accept(NodeVisitor &v) const override
Definition inja.hpp:772
const std::string name
Definition inja.hpp:762
BlockStatementNode(BlockNode *const parent, const std::string &name, size_t pos)
Definition inja.hpp:766
BlockNode *const parent
Definition inja.hpp:764
static std::string convert_dot_to_ptr(std::string_view ptr_name)
Definition inja.hpp:486
void accept(NodeVisitor &v) const override
Definition inja.hpp:503
DataNode(std::string_view ptr_name, size_t pos)
Definition inja.hpp:497
const json::json_pointer ptr
Definition inja.hpp:484
const std::string name
Definition inja.hpp:483
Class for changing the configuration.
Definition inja.hpp:2851
LexerConfig lexer_config
Definition inja.hpp:2852
TemplateStorage template_storage
Definition inja.hpp:2857
FunctionStorage function_storage
Definition inja.hpp:2856
Environment(const std::string &input_path, const std::string &output_path)
Definition inja.hpp:2873
void set_throw_at_missing_includes(bool will_throw)
Sets whether a missing include will throw an error.
Definition inja.hpp:2928
void set_statement(const std::string &open, const std::string &close)
Sets the opener and closer for template statements.
Definition inja.hpp:2879
void set_search_included_templates_in_files(bool search_in_files)
Sets the element notation syntax.
Definition inja.hpp:2923
json load_json(const std::string &filename)
Definition inja.hpp:2999
void set_trim_blocks(bool trim_blocks)
Sets whether to remove the first newline after a block.
Definition inja.hpp:2913
std::string load_file(const std::string &filename)
Definition inja.hpp:2994
RenderConfig render_config
Definition inja.hpp:2854
std::string render_file_with_json_file(const std::string &filename, const std::string &filename_data)
Definition inja.hpp:2962
void set_lstrip_blocks(bool lstrip_blocks)
Sets whether to strip the spaces and tabs from the start of a line to a block.
Definition inja.hpp:2918
void set_comment(const std::string &open, const std::string &close)
Sets the opener and closer for template comments.
Definition inja.hpp:2904
void set_line_statement(const std::string &open)
Sets the opener for template line statements.
Definition inja.hpp:2889
void set_include_callback(const std::function< Template(const std::string &, const std::string &)> &callback)
Sets a function that is called when an included file is not found.
Definition inja.hpp:3051
Template parse(std::string_view input)
Definition inja.hpp:2932
Template parse_file(const std::string &filename)
Definition inja.hpp:2944
void write_with_json_file(const Template &temp, const std::string &filename_data, const std::string &filename_out)
Definition inja.hpp:2984
Template parse_template(const std::string &filename)
Definition inja.hpp:2937
void set_expression(const std::string &open, const std::string &close)
Sets the opener and closer for template expressions.
Definition inja.hpp:2895
void include_template(const std::string &name, const Template &tmpl)
Definition inja.hpp:3044
std::string input_path
Definition inja.hpp:2860
void write_with_json_file(const std::string &filename, const std::string &filename_data, const std::string &filename_out)
Definition inja.hpp:2979
Environment(const std::string &global_path)
Definition inja.hpp:2868
ParserConfig parser_config
Definition inja.hpp:2853
std::string output_path
Definition inja.hpp:2861
void accept(NodeVisitor &v) const override
Definition inja.hpp:648
ExpressionListNode(size_t pos)
Definition inja.hpp:644
std::shared_ptr< ExpressionNode > root
Definition inja.hpp:639
void accept(NodeVisitor &v) const override
Definition inja.hpp:462
ExpressionNode(size_t pos)
Definition inja.hpp:458
ExtendsStatementNode(const std::string &file, size_t pos)
Definition inja.hpp:750
const std::string file
Definition inja.hpp:748
void accept(NodeVisitor &v) const override
Definition inja.hpp:755
ForArrayStatementNode(const std::string &value, BlockNode *const parent, size_t pos)
Definition inja.hpp:680
void accept(NodeVisitor &v) const override
Definition inja.hpp:685
const std::string value
Definition inja.hpp:678
ForObjectStatementNode(const std::string &key, const std::string &value, BlockNode *const parent, size_t pos)
Definition inja.hpp:695
const std::string key
Definition inja.hpp:692
void accept(NodeVisitor &v) const override
Definition inja.hpp:701
const std::string value
Definition inja.hpp:693
ExpressionListNode condition
Definition inja.hpp:664
ForStatementNode(BlockNode *const parent, size_t pos)
Definition inja.hpp:668
virtual void accept(NodeVisitor &v) const override=0
BlockNode *const parent
Definition inja.hpp:666
std::vector< std::shared_ptr< ExpressionNode > > arguments
Definition inja.hpp:524
Associativity associativity
Definition inja.hpp:518
void accept(NodeVisitor &v) const override
Definition inja.hpp:632
CallbackFunction callback
Definition inja.hpp:525
FunctionNode(Op operation, size_t pos)
Definition inja.hpp:535
std::string name
Definition inja.hpp:522
FunctionStorage::Operation Op
Definition inja.hpp:509
unsigned int precedence
Definition inja.hpp:517
FunctionNode(std::string_view name, size_t pos)
Definition inja.hpp:527
Class for builtin functions and user-defined callbacks.
Definition inja.hpp:105
void add_builtin(std::string_view name, int num_args, Operation op)
Definition inja.hpp:202
std::map< std::pair< std::string, int >, FunctionData > function_storage
Definition inja.hpp:169
const int VARIADIC
Definition inja.hpp:167
FunctionData find_function(std::string_view name, int num_args) const
Definition inja.hpp:210
void accept(NodeVisitor &v) const override
Definition inja.hpp:727
BlockNode *const parent
Definition inja.hpp:711
const bool is_nested
Definition inja.hpp:713
BlockNode false_statement
Definition inja.hpp:710
BlockNode true_statement
Definition inja.hpp:709
IfStatementNode(BlockNode *const parent, size_t pos)
Definition inja.hpp:716
ExpressionListNode condition
Definition inja.hpp:708
IfStatementNode(bool is_nested, BlockNode *const parent, size_t pos)
Definition inja.hpp:721
const std::string file
Definition inja.hpp:734
void accept(NodeVisitor &v) const override
Definition inja.hpp:741
IncludeStatementNode(const std::string &file, size_t pos)
Definition inja.hpp:736
Class for lexing an inja Template.
Definition inja.hpp:1097
const LexerConfig & config
Definition inja.hpp:1119
SourceLocation current_position() const
Definition inja.hpp:1364
static std::string_view clear_final_line_if_whitespace(std::string_view text)
Definition inja.hpp:1342
const LexerConfig & get_config() const
Definition inja.hpp:1517
Token scan_string()
Definition inja.hpp:1292
void skip_whitespaces_and_newlines()
Definition inja.hpp:1314
Token make_token(Token::Kind kind) const
Definition inja.hpp:1310
Token scan()
Definition inja.hpp:1381
Token scan_body(std::string_view close, Token::Kind closeKind, std::string_view close_trim=std::string_view(), bool trim=false)
Definition inja.hpp:1128
std::string_view m_in
Definition inja.hpp:1123
Token scan_number()
Definition inja.hpp:1275
size_t pos
Definition inja.hpp:1125
State state
Definition inja.hpp:1121
Token scan_id()
Definition inja.hpp:1261
Lexer(const LexerConfig &config)
Definition inja.hpp:1358
size_t tok_start
Definition inja.hpp:1124
void skip_whitespaces_and_first_newline()
Definition inja.hpp:1322
void start(std::string_view input)
Definition inja.hpp:1368
MinusState minus_state
Definition inja.hpp:1122
void accept(NodeVisitor &v) const override
Definition inja.hpp:476
LiteralNode(std::string_view data_text, size_t pos)
Definition inja.hpp:471
const json value
Definition inja.hpp:469
virtual void visit(const DataNode &node)=0
virtual void visit(const SetStatementNode &node)=0
virtual void visit(const ExpressionListNode &node)=0
virtual ~NodeVisitor()=default
virtual void visit(const StatementNode &node)=0
virtual void visit(const ExtendsStatementNode &node)=0
virtual void visit(const IncludeStatementNode &node)=0
virtual void visit(const TextNode &node)=0
virtual void visit(const ForStatementNode &node)=0
virtual void visit(const LiteralNode &node)=0
virtual void visit(const ExpressionNode &node)=0
virtual void visit(const ForObjectStatementNode &node)=0
virtual void visit(const BlockStatementNode &node)=0
virtual void visit(const IfStatementNode &node)=0
virtual void visit(const FunctionNode &node)=0
virtual void visit(const ForArrayStatementNode &node)=0
virtual void visit(const BlockNode &node)=0
Class for parsing an inja Template.
Definition inja.hpp:1539
std::string load_file(const std::string &filename)
Definition inja.hpp:2165
Template parse(std::string_view input, std::string_view path)
Definition inja.hpp:2151
void get_peek_token()
Definition inja.hpp:1574
void throw_parser_error(const std::string &message) const
Definition inja.hpp:1561
std::string parse_filename() const
Definition inja.hpp:1641
std::stack< BlockStatementNode * > block_statement_stack
Definition inja.hpp:1559
std::stack< ForStatementNode * > for_statement_stack
Definition inja.hpp:1558
ExpressionListNode * current_expression_list
Definition inja.hpp:1555
bool parse_statement(Template &tmpl, Token::Kind closing, std::string_view path)
Definition inja.hpp:1887
TemplateStorage & template_storage
Definition inja.hpp:1546
Token peek_tok
Definition inja.hpp:1549
Token tok
Definition inja.hpp:1549
std::shared_ptr< ExpressionNode > parse_expression(Template &tmpl)
Definition inja.hpp:1659
void parse_into_template(Template &tmpl, std::string_view filename)
Definition inja.hpp:2157
void parse_into(Template &tmpl, std::string_view path)
Definition inja.hpp:2078
const FunctionStorage & function_storage
Definition inja.hpp:1547
const ParserConfig & config
Definition inja.hpp:1543
void get_next_token()
Definition inja.hpp:1565
std::stack< IfStatementNode * > if_statement_stack
Definition inja.hpp:1557
BlockNode * current_block
Definition inja.hpp:1554
void add_to_template_storage(std::string_view path, std::string &template_name)
Definition inja.hpp:1602
std::string_view literal_start
Definition inja.hpp:1552
bool parse_expression(Template &tmpl, Token::Kind closing)
Definition inja.hpp:1654
Lexer lexer
Definition inja.hpp:1545
bool have_peek_tok
Definition inja.hpp:1550
Class for rendering a Template with data.
Definition inja.hpp:2205
const json * data_input
Definition inja.hpp:2217
void visit(const FunctionNode &node) override
Definition inja.hpp:2399
void visit(const ExpressionListNode &node) override
Definition inja.hpp:2676
const std::shared_ptr< json > eval_expression_list(const ExpressionListNode &expression_list)
Definition inja.hpp:2253
std::vector< std::shared_ptr< json > > data_tmp_stack
Definition inja.hpp:2223
void visit(const DataNode &node) override
Definition inja.hpp:2379
void visit(const StatementNode &) override
Definition inja.hpp:2680
size_t current_level
Definition inja.hpp:2213
std::ostream * output_stream
Definition inja.hpp:2218
void throw_renderer_error(const std::string &message, const AstNode &node)
Definition inja.hpp:2282
const RenderConfig config
Definition inja.hpp:2208
std::stack< const json * > data_eval_stack
Definition inja.hpp:2224
json additional_data
Definition inja.hpp:2220
std::stack< const DataNode * > not_found_stack
Definition inja.hpp:2225
void visit(const TextNode &node) override
Definition inja.hpp:2368
std::vector< const BlockStatementNode * > block_statement_stack
Definition inja.hpp:2215
const Template * current_template
Definition inja.hpp:2212
void visit(const ExpressionNode &) override
Definition inja.hpp:2372
void visit(const SetStatementNode &node) override
Definition inja.hpp:2808
bool break_rendering
Definition inja.hpp:2227
void visit(const IncludeStatementNode &node) override
Definition inja.hpp:2773
void visit(const ForStatementNode &) override
Definition inja.hpp:2683
FunctionStorage::Operation Op
Definition inja.hpp:2206
std::vector< const Template * > template_stack
Definition inja.hpp:2214
void visit(const ForArrayStatementNode &node) override
Definition inja.hpp:2686
const FunctionStorage & function_storage
Definition inja.hpp:2210
const TemplateStorage & template_storage
Definition inja.hpp:2209
void visit(const IfStatementNode &node) override
Definition inja.hpp:2764
void visit(const BlockNode &node) override
Definition inja.hpp:2358
void visit(const LiteralNode &node) override
Definition inja.hpp:2375
json * current_loop_data
Definition inja.hpp:2221
void visit(const ExtendsStatementNode &node) override
Definition inja.hpp:2783
void visit(const ForObjectStatementNode &node) override
Definition inja.hpp:2725
void visit(const BlockStatementNode &node) override
Definition inja.hpp:2794
ExpressionListNode expression
Definition inja.hpp:780
const std::string key
Definition inja.hpp:779
SetStatementNode(const std::string &key, size_t pos)
Definition inja.hpp:782
void accept(NodeVisitor &v) const override
Definition inja.hpp:787
StatementNode(size_t pos)
Definition inja.hpp:655
virtual void accept(NodeVisitor &v) const override=0
A class for counting statistics on a Template.
Definition inja.hpp:807
void visit(const LiteralNode &) override
Definition inja.hpp:818
void visit(const IfStatementNode &node) override
Definition inja.hpp:850
void visit(const DataNode &) override
Definition inja.hpp:821
void visit(const IncludeStatementNode &) override
Definition inja.hpp:856
void visit(const ForArrayStatementNode &node) override
Definition inja.hpp:840
unsigned int variable_counter
Definition inja.hpp:870
void visit(const StatementNode &) override
Definition inja.hpp:835
void visit(const BlockNode &node) override
Definition inja.hpp:808
void visit(const FunctionNode &node) override
Definition inja.hpp:825
void visit(const ExpressionListNode &node) override
Definition inja.hpp:831
void visit(const ForStatementNode &) override
Definition inja.hpp:837
void visit(const SetStatementNode &) override
Definition inja.hpp:866
void visit(const ExtendsStatementNode &) override
Definition inja.hpp:859
void visit(const TextNode &) override
Definition inja.hpp:814
void visit(const ExpressionNode &) override
Definition inja.hpp:816
void visit(const ForObjectStatementNode &node) override
Definition inja.hpp:845
void visit(const BlockStatementNode &node) override
Definition inja.hpp:862
const size_t length
Definition inja.hpp:444
TextNode(size_t pos, size_t length)
Definition inja.hpp:446
void accept(NodeVisitor &v) const override
Definition inja.hpp:451
static void store(const inja::Arguments &args)
Definition Storage.cpp:63
static double recall_as_float(const inja::Arguments &args)
Definition Storage.cpp:85
static const std::string & recall(const inja::Arguments &args)
Definition Storage.cpp:67
static bool exists(const inja::Arguments &args)
Definition Storage.cpp:109
static bool is_empty(const inja::Arguments &args)
Definition Storage.cpp:99
Storage & operator=(const Storage &)=delete
static int recall_as_int(const inja::Arguments &args)
Definition Storage.cpp:71
std::map< std::string, std::string > storage
Definition Storage.h:95
#define INJA_THROW(exception)
Definition inja.hpp:45
std::pair< std::string_view, std::string_view > split(std::string_view view, char Separator)
Definition inja.hpp:317
bool starts_with(std::string_view view, std::string_view prefix)
Definition inja.hpp:325
std::string_view slice(std::string_view view, size_t start, size_t end)
Definition inja.hpp:311
std::vector< const nlohmann::json * > Arguments
void replace_substring(std::string &s, const std::string &f, const std::string &t)
Definition inja.hpp:353
SourceLocation get_source_location(std::string_view content, size_t pos)
Definition inja.hpp:330
nlohmann::json json
DataError(const std::string &message, SourceLocation location)
Definition inja.hpp:299
FileError(const std::string &message)
Definition inja.hpp:290
FileError(const std::string &message, SourceLocation location)
Definition inja.hpp:293
const CallbackFunction callback
Definition inja.hpp:163
InjaError(const std::string &type, const std::string &message)
Definition inja.hpp:261
InjaError(const std::string &type, const std::string &message, SourceLocation location)
Definition inja.hpp:268
const std::string type
Definition inja.hpp:256
const SourceLocation location
Definition inja.hpp:259
const std::string message
Definition inja.hpp:257
Class for lexer configuration.
Definition inja.hpp:916
std::string expression_open_force_lstrip
Definition inja.hpp:924
std::string expression_close
Definition inja.hpp:925
std::string expression_close_force_rstrip
Definition inja.hpp:926
std::string comment_open_force_lstrip
Definition inja.hpp:928
std::string comment_close
Definition inja.hpp:929
std::string statement_close
Definition inja.hpp:920
std::string expression_open
Definition inja.hpp:923
std::string comment_close_force_rstrip
Definition inja.hpp:930
std::string statement_open_force_lstrip
Definition inja.hpp:919
std::string statement_close_force_rstrip
Definition inja.hpp:921
std::string open_chars
Definition inja.hpp:931
std::string statement_open_no_lstrip
Definition inja.hpp:918
void update_open_chars()
Definition inja.hpp:936
std::string comment_open
Definition inja.hpp:927
std::string statement_open
Definition inja.hpp:917
std::string line_statement
Definition inja.hpp:922
Class for parser configuration.
Definition inja.hpp:968
bool search_included_templates_in_files
Definition inja.hpp:969
std::function< Template(const std::string &, const std::string &)> include_callback
Definition inja.hpp:971
ParserError(const std::string &message, SourceLocation location)
Definition inja.hpp:278
Class for render configuration.
Definition inja.hpp:977
bool throw_at_missing_includes
Definition inja.hpp:978
RenderError(const std::string &message, SourceLocation location)
Definition inja.hpp:284
The main inja Template.
Definition inja.hpp:886
Template(const std::string &content)
Definition inja.hpp:893
unsigned int count_variables()
Return number of variables (total number, not distinct ones) in the template.
Definition inja.hpp:898
std::map< std::string, std::shared_ptr< BlockStatementNode > > block_storage
Definition inja.hpp:889
BlockNode root
Definition inja.hpp:887
std::string content
Definition inja.hpp:888
Helper-class for the inja Lexer.
Definition inja.hpp:1024
std::string_view text
Definition inja.hpp:1064
constexpr Token()=default
Kind kind
Definition inja.hpp:1063
std::string describe() const
Definition inja.hpp:1072
constexpr Token(Kind kind, std::string_view text)
Definition inja.hpp:1067