Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add collections builtin methods #244

Closed
wants to merge 4 commits into from
Closed
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
182 changes: 150 additions & 32 deletions src/internal_value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ struct SubscriptionVisitor : public visitors::BaseVisitor<>
{
auto field = ConvertString<std::string>(fieldName);
if (!values.HasValue(field))
return InternalValue();
return values.GetBuiltinMethod(field);

return values.GetValueByName(field);
}
Expand All @@ -133,7 +133,7 @@ struct SubscriptionVisitor : public visitors::BaseVisitor<>
{
auto field = ConvertString<std::string>(fieldName);
if (!values.HasValue(field))
return InternalValue();
return values.GetBuiltinMethod(field);

return values.GetValueByName(field);
}
Expand All @@ -144,6 +144,18 @@ struct SubscriptionVisitor : public visitors::BaseVisitor<>
return TargetString(std::move(value));
}

template<typename CharT>
InternalValue operator()(const ListAdapter& values, const std::basic_string<CharT>& fieldName) const
{
return values.GetBuiltinMethod(ConvertString<std::string>(fieldName));
}

template<typename CharT>
InternalValue operator()(const ListAdapter& values, const nonstd::basic_string_view<CharT>& fieldName) const
{
return values.GetBuiltinMethod(ConvertString<std::string>(fieldName));
}

InternalValue operator()(const ListAdapter& values, int64_t index) const
{
if (index < 0 || static_cast<size_t>(index) >= values.GetSize())
Expand Down Expand Up @@ -266,6 +278,11 @@ struct ListConverter : public visitors::BaseVisitor<boost::optional<ListAdapter>
return ListAdapter::CreateAdapter(std::move(list));
}

result_t operator()(const KeyValuePair& kv) const
{
return ListAdapter::CreateAdapter(InternalValueList{kv.key, kv.value});
}

template<typename CharT>
result_t operator() (const std::basic_string<CharT>& str) const
{
Expand Down Expand Up @@ -337,31 +354,6 @@ class ByRef
const T* m_val{};
};

template<typename T>
class ByVal
{
public:
explicit ByVal(T&& val)
: m_val(std::move(val))
{
}
~ByVal() = default;

const T& Get() const { return m_val; }
T& Get() { return m_val; }
bool ShouldExtendLifetime() const { return false; }
bool operator==(const ByVal<T>& other) const
{
return m_val == other.m_val;
}
bool operator!=(const ByVal<T>& other) const
{
return !(*this == other);
}
private:
T m_val;
};

template<typename T>
class BySharedVal
{
Expand Down Expand Up @@ -492,20 +484,26 @@ ListAdapter ListAdapter::CreateAdapter(InternalValueList&& values)
{
public:
explicit Adapter(InternalValueList&& values)
: m_values(std::move(values))
: m_values(std::make_shared<InternalValueList>(std::move(values)))
{
}

size_t GetItemsCountImpl() const { return m_values.size(); }
nonstd::optional<InternalValue> GetItem(int64_t idx) const override { return m_values[static_cast<size_t>(idx)]; }
size_t GetItemsCountImpl() const { return m_values->size(); }
nonstd::optional<InternalValue> GetItem(int64_t idx) const override { return (*m_values)[static_cast<size_t>(idx)]; }
bool ShouldExtendLifetime() const override { return false; }
GenericList CreateGenericList() const override
{
return GenericList([adapter = *this]() -> const IListItemAccessor* { return &adapter; });
}

bool Append(const InternalValue& value) const override
{
m_values->push_back(value);
return true;
}

private:
InternalValueList m_values;
std::shared_ptr<InternalValueList> m_values;
};

return ListAdapter([accessor = Adapter(std::move(values))]() { return &accessor; });
Expand Down Expand Up @@ -698,6 +696,126 @@ InternalValueList ListAdapter::ToValueList() const
return result;
}

InternalValue BuiltinMethod(InternalValue self, Callable::ExpressionCallable method)
{
auto result = InternalValue(RecursiveWrapper<Callable>(Callable(Callable::Kind::UserCallable, std::move(method))));
result.SetParentData(std::move(self));
return result;
}

InternalValue ListAppend(ListAdapter self)
{
return BuiltinMethod(
self,
[self](const CallParams& params, RenderContext&) mutable {
if (params.posParams.size() == 1 && params.kwParams.size() == 0) {
self.Append(params.posParams[0]);
}
return InternalValue();
}
);
}

struct ListExtender : visitors::BaseVisitor<void>
{
ListAdapter& m_self;

ListExtender(ListAdapter& self) : m_self(self) {}

using BaseVisitor::operator();

void operator()(const ListAdapter& adapter) const
{
for (const auto& value : adapter)
m_self.Append(value);
}
};

InternalValue ListExtend(ListAdapter self)
{
return BuiltinMethod(
self,
[self](const CallParams& params, RenderContext&) mutable {
if (params.posParams.size() == 1 && params.kwParams.size() == 0) {
Apply<ListExtender>(params.posParams[0], self);
}
return InternalValue();
}
);
}

InternalValue ListAdapter::GetBuiltinMethod(const std::string& name) const
{
if (!m_accessorProvider || !m_accessorProvider())
return InternalValue();

if (name == "append")
return ListAppend(*this);
if (name == "extend")
return ListExtend(*this);

return InternalValue();
}

struct DictUpdater : public visitors::BaseVisitor<void>
{
MapAdapter& m_self;

DictUpdater(MapAdapter& self) : m_self(self) {}

using BaseVisitor<void>::operator();

void operator()(const MapAdapter& values) const
{
for (const auto& key : values.GetKeys())
m_self.SetValue(key, values.GetValueByName(key));
}
};

InternalValue DictUpdate(MapAdapter self)
{
return BuiltinMethod(
self,
[self](const CallParams& params, RenderContext&) mutable {
for (const auto& kv : params.kwParams)
self.SetValue(kv.first, kv.second);

for (const auto& arg : params.posParams)
Apply<DictUpdater>(arg, self);

return InternalValue();
}
);
}

InternalValue DictItems(MapAdapter self)
{
return BuiltinMethod(
self,
[self](const CallParams&, RenderContext&) {
InternalValueList items;
auto keys = self.GetKeys();
std::sort(keys.begin(), keys.end());
for (const auto& key : keys)
items.push_back(RecursiveWrapper<KeyValuePair>(KeyValuePair{key, self.GetValueByName(key)}));
return InternalValue(ListAdapter::CreateAdapter(std::move(items)));
}
);
}

InternalValue MapAdapter::GetBuiltinMethod(const std::string& name) const
{
if (!m_accessorProvider || !m_accessorProvider())
return InternalValue();

if (name == "update")
return DictUpdate(*this);
if (name == "items")
return DictItems(*this);

return InternalValue();
}

template<template<typename> class Holder, bool CanModify>
class InternalValueMapAdapter : public MapAccessorImpl<InternalValueMapAdapter<Holder, CanModify>>
{
Expand Down Expand Up @@ -857,7 +975,7 @@ class ValuesMapAdapter : public MapAccessorImpl<ValuesMapAdapter<Holder>>

MapAdapter CreateMapAdapter(InternalValueMap&& values)
{
return MapAdapter([accessor = InternalValueMapAdapter<ByVal, true>(std::move(values))]() mutable { return &accessor; });
return MapAdapter([accessor = InternalValueMapAdapter<BySharedVal, true>(std::move(values))]() mutable { return &accessor; });
}

MapAdapter CreateMapAdapter(const InternalValueMap* values)
Expand Down
12 changes: 12 additions & 0 deletions src/internal_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ struct IListAccessor
virtual ListAccessorEnumeratorPtr CreateListAccessorEnumerator() const = 0;
virtual GenericList CreateGenericList() const = 0;
virtual bool ShouldExtendLifetime() const = 0;
virtual bool Append(const InternalValue& value) const { return false; }
};


Expand Down Expand Up @@ -289,6 +290,15 @@ class ListAdapter
}
ListAccessorEnumeratorPtr GetEnumerator() const;

bool Append(const InternalValue& value) const
{
if (m_accessorProvider && m_accessorProvider())
return m_accessorProvider()->Append(value);
return false;
}

InternalValue GetBuiltinMethod(const std::string& name) const;

class Iterator;

Iterator begin() const;
Expand Down Expand Up @@ -360,6 +370,8 @@ class MapAdapter
return GenericMap();
}

InternalValue GetBuiltinMethod(const std::string& name) const;

private:
MapAccessorProvider m_accessorProvider;
};
Expand Down
54 changes: 54 additions & 0 deletions test/expressions_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,60 @@ R"(
{
}

MULTISTR_TEST(ExpressionsMultiStrTest, ListAppendAndExtend,
R"(
{% set l = ['1'] %}
{{ l|join(',') }}
{{ l.append('2') }}
{{ l.extend(['3','4']) }}
{{ l|join(',') }}
)",
//-----------
R"(

1


1,2,3,4
)")
{
}

MULTISTR_TEST(ExpressionsMultiStrTest, DictUpdate,
R"(
{% set a = {'a'='1'} %}
{{ a['a'] }}
{{ a['b'] }}
{{ a['c'] }}
{{ a['d'] }}
{{ a['e'] }}
{{ a.update({'b'='2','c'=3}) }}
{{ a.update(d=4,e=5) }}
{{ a['a'] }}
{{ a['b'] }}
{{ a['c'] }}
{{ a['d'] }}
{{ a['e'] }}
)",
//-----------
R"(

1






1
2
3
4
5
)")
{
}

TEST(ExpressionTest, DoStatement)
{
std::string source = R"(
Expand Down
38 changes: 38 additions & 0 deletions test/statements_tets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -452,3 +452,41 @@ TEST(RawTest, CommentRaw)
std::cout << result << std::endl;
EXPECT_STREQ("", result.c_str());
}

using ForTest = BasicTemplateRenderer;

MULTISTR_TEST(ForTest, ForKeyValueInDictSorted,
R"(
{% set d = {'a'=1,'b'=2} %}
{% for k, v in d|dictsort %}
{{ k }}: {{ v }}
{%- endfor %}
)",
//------------
R"(


a: 1
b: 2
)"
)
{
}

MULTISTR_TEST(ForTest, ForKeyValueInDictItems,
R"(
{% set d = {'a'=1,'b'=2} %}
{% for k, v in d.items() %}
{{ k }}: {{ v }}
{%- endfor %}
)",
//------------
R"(


a: 1
b: 2
)"
)
{
}