mirror of
https://github.com/pantor/inja.git
synced 2026-02-17 00:53:59 +00:00
This commit is contained in:
committed by
GitHub
parent
b3d0e06a95
commit
ea845eee91
10
README.md
10
README.md
@@ -270,6 +270,16 @@ render("{{ isArray(guests) }}", data); // "true"
|
||||
// Implemented type checks: isArray, isBoolean, isFloat, isInteger, isNumber, isObject, isString,
|
||||
```
|
||||
|
||||
The Jinja2 pipe call syntax of functions is also supported:
|
||||
|
||||
```.cpp
|
||||
// Upper neighbour value
|
||||
render("Hello {{ neighbour | upper }}!", data); // "Hello PETER!"
|
||||
|
||||
// Sort array and join with comma
|
||||
render("{{ [\"B\", \"A\", \"C\"] | sort | join(\",\") }}", data); // "A,B,C"
|
||||
```
|
||||
|
||||
### Callbacks
|
||||
|
||||
You can create your own and more complex functions with callbacks. These are implemented with `std::function`, so you can for example use C++ lambdas. Inja `Arguments` are a vector of json pointers.
|
||||
|
||||
@@ -115,6 +115,8 @@ class Lexer {
|
||||
return make_token(Token::Kind::Comma);
|
||||
case ':':
|
||||
return make_token(Token::Kind::Colon);
|
||||
case '|':
|
||||
return make_token(Token::Kind::Pipe);
|
||||
case '(':
|
||||
return make_token(Token::Kind::LeftParen);
|
||||
case ')':
|
||||
|
||||
@@ -350,6 +350,47 @@ class Parser {
|
||||
}
|
||||
arguments.emplace_back(expr);
|
||||
} break;
|
||||
|
||||
// parse function call pipe syntax
|
||||
case Token::Kind::Pipe: {
|
||||
// get function name
|
||||
get_next_token();
|
||||
if (tok.kind != Token::Kind::Id) {
|
||||
throw_parser_error("expected function name, got '" + tok.describe() + "'");
|
||||
}
|
||||
auto func = std::make_shared<FunctionNode>(tok.text, tok.text.data() - tmpl.content.c_str());
|
||||
// add first parameter as last value from arguments
|
||||
func->number_args += 1;
|
||||
func->arguments.emplace_back(arguments.back());
|
||||
arguments.pop_back();
|
||||
get_peek_token();
|
||||
if (peek_tok.kind == Token::Kind::LeftParen) {
|
||||
get_next_token();
|
||||
// parse additional parameters
|
||||
do {
|
||||
get_next_token();
|
||||
auto expr = parse_expression(tmpl);
|
||||
if (!expr) {
|
||||
break;
|
||||
}
|
||||
func->number_args += 1;
|
||||
func->arguments.emplace_back(expr);
|
||||
} while (tok.kind == Token::Kind::Comma);
|
||||
if (tok.kind != Token::Kind::RightParen) {
|
||||
throw_parser_error("expected right parenthesis, got '" + tok.describe() + "'");
|
||||
}
|
||||
}
|
||||
// search store for defined function with such name and number of args
|
||||
auto function_data = function_storage.find_function(func->name, func->number_args);
|
||||
if (function_data.operation == FunctionStorage::Operation::None) {
|
||||
throw_parser_error("unknown function " + func->name);
|
||||
}
|
||||
func->operation = function_data.operation;
|
||||
if (function_data.operation == FunctionStorage::Operation::Callback) {
|
||||
func->callback = function_data.callback;
|
||||
}
|
||||
arguments.emplace_back(func);
|
||||
} break;
|
||||
default:
|
||||
goto break_loop;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ struct Token {
|
||||
GreaterEqual, // >=
|
||||
LessThan, // <
|
||||
LessEqual, // <=
|
||||
Pipe, // |
|
||||
Unknown,
|
||||
Eof,
|
||||
};
|
||||
|
||||
@@ -1002,6 +1002,7 @@ struct Token {
|
||||
GreaterEqual, // >=
|
||||
LessThan, // <
|
||||
LessEqual, // <=
|
||||
Pipe, // |
|
||||
Unknown,
|
||||
Eof,
|
||||
};
|
||||
@@ -1138,6 +1139,8 @@ class Lexer {
|
||||
return make_token(Token::Kind::Comma);
|
||||
case ':':
|
||||
return make_token(Token::Kind::Colon);
|
||||
case '|':
|
||||
return make_token(Token::Kind::Pipe);
|
||||
case '(':
|
||||
return make_token(Token::Kind::LeftParen);
|
||||
case ')':
|
||||
@@ -1797,6 +1800,47 @@ class Parser {
|
||||
}
|
||||
arguments.emplace_back(expr);
|
||||
} break;
|
||||
|
||||
// parse function call pipe syntax
|
||||
case Token::Kind::Pipe: {
|
||||
// get function name
|
||||
get_next_token();
|
||||
if (tok.kind != Token::Kind::Id) {
|
||||
throw_parser_error("expected function name, got '" + tok.describe() + "'");
|
||||
}
|
||||
auto func = std::make_shared<FunctionNode>(tok.text, tok.text.data() - tmpl.content.c_str());
|
||||
// add first parameter as last value from arguments
|
||||
func->number_args += 1;
|
||||
func->arguments.emplace_back(arguments.back());
|
||||
arguments.pop_back();
|
||||
get_peek_token();
|
||||
if (peek_tok.kind == Token::Kind::LeftParen) {
|
||||
get_next_token();
|
||||
// parse additional parameters
|
||||
do {
|
||||
get_next_token();
|
||||
auto expr = parse_expression(tmpl);
|
||||
if (!expr) {
|
||||
break;
|
||||
}
|
||||
func->number_args += 1;
|
||||
func->arguments.emplace_back(expr);
|
||||
} while (tok.kind == Token::Kind::Comma);
|
||||
if (tok.kind != Token::Kind::RightParen) {
|
||||
throw_parser_error("expected right parenthesis, got '" + tok.describe() + "'");
|
||||
}
|
||||
}
|
||||
// search store for defined function with such name and number of args
|
||||
auto function_data = function_storage.find_function(func->name, func->number_args);
|
||||
if (function_data.operation == FunctionStorage::Operation::None) {
|
||||
throw_parser_error("unknown function " + func->name);
|
||||
}
|
||||
func->operation = function_data.operation;
|
||||
if (function_data.operation == FunctionStorage::Operation::Callback) {
|
||||
func->callback = function_data.callback;
|
||||
}
|
||||
arguments.emplace_back(func);
|
||||
} break;
|
||||
default:
|
||||
goto break_loop;
|
||||
}
|
||||
|
||||
@@ -154,6 +154,12 @@ Yeah!
|
||||
data) == R""""(Yeah!
|
||||
)"""");
|
||||
}
|
||||
|
||||
SUBCASE("pipe syntax") {
|
||||
CHECK(env.render("{{ brother.name | upper }}", data) == "CHRIS");
|
||||
CHECK(env.render("{{ brother.name | upper | lower }}", data) == "chris");
|
||||
CHECK(env.render("{{ [\"C\", \"A\", \"B\"] | sort | join(\",\") }}", data) == "A,B,C");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("templates") {
|
||||
|
||||
Reference in New Issue
Block a user