8000 Adds block statement "set". Resolves #45. · jinja2cpp/Jinja2Cpp@6e49e0c · GitHub
[go: up one dir, main page]

Skip to content

Commit 6e49e0c

Browse files
committed
Adds block statement "set". Resolves #45.
1 parent f826cec commit 6e49e0c

File tree

7 files changed

+226
-47
lines changed

7 files changed

+226
-47
lines changed

src/internal_value.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ struct SubscriptionVisitor : public visitors::BaseVisitor<>
3434
return values.GetValueByName(field);
3535
}
3636

37+
template<typename CharT>
38+
InternalValue operator() (std::basic_string<CharT> value, const std::basic_string<CharT>& /*fieldName*/) const
39+
{
40+
return TargetString(std::move(value));
41+
}
42+
3743
InternalValue operator() (const ListAdapter& values, int64_t index) const
3844
{
3945
if (index < 0 || static_cast<size_t>(index) >= values.GetSize())

src/lexer.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,7 @@ class LexScanner
348348

349349
static const Token& EofToken()
350350
{
351-
static Token eof;
352-
eof.type = Token::Eof;
351+
static const Token eof{Token::Eof};
353352
return eof;
354353
}
355354
};

src/statements.cpp

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -211,19 +211,44 @@ void ElseBranchStatement::Render(OutStream& os, RenderContext& values)
211211
m_mainBody->Render(os, values);
212212
}
213213

214-
void SetStatement::Render(OutStream&, RenderContext& values)
215-
{
216-
if (m_expr)
217-
{
218-
InternalValue val = m_expr->Evaluate(values);
219-
if (m_fields.size() == 1)
220-
values.GetCurrentScope()[m_fields[0]] = val;
221-
else
222-
{
223-
for (auto& name : m_fields)
224-
values.GetCurrentScope()[name] = Subscript(val, name, &values);
225-
}
226-
}
214+
void SetStatement::AssingBody(InternalValue body, RenderContext& values)
215+
{
216+
auto &scope = values.GetCurrentScope();
217+
if (m_fields.size() == 1)
218+
scope[m_fields.front()] = body;
219+
else
220+
{
221+
for (const auto& name : m_fields)
222+
scope[name] = Subscript(body, name, &values);
223+
}
224+
}
225+
226+
void SetLineStatement::Render(OutStream&, RenderContext& values)
227+
{
228+
if (!m_expr)
229+
return;
230+
AssingBody(m_expr->Evaluate(values), values);
231+
}
232+
233+
InternalValue SetBlockStatement::RenderBody(RenderContext& values)
234+
{
235+
TargetString result;
236+
auto stream = values.GetRendererCallback()->GetStreamOnString(result);
237+
auto innerValues = values.Clone(true);
238+
m_body->Render(stream, innerValues);
239+
return result;
240+
}
241+
242+
void SetRawBlockStatement::Render(OutStream&, RenderContext& values)
243+
{
244+
AssingBody(RenderBody(values), values);
245+
}
246+
247+
void SetFilteredBlockStatement::Render(OutStream&, RenderContext& values)
248+
{
249+
if (!m_expr)
250+
return;
251+
AssingBody(m_expr->Evaluate(RenderBody(values), values), values);
227252
}
228253

229254
class BlocksRenderer : public RendererBase
@@ -740,7 +765,7 @@ void FilterStatement::Render(OutStream& os, RenderContext& values)
740765
auto argStream = values.GetRendererCallback()->GetStreamOnString(arg);
741766
auto innerValues = values.Clone(true);
742767
m_body->Render(argStream, innerValues);
743-
const auto result = m_expr->Evaluate(arg, values);
768+
const auto result = m_expr->Evaluate(std::move(arg), values);
744769
os.WriteValue(result);
745770
}
746771
} // jinja2

src/statements.h

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,22 +124,75 @@ class ElseBranchStatement : public Statement
124124
class SetStatement : public Statement
125125
{
126126
public:
127-
VISITABLE_STATEMENT();
128-
129127
SetStatement(std::vector<std::string> fields)
130128
: m_fields(std::move(fields))
131129
{
132130
}
133131

134-
void SetAssignmentExpr(ExpressionEvaluatorPtr<> expr)
132+
protected:
133+
void AssingBody(InternalValue, RenderContext&);
134+
135+
private:
136+
const std::vector<std::string> m_fields;
137+
};
138+
139+
class SetLineStatement final : public SetStatement
140+
{
141+
public:
142+
VISITABLE_STATEMENT();
143+
144+
SetLineStatement(std::vector<std::string> fields, ExpressionEvaluatorPtr<> expr)
145+
: SetStatement(std::move(fields)), m_expr(std::move(expr))
135146
{
136-
m_expr = std::move(expr);
137147
}
148+
138149
void Render(OutStream& os, RenderContext& values) override;
139150

140151
private:
141-
std::vector<std::string> m_fields;
142-
ExpressionEvaluatorPtr<> m_expr;
152+
const ExpressionEvaluatorPtr<> m_expr;
153+
};
154+
155+
class SetBlockStatement : public SetStatement
156+
{
157+
public:
158+
using SetStatement::SetStatement;
159+
160+
void SetBody(RendererPtr renderer)
161+
{
162+
m_body = std::move(renderer);
163+
}
164+
165+
protected:
166+
InternalValue RenderBody(RenderContext&);
167+
168+
private:
169+
RendererPtr m_body;
170+
};
171+
172+
class SetRawBlockStatement final : public SetBlockStatement
173+
{
174+
public:
175+
VISITABLE_STATEMENT();
176+
177+
using SetBlockStatement::SetBlockStatement;
178+
179+
void Render(OutStream&, RenderContext&) override;
180+
};
181+
182+
class SetFilteredBlockStatement final : public SetBlockStatement
183+
{
184+
public:
185+
VISITABLE_STATEMENT();
186+
187+
explicit SetFilteredBlockStatement(std::vector<std::string> fields, ExpressionEvaluatorPtr<ExpressionFilter> expr)
188+
: SetBlockStatement(std::move(fields)), m_expr(std::move(expr))
189+
{
190+
}
191+
192+
void Render(OutStream&, RenderContext&) override;
193+
194+
private:
195+
const ExpressionEvaluatorPtr<ExpressionFilter> m_expr;
143196
};
144197

145198
class ParentBlockStatement : public Statement

src/template_parser.cpp

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ StatementsParser::ParseResult StatementsParser::Parse(LexScanner& lexer, Stateme
3232
case Keyword::Set:
3333
result = ParseSet(lexer, statementsInfo, tok);
3434
break;
35+
case Keyword::EndSet:
36+
result = ParseEndSet(lexer, statementsInfo, tok);
37+
break;
3538
case Keyword::Block:
3639
result = ParseBlock(lexer, statementsInfo, tok);
3740
break;
@@ -79,8 +82,6 @@ StatementsParser::ParseResult StatementsParser::Parse(LexScanner& lexer, Stateme
7982
case Keyword::EndFilter:
8083
result = ParseEndFilter(lexer, statementsInfo, tok);
8184
break;
82-
case Keyword::EndSet:
83-
return MakeParseError(ErrorCode::YetUnsupported, tok);
8485
default:
8586
return MakeParseError(ErrorCode::UnexpectedToken, tok);
8687
}
@@ -320,30 +321,60 @@ StatementsParser::ParseResult StatementsParser::ParseSet(LexScanner& lexer, Stat
320321
if (vars.empty())
321322
return MakeParseError(ErrorCode::ExpectedIdentifier, lexer.PeekNextToken());
322323

323-
auto operTok = lexer.NextToken();
324-
ExpressionEvaluatorPtr<> valueExpr;
325-
if (operTok == '=')
324+
ExpressionParser exprParser(m_settings);
325+
if (lexer.EatIfEqual('='))
326326
{
327-
ExpressionParser exprParser(m_settings);
328-
auto expr = exprParser.ParseFullExpression(lexer);
327+
const auto expr = exprParser.ParseFullExpression(lexer);
329328
if (!expr)
330329
return expr.get_unexpected();
331-
valueExpr = *expr;
330+
statementsInfo.back().currentComposition->AddRenderer(
331+
std::make_shared<SetLineStatement>(std::move(vars), *expr));
332+
}
333+
else if (lexer.EatIfEqual('|'))
334+
{
335+
const auto expr = exprParser.ParseFilterExpression(lexer);
336+
if (!expr)
337+
return expr.get_unexpected();
338+
auto statementInfo = StatementInfo::Create(
339+
StatementInfo::SetStatement, stmtTok);
340+
statementInfo.renderer = std::make_shared<SetFilteredBlockStatement>(
341+
std::move(vars), *expr);
342+
statementsInfo.push_back(std::move(statementInfo));
332343
}
333344
else
334-
return MakeParseError(ErrorCode::YetUnsupported, operTok, {stmtTok}); // TODO: Add handling of the block assignments
335-
336 5279 -
auto renderer = std::make_shared<SetStatement>(vars);
337-
renderer->SetAssignmentExpr(valueExpr);
338-
statementsInfo.back().currentComposition->AddRenderer(renderer);
345+
{
346+
auto operTok = lexer.NextToken();
347+
if (lexer.NextToken() != Token::Eof)
348+
return MakeParseError(ErrorCode::YetUnsupported, operTok, {std::move(stmtTok)});
349+
auto statementInfo = StatementInfo::Create(
350+
StatementInfo::SetStatement, stmtTok);
351+
statementInfo.renderer = std::make_shared<SetRawBlockStatement>(
352+
std::move(vars));
353+
statementsInfo.push_back(std::move(statementInfo));
354+
}
339355

340-
return ParseResult();
356+
return {};
341357
}
342358

343-
StatementsParser::ParseResult StatementsParser::ParseEndSet(LexScanner& /*lexer*/, StatementInfoList& /*statementsInfo*/
359+
StatementsParser::ParseResult StatementsParser::ParseEndSet(LexScanner&
360+
, StatementInfoList& statementsInfo
344361
, const Token& stmtTok)
345362
{
346-
return MakeParseError(ErrorCode::YetUnsupported, stmtTok);
363+
if (statementsInfo.size() <= 1)
364+
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
365+
366+
const auto info = statementsInfo.back();
367+
if (info.type != StatementInfo::SetStatement)
368+
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
369+
370+
auto &renderer = *boost::polymorphic_downcast<SetBlockStatement*>(
371+
info.renderer.get());
372+
renderer.SetBody(info.compositions[0]);
373+
374+
statementsInfo.pop_back();
375+
statementsInfo.back().currentComposition->AddRenderer(info.renderer);
376+
377+
return {};
347378
}
348379

349380
StatementsParser::ParseResult StatementsParser::ParseBlock(LexScanner& lexer, StatementInfoList& statementsInfo
@@ -915,10 +946,10 @@ StatementsParser::ParseResult StatementsParser::ParseFilter(LexScanner& lexer, S
915946

916947
StatementsParser::ParseResult StatementsParser::ParseEndFilter(LexScanner&, StatementInfoList& statementsInfo, const Token& stmtTok)
917948
{
918-
if (statementsInfo.size() <= 1)
949+
if (statementsInfo.size() <= 1)
919950
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
920951

921-
auto info = statementsInfo.back();
952+
const auto info = statementsInfo.back();
922953
if (info.type != StatementInfo::FilterStatement)
923954
{
924955
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);

test/errors_test.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -380,14 +380,12 @@ INSTANTIATE_TEST_CASE_P(StatementsTest_1, ErrorsGenericTest, ::testing::Values(
380380
"noname.j2tpl:2:4: error: Unexpected statement: 'endfor'\n{% endfor %}\n---^-------"},
381381
InputOutputPair{"{% set %}",
382382
"noname.j2tpl:1:8: error: Identifier expected\n{% set %}\n ---^-------"},
383-
InputOutputPair{"{% set id%}",
384-
"noname.j2tpl:1:10: error: This feature has not been supported yet\n{% set id%}\n ---^-------"},
385383
InputOutputPair{"{% set 10%}",
386384
"noname.j2tpl:1:8: error: Identifier expected\n{% set 10%}\n ---^-------"},
387385
InputOutputPair{"{% set i = {key] %}",
388386
"noname.j2tpl:1:13: error: String expected\n{% set i = {key] %}\n ---^-------"},
389387
InputOutputPair{"{% set id=10%}\n{% endset %}",
390-
"noname.j2tpl:2:4: error: This feature has not been supported yet\n{% endset %}\n---^-------"},
388+
"noname.j2tpl:2:4: error: Unexpected statement: 'endset'\n{% endset %}\n---^-------"},
391389
InputOutputPair{"{% extends %}",
392390
"noname.j2tpl:1:12: error: Unexpected token '<<End of block>>'. Expected: '<<Identifier>>', '<<String>>'\n{% extends %}\n ---^-------"},
393391
InputOutputPair{"{% extends 10 %}",

test/statements_tets.cpp

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,14 +202,13 @@ TEST(FilterStatement, General)
202202
ASSERT_TRUE(tpl.Load(source));
203203

204204
const auto result = tpl.RenderAsString({}).value();
205-
std::cout << result << std::endl;
206205
EXPECT_STREQ("\n THIS TEXT BECOMES UPPERCASE\n", result.c_str());
207206
}
208207

209208
TEST(FilterStatement, ChainAndParams)
210209
{
211210
const std::string source = R"(
212-
{% filter list | sort(reverse=true) | unique | join("+") %}
211+
{% filter trim | list | sort(reverse=true) | unique | join("+") %}
213212
11222333445556677890
214213
{% endfilter %}
215214
)";
@@ -218,6 +217,74 @@ TEST(FilterStatement, ChainAndParams)
218217
ASSERT_TRUE(tpl.Load(source));
219218

220219
const auto result = tpl.RenderAsString({}).value();
221-
std::cout << result << std::endl;
222-
EXPECT_STREQ("\n9+8+7+6+5+4+3+2+1+0+\n", result.c_str());
220+
EXPECT_STREQ("\n9+8+7+6+5+4+3+2+1+0", result.c_str());
223221
}
222+
223+
TEST(SetBlockStatement, OneVar)
224+
{
225+
const std::string source = R"(
226+
{% set foo %}
227+
11222333445556677890
228+
{% endset %}
229+
|{{foo}}|
230+
)";
231+
232+
Template tpl;
233+
ASSERT_TRUE(tpl.Load(source));
234+
235+
const auto result = tpl.RenderAsString({}).value();
236+
EXPECT_STREQ("\n|11222333445556677890\n|\n", result.c_str());
237+
}
238+
239+
TEST(SetBlockStatement, MoreVars)
240+
{
241+
const std::string source = R"(
242+
{% set foo1,foo2,foo3,foo4,foo5 %}
243+
11222333445556677890
244+
{% endset %}
245+
|{{foo1}}|
246+
|{{foo2}}|
247+
|{{foo5}}|
248+
)";
249+
250+
Template tpl;
251+
ASSERT_TRUE(tpl.Load(source));
252+
253+
const auto result = tpl.RenderAsString({}).value();
254+
EXPECT_STREQ("\n|11222333445556677890\n|\n|11222333445556677890\n|\n|11222333445556677890\n|\n", result.c_str());
255+
}
256+
257+
TEST(SetBlockStatement, OneVarFiltered)
258+
{
259+
const std::string source = R"(
260+
{% set foo | trim | list | sort(reverse=true) | unique | join("+") %}
261+
11222333445556677890
262+
{% endset %}
263+
|{{foo}}|
264+
)";
265+
266+
Template tpl;
267+
const auto load = tpl.Load(source);
268+
ASSERT_TRUE(load) << load.error();
269+
270+
const auto result = tpl.RenderAsString({}).value();
271+
EXPECT_STREQ("\n|9+8+7+6+5+4+3+2+1+0|\n", result.c_str());
272+
}
273+
274+
TEST(SetBlockStatement, MoreVarsFiltered)
275+
{
276+
const std::string source = R"(
277+
{% set foo1,foo2,foo3,foo4,foo5 | trim | list | sort(reverse=true) | unique | join("+") %}
278+
11222333445556677890
279+
{% endset %}
280+
|{{foo1}}|
281+
|{{foo2}}|
282+
|{{foo5}}|
283+
)";
284+
285+
Template tpl;
286+
ASSERT_TRUE(tpl.Load(source));
287+
288+
const auto result = tpl.RenderAsString({}).value();
289+
EXPECT_STREQ("\n|9+8+7+6+5+4+3+2+1+0|\n|9+8+7+6+5+4+3+2+1+0|\n|9+8+7+6+5+4+3+2+1+0|\n", result.c_str());
290+
}

0 commit comments

Comments
 (0)
0