From dc7a2693e9346e8c953aa1b23d875f3e2a04bb65 Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sun, 3 Dec 2023 14:31:47 +0300 Subject: [PATCH 1/4] Add list.append, list.extend --- src/internal_value.cpp | 87 +++++++++++++++++++++++++++++++++++++-- src/internal_value.h | 10 +++++ test/expressions_test.cpp | 19 +++++++++ 3 files changed, 112 insertions(+), 4 deletions(-) diff --git a/src/internal_value.cpp b/src/internal_value.cpp index 3f64eef..152bf27 100644 --- a/src/internal_value.cpp +++ b/src/internal_value.cpp @@ -144,6 +144,18 @@ struct SubscriptionVisitor : public visitors::BaseVisitor<> return TargetString(std::move(value)); } + template + InternalValue operator()(const ListAdapter& values, const std::basic_string& fieldName) const + { + return values.GetBuiltinMethod(ConvertString(fieldName)); + } + + template + InternalValue operator()(const ListAdapter& values, const nonstd::basic_string_view& fieldName) const + { + return values.GetBuiltinMethod(ConvertString(fieldName)); + } + InternalValue operator()(const ListAdapter& values, int64_t index) const { if (index < 0 || static_cast(index) >= values.GetSize()) @@ -492,20 +504,26 @@ ListAdapter ListAdapter::CreateAdapter(InternalValueList&& values) { public: explicit Adapter(InternalValueList&& values) - : m_values(std::move(values)) + : m_values(std::make_shared(std::move(values))) { } - size_t GetItemsCountImpl() const { return m_values.size(); } - nonstd::optional GetItem(int64_t idx) const override { return m_values[static_cast(idx)]; } + size_t GetItemsCountImpl() const { return m_values->size(); } + nonstd::optional GetItem(int64_t idx) const override { return (*m_values)[static_cast(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 m_values; }; return ListAdapter([accessor = Adapter(std::move(values))]() { return &accessor; }); @@ -698,6 +716,67 @@ InternalValueList ListAdapter::ToValueList() const return result; } +InternalValue BuiltinMethod(InternalValue self, Callable::ExpressionCallable method) +{ + auto result = InternalValue(RecursiveWrapper(Callable(Callable::Kind::SpecialFunc, 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 +{ + 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(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(); +} + template class Holder, bool CanModify> class InternalValueMapAdapter : public MapAccessorImpl> { diff --git a/src/internal_value.h b/src/internal_value.h index b674db1..975e1bc 100644 --- a/src/internal_value.h +++ b/src/internal_value.h @@ -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; } }; @@ -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; diff --git a/test/expressions_test.cpp b/test/expressions_test.cpp index 33f044a..a4d04ef 100644 --- a/test/expressions_test.cpp +++ b/test/expressions_test.cpp @@ -113,6 +113,25 @@ 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 +)") +{ +} + TEST(ExpressionTest, DoStatement) { std::string source = R"( From 55863ed203de32f51a538ae0f11a5548e60d22d8 Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sun, 3 Dec 2023 14:39:02 +0300 Subject: [PATCH 2/4] Add dict.update --- src/internal_value.cpp | 73 ++++++++++++++++++++++++--------------- src/internal_value.h | 2 ++ test/expressions_test.cpp | 35 +++++++++++++++++++ 3 files changed, 82 insertions(+), 28 deletions(-) diff --git a/src/internal_value.cpp b/src/internal_value.cpp index 152bf27..1fe0711 100644 --- a/src/internal_value.cpp +++ b/src/internal_value.cpp @@ -123,7 +123,7 @@ struct SubscriptionVisitor : public visitors::BaseVisitor<> { auto field = ConvertString(fieldName); if (!values.HasValue(field)) - return InternalValue(); + return values.GetBuiltinMethod(field); return values.GetValueByName(field); } @@ -133,7 +133,7 @@ struct SubscriptionVisitor : public visitors::BaseVisitor<> { auto field = ConvertString(fieldName); if (!values.HasValue(field)) - return InternalValue(); + return values.GetBuiltinMethod(field); return values.GetValueByName(field); } @@ -349,31 +349,6 @@ class ByRef const T* m_val{}; }; -template -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& other) const - { - return m_val == other.m_val; - } - bool operator!=(const ByVal& other) const - { - return !(*this == other); - } -private: - T m_val; -}; - template class BySharedVal { @@ -777,6 +752,48 @@ InternalValue ListAdapter::GetBuiltinMethod(const std::string& name) const return InternalValue(); } +struct DictUpdater : public visitors::BaseVisitor +{ + MapAdapter& m_self; + + DictUpdater(MapAdapter& self) : m_self(self) {} + + using BaseVisitor::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(arg, self); + + return InternalValue(); + } + ); +} + +InternalValue MapAdapter::GetBuiltinMethod(const std::string& name) const +{ + if (!m_accessorProvider || !m_accessorProvider()) + return InternalValue(); + + if (name == "update") + return DictUpdate(*this); + + return InternalValue(); +} + template class Holder, bool CanModify> class InternalValueMapAdapter : public MapAccessorImpl> { @@ -936,7 +953,7 @@ class ValuesMapAdapter : public MapAccessorImpl> MapAdapter CreateMapAdapter(InternalValueMap&& values) { - return MapAdapter([accessor = InternalValueMapAdapter(std::move(values))]() mutable { return &accessor; }); + return MapAdapter([accessor = InternalValueMapAdapter(std::move(values))]() mutable { return &accessor; }); } MapAdapter CreateMapAdapter(const InternalValueMap* values) diff --git a/src/internal_value.h b/src/internal_value.h index 975e1bc..66d4d54 100644 --- a/src/internal_value.h +++ b/src/internal_value.h @@ -370,6 +370,8 @@ class MapAdapter return GenericMap(); } + InternalValue GetBuiltinMethod(const std::string& name) const; + private: MapAccessorProvider m_accessorProvider; }; diff --git a/test/expressions_test.cpp b/test/expressions_test.cpp index a4d04ef..6be2497 100644 --- a/test/expressions_test.cpp +++ b/test/expressions_test.cpp @@ -132,6 +132,41 @@ R"( { } +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"( From ede40f1e67d1b010a8b69cda4ea63f44d9fe48d8 Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sun, 3 Dec 2023 15:35:57 +0300 Subject: [PATCH 3/4] Fix for loop with dictsort --- src/internal_value.cpp | 5 +++++ test/statements_tets.cpp | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/internal_value.cpp b/src/internal_value.cpp index 1fe0711..81bb6a1 100644 --- a/src/internal_value.cpp +++ b/src/internal_value.cpp @@ -278,6 +278,11 @@ struct ListConverter : public visitors::BaseVisitor return ListAdapter::CreateAdapter(std::move(list)); } + result_t operator()(const KeyValuePair& kv) const + { + return ListAdapter::CreateAdapter(InternalValueList{kv.key, kv.value}); + } + template result_t operator() (const std::basic_string& str) const { diff --git a/test/statements_tets.cpp b/test/statements_tets.cpp index d3dac52..7fc1fca 100644 --- a/test/statements_tets.cpp +++ b/test/statements_tets.cpp @@ -452,3 +452,23 @@ 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 +)" +) +{ +} From 14e29e9ce1d1f62646d0cbb3608cc27f609bcb92 Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sun, 3 Dec 2023 15:57:11 +0300 Subject: [PATCH 4/4] Add dict.items --- src/internal_value.cpp | 19 ++++++++++++++++++- test/statements_tets.cpp | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/internal_value.cpp b/src/internal_value.cpp index 81bb6a1..84bcbc6 100644 --- a/src/internal_value.cpp +++ b/src/internal_value.cpp @@ -698,7 +698,7 @@ InternalValueList ListAdapter::ToValueList() const InternalValue BuiltinMethod(InternalValue self, Callable::ExpressionCallable method) { - auto result = InternalValue(RecursiveWrapper(Callable(Callable::Kind::SpecialFunc, std::move(method)))); + auto result = InternalValue(RecursiveWrapper(Callable(Callable::Kind::UserCallable, std::move(method)))); result.SetParentData(std::move(self)); return result; } @@ -788,6 +788,21 @@ InternalValue DictUpdate(MapAdapter self) ); } +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{key, self.GetValueByName(key)})); + return InternalValue(ListAdapter::CreateAdapter(std::move(items))); + } + ); +} + InternalValue MapAdapter::GetBuiltinMethod(const std::string& name) const { if (!m_accessorProvider || !m_accessorProvider()) @@ -795,6 +810,8 @@ InternalValue MapAdapter::GetBuiltinMethod(const std::string& name) const if (name == "update") return DictUpdate(*this); + if (name == "items") + return DictItems(*this); return InternalValue(); } diff --git a/test/statements_tets.cpp b/test/statements_tets.cpp index 7fc1fca..43f7873 100644 --- a/test/statements_tets.cpp +++ b/test/statements_tets.cpp @@ -466,6 +466,24 @@ R"( 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 )"