templates, split parser and renderer, c++ class parser

This commit is contained in:
pantor
2017-11-21 21:19:52 +01:00
parent 27140612b3
commit 2c96b3bdf5
7 changed files with 370 additions and 398 deletions
+2 -2
View File
@@ -6,7 +6,7 @@
using json = nlohmann::json;
TEST_CASE("Files handling") {
TEST_CASE("loading") {
inja::Environment env = inja::Environment();
json data;
data["name"] = "Jeff";
@@ -24,7 +24,7 @@ TEST_CASE("Files handling") {
}
}
TEST_CASE("Complete files") {
TEST_CASE("complete-files") {
inja::Environment env = inja::Environment("data/");
for (std::string test_name : {"simple-file", "nested", "nested-line"}) {
-10
View File
@@ -1,10 +0,0 @@
#include "catch.hpp"
#include "nlohmann/json.hpp"
#include "inja.hpp"
TEST_CASE("Dot to pointer notation") {
CHECK( inja::dot_to_json_pointer_notation("person.names.surname") == "/person/names/surname" );
CHECK( inja::dot_to_json_pointer_notation("guests.2") == "/guests/2" );
}
-131
View File
@@ -1,131 +0,0 @@
#include "catch.hpp"
#include "nlohmann/json.hpp"
#include "inja.hpp"
using json = nlohmann::json;
using Type = inja::Parsed::Type;
/* TEST_CASE("Parse structure") {
inja::Parser parser = inja::Parser();
SECTION("Basic string") {
std::string test = "lorem ipsum";
json result = {{{"type", Type::String}, {"text", "lorem ipsum"}}};
CHECK( parser.parse(test) == result );
}
SECTION("Empty string") {
std::string test = "";
json result = {};
CHECK( parser.parse(test) == result );
}
SECTION("Variable") {
std::string test = "{{ name }}";
json result = {{{"type", Type::Expression}, {"command", "name"}}};
CHECK( parser.parse(test) == result );
}
SECTION("Combined string and variables") {
std::string test = "Hello {{ name }}!";
json result = {
{{"type", Type::String}, {"text", "Hello "}},
{{"type", Type::Expression}, {"command", "name"}},
{{"type", Type::String}, {"text", "!"}}
};
CHECK( parser.parse(test) == result );
}
SECTION("Multiple variables") {
std::string test = "Hello {{ name }}! I come from {{ city }}.";
json result = {
{{"type", Type::String}, {"text", "Hello "}},
{{"type", Type::Expression}, {"command", "name"}},
{{"type", Type::String}, {"text", "! I come from "}},
{{"type", Type::Expression}, {"command", "city"}},
{{"type", Type::String}, {"text", "."}}
};
CHECK( parser.parse(test) == result );
}
SECTION("Loops") {
std::string test = "open {% for e in list %}lorem{% endfor %} closing";
json result = {
{{"type", Type::String}, {"text", "open "}},
{{"type", Type::Loop}, {"item", "e"}, {"list", "list"}, {"children", {
{{"type", Type::String}, {"text", "lorem"}}
}}},
{{"type", Type::String}, {"text", " closing"}}
};
CHECK( parser.parse(test) == result );
}
SECTION("Nested loops") {
std::string test = "{% for e in list %}{% for b in list2 %}lorem{% endfor %}{% endfor %}";
json result = {
{{"type", Type::Loop}, {"item", "e"}, {"list", "list"}, {"children", {
{{"type", Type::Loop}, {"item", "b"}, {"list", "list2"}, {"children", {
{{"type", Type::String}, {"text", "lorem"}}
}}}
}}}
};
CHECK( parser.parse(test) == result );
}
SECTION("Basic conditional") {
std::string test = "{% if true %}Hello{% endif %}";
json result = {
{{"type", Type::Condition}, {"children", {
{{"type", Type::ConditionBranch}, {"command", "if true"}, {"children", {
{{"type", Type::String}, {"text", "Hello"}}
}}}
}}}
};
CHECK( parser.parse(test) == result );
}
SECTION("If/else if/else conditional") {
std::string test = "if: {% if maybe %}first if{% else if perhaps %}first else if{% else if sometimes %}second else if{% else %}test else{% endif %}";
json result = {
{{"type", Type::String}, {"text", "if: "}},
{{"type", Type::Condition}, {"children", {
{{"type", Type::ConditionBranch}, {"command", "if maybe"}, {"children", {
{{"type", Type::String}, {"text", "first if"}}
}}},
{{"type", Type::ConditionBranch}, {"command", "else if perhaps"}, {"children", {
{{"type", Type::String}, {"text", "first else if"}}
}}},
{{"type", Type::ConditionBranch}, {"command", "else if sometimes"}, {"children", {
{{"type", Type::String}, {"text", "second else if"}}
}}},
{{"type", Type::ConditionBranch}, {"command", "else"}, {"children", {
{{"type", Type::String}, {"text", "test else"}}
}}},
}}}
};
CHECK( parser.parse(test) == result );
}
SECTION("Comments") {
std::string test = "{# lorem ipsum #}";
json result = {{{"type", Type::Comment}, {"text", "lorem ipsum"}}};
CHECK( parser.parse(test) == result );
}
SECTION("Line Statements") {
std::string test = R"(## if true
lorem ipsum
## endif)";
json result = {
{{"type", Type::Condition}, {"children", {
{{"type", Type::ConditionBranch}, {"command", "if true"}, {"children", {
{{"type", Type::String}, {"text", "lorem ipsum"}}
}}}
}}}
};
CHECK( parser.parse(test) == result );
}
} */
+81 -20
View File
@@ -3,10 +3,11 @@
#include "inja.hpp"
using json = nlohmann::json;
TEST_CASE("Renderer") {
TEST_CASE("types") {
inja::Environment env = inja::Environment();
json data;
data["name"] = "Peter";
@@ -18,12 +19,12 @@ TEST_CASE("Renderer") {
data["brother"]["daughter0"] = { { "name", "Maria" } };
data["is_happy"] = true;
SECTION("Basic") {
CHECK( env.render("Hello World!", data) == "Hello World!" );
SECTION("basic") {
CHECK( env.render("", data) == "" );
CHECK( env.render("Hello World!", data) == "Hello World!" );
}
SECTION("Variables") {
SECTION("variables") {
CHECK( env.render("Hello {{ name }}!", data) == "Hello Peter!" );
CHECK( env.render("{{ name }}", data) == "Peter" );
CHECK( env.render("{{name}}", data) == "Peter" );
@@ -32,19 +33,22 @@ TEST_CASE("Renderer") {
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!" );
CHECK_THROWS_WITH( env.render("{{unknown}}", data), "Did not found json element: unknown" );
}
SECTION("Comments") {
SECTION("comments") {
CHECK( env.render("Hello{# This is a comment #}!", data) == "Hello!" );
CHECK( env.render("{# --- #Todo --- #}", data) == "" );
}
SECTION("Loops") {
SECTION("loops") {
CHECK( env.render("{% for name in names %}a{% endfor %}", data) == "aa" );
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") {
SECTION("conditionals") {
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..." );
@@ -54,10 +58,12 @@ TEST_CASE("Renderer") {
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" );
CHECK( env.render("{% if age == 28 %}28{% else if age == 29 %}29{% endif %}", data) == "29" );
CHECK( env.render("{% if age == 26 %}26{% else if age == 27 %}27{% else if age == 28 %}28{% else %}29{% endif %}", data) == "29" );
}
}
TEST_CASE("Render functions") {
TEST_CASE("functions") {
inja::Environment env = inja::Environment();
json data;
@@ -66,60 +72,115 @@ TEST_CASE("Render functions") {
data["names"] = {"Jeff", "Seb", "Peter", "Tom"};
data["temperature"] = 25.6789;
SECTION("Upper") {
SECTION("upper") {
CHECK( env.render("{{ upper(name) }}", data) == "PETER" );
CHECK( env.render("{{ upper( name ) }}", data) == "PETER" );
CHECK( env.render("{{ upper(city) }}", data) == "NEW YORK" );
CHECK( env.render("{{ upper(upper(name)) }}", data) == "PETER" );
CHECK_THROWS_WITH( env.render("{{ upper(5) }}", data), "[json.exception.type_error.302] type must be string, but is number" );
CHECK_THROWS_WITH( env.render("{{ upper(true) }}", data), "[json.exception.type_error.302] type must be string, but is boolean" );
}
SECTION("Lower") {
SECTION("lower") {
CHECK( env.render("{{ lower(name) }}", data) == "peter" );
CHECK( env.render("{{ lower(city) }}", data) == "new york" );
CHECK_THROWS_WITH( env.render("{{ lower(5.45) }}", data), "[json.exception.type_error.302] type must be string, but is number" );
}
SECTION("Range") {
// CHECK( env.render("range(4)", data) == std::vector<int>({0, 1, 2, 3}) );
SECTION("range") {
CHECK( env.render("{{ range(2) }}", data) == "[0,1]" );
CHECK( env.render("{{ range(4) }}", data) == "[0,1,2,3]" );
CHECK_THROWS_WITH( env.render("{{ range(name) }}", data), "[json.exception.type_error.302] type must be number, but is string" );
}
SECTION("Length") {
SECTION("length") {
CHECK( env.render("{{ length(names) }}", data) == "4" );
CHECK_THROWS_WITH( env.render("{{ length(5) }}", data), "[json.exception.type_error.302] type must be array, but is number" );
}
SECTION("Round") {
SECTION("round") {
CHECK( env.render("{{ round(4, 0) }}", data) == "4.0" );
CHECK( env.render("{{ round(temperature, 2) }}", data) == "25.68" );
CHECK_THROWS_WITH( env.render("{{ round(name, 2) }}", data), "[json.exception.type_error.302] type must be number, but is string" );
}
SECTION("DivisibleBy") {
SECTION("divisibleBy") {
CHECK( env.render("{{ divisibleBy(50, 5) }}", data) == "true" );
CHECK( env.render("{{ divisibleBy(12, 3) }}", data) == "true" );
CHECK( env.render("{{ divisibleBy(11, 3) }}", data) == "false" );
CHECK_THROWS_WITH( env.render("{{ divisibleBy(name, 2) }}", data), "[json.exception.type_error.302] type must be number, but is string" );
}
SECTION("Odd") {
SECTION("odd") {
CHECK( env.render("{{ odd(11) }}", data) == "true" );
CHECK( env.render("{{ odd(12) }}", data) == "false" );
CHECK_THROWS_WITH( env.render("{{ odd(name) }}", data), "[json.exception.type_error.302] type must be number, but is string" );
}
SECTION("Even") {
SECTION("even") {
CHECK( env.render("{{ even(11) }}", data) == "false" );
CHECK( env.render("{{ even(12) }}", data) == "true" );
CHECK_THROWS_WITH( env.render("{{ even(name) }}", data), "[json.exception.type_error.302] type must be number, but is string" );
}
}
TEST_CASE("Renderer other syntax") {
TEST_CASE("combinations") {
inja::Environment env = inja::Environment();
json data;
data["name"] = "Peter";
data["city"] = "Brunswick";
data["age"] = 29;
data["names"] = {"Jeff", "Seb"};
data["brother"]["name"] = "Chris";
data["brother"]["daughters"] = {"Maria", "Helen"};
data["brother"]["daughter0"] = { { "name", "Maria" } };
data["is_happy"] = true;
SECTION("Other expression syntax") {
CHECK( env.render("{% if upper(\"Peter\") == \"PETER\" %}TRUE{% endif %}", data) == "TRUE" );
CHECK( env.render("{% if lower(upper(name)) == \"peter\" %}TRUE{% endif %}", data) == "TRUE" );
CHECK( env.render("{% for i in range(4) %}{{ index1 }}{% endfor %}", data) == "1234" );
}
TEST_CASE("templates") {
inja::Environment env = inja::Environment();
inja::Template temp = env.parse("{% if is_happy %}{{ name }}{% else %}{{ city }}{% endif %}");
json data;
data["name"] = "Peter";
data["city"] = "Brunswick";
data["is_happy"] = true;
CHECK( temp.render(data) == "Peter" );
data["is_happy"] = false;
CHECK( temp.render(data) == "Brunswick" );
}
TEST_CASE("other-syntax") {
json data;
data["name"] = "Peter";
data["city"] = "Brunswick";
data["age"] = 29;
data["names"] = {"Jeff", "Seb"};
data["brother"]["name"] = "Chris";
data["brother"]["daughters"] = {"Maria", "Helen"};
data["brother"]["daughter0"] = { { "name", "Maria" } };
data["is_happy"] = true;
SECTION("variables") {
inja::Environment env = inja::Environment();
env.setElementNotation(inja::ElementNotation::Dot);
CHECK( env.render("{{ name }}", data) == "Peter" );
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!" );
CHECK_THROWS_WITH( env.render("{{unknown}}", data), "Did not found json element: unknown" );
}
SECTION("other expression syntax") {
inja::Environment env = inja::Environment();
CHECK( env.render("Hello {{ name }}!", data) == "Hello Peter!" );
@@ -130,7 +191,7 @@ TEST_CASE("Renderer other syntax") {
CHECK( env.render("Hello (& name &)!", data) == "Hello Peter!" );
}
SECTION("Other comment syntax") {
SECTION("other comment syntax") {
inja::Environment env = inja::Environment();
env.setComment("\\(&", "&\\)");
+14 -9
View File
@@ -4,11 +4,16 @@
TEST_CASE("Basic search in string") {
TEST_CASE("dot to pointer") {
CHECK( inja::dot_to_json_pointer_notation("person.names.surname") == "/person/names/surname" );
CHECK( inja::dot_to_json_pointer_notation("guests.2") == "/guests/2" );
}
TEST_CASE("basic-search") {
std::string input = "lorem ipsum dolor it";
inja::Regex regex("i(.*)m");
SECTION("Basic search from start") {
SECTION("from start") {
inja::Match match = inja::search(input, regex, 0);
CHECK( match.found() == true );
CHECK( match.position() == 6 );
@@ -18,17 +23,17 @@ TEST_CASE("Basic search in string") {
CHECK( match.str(1) == "psu" );
}
SECTION("Basic search from position") {
SECTION("from position") {
inja::Match match = inja::search(input, regex, 8);
CHECK( match.found() == false );
CHECK( match.length() == 0 );
}
}
TEST_CASE("Search in string with multiple possible regexes") {
TEST_CASE("search-with-multiple-possible-regexes") {
std::string input = "lorem ipsum dolor amit estas tronum.";
SECTION("Basic 1") {
SECTION("basic 1") {
std::vector<inja::Regex> regex_patterns = { inja::Regex("tras"), inja::Regex("do(\\w*)or"), inja::Regex("es(\\w*)as"), inja::Regex("ip(\\w*)um") };
inja::Match match = inja::search(input, regex_patterns, 0);
CHECK( match.regex_number() == 3 );
@@ -36,7 +41,7 @@ TEST_CASE("Search in string with multiple possible regexes") {
CHECK( match.str(1) == "s" );
}
SECTION("Basic 2") {
SECTION("basic 2") {
std::vector<inja::Regex> regex_patterns = { inja::Regex("tras"), inja::Regex("ip(\\w*)um"), inja::Regex("do(\\w*)or"), inja::Regex("es(\\w*)as") };
inja::Match match = inja::search(input, regex_patterns, 0);
CHECK( match.regex_number() == 1 );
@@ -45,7 +50,7 @@ TEST_CASE("Search in string with multiple possible regexes") {
}
}
TEST_CASE("Search on level") {
TEST_CASE("search-on-level") {
std::string input = "(% up %)(% up %)Test(% N1 %)(% down %)...(% up %)(% N2 %)(% up %)(% N3 %)(% down %)(% N4 %)(% down %)(% N5 %)(% down %)";
inja::Regex regex_statement("\\(\\% (.*?) \\%\\)");
@@ -53,7 +58,7 @@ TEST_CASE("Search on level") {
inja::Regex regex_level_down("down");
inja::Regex regex_search("N(\\d+)");
SECTION("First instance") {
SECTION("first instance") {
inja::Match open_match = inja::search(input, regex_statement, 0);
CHECK( open_match.position() == 0 );
CHECK( open_match.end_position() == 8 );
@@ -64,7 +69,7 @@ TEST_CASE("Search on level") {
CHECK( match.end_position() == 109 );
}
SECTION("Second instance") {
SECTION("second instance") {
inja::Match open_match = inja::search(input, regex_statement, 4);
CHECK( open_match.position() == 8 );