Skip to content

Commit 0bd1418

Browse files
authored
Implement 'macro' and 'call' statement (#35)
* Begin implementation of macros * Implement 'macro' statement * Fix build * Implement 'call' statement * Add extra tests * Fix crash in tests
1 parent 6331e7b commit 0bd1418

15 files changed

+718
-121
lines changed

CMakeLists.txt

+7-5
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,14 @@ else ()
4444
set (MSVC_RUNTIME_TYPE "/MD")
4545
endif ()
4646
if (CMAKE_BUILD_TYPE MATCHES "Debug")
47+
message("#######>>>>>>>>>>!!!!!!!!!!!!!! AAAAAAAAAAAAAAAAAAAAAAAAAAAA")
4748
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MSVC_RUNTIME_TYPE}d")
49+
set (Boost_USE_DEBUG_RUNTIME ON)
4850
else ()
4951
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MSVC_RUNTIME_TYPE}")
52+
set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${MSVC_RUNTIME_TYPE}")
53+
set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/PROFILE")
54+
set (Boost_USE_DEBUG_RUNTIME OFF)
5055
endif ()
5156
endif()
5257

@@ -133,24 +138,21 @@ add_library(${LIB_TARGET_NAME} STATIC
133138
${PublicHeaders}
134139
)
135140

136-
target_link_libraries(${LIB_TARGET_NAME} PUBLIC ThirdParty::nonstd Boost::boost Boost::system Boost::filesystem)
141+
target_link_libraries(${LIB_TARGET_NAME} PUBLIC ThirdParty::nonstd Boost::boost) # Boost::system Boost::filesystem)
137142

138143
target_include_directories(${LIB_TARGET_NAME}
139144
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
140145

141146
if (WITH_TESTS)
142147
enable_testing()
143148

144-
message (STATUS "############# GTEST_INCLUDE_DIRS=${GTEST_INCLUDE_DIRS}")
145-
message (STATUS "############# GTEST_BOTH_LIBRARIES=${GTEST_BOTH_LIBRARIES}")
146-
147149
include_directories(
148150
${GTEST_INCLUDE_DIRS}
149151
)
150152

151153
CollectSources(TestSources TestHeaders ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/test)
152154
add_executable(jinja2cpp_tests ${TestSources} ${TestHeaders})
153-
target_link_libraries(jinja2cpp_tests ${GTEST_BOTH_LIBRARIES} ${LIB_TARGET_NAME} ${EXTRA_TEST_LIBS})
155+
target_link_libraries(jinja2cpp_tests ${GTEST_BOTH_LIBRARIES} ${LIB_TARGET_NAME} ${EXTRA_TEST_LIBS} ${Boost_LIBRARIES})
154156

155157
add_custom_command(
156158
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/test_data/simple_template1.j2tpl

include/jinja2cpp/parse_result.h

-75
This file was deleted.

src/expression_evaluator.cpp

+13-1
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,8 @@ enum ParamState
366366
MappedKw,
367367
};
368368

369-
ParsedArguments ParseCallParams(const std::initializer_list<ArgumentInfo>& args, const CallParams& params, bool& isSucceeded)
369+
template<typename T>
370+
ParsedArguments ParseCallParamsImpl(const T& args, const CallParams& params, bool& isSucceeded)
370371
{
371372
struct ArgInfo
372373
{
@@ -503,5 +504,16 @@ ParsedArguments ParseCallParams(const std::initializer_list<ArgumentInfo>& args,
503504

504505
return result;
505506
}
507+
508+
ParsedArguments ParseCallParams(const std::initializer_list<ArgumentInfo>& args, const CallParams& params, bool& isSucceeded)
509+
{
510+
return ParseCallParamsImpl(args, params, isSucceeded);
511+
}
512+
513+
ParsedArguments ParseCallParams(const std::vector<ArgumentInfo>& args, const CallParams& params, bool& isSucceeded)
514+
{
515+
return ParseCallParamsImpl(args, params, isSucceeded);
516+
}
517+
506518
}
507519
}

src/expression_evaluator.h

+1-20
Original file line numberDiff line numberDiff line change
@@ -328,26 +328,7 @@ class IfExpression
328328
namespace helpers
329329
{
330330
ParsedArguments ParseCallParams(const std::initializer_list<ArgumentInfo>& argsInfo, const CallParams& params, bool& isSucceeded);
331-
332-
//constexpr size_t NoPosParam = std::numeric_limits<size_t>::max();
333-
334-
//inline bool FindParam(const CallParams& params, size_t pos, std::string paramName, ExpressionEvaluatorPtr<>& value)
335-
//{
336-
// auto p = params.kwParams.find(paramName);
337-
// if (p != params.kwParams.end())
338-
// {
339-
// value = p->second;
340-
// return true;
341-
// }
342-
343-
// if (pos < params.posParams.size())
344-
// {
345-
// value = params.posParams[pos];
346-
// return true;
347-
// }
348-
349-
// return false;
350-
//}
331+
ParsedArguments ParseCallParams(const std::vector<ArgumentInfo>& args, const CallParams& params, bool& isSucceeded);
351332
}
352333
} // jinja2
353334

src/expression_parser.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class ExpressionParser
1919
ExpressionParser();
2020
ParseResult<RendererPtr> Parse(LexScanner& lexer);
2121
ParseResult<ExpressionEvaluatorPtr<FullExpressionEvaluator>> ParseFullExpression(LexScanner& lexer, bool includeIfPart = true);
22+
ParseResult<CallParams> ParseCallParams(LexScanner& lexer);
2223
private:
2324
ParseResult<ExpressionEvaluatorPtr<Expression>> ParseLogicalNot(LexScanner& lexer);
2425
ParseResult<ExpressionEvaluatorPtr<Expression>> ParseLogicalOr(LexScanner& lexer);
@@ -34,7 +35,6 @@ class ExpressionParser
3435
ParseResult<ExpressionEvaluatorPtr<Expression>> ParseDictionary(LexScanner& lexer);
3536
ParseResult<ExpressionEvaluatorPtr<Expression>> ParseTuple(LexScanner& lexer);
3637
ParseResult<ExpressionEvaluatorPtr<Expression>> ParseCall(LexScanner& lexer, ExpressionEvaluatorPtr<Expression> valueRef);
37-
ParseResult<CallParams> ParseCallParams(LexScanner& lexer);
3838
ParseResult<ExpressionEvaluatorPtr<Expression>> ParseSubscript(LexScanner& lexer, ExpressionEvaluatorPtr<Expression> valueRef);
3939
ParseResult<ExpressionEvaluatorPtr<ExpressionFilter>> ParseFilterExpression(LexScanner& lexer);
4040
ParseResult<ExpressionEvaluatorPtr<IfExpression>> ParseIfExpression(LexScanner& lexer);

src/helpers.h

+24
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,23 @@ struct StringConverter<std::wstring, std::string>
6565
{
6666
std::mbstate_t state = std::mbstate_t();
6767
auto src = from.data();
68+
#ifndef _MSC_VER
6869
std::size_t len = 1 + std::wcsrtombs(nullptr, &src, 0, &state);
70+
#else
71+
std::size_t len = 0;
72+
auto err = wcsrtombs_s(&len, nullptr, 0, &src, 0, &state);
73+
if (err != 0)
74+
return std::string();
75+
++ len;
76+
#endif
6977
std::string result;
7078
result.resize(len);
7179
src = from.data();
80+
#ifndef _MSC_VER
7281
std::wcsrtombs(&result[0], &src, from.size(), &state);
82+
#else
83+
wcsrtombs_s(&len, &result[0], len, &src, from.size(), &state);
84+
#endif
7385
return result;
7486
}
7587
};
@@ -81,11 +93,23 @@ struct StringConverter<std::string, std::wstring>
8193
{
8294
std::mbstate_t state = std::mbstate_t();
8395
auto src = from.data();
96+
#ifndef _MSC_VER
8497
std::size_t len = 1 + std::mbsrtowcs(NULL, &src, 0, &state);
98+
#else
99+
std::size_t len = 0;
100+
auto err = mbsrtowcs_s(&len, NULL, 0, &src, 0, &state);
101+
if (err != 0)
102+
return std::wstring();
103+
++len;
104+
#endif
85105
std::wstring result;
86106
result.resize(len);
87107
src = from.data();
108+
#ifndef _MSC_VER
88109
std::mbsrtowcs(&result[0], &src, result.size(), &state);
110+
#else
111+
mbsrtowcs_s(&len, &result[0], len, &src, result.size(), &state);
112+
#endif
89113
return result;
90114
}
91115
};

src/lexer.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ struct Token
9393
ExprEnd,
9494
};
9595

96-
Type type;
97-
CharRange range;
96+
Type type = Unknown;
97+
CharRange range = {0, 0};
9898
InternalValue value;
9999

100100
bool IsEof() const

src/render_context.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@ class RenderContext
5252
{
5353
for (auto p = m_scopes.rbegin(); p != m_scopes.rend(); ++ p)
5454
{
55-
auto valP = p->find(val);
56-
if (valP != p->end())
55+
auto& map = *p;
56+
auto valP = map.find(val);
57+
if (valP != map.end())
5758
{
5859
found = true;
5960
return valP;

src/statements.cpp

+109
Original file line numberDiff line numberDiff line change
@@ -307,4 +307,113 @@ void ExtendsStatement::Render(OutStream& os, RenderContext& values)
307307
renderer->Render(os, values);
308308
}
309309

310+
void MacroStatement::PrepareMacroParams(RenderContext& values)
311+
{
312+
for (auto& p : m_params)
313+
{
314+
ArgumentInfo info(p.paramName, !p.defaultValue);
315+
if (p.defaultValue)
316+
info.defaultVal = p.defaultValue->Evaluate(values);
317+
m_preparedParams.push_back(std::move(info));
318+
}
319+
}
320+
321+
void MacroStatement::Render(OutStream& os, RenderContext& values)
322+
{
323+
PrepareMacroParams(values);
324+
325+
values.GetCurrentScope()[m_name] = Callable([this](const CallParams& callParams, OutStream& stream, RenderContext& context) {
326+
InvokeMacroRenderer(callParams, stream, context);
327+
});
328+
}
329+
330+
void MacroStatement::InvokeMacroRenderer(const CallParams& callParams, OutStream& stream, RenderContext& context)
331+
{
332+
InternalValueMap callArgs;
333+
InternalValueMap kwArgs;
334+
InternalValueList varArgs;
335+
336+
SetupCallArgs(m_preparedParams, callParams, context, callArgs, kwArgs, varArgs);
337+
InternalValueList arguments;
338+
InternalValueList defaults;
339+
for (auto& a : m_preparedParams)
340+
{
341+
arguments.emplace_back(a.name);
342+
defaults.emplace_back(a.defaultVal);
343+
}
344+
345+
auto& scope = context.EnterScope();
346+
for (auto& a : callArgs)
347+
scope[a.first] = std::move(a.second);
348+
349+
scope["kwargs"] = MapAdapter::CreateAdapter(std::move(kwArgs));
350+
scope["varargs"] = ListAdapter::CreateAdapter(std::move(varArgs));
351+
352+
scope["name"] = m_name;
353+
scope["arguments"] = ListAdapter::CreateAdapter(std::move(arguments));
354+
scope["defaults"] = ListAdapter::CreateAdapter(std::move(defaults));
355+
356+
m_mainBody->Render(stream, context);
357+
358+
context.ExitScope();
359+
}
360+
361+
void MacroStatement::SetupCallArgs(const std::vector<ArgumentInfo>& argsInfo, const CallParams& callParams, RenderContext& context, InternalValueMap& callArgs, InternalValueMap& kwArgs, InternalValueList& varArgs)
362+
{
363+
bool isSucceeded = true;
364+
ParsedArguments args = helpers::ParseCallParams(argsInfo, callParams, isSucceeded);
365+
366+
for (auto& a : args.args)
367+
callArgs[a.first] = a.second->Evaluate(context);
368+
369+
for (auto& a : args.extraKwArgs)
370+
kwArgs[a.first] = a.second->Evaluate(context);
371+
372+
for (auto& a : args.extraPosArgs)
373+
varArgs.push_back(a->Evaluate(context));
374+
}
375+
376+
void MacroStatement::SetupMacroScope(InternalValueMap& scope)
377+
{
378+
;
379+
}
380+
381+
void MacroCallStatement::Render(OutStream& os, RenderContext& values)
382+
{
383+
bool isMacroFound = false;
384+
auto macroPtr = values.FindValue(m_macroName, isMacroFound);
385+
if (!isMacroFound)
386+
return;
387+
388+
auto& fnVal = macroPtr->second;
389+
const Callable* callable = boost::get<Callable>(&fnVal);
390+
if (callable == nullptr || callable->GetType() == Callable::Type::Expression)
391+
return;
392+
393+
PrepareMacroParams(values);
394+
auto& curScope = values.GetCurrentScope();
395+
auto callerP = curScope.find("caller");
396+
bool hasCallerVal = callerP != curScope.end();
397+
InternalValue prevCaller;
398+
if (hasCallerVal)
399+
prevCaller = callerP->second;
400+
401+
curScope["caller"] = Callable([this](const CallParams& callParams, OutStream& stream, RenderContext& context) {
402+
InvokeMacroRenderer(callParams, stream, context);
403+
});
404+
405+
callable->GetStatementCallable()(m_callParams, os, values);
406+
407+
if (hasCallerVal)
408+
curScope["caller"] = prevCaller;
409+
else
410+
values.GetCurrentScope().erase("caller");
411+
}
412+
413+
void MacroCallStatement::SetupMacroScope(InternalValueMap& scope)
414+
{
415+
416+
}
417+
418+
310419
} // jinja2

0 commit comments

Comments
 (0)