diff --git a/src/core/expr/declarations.h b/src/core/expr/declarations.h index 76e0a565f7..856f7ba942 100644 --- a/src/core/expr/declarations.h +++ b/src/core/expr/declarations.h @@ -104,7 +104,7 @@ namespace expr { Type, Func, List, - NamedList, + Dict, Frame, SliceAll, SliceInt, diff --git a/src/core/expr/eval_context.cc b/src/core/expr/eval_context.cc index 45fe8e07ff..d389091a35 100644 --- a/src/core/expr/eval_context.cc +++ b/src/core/expr/eval_context.cc @@ -286,7 +286,7 @@ py::oobj EvalContext::evaluate_delete() { Kind jkind = jexpr_->get_expr_kind(); if (jkind == Kind::SliceAll) { evaluate_delete_rows(); - } else if (jkind == Kind::NamedList) { + } else if (jkind == Kind::Dict) { throw TypeError() << "When del operator is applied, `j` selector cannot " "be a dictionary"; } diff --git a/src/core/expr/expr.cc b/src/core/expr/expr.cc index d416df26f8..e9e69795ae 100644 --- a/src/core/expr/expr.cc +++ b/src/core/expr/expr.cc @@ -21,9 +21,7 @@ //------------------------------------------------------------------------------ #include "expr/expr.h" #include "expr/head.h" -#include "expr/head_frame.h" #include "expr/head_func.h" -#include "expr/head_list.h" #include "expr/workframe.h" #include "expr/eval_context.h" #include "datatable.h" @@ -39,47 +37,9 @@ namespace expr { //------------------------------------------------------------------------------ OldExpr::OldExpr(py::robj src) - : FExpr() { - if (src.is_dtexpr()) _init_from_dtexpr(src); - // else if (src.is_int()) _init_from_int(src); - // else if (src.is_string()) _init_from_string(src); - // else if (src.is_float()) _init_from_float(src); - // else if (src.is_bool()) _init_from_bool(src); - // else if (src.is_slice()) _init_from_slice(src); - else if (src.is_list_or_tuple()) _init_from_list(src); - else if (src.is_dict()) _init_from_dictionary(src); - // else if (src.is_anytype()) _init_from_type(src); - else if (src.is_generator()) _init_from_iterable(src); - // else if (src.is_none()) _init_from_none(); - else if (src.is_frame()) _init_from_frame(src); - // else if (src.is_range()) _init_from_range(src); - else if (src.is_pandas_frame() || - src.is_pandas_series()) _init_from_pandas(src); - else if (src.is_numpy_array() || - src.is_numpy_marray()) _init_from_numpy(src); - // else if (src.is_ellipsis()) _init_from_ellipsis(); - else { - throw TypeError() << "An object of type " << src.typeobj() - << " cannot be used in an Expr"; - } -} - - -void OldExpr::_init_from_dictionary(py::robj src) { - strvec names; - for (auto kv : src.to_pydict()) { - if (!kv.first.is_string()) { - throw TypeError() << "Keys in the dictionary must be strings"; - } - names.push_back(kv.first.to_string()); - inputs.emplace_back(as_fexpr(kv.second)); - } - head = ptrHead(new Head_NamedList(std::move(names))); -} - + xassert(src.is_dtexpr()); -void OldExpr::_init_from_dtexpr(py::robj src) { auto op = src.get_attr("_op").to_size_t(); auto args = src.get_attr("_args").to_otuple(); auto params = src.get_attr("_params").to_otuple(); @@ -92,43 +52,6 @@ void OldExpr::_init_from_dtexpr(py::robj src) { -void OldExpr::_init_from_frame(py::robj src) { - head = Head_Frame::from_datatable(src); -} - - -void OldExpr::_init_from_iterable(py::robj src) { - for (auto elem : src.to_oiter()) { - inputs.emplace_back(as_fexpr(elem)); - } - head = ptrHead(new Head_List); -} - - -void OldExpr::_init_from_list(py::robj src) { - auto srclist = src.to_pylist(); - size_t nelems = srclist.size(); - for (size_t i = 0; i < nelems; ++i) { - inputs.emplace_back(as_fexpr(srclist[i])); - } - head = ptrHead(new Head_List); -} - - -void OldExpr::_init_from_numpy(py::robj src) { - head = Head_Frame::from_numpy(src); -} - - -void OldExpr::_init_from_pandas(py::robj src) { - head = Head_Frame::from_pandas(src); -} - - - - - - //------------------------------------------------------------------------------ // Expr core functionality //------------------------------------------------------------------------------ diff --git a/src/core/expr/expr.h b/src/core/expr/expr.h index c9a74db8d9..18475da1db 100644 --- a/src/core/expr/expr.h +++ b/src/core/expr/expr.h @@ -117,17 +117,6 @@ class OldExpr : public FExpr { void prepare_by(EvalContext&, Workframe&, std::vector&) const override; std::shared_ptr unnegate_column() const override; - - private: - // Construction helpers - void _init_from_dictionary(py::robj); - void _init_from_dtexpr(py::robj); - void _init_from_frame(py::robj); - void _init_from_iterable(py::robj); - void _init_from_list(py::robj); - void _init_from_numpy(py::robj); - void _init_from_pandas(py::robj); - void _init_from_type(py::robj); }; diff --git a/src/core/expr/fexpr.cc b/src/core/expr/fexpr.cc index f33f88c749..5d7147f29a 100644 --- a/src/core/expr/fexpr.cc +++ b/src/core/expr/fexpr.cc @@ -20,6 +20,9 @@ // IN THE SOFTWARE. //------------------------------------------------------------------------------ #include "expr/fexpr.h" +#include "expr/fexpr_dict.h" +#include "expr/fexpr_frame.h" +#include "expr/fexpr_list.h" #include "expr/fexpr_literal.h" #include "expr/expr.h" // OldExpr #include "python/obj.h" @@ -72,30 +75,28 @@ static ptrExpr extract_fexpr(py::robj src) { ptrExpr as_fexpr(py::robj src) { if (src.is_fexpr()) return extract_fexpr(src); - else if (src.is_dtexpr()) ; + else if (src.is_dtexpr()) return std::make_shared(src); else if (src.is_int()) return FExpr_Literal_Int::make(src); else if (src.is_string()) return FExpr_Literal_String::make(src); else if (src.is_float()) return FExpr_Literal_Float::make(src); else if (src.is_bool()) return FExpr_Literal_Bool::make(src); else if (src.is_slice()) return FExpr_Literal_Slice::make(src); - else if (src.is_list_or_tuple()) ; - else if (src.is_dict()) ; + else if (src.is_list_or_tuple()) return FExpr_List::make(src); + else if (src.is_dict()) return FExpr_Dict::make(src); else if (src.is_anytype()) return FExpr_Literal_Type::make(src); - else if (src.is_generator()) ; + else if (src.is_generator()) return FExpr_List::make(src); else if (src.is_none()) return FExpr_Literal_None::make(); - else if (src.is_frame()) ; + else if (src.is_frame()) return FExpr_Frame::from_datatable(src); else if (src.is_range()) return FExpr_Literal_Range::make(src); else if (src.is_pandas_frame() || - src.is_pandas_series()) ; + src.is_pandas_series()) return FExpr_Frame::from_pandas(src); else if (src.is_numpy_array() || - src.is_numpy_marray()) ; + src.is_numpy_marray()) return FExpr_Frame::from_numpy(src); else if (src.is_ellipsis()) return ptrExpr(new FExpr_Literal_SliceAll()); else { throw TypeError() << "An object of type " << src.typeobj() << " cannot be used in an FExpr"; } - // TODO: remove this - return std::make_shared(src); } diff --git a/src/core/expr/fexpr_dict.cc b/src/core/expr/fexpr_dict.cc new file mode 100644 index 0000000000..27200ffec9 --- /dev/null +++ b/src/core/expr/fexpr_dict.cc @@ -0,0 +1,129 @@ +//------------------------------------------------------------------------------ +// Copyright 2019-2020 H2O.ai +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//------------------------------------------------------------------------------ +#include "expr/eval_context.h" +#include "expr/fexpr_dict.h" +#include "expr/workframe.h" +#include "utils/assert.h" +#include "utils/exceptions.h" +namespace dt { +namespace expr { + + +//------------------------------------------------------------------------------ +// Constructors +//------------------------------------------------------------------------------ + +FExpr_Dict::FExpr_Dict(strvec&& names, vecExpr&& args) + : names_(std::move(names)), + args_(std::move(args)) +{ + xassert(names_.size() == args_.size()); +} + + +ptrExpr FExpr_Dict::make(py::robj src) { + strvec names; + vecExpr args; + for (auto kv : src.to_pydict()) { + if (!kv.first.is_string()) { + throw TypeError() << "Keys in the dictionary must be strings"; + } + names.push_back(kv.first.to_string()); + args.emplace_back(as_fexpr(kv.second)); + } + return ptrExpr(new FExpr_Dict(std::move(names), std::move(args))); +} + + + + +//------------------------------------------------------------------------------ +// Evaluation +//------------------------------------------------------------------------------ + +Workframe FExpr_Dict::evaluate_n(EvalContext& ctx) const { + Workframe outputs(ctx); + for (size_t i = 0; i < args_.size(); ++i) { + Workframe arg_out = args_[i]->evaluate_n(ctx); + arg_out.rename(names_[i]); + outputs.cbind( std::move(arg_out) ); + } + return outputs; +} + + +Workframe FExpr_Dict::evaluate_r(EvalContext& ctx, const sztvec&) const { + return evaluate_n(ctx); +} + + +Workframe FExpr_Dict::evaluate_f(EvalContext&, size_t) const { + throw TypeError() << "A dictionary cannot be used as an f-selector"; +} + + +Workframe FExpr_Dict::evaluate_j(EvalContext& ctx) const { + return evaluate_n(ctx); +} + + +RowIndex FExpr_Dict::evaluate_i(EvalContext&) const { + throw TypeError() << "A dictionary cannot be used as an i-selector"; +} + + +RiGb FExpr_Dict::evaluate_iby(EvalContext&) const { + throw TypeError() << "A dictionary cannot be used as an i-selector"; +} + + + + +//------------------------------------------------------------------------------ +// Misc +//------------------------------------------------------------------------------ + +Kind FExpr_Dict::get_expr_kind() const { + return Kind::Dict; +} + + +int FExpr_Dict::precedence() const noexcept { + return 17; +} + + +std::string FExpr_Dict::repr() const { + std::string out = "{"; + for (size_t i = 0; i < names_.size(); ++i) { + if (i) out += ", "; + out += names_[i]; // TODO: escape if necessary + out += '='; + out += args_[i]->repr(); + } + out += '}'; + return out; +} + + + +}} // namespace dt::expr diff --git a/src/core/expr/head_list.h b/src/core/expr/fexpr_dict.h similarity index 56% rename from src/core/expr/head_list.h rename to src/core/expr/fexpr_dict.h index 2a7fefcf5b..c5af18794c 100644 --- a/src/core/expr/head_list.h +++ b/src/core/expr/fexpr_dict.h @@ -19,45 +19,39 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //------------------------------------------------------------------------------ -#ifndef dt_EXPR_HEAD_LIST_h -#define dt_EXPR_HEAD_LIST_h +#ifndef dt_EXPR_FEXPR_DICT_h +#define dt_EXPR_FEXPR_DICT_h #include #include -#include "expr/head.h" +#include "expr/declarations.h" +#include "expr/fexpr.h" namespace dt { namespace expr { -class Head_List : public Head { - public: - Kind get_expr_kind() const override; - Workframe evaluate_n(const vecExpr&, EvalContext&) const override; - Workframe evaluate_j(const vecExpr&, EvalContext&) const override; - Workframe evaluate_r(const vecExpr&, EvalContext&, const sztvec&) const override; - Workframe evaluate_f(EvalContext&, size_t) const override; - RowIndex evaluate_i(const vecExpr&, EvalContext&) const override; - RiGb evaluate_iby(const vecExpr&, EvalContext&) const override; - void prepare_by(const vecExpr&, EvalContext&, Workframe&, std::vector&) const override; -}; - - - -class Head_NamedList : public Head { +class FExpr_Dict : public FExpr { private: - strvec names; + strvec names_; + vecExpr args_; public: - Head_NamedList(strvec&&); - Kind get_expr_kind() const override; - Workframe evaluate_n(const vecExpr&, EvalContext&) const override; - Workframe evaluate_j(const vecExpr&, EvalContext&) const override; - Workframe evaluate_r(const vecExpr&, EvalContext&, const sztvec&) const override; + FExpr_Dict(strvec&&, vecExpr&&); + static ptrExpr make(py::robj); + + Workframe evaluate_n(EvalContext&) const override; + Workframe evaluate_j(EvalContext&) const override; + Workframe evaluate_r(EvalContext&, const sztvec&) const override; Workframe evaluate_f(EvalContext&, size_t) const override; - RowIndex evaluate_i(const vecExpr&, EvalContext&) const override; - RiGb evaluate_iby(const vecExpr&, EvalContext&) const override; + RowIndex evaluate_i(EvalContext&) const override; + RiGb evaluate_iby(EvalContext&) const override; + + Kind get_expr_kind() const override; + int precedence() const noexcept override; + std::string repr() const override; }; + }} // namespace dt::expr #endif diff --git a/src/core/expr/head_frame.cc b/src/core/expr/fexpr_frame.cc similarity index 85% rename from src/core/expr/head_frame.cc rename to src/core/expr/fexpr_frame.cc index e8967a2c36..38113f53fd 100644 --- a/src/core/expr/head_frame.cc +++ b/src/core/expr/fexpr_frame.cc @@ -20,8 +20,7 @@ // IN THE SOFTWARE. //------------------------------------------------------------------------------ #include "expr/eval_context.h" -#include "expr/expr.h" -#include "expr/head_frame.h" +#include "expr/fexpr_frame.h" #include "expr/workframe.h" #include "frame/py_frame.h" #include "ltype.h" @@ -36,31 +35,27 @@ namespace expr { // Constructors //------------------------------------------------------------------------------ -ptrHead Head_Frame::from_datatable(py::robj src) { - return ptrHead(new Head_Frame(src)); +ptrExpr FExpr_Frame::from_datatable(py::robj src) { + return ptrExpr(new FExpr_Frame(src)); } -ptrHead Head_Frame::from_numpy(py::robj src) { +ptrExpr FExpr_Frame::from_numpy(py::robj src) { py::oobj src_frame = py::Frame::oframe(src); - return ptrHead(new Head_Frame(src_frame, /* ignore_names_= */ true)); + return ptrExpr(new FExpr_Frame(src_frame, /* ignore_names_= */ true)); } -ptrHead Head_Frame::from_pandas(py::robj src) { +ptrExpr FExpr_Frame::from_pandas(py::robj src) { py::oobj src_frame = py::Frame::oframe(src); - return ptrHead(new Head_Frame(src_frame)); + return ptrExpr(new FExpr_Frame(src_frame)); } -Head_Frame::Head_Frame(py::robj src, bool ignore_names) +FExpr_Frame::FExpr_Frame(py::robj src, bool ignore_names) : container_(src), dt_(src.to_datatable()), ignore_names_(ignore_names) {} -Kind Head_Frame::get_expr_kind() const { - return Kind::Frame; -} - //------------------------------------------------------------------------------ @@ -79,12 +74,8 @@ Kind Head_Frame::get_expr_kind() const { // more advanced types of joins, an explicit `join()` clause has to // be used. // -Workframe Head_Frame::evaluate_n( - const vecExpr& args, EvalContext& ctx) const +Workframe FExpr_Frame::evaluate_n(EvalContext& ctx) const { - (void) args; - xassert(args.size() == 0); - size_t nrows = dt_->nrows(); if (!(nrows == ctx.nrows() || nrows == 1)) { throw ValueError() << "Frame has " << nrows << " rows, and " @@ -111,10 +102,8 @@ Workframe Head_Frame::evaluate_n( // In addition, standalone X in j can be used to with an i-filter: // DT[, X] is thus equivalent to X[DT[:, ], :]. // -Workframe Head_Frame::evaluate_j( - const vecExpr& args, EvalContext& ctx) const -{ - return evaluate_n(args, ctx); +Workframe FExpr_Frame::evaluate_j(EvalContext& ctx) const { + return evaluate_n(ctx); } @@ -126,14 +115,14 @@ Workframe Head_Frame::evaluate_j( // mode. The stypes of the RHS can be ignored, since the stypes of X // take precedence in this case. // -Workframe Head_Frame::evaluate_r( - const vecExpr& args, EvalContext& ctx, const sztvec& indices) const +Workframe FExpr_Frame::evaluate_r( + EvalContext& ctx, const sztvec& indices) const { // Allow to assign an empty frame to an empty column set (see issue #1544) if (indices.size() == 0 && dt_->nrows() == 0 && dt_->ncols() == 0) { return Workframe(ctx); } - return evaluate_n(args, ctx); + return evaluate_n(ctx); } @@ -141,7 +130,7 @@ Workframe Head_Frame::evaluate_r( // If X is a Frame, then the expression f[X] (as in DT[:, f[X]]) just // doesn't make much sense, so we disallow it. // -Workframe Head_Frame::evaluate_f(EvalContext&, size_t) const { +Workframe FExpr_Frame::evaluate_f(EvalContext&, size_t) const { throw TypeError() << "A Frame cannot be used inside an f-expression"; } @@ -167,7 +156,7 @@ Workframe Head_Frame::evaluate_f(EvalContext&, size_t) const { // to mean the same as DT[X == f[X.name], :]. For now, however, the // use of keyed frames in i node is disallowed. // -RowIndex Head_Frame::evaluate_i(const vecExpr&, EvalContext& ctx) const { +RowIndex FExpr_Frame::evaluate_i(EvalContext& ctx) const { if (dt_->ncols() != 1) { throw ValueError() << "Only a single-column Frame may be used as `i` " "selector, instead got a Frame with " << dt_->ncols() << " columns"; @@ -216,12 +205,32 @@ RowIndex Head_Frame::evaluate_i(const vecExpr&, EvalContext& ctx) const { // // I cannot think of a good interpretation of such notation. // -RiGb Head_Frame::evaluate_iby(const vecExpr&, EvalContext&) const { +RiGb FExpr_Frame::evaluate_iby(EvalContext&) const { throw TypeError() << "A Frame cannot be used as an i-selector " "in the presence of a groupby"; } +//------------------------------------------------------------------------------ +// Miscellaneous +//------------------------------------------------------------------------------ + +Kind FExpr_Frame::get_expr_kind() const { + return Kind::Frame; +} + + +int FExpr_Frame::precedence() const noexcept { + return 16; +} + + +std::string FExpr_Frame::repr() const { + return container_.repr().to_string(); +} + + + }} // namespace dt::expr diff --git a/src/core/expr/head_frame.h b/src/core/expr/fexpr_frame.h similarity index 74% rename from src/core/expr/head_frame.h rename to src/core/expr/fexpr_frame.h index ecd9c0db49..0f245e0886 100644 --- a/src/core/expr/head_frame.h +++ b/src/core/expr/fexpr_frame.h @@ -19,11 +19,11 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //------------------------------------------------------------------------------ -#ifndef dt_EXPR_HEAD_FRAME_h -#define dt_EXPR_HEAD_FRAME_h +#ifndef dt_EXPR_FEXPR_FRAME_h +#define dt_EXPR_FEXPR_FRAME_h #include #include -#include "expr/head.h" +#include "expr/fexpr.h" namespace dt { namespace expr { @@ -39,7 +39,7 @@ namespace expr { * The flag `ignore_names_` is set when the class is create from a * numpy array, since the numpy array has no column names. */ -class Head_Frame : public Head { +class FExpr_Frame : public FExpr { private: py::oobj container_; DataTable* dt_; @@ -47,19 +47,22 @@ class Head_Frame : public Head { size_t : 56; public: - static ptrHead from_datatable(py::robj src); - static ptrHead from_numpy(py::robj src); - static ptrHead from_pandas(py::robj src); + static ptrExpr from_datatable(py::robj src); + static ptrExpr from_numpy(py::robj src); + static ptrExpr from_pandas(py::robj src); - Head_Frame(py::robj src, bool ignore_names = false); + FExpr_Frame(py::robj src, bool ignore_names = false); Kind get_expr_kind() const override; - Workframe evaluate_n(const vecExpr&, EvalContext&) const override; - Workframe evaluate_j(const vecExpr&, EvalContext&) const override; - Workframe evaluate_r(const vecExpr&, EvalContext&, const sztvec&) const override; + Workframe evaluate_n(EvalContext&) const override; + Workframe evaluate_j(EvalContext&) const override; + Workframe evaluate_r(EvalContext&, const sztvec&) const override; Workframe evaluate_f(EvalContext&, size_t) const override; - RowIndex evaluate_i(const vecExpr&, EvalContext&) const override; - RiGb evaluate_iby(const vecExpr&, EvalContext&) const override; + RowIndex evaluate_i(EvalContext&) const override; + RiGb evaluate_iby(EvalContext&) const override; + + int precedence() const noexcept override; + std::string repr() const override; }; diff --git a/src/core/expr/head_list.cc b/src/core/expr/fexpr_list.cc similarity index 64% rename from src/core/expr/head_list.cc rename to src/core/expr/fexpr_list.cc index 6b07d168ba..0110c9181a 100644 --- a/src/core/expr/head_list.cc +++ b/src/core/expr/fexpr_list.cc @@ -19,30 +19,51 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //------------------------------------------------------------------------------ -#include "expr/head_list.h" -#include "expr/expr.h" -#include "expr/workframe.h" #include "expr/eval_context.h" +#include "expr/fexpr_list.h" +#include "expr/workframe.h" #include "utils/assert.h" #include "utils/exceptions.h" -#include "sort.h" namespace dt { namespace expr { //------------------------------------------------------------------------------ -// Head_List +// Constructors //------------------------------------------------------------------------------ -Kind Head_List::get_expr_kind() const { - return Kind::List; +FExpr_List::FExpr_List(vecExpr&& args) + : args_(std::move(args)) +{} + + +ptrExpr FExpr_List::make(py::robj src) { + vecExpr args; + if (src.is_list_or_tuple()) { + auto srclist = src.to_pylist(); + args.reserve(srclist.size()); + for (size_t i = 0; i < srclist.size(); ++i) { + args.emplace_back(as_fexpr(srclist[i])); + } + } + else { + for (auto elem : src.to_oiter()) { + args.emplace_back(as_fexpr(elem)); + } + } + return ptrExpr(new FExpr_List(std::move(args))); } -Workframe Head_List::evaluate_n( - const vecExpr& inputs, EvalContext& ctx) const -{ + + + +//------------------------------------------------------------------------------ +// Evaluation +//------------------------------------------------------------------------------ + +Workframe FExpr_List::evaluate_n(EvalContext& ctx) const { Workframe outputs(ctx); - for (const auto& arg : inputs) { + for (const auto& arg : args_) { outputs.cbind( arg->evaluate_n(ctx) ); } return outputs; @@ -52,23 +73,23 @@ Workframe Head_List::evaluate_n( // Evaluate list as a replacement target when replacing columns at // `indices` within the "root" Frame. // -Workframe Head_List::evaluate_r( - const vecExpr& inputs, EvalContext& ctx, const sztvec& indices) const +Workframe FExpr_List::evaluate_r( + EvalContext& ctx, const sztvec& indices) const { Workframe outputs(ctx); - if (inputs.size() == indices.size()) { - for (size_t i = 0; i < inputs.size(); ++i) { - outputs.cbind( inputs[i]->evaluate_r(ctx, {indices[i]}) ); + if (args_.size() == indices.size()) { + for (size_t i = 0; i < args_.size(); ++i) { + outputs.cbind( args_[i]->evaluate_r(ctx, {indices[i]}) ); } } - else if (inputs.size() == 1) { + else if (args_.size() == 1) { for (size_t i = 0; i < indices.size(); ++i) { - outputs.cbind( inputs[0]->evaluate_r(ctx, {indices[i]}) ); + outputs.cbind( args_[0]->evaluate_r(ctx, {indices[i]}) ); } } else { throw ValueError() << "The LHS of the replacement has " << indices.size() - << " columns, while the RHS has " << inputs.size() + << " columns, while the RHS has " << args_.size() << " replacement expressions"; } return outputs; @@ -76,7 +97,7 @@ Workframe Head_List::evaluate_r( -Workframe Head_List::evaluate_f(EvalContext&, size_t) const { +Workframe FExpr_List::evaluate_f(EvalContext&, size_t) const { throw TypeError() << "A list or a sequence cannot be used inside an f-selector"; } @@ -111,10 +132,10 @@ static const char* _name_type(Kind t) { // element kinds, an error must be thrown. // A list containing only `None` or `slice_all` should resolve as Kind::Int. // -static Kind _resolve_list_kind(const vecExpr& inputs) { +static Kind _resolve_list_kind(const vecExpr& args) { auto listkind = Kind::Unknown; - for (size_t i = 0; i < inputs.size(); ++i) { - auto kind = inputs[i]->get_expr_kind(); + for (size_t i = 0; i < args.size(); ++i) { + auto kind = args[i]->get_expr_kind(); xassert(kind != Kind::Unknown); if (kind == listkind) continue; if (kind == Kind::Bool) { @@ -133,7 +154,7 @@ static Kind _resolve_list_kind(const vecExpr& inputs) { throw TypeError() << "A floating value cannot be used as a column selector"; } - if (kind == Kind::List || kind == Kind::NamedList) { + if (kind == Kind::List || kind == Kind::Dict) { throw TypeError() << "Nested lists are not supported as a column selector"; } @@ -153,41 +174,37 @@ static Kind _resolve_list_kind(const vecExpr& inputs) { } -static Workframe _evaluate_bool_list(const vecExpr& inputs, EvalContext& ctx) { +static Workframe _evaluate_bool_list(const vecExpr& args, EvalContext& ctx) { DataTable* df = ctx.get_datatable(0); - if (inputs.size() != df->ncols()) { + if (args.size() != df->ncols()) { throw ValueError() << "The length of boolean list in `j` selector does not match the " "number of columns in the Frame: " - << inputs.size() << " vs " << df->ncols(); + << args.size() << " vs " << df->ncols(); } Workframe outputs(ctx); - for (size_t i = 0; i < inputs.size(); ++i) { - bool x = inputs[i]->evaluate_bool(); + for (size_t i = 0; i < args.size(); ++i) { + bool x = args[i]->evaluate_bool(); if (x) outputs.add_ref_column(0, i); } return outputs; } -static Workframe _evaluate_f_list( - const vecExpr& inputs, EvalContext& ctx) -{ +static Workframe _evaluate_f_list(const vecExpr& args, EvalContext& ctx) { Workframe outputs(ctx); - for (const auto& arg : inputs) { + for (const auto& arg : args) { outputs.cbind( arg->evaluate_j(ctx) ); } return outputs; } -Workframe Head_List::evaluate_j( - const vecExpr& inputs, EvalContext& ctx) const -{ - auto kind = _resolve_list_kind(inputs); - if (kind == Kind::Bool) return _evaluate_bool_list(inputs, ctx); - if (kind == Kind::Func) return evaluate_n(inputs, ctx); - return _evaluate_f_list(inputs, ctx); +Workframe FExpr_List::evaluate_j(EvalContext& ctx) const { + auto kind = _resolve_list_kind(args_); + if (kind == Kind::Bool) return _evaluate_bool_list(args_, ctx); + if (kind == Kind::Func) return evaluate_n(ctx); + return _evaluate_f_list(args_, ctx); } @@ -196,10 +213,10 @@ Workframe Head_List::evaluate_j( // i-evaluation //------------------------------------------------------------------------------ -static RowIndex _evaluate_i_other(const vecExpr& inputs, EvalContext& ctx) { +static RowIndex _evaluate_i_other(const vecExpr& args, EvalContext& ctx) { std::vector rowindices; - for (size_t i = 0; i < inputs.size(); ++i) { - auto ikind = inputs[i]->get_expr_kind(); + for (size_t i = 0; i < args.size(); ++i) { + auto ikind = args[i]->get_expr_kind(); if (ikind == Kind::None) continue; if (!(ikind == Kind::Int || ikind == Kind::SliceInt || ikind == Kind::SliceAll || ikind == Kind::Func || @@ -207,7 +224,7 @@ static RowIndex _evaluate_i_other(const vecExpr& inputs, EvalContext& ctx) { throw TypeError() << "Invalid expression of type " << _name_type(ikind) << " at index " << i << " in the i-selector list"; } - RowIndex ri = inputs[i]->evaluate_i(ctx); + RowIndex ri = args[i]->evaluate_i(ctx); if (!ri) ri = RowIndex(0, ctx.nrows(), 1); rowindices.push_back(std::move(ri)); } @@ -215,23 +232,23 @@ static RowIndex _evaluate_i_other(const vecExpr& inputs, EvalContext& ctx) { } -static RowIndex _evaluate_i_bools(const vecExpr& inputs, EvalContext& ctx) { +static RowIndex _evaluate_i_bools(const vecExpr& args, EvalContext& ctx) { size_t nrows = ctx.nrows(); - if (inputs.size() != nrows) { + if (args.size() != nrows) { throw ValueError() << "The length of boolean list in i selector does not match the " - "number of rows in the Frame: " << inputs.size() << " vs " << nrows; + "number of rows in the Frame: " << args.size() << " vs " << nrows; } Buffer databuf = Buffer::mem(nrows * sizeof(int32_t)); auto data = static_cast(databuf.xptr()); size_t data_index = 0; for (size_t i = 0; i < nrows; ++i) { - if (inputs[i]->get_expr_kind() != Kind::Bool) { + if (args[i]->get_expr_kind() != Kind::Bool) { throw TypeError() << "Element " << i << " in the i-selector list is " - << _name_type(inputs[i]->get_expr_kind()) << ", whereas the previous " + << _name_type(args[i]->get_expr_kind()) << ", whereas the previous " "elements were boolean"; } - bool x = inputs[i]->evaluate_bool(); + bool x = args[i]->evaluate_bool(); if (x) { data[data_index++] = static_cast(i); } @@ -241,15 +258,15 @@ static RowIndex _evaluate_i_bools(const vecExpr& inputs, EvalContext& ctx) { } -static RowIndex _evaluate_i_ints(const vecExpr& inputs, EvalContext& ctx) { +static RowIndex _evaluate_i_ints(const vecExpr& args, EvalContext& ctx) { auto inrows = static_cast(ctx.nrows()); - Buffer databuf = Buffer::mem(inputs.size() * sizeof(int32_t)); + Buffer databuf = Buffer::mem(args.size() * sizeof(int32_t)); int32_t* data = static_cast(databuf.xptr()); size_t data_index = 0; - for (size_t i = 0; i < inputs.size(); ++i) { - auto ikind = inputs[i]->get_expr_kind(); + for (size_t i = 0; i < args.size(); ++i) { + auto ikind = args[i]->get_expr_kind(); if (ikind == Kind::Int) { - int64_t x = inputs[i]->evaluate_int(); + int64_t x = args[i]->evaluate_int(); if (x < -inrows || x >= inrows) { throw ValueError() << "Index " << x << " is invalid for a Frame with " << inrows << " rows"; @@ -258,7 +275,7 @@ static RowIndex _evaluate_i_ints(const vecExpr& inputs, EvalContext& ctx) { } else if (ikind == Kind::None) {} // skip else if (ikind == Kind::SliceAll || ikind == Kind::SliceInt) { - return _evaluate_i_other(inputs, ctx); + return _evaluate_i_other(args, ctx); } else { throw TypeError() << "Invalid item of type " << _name_type(ikind) @@ -272,44 +289,37 @@ static RowIndex _evaluate_i_ints(const vecExpr& inputs, EvalContext& ctx) { -RowIndex Head_List::evaluate_i(const vecExpr& inputs, EvalContext& ctx) const -{ - if (inputs.empty()) { +RowIndex FExpr_List::evaluate_i(EvalContext& ctx) const { + if (args_.empty()) { return RowIndex(0, 0, 1); // Select-nothing rowindex } - auto kind0 = inputs[0]->get_expr_kind(); - if (kind0 == Kind::Bool) return _evaluate_i_bools(inputs, ctx); - if (kind0 == Kind::Int) return _evaluate_i_ints(inputs, ctx); - return _evaluate_i_other(inputs, ctx); + auto kind0 = args_[0]->get_expr_kind(); + if (kind0 == Kind::Bool) return _evaluate_i_bools(args_, ctx); + if (kind0 == Kind::Int) return _evaluate_i_ints(args_, ctx); + return _evaluate_i_other(args_, ctx); } -RiGb Head_List::evaluate_iby(const vecExpr&, EvalContext&) const { - throw NotImplError() << "Head_List::evaluate_iby() not implemented yet"; +RiGb FExpr_List::evaluate_iby(EvalContext&) const { + throw NotImplError() << "FExpr_List::evaluate_iby() not implemented yet"; } - -//------------------------------------------------------------------------------ -// prepare_by -//------------------------------------------------------------------------------ - -void Head_List::prepare_by(const vecExpr& inputs, EvalContext& ctx, - Workframe& outwf, std::vector& outflags) - const +void FExpr_List::prepare_by( + EvalContext& ctx, Workframe& outwf, std::vector& outflags) const { - if (inputs.empty()) return; + if (args_.empty()) return; - auto kind = _resolve_list_kind(inputs); + auto kind = _resolve_list_kind(args_); if (kind == Kind::Str || kind == Kind::Int) { - for (const auto& arg : inputs) { + for (const auto& arg : args_) { outwf.cbind( arg->evaluate_f(ctx, 0) ); outflags.push_back(SortFlag::NONE); } } else if (kind == Kind::Func) { - for (const auto& arg : inputs) { + for (const auto& arg : args_) { auto negcol = arg->unnegate_column(); if (negcol) { outwf.cbind( negcol->evaluate_n(ctx) ); @@ -330,59 +340,30 @@ void Head_List::prepare_by(const vecExpr& inputs, EvalContext& ctx, //------------------------------------------------------------------------------ -// Head_NamedList +// Miscellaneous //------------------------------------------------------------------------------ -Head_NamedList::Head_NamedList(strvec&& names_) - : names(std::move(names_)) {} - -Kind Head_NamedList::get_expr_kind() const { - return Kind::NamedList; -} - - -Workframe Head_NamedList::evaluate_n( - const vecExpr& inputs, EvalContext& ctx) const -{ - xassert(inputs.size() == names.size()); - Workframe outputs(ctx); - for (size_t i = 0; i < inputs.size(); ++i) { - Workframe arg_out = inputs[i]->evaluate_n(ctx); - arg_out.rename(names[i]); - outputs.cbind( std::move(arg_out) ); - } - return outputs; -} - - -Workframe Head_NamedList::evaluate_r( - const vecExpr& args, EvalContext& ctx, const sztvec&) const -{ - return evaluate_n(args, ctx); -} - - -Workframe Head_NamedList::evaluate_f(EvalContext&, size_t) const { - throw TypeError() << "A dictionary cannot be used as an f-selector"; +Kind FExpr_List::get_expr_kind() const { + return Kind::List; } -Workframe Head_NamedList::evaluate_j( - const vecExpr& inputs, EvalContext& ctx) const -{ - return evaluate_n(inputs, ctx); +int FExpr_List::precedence() const noexcept { + return 17; } -RowIndex Head_NamedList::evaluate_i(const vecExpr&, EvalContext&) const { - throw TypeError() << "A dictionary cannot be used as an i-selector"; +std::string FExpr_List::repr() const { + std::string out = "["; + for (size_t i = 0; i < args_.size(); ++i) { + if (i) out += ", "; + out += args_[i]->repr(); + } + out += ']'; + return out; } -RiGb Head_NamedList::evaluate_iby(const vecExpr&, EvalContext&) const { - throw TypeError() << "A dictionary cannot be used as an i-selector"; -} - }} // namespace dt::expr diff --git a/src/core/expr/fexpr_list.h b/src/core/expr/fexpr_list.h new file mode 100644 index 0000000000..2ef74a0fd2 --- /dev/null +++ b/src/core/expr/fexpr_list.h @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +// Copyright 2019-2020 H2O.ai +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//------------------------------------------------------------------------------ +#ifndef dt_EXPR_FEXPR_LIST_h +#define dt_EXPR_FEXPR_LIST_h +#include +#include +#include "expr/fexpr.h" +namespace dt { +namespace expr { + + +class FExpr_List : public FExpr { + private: + vecExpr args_; + + public: + FExpr_List(vecExpr&& args); + static ptrExpr make(py::robj); + + Workframe evaluate_n(EvalContext&) const override; + Workframe evaluate_j(EvalContext&) const override; + Workframe evaluate_r(EvalContext&, const sztvec&) const override; + Workframe evaluate_f(EvalContext&, size_t) const override; + RowIndex evaluate_i(EvalContext&) const override; + RiGb evaluate_iby(EvalContext&) const override; + void prepare_by(EvalContext&, Workframe&, std::vector&) const override; + + Kind get_expr_kind() const override; + int precedence() const noexcept override; + std::string repr() const override; +}; + + + + +}} // namespace dt::expr +#endif diff --git a/src/core/expr/head.h b/src/core/expr/head.h index 19a76805ff..0dd16f6257 100644 --- a/src/core/expr/head.h +++ b/src/core/expr/head.h @@ -75,18 +75,15 @@ namespace expr { * The hierarchy of Head subclasses is the following: * * Head - * +-- Head_Frame * +-- Head_Func - * | +-- Head_Func_Binary - * | +-- Head_Func_Cast - * | +-- Head_Func_Colset - * | +-- Head_Func_Unary - * | +-- Head_Reduce - * | +-- Head_Reduce_Binary - * | +-- Head_Reduce_Nullary - * | +-- Head_Reduce_Unary - * +-- Head_List - * +-- Head_NamedList + * +-- Head_Func_Binary + * +-- Head_Func_Cast + * +-- Head_Func_Colset + * +-- Head_Func_Unary + * +-- Head_Reduce + * +-- Head_Reduce_Binary + * +-- Head_Reduce_Nullary + * +-- Head_Reduce_Unary * */ class Head {