#include "template_parser.h"
#include "renderer.h"
#include <boost/cast.hpp>
namespace jinja2
{
StatementsParser::ParseResult StatementsParser::Parse(LexScanner& lexer, StatementInfoList& statementsInfo)
{
Token tok = lexer.NextToken();
ParseResult result;
switch (lexer.GetAsKeyword(tok))
{
case Keyword::For:
result = ParseFor(lexer, statementsInfo, tok);
break;
case Keyword::Endfor:
result = ParseEndFor(lexer, statementsInfo, tok);
break;
case Keyword::If:
result = ParseIf(lexer, statementsInfo, tok);
break;
case Keyword::Else:
result = ParseElse(lexer, statementsInfo, tok);
break;
case Keyword::ElIf:
result = ParseElIf(lexer, statementsInfo, tok);
break;
case Keyword::EndIf:
result = ParseEndIf(lexer, statementsInfo, tok);
break;
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;
case Keyword::EndBlock:
result = ParseEndBlock(lexer, statementsInfo, tok);
break;
case Keyword::Extends:
result = ParseExtends(lexer, statementsInfo, tok);
break;
case Keyword::Macro:
result = ParseMacro(lexer, statementsInfo, tok);
break;
case Keyword::EndMacro:
result = ParseEndMacro(lexer, statementsInfo, tok);
break;
case Keyword::Call:
result = ParseCall(lexer, statementsInfo, tok);
break;
case Keyword::EndCall:
result = ParseEndCall(lexer, statementsInfo, tok);
break;
case Keyword::Include:
result = ParseInclude(lexer, statementsInfo, tok);
break;
case Keyword::Import:
result = ParseImport(lexer, statementsInfo, tok);
break;
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::With:
result = ParseWith(lexer, statementsInfo, tok);
break;
case Keyword::EndWith:
result = ParseEndWith(lexer, statementsInfo, tok);
break;
case Keyword::Filter:
result = ParseFilter(lexer, statementsInfo, tok);
break;
case Keyword::EndFilter:
result = ParseEndFilter(lexer, statementsInfo, tok);
break;
default:
return MakeParseError(ErrorCode::UnexpectedToken, tok);
}
if (result)
{
tok = lexer.PeekNextToken();
if (tok != Token::Eof)
return MakeParseError(ErrorCode::ExpectedEndOfStatement, tok);
}
return result;
}
struct ErrorTokenConverter
{
const Token& baseTok;
explicit ErrorTokenConverter(const Token& t)
: baseTok(t)
{}
Token operator()(const Token& tok) const
{
return tok;
}
template<typename T>
Token operator()(T tokType) const
{
auto newTok = baseTok;
newTok.type = static_cast<Token::Type>(tokType);
if (newTok.type == Token::Identifier || newTok.type == Token::String)
newTok.range.endOffset = newTok.range.startOffset;
return newTok;
}
};
template<typename ... Args>
auto MakeParseErrorTL(ErrorCode code, const Token& baseTok, Args ... expectedTokens)
{
ErrorTokenConverter tokCvt(baseTok);
return MakeParseError(code, baseTok, {tokCvt(expectedTokens)...});
}
StatementsParser::ParseResult StatementsParser::ParseFor(LexScanner &lexer, StatementInfoList &statementsInfo,
const Token &stmtTok)
{
std::vector<std::string> vars;
while (lexer.PeekNextToken() == Token::Identifier)
{
auto tok = lexer.NextToken();
vars.push_back(AsString(tok.value));
if (lexer.NextToken() != ',')
{
lexer.ReturnToken();
break;
}
}
if (vars.empty())
return MakeParseError(ErrorCode::ExpectedIdentifier, lexer.PeekNextToken());
if (!lexer.EatIfEqual(Keyword::In))
{
Token tok1 = lexer.PeekNextToken();
Token tok2 = tok1;
tok2.type = Token::Identifier;
tok2.range.endOffset = tok2.range.startOffset;
tok2.value = InternalValue();
return MakeParseErrorTL(ErrorCode::ExpectedToken, tok1, tok2, Token::In, ',');
}
auto pivotToken = lexer.PeekNextToken();
ExpressionParser exprPraser(m_settings);
auto valueExpr = exprPraser.ParseFullExpression(lexer, false);
if (!valueExpr)
return valueExpr.get_unexpected();
// return MakeParseError(ErrorCode::ExpectedExpression, pivotToken);
Token flagsTok;
bool isRecursive = false;
if (lexer.EatIfEqual(Keyword::Recursive, &flagsTok))
{
isRecursive = true;
}
ExpressionEvaluatorPtr<> ifExpr;
if (lexer.EatIfEqual(Keyword::If))
{
auto parsedExpr = exprPraser.ParseFullExpression(lexer, false);
if (!parsedExpr)
return parsedExpr.get_unexpected();
ifExpr = *parsedExpr;
}
else if (lexer.PeekNextToken() != Token::Eof)
{
auto tok1 = lexer.PeekNextToken();
return MakeParseErrorTL(ErrorCode::ExpectedToken, tok1, Token::If, Token::Recursive, Token::Eof);
}
auto renderer = std::make_shared<ForStatement>(vars, *valueExpr, ifExpr, isRecursive);
StatementInfo statementInfo = StatementInfo::Create(StatementInfo::ForStatement, stmtTok);
statementInfo.renderer = renderer;
statementsInfo.push_back(statementInfo);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseEndFor(LexScanner&, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (statementsInfo.size() <= 1)
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
StatementInfo info = statementsInfo.back();
RendererPtr elseRenderer;
if (info.type == StatementInfo::ElseIfStatement)
{
auto r = std::static_pointer_cast<ElseBranchStatement>(info.renderer);
r->SetMainBody(info.compositions[0]);
elseRenderer = std::static_pointer_cast<IRendererBase>(r);
statementsInfo.pop_back();
info = statementsInfo.back();
}
if (info.type != StatementInfo::ForStatement)
{
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
}
statementsInfo.pop_back();
auto renderer = static_cast<ForStatement*>(info.renderer.get());
renderer->SetMainBody(info.compositions[0]);
if (elseRenderer)
renderer->SetElseBody(elseRenderer);
statementsInfo.back().currentComposition->AddRenderer(info.renderer);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseIf(LexScanner &lexer, StatementInfoList &statementsInfo,
const Token &stmtTok)
{
auto pivotTok = lexer.PeekNextToken();
ExpressionParser exprParser(m_settings);
auto valueExpr = exprParser.ParseFullExpression(lexer);
if (!valueExpr)
return MakeParseError(ErrorCode::ExpectedExpression, pivotTok);
auto renderer = std::make_shared<IfStatement>(*valueExpr);
StatementInfo statementInfo = StatementInfo::Create(StatementInfo::IfStatement, stmtTok);
statementInfo.renderer = renderer;
statementsInfo.push_back(statementInfo);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseElse(LexScanner& /*lexer*/, StatementInfoList& statementsInfo
, const Token& stmtTok)
{
auto renderer = std::make_shared<ElseBranchStatement>(ExpressionEvaluatorPtr<>());
StatementInfo statementInfo = StatementInfo::Create(StatementInfo::ElseIfStatement, stmtTok);
statementInfo.renderer = std::static_pointer_cast<IRendererBase>(renderer);
statementsInfo.push_back(statementInfo);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseElIf(LexScanner& lexer, StatementInfoList& statementsInfo
, const Token& stmtTok)
{
auto pivotTok = lexer.PeekNextToken();
ExpressionParser exprParser(m_settings);
auto valueExpr = exprParser.ParseFullExpression(lexer);
if (!valueExpr)
return MakeParseError(ErrorCode::ExpectedExpression, pivotTok);
auto renderer = std::make_shared<ElseBranchStatement>(*valueExpr);
StatementInfo statementInfo = StatementInfo::Create(StatementInfo::ElseIfStatement, stmtTok);
statementInfo.renderer = std::static_pointer_cast<IRendererBase>(renderer);
statementsInfo.push_back(statementInfo);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseEndIf(LexScanner&, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (statementsInfo.size() <= 1)
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
auto info = statementsInfo.back();
statementsInfo.pop_back();
std::list<StatementPtr<ElseBranchStatement>> elseBranches;
auto errorTok = stmtTok;
while (info.type != StatementInfo::IfStatement)
{
if (info.type != StatementInfo::ElseIfStatement)
return MakeParseError(ErrorCode::UnexpectedStatement, errorTok);
auto elseRenderer = std::static_pointer_cast<ElseBranchStatement>(info.renderer);
elseRenderer->SetMainBody(info.compositions[0]);
elseBranches.push_front(elseRenderer);
errorTok = info.token;
info = statementsInfo.back();
statementsInfo.pop_back();
}
auto renderer = static_cast<IfStatement*>(info.renderer.get());
renderer->SetMainBody(info.compositions[0]);
for (auto& b : elseBranches)
renderer->AddElseBranch(b);
statementsInfo.back().currentComposition->AddRenderer(info.renderer);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseSet(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
{
std::vector<std::string> vars;
while (lexer.PeekNextToken() == Token::Identifier)
{
auto tok = lexer.NextToken();
vars.push_back(AsString(tok.value));
if (lexer.NextToken() != ',')
{
lexer.ReturnToken();
break;
}
}
if (vars.empty())
return MakeParseError(ErrorCode::ExpectedIdentifier, lexer.PeekNextToken());
ExpressionParser exprParser(m_settings);
if (lexer.EatIfEqual('='))
{
const auto expr = exprParser.ParseFullExpression(lexer);
if (!expr)
return expr.get_unexpected();
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
{
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 {};
}
StatementsParser::ParseResult StatementsParser::ParseEndSet(LexScanner&
, StatementInfoList& statementsInfo
, const Token& 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
, const Token& stmtTok)
{
if (statementsInfo.empty())
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
Token nextTok = lexer.NextToken();
if (nextTok != Token::Identifier)
return MakeParseError(ErrorCode::ExpectedIdentifier, nextTok);
std::string blockName = AsString(nextTok.value);
auto& info = statementsInfo.back();
RendererPtr blockRenderer;
StatementInfo::Type blockType = StatementInfo::ParentBlockStatement;
if (info.type == StatementInfo::ExtendsStatement)
{
blockRenderer = std::make_shared<BlockStatement>(blockName);
blockType = StatementInfo::BlockStatement;
}
else
{
bool isScoped = false;
if (lexer.EatIfEqual(Keyword::Scoped, &nextTok))
isScoped = true;
else
{
nextTok = lexer.PeekNextToken();
if (nextTok != Token::Eof)
return MakeParseErrorTL(ErrorCode::ExpectedToken, nextTok, Token::Scoped);
}
blockRenderer = std::make_shared<ParentBlockStatement>(blockName, isScoped);
}
StatementInfo statementInfo = StatementInfo::Create(blockType, stmtTok);
statementInfo.renderer = std::move(blockRenderer);
statementsInfo.push_back(statementInfo);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseEndBlock(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (statementsInfo.size() <= 1)
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
Token nextTok = lexer.PeekNextToken();
if (nextTok != Token::Identifier && nextTok != Token::Eof)
{
Token tok2;
tok2.type = Token::Identifier;
Token tok3;
tok3.type = Token::Eof;
return MakeParseError(ErrorCode::ExpectedToken, nextTok, {tok2, tok3});
}
if (nextTok == Token::Identifier)
lexer.EatToken();
auto info = statementsInfo.back();
statementsInfo.pop_back();
if (info.type == StatementInfo::BlockStatement)
{
auto blockStmt = std::static_pointer_cast<BlockStatement>(info.renderer);
blockStmt->SetMainBody(info.compositions[0]);
auto& extendsInfo = statementsInfo.back();
auto extendsStmt = std::static_pointer_cast<ExtendsStatement>(extendsInfo.renderer);
extendsStmt->AddBlock(std::static_pointer_cast<BlockStatement>(info.renderer));
}
else if (info.type == StatementInfo::ParentBlockStatement)
{
auto blockStmt = std::static_pointer_cast<ParentBlockStatement>(info.renderer);
blockStmt->SetMainBody(info.compositions[0]);
statementsInfo.back().currentComposition->AddRenderer(info.renderer);
}
else
{
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
}
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseExtends(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (statementsInfo.empty())
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
if (!m_env)
return MakeParseError(ErrorCode::TemplateEnvAbsent, stmtTok);
Token tok = lexer.NextToken();
if (tok != Token::String && tok != Token::Identifier)
{
auto tok2 = tok;
tok2.type = Token::Identifier;
tok2.range.endOffset = tok2.range.startOffset;
tok2.value = EmptyValue{};
return MakeParseErrorTL(ErrorCode::ExpectedToken, tok, tok2, Token::String);
}
auto renderer = std::make_shared<ExtendsStatement>(AsString(tok.value), tok == Token::String);
statementsInfo.back().currentComposition->AddRenderer(renderer);
StatementInfo statementInfo = StatementInfo::Create(StatementInfo::ExtendsStatement, stmtTok);
statementInfo.renderer = renderer;
statementsInfo.push_back(statementInfo);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseMacro(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (statementsInfo.empty())
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
Token nextTok = lexer.NextToken();
if (nextTok != Token::Identifier)
return MakeParseError(ErrorCode::ExpectedIdentifier, nextTok);
std::string macroName = AsString(nextTok.value);
MacroParams macroParams;
if (lexer.EatIfEqual('('))
{
auto result = ParseMacroParams(lexer);
if (!result)
return result.get_unexpected();
macroParams = std::move(result.value());
}
else if (lexer.PeekNextToken() != Token::Eof)
{
Token tok = lexer.PeekNextToken();
return MakeParseErrorTL(ErrorCode::UnexpectedToken, tok, Token::RBracket, Token::Eof);
}
auto renderer = std::make_shared<MacroStatement>(std::move(macroName), std::move(macroParams));
StatementInfo statementInfo = StatementInfo::Create(StatementInfo::MacroStatement, stmtTok);
statementInfo.renderer = renderer;
statementsInfo.push_back(statementInfo);
return ParseResult();
}
nonstd::expected<MacroParams, ParseError> StatementsParser::ParseMacroParams(LexScanner& lexer)
{
MacroParams items;
if (lexer.EatIfEqual(')'))
return std::move(items);
ExpressionParser exprParser(m_settings);
do
{
Token name = lexer.NextToken();
if (name != Token::Identifier)
return MakeParseError(ErrorCode::ExpectedIdentifier, name);
ExpressionEvaluatorPtr<> defVal;
if (lexer.EatIfEqual('='))
{
auto result = exprParser.ParseFullExpression(lexer, false);
if (!result)
return result.get_unexpected();
defVal = *result;
}
MacroParam p;
p.paramName = AsString(name.value);
p.defaultValue = std::move(defVal);
items.push_back(std::move(p));
} while (lexer.EatIfEqual(','));
auto tok = lexer.NextToken();
if (tok != ')')
return MakeParseError(ErrorCode::ExpectedRoundBracket, tok);
return std::move(items);
}
StatementsParser::ParseResult StatementsParser::ParseEndMacro(LexScanner&, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (statementsInfo.size() <= 1)
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
StatementInfo info = statementsInfo.back();
if (info.type != StatementInfo::MacroStatement)
{
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
}
statementsInfo.pop_back();
auto renderer = static_cast<MacroStatement*>(info.renderer.get());
renderer->SetMainBody(info.compositions[0]);
statementsInfo.back().currentComposition->AddRenderer(info.renderer);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseCall(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (statementsInfo.empty())
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
MacroParams callbackParams;
if (lexer.EatIfEqual('('))
{
auto result = ParseMacroParams(lexer);
if (!result)
return result.get_unexpected();
callbackParams = std::move(result.value());
}
Token nextTok = lexer.NextToken();
if (nextTok != Token::Identifier)
{
Token tok = nextTok;
Token tok1;
tok1.type = Token::Identifier;
return MakeParseError(ErrorCode::UnexpectedToken, tok, {tok1});
}
std::string macroName = AsString(nextTok.value);
CallParamsInfo callParams;
if (lexer.EatIfEqual('('))
{
ExpressionParser exprParser(m_settings);
auto result = exprParser.ParseCallParams(lexer);
if (!result)
return result.get_unexpected();
callParams = std::move(result.value());
}
auto renderer = std::make_shared<MacroCallStatement>(std::move(macroName), std::move(callParams), std::move(callbackParams));
StatementInfo statementInfo = StatementInfo::Create(StatementInfo::MacroCallStatement, stmtTok);
statementInfo.renderer = renderer;
statementsInfo.push_back(statementInfo);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseEndCall(LexScanner&, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (statementsInfo.size() <= 1)
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
StatementInfo info = statementsInfo.back();
if (info.type != StatementInfo::MacroCallStatement)
{
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
}
statementsInfo.pop_back();
auto renderer = static_cast<MacroCallStatement*>(info.renderer.get());
renderer->SetMainBody(info.compositions[0]);
statementsInfo.back().currentComposition->AddRenderer(info.renderer);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseInclude(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (statementsInfo.empty())
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
// auto operTok = lexer.NextToken();
ExpressionEvaluatorPtr<> valueExpr;
ExpressionParser exprParser(m_settings);
auto expr = exprParser.ParseFullExpression(lexer);
if (!expr)
return expr.get_unexpected();
valueExpr = *expr;
Token nextTok = lexer.PeekNextToken();
bool isIgnoreMissing = false;
bool isWithContext = true;
bool hasIgnoreMissing = false;
if (lexer.EatIfEqual(Keyword::Ignore))
{
if (lexer.EatIfEqual(Keyword::Missing))
isIgnoreMissing = true;
else
return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::Missing);
hasIgnoreMissing = true;
nextTok = lexer.PeekNextToken();
}
auto kw = lexer.GetAsKeyword(nextTok);
bool hasContextControl = false;
if (kw == Keyword::With || kw == Keyword::Without)
{
lexer.EatToken();
isWithContext = kw == Keyword::With;
if (!lexer.EatIfEqual(Keyword::Context))
return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::Context);
nextTok = lexer.PeekNextToken();
hasContextControl = true;
}
if (nextTok != Token::Eof)
{
if (hasContextControl)
return MakeParseErrorTL(ErrorCode::ExpectedEndOfStatement, nextTok, Token::Eof);
if (hasIgnoreMissing)
return MakeParseErrorTL(ErrorCode::UnexpectedToken, nextTok, Token::Eof, Token::With, Token::Without);
return MakeParseErrorTL(ErrorCode::UnexpectedToken, nextTok, Token::Eof, Token::Ignore, Token::With, Token::Without);
}
if (!m_env && !isIgnoreMissing)
return MakeParseError(ErrorCode::TemplateEnvAbsent, stmtTok);
auto renderer = std::make_shared<IncludeStatement>(isIgnoreMissing, isWithContext);
renderer->SetIncludeNamesExpr(valueExpr);
statementsInfo.back().currentComposition->AddRenderer(renderer);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseImport(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (!m_env)
return MakeParseError(ErrorCode::TemplateEnvAbsent, stmtTok);
ExpressionEvaluatorPtr<> valueExpr;
ExpressionParser exprParser(m_settings);
auto expr = exprParser.ParseFullExpression(lexer);
if (!expr)
return expr.get_unexpected();
valueExpr = *expr;
if (!lexer.EatIfEqual(Keyword::As))
return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::As);
Token name;
if (!lexer.EatIfEqual(Token::Identifier, &name))
return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::Identifier);
Token nextTok = lexer.PeekNextToken();
auto kw = lexer.GetAsKeyword(nextTok);
bool hasContextControl = false;
bool isWithContext = false;
if (kw == Keyword::With || kw == Keyword::Without)
{
lexer.EatToken();
isWithContext = kw == Keyword::With;
if (!lexer.EatIfEqual(Keyword::Context))
return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::Context);
nextTok = lexer.PeekNextToken();
hasContextControl = true;
}
if (nextTok != Token::Eof)
{
if (hasContextControl)
return MakeParseErrorTL(ErrorCode::ExpectedEndOfStatement, nextTok, Token::Eof);
return MakeParseErrorTL(ErrorCode::UnexpectedToken, nextTok, Token::Eof, Token::With, Token::Without);
}
auto renderer = std::make_shared<ImportStatement>(isWithContext);
renderer->SetImportNameExpr(valueExpr);
renderer->SetNamespace(AsString(name.value));
statementsInfo.back().currentComposition->AddRenderer(renderer);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseFrom(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (!m_env)
return MakeParseError(ErrorCode::TemplateEnvAbsent, stmtTok);
ExpressionEvaluatorPtr<> valueExpr;
ExpressionParser exprParser(m_settings);
auto expr = exprParser.ParseFullExpression(lexer);
if (!expr)
return expr.get_unexpected();
valueExpr = *expr;
if (!lexer.EatIfEqual(Keyword::Import))
return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::Identifier);
std::vector<std::pair<std::string, std::string>> mappedNames;
Token nextTok;
bool hasContextControl = false;
bool isWithContext = false;
for (;;)
{
bool hasComma = false;
if (!mappedNames.empty())
{
if (!lexer.EatIfEqual(Token::Comma))
hasComma = true;;
}
nextTok = lexer.PeekNextToken();
auto kw = lexer.GetAsKeyword(nextTok);
if (kw == Keyword::With || kw == Keyword::Without)
{
lexer.NextToken();
if (lexer.EatIfEqual(Keyword::Context))
{
hasContextControl = true;
isWithContext = kw == Keyword::With;
nextTok = lexer.PeekNextToken();
break;
}
else
{
lexer.ReturnToken();
}
}
if (hasComma)
break;
std::pair<std::string, std::string> macroMap;
if (!lexer.EatIfEqual(Token::Identifier, &nextTok))
return MakeParseErrorTL(ErrorCode::ExpectedToken, nextTok, Token::Identifier);
macroMap.first = AsString(nextTok.value);
if (lexer.EatIfEqual(Keyword::As))
{
if (!lexer.EatIfEqual(Token::Identifier, &nextTok))
return MakeParseErrorTL(ErrorCode::ExpectedToken, nextTok, Token::Identifier);
macroMap.second = AsString(nextTok.value);
}
else
{
macroMap.second = macroMap.first;
}
mappedNames.push_back(std::move(macroMap));
}
if (nextTok != Token::Eof)
{
if (hasContextControl)
return MakeParseErrorTL(ErrorCode::ExpectedEndOfStatement, nextTok, Token::Eof);
if (mappedNames.empty())
MakeParseErrorTL(ErrorCode::UnexpectedToken, nextTok, Token::Eof, Token::Identifier);
else
MakeParseErrorTL(ErrorCode::UnexpectedToken, nextTok, Token::Eof, Token::Comma, Token::With, Token::Without);
}
auto renderer = std::make_shared<ImportStatement>(isWithContext);
renderer->SetImportNameExpr(valueExpr);
for (auto& nameInfo : mappedNames)
renderer->AddNameToImport(std::move(nameInfo.first), std::move(nameInfo.second));
statementsInfo.back().currentComposition->AddRenderer(renderer);
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();
}
StatementsParser::ParseResult StatementsParser::ParseWith(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
{
std::vector<std::pair<std::string, ExpressionEvaluatorPtr<>>> vars;
ExpressionParser exprParser(m_settings);
while (lexer.PeekNextToken() == Token::Identifier)
{
auto nameTok = lexer.NextToken();
if (!lexer.EatIfEqual('='))
return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), '=');
auto expr = exprParser.ParseFullExpression(lexer);
if (!expr)
return expr.get_unexpected();
auto valueExpr = *expr;
vars.emplace_back(AsString(nameTok.value), valueExpr);
if (!lexer.EatIfEqual(','))
break;
}
auto nextTok = lexer.PeekNextToken();
if (vars.empty())
return MakeParseError(ErrorCode::ExpectedIdentifier, nextTok);
if (nextTok != Token::Eof)
return MakeParseErrorTL(ErrorCode::ExpectedToken, nextTok, Token::Eof, ',');
auto renderer = std::make_shared<WithStatement>();
renderer->SetScopeVars(std::move(vars));
StatementInfo statementInfo = StatementInfo::Create(StatementInfo::WithStatement, stmtTok);
statementInfo.renderer = renderer;
statementsInfo.push_back(statementInfo);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseEndWith(LexScanner& /*lexer*/, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (statementsInfo.size() <= 1)
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
StatementInfo info = statementsInfo.back();
if (info.type != StatementInfo::WithStatement)
{
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
}
statementsInfo.pop_back();
auto renderer = static_cast<WithStatement*>(info.renderer.get());
renderer->SetMainBody(info.compositions[0]);
statementsInfo.back().currentComposition->AddRenderer(info.renderer);
return ParseResult();
}
StatementsParser::ParseResult StatementsParser::ParseFilter(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
{
ExpressionParser exprParser(m_settings);
auto filterExpr = exprParser.ParseFilterExpression(lexer);
if (!filterExpr)
{
return filterExpr.get_unexpected();
}
auto renderer = std::make_shared<FilterStatement>(*filterExpr);
auto statementInfo = StatementInfo::Create(
StatementInfo::FilterStatement, stmtTok);
statementInfo.renderer = std::move(renderer);
statementsInfo.push_back(std::move(statementInfo));
return {};
}
StatementsParser::ParseResult StatementsParser::ParseEndFilter(LexScanner&, StatementInfoList& statementsInfo, const Token& stmtTok)
{
if (statementsInfo.size() <= 1)
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
const auto info = statementsInfo.back();
if (info.type != StatementInfo::FilterStatement)
{
return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
}
statementsInfo.pop_back();
auto &renderer = *boost::polymorphic_downcast<FilterStatement*>(info.renderer.get());
renderer.SetBody(info.compositions[0]);
statementsInfo.back().currentComposition->AddRenderer(info.renderer);
return {};
}
} // namespace jinja2