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
1 change: 1 addition & 0 deletions include/jinja2cpp/error_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ enum class ErrorCode
TemplateNotFound,
TemplateNotParsed,
InvalidValueType,
ExtensionDisabled,
};

struct SourceLocation
Expand Down
13 changes: 13 additions & 0 deletions include/jinja2cpp/template_env.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,24 @@ namespace jinja2
class IErrorHandler;
class IFilesystemHandler;

enum class Jinja2CompatMode
{
None,
Vesrsion_2_10,
};

struct Settings
{
struct Extensions
{
bool Do = false;
};

bool useLineStatements = false;
bool trimBlocks = false;
bool lstripBlocks = false;
Extensions extensions;
Jinja2CompatMode jinja2CompatMode = Jinja2CompatMode::None;
};

class TemplateEnv
Expand Down
3 changes: 3 additions & 0 deletions src/error_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@ void RenderErrorInfo(std::basic_ostream<CharT>& os, const ErrorInfoTpl<CharT>& e
case ErrorCode::InvalidValueType:
os << UNIVERSAL_STR("Invalid value type");
break;
case ErrorCode::ExtensionDisabled:
os << UNIVERSAL_STR("Extension disabled");
break;
}
os << std::endl << errInfo.GetLocationDescr();
}
Expand Down
3 changes: 2 additions & 1 deletion src/expression_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <sstream>
#include <unordered_set>
#include <jinja2cpp/template_env.h>

namespace jinja2
{
Expand All @@ -16,7 +17,7 @@ auto ReplaceErrorIfPossible(T& result, const Token& pivotTok, ErrorCode newError
return result.get_unexpected();
}

ExpressionParser::ExpressionParser()
ExpressionParser::ExpressionParser(const Settings& /* settings */)
{

}
Expand Down
3 changes: 2 additions & 1 deletion src/expression_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "renderer.h"

#include <nonstd/expected.hpp>
#include <jinja2cpp/template_env.h>

namespace jinja2
{
Expand All @@ -16,7 +17,7 @@ class ExpressionParser
template<typename T>
using ParseResult = nonstd::expected<T, ParseError>;

ExpressionParser();
ExpressionParser(const Settings& settings);
ParseResult<RendererPtr> Parse(LexScanner& lexer);
ParseResult<ExpressionEvaluatorPtr<FullExpressionEvaluator>> ParseFullExpression(LexScanner& lexer, bool includeIfPart = true);
ParseResult<CallParams> ParseCallParams(LexScanner& lexer);
Expand Down
2 changes: 2 additions & 0 deletions src/lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ struct Token
Context,
From,
As,
Do,

// Template control
CommentBegin,
Expand Down Expand Up @@ -169,6 +170,7 @@ enum class Keyword
Context,
From,
As,
Do,
};

struct LexerHelper
Expand Down
4 changes: 4 additions & 0 deletions src/statements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -635,4 +635,8 @@ void MacroCallStatement::SetupMacroScope(InternalValueMap&)

}

void DoStatement::Render(OutStream& os, RenderContext& values)
{
m_expr->Evaluate(values);
}
} // jinja2
13 changes: 13 additions & 0 deletions src/statements.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,19 @@ class MacroCallStatement : public MacroStatement
std::string m_macroName;
CallParams m_callParams;
};

class DoStatement : public Statement
{
public:
VISITABLE_STATEMENT();

DoStatement(ExpressionEvaluatorPtr <> expr) : m_expr(expr) {}

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

private:
ExpressionEvaluatorPtr<> m_expr;
};
} // jinja2


Expand Down
38 changes: 29 additions & 9 deletions src/template_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ StatementsParser::ParseResult StatementsParser::Parse(LexScanner& lexer, Stateme
case Keyword::From:
result = ParseFrom(lexer, statementsInfo, tok);
break;
case Keyword::Do:
if (!m_settings.extensions.Do)
return MakeParseError(ErrorCode::ExtensionDisabled, tok);
result = ParseDo(lexer, statementsInfo, tok);
break;
case Keyword::Filter:
case Keyword::EndFilter:
case Keyword::EndSet:
Expand Down Expand Up @@ -142,7 +147,7 @@ StatementsParser::ParseResult StatementsParser::ParseFor(LexScanner &lexer, Stat
}

auto pivotToken = lexer.PeekNextToken();
ExpressionParser exprPraser;
ExpressionParser exprPraser(m_settings);
auto valueExpr = exprPraser.ParseFullExpression(lexer, false);
if (!valueExpr)
return valueExpr.get_unexpected();
Expand Down Expand Up @@ -213,7 +218,7 @@ StatementsParser::ParseResult StatementsParser::ParseIf(LexScanner &lexer, State
const Token &stmtTok)
{
auto pivotTok = lexer.PeekNextToken();
ExpressionParser exprParser;
ExpressionParser exprParser(m_settings);
auto valueExpr = exprParser.ParseFullExpression(lexer);
if (!valueExpr)
return MakeParseError(ErrorCode::ExpectedExpression, pivotTok);
Expand All @@ -239,7 +244,7 @@ StatementsParser::ParseResult StatementsParser::ParseElIf(LexScanner& lexer, Sta
, const Token& stmtTok)
{
auto pivotTok = lexer.PeekNextToken();
ExpressionParser exprParser;
ExpressionParser exprParser(m_settings);
auto valueExpr = exprParser.ParseFullExpression(lexer);
if (!valueExpr)
return MakeParseError(ErrorCode::ExpectedExpression, pivotTok);
Expand Down Expand Up @@ -309,7 +314,7 @@ StatementsParser::ParseResult StatementsParser::ParseSet(LexScanner& lexer, Stat
ExpressionEvaluatorPtr<> valueExpr;
if (operTok == '=')
{
ExpressionParser exprParser;
ExpressionParser exprParser(m_settings);
auto expr = exprParser.ParseFullExpression(lexer);
if (!expr)
return expr.get_unexpected();
Expand Down Expand Up @@ -478,7 +483,7 @@ nonstd::expected<MacroParams, ParseError> StatementsParser::ParseMacroParams(Lex
if (lexer.EatIfEqual(')'))
return std::move(items);

ExpressionParser exprParser;
ExpressionParser exprParser(m_settings);

do
{
Expand Down Expand Up @@ -562,7 +567,7 @@ StatementsParser::ParseResult StatementsParser::ParseCall(LexScanner& lexer, Sta
CallParams callParams;
if (lexer.EatIfEqual('('))
{
ExpressionParser exprParser;
ExpressionParser exprParser(m_settings);
auto result = exprParser.ParseCallParams(lexer);
if (!result)
return result.get_unexpected();
Expand Down Expand Up @@ -606,7 +611,7 @@ StatementsParser::ParseResult StatementsParser::ParseInclude(LexScanner& lexer,

// auto operTok = lexer.NextToken();
ExpressionEvaluatorPtr<> valueExpr;
ExpressionParser exprParser;
ExpressionParser exprParser(m_settings);
auto expr = exprParser.ParseFullExpression(lexer);
if (!expr)
return expr.get_unexpected();
Expand Down Expand Up @@ -662,7 +667,7 @@ StatementsParser::ParseResult StatementsParser::ParseInclude(LexScanner& lexer,
StatementsParser::ParseResult StatementsParser::ParseImport(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
{
ExpressionEvaluatorPtr<> valueExpr;
ExpressionParser exprParser;
ExpressionParser exprParser(m_settings);
auto expr = exprParser.ParseFullExpression(lexer);
if (!expr)
return expr.get_unexpected();
Expand Down Expand Up @@ -709,7 +714,7 @@ StatementsParser::ParseResult StatementsParser::ParseImport(LexScanner& lexer, S
StatementsParser::ParseResult StatementsParser::ParseFrom(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
{
ExpressionEvaluatorPtr<> valueExpr;
ExpressionParser exprParser;
ExpressionParser exprParser(m_settings);
auto expr = exprParser.ParseFullExpression(lexer);
if (!expr)
return expr.get_unexpected();
Expand Down Expand Up @@ -795,4 +800,19 @@ StatementsParser::ParseResult StatementsParser::ParseFrom(LexScanner& lexer, Sta
return ParseResult();
}

StatementsParser::ParseResult StatementsParser::ParseDo(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
{
ExpressionEvaluatorPtr<> valueExpr;
ExpressionParser exprParser(m_settings);
auto expr = exprParser.ParseFullExpression(lexer);
if (!expr)
return expr.get_unexpected();
valueExpr = *expr;

auto renderer = std::make_shared<DoStatement>(valueExpr);
statementsInfo.back().currentComposition->AddRenderer(renderer);

return jinja2::StatementsParser::ParseResult();
}

}
16 changes: 13 additions & 3 deletions src/template_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ template<typename T = void>
struct ParserTraitsBase
{
static Token::Type s_keywords[];
static KeywordsInfo s_keywordsInfo[39];
static KeywordsInfo s_keywordsInfo[40];
static std::unordered_map<int, MultiStringLiteral> s_tokens;
};

Expand Down Expand Up @@ -194,6 +194,10 @@ class StatementsParser
public:
using ParseResult = nonstd::expected<void, ParseError>;

StatementsParser(const Settings& settings)
: m_settings(settings)
{}

ParseResult Parse(LexScanner& lexer, StatementInfoList& statementsInfo);

private:
Expand All @@ -216,6 +220,10 @@ class StatementsParser
ParseResult ParseInclude(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
ParseResult ParseImport(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
ParseResult ParseFrom(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
ParseResult ParseDo(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);

private:
Settings m_settings;
};

template<typename CharT>
Expand Down Expand Up @@ -532,7 +540,7 @@ class TemplateParser : public LexerHelper
if (!lexer.Preprocess())
return MakeParseError(ErrorCode::Unspecified, MakeToken(Token::Unknown, {range.startOffset, range.startOffset + 1}));

P praser;
P praser(m_settings);
LexScanner scanner(lexer);
auto result = praser.Parse(scanner, std::forward<Args>(args)...);
if (!result)
Expand Down Expand Up @@ -773,7 +781,7 @@ class TemplateParser : public LexerHelper
};

template<typename T>
KeywordsInfo ParserTraitsBase<T>::s_keywordsInfo[39] = {
KeywordsInfo ParserTraitsBase<T>::s_keywordsInfo[40] = {
{UNIVERSAL_STR("for"), Keyword::For},
{UNIVERSAL_STR("endfor"), Keyword::Endfor},
{UNIVERSAL_STR("in"), Keyword::In},
Expand Down Expand Up @@ -813,6 +821,7 @@ KeywordsInfo ParserTraitsBase<T>::s_keywordsInfo[39] = {
{UNIVERSAL_STR("context"), Keyword::Context},
{UNIVERSAL_STR("from"), Keyword::From},
{UNIVERSAL_STR("as"), Keyword::As},
{UNIVERSAL_STR("do"), Keyword::Do},
};

template<typename T>
Expand Down Expand Up @@ -878,6 +887,7 @@ std::unordered_map<int, MultiStringLiteral> ParserTraitsBase<T>::s_tokens = {
{Token::Context, UNIVERSAL_STR("context")},
{Token::From, UNIVERSAL_STR("form")},
{Token::As, UNIVERSAL_STR("as")},
{Token::Do, UNIVERSAL_STR("do")},
{Token::CommentBegin, UNIVERSAL_STR("{#")},
{Token::CommentEnd, UNIVERSAL_STR("#}")},
{Token::StmtBegin, UNIVERSAL_STR("{%")},
Expand Down
31 changes: 31 additions & 0 deletions test/errors_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
using namespace jinja2;

struct ErrorsGenericTestTag;
struct ErrorsGenericExtensionTestTag;
using ErrorsGenericTest = InputOutputPairTest<ErrorsGenericTestTag>;
using ErrorsGenericExtensionsTest = InputOutputPairTest<ErrorsGenericExtensionTestTag>;


TEST_P(ErrorsGenericTest, Test)
Expand All @@ -27,6 +29,26 @@ TEST_P(ErrorsGenericTest, Test)
EXPECT_EQ(expectedResult, result);
}

TEST_P(ErrorsGenericExtensionsTest, Test)
{
auto& testParam = GetParam();
std::string source = testParam.tpl;

TemplateEnv env;
env.GetSettings().extensions.Do = true;

Template tpl(&env);
auto parseResult = tpl.Load(source);
EXPECT_FALSE(parseResult.has_value());

std::ostringstream errorDescr;
errorDescr << parseResult.error();
std::string result = errorDescr.str();
std::cout << result << std::endl;
std::string expectedResult = testParam.result;
EXPECT_EQ(expectedResult, result);
}

INSTANTIATE_TEST_CASE_P(BasicTest, ErrorsGenericTest, ::testing::Values(
InputOutputPair{"{{}}",
"noname.j2tpl:1:3: error: Unexpected token: '<<End of block>>'\n{{}}\n--^-------"},
Expand Down Expand Up @@ -236,6 +258,15 @@ INSTANTIATE_TEST_CASE_P(StatementsTest_2, ErrorsGenericTest, ::testing::Values(
"noname.j2tpl:1:20: error: Unexpected token: '*'\n{% call name(param=*) %}{% endcall %}\n ---^-------"},
InputOutputPair{"{% block b %}{% endcall %}",
"noname.j2tpl:1:17: error: Unexpected statement: 'endcall'\n{% block b %}{% endcall %}\n ---^-------"},
InputOutputPair{"{% do 'Hello World' %}",
"noname.j2tpl:1:4: error: Extension disabled\n{% do 'Hello World' %}\n---^-------"},
InputOutputPair{"{{}}",
"noname.j2tpl:1:3: error: Unexpected token: '<<End of block>>'\n{{}}\n--^-------"}
));

INSTANTIATE_TEST_CASE_P(ExtensionStatementsTest, ErrorsGenericExtensionsTest, ::testing::Values(
InputOutputPair{"{% do %}",
"noname.j2tpl:1:7: error: Unexpected token: '<<End of block>>'\n{% do %}\n ---^-------"},
InputOutputPair{"{% do 1 + %}",
"noname.j2tpl:1:11: error: Unexpected token: '<<End of block>>'\n{% do 1 + %}\n ---^-------"}
));
36 changes: 36 additions & 0 deletions test/expressions_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,42 @@ TEST(ExpressionsTest, IfExpression)
EXPECT_STREQ(expectedResult.c_str(), result.c_str());
}

TEST(ExpressionTest, DoStatement)
{
std::string source = R"(
{{ data.strValue }}{% do setData('Inner Value') %}
{{ data.strValue }}
)";

TemplateEnv env;
env.GetSettings().extensions.Do = true;

TestInnerStruct innerStruct;
innerStruct.strValue = "Outer Value";

ValuesMap params = {
{"data", Reflect(&innerStruct)},
{"setData", MakeCallable(
[&innerStruct](const std::string& val) -> Value {
innerStruct.strValue = val;
return "String not to be shown";
},
ArgInfo{"val"})
},
};

Template tpl(&env);

ASSERT_TRUE(tpl.Load(source));
std::string result = tpl.RenderAsString(params).value();
std::cout << result << std::endl;
std::string expectedResult = R"(
Outer ValueInner Value
)";

EXPECT_STREQ(expectedResult.c_str(), result.c_str());
}

struct LogicalExprTestTag;
using LogicalExprTest = InputOutputPairTest<LogicalExprTestTag>;

Expand Down