# HG changeset patch # User Jaroslav Hajek # Date 1232539369 -3600 # Node ID 906f976d35a842d47d4c1687c42fd20affdeae18 # Parent 1cb63ac13934e508fced41fffea517cf2aa04783 further improve struct&cell indexing & indexed assignment diff --git a/src/ChangeLog b/src/ChangeLog --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,30 @@ +2009-01-21 Jaroslav Hajek + + * ov.h (octave_value::subsref (..., bool auto_add)): New method. + (octave_value::next_subsref (bool auto_add, ...)): New method. + * ov.cc (octave_value::next_subsref (bool auto_add, ...)): New method. + * ov-base.h (octave_base_value::subsref (..., bool auto_add)): New + virtual method. + * ov-base.cc (octave_base_value::subsref (..., bool auto_add)): New + virtual method. + * ov-cell.cc (octave_cell::subsref (..., bool auto_add)): New virtual + method. + * ov-cell.h (octave_cell::subsref (..., bool auto_add)): Declare it. + * ov-struct.cc (octave_struct::subsref (..., bool auto_add)): New + virtual method. + (octave_struct::subsref (const std::string& type, const + std::list& idx)): Do not allow resizing. + * ov-struct.h (octave_struct::subsref (..., bool auto_add)): Declare + it. + * ov-struct.cc (octave_struct::dotref (..., bool auto_add)): New + virtual method. + * ov-struct.h (octave_struct::dotref (..., bool auto_add)): Declare it. + * pt-idx.cc (tree_index_expression::rvalue): Do not reevaluate already + evaluated part of the index chain. + (tree_index_expression::rvalue): Do not reevaluate already evaluated + part of the index chain. Do not actually perform trailing indexing. + Do not allow indexing cs-lists. + 2009-01-20 John W. Eaton * file-io.cc (Ffstat): New function. diff --git a/src/ov-base.cc b/src/ov-base.cc --- a/src/ov-base.cc +++ b/src/ov-base.cc @@ -99,6 +99,15 @@ } octave_value +octave_base_value::subsref (const std::string& type, + const std::list& idx, + bool auto_add) +{ + // This way we may get a more meaningful error message. + return subsref (type, idx); +} + +octave_value octave_base_value::do_index_op (const octave_value_list&, bool) { std::string nm = type_name (); diff --git a/src/ov-base.h b/src/ov-base.h --- a/src/ov-base.h +++ b/src/ov-base.h @@ -164,6 +164,11 @@ int nargout); virtual octave_value + subsref (const std::string& type, + const std::list& idx, + bool auto_add); + + virtual octave_value do_index_op (const octave_value_list& idx, bool resize_ok = false); virtual octave_value_list diff --git a/src/ov-cell.cc b/src/ov-cell.cc --- a/src/ov-cell.cc +++ b/src/ov-cell.cc @@ -117,6 +117,56 @@ } octave_value +octave_cell::subsref (const std::string& type, + const std::list& idx, + bool auto_add) +{ + octave_value retval; + + switch (type[0]) + { + case '(': + retval = do_index_op (idx.front (), auto_add); + break; + + case '{': + { + octave_value tmp = do_index_op (idx.front (), auto_add); + + if (! error_state) + { + Cell tcell = tmp.cell_value (); + + if (tcell.length () == 1) + retval = tcell(0,0); + else + retval = octave_value (octave_value_list (tcell), auto_add); + } + } + break; + + case '.': + { + std::string nm = type_name (); + error ("%s cannot be indexed with %c", nm.c_str (), type[0]); + } + break; + + default: + panic_impossible (); + } + + // FIXME -- perhaps there should be an + // octave_value_list::next_subsref member function? See also + // octave_user_function::subsref. + + if (idx.size () > 1) + retval = retval.next_subsref (auto_add, type, idx); + + return retval; +} + +octave_value octave_cell::subsasgn (const std::string& type, const std::list& idx, const octave_value& rhs) diff --git a/src/ov-cell.h b/src/ov-cell.h --- a/src/ov-cell.h +++ b/src/ov-cell.h @@ -80,6 +80,10 @@ octave_value_list subsref (const std::string& type, const std::list& idx, int); + octave_value subsref (const std::string& type, + const std::list& idx, + bool auto_add); + octave_value subsasgn (const std::string& type, const std::list& idx, const octave_value& rhs); diff --git a/src/ov-struct.cc b/src/ov-struct.cc --- a/src/ov-struct.cc +++ b/src/ov-struct.cc @@ -53,7 +53,7 @@ DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA(octave_struct, "struct", "struct"); Cell -octave_struct::dotref (const octave_value_list& idx) +octave_struct::dotref (const octave_value_list& idx, bool auto_add) { Cell retval; @@ -65,7 +65,9 @@ if (p != map.end ()) retval = map.contents (p); - else + else if (auto_add) + retval = (numel () == 0) ? Cell (dim_vector (1)) : Cell (dims ()); + else error ("structure has no member `%s'", nm.c_str ()); return retval; @@ -119,7 +121,7 @@ if (! error_state) { - Cell t = tmp.index (idx.front (), true); + Cell t = tmp.index (idx.front ()); retval(0) = (t.length () == 1) ? t(0) : octave_value (t, true); @@ -130,7 +132,7 @@ } } else - retval(0) = map.index (idx.front (), false); + retval(0) = map.index (idx.front ()); } break; @@ -163,6 +165,72 @@ return retval; } +octave_value +octave_struct::subsref (const std::string& type, + const std::list& idx, + bool auto_add) +{ + octave_value retval; + + int skip = 1; + + switch (type[0]) + { + case '(': + { + if (type.length () > 1 && type[1] == '.') + { + std::list::const_iterator p = idx.begin (); + octave_value_list key_idx = *++p; + + Cell tmp = dotref (key_idx, auto_add); + + if (! error_state) + { + Cell t = tmp.index (idx.front (), auto_add); + + retval = (t.length () == 1) ? t(0) : octave_value (t, true); + + // We handled two index elements, so tell + // next_subsref to skip both of them. + + skip++; + } + } + else + retval = map.index (idx.front (), auto_add); + } + break; + + case '.': + { + if (map.numel() > 0) + { + Cell t = dotref (idx.front (), auto_add); + + retval = (t.length () == 1) ? t(0) : octave_value (t, true); + } + } + break; + + case '{': + gripe_invalid_index_type (type_name (), type[0]); + break; + + default: + panic_impossible (); + } + + // FIXME -- perhaps there should be an + // octave_value_list::next_subsref member function? See also + // octave_user_function::subsref. + + if (idx.size () > 1) + retval = retval.next_subsref (auto_add, type, idx, skip); + + return retval; +} + /* %!test %! x(1).a.a = 1; x(2).a.a = 2; diff --git a/src/ov-struct.h b/src/ov-struct.h --- a/src/ov-struct.h +++ b/src/ov-struct.h @@ -63,7 +63,7 @@ octave_base_value *clone (void) const { return new octave_struct (*this); } octave_base_value *empty_clone (void) const { return new octave_struct (); } - Cell dotref (const octave_value_list& idx); + Cell dotref (const octave_value_list& idx, bool auto_add = false); octave_value subsref (const std::string& type, const std::list& idx) @@ -75,6 +75,10 @@ octave_value_list subsref (const std::string&, const std::list&, int); + octave_value subsref (const std::string& type, + const std::list& idx, + bool auto_add); + static octave_value numeric_conv (const octave_value& val, const std::string& type); diff --git a/src/ov.cc b/src/ov.cc --- a/src/ov.cc +++ b/src/ov.cc @@ -1086,6 +1086,22 @@ return *this; } +octave_value +octave_value::next_subsref (bool auto_add, const std::string& type, + const std::list& idx, + size_t skip) +{ + if (! error_state && idx.size () > skip) + { + std::list new_idx (idx); + for (size_t i = 0; i < skip; i++) + new_idx.erase (new_idx.begin ()); + return subsref (type.substr (skip), new_idx, auto_add); + } + else + return *this; +} + octave_value_list octave_value::do_multi_index_op (int nargout, const octave_value_list& idx) { diff --git a/src/ov.h b/src/ov.h --- a/src/ov.h +++ b/src/ov.h @@ -351,12 +351,17 @@ const octave_value_list& idx); octave_value subsref (const std::string& type, - const std::list& idx) + const std::list& idx) { return rep->subsref (type, idx); } + octave_value subsref (const std::string& type, + const std::list& idx, + bool auto_add) + { return rep->subsref (type, idx, auto_add); } + octave_value_list subsref (const std::string& type, - const std::list& idx, - int nargout); + const std::list& idx, + int nargout); octave_value next_subsref (const std::string& type, const std::list& idx, @@ -367,6 +372,10 @@ std::list& idx, size_t skip = 1); + octave_value next_subsref (bool auto_add, const std::string& type, const + std::list& idx, + size_t skip = 1); + octave_value do_index_op (const octave_value_list& idx, bool resize_ok = false) { return rep->do_index_op (idx, resize_ok); } diff --git a/src/pt-idx.cc b/src/pt-idx.cc --- a/src/pt-idx.cc +++ b/src/pt-idx.cc @@ -263,6 +263,18 @@ return m; } +static void +gripe_invalid_inquiry_subscript (void) +{ + error ("invalid dimension inquiry of a non-existent value"); +} + +static void +gripe_indexed_cs_list (void) +{ + error ("a cs-list cannot be further indexed"); +} + octave_value_list tree_index_expression::rvalue (int nargout) { @@ -302,6 +314,7 @@ first_expr_val = expr->rvalue (); octave_value tmp = first_expr_val; + octave_idx_type tmpi = 0; std::list idx; @@ -330,9 +343,14 @@ // value to the built-in __end__ function. octave_value_list tmp_list - = first_expr_val.subsref (type.substr (0, i), idx, nargout); + = tmp.subsref (type.substr (tmpi, i - tmpi), idx, nargout); tmp = tmp_list(0); + tmpi = i; + idx.clear (); + + if (tmp.is_cs_list ()) + gripe_indexed_cs_list (); if (error_state) break; @@ -372,7 +390,7 @@ } if (! error_state) - retval = first_expr_val.subsref (type, idx, nargout); + retval = tmp.subsref (type.substr (tmpi, n - tmpi), idx, nargout); } return retval; @@ -391,12 +409,6 @@ return retval; } -static void -gripe_invalid_inquiry_subscript (void) -{ - error ("invalid dimension inquiry of a non-existent value"); -} - octave_lvalue tree_index_expression::lvalue (void) { @@ -414,8 +426,6 @@ if (! error_state) { - bool have_new_struct_field = false; - // I think it is OK to have a copy here. const octave_value *tro = retval.object (); @@ -426,91 +436,123 @@ first_retval_object = *tro; octave_value tmp = first_retval_object; + octave_idx_type tmpi = 0; + std::list tmpidx; for (int i = 0; i < n; i++) { - if (i > 0) - { - tree_argument_list *al = *p_args; + if (retval.numel () != 1) + gripe_indexed_cs_list (); + + if (i > 0) + { + tree_argument_list *al = *p_args; - if (al && al->has_magic_end ()) - { - // We have an expression like - // - // x{end}.a(end) - // - // and we are looking at the argument list that - // contains the second (or third, etc.) "end" token, - // so we must evaluate everything up to the point of - // that argument list so we pass the appropriate - // value to the built-in __end__ function. + if (al && al->has_magic_end ()) + { + // We have an expression like + // + // x{end}.a(end) + // + // and we are looking at the argument list that + // contains the second (or third, etc.) "end" token, + // so we must evaluate everything up to the point of + // that argument list so we pass the appropriate + // value to the built-in __end__ function. - if (first_retval_object.is_defined ()) + if (tmp.is_defined ()) { - octave_value_list tmp_list - = first_retval_object.subsref (type.substr (0, i), idx, 1); + if (tmpi < i) + { + tmp = tmp.subsref (type.substr (tmpi, i - tmpi), tmpidx, true); - tmp = tmp_list(0); + tmpi = i; + tmpidx.clear (); + } } else gripe_invalid_inquiry_subscript (); - if (error_state) - break; - } - } + if (tmp.is_undefined ()) + gripe_invalid_inquiry_subscript (); + else if (tmp.is_cs_list ()) + gripe_indexed_cs_list (); + + if (error_state) + break; + } + } switch (type[i]) { case '(': - idx.push_back (make_value_list (*p_args, *p_arg_nm, &tmp)); - break; + { + octave_value_list tidx + = make_value_list (*p_args, *p_arg_nm, &tmp); + idx.push_back (tidx); + tmpidx.push_back (tidx); + } + break; case '{': { octave_value_list tidx = make_value_list (*p_args, *p_arg_nm, &tmp); - idx.push_back (tidx); + if (! tidx.all_scalars ()) + { + octave_idx_type nel = 1; - if (! tidx.all_scalars () && retval.numel () == 1) - { + octave_idx_type nidx = tidx.length (); + // Possible cs-list. + bool has_magic_colon = tidx.has_magic_colon (); + dim_vector dims; - if (tidx.has_magic_colon ()) + if (has_magic_colon) { - if (first_retval_object.is_defined ()) + if (tmp.is_defined ()) { - octave_value_list tmp_list - = first_retval_object.subsref (type, idx, 1); - - if (! error_state) + if (tmpi < i) { - octave_value val = tmp_list(0); + tmp = tmp.subsref (type.substr (tmpi, i - tmpi), tmpidx, true); + tmpi = i; + tmpidx.clear (); + } - if (val.is_cs_list ()) - retval.numel (val.numel ()); - } + if (tmp.is_undefined ()) + gripe_invalid_inquiry_subscript (); + else if (tmp.is_cs_list ()) + gripe_indexed_cs_list (); + + dims = (nidx == 1) ? dim_vector (tmp.numel (), 1) : tmp.dims (); } else gripe_invalid_inquiry_subscript (); - } - else - { - octave_idx_type nel = 1; - octave_idx_type nidx = tidx.length (); + if (error_state) + break; + } + + for (octave_idx_type j = 0; j < nidx; j++) + { + octave_value val = tidx(j); - for (octave_idx_type j = 0; j < nidx; j++) - { - octave_value val = tidx(j); + if (val.is_magic_colon ()) + nel *= dims (j); + else + nel *= val.numel (); + } - nel *= val.numel (); - } + retval.numel (nel); + } - retval.numel (nel); - } - } + if (error_state) + break; + + idx.push_back (tidx); + tmpidx.push_back (tidx); + } break; @@ -520,106 +562,92 @@ if (error_state) break; - if (i > 0 && type [i-1] == '(' && retval.numel () == 1 - && ! idx.back ().all_scalars ()) + if (i > 0 && type [i-1] == '(') { // Possible cs-list. - std::string ttype = type.substr (0, i); - octave_value_list xidx = idx.back (); - if (xidx.has_magic_colon ()) - { - if (first_retval_object.is_defined () && ! have_new_struct_field) - { - octave_value_list tmp_list - = first_retval_object.subsref (ttype, idx, 1); - - if (! error_state) - { - octave_value val = tmp_list(0); - - if (val.is_map ()) - retval.numel (val.numel ()); - } - } - else - gripe_invalid_inquiry_subscript (); - } - else + if (! xidx.all_scalars ()) { octave_idx_type nel = 1; octave_idx_type nidx = xidx.length (); + // Possible cs-list. + bool has_magic_colon = xidx.has_magic_colon (); + dim_vector dims; + + if (has_magic_colon) + { + // Evaluate everything up to the point preceding the last paren. + if (tmp.is_defined ()) + { + if (tmpi < i-1) + { + tmpidx.pop_back (); + tmp = tmp.subsref (type.substr (tmpi, i-1 - tmpi), tmpidx, true); + tmpi = i - 1; + tmpidx.clear (); + tmpidx.push_back (xidx); + } + + if (tmp.is_undefined ()) + gripe_invalid_inquiry_subscript (); + else if (tmp.is_cs_list ()) + gripe_indexed_cs_list (); + + dims = (nidx == 1) ? dim_vector (tmp.numel (), 1) : tmp.dims (); + } + else + gripe_invalid_inquiry_subscript (); + + if (error_state) + break; + } + for (octave_idx_type j = 0; j < nidx; j++) { octave_value val = xidx(j); - nel *= val.numel (); + if (val.is_magic_colon ()) + nel *= dims (j); + else + nel *= val.numel (); } retval.numel (nel); } } - else if (retval.numel () == 1 && first_retval_object.is_defined ()) + else { - octave_value tobj = first_retval_object; - - std::string ttype = type.substr (0, i); + // A plain struct component can also yield a list reference. + if (tmp.is_defined () && tmpi < i) + { + tmp = tmp.subsref (type.substr (tmpi, i - tmpi), tmpidx, true); - if (i > 0) - { - // Here we need to ensure that keys do exist. - - octave_value_list tmp_list - = first_retval_object.subsref (ttype, idx, 1); - - if (tmp_list.length () > 0) tobj = tmp_list (0); + tmpi = i; + tmpidx.clear (); } - - std::string key = tidx.string_value (); + if (tmp.is_cs_list ()) + gripe_indexed_cs_list (); + else if (tmp.is_map ()) + retval.numel (tmp.numel ()); + else + tmp = Octave_map (); - if (! error_state) - { - if (tobj.is_map ()) - { - Octave_map map = tobj.map_value (); - if (map.contains (key)) - retval.numel (map.contents (key).numel ()); - else - { - map.contents (key) = octave_value (); - if (i > 0) - first_retval_object = - first_retval_object.subsasgn (ttype, idx, map); - else - first_retval_object = map; + if (error_state) + break; - have_new_struct_field = true; - } - } - else - { - Octave_map map (key, octave_value ()); - if (i > 0) - first_retval_object = - first_retval_object.subsasgn (ttype, idx, map); - else - first_retval_object = map; - - have_new_struct_field = true; - } - } } - if (! error_state) - idx.push_back (tidx); - else + if (error_state) break; + idx.push_back (tidx); + tmpidx.push_back (tidx); + } break;