diff --git a/src/expression_evaluator.cpp b/src/expression_evaluator.cpp index 136e33d..c5e339b 100644 --- a/src/expression_evaluator.cpp +++ b/src/expression_evaluator.cpp @@ -179,7 +179,8 @@ InternalValue DictCreator::Evaluate(RenderContext& context) InternalValueMap result; for (auto& e : m_exprs) { - result[e.first] = e.second->Evaluate(context); + auto key = e.first->Evaluate(context); + result[AsString(key)] = e.second->Evaluate(context); } return CreateMapAdapter(std::move(result));; diff --git a/src/expression_evaluator.h b/src/expression_evaluator.h index 06b1c40..767bbb7 100644 --- a/src/expression_evaluator.h +++ b/src/expression_evaluator.h @@ -348,7 +348,7 @@ class DictionaryCreator : public Expression class DictCreator : public Expression { public: - DictCreator(std::unordered_map> exprs) + DictCreator(std::vector, ExpressionEvaluatorPtr<>>> exprs) : m_exprs(std::move(exprs)) { } @@ -363,7 +363,7 @@ class DictCreator : public Expression return m_exprs == val->m_exprs; } private: - std::unordered_map> m_exprs; + std::vector, ExpressionEvaluatorPtr<>>> m_exprs; }; class UnaryExpression : public Expression diff --git a/src/expression_parser.cpp b/src/expression_parser.cpp index c892936..f3b0a3a 100644 --- a/src/expression_parser.cpp +++ b/src/expression_parser.cpp @@ -369,31 +369,31 @@ ExpressionParser::ParseResult> ExpressionPars { ExpressionEvaluatorPtr result; - std::unordered_map> items; + std::vector, ExpressionEvaluatorPtr>> items; if (lexer.EatIfEqual('}')) return std::make_shared(std::move(items)); do { - Token key = lexer.NextToken(); - if (key != Token::String) - return MakeParseError(ErrorCode::ExpectedStringLiteral, key); + auto pivotTok = lexer.PeekNextToken(); + auto key = ParseFullExpression(lexer); + if (!key) + return ReplaceErrorIfPossible(key, pivotTok, ErrorCode::ExpectedExpression); - if (!lexer.EatIfEqual('=')) + if (!lexer.EatIfEqual('=') && !lexer.EatIfEqual(':')) { auto tok = lexer.PeekNextToken(); auto tok1 = tok; - tok1.type = Token::Assign; + tok1.type = Token::Colon; return MakeParseError(ErrorCode::ExpectedToken, tok, {tok1}); } - auto pivotTok = lexer.PeekNextToken(); - auto expr = ParseFullExpression(lexer); - if (!expr) - return ReplaceErrorIfPossible(expr, pivotTok, ErrorCode::ExpectedExpression); - - items[AsString(key.value)] = *expr; + pivotTok = lexer.PeekNextToken(); + auto value = ParseFullExpression(lexer); + if (!value) + return ReplaceErrorIfPossible(value, pivotTok, ErrorCode::ExpectedExpression); + items.emplace_back(*key, *value); } while (lexer.EatIfEqual(',')); auto tok = lexer.NextToken(); diff --git a/src/lexer.h b/src/lexer.h index e644de2..73d50d2 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -36,6 +36,7 @@ struct Token LCrlBracket = '{', RCrlBracket = '}', Assign = '=', + Colon = ':', Comma = ',', Eof = 256, diff --git a/src/template_parser.h b/src/template_parser.h index c183f5b..9192f5d 100644 --- a/src/template_parser.h +++ b/src/template_parser.h @@ -1016,6 +1016,7 @@ std::unordered_map ParserTraitsBase::s_tokens = { { Token::LCrlBracket, UNIVERSAL_STR("{") }, { Token::RCrlBracket, UNIVERSAL_STR("}") }, { Token::Assign, UNIVERSAL_STR("=") }, + { Token::Colon, UNIVERSAL_STR(":") }, { Token::Comma, UNIVERSAL_STR(",") }, { Token::Eof, UNIVERSAL_STR("<>") }, { Token::Equal, UNIVERSAL_STR("==") }, diff --git a/test/errors_test.cpp b/test/errors_test.cpp index bcb137b..0cdf07b 100644 --- a/test/errors_test.cpp +++ b/test/errors_test.cpp @@ -300,7 +300,7 @@ INSTANTIATE_TEST_SUITE_P(BasicExpressionsTest, ErrorsGenericTest, ::testing::Val InputOutputPair{"{{ 1 if 2 is 3 else 2 }}", "noname.j2tpl:1:14: error: Identifier expected\n{{ 1 if 2 is 3 else 2 }}\n ---^-------"}, InputOutputPair{"{{ 1 if 2 == 3 else {1} }}", - "noname.j2tpl:1:22: error: String expected\n{{ 1 if 2 == 3 else {1} }}\n ---^-------"}, + "noname.j2tpl:1:23: error: Unexpected token '}'. Expected: ':'\n{{ 1 if 2 == 3 else {1} }}\n ---^-------"}, InputOutputPair{"{{ 1 if 2 is equalto(10,) else 2 }}", "noname.j2tpl:1:25: error: Unexpected token: ')'\n{{ 1 if 2 is equalto(10,) else 2 }}\n ---^-------"}, InputOutputPair{"{{ range(1, 3, ) }}", @@ -308,19 +308,19 @@ INSTANTIATE_TEST_SUITE_P(BasicExpressionsTest, ErrorsGenericTest, ::testing::Val InputOutputPair{"{{ range(1, 3} }}", "noname.j2tpl:1:14: error: ')' expected\n{{ range(1, 3} }}\n ---^-------"}, InputOutputPair{"{{ {1, 3, 5] }}", - "noname.j2tpl:1:5: error: String expected\n{{ {1, 3, 5] }}\n ---^-------"}, + "noname.j2tpl:1:6: error: Unexpected token ','. Expected: ':'\n{{ {1, 3, 5] }}\n ---^-------"}, InputOutputPair{"{{ {'key'} }}", - "noname.j2tpl:1:10: error: Unexpected token '}'. Expected: '='\n{{ {'key'} }}\n ---^-------"}, + "noname.j2tpl:1:10: error: Unexpected token '}'. Expected: ':'\n{{ {'key'} }}\n ---^-------"}, InputOutputPair{"{{ {'key'=} }}", "noname.j2tpl:1:11: error: Expected expression, got: '}'\n{{ {'key'=} }}\n ---^-------"}, InputOutputPair{"{{ {'key'=,} }}", "noname.j2tpl:1:11: error: Expected expression, got: ','\n{{ {'key'=,} }}\n ---^-------"}, InputOutputPair{"{{ {=1} }}", - "noname.j2tpl:1:5: error: String expected\n{{ {=1} }}\n ---^-------"}, + "noname.j2tpl:1:5: error: Expected expression, got: '='\n{{ {=1} }}\n ---^-------"}, InputOutputPair{"{{ {'key'=1] }}", "noname.j2tpl:1:12: error: '}' expected\n{{ {'key'=1] }}\n ---^-------"}, InputOutputPair{"{{ {'key'=1,} }}", - "noname.j2tpl:1:13: error: String expected\n{{ {'key'=1,} }}\n ---^-------"}, + "noname.j2tpl:1:13: error: Expected expression, got: '}'\n{{ {'key'=1,} }}\n ---^-------"}, InputOutputPair{"{{ [1, 3, 5} }}", "noname.j2tpl:1:12: error: ']' expected\n{{ [1, 3, 5} }}\n ---^-------"}, InputOutputPair{"{{ [1, 3,] }}", @@ -369,7 +369,7 @@ INSTANTIATE_TEST_SUITE_P(StatementsTest_1, ErrorsGenericTest, ::testing::Values( InputOutputPair{"{% for i in range(10) endfor%}", "noname.j2tpl:1:23: error: Unexpected token 'endfor'. Expected: 'if', 'recursive', '<>'\n{% for i in range(10) endfor%}\n ---^-------"}, InputOutputPair{"{% for i in range(10) if {key} %}", - "noname.j2tpl:1:27: error: String expected\n{% for i in range(10) if {key} %}\n ---^-------"}, + "noname.j2tpl:1:30: error: Unexpected token '}'. Expected: ':'\n{% for i in range(10) if {key} %}\n ---^-------"}, InputOutputPair{"{% for i in range(10) if true else hello %}", "noname.j2tpl:1:31: error: Expected end of statement, got: 'else'\n{% for i in range(10) if true else hello %}\n ---^-------"}, InputOutputPair{"{% for i in range(10) %}\n{% endif %}", @@ -383,7 +383,7 @@ INSTANTIATE_TEST_SUITE_P(StatementsTest_1, ErrorsGenericTest, ::testing::Values( InputOutputPair{"{% set 10%}", "noname.j2tpl:1:8: error: Identifier expected\n{% set 10%}\n ---^-------"}, InputOutputPair{"{% set i = {key] %}", - "noname.j2tpl:1:13: error: String expected\n{% set i = {key] %}\n ---^-------"}, + "noname.j2tpl:1:16: error: Unexpected token ']'. Expected: ':'\n{% set i = {key] %}\n ---^-------"}, InputOutputPair{"{% set id=10%}\n{% endset %}", "noname.j2tpl:2:4: error: Unexpected statement: 'endset'\n{% endset %}\n---^-------"}, InputOutputPair{"{% extends %}", diff --git a/test/expressions_test.cpp b/test/expressions_test.cpp index 33f044a..dadbdc4 100644 --- a/test/expressions_test.cpp +++ b/test/expressions_test.cpp @@ -285,6 +285,8 @@ INSTANTIATE_TEST_SUITE_P(ComplexSubscriptionTest, ExpressionSubstitutionTest, :: InputOutputPair{"mapValue.reflectedList[1]['intValue']", "1"}, InputOutputPair{"mapValue['reflectedList'][1]['intValue']", "1"}, InputOutputPair{"mapValue.reflectedList[1].intValue", "1"}, + InputOutputPair{"{'fieldName':'field', 'fieldValue':10}.fieldName", "field"}, + InputOutputPair{"{stringValue:'field'}.rain", "field"}, InputOutputPair{"{'fieldName'='field', 'fieldValue'=10}.fieldName", "field"}, InputOutputPair{"{'fieldName'='field', 'fieldValue'=10}['fieldValue']", "10"}, InputOutputPair{R"( ([