diff --git a/phylanx/ir/dictionary.hpp b/phylanx/ir/dictionary.hpp index 6093b667f..1642c6e47 100644 --- a/phylanx/ir/dictionary.hpp +++ b/phylanx/ir/dictionary.hpp @@ -118,6 +118,9 @@ namespace phylanx { namespace ir { std::size_t size() const; bool empty() const; + bool has_key( + phylanx::execution_tree::primitive_argument_type const& key) const; + phylanx::execution_tree::primitive_argument_type& operator[]( phylanx::execution_tree::primitive_argument_type const& key); phylanx::execution_tree::primitive_argument_type& operator[]( diff --git a/phylanx/plugins/listops/delete_operation.hpp b/phylanx/plugins/listops/delete_operation.hpp new file mode 100644 index 000000000..03d7284c9 --- /dev/null +++ b/phylanx/plugins/listops/delete_operation.hpp @@ -0,0 +1,59 @@ +// Copyright (c) 2021 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace phylanx { namespace execution_tree { namespace primitives { + /// \brief + /// \param + class delete_operation + : public primitive_component_base + , public std::enable_shared_from_this + { + protected: + hpx::future eval( + primitive_arguments_type const& operands, + primitive_arguments_type const& args, + eval_context ctx) const override; + + public: + static match_pattern_type const match_data; + + delete_operation() = default; + + delete_operation(primitive_arguments_type&& operands, + std::string const& name, std::string const& codename); + + private: + primitive_argument_type delete_elements( + primitive_argument_type&& target, + primitive_argument_type&& index, eval_context ctx) const; + + primitive_argument_type delete_from_dict(ir::dictionary&& target, + primitive_argument_type&& index, eval_context ctx) const; + primitive_argument_type delete_from_list(ir::range&& target, + primitive_argument_type&& index, eval_context ctx) const; + }; + + inline primitive create_delete_operation(hpx::id_type const& locality, + primitive_arguments_type&& operands, std::string const& name = "", + std::string const& codename = "") + { + return create_primitive_component( + locality, "delete", std::move(operands), name, codename); + } +}}} // namespace phylanx::execution_tree::primitives diff --git a/phylanx/plugins/listops/listops.hpp b/phylanx/plugins/listops/listops.hpp index e1f7fb1dc..16ce09f7d 100644 --- a/phylanx/plugins/listops/listops.hpp +++ b/phylanx/plugins/listops/listops.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018 Hartmut Kaiser +// Copyright (c) 2018-20s21 Hartmut Kaiser // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/python/phylanx/ast/physl.py b/python/phylanx/ast/physl.py index 6a176f7ba..23c3bbb3d 100644 --- a/python/phylanx/ast/physl.py +++ b/python/phylanx/ast/physl.py @@ -959,6 +959,25 @@ def _Constant(self, node): # everything that's not a string can be directly passed on return '%s' % node.value + def _Delete(self, node): + """class Delete(targets) + + Represents a del statement. + + `targets` is a list of nodes, such as Name, Attribute or Subscript nodes. + """ + + if len(node.targets) > 1: + raise Exception("Phylanx does not support del operators with " + \ + "more than one argument.") + + if isinstance(node.targets[0], ast.Subscript): + target = self._apply_rule(node.targets[0].value) + index = self._apply_rule(node.targets[0].slice) + return ['delete', (target, index)] + + return '' + def _Div(self, node): """Leaf node, returning raw string of the 'division' operation.""" diff --git a/src/ir/dictionary.cpp b/src/ir/dictionary.cpp index 42b6c4d35..12dc523ff 100644 --- a/src/ir/dictionary.cpp +++ b/src/ir/dictionary.cpp @@ -327,6 +327,12 @@ namespace phylanx { namespace ir { return dict().empty(); } + bool dictionary::has_key( + phylanx::execution_tree::primitive_argument_type const& key) const + { + return dict().find(key) != dict().end(); + } + phylanx::execution_tree::primitive_argument_type& dictionary::operator[]( phylanx::execution_tree::primitive_argument_type const& key) { diff --git a/src/plugins/listops/append_operation.cpp b/src/plugins/listops/append_operation.cpp index 073238b96..ffd679a6b 100644 --- a/src/plugins/listops/append_operation.cpp +++ b/src/plugins/listops/append_operation.cpp @@ -57,7 +57,7 @@ namespace phylanx { namespace execution_tree { namespace primitives primitive_argument_type append_operation::handle_list_operands( primitive_argument_type && op1, primitive_argument_type && rhs) const { - ir::range lhs = + ir::range&& lhs = extract_list_value_strict(std::move(op1), name_, codename_); if (lhs.is_ref()) diff --git a/src/plugins/listops/delete_operation.cpp b/src/plugins/listops/delete_operation.cpp new file mode 100644 index 000000000..82c632597 --- /dev/null +++ b/src/plugins/listops/delete_operation.cpp @@ -0,0 +1,135 @@ +// Copyright (c) 2021 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +namespace phylanx { namespace execution_tree { namespace primitives { + + /////////////////////////////////////////////////////////////////////////// + match_pattern_type const delete_operation::match_data = { + hpx::make_tuple("delete", std::vector{"delete(_1, _2)"}, + &create_delete_operation, &create_primitive, + R"(target, index + Args: + + target (list or dict) : the container to delete a value from + index (key or list) : the index (indices) of the element(s) to + remove from the dictionary or list + + Returns: + + A new list or dictionary with the element(s) removed. Note that + `target` is not modified by this operation.)")}; + + /////////////////////////////////////////////////////////////////////////// + delete_operation::delete_operation(primitive_arguments_type&& operands, + std::string const& name, std::string const& codename) + : primitive_component_base(std::move(operands), name, codename) + { + } + + /////////////////////////////////////////////////////////////////////////// + primitive_argument_type delete_operation::delete_from_dict( + ir::dictionary&& target, primitive_argument_type&& index, + eval_context ctx) const + { + if (!target.has_key(index)) + { + HPX_THROW_EXCEPTION(hpx::bad_parameter, + "delete_operation::delete_from_dict", + generate_error_message( + "attempting to delete a non-existing dictionary entry", + ctx)); + } + + target.dict().erase(index); + return primitive_argument_type{std::move(target)}; + } + + primitive_argument_type delete_operation::delete_from_list( + ir::range&& target, primitive_argument_type&& index, + eval_context ctx) const + { + HPX_THROW_EXCEPTION(hpx::bad_parameter, + "delete_operation::delete_from_list", + generate_error_message( + "deleting an entry from list is not supported yet", ctx)); + } + + primitive_argument_type delete_operation::delete_elements( + primitive_argument_type&& target, primitive_argument_type&& index, + eval_context ctx) const + { + if (is_dictionary_operand_strict(target)) + { + return delete_from_dict(extract_dictionary_value_strict( + std::move(target), name_, codename_), + std::move(index), std::move(ctx)); + } + + if (is_list_operand_strict(target)) + { + return delete_from_list( + extract_list_value_strict(std::move(target), name_, codename_), + std::move(index), std::move(ctx)); + } + + HPX_THROW_EXCEPTION(hpx::bad_parameter, + "delete_operation::delete_elements", + generate_error_message("the delete_operation supports removing " + "elements from lists and dictionaries only", + ctx)); + } + + ////////////////////////////////////////////////////////////////////////// + hpx::future delete_operation::eval( + primitive_arguments_type const& operands, + primitive_arguments_type const& args, eval_context ctx) const + { + if (operands.size() != 2) + { + HPX_THROW_EXCEPTION(hpx::bad_parameter, "delete_operation::eval", + generate_error_message( + "the delete_operation primitive requires two arguments", + ctx)); + } + + if (!valid(operands[0]) || !valid(operands[1])) + { + HPX_THROW_EXCEPTION(hpx::bad_parameter, "delete_operation::eval", + generate_error_message( + "the delete_operation primitive requires that the " + "argument given by the operand are valid", + ctx)); + } + + auto this_ = this->shared_from_this(); + return hpx::dataflow( + hpx::launch::sync, + [this_ = std::move(this_), ctx = std::move(ctx)]( + hpx::future&& arg0, + hpx::future&& arg1) mutable + -> primitive_argument_type { + return this_->delete_elements( + arg0.get(), arg1.get(), std::move(ctx)); + }, + value_operand(operands[0], args, name_, codename_, ctx), + value_operand(operands[1], args, name_, codename_, ctx)); + } +}}} // namespace phylanx::execution_tree::primitives diff --git a/src/plugins/listops/listops.cpp b/src/plugins/listops/listops.cpp index bc1573358..c47b92650 100644 --- a/src/plugins/listops/listops.cpp +++ b/src/plugins/listops/listops.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018 Hartmut Kaiser +// Copyright (c) 2018-2021Hartmut Kaiser // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,6 +13,8 @@ PHYLANX_REGISTER_PLUGIN_MODULE(); PHYLANX_REGISTER_PLUGIN_FACTORY(append_operation_plugin, phylanx::execution_tree::primitives::append_operation::match_data); +PHYLANX_REGISTER_PLUGIN_FACTORY(delete_operation_plugin, + phylanx::execution_tree::primitives::delete_operation::match_data); PHYLANX_REGISTER_PLUGIN_FACTORY(dict_operation_plugin, phylanx::execution_tree::primitives::dict_operation::match_data); PHYLANX_REGISTER_PLUGIN_FACTORY(len_operation_plugin, diff --git a/tests/regressions/python/1315_del_operator.py b/tests/regressions/python/1315_del_operator.py new file mode 100644 index 000000000..bc1342f71 --- /dev/null +++ b/tests/regressions/python/1315_del_operator.py @@ -0,0 +1,19 @@ +# Copyright (c) 2021 Steven R. Brandt +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +# 1315: Missing del operator for dictionaries + +from phylanx import Phylanx + + +@Phylanx +def test(): + a = {"a": 1, "b": 2, "c": 3} + del a["a"] + return a + + +result = test() +assert result == {"b": 2, "c": 3}, result diff --git a/tests/regressions/python/CMakeLists.txt b/tests/regressions/python/CMakeLists.txt index 12aa2bc23..1b9607af8 100644 --- a/tests/regressions/python/CMakeLists.txt +++ b/tests/regressions/python/CMakeLists.txt @@ -73,6 +73,7 @@ set(tests 1303_iterate_over_array 1306_indexing 1312_matrix_iteration + 1315_del_operator array_len_494 array_shape_486 array_subscript_403