diff --git a/src/internal_value.cpp b/src/internal_value.cpp index 3f64eeff..84bcbc68 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); } @@ -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()) @@ -266,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 { @@ -337,31 +354,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 { @@ -492,20 +484,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 +696,126 @@ InternalValueList ListAdapter::ToValueList() const return result; } +InternalValue BuiltinMethod(InternalValue self, Callable::ExpressionCallable method) +{ + auto result = InternalValue(RecursiveWrapper(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 +{ + 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(); +} + +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 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()) + return InternalValue(); + + if (name == "update") + return DictUpdate(*this); + if (name == "items") + return DictItems(*this); + + return InternalValue(); +} + template class Holder, bool CanModify> class InternalValueMapAdapter : public MapAccessorImpl> { @@ -857,7 +975,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 b674db1c..66d4d54e 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; @@ -360,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 33f044ae..6be2497d 100644 --- a/test/expressions_test.cpp +++ b/test/expressions_test.cpp @@ -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"( diff --git a/test/statements_tets.cpp b/test/statements_tets.cpp index d3dac52b..43f78733 100644 --- a/test/statements_tets.cpp +++ b/test/statements_tets.cpp @@ -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 +)" +) +{ +}