Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/internal_value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ struct SubscriptionVisitor : public visitors::BaseVisitor<>
return values.GetValueByName(field);
}

template<typename CharT>
InternalValue operator() (std::basic_string<CharT> value, const std::basic_string<CharT>& /*fieldName*/) const
{
return TargetString(std::move(value));
}

InternalValue operator() (const ListAdapter& values, int64_t index) const
{
if (index < 0 || static_cast<size_t>(index) >= values.GetSize())
Expand Down
53 changes: 39 additions & 14 deletions src/statements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,19 +211,44 @@ void ElseBranchStatement::Render(OutStream& os, RenderContext& values)
m_mainBody->Render(os, values);
}

void SetStatement::Render(OutStream&, RenderContext& values)
{
if (m_expr)
{
InternalValue val = m_expr->Evaluate(values);
if (m_fields.size() == 1)
values.GetCurrentScope()[m_fields[0]] = val;
else
{
for (auto& name : m_fields)
values.GetCurrentScope()[name] = Subscript(val, name, &values);
}
}
void SetStatement::AssignBody(InternalValue body, RenderContext& values)
{
auto &scope = values.GetCurrentScope();
if (m_fields.size() == 1)
scope[m_fields.front()] = std::move(body);
else
{
for (const auto& name : m_fields)
scope[name] = Subscript(body, name, &values);
}
}

void SetLineStatement::Render(OutStream&, RenderContext& values)
{
if (!m_expr)
return;
AssignBody(m_expr->Evaluate(values), values);
}

InternalValue SetBlockStatement::RenderBody(RenderContext& values)
{
TargetString result;
auto stream = values.GetRendererCallback()->GetStreamOnString(result);
auto innerValues = values.Clone(true);
m_body->Render(stream, innerValues);
return result;
}

void SetRawBlockStatement::Render(OutStream&, RenderContext& values)
{
AssignBody(RenderBody(values), values);
}

void SetFilteredBlockStatement::Render(OutStream&, RenderContext& values)
{
if (!m_expr)
return;
AssignBody(m_expr->Evaluate(RenderBody(values), values), values);
}

class BlocksRenderer : public RendererBase
Expand Down Expand Up @@ -740,7 +765,7 @@ void FilterStatement::Render(OutStream& os, RenderContext& values)
auto argStream = values.GetRendererCallback()->GetStreamOnString(arg);
auto innerValues = values.Clone(true);
m_body->Render(argStream, innerValues);
const auto result = m_expr->Evaluate(arg, values);
const auto result = m_expr->Evaluate(std::move(arg), values);
os.WriteValue(result);
}
} // jinja2
65 changes: 59 additions & 6 deletions src/statements.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,22 +124,75 @@ class ElseBranchStatement : public Statement
class SetStatement : public Statement
{
public:
VISITABLE_STATEMENT();

SetStatement(std::vector<std::string> fields)
: m_fields(std::move(fields))
{
}

void SetAssignmentExpr(ExpressionEvaluatorPtr<> expr)
protected:
void AssignBody(InternalValue, RenderContext&);

private:
const std::vector<std::string> m_fields;
};

class SetLineStatement final : public SetStatement
{
public:
VISITABLE_STATEMENT();

SetLineStatement(std::vector<std::string> fields, ExpressionEvaluatorPtr<> expr)
: SetStatement(std::move(fields)), m_expr(std::move(expr))
{
m_expr = std::move(expr);
}

void Render(OutStream& os, RenderContext& values) override;

private:
std::vector<std::string> m_fields;
ExpressionEvaluatorPtr<> m_expr;
const ExpressionEvaluatorPtr<> m_expr;
};

class SetBlockStatement : public SetStatement
{
public:
using SetStatement::SetStatement;

void SetBody(RendererPtr renderer)
{
m_body = std::move(renderer);
}

protected:
InternalValue RenderBody(RenderContext&);

private:
RendererPtr m_body;
};

class SetRawBlockStatement final : public SetBlockStatement
{
public:
VISITABLE_STATEMENT();

using SetBlockStatement::SetBlockStatement;

void Render(OutStream&, RenderContext&) override;
};

class SetFilteredBlockStatement final : public SetBlockStatement
{
public:
VISITABLE_STATEMENT();

explicit SetFilteredBlockStatement(std::vector<std::string> fields, ExpressionEvaluatorPtr<ExpressionFilter> expr)
: SetBlockStatement(std::move(fields)), m_expr(std::move(expr))
{
}

void Render(OutStream&, RenderContext&) override;

private:
const ExpressionEvaluatorPtr<ExpressionFilter> m_expr;
};

class ParentBlockStatement : public Statement
Expand Down
67 changes: 49 additions & 18 deletions src/template_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ StatementsParser::ParseResult StatementsParser::Parse(LexScanner& lexer, Stateme
case Keyword::Set:
result = ParseSet(lexer, statementsInfo, tok);
break;
case Keyword::EndSet:
result = ParseEndSet(lexer, statementsInfo, tok);
break;
case Keyword::Block:
result = ParseBlock(lexer, statementsInfo, tok);
break;
Expand Down Expand Up @@ -79,8 +82,6 @@ StatementsParser::ParseResult StatementsParser::Parse(LexScanner& lexer, Stateme
case Keyword::EndFilter:
result = ParseEndFilter(lexer, statementsInfo, tok);
break;
case Keyword::EndSet:
return MakeParseError(ErrorCode::YetUnsupported, tok);
default:
return MakeParseError(ErrorCode::UnexpectedToken, tok);
}
Expand Down Expand Up @@ -320,30 +321,60 @@ StatementsParser::ParseResult StatementsParser::ParseSet(LexScanner& lexer, Stat
if (vars.empty())
return MakeParseError(ErrorCode::ExpectedIdentifier, lexer.PeekNextToken());

auto operTok = lexer.NextToken();
ExpressionEvaluatorPtr<> valueExpr;
if (operTok == '=')
ExpressionParser exprParser(m_settings);
if (lexer.EatIfEqual('='))
{
ExpressionParser exprParser(m_settings);
auto expr = exprParser.ParseFullExpression(lexer);
const auto expr = exprParser.ParseFullExpression(lexer);
if (!expr)
return expr.get_unexpected();
valueExpr = *expr;
statementsInfo.back().currentComposition->AddRenderer(
std::make_shared<SetLineStatement>(std::move(vars), *expr));
}
else if (lexer.EatIfEqual('|'))
{
const auto expr = exprParser.ParseFilterExpression(lexer);
if (!expr)
return expr.get_unexpected();
auto statementInfo = StatementInfo::Create(
StatementInfo::SetStatement, stmtTok);
statementInfo.renderer = std::make_shared<SetFilteredBlockStatement>(
std::move(vars), *expr);
statementsInfo.push_back(std::move(statementInfo));
}
else
return MakeParseError(ErrorCode::YetUnsupported, operTok, {stmtTok}); // TODO: Add handling of the block assignments

auto renderer = std::make_shared<SetStatement>(vars);
renderer->SetAssignmentExpr(valueExpr);
statementsInfo.back().currentComposition->AddRenderer(renderer);
{
auto operTok = lexer.NextToken();
if (lexer.NextToken() != Token::Eof)
return MakeParseError(ErrorCode::YetUnsupported, operTok, {std::move(stmtTok)});
auto statementInfo = StatementInfo::Create(
StatementInfo::SetStatement, stmtTok);
statementInfo.renderer = std::make_shared<SetRawBlockStatement>(
std::move(vars));
statementsInfo.push_back(std::move(statementInfo));
}

return ParseResult();
return {};
}

StatementsParser::ParseResult StatementsParser::ParseEndSet(LexScanner& /*lexer*/, StatementInfoList& /*statementsInfo*/
StatementsParser::ParseResult StatementsParser::ParseEndSet(LexScanner&
, StatementInfoList& statementsInfo
, const Token& stmtTok)
{
return MakeParseError(ErrorCode::YetUnsupported, stmtTok);
if (statementsInfo.size() <= 1)
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);

const auto info = statementsInfo.back();
if (info.type != StatementInfo::SetStatement)
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);

auto &renderer = *boost::polymorphic_downcast<SetBlockStatement*>(
info.renderer.get());
renderer.SetBody(info.compositions[0]);

statementsInfo.pop_back();
statementsInfo.back().currentComposition->AddRenderer(info.renderer);

return {};
}

StatementsParser::ParseResult StatementsParser::ParseBlock(LexScanner& lexer, StatementInfoList& statementsInfo
Expand Down Expand Up @@ -915,10 +946,10 @@ StatementsParser::ParseResult StatementsParser::ParseFilter(LexScanner& lexer, S

StatementsParser::ParseResult StatementsParser::ParseEndFilter(LexScanner&, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (statementsInfo.size() <= 1)
if (statementsInfo.size() <= 1)
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);

auto info = statementsInfo.back();
const auto info = statementsInfo.back();
if (info.type != StatementInfo::FilterStatement)
{
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
Expand Down
4 changes: 1 addition & 3 deletions test/errors_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,14 +380,12 @@ INSTANTIATE_TEST_CASE_P(StatementsTest_1, ErrorsGenericTest, ::testing::Values(
"noname.j2tpl:2:4: error: Unexpected statement: 'endfor'\n{% endfor %}\n---^-------"},
InputOutputPair{"{% set %}",
"noname.j2tpl:1:8: error: Identifier expected\n{% set %}\n ---^-------"},
InputOutputPair{"{% set id%}",
"noname.j2tpl:1:10: error: This feature has not been supported yet\n{% set id%}\n ---^-------"},
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 ---^-------"},
InputOutputPair{"{% set id=10%}\n{% endset %}",
"noname.j2tpl:2:4: error: This feature has not been supported yet\n{% endset %}\n---^-------"},
"noname.j2tpl:2:4: error: Unexpected statement: 'endset'\n{% endset %}\n---^-------"},
InputOutputPair{"{% extends %}",
"noname.j2tpl:1:12: error: Unexpected token '<<End of block>>'. Expected: '<<Identifier>>', '<<String>>'\n{% extends %}\n ---^-------"},
InputOutputPair{"{% extends 10 %}",
Expand Down
75 changes: 71 additions & 4 deletions test/statements_tets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,14 +202,13 @@ TEST(FilterStatement, General)
ASSERT_TRUE(tpl.Load(source));

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

TEST(FilterStatement, ChainAndParams)
{
const std::string source = R"(
{% filter list | sort(reverse=true) | unique | join("+") %}
{% filter trim | list | sort(reverse=true) | unique | join("+") %}
11222333445556677890
{% endfilter %}
)";
Expand All @@ -218,6 +217,74 @@ TEST(FilterStatement, ChainAndParams)
ASSERT_TRUE(tpl.Load(source));

const auto result = tpl.RenderAsString({}).value();
std::cout << result << std::endl;
EXPECT_STREQ("\n9+8+7+6+5+4+3+2+1+0+\n", result.c_str());
EXPECT_STREQ("\n9+8+7+6+5+4+3+2+1+0", result.c_str());
}

TEST(SetBlockStatement, OneVar)
{
const std::string source = R"(
{% set foo %}
11222333445556677890
{% endset %}
|{{foo}}|
)";

Template tpl;
ASSERT_TRUE(tpl.Load(source));

const auto result = tpl.RenderAsString({}).value();
EXPECT_STREQ("\n|11222333445556677890\n|\n", result.c_str());
}

TEST(SetBlockStatement, MoreVars)
{
const std::string source = R"(
{% set foo1,foo2,foo3,foo4,foo5 %}
11222333445556677890
{% endset %}
|{{foo1}}|
|{{foo2}}|
|{{foo5}}|
)";

Template tpl;
ASSERT_TRUE(tpl.Load(source));

const auto result = tpl.RenderAsString({}).value();
EXPECT_STREQ("\n|11222333445556677890\n|\n|11222333445556677890\n|\n|11222333445556677890\n|\n", result.c_str());
}

TEST(SetBlockStatement, OneVarFiltered)
{
const std::string source = R"(
{% set foo | trim | list | sort(reverse=true) | unique | join("+") %}
11222333445556677890
{% endset %}
|{{foo}}|
)";

Template tpl;
const auto load = tpl.Load(source);
ASSERT_TRUE(load) << load.error();

const auto result = tpl.RenderAsString({}).value();
EXPECT_STREQ("\n|9+8+7+6+5+4+3+2+1+0|\n", result.c_str());
}

TEST(SetBlockStatement, MoreVarsFiltered)
{
const std::string source = R"(
{% set foo1,foo2,foo3,foo4,foo5 | trim | list | sort(reverse=true) | unique | join("+") %}
11222333445556677890
{% endset %}
|{{foo1}}|
|{{foo2}}|
|{{foo5}}|
)";

Template tpl;
ASSERT_TRUE(tpl.Load(source));

const auto result = tpl.RenderAsString({}).value();
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());
}