From c56cbd63e93a3241858f2a5676ab9a78e76badae Mon Sep 17 00:00:00 2001 From: pantor Date: Sun, 26 Jul 2020 12:52:36 +0200 Subject: [PATCH] add atid operation --- include/inja/function_storage.hpp | 1 + include/inja/lexer.hpp | 2 ++ include/inja/node.hpp | 6 +++++- include/inja/parser.hpp | 6 +++++- include/inja/renderer.hpp | 10 ++++++++++ include/inja/token.hpp | 1 + single_include/inja/inja.hpp | 26 +++++++++++++++++++++++++- test/test-functions.cpp | 4 ++++ 8 files changed, 53 insertions(+), 3 deletions(-) diff --git a/include/inja/function_storage.hpp b/include/inja/function_storage.hpp index e0d740fa..7767f9bf 100644 --- a/include/inja/function_storage.hpp +++ b/include/inja/function_storage.hpp @@ -36,6 +36,7 @@ class FunctionStorage { Division, Power, Modulo, + AtId, At, Default, DivisibleBy, diff --git a/include/inja/lexer.hpp b/include/inja/lexer.hpp index 5b218b89..d93aafc0 100644 --- a/include/inja/lexer.hpp +++ b/include/inja/lexer.hpp @@ -108,6 +108,8 @@ class Lexer { return make_token(Token::Kind::Power); case '%': return make_token(Token::Kind::Percent); + case '.': + return make_token(Token::Kind::Dot); case ',': return make_token(Token::Kind::Comma); case ':': diff --git a/include/inja/node.hpp b/include/inja/node.hpp index 62707f81..6247d129 100644 --- a/include/inja/node.hpp +++ b/include/inja/node.hpp @@ -141,7 +141,7 @@ class FunctionNode : public ExpressionNode { size_t number_args; CallbackFunction callback; - explicit FunctionNode(nonstd::string_view name, size_t pos) : ExpressionNode(pos), precedence(5), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) { } + explicit FunctionNode(nonstd::string_view name, size_t pos) : ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) { } explicit FunctionNode(Op operation, size_t pos) : ExpressionNode(pos), operation(operation), number_args(1) { switch (operation) { case Op::Not: { @@ -208,6 +208,10 @@ class FunctionNode : public ExpressionNode { precedence = 4; associativity = Associativity::Left; } break; + case Op::AtId: { + precedence = 8; + associativity = Associativity::Left; + } break; default: { precedence = 1; associativity = Associativity::Left; diff --git a/include/inja/parser.hpp b/include/inja/parser.hpp index ae7b7a14..c6d46a3b 100644 --- a/include/inja/parser.hpp +++ b/include/inja/parser.hpp @@ -166,7 +166,8 @@ class Parser { case Token::Kind::Times: case Token::Kind::Slash: case Token::Kind::Power: - case Token::Kind::Percent: { + case Token::Kind::Percent: + case Token::Kind::Dot: { parse_operator: FunctionStorage::Operation operation; @@ -220,6 +221,9 @@ class Parser { case Token::Kind::Percent: { operation = FunctionStorage::Operation::Modulo; } break; + case Token::Kind::Dot: { + operation = FunctionStorage::Operation::AtId; + } break; default: { throw_parser_error("unknown operator in parser."); } diff --git a/include/inja/renderer.hpp b/include/inja/renderer.hpp index 9a4dae04..5427c3d1 100644 --- a/include/inja/renderer.hpp +++ b/include/inja/renderer.hpp @@ -311,6 +311,16 @@ class Renderer : public NodeVisitor { json_tmp_stack.push_back(result_ptr); json_eval_stack.push(result_ptr.get()); } break; + case Op::AtId: { + json_eval_stack.pop(); // Pop id nullptr + auto container = get_arguments<1, false>(node)[0]; + if (not_found_stack.empty()) { + throw_renderer_error("could not find element with given name", node); + } + auto id_node = not_found_stack.top(); + not_found_stack.pop(); + json_eval_stack.push(&container->at(id_node->name)); + } break; case Op::At: { auto args = get_arguments<2>(node); json_eval_stack.push(&args[0]->at(args[1]->get())); diff --git a/include/inja/token.hpp b/include/inja/token.hpp index 00df0423..c0001386 100644 --- a/include/inja/token.hpp +++ b/include/inja/token.hpp @@ -33,6 +33,7 @@ struct Token { Percent, // % Power, // ^ Comma, // , + Dot, // . Colon, // : LeftParen, // ( RightParen, // ) diff --git a/single_include/inja/inja.hpp b/single_include/inja/inja.hpp index 755827b8..b1119dbc 100644 --- a/single_include/inja/inja.hpp +++ b/single_include/inja/inja.hpp @@ -1550,6 +1550,7 @@ class FunctionStorage { Division, Power, Modulo, + AtId, At, Default, DivisibleBy, @@ -1770,6 +1771,7 @@ struct Token { Percent, // % Power, // ^ Comma, // , + Dot, // . Colon, // : LeftParen, // ( RightParen, // ) @@ -1984,6 +1986,8 @@ class Lexer { return make_token(Token::Kind::Power); case '%': return make_token(Token::Kind::Percent); + case '.': + return make_token(Token::Kind::Dot); case ',': return make_token(Token::Kind::Comma); case ':': @@ -2849,7 +2853,8 @@ class Parser { case Token::Kind::Times: case Token::Kind::Slash: case Token::Kind::Power: - case Token::Kind::Percent: { + case Token::Kind::Percent: + case Token::Kind::Dot: { parse_operator: FunctionStorage::Operation operation; @@ -2903,6 +2908,10 @@ class Parser { case Token::Kind::Percent: { operation = FunctionStorage::Operation::Modulo; } break; + case Token::Kind::Dot: { + std::cout << "test" << std::endl; + operation = FunctionStorage::Operation::AtId; + } break; default: { throw_parser_error("unknown operator in parser."); } @@ -3553,6 +3562,21 @@ class Renderer : public NodeVisitor { json_tmp_stack.push_back(result_ptr); json_eval_stack.push(result_ptr.get()); } break; + case Op::AtId: { + std::cout << "test" << std::endl; + auto container = get_arguments<1, false>(node)[0]; + auto id = get_arguments<1, false>(node)[0]; + if (id == nullptr) { + auto id_node = not_found_stack.top(); + not_found_stack.pop(); + + auto ptr = json::json_pointer(id_node->ptr); + json_eval_stack.push(&container->at(ptr)); + + } else { + json_eval_stack.push(id); + } + } break; case Op::At: { auto args = get_arguments<2>(node); json_eval_stack.push(&args[0]->at(args[1]->get())); diff --git a/test/test-functions.cpp b/test/test-functions.cpp index b8464460..def0c6db 100644 --- a/test/test-functions.cpp +++ b/test/test-functions.cpp @@ -254,6 +254,7 @@ TEST_CASE("combinations") { data["brother"]["daughters"] = {"Maria", "Helen"}; data["brother"]["daughter0"] = {{"name", "Maria"}}; data["is_happy"] = true; + data["list_of_objects"] = {{{"a", 2}}, {{"b", 3}}, {{"c", 4}}, {{"d", 5}}}; CHECK(env.render("{% if upper(\"Peter\") == \"PETER\" %}TRUE{% endif %}", data) == "TRUE"); CHECK(env.render("{% if lower(upper(name)) == \"peter\" %}TRUE{% endif %}", data) == "TRUE"); @@ -263,4 +264,7 @@ TEST_CASE("combinations") { CHECK(env.render("{{ upper(first(sort(brother.daughters)) + \"_test\") }}", data) == "HELEN_TEST"); CHECK(env.render("{% for i in range(3) %}{{ at(names, i) }}{% endfor %}", data) == "JeffSebChris"); CHECK(env.render("{% if not is_happy or age > 26 %}TRUE{% endif %}", data) == "TRUE"); + CHECK(env.render("{{ last(list_of_objects).d * 2}}", data) == "10"); + CHECK(env.render("{{ last(range(5)) * 2 }}", data) == "8"); + CHECK(env.render("{{ last(range(5 * 2)) }}", data) == "9"); }