mirror of
https://github.com/pantor/inja.git
synced 2026-04-04 23:28:52 +00:00
add round function, updated readme
This commit is contained in:
26
README.md
26
README.md
@@ -1,7 +1,6 @@
|
||||
[<div align="center"><img width="500" src="https://raw.githubusercontent.com/pantor/inja/master/doc/logo.jpg"></div>](https://github.com/pantor/inja/releases)
|
||||
|
||||
|
||||
|
||||
[](https://travis-ci.org/pantor/inja)
|
||||
[](https://ci.appveyor.com/project/pantor/inja)
|
||||
[](https://coveralls.io/r/pantor/inja)
|
||||
@@ -10,7 +9,7 @@
|
||||
[](https://raw.githubusercontent.com/pantor/inja/master/LICENSE)
|
||||
|
||||
|
||||
Inja is a template engine for modern C++, loosely inspired by [jinja](http://jinja.pocoo.org) for python. It has an easy and yet powerful template syntax with all variables, loops, conditions, includes, blocks, comments you need, nested and combined as you like. The rendering syntax is works like magic and uses the wonderful [json](https://github.com/nlohmann/json) library by nlohmann for data input. Most importantly, *inja* needs only two header files, which is (nearly) as trivial as integration in C++ can get. Of course, everything is tested on all relevant compilers. Have a look what it looks like:
|
||||
Inja is a template engine for modern C++, loosely inspired by [jinja](http://jinja.pocoo.org) for python. It has an easy and yet powerful template syntax with all variables, loops, conditions, includes, blocks, comments you need, nested and combined as you like. Inja uses the wonderful [json](https://github.com/nlohmann/json) library by nlohmann for data input and handling. Most importantly, *inja* needs only two header files, which is (nearly) as trivial as integration in C++ can get. Of course, everything is tested on all relevant compilers. Have a look what it looks like:
|
||||
|
||||
```c++
|
||||
json data;
|
||||
@@ -36,7 +35,12 @@ using json = nlohmann::json;
|
||||
|
||||
## Tutorial
|
||||
|
||||
This tutorial will give you an idea how to use inja. It will explain the most important concepts and give practical advices using examples and exectuable code. Beside this tutorial, you can check the [documentation]() for further information.
|
||||
|
||||
### Template Rendering
|
||||
|
||||
The basic template rendering takes a template as a `std::string` and a `json` object for all data. It returns the rendered template as an `std::string`.
|
||||
|
||||
```c++
|
||||
json data;
|
||||
data["name"] = "world";
|
||||
@@ -56,11 +60,11 @@ result = env.render_template("./template.txt", data);
|
||||
result = env.render_template("./template.txt", "./data.json");
|
||||
|
||||
// Or write a rendered template file
|
||||
env.write("./template.txt", "./result.txt")
|
||||
env.write("./template.txt", data, "./result.txt")
|
||||
env.write("./template.txt", "./data.json", "./result.txt")
|
||||
```
|
||||
|
||||
The environment class can be configured.
|
||||
The environment class can be configured to your needs.
|
||||
```c++
|
||||
// With default settings
|
||||
Environment env_default = Environment();
|
||||
@@ -80,7 +84,7 @@ env.setLineStatements("##"); // Line statement (just an opener)
|
||||
|
||||
### Variables
|
||||
|
||||
Variables can be rendered within the `{{ ... }}` expressions.
|
||||
Variables are rendered within the `{{ ... }}` expressions.
|
||||
```c++
|
||||
json data;
|
||||
data["neighbour"] = "Peter";
|
||||
@@ -115,11 +119,11 @@ render(R"(Guest List:
|
||||
2: Pierre
|
||||
3: Tom */
|
||||
```
|
||||
In a loop, the special variables `number index`, `number index1`, `bool is_first` and `bool is_last` are available.
|
||||
In a loop, the special variables `index (number)`, `index1 (number)`, `is_first (boolean)` and `is_last (boolean)` are available.
|
||||
|
||||
#### Conditions
|
||||
|
||||
Conditions support if, else if and else statements. Following conditions for example:
|
||||
Conditions support the typical if, else if and else statements. Following conditions are for example possible:
|
||||
```c++
|
||||
// Standard comparisons with variable
|
||||
render("{% if time/hour >= 18 %}…{% endif %}", data); // True
|
||||
@@ -136,7 +140,7 @@ render("{% if not guest_count %}…{% endif %}", data); // True
|
||||
|
||||
#### Includes
|
||||
|
||||
Include other files, relative from the current file location.
|
||||
This include other files, relative from the current file location.
|
||||
```
|
||||
{% include "footer.html" %}
|
||||
```
|
||||
@@ -145,7 +149,7 @@ Include other files, relative from the current file location.
|
||||
|
||||
A few functions are implemented within the inja template syntax. They can be called with
|
||||
```c++
|
||||
// upper(<string>)
|
||||
// Upper and lower function, for string cases
|
||||
render("Hello {{ upper(neighbour) }}!", data); // "Hello PETER!"
|
||||
render("Hello {{ lower(neighbour) }}!", data); // "Hello peter!"
|
||||
|
||||
@@ -154,6 +158,10 @@ render("{% for i in range(4) %}{{ index1 }}{% endfor %}", data); // "1234"
|
||||
|
||||
// Length function (but please don't combine with range, use list directly...)
|
||||
render("I count {{ length(guests) }} guests.", data); // "I count 3 guests."
|
||||
|
||||
// Round numbers to a given precision
|
||||
render({{ round(3.1415, 0) }}, data) // 3
|
||||
render({{ round(3.1415, 3) }}, data) // 3.142
|
||||
```
|
||||
|
||||
### Comments
|
||||
|
||||
64
src/inja.hpp
64
src/inja.hpp
@@ -102,8 +102,6 @@ inline Match search(const std::string& input, std::vector<Regex> regexes, size_t
|
||||
if (not search_match.found()) { return Match(); }
|
||||
|
||||
// Vector of id vs groups
|
||||
// 0: 1, 1: 2, 2: 3, 3: 2
|
||||
// 0 1 1 2 2 2 3 3
|
||||
std::vector<int> regex_mark_counts;
|
||||
for (int i = 0; i < regexes.size(); i++) {
|
||||
for (int j = 0; j < regexes[i].mark_count() + 1; j++) {
|
||||
@@ -240,7 +238,8 @@ public:
|
||||
Upper,
|
||||
Lower,
|
||||
Range,
|
||||
Length
|
||||
Length,
|
||||
Round
|
||||
};
|
||||
|
||||
const std::map<Function, Regex> regex_map_functions = {
|
||||
@@ -248,6 +247,7 @@ public:
|
||||
{Function::Lower, Regex{"lower\\(\\s*(.*?)\\s*\\)"}},
|
||||
{Function::Range, Regex{"range\\(\\s*(.*?)\\s*\\)"}},
|
||||
{Function::Length, Regex{"length\\(\\s*(.*?)\\s*\\)"}},
|
||||
{Function::Round, Regex{"length\\(\\s*(.*?)\\s*,\\s*(.*?)\\s*\\)"}},
|
||||
};
|
||||
|
||||
Parser() { }
|
||||
@@ -373,10 +373,24 @@ public:
|
||||
Environment(): Environment("./") { }
|
||||
Environment(std::string global_path): global_path(global_path), parser() { }
|
||||
|
||||
void setExpression(std::string open, std::string close) {
|
||||
parser.regex_map_delimiters[Parser::Delimiter::Expression] = Regex{open + close};
|
||||
void setStatement(std::string open, std::string close) {
|
||||
parser.regex_map_delimiters[Parser::Delimiter::Statement] = Regex{open + "\\s*(.+?)\\s*" + close};
|
||||
}
|
||||
|
||||
void setLineStatement(std::string open) {
|
||||
parser.regex_map_delimiters[Parser::Delimiter::LineStatement] = Regex{"(?:^|\\n)" + open + "\\s*(.+)\\s*"};
|
||||
}
|
||||
|
||||
void setExpression(std::string open, std::string close) {
|
||||
parser.regex_map_delimiters[Parser::Delimiter::Expression] = Regex{open + "\\s*(.+?)\\s*" + close};
|
||||
}
|
||||
|
||||
void setComment(std::string open, std::string close) {
|
||||
parser.regex_map_delimiters[Parser::Delimiter::Comment] = Regex{open + "\\s*(.+?)\\s*" + close};
|
||||
}
|
||||
|
||||
|
||||
|
||||
json eval_variable(std::string input, json data) {
|
||||
return eval_variable(input, data, true);
|
||||
}
|
||||
@@ -413,6 +427,13 @@ public:
|
||||
if (not list.is_array()) { throw std::runtime_error("Argument in length function is not a list."); }
|
||||
return list.size();
|
||||
}
|
||||
case Parser::Function::Round: {
|
||||
json number = eval_variable(match_function.str(1), data);
|
||||
json precision = eval_variable(match_function.str(2), data);
|
||||
if (not number.is_number()) { throw std::runtime_error("Argument in length function is not a number."); }
|
||||
if (not precision.is_number()) { throw std::runtime_error("Argument in length function is not a number."); }
|
||||
return std::round(number.get<double>() * std::pow(10.0, precision.get<int>())) / std::pow(10.0, precision.get<int>());
|
||||
}
|
||||
}
|
||||
|
||||
if (input[0] != '/') { input.insert(0, "/"); }
|
||||
@@ -441,34 +462,22 @@ public:
|
||||
return (std::find(list.begin(), list.end(), item) != list.end());
|
||||
}
|
||||
case Parser::ConditionOperators::Equal: {
|
||||
json comp1 = eval_variable(match_condition.str(1), data);
|
||||
json comp2 = eval_variable(match_condition.str(2), data);
|
||||
return comp1 == comp2;
|
||||
return eval_variable(match_condition.str(1), data) == eval_variable(match_condition.str(2), data);
|
||||
}
|
||||
case Parser::ConditionOperators::Greater: {
|
||||
json comp1 = eval_variable(match_condition.str(1), data);
|
||||
json comp2 = eval_variable(match_condition.str(2), data);
|
||||
return comp1 > comp2;
|
||||
return eval_variable(match_condition.str(1), data) > eval_variable(match_condition.str(2), data);
|
||||
}
|
||||
case Parser::ConditionOperators::Less: {
|
||||
json comp1 = eval_variable(match_condition.str(1), data);
|
||||
json comp2 = eval_variable(match_condition.str(2), data);
|
||||
return comp1 < comp2;
|
||||
return eval_variable(match_condition.str(1), data) < eval_variable(match_condition.str(2), data);
|
||||
}
|
||||
case Parser::ConditionOperators::GreaterEqual: {
|
||||
json comp1 = eval_variable(match_condition.str(1), data);
|
||||
json comp2 = eval_variable(match_condition.str(2), data);
|
||||
return comp1 >= comp2;
|
||||
return eval_variable(match_condition.str(1), data) >= eval_variable(match_condition.str(2), data);
|
||||
}
|
||||
case Parser::ConditionOperators::LessEqual: {
|
||||
json comp1 = eval_variable(match_condition.str(1), data);
|
||||
json comp2 = eval_variable(match_condition.str(2), data);
|
||||
return comp1 <= comp2;
|
||||
return eval_variable(match_condition.str(1), data) <= eval_variable(match_condition.str(2), data);
|
||||
}
|
||||
case Parser::ConditionOperators::Different: {
|
||||
json comp1 = eval_variable(match_condition.str(1), data);
|
||||
json comp2 = eval_variable(match_condition.str(2), data);
|
||||
return comp1 != comp2;
|
||||
return eval_variable(match_condition.str(1), data) != eval_variable(match_condition.str(2), data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,7 +566,6 @@ public:
|
||||
return render_tree(parsed, data, path);
|
||||
}
|
||||
|
||||
|
||||
std::string render(std::string input, json data) {
|
||||
return render(input, data, "./");
|
||||
}
|
||||
@@ -572,6 +580,14 @@ public:
|
||||
json data = load_json(filename_data);
|
||||
return render_template(filename, data);
|
||||
}
|
||||
|
||||
void write(std::string filename, json data, std::string filename_out) {
|
||||
|
||||
}
|
||||
|
||||
void write(std::string filename, std::string filename_data, std::string filename_out) {
|
||||
|
||||
}
|
||||
|
||||
std::string load_file(std::string filename) {
|
||||
std::ifstream file(global_path + filename);
|
||||
|
||||
Reference in New Issue
Block a user