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

Adding support for del d[...] for removing elements from dictionaries #1316

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions phylanx/ir/dictionary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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[](
Expand Down
59 changes: 59 additions & 0 deletions phylanx/plugins/listops/delete_operation.hpp
Original file line number Diff line number Diff line change
@@ -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 <phylanx/config.hpp>
#include <phylanx/execution_tree/primitives/base_primitive.hpp>
#include <phylanx/execution_tree/primitives/primitive_component_base.hpp>

#include <hpx/futures/future.hpp>

#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>

namespace phylanx { namespace execution_tree { namespace primitives {
/// \brief
/// \param
class delete_operation
: public primitive_component_base
, public std::enable_shared_from_this<delete_operation>
{
protected:
hpx::future<primitive_argument_type> 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
3 changes: 2 additions & 1 deletion phylanx/plugins/listops/listops.hpp
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -8,6 +8,7 @@

#include <phylanx/plugins/listops/append_operation.hpp>
#include <phylanx/plugins/listops/car_cdr_operation.hpp>
#include <phylanx/plugins/listops/delete_operation.hpp>
#include <phylanx/plugins/listops/dictionary_operation.hpp>
#include <phylanx/plugins/listops/len_operation.hpp>
#include <phylanx/plugins/listops/make_list.hpp>
Expand Down
19 changes: 19 additions & 0 deletions python/phylanx/ast/physl.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""

Expand Down
6 changes: 6 additions & 0 deletions src/ir/dictionary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/listops/append_operation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
135 changes: 135 additions & 0 deletions src/plugins/listops/delete_operation.cpp
Original file line number Diff line number Diff line change
@@ -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 <phylanx/config.hpp>
#include <phylanx/ir/dictionary.hpp>
#include <phylanx/plugins/listops/delete_operation.hpp>
#include <phylanx/util/matrix_iterators.hpp>

#include <hpx/errors/throw_exception.hpp>
#include <hpx/include/lcos.hpp>
#include <hpx/include/util.hpp>

#include <cstddef>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>

///////////////////////////////////////////////////////////////////////////////
namespace phylanx { namespace execution_tree { namespace primitives {

///////////////////////////////////////////////////////////////////////////
match_pattern_type const delete_operation::match_data = {
hpx::make_tuple("delete", std::vector<std::string>{"delete(_1, _2)"},
&create_delete_operation, &create_primitive<delete_operation>,
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<primitive_argument_type> 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<primitive_argument_type>&& arg0,
hpx::future<primitive_argument_type>&& 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
4 changes: 3 additions & 1 deletion src/plugins/listops/listops.cpp
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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,
Expand Down
19 changes: 19 additions & 0 deletions tests/regressions/python/1315_del_operator.py
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions tests/regressions/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down