mirror of
https://github.com/pantor/inja.git
synced 2026-02-17 09:03:58 +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,
|
// 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
|
### 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.
|
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);
|
return make_token(Token::Kind::Comma);
|
||||||
case ':':
|
case ':':
|
||||||
return make_token(Token::Kind::Colon);
|
return make_token(Token::Kind::Colon);
|
||||||
|
case '|':
|
||||||
|
return make_token(Token::Kind::Pipe);
|
||||||
case '(':
|
case '(':
|
||||||
return make_token(Token::Kind::LeftParen);
|
return make_token(Token::Kind::LeftParen);
|
||||||
case ')':
|
case ')':
|
||||||
|
|||||||
@@ -350,6 +350,47 @@ class Parser {
|
|||||||
}
|
}
|
||||||
arguments.emplace_back(expr);
|
arguments.emplace_back(expr);
|
||||||
} break;
|
} 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:
|
default:
|
||||||
goto break_loop;
|
goto break_loop;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ struct Token {
|
|||||||
GreaterEqual, // >=
|
GreaterEqual, // >=
|
||||||
LessThan, // <
|
LessThan, // <
|
||||||
LessEqual, // <=
|
LessEqual, // <=
|
||||||
|
Pipe, // |
|
||||||
Unknown,
|
Unknown,
|
||||||
Eof,
|
Eof,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1002,6 +1002,7 @@ struct Token {
|
|||||||
GreaterEqual, // >=
|
GreaterEqual, // >=
|
||||||
LessThan, // <
|
LessThan, // <
|
||||||
LessEqual, // <=
|
LessEqual, // <=
|
||||||
|
Pipe, // |
|
||||||
Unknown,
|
Unknown,
|
||||||
Eof,
|
Eof,
|
||||||
};
|
};
|
||||||
@@ -1138,6 +1139,8 @@ class Lexer {
|
|||||||
return make_token(Token::Kind::Comma);
|
return make_token(Token::Kind::Comma);
|
||||||
case ':':
|
case ':':
|
||||||
return make_token(Token::Kind::Colon);
|
return make_token(Token::Kind::Colon);
|
||||||
|
case '|':
|
||||||
|
return make_token(Token::Kind::Pipe);
|
||||||
case '(':
|
case '(':
|
||||||
return make_token(Token::Kind::LeftParen);
|
return make_token(Token::Kind::LeftParen);
|
||||||
case ')':
|
case ')':
|
||||||
@@ -1797,6 +1800,47 @@ class Parser {
|
|||||||
}
|
}
|
||||||
arguments.emplace_back(expr);
|
arguments.emplace_back(expr);
|
||||||
} break;
|
} 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:
|
default:
|
||||||
goto break_loop;
|
goto break_loop;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,6 +154,12 @@ Yeah!
|
|||||||
data) == R""""(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") {
|
TEST_CASE("templates") {
|
||||||
|
|||||||
Reference in New Issue
Block a user