From df8764f70b225b665f45d3ef41edc533fbd8af6b Mon Sep 17 00:00:00 2001 From: pantor Date: Mon, 14 Aug 2017 15:16:01 +0200 Subject: [PATCH] type enum, clode cleaning, readme tables --- README.md | 41 +++++-- src/inja.hpp | 210 +++++++++++++++++--------------- test/src/unit-files.cpp | 8 +- test/src/unit-parser.cpp | 95 ++++++++------- test/src/unit-renderer.cpp | 48 ++++---- test/src/unit-string-helper.cpp | 80 ++++++------ 6 files changed, 263 insertions(+), 219 deletions(-) diff --git a/README.md b/README.md index b44f77c..a167a0a 100644 --- a/README.md +++ b/README.md @@ -69,19 +69,39 @@ Statements can be written with the `(% ... %)` syntax. The most important statem #### Loops -```c++ -json data; -data["guests"] = { "Jeff", "Pierre", "Tom" }; - -render("""Guests: + + + + + + + + + + + + + +
TemplateJsonResult
+
+Guests:
 (% for guest in guests %)- {{ guest }}
-(% endfor %)""", data);
-/* Guests:
+(% endfor %)
+                
+
+
+{
+    "guests:  ["Jeff", "Pierre", "Tom"];
+}
+  				
+
+
+Guests:
 - Jeff
 - Pierre
 - Tom
-*/
-```
+                
+
In the loop, some special variables are available: - int index @@ -113,3 +133,6 @@ Currently, the following compilers are tested: ## License The class is licensed under the [MIT License](https://raw.githubusercontent.com/pantor/inja/master/LICENSE). + + + diff --git a/src/inja.hpp b/src/inja.hpp index 9c8a33f..32a3fd2 100644 --- a/src/inja.hpp +++ b/src/inja.hpp @@ -1,10 +1,10 @@ -/* - Inja - A Template Engine for Modern C++ -*/ - #ifndef PANTOR_INJA_HPP #define PANTOR_INJA_HPP +#ifndef NLOHMANN_JSON_HPP + static_assert(false, "nlohmann/json not found."); +#endif + #include #include @@ -12,11 +12,6 @@ #include -#ifndef NLOHMANN_JSON_HPP - static_assert(false, "nlohmann/json not found."); -#endif - - namespace inja { using json = nlohmann::json; @@ -33,34 +28,43 @@ inline string join_strings(std::vector vector, string delimiter) { return ss.str(); } - struct SearchMatch { SearchMatch() { } - SearchMatch(std::smatch match, size_t offset): SearchMatch(match, offset, 1, -1) { } - - SearchMatch(std::smatch match, size_t offset, int inner_group, int regex_number): inner_group(inner_group), regex_number(regex_number) { + SearchMatch(std::smatch match, size_t offset): match(match), offset(offset) { position = offset + match.position(); length = match.length(); end_position = position + length; found = !match.empty(); - outer = match[0].str(); - inner = match[inner_group].str(); + outer = match.str(0); + inner = match.str(1); prefix = match.prefix(); suffix = match.suffix(); } + std::smatch match; + size_t offset; + string outer, inner, prefix, suffix; size_t position, end_position; int length; bool found = false; - int regex_number = -1, inner_group = 1; }; -struct SearchClosedMatch { - SearchClosedMatch() { } +struct SearchMatchVector: public SearchMatch { + SearchMatchVector(): SearchMatch() { } - SearchClosedMatch(string input, SearchMatch open_match, SearchMatch close_match): open_match(open_match), close_match(close_match) { + SearchMatchVector(std::smatch match, size_t offset, int inner_group, int regex_number): SearchMatch(match, offset), regex_number(regex_number) { + inner = match.str(inner_group); + } + + int regex_number = -1; +}; + +struct SearchClosedMatch: public SearchMatch { + SearchClosedMatch(): SearchMatch() { } + + SearchClosedMatch(string input, SearchMatch open_match, SearchMatch close_match): SearchMatch(), open_match(open_match), close_match(close_match) { position = open_match.position; length = close_match.end_position - open_match.position; end_position = close_match.end_position; @@ -71,11 +75,6 @@ struct SearchClosedMatch { suffix = close_match.suffix; } - string outer, inner, prefix, suffix; - size_t position, end_position; - int length; - bool found = false; - SearchMatch open_match, close_match; }; @@ -92,17 +91,7 @@ inline SearchMatch search(string input, std::regex regex, size_t position) { return SearchMatch(match, position); } -inline SearchMatch search(string input, std::vector regex_patterns, size_t position) { - auto first = input.cbegin(); - auto last = input.cend(); - - string regex_pattern = "(" + join_strings(regex_patterns, ")|(") + ")"; - std::regex regex(regex_pattern); - - if (position >= input.length()) { - return SearchMatch(); - } - +inline SearchMatchVector search(string input, std::vector regex_patterns, size_t position) { // Vector of id vs groups std::vector regex_mark_counts; for (int i = 0; i < regex_patterns.size(); i++) { @@ -110,7 +99,17 @@ inline SearchMatch search(string input, std::vector regex_patterns, size regex_mark_counts.push_back(i); } } - + + string regex_pattern = "(" + join_strings(regex_patterns, ")|(") + ")"; + std::regex regex(regex_pattern); + + auto first = input.cbegin(); + auto last = input.cend(); + + if (position >= input.length()) { + return SearchMatchVector(); + } + std::smatch match; std::regex_search(first + position, last, match, regex); @@ -124,10 +123,10 @@ inline SearchMatch search(string input, std::vector regex_patterns, size } } - return SearchMatch(match, position, number_inner, number_regex); + return SearchMatchVector(match, position, number_inner, number_regex); } -inline SearchClosedMatch search_on_level(string input, std::regex regex_statement, std::regex regex_level_up, std::regex regex_level_down, std::regex regex_search, SearchMatch open_match) { +inline SearchClosedMatch search_closed_match_on_level(string input, std::regex regex_statement, std::regex regex_level_up, std::regex regex_level_down, std::regex regex_search, SearchMatch open_match) { int level = 0; size_t current_position = open_match.end_position; @@ -145,13 +144,23 @@ inline SearchClosedMatch search_on_level(string input, std::regex regex_statemen return SearchClosedMatch(input, open_match, statement_match); } -inline SearchClosedMatch search_close(string input, std::regex regex_statement, std::regex regex_open, std::regex regex_close, SearchMatch open_match) { - return search_on_level(input, regex_statement, regex_open, regex_close, regex_close, open_match); +inline SearchClosedMatch search_closed_match(string input, std::regex regex_statement, std::regex regex_open, std::regex regex_close, SearchMatch open_match) { + return search_closed_match_on_level(input, regex_statement, regex_open, regex_close, regex_close, open_match); } - - +namespace Parser { + enum Type { + String, + Loop, + Condition, + ConditionBranch, + Include, + Comment, + Variable + }; +} + class Environment { std::regex regex_statement; @@ -168,12 +177,12 @@ public: Environment(string global_path): global_path(global_path) { const string regex_pattern_statement = "\\(\\%\\s*(.+?)\\s*\\%\\)"; - // const string regex_pattern_line_statement = "^## (.*)$"; + const string regex_pattern_line_statement = "^##\\s*(.+)\\s*$"; const string regex_pattern_expression = "\\{\\{\\s*(.+?)\\s*\\}\\}"; const string regex_pattern_comment = "\\{#\\s*(.*?)\\s*#\\}"; regex_statement = std::regex(regex_pattern_statement); - // regex_line_statement = std::regex(regex_pattern_line_statement); + regex_line_statement = std::regex(regex_pattern_line_statement); regex_expression = std::regex(regex_pattern_expression); regex_comment = std::regex(regex_pattern_comment); regex_pattern_delimiters = { regex_pattern_statement, regex_pattern_expression, regex_pattern_comment }; @@ -184,11 +193,11 @@ public: json result; size_t current_position = 0; - SearchMatch statement_match = search(input, regex_pattern_delimiters, current_position); + SearchMatchVector statement_match = search(input, regex_pattern_delimiters, current_position); while (statement_match.found) { current_position = statement_match.end_position; if (!statement_match.prefix.empty()) { - result.push_back({{"type", "string"}, {"text", statement_match.prefix}}); + result += {{"type", Parser::Type::String}, {"text", statement_match.prefix}}; } // Regex matched a statement "(% ... %)" @@ -206,63 +215,63 @@ public: std::smatch inner_statement_match; // Loop if (std::regex_match(statement_match.inner, inner_statement_match, regex_loop_open)) { - SearchClosedMatch loop_match = search_close(input, regex_statement, regex_loop_open, regex_loop_close, statement_match); + SearchClosedMatch loop_match = search_closed_match(input, regex_statement, regex_loop_open, regex_loop_close, statement_match); current_position = loop_match.end_position; - string loop_command = inner_statement_match[0].str(); - result.push_back({{"type", "loop"}, {"command", loop_command}, {"inner", loop_match.inner}}); + string loop_command = inner_statement_match.str(0); + result += {{"type", Parser::Type::Loop}, {"command", loop_command}, {"inner", loop_match.inner}}; } // Include else if (std::regex_match(statement_match.inner, inner_statement_match, regex_include)) { - string include_command = inner_statement_match[0].str(); - string filename = inner_statement_match[1].str(); - result.push_back({{"type", "include"}, {"filename", filename}}); + string include_command = inner_statement_match.str(0); + string filename = inner_statement_match.str(1); + result += {{"type", Parser::Type::Include}, {"filename", filename}}; } // Condition else if (std::regex_match(statement_match.inner, inner_statement_match, regex_condition_open)) { - string if_command = inner_statement_match[0].str(); - json condition_result = {{"type", "condition"}, {"children", json::array()}}; + string if_command = inner_statement_match.str(0); + json condition_result = {{"type", Parser::Type::Condition}, {"children", json::array()}}; SearchMatch condition_match = statement_match; - SearchClosedMatch else_if_match = search_on_level(input, regex_statement, regex_condition_open, regex_condition_close, regex_condition_else_if, condition_match); + SearchClosedMatch else_if_match = search_closed_match_on_level(input, regex_statement, regex_condition_open, regex_condition_close, regex_condition_else_if, condition_match); while (else_if_match.found) { condition_match = else_if_match.close_match; - condition_result["children"].push_back({{"type", "condition_branch"}, {"command", else_if_match.open_match.inner}, {"inner", else_if_match.inner}}); + condition_result["children"] += {{"type", Parser::Type::ConditionBranch}, {"command", else_if_match.open_match.inner}, {"inner", else_if_match.inner}}; - else_if_match = search_on_level(input, regex_statement, regex_condition_open, regex_condition_close, regex_condition_else_if, condition_match); + else_if_match = search_closed_match_on_level(input, regex_statement, regex_condition_open, regex_condition_close, regex_condition_else_if, condition_match); } - SearchClosedMatch else_match = search_on_level(input, regex_statement, regex_condition_open, regex_condition_close, regex_condition_else, condition_match); + SearchClosedMatch else_match = search_closed_match_on_level(input, regex_statement, regex_condition_open, regex_condition_close, regex_condition_else, condition_match); if (else_match.found) { condition_match = else_match.close_match; - condition_result["children"].push_back({{"type", "condition_branch"}, {"command", else_match.open_match.inner}, {"inner", else_match.inner}}); + condition_result["children"] += {{"type", Parser::Type::ConditionBranch}, {"command", else_match.open_match.inner}, {"inner", else_match.inner}}; } - SearchClosedMatch last_if_match = search_close(input, regex_statement, regex_condition_open, regex_condition_close, condition_match); + SearchClosedMatch last_if_match = search_closed_match(input, regex_statement, regex_condition_open, regex_condition_close, condition_match); - condition_result["children"].push_back({{"type", "condition_branch"}, {"command", last_if_match.open_match.inner}, {"inner", last_if_match.inner}}); + condition_result["children"] += {{"type", Parser::Type::ConditionBranch}, {"command", last_if_match.open_match.inner}, {"inner", last_if_match.inner}}; current_position = last_if_match.end_position; - result.push_back(condition_result); + result += condition_result; } } // Regex matched an expression "{{ ... }}" else if (statement_match.regex_number == 1) { - result.push_back({{"type", "variable"}, {"command", statement_match.inner}}); + result += {{"type", Parser::Type::Variable}, {"command", statement_match.inner}}; } // Regex matched an comment "{# ... #}" else if (statement_match.regex_number == 2) { - result.push_back({{"type", "comment"}, {"text", statement_match.inner}}); + result += {{"type", Parser::Type::Comment}, {"text", statement_match.inner}}; } statement_match = search(input, regex_pattern_delimiters, current_position); } if (current_position < input.length()) { - result.push_back({{"type", "string"}, {"text", input.substr(current_position)}}); + result += {{"type", Parser::Type::String}, {"text", input.substr(current_position)}}; } return result; @@ -304,6 +313,7 @@ public: } bool parse_condition(string condition, json data) { + const std::regex regex_condition_not("not (.*)"); const std::regex regex_condition_equal("(.*) == (.*)"); const std::regex regex_condition_greater("(.*) > (.*)"); const std::regex regex_condition_less("(.*) < (.*)"); @@ -314,32 +324,41 @@ public: std::smatch match_condition; if (std::regex_match(condition, match_condition, regex_condition_equal)) { - json comp1 = parse_variable(match_condition[1].str(), data); - json comp2 = parse_variable(match_condition[2].str(), data); + return !parse_condition(match_condition.str(1), data); + } + else if (std::regex_match(condition, match_condition, regex_condition_equal)) { + json comp1 = parse_variable(match_condition.str(1), data); + json comp2 = parse_variable(match_condition.str(2), data); return comp1 == comp2; - } else if (std::regex_match(condition, match_condition, regex_condition_greater)) { - json comp1 = parse_variable(match_condition[1].str(), data); - json comp2 = parse_variable(match_condition[2].str(), data); + } + else if (std::regex_match(condition, match_condition, regex_condition_greater)) { + json comp1 = parse_variable(match_condition.str(1), data); + json comp2 = parse_variable(match_condition.str(2), data); return comp1 > comp2; - } else if (std::regex_match(condition, match_condition, regex_condition_less)) { - json comp1 = parse_variable(match_condition[1].str(), data); - json comp2 = parse_variable(match_condition[2].str(), data); + } + else if (std::regex_match(condition, match_condition, regex_condition_less)) { + json comp1 = parse_variable(match_condition.str(1), data); + json comp2 = parse_variable(match_condition.str(2), data); return comp1 < comp2; - } else if (std::regex_match(condition, match_condition, regex_condition_greater_equal)) { - json comp1 = parse_variable(match_condition[1].str(), data); - json comp2 = parse_variable(match_condition[2].str(), data); + } + else if (std::regex_match(condition, match_condition, regex_condition_greater_equal)) { + json comp1 = parse_variable(match_condition.str(1), data); + json comp2 = parse_variable(match_condition.str(2), data); return comp1 >= comp2; - } else if (std::regex_match(condition, match_condition, regex_condition_less_equal)) { - json comp1 = parse_variable(match_condition[1].str(), data); - json comp2 = parse_variable(match_condition[2].str(), data); + } + else if (std::regex_match(condition, match_condition, regex_condition_less_equal)) { + json comp1 = parse_variable(match_condition.str(1), data); + json comp2 = parse_variable(match_condition.str(2), data); return comp1 <= comp2; - } else if (std::regex_match(condition, match_condition, regex_condition_different)) { - json comp1 = parse_variable(match_condition[1].str(), data); - json comp2 = parse_variable(match_condition[2].str(), data); + } + else if (std::regex_match(condition, match_condition, regex_condition_different)) { + json comp1 = parse_variable(match_condition.str(1), data); + json comp2 = parse_variable(match_condition.str(2), data); return comp1 != comp2; - } else if (std::regex_match(condition, match_condition, regex_condition_in)) { - json item = parse_variable(match_condition[1].str(), data); - json list = parse_variable(match_condition[2].str(), data); + } + else if (std::regex_match(condition, match_condition, regex_condition_in)) { + json item = parse_variable(match_condition.str(1), data); + json list = parse_variable(match_condition.str(2), data); return (std::find(list.begin(), list.end(), item) != list.end()); } @@ -364,24 +383,24 @@ public: string render_tree(json input, json data, string path) { string result = ""; for (auto element: input) { - if (element["type"] == "string") { + if (element["type"] == Parser::Type::String) { result += element["text"]; } - else if (element["type"] == "variable") { + else if (element["type"] == Parser::Type::Variable) { json variable = parse_variable(element["command"], data); result += render_json(variable); } - else if (element["type"] == "include") { + else if (element["type"] == Parser::Type::Include) { result += render_template(path + element["filename"].get(), data); } - else if (element["type"] == "loop") { + else if (element["type"] == Parser::Type::Loop) { const std::regex regex_loop_list("for (\\w+) in (.+)"); string command = element["command"].get(); std::smatch match_command; if (std::regex_match(command, match_command, regex_loop_list)) { - string item_name = match_command[1].str(); - string list_name = match_command[2].str(); + string item_name = match_command.str(1); + string list_name = match_command.str(2); json list = parse_variable(list_name, data); for (int i = 0; i < list.size(); i++) { @@ -394,7 +413,7 @@ public: } } } - else if (element["type"] == "condition") { + else if (element["type"] == Parser::Type::Condition) { const std::regex regex_condition("(if|else if|else) ?(.*)"); json branches = element["children"]; @@ -403,8 +422,8 @@ public: string command = branch["command"].get(); std::smatch match_command; if (std::regex_match(command, match_command, regex_condition)) { - string condition_type = match_command[1].str(); - string condition = match_command[2].str(); + string condition_type = match_command.str(1); + string condition = match_command.str(2); if (parse_condition(condition, data) || condition_type == "else") { result += render_tree(branch["children"], data, path); @@ -453,8 +472,7 @@ public: }; inline string render(string input, json data) { - Environment env = Environment(); - return env.render(input, data); + return Environment().render(input, data); } } // namespace inja diff --git a/test/src/unit-files.cpp b/test/src/unit-files.cpp index d55f1be..f2b8bde 100644 --- a/test/src/unit-files.cpp +++ b/test/src/unit-files.cpp @@ -13,15 +13,15 @@ TEST_CASE("files handling") { data["name"] = "Jeff"; SECTION("files should be loaded") { - REQUIRE( env.load_file("../test/data/simple.txt") == "Hello {{ name }}." ); + CHECK( env.load_file("../test/data/simple.txt") == "Hello {{ name }}." ); } SECTION("files should be rendered") { - REQUIRE( env.render_template("../test/data/simple.txt", data) == "Hello Jeff." ); + CHECK( env.render_template("../test/data/simple.txt", data) == "Hello Jeff." ); } SECTION("file includes should be rendered") { - REQUIRE( env.render_template("../test/data/include.txt", data) == "Answer: Hello Jeff." ); + CHECK( env.render_template("../test/data/include.txt", data) == "Answer: Hello Jeff." ); } } @@ -30,7 +30,7 @@ TEST_CASE("complete files") { for (std::string test_name : {"simple-file", "nested"}) { SECTION(test_name) { - REQUIRE( env.render_template_with_json_file(test_name + "/template.txt", test_name + "/data.json") == env.load_file(test_name + "/result.txt") ); + CHECK( env.render_template_with_json_file(test_name + "/template.txt", test_name + "/data.json") == env.load_file(test_name + "/result.txt") ); } } } \ No newline at end of file diff --git a/test/src/unit-parser.cpp b/test/src/unit-parser.cpp index 7ac6fde..932230f 100644 --- a/test/src/unit-parser.cpp +++ b/test/src/unit-parser.cpp @@ -5,6 +5,7 @@ using Environment = inja::Environment; using json = nlohmann::json; +using Type = inja::Parser::Type; TEST_CASE("parser") { @@ -12,89 +13,89 @@ TEST_CASE("parser") { SECTION("basic") { std::string test = "asdf"; - json result = {{{"type", "string"}, {"text", "asdf"}}}; + json result = {{{"type", Type::String}, {"text", "asdf"}}}; - REQUIRE( env.parse(test) == result ); + CHECK( env.parse(test) == result ); } SECTION("variables") { std::string test = "{{ name }}"; - json result = {{{"type", "variable"}, {"command", "name"}}}; - REQUIRE( env.parse(test) == result ); + json result = {{{"type", Type::Variable}, {"command", "name"}}}; + CHECK( env.parse(test) == result ); std::string test_combined = "Hello {{ name }}!"; json result_combined = { - {{"type", "string"}, {"text", "Hello "}}, - {{"type", "variable"}, {"command", "name"}}, - {{"type", "string"}, {"text", "!"}} + {{"type", Type::String}, {"text", "Hello "}}, + {{"type", Type::Variable}, {"command", "name"}}, + {{"type", Type::String}, {"text", "!"}} }; - REQUIRE( env.parse(test_combined) == result_combined ); + CHECK( env.parse(test_combined) == result_combined ); std::string test_multiple = "Hello {{ name }}! I come from {{ city }}."; json result_multiple = { - {{"type", "string"}, {"text", "Hello "}}, - {{"type", "variable"}, {"command", "name"}}, - {{"type", "string"}, {"text", "! I come from "}}, - {{"type", "variable"}, {"command", "city"}}, - {{"type", "string"}, {"text", "."}} + {{"type", Type::String}, {"text", "Hello "}}, + {{"type", Type::Variable}, {"command", "name"}}, + {{"type", Type::String}, {"text", "! I come from "}}, + {{"type", Type::Variable}, {"command", "city"}}, + {{"type", Type::String}, {"text", "."}} }; - REQUIRE( env.parse(test_multiple) == result_multiple ); + CHECK( env.parse(test_multiple) == result_multiple ); } SECTION("loops") { std::string test = "open (% for e in list %)lorem(% endfor %) closing"; json result = { - {{"type", "string"}, {"text", "open "}}, - {{"type", "loop"}, {"command", "for e in list"}, {"children", { - {{"type", "string"}, {"text", "lorem"}} + {{"type", Type::String}, {"text", "open "}}, + {{"type", Type::Loop}, {"command", "for e in list"}, {"children", { + {{"type", Type::String}, {"text", "lorem"}} }}}, - {{"type", "string"}, {"text", " closing"}} + {{"type", Type::String}, {"text", " closing"}} }; std::string test_nested = "(% for e in list %)(% for b in list2 %)lorem(% endfor %)(% endfor %)"; json result_nested = { - {{"type", "loop"}, {"command", "for e in list"}, {"children", { - {{"type", "loop"}, {"command", "for b in list2"}, {"children", { - {{"type", "string"}, {"text", "lorem"}} + {{"type", Type::Loop}, {"command", "for e in list"}, {"children", { + {{"type", Type::Loop}, {"command", "for b in list2"}, {"children", { + {{"type", Type::String}, {"text", "lorem"}} }}} }}} }; - REQUIRE( env.parse(test) == result ); - REQUIRE( env.parse(test_nested) == result_nested ); + CHECK( env.parse(test) == result ); + CHECK( env.parse(test_nested) == result_nested ); } SECTION("conditionals") { std::string test = "(% if true %)dfgh(% endif %)"; json result = { - {{"type", "condition"}, {"children", { - {{"type", "condition_branch"}, {"command", "if true"}, {"children", { - {{"type", "string"}, {"text", "dfgh"}} + {{"type", Type::Condition}, {"children", { + {{"type", Type::ConditionBranch}, {"command", "if true"}, {"children", { + {{"type", Type::String}, {"text", "dfgh"}} }}} }}} }; std::string test2 = "if: (% if maybe %)first if(% else if perhaps %)first else if(% else if sometimes %)second else if(% else %)test else(% endif %)"; json result2 = { - {{"type", "string"}, {"text", "if: "}}, - {{"type", "condition"}, {"children", { - {{"type", "condition_branch"}, {"command", "if maybe"}, {"children", { - {{"type", "string"}, {"text", "first if"}} + {{"type", Type::String}, {"text", "if: "}}, + {{"type", Type::Condition}, {"children", { + {{"type", Type::ConditionBranch}, {"command", "if maybe"}, {"children", { + {{"type", Type::String}, {"text", "first if"}} }}}, - {{"type", "condition_branch"}, {"command", "else if perhaps"}, {"children", { - {{"type", "string"}, {"text", "first else if"}} + {{"type", Type::ConditionBranch}, {"command", "else if perhaps"}, {"children", { + {{"type", Type::String}, {"text", "first else if"}} }}}, - {{"type", "condition_branch"}, {"command", "else if sometimes"}, {"children", { - {{"type", "string"}, {"text", "second else if"}} + {{"type", Type::ConditionBranch}, {"command", "else if sometimes"}, {"children", { + {{"type", Type::String}, {"text", "second else if"}} }}}, - {{"type", "condition_branch"}, {"command", "else"}, {"children", { - {{"type", "string"}, {"text", "test else"}} + {{"type", Type::ConditionBranch}, {"command", "else"}, {"children", { + {{"type", Type::String}, {"text", "test else"}} }}}, }}} }; - REQUIRE( env.parse(test) == result ); - REQUIRE( env.parse(test2) == result2 ); + CHECK( env.parse(test) == result ); + CHECK( env.parse(test2) == result2 ); } @@ -108,17 +109,17 @@ TEST_CASE("parser") { data["brother"]["daughter0"] = { { "name", "Maria" } }; SECTION("variables from values") { - REQUIRE( env.parse_variable("42", data) == 42 ); - REQUIRE( env.parse_variable("3.1415", data) == 3.1415 ); - REQUIRE( env.parse_variable("\"hello\"", data) == "hello" ); + CHECK( env.parse_variable("42", data) == 42 ); + CHECK( env.parse_variable("3.1415", data) == 3.1415 ); + CHECK( env.parse_variable("\"hello\"", data) == "hello" ); } SECTION("variables from JSON data") { - REQUIRE( env.parse_variable("name", data) == "Peter" ); - REQUIRE( env.parse_variable("age", data) == 29 ); - REQUIRE( env.parse_variable("names/1", data) == "Seb" ); - REQUIRE( env.parse_variable("brother/name", data) == "Chris" ); - REQUIRE( env.parse_variable("brother/daughters/0", data) == "Maria" ); - REQUIRE_THROWS_WITH( env.parse_variable("noelement", data), "JSON pointer found no element." ); + CHECK( env.parse_variable("name", data) == "Peter" ); + CHECK( env.parse_variable("age", data) == 29 ); + CHECK( env.parse_variable("names/1", data) == "Seb" ); + CHECK( env.parse_variable("brother/name", data) == "Chris" ); + CHECK( env.parse_variable("brother/daughters/0", data) == "Maria" ); + CHECK_THROWS_WITH( env.parse_variable("noelement", data), "JSON pointer found no element." ); } } \ No newline at end of file diff --git a/test/src/unit-renderer.cpp b/test/src/unit-renderer.cpp index c38a5f1..8e686bb 100644 --- a/test/src/unit-renderer.cpp +++ b/test/src/unit-renderer.cpp @@ -20,43 +20,43 @@ TEST_CASE("Renderer") { data["is_happy"] = true; SECTION("Basic") { - REQUIRE( env.render("Hello World!", data) == "Hello World!" ); - REQUIRE( env.render("", data, "../") == "" ); + CHECK( env.render("Hello World!", data) == "Hello World!" ); + CHECK( env.render("", data, "../") == "" ); } SECTION("Variables") { - REQUIRE( env.render("Hello {{ name }}!", data) == "Hello Peter!" ); - REQUIRE( env.render("{{ name }}", data) == "Peter" ); - REQUIRE( env.render("{{name}}", data) == "Peter" ); - REQUIRE( env.render("{{ name }} is {{ age }} years old.", data) == "Peter is 29 years old." ); - REQUIRE( env.render("Hello {{ name }}! I come from {{ city }}.", data) == "Hello Peter! I come from Brunswick." ); - REQUIRE( env.render("Hello {{ names/1 }}!", data) == "Hello Seb!" ); - REQUIRE( env.render("Hello {{ brother/name }}!", data) == "Hello Chris!" ); - REQUIRE( env.render("Hello {{ brother/daughter0/name }}!", data) == "Hello Maria!" ); + CHECK( env.render("Hello {{ name }}!", data) == "Hello Peter!" ); + CHECK( env.render("{{ name }}", data) == "Peter" ); + CHECK( env.render("{{name}}", data) == "Peter" ); + CHECK( env.render("{{ name }} is {{ age }} years old.", data) == "Peter is 29 years old." ); + CHECK( env.render("Hello {{ name }}! I come from {{ city }}.", data) == "Hello Peter! I come from Brunswick." ); + CHECK( env.render("Hello {{ names/1 }}!", data) == "Hello Seb!" ); + CHECK( env.render("Hello {{ brother/name }}!", data) == "Hello Chris!" ); + CHECK( env.render("Hello {{ brother/daughter0/name }}!", data) == "Hello Maria!" ); } SECTION("Comments") { - REQUIRE( env.render("Hello{# This is a comment #}!", data) == "Hello!" ); - REQUIRE( env.render("{# --- #Todo --- #}", data) == "" ); + CHECK( env.render("Hello{# This is a comment #}!", data) == "Hello!" ); + CHECK( env.render("{# --- #Todo --- #}", data) == "" ); } SECTION("Loops") { - REQUIRE( env.render("Hello (% for name in names %){{ name }} (% endfor %)!", data) == "Hello Jeff Seb !" ); - REQUIRE( env.render("Hello (% for name in names %){{ index }}: {{ name }}, (% endfor %)!", data) == "Hello 0: Jeff, 1: Seb, !" ); + CHECK( env.render("Hello (% for name in names %){{ name }} (% endfor %)!", data) == "Hello Jeff Seb !" ); + CHECK( env.render("Hello (% for name in names %){{ index }}: {{ name }}, (% endfor %)!", data) == "Hello 0: Jeff, 1: Seb, !" ); } SECTION("Conditionals") { - REQUIRE( env.render("(% if is_happy %)Yeah!(% endif %)", data) == "Yeah!" ); - REQUIRE( env.render("(% if is_sad %)Yeah!(% endif %)", data) == "" ); - REQUIRE( env.render("(% if is_sad %)Yeah!(% else %)Nooo...(% endif %)", data) == "Nooo..." ); - REQUIRE( env.render("(% if age == 29 %)Right(% else %)Wrong(% endif %)", data) == "Right" ); - REQUIRE( env.render("(% if age > 29 %)Right(% else %)Wrong(% endif %)", data) == "Wrong" ); - REQUIRE( env.render("(% if age <= 29 %)Right(% else %)Wrong(% endif %)", data) == "Right" ); - REQUIRE( env.render("(% if age != 28 %)Right(% else %)Wrong(% endif %)", data) == "Right" ); - REQUIRE( env.render("(% if age >= 30 %)Right(% else %)Wrong(% endif %)", data) == "Wrong" ); - REQUIRE( env.render("(% if age in [28, 29, 30] %)True(% endif %)", data) == "True" ); + CHECK( env.render("(% if is_happy %)Yeah!(% endif %)", data) == "Yeah!" ); + CHECK( env.render("(% if is_sad %)Yeah!(% endif %)", data) == "" ); + CHECK( env.render("(% if is_sad %)Yeah!(% else %)Nooo...(% endif %)", data) == "Nooo..." ); + CHECK( env.render("(% if age == 29 %)Right(% else %)Wrong(% endif %)", data) == "Right" ); + CHECK( env.render("(% if age > 29 %)Right(% else %)Wrong(% endif %)", data) == "Wrong" ); + CHECK( env.render("(% if age <= 29 %)Right(% else %)Wrong(% endif %)", data) == "Right" ); + CHECK( env.render("(% if age != 28 %)Right(% else %)Wrong(% endif %)", data) == "Right" ); + CHECK( env.render("(% if age >= 30 %)Right(% else %)Wrong(% endif %)", data) == "Wrong" ); + CHECK( env.render("(% if age in [28, 29, 30] %)True(% endif %)", data) == "True" ); // Only works with gcc-5 - // REQUIRE( env.render("(% if name in [\"Simon\", \"Tom\"] %)Test1(% else if name in [\"Peter\"] %)Test2(% else %)Test3(% endif %)", data) == "Test2" ); + // CHECK( env.render("(% if name in [\"Simon\", \"Tom\"] %)Test1(% else if name in [\"Peter\"] %)Test2(% else %)Test3(% endif %)", data) == "Test2" ); } } \ No newline at end of file diff --git a/test/src/unit-string-helper.cpp b/test/src/unit-string-helper.cpp index 84137ed..f054d15 100644 --- a/test/src/unit-string-helper.cpp +++ b/test/src/unit-string-helper.cpp @@ -8,10 +8,10 @@ using json = nlohmann::json; TEST_CASE("string vector join function") { - REQUIRE( inja::join_strings({"1", "2", "3"}, ",") == "1,2,3" ); - REQUIRE( inja::join_strings({"1", "2", "3", "4", "5"}, " ") == "1 2 3 4 5" ); - REQUIRE( inja::join_strings({}, " ") == "" ); - REQUIRE( inja::join_strings({"single"}, "---") == "single" ); + CHECK( inja::join_strings({"1", "2", "3"}, ",") == "1,2,3" ); + CHECK( inja::join_strings({"1", "2", "3", "4", "5"}, " ") == "1 2 3 4 5" ); + CHECK( inja::join_strings({}, " ") == "" ); + CHECK( inja::join_strings({"single"}, "---") == "single" ); } TEST_CASE("basic search in string") { @@ -20,18 +20,18 @@ TEST_CASE("basic search in string") { SECTION("basic search from start") { inja::SearchMatch match = inja::search(input, regex, 0); - REQUIRE( match.found == true ); - REQUIRE( match.position == 6 ); - REQUIRE( match.length == 5 ); - REQUIRE( match.end_position == 11 ); - REQUIRE( match.outer == "ipsum" ); - REQUIRE( match.inner == "psu" ); + CHECK( match.found == true ); + CHECK( match.position == 6 ); + CHECK( match.length == 5 ); + CHECK( match.end_position == 11 ); + CHECK( match.outer == "ipsum" ); + CHECK( match.inner == "psu" ); } SECTION("basic search from position") { inja::SearchMatch match = inja::search(input, regex, 8); - REQUIRE( match.found == false ); - REQUIRE( match.length == 0 ); + CHECK( match.found == false ); + CHECK( match.length == 0 ); } } @@ -40,54 +40,56 @@ TEST_CASE("search in string with multiple possible regexes") { SECTION("basic 1") { std::vector regex_patterns = { "tras", "do(\\w*)or", "es(\\w*)as", "ip(\\w*)um" }; - inja::SearchMatch match = inja::search(input, regex_patterns, 0); - REQUIRE( match.regex_number == 3 ); - REQUIRE( match.outer == "ipsum" ); - REQUIRE( match.inner == "s" ); + inja::SearchMatchVector match = inja::search(input, regex_patterns, 0); + CHECK( match.regex_number == 3 ); + CHECK( match.outer == "ipsum" ); + CHECK( match.inner == "s" ); } SECTION("basic 2") { std::vector regex_patterns = { "tras", "ip(\\w*)um", "do(\\w*)or", "es(\\w*)as" }; - inja::SearchMatch match = inja::search(input, regex_patterns, 0); - REQUIRE( match.regex_number == 1 ); - REQUIRE( match.outer == "ipsum" ); - REQUIRE( match.inner == "s" ); + inja::SearchMatchVector match = inja::search(input, regex_patterns, 0); + CHECK( match.regex_number == 1 ); + CHECK( match.outer == "ipsum" ); + CHECK( match.inner == "s" ); } } TEST_CASE("search on level") { - std::string input = "(% up %)(% up %)(% N1 %)(% down %)...(% up %)(% N2 %)(% up %)(% N3 %)(% down %)(% N4 %)(% down %)(% N5 %)(% down %)"; + std::string input = "(% up %)(% up %)Test(% N1 %)(% down %)...(% up %)(% N2 %)(% up %)(% N3 %)(% down %)(% N4 %)(% down %)(% N5 %)(% down %)"; std::regex regex_statement("\\(\\% (.*?) \\%\\)"); std::regex regex_level_up("up"); std::regex regex_level_down("down"); std::regex regex_search("N(\\d+)"); - SECTION("basic 1") { + SECTION("first instance") { inja::SearchMatch open_match = inja::search(input, regex_statement, 0); + CHECK( open_match.position == 0 ); + CHECK( open_match.end_position == 8 ); + CHECK( open_match.inner == "up" ); - REQUIRE( open_match.position == 0 ); - REQUIRE( open_match.end_position == 8 ); - REQUIRE( open_match.inner == "up" ); - - inja::SearchClosedMatch match = inja::search_on_level(input, regex_statement, regex_level_up, regex_level_down, regex_search, open_match); - - REQUIRE( match.position == 0 ); - REQUIRE( match.end_position == 105 ); + inja::SearchClosedMatch match = inja::search_closed_match_on_level(input, regex_statement, regex_level_up, regex_level_down, regex_search, open_match); + CHECK( match.position == 0 ); + CHECK( match.end_position == 109 ); } - SECTION("basic 1") { + SECTION("second instance") { inja::SearchMatch open_match = inja::search(input, regex_statement, 4); - REQUIRE( open_match.position == 8 ); - REQUIRE( open_match.end_position == 16 ); - REQUIRE( open_match.inner == "up" ); + CHECK( open_match.position == 8 ); + CHECK( open_match.end_position == 16 ); + CHECK( open_match.inner == "up" ); - inja::SearchClosedMatch match = inja::search_on_level(input, regex_statement, regex_level_up, regex_level_down, regex_search, open_match); + inja::SearchClosedMatch match = inja::search_closed_match_on_level(input, regex_statement, regex_level_up, regex_level_down, regex_search, open_match); - REQUIRE( match.position == 8 ); - // REQUIRE( match.end_position == 24 ); - // REQUIRE( match.outer == "(% up %)(% N1 %)(% down %)" ); - // REQUIRE( match.inner == "(% N1 %)" ); + CHECK( match.open_match.position == 8 ); + CHECK( match.open_match.end_position== 16 ); + CHECK( match.close_match.position == 20 ); + CHECK( match.close_match.end_position == 28 ); + CHECK( match.position == 8 ); + CHECK( match.end_position == 28 ); + CHECK( match.outer == "(% up %)Test(% N1 %)" ); + CHECK( match.inner == "Test" ); } } \ No newline at end of file