more tests, more travis jobs, more readme

This commit is contained in:
pantor
2017-08-14 16:12:30 +02:00
parent 91c2bb46e9
commit 27ce2ffcc5
4 changed files with 127 additions and 57 deletions
+3 -3
View File
@@ -60,9 +60,9 @@ matrix:
- compiler: clang
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7']
packages: clang-3.7
env: COMPILER=clang++-3.7
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8']
packages: clang-3.8
env: COMPILER=clang++-3.8
script:
+34 -9
View File
@@ -23,6 +23,10 @@ Inja is headers only. Just one dependency: json by nlohmann.
```c++
#include "json.hpp"
#include "inja.hpp"
// For convenience
using namespace inja;
using json = nlohmann::json;
```
@@ -34,10 +38,10 @@ Inja is headers only. Just one dependency: json by nlohmann.
json data;
data["name"] = "world";
inja::render("Hello {{ name }}!", data); // "Hello World!"
render("Hello {{ name }}!", data); // "Hello World!"
// For more advanced usage, an environment is recommended
inja::Environment env = inja::Environment();
Environment env = Environment();
// Render a string with json data
std::string result = env.render("Hello {{ name }}!", data);
@@ -52,16 +56,32 @@ std::string result_template_2 = env.render_temlate_with_json_file("template.txt"
The environment class can be configured.
```c++
// With default settings
inja::Environment env_default = inja::Environment();
Environment env_default = Environment();
// With global path to template files
inja::Environment env_default = inja::Environment("../path/templates/");
Environment env_default = Environment("../path/templates/");
```
### Variables
Variables can be rendered with the `{{ ... }}` syntax.
```c++
json data;
data["name"] = "world";
data["guests"] = {"Jeff", "Pierre", "Tom"};
data["time"]["start"]["hour"] = 16;
data["time"]["end"]["hour"] = 21;
string template = """
{{ guests/0 }}
{{ time/start/hour }} to {{ time/end/hour }} or {{ 24 }}
""";
```
Valid Json -> Printed. Json Pointer.
### Statements
@@ -106,12 +126,17 @@ Guests:
</table>
In the loop, some special variables are available:
- int index
- bool is_first
- bool is_last
- `int index, index1`
- `bool is_first`
- `bool is_last`
#### Conditions
If, else if, else. Nested. conditions:
- `not`
- `==`, `>`, `<`, `>=`, `<=`, `!=`
- `in`
#### Includes
Include other files like `(% include "footer.html" %)`. Relative from file.
@@ -121,7 +146,7 @@ Include other files like `(% include "footer.html" %)`. Relative from file.
Comments can be rendered with the `{# ... #}` syntax.
```c++
inja::render("Hello{# Todo #}!", data); // "Hello!"
render("Hello{# Todo #}!", data); // "Hello!"
```
## Supported compilers
@@ -129,7 +154,7 @@ inja::render("Hello{# Todo #}!", data); // "Hello!"
Currently, the following compilers are tested:
- GCC 4.9 - 7.1 (and possibly later)
- Clang 3.6 - 3.7 (and possibly later)
- Clang 3.6 - 3.8 (and possibly later)
## License
+56 -44
View File
@@ -100,6 +100,7 @@ inline SearchMatchVector search(string input, std::vector<string> regex_patterns
}
}
// TODO use search(...)
string regex_pattern = "(" + join_strings(regex_patterns, ")|(") + ")";
std::regex regex(regex_pattern);
@@ -383,56 +384,67 @@ public:
string render_tree(json input, json data, string path) {
string result = "";
for (auto element: input) {
if (element["type"] == Parser::Type::String) {
result += element["text"];
}
else if (element["type"] == Parser::Type::Variable) {
json variable = parse_variable(element["command"], data);
result += render_json(variable);
}
else if (element["type"] == Parser::Type::Include) {
result += render_template(path + element["filename"].get<string>(), data);
}
else if (element["type"] == Parser::Type::Loop) {
const std::regex regex_loop_list("for (\\w+) in (.+)");
string command = element["command"].get<string>();
std::smatch match_command;
if (std::regex_match(command, match_command, regex_loop_list)) {
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++) {
json data_loop = data;
data_loop[item_name] = 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_tree(element["children"], data_loop, path);
}
switch ( (Parser::Type) element["type"] ) {
case Parser::Type::String: {
result += element["text"];
break;
}
}
else if (element["type"] == Parser::Type::Condition) {
const std::regex regex_condition("(if|else if|else) ?(.*)");
json branches = element["children"];
for (auto branch: branches) {
case Parser::Type::Variable: {
json variable = parse_variable(element["command"], data);
result += render_json(variable);
break;
}
case Parser::Type::Include: {
result += render_template(path + element["filename"].get<string>(), data);
break;
}
case Parser::Type::Loop: {
const std::regex regex_loop_list("for (\\w+) in (.+)");
string command = branch["command"].get<string>();
string command = element["command"].get<string>();
std::smatch match_command;
if (std::regex_match(command, match_command, regex_condition)) {
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);
break;
if (std::regex_match(command, match_command, regex_loop_list)) {
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++) {
json data_loop = data;
data_loop[item_name] = 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_tree(element["children"], data_loop, path);
}
}
break;
}
}
case Parser::Type::Condition: {
const std::regex regex_condition("(if|else if|else) ?(.*)");
for (auto branch: element["children"]) {
string command = branch["command"].get<string>();
std::smatch match_command;
if (std::regex_match(command, match_command, regex_condition)) {
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);
break;
}
}
}
break;
}
case Parser::Type::Comment: {
break;
}
default: {
throw std::runtime_error("Unknown type in renderer.");
}
}
}
return result;
}
+34 -1
View File
@@ -8,7 +8,7 @@ using json = nlohmann::json;
using Type = inja::Parser::Type;
TEST_CASE("parser") {
TEST_CASE("parse structure") {
Environment env = Environment();
SECTION("basic") {
@@ -97,7 +97,10 @@ TEST_CASE("parser") {
CHECK( env.parse(test) == result );
CHECK( env.parse(test2) == result2 );
}
}
TEST_CASE("parse json") {
Environment env = Environment();
json data;
data["name"] = "Peter";
@@ -122,4 +125,34 @@ TEST_CASE("parser") {
CHECK( env.parse_variable("brother/daughters/0", data) == "Maria" );
CHECK_THROWS_WITH( env.parse_variable("noelement", data), "JSON pointer found no element." );
}
}
TEST_CASE("parse conditions") {
Environment env = Environment();
json data;
data["age"] = 29;
data["brother"] = "Peter";
data["father"] = "Peter";
SECTION("elements") {
// CHECK( env.parse_condition("age", data) );
CHECK_FALSE( env.parse_condition("size", data) );
}
SECTION("numbers") {
CHECK( env.parse_condition("age == 29", data) );
CHECK( env.parse_condition("age >= 29", data) );
CHECK( env.parse_condition("age <= 29", data) );
CHECK( env.parse_condition("age < 100", data) );
CHECK_FALSE( env.parse_condition("age > 29", data) );
CHECK_FALSE( env.parse_condition("age != 29", data) );
CHECK_FALSE( env.parse_condition("age < 28", data) );
CHECK_FALSE( env.parse_condition("age < -100.0", data) );
}
SECTION("strings") {
CHECK( env.parse_condition("brother == father", data) );
CHECK( env.parse_condition("brother == \"Peter\"", data) );
}
}