Skip to content

Commit

Permalink
Implement 'for' loops with 'if' condition
Browse files Browse the repository at this point in the history
  • Loading branch information
flexferrum committed Jul 5, 2018
1 parent 3f4c4b6 commit 3a593f5
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 10 deletions.
15 changes: 9 additions & 6 deletions src/expression_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ RendererPtr ExpressionParser::Parse(LexScanner& lexer)
return result;
}

ExpressionEvaluatorPtr<FullExpressionEvaluator> ExpressionParser::ParseFullExpression(LexScanner &lexer)
ExpressionEvaluatorPtr<FullExpressionEvaluator> ExpressionParser::ParseFullExpression(LexScanner &lexer, bool includeIfPart)
{
ExpressionEvaluatorPtr<FullExpressionEvaluator> result;
LexScanner::StateSaver saver(lexer);
Expand All @@ -45,11 +45,14 @@ ExpressionEvaluatorPtr<FullExpressionEvaluator> ExpressionParser::ParseFullExpre
ExpressionEvaluatorPtr<IfExpression> ifExpr;
if (lexer.PeekNextToken() == Token::If)
{
lexer.EatToken();
ifExpr = ParseIfExpression(lexer);
if (!ifExpr)
return result;
evaluator->SetTester(ifExpr);
if (includeIfPart)
{
lexer.EatToken();
ifExpr = ParseIfExpression(lexer);
if (!ifExpr)
return result;
evaluator->SetTester(ifExpr);
}
}

saver.Commit();
Expand Down
2 changes: 1 addition & 1 deletion src/expression_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class ExpressionParser
public:
ExpressionParser();
RendererPtr Parse(LexScanner& lexer);
ExpressionEvaluatorPtr<FullExpressionEvaluator> ParseFullExpression(LexScanner& lexer);
ExpressionEvaluatorPtr<FullExpressionEvaluator> ParseFullExpression(LexScanner& lexer, bool includeIfPart = true);
private:
ExpressionEvaluatorPtr<Expression> ParseLogicalNot(LexScanner& lexer);
ExpressionEvaluatorPtr<Expression> ParseLogicalOr(LexScanner& lexer);
Expand Down
5 changes: 5 additions & 0 deletions src/internal_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,18 @@ class ListAdapter
public:
ListAdapter() {}
explicit ListAdapter(ListAccessorProvider prov) : m_accessorProvider(std::move(prov)) {}
ListAdapter(const ListAdapter&) = default;
ListAdapter(ListAdapter&&) = default;

static ListAdapter CreateAdapter(InternalValueList&& values);
static ListAdapter CreateAdapter(const GenericList& values);
static ListAdapter CreateAdapter(const ValuesList& values);
static ListAdapter CreateAdapter(GenericList&& values);
static ListAdapter CreateAdapter(ValuesList&& values);

ListAdapter& operator = (const ListAdapter&) = default;
ListAdapter& operator = (ListAdapter&&) = default;

size_t GetSize() const
{
if (m_accessorProvider && m_accessorProvider())
Expand Down
22 changes: 22 additions & 0 deletions src/statements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,28 @@ void ForStatement::Render(OutStream& os, RenderContext& values)
if (!isConverted)
return;

if (m_ifExpr)
{
auto& tempContext = values.EnterScope();
InternalValueList newLoopItems;
for (auto& curValue : loopItems)
{
if (m_vars.size() > 1)
{
for (auto& varName : m_vars)
tempContext[varName] = Subscript(curValue, varName);
}
else
tempContext[m_vars[0]] = curValue;

if (ConvertToBool(m_ifExpr->Evaluate(values)))
newLoopItems.push_back(curValue);
}
values.ExitScope();

loopItems = ListAdapter::CreateAdapter(std::move(newLoopItems));
}

int64_t itemsNum = static_cast<int64_t>(loopItems.GetSize());
loopVar["length"] = InternalValue(itemsNum);
bool loopRendered = false;
Expand Down
4 changes: 3 additions & 1 deletion src/statements.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ using StatementPtr = std::shared_ptr<T>;
class ForStatement : public Statement
{
public:
ForStatement(std::vector<std::string> vars, ExpressionEvaluatorPtr<> expr)
ForStatement(std::vector<std::string> vars, ExpressionEvaluatorPtr<> expr, ExpressionEvaluatorPtr<> ifExpr)
: m_vars(std::move(vars))
, m_value(expr)
, m_ifExpr(ifExpr)
{
}

Expand All @@ -40,6 +41,7 @@ class ForStatement : public Statement
private:
std::vector<std::string> m_vars;
ExpressionEvaluatorPtr<> m_value;
ExpressionEvaluatorPtr<> m_ifExpr;
RendererPtr m_mainBody;
RendererPtr m_elseBody;
};
Expand Down
8 changes: 6 additions & 2 deletions src/template_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,15 @@ bool StatementsParser::ParseFor(LexScanner& lexer, StatementInfoList& statements
return false;

ExpressionParser exprPraser;
auto valueExpr = exprPraser.ParseFullExpression(lexer);
auto valueExpr = exprPraser.ParseFullExpression(lexer, false);
if (!valueExpr)
return false;

auto renderer = std::make_shared<ForStatement>(vars, valueExpr);
ExpressionEvaluatorPtr<> ifExpr;
if (lexer.EatIfEqual(Token::If))
ifExpr = exprPraser.ParseFullExpression(lexer, false);

auto renderer = std::make_shared<ForStatement>(vars, valueExpr, ifExpr);
StatementInfo statementInfo = StatementInfo::Create(StatementInfo::ForStatement, pos);
statementInfo.renderer = renderer;
statementsInfo.push_back(statementInfo);
Expand Down
51 changes: 51 additions & 0 deletions test/forloop_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,57 @@ a[4] = image[b];
EXPECT_EQ(expectedResult, result);
}

TEST(ForLoopTest, LoopWithIf)
{
std::string source = R"(
{% for i in range(10) if i is even %}
a[{{i}}] = image[{{i}}];
{% endfor %}
)";

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

ValuesMap params = {
};

std::string result = tpl.RenderAsString(params);
std::cout << result << std::endl;
std::string expectedResult = R"(
a[0] = image[0];
a[2] = image[2];
a[4] = image[4];
a[6] = image[6];
a[8] = image[8];
)";
EXPECT_EQ(expectedResult, result);
}

TEST(ForLoopTest, LoopVariableWithIf)
{
std::string source = R"(
{% for i in its if i is even%}
{{i}} length={{loop.length}}, index={{loop.index}}, index0={{loop.index0}}, first={{loop.first}}, last={{loop.last}}, previtem={{loop.previtem}}, nextitem={{loop.nextitem}};
{% endfor %}
)";

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

ValuesMap params = {
{"its", ValuesList{0, 1, 2, 3, 4} }
};

std::string result = mytemplate.RenderAsString(params);
std::cout << result << std::endl;
std::string expectedResult = R"(
0 length=3, index=1, index0=0, first=true, last=false, previtem=, nextitem=2;
2 length=3, index=2, index0=1, first=false, last=false, previtem=0, nextitem=4;
4 length=3, index=3, index0=2, first=false, last=true, previtem=2, nextitem=;
)";
EXPECT_EQ(expectedResult, result);
}

TEST(ForLoopTest, LoopVariable)
{
std::string source = R"(
Expand Down

0 comments on commit 3a593f5

Please sign in to comment.