add at function

This commit is contained in:
pantor
2019-04-07 16:15:12 +02:00
parent bf071019b9
commit d5532ac26a
6 changed files with 38 additions and 14 deletions
+1
View File
@@ -181,6 +181,7 @@ render("Hello {{ lower(neighbour) }}!", data); // "Hello peter!"
// Range function, useful for loops
render("{% for i in range(4) %}{{ loop.index1 }}{% endfor %}", data); // "1234"
render("{% for i in range(3) %}{{ at(guests, i) }} {% endfor %}", data); // "Jeff Tom Patrick "
// Length function (please don't combine with range, use list directly...)
render("I count {{ length(guests) }} guests.", data); // "I count 3 guests."
+1
View File
@@ -38,6 +38,7 @@ struct Bytecode {
GreaterEqual,
Less,
LessEqual,
At,
Different,
DivisibleBy,
Even,
+3 -2
View File
@@ -18,6 +18,7 @@ namespace inja {
class ParserStatic {
ParserStatic() {
functions.add_builtin("at", 2, Bytecode::Op::At);
functions.add_builtin("default", 2, Bytecode::Op::Default);
functions.add_builtin("divisibleBy", 2, Bytecode::Op::DivisibleBy);
functions.add_builtin("even", 1, Bytecode::Op::Even);
@@ -183,8 +184,8 @@ class Parser {
append_callback(tmpl, func_token.text, num_args);
return true;
}
} else if (m_tok.text == static_cast<decltype(m_tok.text)>("true") ||
m_tok.text == static_cast<decltype(m_tok.text)>("false") ||
} else if (m_tok.text == static_cast<decltype(m_tok.text)>("true") ||
m_tok.text == static_cast<decltype(m_tok.text)>("false") ||
m_tok.text == static_cast<decltype(m_tok.text)>("null")) {
// true, false, null are json literals
if (brace_level == 0 && bracket_level == 0) {
+11 -4
View File
@@ -132,8 +132,8 @@ class Renderer {
enum class Type { Map, Array };
Type loop_type;
nonstd::string_view key_name; // variable name for keys
nonstd::string_view value_name; // variable name for values
nonstd::string_view key_name; // variable name for keys
nonstd::string_view value_name; // variable name for values
json data; // data with loop info added
json values; // values to iterate over
@@ -145,8 +145,8 @@ class Renderer {
// loop over map
using KeyValue = std::pair<nonstd::string_view, json*>;
using MapValues = std::vector<KeyValue>;
MapValues map_values; // values to iterate over
MapValues::iterator map_it; // iterator over values
MapValues map_values; // values to iterate over
MapValues::iterator map_it; // iterator over values
};
@@ -235,6 +235,13 @@ class Renderer {
m_stack.emplace_back(std::move(result));
break;
}
case Bytecode::Op::At: {
auto args = get_args(bc);
auto result = args[0]->at(args[1]->get<int>());
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::First: {
auto result = get_args(bc)[0]->front();
pop_args(bc);
+15 -6
View File
@@ -1441,6 +1441,7 @@ struct Bytecode {
GreaterEqual,
Less,
LessEqual,
At,
Different,
DivisibleBy,
Even,
@@ -2017,6 +2018,7 @@ namespace inja {
class ParserStatic {
ParserStatic() {
functions.add_builtin("at", 2, Bytecode::Op::At);
functions.add_builtin("default", 2, Bytecode::Op::Default);
functions.add_builtin("divisibleBy", 2, Bytecode::Op::DivisibleBy);
functions.add_builtin("even", 1, Bytecode::Op::Even);
@@ -2182,8 +2184,8 @@ class Parser {
append_callback(tmpl, func_token.text, num_args);
return true;
}
} else if (m_tok.text == static_cast<decltype(m_tok.text)>("true") ||
m_tok.text == static_cast<decltype(m_tok.text)>("false") ||
} else if (m_tok.text == static_cast<decltype(m_tok.text)>("true") ||
m_tok.text == static_cast<decltype(m_tok.text)>("false") ||
m_tok.text == static_cast<decltype(m_tok.text)>("null")) {
// true, false, null are json literals
if (brace_level == 0 && bracket_level == 0) {
@@ -2738,8 +2740,8 @@ class Renderer {
enum class Type { Map, Array };
Type loop_type;
nonstd::string_view key_name; // variable name for keys
nonstd::string_view value_name; // variable name for values
nonstd::string_view key_name; // variable name for keys
nonstd::string_view value_name; // variable name for values
json data; // data with loop info added
json values; // values to iterate over
@@ -2751,8 +2753,8 @@ class Renderer {
// loop over map
using KeyValue = std::pair<nonstd::string_view, json*>;
using MapValues = std::vector<KeyValue>;
MapValues map_values; // values to iterate over
MapValues::iterator map_it; // iterator over values
MapValues map_values; // values to iterate over
MapValues::iterator map_it; // iterator over values
};
@@ -2841,6 +2843,13 @@ class Renderer {
m_stack.emplace_back(std::move(result));
break;
}
case Bytecode::Op::At: {
auto args = get_args(bc);
auto result = args[0]->at(args[1]->get<int>());
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::First: {
auto result = get_args(bc)[0]->front();
pop_args(bc);
+7 -2
View File
@@ -62,11 +62,10 @@ TEST_CASE("types") {
CHECK( env.render("{% for name in names %}{{ loop.index }}: {{ name }}{% if not loop.is_last %}, {% endif %}{% endfor %}!", data) == "0: Jeff, 1: Seb!" );
CHECK( env.render("{% for name in names %}{{ loop.index }}: {{ name }}{% if loop.is_last == false %}, {% endif %}{% endfor %}!", data) == "0: Jeff, 1: Seb!" );
data["empty_loop"] = {};
CHECK( env.render("{% for name in empty_loop %}a{% endfor %}", data) == "" );
CHECK( env.render("{% for name in {} %}a{% endfor %}", data) == "" );
CHECK_THROWS_WITH( env.render("{% for name ins names %}a{% endfor %}", data), "[inja.exception.parser_error] expected 'in', got 'ins'" );
CHECK_THROWS_WITH( env.render("{% for name in empty_loop %}a{% endfor %}", data), "[inja.exception.render_error] variable 'empty_loop' not found" );
// CHECK_THROWS_WITH( env.render("{% for name in relatives %}{{ name }}{% endfor %}", data), "[inja.exception.json_error] [json.exception.type_error.302] type must be array, but is object" );
}
@@ -144,6 +143,7 @@ TEST_CASE("functions") {
data["brother"]["daughters"] = {"Maria", "Helen"};
data["property"] = "name";
data["age"] = 29;
data["i"] = 1;
data["is_happy"] = true;
data["is_sad"] = false;
data["vars"] = {2, 3, 4, 0, -1, -2, -3};
@@ -182,6 +182,11 @@ TEST_CASE("functions") {
// CHECK_THROWS_WITH( env.render("{{ sort(5) }}", data), "[inja.exception.json_error] [json.exception.type_error.302] type must be array, but is number" );
}
SECTION("at") {
CHECK( env.render("{{ at(names, 0) }}", data) == "Jeff" );
CHECK( env.render("{{ at(names, i) }}", data) == "Seb" );
}
SECTION("first") {
CHECK( env.render("{{ first(names) }}", data) == "Jeff" );
// CHECK_THROWS_WITH( env.render("{{ first(5) }}", data), "[inja.exception.json_error] [json.exception.type_error.302] type must be array, but is number" );