/* Inja - A Template Engine for Modern C++ version 1.0.0 https://github.com/pantor/inja Licensed under the MIT License . Copyright (c) 2017-2018 Pantor . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef PANTOR_INJA_HPP #define PANTOR_INJA_HPP #ifndef NLOHMANN_JSON_HPP static_assert(false, "nlohmann/json not found."); #endif #include #include #include #include #include #include #include #include #include namespace inja { using json = nlohmann::json; /*! @brief dot notation to json pointer notiation */ inline std::string dot_to_json_pointer_notation(std::string dot) { std::string result = dot; while (result.find(".") != std::string::npos) { result.replace(result.find("."), 1, "/"); } result.insert(0, "/"); return result; } /*! @brief inja regex class, saves string pattern in addition to std::regex */ class Regex: public std::regex { std::string pattern_; public: Regex(): std::regex() {} explicit Regex(const std::string& pattern): std::regex(pattern, std::regex_constants::ECMAScript), pattern_(pattern) { } std::string pattern() { return pattern_; } }; class Match: public std::match_results { size_t offset_ = 0; unsigned int group_offset_ = 0; Regex regex_; public: Match(): std::match_results() { } explicit Match(size_t offset): std::match_results(), offset_(offset) { } explicit Match(size_t offset, const Regex& regex): std::match_results(), offset_(offset), regex_(regex) { } void set_group_offset(unsigned int group_offset) { group_offset_ = group_offset; } void set_regex(Regex regex) { regex_ = regex; } size_t position() const { return offset_ + std::match_results::position(); } size_t end_position() const { return position() + length(); } bool found() const { return not empty(); } const std::string str() const { return str(0); } const std::string str(int i) const { return std::match_results::str(i + group_offset_); } Regex regex() const { return regex_; } }; template class MatchType: public Match { T type_; public: MatchType() : Match() { } explicit MatchType(const Match& obj) : Match(obj) { } MatchType(Match&& obj) : Match(std::move(obj)) { } void set_type(T type) { type_ = type; } T type() const { return type_; } }; class MatchClosed { public: Match open_match, close_match; MatchClosed() { } MatchClosed(Match& open_match, Match& close_match): open_match(open_match), close_match(close_match) { } size_t position() const { return open_match.position(); } size_t end_position() const { return close_match.end_position(); } int length() const { return close_match.end_position() - open_match.position(); } bool found() const { return open_match.found() and close_match.found(); } std::string prefix() const { return open_match.prefix().str(); } std::string suffix() const { return close_match.suffix().str(); } std::string outer() const { return open_match.str() + static_cast(open_match.suffix()).substr(0, close_match.end_position() - open_match.end_position()); } std::string inner() const { return static_cast(open_match.suffix()).substr(0, close_match.position() - open_match.end_position()); } }; inline Match search(const std::string& input, Regex regex, size_t position) { if (position >= input.length()) { return Match(); } Match match{position, regex}; std::regex_search(input.cbegin() + position, input.cend(), match, regex); return match; } template inline MatchType search(const std::string& input, std::map& regexes, size_t position) { // Map to vectors std::vector class_vector; std::vector regexes_vector; for (const auto element: regexes) { class_vector.push_back(element.first); regexes_vector.push_back(element.second); } // Regex join std::stringstream ss; for (size_t i = 0; i < regexes_vector.size(); ++i) { if (i != 0) { ss << ")|("; } ss << regexes_vector[i].pattern(); } Regex regex{"(" + ss.str() + ")"}; MatchType search_match = search(input, regex, position); if (not search_match.found()) { return MatchType(); } // Vector of id vs groups std::vector regex_mark_counts = {}; for (unsigned int i = 0; i < regexes_vector.size(); i++) { for (unsigned int j = 0; j < regexes_vector[i].mark_count() + 1; j++) { regex_mark_counts.push_back(i); } } for (unsigned int i = 1; i < search_match.size(); i++) { if (search_match.length(i) > 0) { search_match.set_group_offset(i); search_match.set_type(class_vector[regex_mark_counts[i]]); search_match.set_regex(regexes_vector[regex_mark_counts[i]]); return search_match; } } throw std::runtime_error("Error while searching in input: " + input); return search_match; } inline MatchClosed search_closed_on_level(const std::string& input, const Regex& regex_statement, const Regex& regex_level_up, const Regex& regex_level_down, const Regex& regex_search, Match open_match) { int level = 0; size_t current_position = open_match.end_position(); Match match_delimiter = search(input, regex_statement, current_position); while (match_delimiter.found()) { current_position = match_delimiter.end_position(); const std::string inner = match_delimiter.str(1); if (std::regex_match(inner.cbegin(), inner.cend(), regex_search) and level == 0) { break; } if (std::regex_match(inner.cbegin(), inner.cend(), regex_level_up)) { level += 1; } else if (std::regex_match(inner.cbegin(), inner.cend(), regex_level_down)) { level -= 1; } match_delimiter = search(input, regex_statement, current_position); } return MatchClosed(open_match, match_delimiter); } inline MatchClosed search_closed(const std::string& input, const Regex& regex_statement, const Regex& regex_open, const Regex& regex_close, Match& open_match) { return search_closed_on_level(input, regex_statement, regex_open, regex_close, regex_close, open_match); } template inline MatchType match(const std::string& input, std::map regexes) { MatchType match; for (const auto e : regexes) { if (std::regex_match(input.cbegin(), input.cend(), match, e.second)) { match.set_type(e.first); match.set_regex(e.second); return match; } } return match; } enum class ElementNotation { Pointer, Dot }; struct Parsed { enum class Type { Main, String, Comment, Expression, Loop, Condition, ConditionBranch }; enum class Delimiter { Statement, LineStatement, Expression, Comment }; enum class Statement { Loop, Condition, Include }; enum class Function { Not, And, Or, In, Equal, Greater, Less, GreaterEqual, LessEqual, Different, Upper, Lower, Range, Length, Sort, First, Last, Round, DivisibleBy, Odd, Even, Max, Min, ReadJson, Default, Callback }; enum class Condition { If, ElseIf, Else }; enum class Loop { ForListIn, ForMapIn }; struct Element { Type type; std::string inner; std::vector> children; explicit Element(const Type type): Element(type, "") { } explicit Element(const Type type, const std::string& inner): type(type), inner(inner), children({}) { } }; struct ElementString: public Element { const std::string text; explicit ElementString(const std::string& text): Element(Type::String), text(text) { } }; struct ElementComment: public Element { const std::string text; explicit ElementComment(const std::string& text): Element(Type::Comment), text(text) { } }; struct ElementExpression: public Element { Function function; std::vector args; std::string command; explicit ElementExpression(): ElementExpression(Function::ReadJson) { } explicit ElementExpression(const Function function_): Element(Type::Expression), function(function_), args({}), command("") { } }; typedef std::vector Arguments; struct ElementLoop: public Element { Loop loop; const std::string key; const std::string value; const ElementExpression list; explicit ElementLoop(const Loop loop_, const std::string& value, const ElementExpression& list, const std::string& inner): Element(Type::Loop, inner), loop(loop_), value(value), list(list) { } explicit ElementLoop(const Loop loop_, const std::string& key, const std::string& value, const ElementExpression& list, const std::string& inner): Element(Type::Loop, inner), loop(loop_), key(key), value(value), list(list) { } }; struct ElementConditionContainer: public Element { explicit ElementConditionContainer(): Element(Type::Condition) { } }; struct ElementConditionBranch: public Element { const Condition condition_type; const ElementExpression condition; explicit ElementConditionBranch(const std::string& inner, const Condition condition_type): Element(Type::ConditionBranch, inner), condition_type(condition_type) { } explicit ElementConditionBranch(const std::string& inner, const Condition condition_type, const ElementExpression& condition): Element(Type::ConditionBranch, inner), condition_type(condition_type), condition(condition) { } }; }; class Template { public: const Parsed::Element parsed_template; explicit Template(const Parsed::Element& parsed_template): parsed_template(parsed_template) { } }; class Renderer { public: ElementNotation element_notation; std::map> map_callbacks; template bool eval_expression(const Parsed::ElementExpression& element, json data) { const json var = eval_function(element, data); if (var.empty()) { return false; } else if (var.is_number()) { return (var != 0); } else if (var.is_string()) { return not var.empty(); } return var.get(); } template T eval_expression(const Parsed::ElementExpression& element, json data) { return eval_function(element, data).get(); } json eval_function(const Parsed::ElementExpression& element, json data) { switch (element.function) { case Parsed::Function::Upper: { std::string str = eval_expression(element.args[0], data); std::transform(str.begin(), str.end(), str.begin(), toupper); return str; } case Parsed::Function::Lower: { std::string str = eval_expression(element.args[0], data); std::transform(str.begin(), str.end(), str.begin(), tolower); return str; } case Parsed::Function::Range: { const int number = eval_expression(element.args[0], data); std::vector result(number); std::iota(std::begin(result), std::end(result), 0); return result; } case Parsed::Function::Length: { const std::vector list = eval_expression>(element.args[0], data); return list.size(); } case Parsed::Function::Sort: { std::vector list = eval_expression>(element.args[0], data); std::sort(list.begin(), list.end()); return list; } case Parsed::Function::First: { const std::vector list = eval_expression>(element.args[0], data); return list.front(); } case Parsed::Function::Last: { const std::vector list = eval_expression>(element.args[0], data); return list.back(); } case Parsed::Function::Round: { const double number = eval_expression(element.args[0], data); const int precision = eval_expression(element.args[1], data); return std::round(number * std::pow(10.0, precision)) / std::pow(10.0, precision); } case Parsed::Function::DivisibleBy: { const int number = eval_expression(element.args[0], data); const int divisor = eval_expression(element.args[1], data); return (number % divisor == 0); } case Parsed::Function::Odd: { const int number = eval_expression(element.args[0], data); return (number % 2 != 0); } case Parsed::Function::Even: { const int number = eval_expression(element.args[0], data); return (number % 2 == 0); } case Parsed::Function::Max: { const std::vector list = eval_expression>(element.args[0], data); return *std::max_element(list.begin(), list.end()); } case Parsed::Function::Min: { const std::vector list = eval_expression>(element.args[0], data); return *std::min_element(list.begin(), list.end()); } case Parsed::Function::Not: { return not eval_expression(element.args[0], data); } case Parsed::Function::And: { return (eval_expression(element.args[0], data) and eval_expression(element.args[1], data)); } case Parsed::Function::Or: { return (eval_expression(element.args[0], data) or eval_expression(element.args[1], data)); } case Parsed::Function::In: { const json value = eval_expression(element.args[0], data); const json list = eval_expression(element.args[1], data); return (std::find(list.begin(), list.end(), value) != list.end()); } case Parsed::Function::Equal: { return eval_expression(element.args[0], data) == eval_expression(element.args[1], data); } case Parsed::Function::Greater: { return eval_expression(element.args[0], data) > eval_expression(element.args[1], data); } case Parsed::Function::Less: { return eval_expression(element.args[0], data) < eval_expression(element.args[1], data); } case Parsed::Function::GreaterEqual: { return eval_expression(element.args[0], data) >= eval_expression(element.args[1], data); } case Parsed::Function::LessEqual: { return eval_expression(element.args[0], data) <= eval_expression(element.args[1], data); } case Parsed::Function::Different: { return eval_expression(element.args[0], data) != eval_expression(element.args[1], data); } case Parsed::Function::ReadJson: { if ( json::accept(element.command) ) { return json::parse(element.command); } // Json Raw Data std::string input = element.command; switch (element_notation) { case ElementNotation::Pointer: { if (input[0] != '/') { input.insert(0, "/"); } break; } case ElementNotation::Dot: { input = dot_to_json_pointer_notation(input); break; } } const json result = data[json::json_pointer(input)]; if (result.is_null()) { throw std::runtime_error("Did not found json element: " + element.command); } return result; } case Parsed::Function::Default: { try { return eval_expression(element.args[0], data); } catch (std::exception& exception) { return eval_expression(element.args[1], data); } } case Parsed::Function::Callback: { return map_callbacks[element.command](element.args, data); } } throw std::runtime_error("Unknown function in renderer."); return json(); } std::string render(Template temp, json data) { std::string result = ""; for (auto element: temp.parsed_template.children) { switch (element->type) { case Parsed::Type::Main: { throw std::runtime_error("Main type in renderer."); } case Parsed::Type::String: { auto element_string = std::static_pointer_cast(element); result += element_string->text; break; } case Parsed::Type::Expression: { auto element_expression = std::static_pointer_cast(element); json variable = eval_expression(*element_expression, data); if (variable.is_string()) { result += variable.get(); } else { std::stringstream ss; ss << variable; result += ss.str(); } break; } case Parsed::Type::Loop: { auto element_loop = std::static_pointer_cast(element); switch (element_loop->loop) { case Parsed::Loop::ForListIn: { const std::vector list = eval_expression>(element_loop->list, data); for (unsigned int i = 0; i < list.size(); i++) { json data_loop = data; data_loop[element_loop->value] = list[i]; data_loop["index"] = i; data_loop["index1"] = i + 1; data_loop["is_first"] = (i == 0); data_loop["is_last"] = (i == list.size() - 1); result += render(Template(*element_loop), data_loop); } break; } case Parsed::Loop::ForMapIn: { const std::map map = eval_expression>(element_loop->list, data); for (auto const& item : map) { json data_loop = data; data_loop[element_loop->key] = item.first; data_loop[element_loop->value] = item.second; result += render(Template(*element_loop), data_loop); } break; } } break; } case Parsed::Type::Condition: { auto element_condition = std::static_pointer_cast(element); for (auto branch: element_condition->children) { auto element_branch = std::static_pointer_cast(branch); if (element_branch->condition_type == Parsed::Condition::Else || eval_expression(element_branch->condition, data)) { result += render(Template(*element_branch), data); break; } } break; } case Parsed::Type::ConditionBranch: { throw std::runtime_error("ConditionBranch type in renderer."); } case Parsed::Type::Comment: { break; } } } return result; } }; class Parser { public: static Regex function_regex(std::string name, int number_arguments) { std::string pattern = name; if (number_arguments > 0) { pattern += "\\("; for (int i = 0; i < number_arguments; i++) { if (i != 0) pattern += ","; pattern += "(.*)"; } pattern += "\\)"; } return Regex{"\\s*" + pattern + "\\s*"}; } std::map regex_map_delimiters = { {Parsed::Delimiter::Statement, Regex{"\\{\\%\\s*(.+?)\\s*\\%\\}"}}, {Parsed::Delimiter::LineStatement, Regex{"(?:^|\\n)##\\s*(.+)\\s*"}}, {Parsed::Delimiter::Expression, Regex{"\\{\\{\\s*(.+?)\\s*\\}\\}"}}, {Parsed::Delimiter::Comment, Regex{"\\{#\\s*(.*?)\\s*#\\}"}} }; const std::map regex_map_statement_openers = { {Parsed::Statement::Loop, Regex{"for (.+)"}}, {Parsed::Statement::Condition, Regex{"if (.+)"}}, {Parsed::Statement::Include, Regex{"include \"(.+)\""}} }; const std::map regex_map_statement_closers = { {Parsed::Statement::Loop, Regex{"endfor"}}, {Parsed::Statement::Condition, Regex{"endif"}} }; const std::map regex_map_loop = { {Parsed::Loop::ForListIn, Regex{"for (\\w+) in (.+)"}}, {Parsed::Loop::ForMapIn, Regex{"for (\\w+), (\\w+) in (.+)"}}, }; const std::map regex_map_condition = { {Parsed::Condition::If, Regex{"if (.+)"}}, {Parsed::Condition::ElseIf, Regex{"else if (.+)"}}, {Parsed::Condition::Else, Regex{"else"}} }; const std::map regex_map_functions = { {Parsed::Function::Not, Regex{"not (.+)"}}, {Parsed::Function::And, Regex{"(.+) and (.+)"}}, {Parsed::Function::Or, Regex{"(.+) or (.+)"}}, {Parsed::Function::In, Regex{"(.+) in (.+)"}}, {Parsed::Function::Equal, Regex{"(.+) == (.+)"}}, {Parsed::Function::Greater, Regex{"(.+) > (.+)"}}, {Parsed::Function::Less, Regex{"(.+) < (.+)"}}, {Parsed::Function::GreaterEqual, Regex{"(.+) >= (.+)"}}, {Parsed::Function::LessEqual, Regex{"(.+) <= (.+)"}}, {Parsed::Function::Different, Regex{"(.+) != (.+)"}}, {Parsed::Function::Upper, function_regex("upper", 1)}, {Parsed::Function::Lower, function_regex("lower", 1)}, {Parsed::Function::Range, function_regex("range", 1)}, {Parsed::Function::Length, function_regex("length", 1)}, {Parsed::Function::Sort, function_regex("sort", 1)}, {Parsed::Function::First, function_regex("first", 1)}, {Parsed::Function::Last, function_regex("last", 1)}, {Parsed::Function::Round, function_regex("round", 2)}, {Parsed::Function::DivisibleBy, function_regex("divisibleBy", 2)}, {Parsed::Function::Odd, function_regex("odd", 1)}, {Parsed::Function::Even, function_regex("even", 1)}, {Parsed::Function::Max, function_regex("max", 1)}, {Parsed::Function::Min, function_regex("min", 1)}, {Parsed::Function::ReadJson, Regex{"\\s*([^\\(\\)]*\\S)\\s*"}}, {Parsed::Function::Default, function_regex("default", 2)} }; std::map regex_map_callbacks; Parser() { } Parsed::ElementExpression parse_expression(const std::string& input) { MatchType match_callback = match(input, regex_map_callbacks); if (!match_callback.type().empty()) { std::vector args = {}; for (unsigned int i = 1; i < match_callback.size(); i++) { // str(0) is whole group args.push_back( parse_expression(match_callback.str(i)) ); } Parsed::ElementExpression result = Parsed::ElementExpression(Parsed::Function::Callback); result.args = args; result.command = match_callback.type(); return result; } MatchType match_function = match(input, regex_map_functions); switch ( match_function.type() ) { case Parsed::Function::ReadJson: { Parsed::ElementExpression result = Parsed::ElementExpression(Parsed::Function::ReadJson); result.command = match_function.str(1); return result; } default: { std::vector args = {}; for (unsigned int i = 1; i < match_function.size(); i++) { // str(0) is whole group args.push_back( parse_expression(match_function.str(i)) ); } Parsed::ElementExpression result = Parsed::ElementExpression(match_function.type()); result.args = args; return result; } } } std::vector> parse_level(const std::string& input, const std::string& path) { std::vector> result; size_t current_position = 0; MatchType match_delimiter = search(input, regex_map_delimiters, current_position); while (match_delimiter.found()) { current_position = match_delimiter.end_position(); std::string string_prefix = match_delimiter.prefix(); if (not string_prefix.empty()) { result.emplace_back( std::make_shared(string_prefix) ); } std::string delimiter_inner = match_delimiter.str(1); switch ( match_delimiter.type() ) { case Parsed::Delimiter::Statement: case Parsed::Delimiter::LineStatement: { MatchType match_statement = match(delimiter_inner, regex_map_statement_openers); switch ( match_statement.type() ) { case Parsed::Statement::Loop: { MatchClosed loop_match = search_closed(input, match_delimiter.regex(), regex_map_statement_openers.at(Parsed::Statement::Loop), regex_map_statement_closers.at(Parsed::Statement::Loop), match_delimiter); current_position = loop_match.end_position(); const std::string loop_inner = match_statement.str(0); MatchType match_command = match(loop_inner, regex_map_loop); switch (match_command.type()) { case Parsed::Loop::ForListIn: { const std::string value_name = match_command.str(1); const std::string list_name = match_command.str(2); result.emplace_back( std::make_shared(match_command.type(), value_name, parse_expression(list_name), loop_match.inner())); break; } case Parsed::Loop::ForMapIn: { const std::string key_name = match_command.str(1); const std::string value_name = match_command.str(2); const std::string list_name = match_command.str(3); result.emplace_back( std::make_shared(match_command.type(), key_name, value_name, parse_expression(list_name), loop_match.inner())); break; } default: { throw std::runtime_error("Unknown loop statement."); } } break; } case Parsed::Statement::Condition: { auto condition_container = std::make_shared(); Match condition_match = match_delimiter; MatchClosed else_if_match = search_closed_on_level(input, match_delimiter.regex(), regex_map_statement_openers.at(Parsed::Statement::Condition), regex_map_statement_closers.at(Parsed::Statement::Condition), regex_map_condition.at(Parsed::Condition::ElseIf), condition_match); while (else_if_match.found()) { condition_match = else_if_match.close_match; const std::string else_if_match_inner = else_if_match.open_match.str(1); MatchType match_command = match(else_if_match_inner, regex_map_condition); condition_container->children.push_back( std::make_shared(else_if_match.inner(), match_command.type(), parse_expression(match_command.str(1))) ); else_if_match = search_closed_on_level(input, match_delimiter.regex(), regex_map_statement_openers.at(Parsed::Statement::Condition), regex_map_statement_closers.at(Parsed::Statement::Condition), regex_map_condition.at(Parsed::Condition::ElseIf), condition_match); } MatchClosed else_match = search_closed_on_level(input, match_delimiter.regex(), regex_map_statement_openers.at(Parsed::Statement::Condition), regex_map_statement_closers.at(Parsed::Statement::Condition), regex_map_condition.at(Parsed::Condition::Else), condition_match); if (else_match.found()) { condition_match = else_match.close_match; const std::string else_match_inner = else_match.open_match.str(1); MatchType match_command = match(else_match_inner, regex_map_condition); condition_container->children.push_back( std::make_shared(else_match.inner(), match_command.type(), parse_expression(match_command.str(1))) ); } MatchClosed last_if_match = search_closed(input, match_delimiter.regex(), regex_map_statement_openers.at(Parsed::Statement::Condition), regex_map_statement_closers.at(Parsed::Statement::Condition), condition_match); const std::string last_if_match_inner = last_if_match.open_match.str(1); MatchType match_command = match(last_if_match_inner, regex_map_condition); if (match_command.type() == Parsed::Condition::Else) { condition_container->children.push_back( std::make_shared(last_if_match.inner(), match_command.type()) ); } else { condition_container->children.push_back( std::make_shared(last_if_match.inner(), match_command.type(), parse_expression(match_command.str(1))) ); } current_position = last_if_match.end_position(); result.emplace_back(condition_container); break; } case Parsed::Statement::Include: { std::string included_filename = path + match_statement.str(1); Template included_template = parse_template(included_filename); for (auto element : included_template.parsed_template.children) { result.emplace_back(element); } break; } } break; } case Parsed::Delimiter::Expression: { result.emplace_back( std::make_shared(parse_expression(delimiter_inner)) ); break; } case Parsed::Delimiter::Comment: { result.emplace_back( std::make_shared(delimiter_inner) ); break; } } match_delimiter = search(input, regex_map_delimiters, current_position); } if (current_position < input.length()) { result.emplace_back( std::make_shared(input.substr(current_position)) ); } return result; } std::shared_ptr parse_tree(std::shared_ptr current_element, const std::string& path) { if (not current_element->inner.empty()) { current_element->children = parse_level(current_element->inner, path); current_element->inner.clear(); } if (not current_element->children.empty()) { for (auto& child: current_element->children) { child = parse_tree(child, path); } } return current_element; } Template parse(const std::string& input) { auto parsed = parse_tree(std::make_shared(Parsed::Element(Parsed::Type::Main, input)), "./"); return Template(*parsed); } Template parse_template(const std::string& filename) { std::string input = load_file(filename); std::string path = filename.substr(0, filename.find_last_of("/\\") + 1); auto parsed = parse_tree(std::make_shared(Parsed::Element(Parsed::Type::Main, input)), path); return Template(*parsed); } std::string load_file(const std::string& filename) { std::ifstream file(filename); std::string text((std::istreambuf_iterator(file)), std::istreambuf_iterator()); return text; } }; /*! @brief Environment class */ class Environment { const std::string input_path; const std::string output_path; ElementNotation element_notation = ElementNotation::Pointer; Parser parser = Parser(); public: Renderer renderer = Renderer(); Environment(): Environment("./") { } explicit Environment(const std::string& global_path): input_path(global_path), output_path(global_path), parser() { } explicit Environment(const std::string& input_path, const std::string& output_path): input_path(input_path), output_path(output_path), parser() { } void set_statement(const std::string& open, const std::string& close) { parser.regex_map_delimiters[Parsed::Delimiter::Statement] = Regex{open + "\\s*(.+?)\\s*" + close}; } void set_line_statement(const std::string& open) { parser.regex_map_delimiters[Parsed::Delimiter::LineStatement] = Regex{"(?:^|\\n)" + open + "\\s*(.+)\\s*"}; } void set_expression(const std::string& open, const std::string& close) { parser.regex_map_delimiters[Parsed::Delimiter::Expression] = Regex{open + "\\s*(.+?)\\s*" + close}; } void set_comment(const std::string& open, const std::string& close) { parser.regex_map_delimiters[Parsed::Delimiter::Comment] = Regex{open + "\\s*(.+?)\\s*" + close}; } void set_element_notation(const ElementNotation element_notation_) { element_notation = element_notation_; } Template parse(const std::string& input) { Template parsed = parser.parse(input); return parsed; } Template parse_template(const std::string& filename) { Template parsed = parser.parse_template(input_path + filename); return parsed; } std::string render(const std::string& input, json data) { const std::string text = input; renderer.element_notation = element_notation; return renderer.render(parse(text), data); } std::string render_template(const Template temp, json data) { renderer.element_notation = element_notation; return renderer.render(temp, data); } std::string render_file(const std::string& filename, json data) { renderer.element_notation = element_notation; return renderer.render(parse_template(filename), data); } std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) { json data = load_json(filename_data); return render_file(filename, data); } void write(const std::string& filename, json data, const std::string& filename_out) { std::ofstream file(output_path + filename_out); file << render_file(filename, data); file.close(); } void write(const std::string& filename, const std::string& filename_data, const std::string& filename_out) { json data = load_json(filename_data); write(filename, data, filename_out); } std::string load_global_file(const std::string& filename) { return parser.load_file(input_path + filename); } json load_json(const std::string& filename) { std::ifstream file(input_path + filename); json j; file >> j; return j; } void add_callback(std::string name, int number_arguments, std::function callback) { parser.regex_map_callbacks[name] = Parser::function_regex(name, number_arguments); renderer.map_callbacks[name] = callback; } template T get_argument(Parsed::Arguments args, int index, json data) { return renderer.eval_expression(args[index], data); } }; /*! @brief render with default settings */ inline std::string render(const std::string& input, json data) { return Environment().render(input, data); } } // namespace inja #endif // PANTOR_INJA_HPP