changeset 8551:906f976d35a8

further improve struct&cell indexing & indexed assignment
author Jaroslav Hajek <highegg@gmail.com>
date Wed, 21 Jan 2009 13:02:49 +0100
parents 1cb63ac13934
children 3591fe09f3b1
files src/ChangeLog src/ov-base.cc src/ov-base.h src/ov-cell.cc src/ov-cell.h src/ov-struct.cc src/ov-struct.h src/ov.cc src/ov.h src/pt-idx.cc
diffstat 10 files changed, 359 insertions(+), 139 deletions(-) [+]
line wrap: on
line diff
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,30 @@
+2009-01-21  Jaroslav Hajek  <highegg@gmail.com>
+
+	* 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<octave_value_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  <jwe@octave.org>
 
 	* file-io.cc (Ffstat): New function.
--- 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<octave_value_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 ();
--- 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<octave_value_list>& idx,
+           bool auto_add);
+
+  virtual octave_value
   do_index_op (const octave_value_list& idx, bool resize_ok = false);
 
   virtual octave_value_list
--- 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<octave_value_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<octave_value_list>& idx,
 		       const octave_value& rhs)
--- 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<octave_value_list>& idx, int);
 
+  octave_value subsref (const std::string& type,
+			const std::list<octave_value_list>& idx,
+                        bool auto_add);
+
   octave_value subsasgn (const std::string& type,
 			 const std::list<octave_value_list>& idx,
 			 const octave_value& rhs);
--- 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<octave_value_list>& idx,
+			bool auto_add)
+{
+  octave_value retval;
+
+  int skip = 1;
+
+  switch (type[0])
+    {
+    case '(':
+      {
+	if (type.length () > 1 && type[1] == '.')
+	  {
+	    std::list<octave_value_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;
--- 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<octave_value_list>& idx)
@@ -75,6 +75,10 @@
   octave_value_list subsref (const std::string&,
 			     const std::list<octave_value_list>&, int);
 
+  octave_value subsref (const std::string& type,
+			const std::list<octave_value_list>& idx,
+                        bool auto_add);
+
   static octave_value numeric_conv (const octave_value& val,
 				    const std::string& type);
 
--- 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<octave_value_list>& idx,
+			    size_t skip) 
+{
+  if (! error_state && idx.size () > skip)
+    {
+      std::list<octave_value_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)
 {
--- 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<octave_value_list>& idx)
+                        const std::list<octave_value_list>& idx)
     { return rep->subsref (type, idx); }
 
+  octave_value subsref (const std::string& type,
+                        const std::list<octave_value_list>& idx,
+                        bool auto_add)
+    { return rep->subsref (type, idx, auto_add); }
+
   octave_value_list subsref (const std::string& type,
-				     const std::list<octave_value_list>& idx,
-    				     int nargout);
+                             const std::list<octave_value_list>& idx,
+                             int nargout);
 
   octave_value next_subsref (const std::string& type, const
 			     std::list<octave_value_list>& idx,
@@ -367,6 +372,10 @@
 				  std::list<octave_value_list>& idx,
 				  size_t skip = 1);
 
+  octave_value next_subsref (bool auto_add, const std::string& type, const
+			     std::list<octave_value_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); }
--- 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<octave_value_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<octave_value_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;