Skip to content

Commit

Permalink
Implement 'macro' and 'call' statement (#35)
Browse files Browse the repository at this point in the history
* Begin implementation of macros

* Implement 'macro' statement

* Fix build

* Implement 'call' statement

* Add extra tests

* Fix crash in tests
  • Loading branch information
flexferrum authored Aug 21, 2018
1 parent 6331e7b commit 0bd1418
Show file tree
Hide file tree
Showing 15 changed files with 718 additions and 121 deletions.
12 changes: 7 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,14 @@ else ()
set (MSVC_RUNTIME_TYPE "/MD")
endif ()
if (CMAKE_BUILD_TYPE MATCHES "Debug")
message("#######>>>>>>>>>>!!!!!!!!!!!!!! AAAAAAAAAAAAAAAAAAAAAAAAAAAA")
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MSVC_RUNTIME_TYPE}d")
set (Boost_USE_DEBUG_RUNTIME ON)
else ()
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MSVC_RUNTIME_TYPE}")
set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${MSVC_RUNTIME_TYPE}")
set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/PROFILE")
set (Boost_USE_DEBUG_RUNTIME OFF)
endif ()
endif()

Expand Down Expand Up @@ -133,24 +138,21 @@ add_library(${LIB_TARGET_NAME} STATIC
${PublicHeaders}
)

target_link_libraries(${LIB_TARGET_NAME} PUBLIC ThirdParty::nonstd Boost::boost Boost::system Boost::filesystem)
target_link_libraries(${LIB_TARGET_NAME} PUBLIC ThirdParty::nonstd Boost::boost) # Boost::system Boost::filesystem)

target_include_directories(${LIB_TARGET_NAME}
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)

if (WITH_TESTS)
enable_testing()

message (STATUS "############# GTEST_INCLUDE_DIRS=${GTEST_INCLUDE_DIRS}")
message (STATUS "############# GTEST_BOTH_LIBRARIES=${GTEST_BOTH_LIBRARIES}")

include_directories(
${GTEST_INCLUDE_DIRS}
)

CollectSources(TestSources TestHeaders ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/test)
add_executable(jinja2cpp_tests ${TestSources} ${TestHeaders})
target_link_libraries(jinja2cpp_tests ${GTEST_BOTH_LIBRARIES} ${LIB_TARGET_NAME} ${EXTRA_TEST_LIBS})
target_link_libraries(jinja2cpp_tests ${GTEST_BOTH_LIBRARIES} ${LIB_TARGET_NAME} ${EXTRA_TEST_LIBS} ${Boost_LIBRARIES})

add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/test_data/simple_template1.j2tpl
Expand Down
75 changes: 0 additions & 75 deletions include/jinja2cpp/parse_result.h

This file was deleted.

14 changes: 13 additions & 1 deletion src/expression_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,8 @@ enum ParamState
MappedKw,
};

ParsedArguments ParseCallParams(const std::initializer_list<ArgumentInfo>& args, const CallParams& params, bool& isSucceeded)
template<typename T>
ParsedArguments ParseCallParamsImpl(const T& args, const CallParams& params, bool& isSucceeded)
{
struct ArgInfo
{
Expand Down Expand Up @@ -503,5 +504,16 @@ ParsedArguments ParseCallParams(const std::initializer_list<ArgumentInfo>& args,

return result;
}

ParsedArguments ParseCallParams(const std::initializer_list<ArgumentInfo>& args, const CallParams& params, bool& isSucceeded)
{
return ParseCallParamsImpl(args, params, isSucceeded);
}

ParsedArguments ParseCallParams(const std::vector<ArgumentInfo>& args, const CallParams& params, bool& isSucceeded)
{
return ParseCallParamsImpl(args, params, isSucceeded);
}

}
}
21 changes: 1 addition & 20 deletions src/expression_evaluator.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,26 +328,7 @@ class IfExpression
namespace helpers
{
ParsedArguments ParseCallParams(const std::initializer_list<ArgumentInfo>& argsInfo, const CallParams& params, bool& isSucceeded);

//constexpr size_t NoPosParam = std::numeric_limits<size_t>::max();

//inline bool FindParam(const CallParams& params, size_t pos, std::string paramName, ExpressionEvaluatorPtr<>& value)
//{
// auto p = params.kwParams.find(paramName);
// if (p != params.kwParams.end())
// {
// value = p->second;
// return true;
// }

// if (pos < params.posParams.size())
// {
// value = params.posParams[pos];
// return true;
// }

// return false;
//}
ParsedArguments ParseCallParams(const std::vector<ArgumentInfo>& args, const CallParams& params, bool& isSucceeded);
}
} // jinja2

Expand Down
2 changes: 1 addition & 1 deletion src/expression_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class ExpressionParser
ExpressionParser();
ParseResult<RendererPtr> Parse(LexScanner& lexer);
ParseResult<ExpressionEvaluatorPtr<FullExpressionEvaluator>> ParseFullExpression(LexScanner& lexer, bool includeIfPart = true);
ParseResult<CallParams> ParseCallParams(LexScanner& lexer);
private:
ParseResult<ExpressionEvaluatorPtr<Expression>> ParseLogicalNot(LexScanner& lexer);
ParseResult<ExpressionEvaluatorPtr<Expression>> ParseLogicalOr(LexScanner& lexer);
Expand All @@ -34,7 +35,6 @@ class ExpressionParser
ParseResult<ExpressionEvaluatorPtr<Expression>> ParseDictionary(LexScanner& lexer);
ParseResult<ExpressionEvaluatorPtr<Expression>> ParseTuple(LexScanner& lexer);
ParseResult<ExpressionEvaluatorPtr<Expression>> ParseCall(LexScanner& lexer, ExpressionEvaluatorPtr<Expression> valueRef);
ParseResult<CallParams> ParseCallParams(LexScanner& lexer);
ParseResult<ExpressionEvaluatorPtr<Expression>> ParseSubscript(LexScanner& lexer, ExpressionEvaluatorPtr<Expression> valueRef);
ParseResult<ExpressionEvaluatorPtr<ExpressionFilter>> ParseFilterExpression(LexScanner& lexer);
ParseResult<ExpressionEvaluatorPtr<IfExpression>> ParseIfExpression(LexScanner& lexer);
Expand Down
24 changes: 24 additions & 0 deletions src/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,23 @@ struct StringConverter<std::wstring, std::string>
{
std::mbstate_t state = std::mbstate_t();
auto src = from.data();
#ifndef _MSC_VER
std::size_t len = 1 + std::wcsrtombs(nullptr, &src, 0, &state);
#else
std::size_t len = 0;
auto err = wcsrtombs_s(&len, nullptr, 0, &src, 0, &state);
if (err != 0)
return std::string();
++ len;
#endif
std::string result;
result.resize(len);
src = from.data();
#ifndef _MSC_VER
std::wcsrtombs(&result[0], &src, from.size(), &state);
#else
wcsrtombs_s(&len, &result[0], len, &src, from.size(), &state);
#endif
return result;
}
};
Expand All @@ -81,11 +93,23 @@ struct StringConverter<std::string, std::wstring>
{
std::mbstate_t state = std::mbstate_t();
auto src = from.data();
#ifndef _MSC_VER
std::size_t len = 1 + std::mbsrtowcs(NULL, &src, 0, &state);
#else
std::size_t len = 0;
auto err = mbsrtowcs_s(&len, NULL, 0, &src, 0, &state);
if (err != 0)
return std::wstring();
++len;
#endif
std::wstring result;
result.resize(len);
src = from.data();
#ifndef _MSC_VER
std::mbsrtowcs(&result[0], &src, result.size(), &state);
#else
mbsrtowcs_s(&len, &result[0], len, &src, result.size(), &state);
#endif
return result;
}
};
Expand Down
4 changes: 2 additions & 2 deletions src/lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ struct Token
ExprEnd,
};

Type type;
CharRange range;
Type type = Unknown;
CharRange range = {0, 0};
InternalValue value;

bool IsEof() const
Expand Down
5 changes: 3 additions & 2 deletions src/render_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ class RenderContext
{
for (auto p = m_scopes.rbegin(); p != m_scopes.rend(); ++ p)
{
auto valP = p->find(val);
if (valP != p->end())
auto& map = *p;
auto valP = map.find(val);
if (valP != map.end())
{
found = true;
return valP;
Expand Down
109 changes: 109 additions & 0 deletions src/statements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,4 +307,113 @@ void ExtendsStatement::Render(OutStream& os, RenderContext& values)
renderer->Render(os, values);
}

void MacroStatement::PrepareMacroParams(RenderContext& values)
{
for (auto& p : m_params)
{
ArgumentInfo info(p.paramName, !p.defaultValue);
if (p.defaultValue)
info.defaultVal = p.defaultValue->Evaluate(values);
m_preparedParams.push_back(std::move(info));
}
}

void MacroStatement::Render(OutStream& os, RenderContext& values)
{
PrepareMacroParams(values);

values.GetCurrentScope()[m_name] = Callable([this](const CallParams& callParams, OutStream& stream, RenderContext& context) {
InvokeMacroRenderer(callParams, stream, context);
});
}

void MacroStatement::InvokeMacroRenderer(const CallParams& callParams, OutStream& stream, RenderContext& context)
{
InternalValueMap callArgs;
InternalValueMap kwArgs;
InternalValueList varArgs;

SetupCallArgs(m_preparedParams, callParams, context, callArgs, kwArgs, varArgs);
InternalValueList arguments;
InternalValueList defaults;
for (auto& a : m_preparedParams)
{
arguments.emplace_back(a.name);
defaults.emplace_back(a.defaultVal);
}

auto& scope = context.EnterScope();
for (auto& a : callArgs)
scope[a.first] = std::move(a.second);

scope["kwargs"] = MapAdapter::CreateAdapter(std::move(kwArgs));
scope["varargs"] = ListAdapter::CreateAdapter(std::move(varArgs));

scope["name"] = m_name;
scope["arguments"] = ListAdapter::CreateAdapter(std::move(arguments));
scope["defaults"] = ListAdapter::CreateAdapter(std::move(defaults));

m_mainBody->Render(stream, context);

context.ExitScope();
}

void MacroStatement::SetupCallArgs(const std::vector<ArgumentInfo>& argsInfo, const CallParams& callParams, RenderContext& context, InternalValueMap& callArgs, InternalValueMap& kwArgs, InternalValueList& varArgs)
{
bool isSucceeded = true;
ParsedArguments args = helpers::ParseCallParams(argsInfo, callParams, isSucceeded);

for (auto& a : args.args)
callArgs[a.first] = a.second->Evaluate(context);

for (auto& a : args.extraKwArgs)
kwArgs[a.first] = a.second->Evaluate(context);

for (auto& a : args.extraPosArgs)
varArgs.push_back(a->Evaluate(context));
}

void MacroStatement::SetupMacroScope(InternalValueMap& scope)
{
;
}

void MacroCallStatement::Render(OutStream& os, RenderContext& values)
{
bool isMacroFound = false;
auto macroPtr = values.FindValue(m_macroName, isMacroFound);
if (!isMacroFound)
return;

auto& fnVal = macroPtr->second;
const Callable* callable = boost::get<Callable>(&fnVal);
if (callable == nullptr || callable->GetType() == Callable::Type::Expression)
return;

PrepareMacroParams(values);
auto& curScope = values.GetCurrentScope();
auto callerP = curScope.find("caller");
bool hasCallerVal = callerP != curScope.end();
InternalValue prevCaller;
if (hasCallerVal)
prevCaller = callerP->second;

curScope["caller"] = Callable([this](const CallParams& callParams, OutStream& stream, RenderContext& context) {
InvokeMacroRenderer(callParams, stream, context);
});

callable->GetStatementCallable()(m_callParams, os, values);

if (hasCallerVal)
curScope["caller"] = prevCaller;
else
values.GetCurrentScope().erase("caller");
}

void MacroCallStatement::SetupMacroScope(InternalValueMap& scope)
{

}


} // jinja2
Loading

0 comments on commit 0bd1418

Please sign in to comment.