changeset 24480:943e9b1d2d16 v8.2.2780

patch 8.2.2780: Vim9: for loop over blob doesn't work Commit: https://github.com/vim/vim/commit/d551d6c268e435e2fbba22775510fbd0a54477f6 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Apr 18 13:15:58 2021 +0200 patch 8.2.2780: Vim9: for loop over blob doesn't work Problem: Vim9: for loop over blob doesn't work. Solution: Make it work.
author Bram Moolenaar <Bram@vim.org>
date Sun, 18 Apr 2021 13:30:03 +0200
parents 22651692d78e
children fbf62b847fe2
files src/testdir/test_blob.vim src/version.c src/vim9compile.c src/vim9execute.c
diffstat 4 files changed, 72 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_blob.vim
+++ b/src/testdir/test_blob.vim
@@ -283,33 +283,36 @@ func Test_blob_index_assign()
 endfunc
 
 func Test_blob_for_loop()
-  let blob = 0z00010203
-  let i = 0
-  for byte in blob
-    call assert_equal(i, byte)
-    let i += 1
-  endfor
-    call assert_equal(4, i)
+  let lines =<< trim END
+      VAR blob = 0z00010203
+      VAR i = 0
+      for byte in blob
+        call assert_equal(i, byte)
+        LET i += 1
+      endfor
+      call assert_equal(4, i)
 
-  let blob = 0z00
-  call remove(blob, 0)
-  call assert_equal(0, len(blob))
-  for byte in blob
-    call assert_error('loop over empty blob')
-  endfor
+      LET blob = 0z00
+      call remove(blob, 0)
+      call assert_equal(0, len(blob))
+      for byte in blob
+        call assert_report('loop over empty blob')
+      endfor
 
-  let blob = 0z0001020304
-  let i = 0
-  for byte in blob
-    call assert_equal(i, byte)
-    if i == 1
-      call remove(blob, 0)
-    elseif i == 3
-      call remove(blob, 3)
-    endif
-    let i += 1
-  endfor
-  call assert_equal(5, i)
+      LET blob = 0z0001020304
+      LET i = 0
+      for byte in blob
+        call assert_equal(i, byte)
+        if i == 1
+          call remove(blob, 0)
+        elseif i == 3
+          call remove(blob, 3)
+        endif
+        LET i += 1
+      endfor
+      call assert_equal(5, i)
+  END
+  call CheckLegacyAndVim9Success(lines)
 endfunc
 
 func Test_blob_concatenate()
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2780,
+/**/
     2779,
 /**/
     2778,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -7508,13 +7508,12 @@ compile_for(char_u *arg_start, cctx_T *c
     }
     arg_end = arg;
 
-    // If we know the type of "var" and it is a not a list or string we can
+    // If we know the type of "var" and it is a not a supported type we can
     // give an error now.
     vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
     if (vartype->tt_type != VAR_LIST && vartype->tt_type != VAR_STRING
-						&& vartype->tt_type != VAR_ANY)
-    {
-	// TODO: support Blob
+		&& vartype->tt_type != VAR_BLOB && vartype->tt_type != VAR_ANY)
+    {
 	semsg(_(e_for_loop_on_str_not_supported),
 					       vartype_name(vartype->tt_type));
 	drop_scope(cctx);
@@ -7523,6 +7522,8 @@ compile_for(char_u *arg_start, cctx_T *c
 
     if (vartype->tt_type == VAR_STRING)
 	item_type = &t_string;
+    else if (vartype->tt_type == VAR_BLOB)
+	item_type = &t_number;
     else if (vartype->tt_type == VAR_LIST
 				     && vartype->tt_member->tt_type != VAR_ANY)
     {
@@ -7530,7 +7531,7 @@ compile_for(char_u *arg_start, cctx_T *c
 	    item_type = vartype->tt_member;
 	else if (vartype->tt_member->tt_type == VAR_LIST
 		      && vartype->tt_member->tt_member->tt_type != VAR_ANY)
-	    // TODO: should get the type from 
+	    // TODO: should get the type for each lhs
 	    item_type = vartype->tt_member->tt_member;
     }
 
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2900,8 +2900,8 @@ call_def_function(
 		    {
 			char_u	*str = ltv->vval.v_string;
 
-			// Push the next character from the string.  The index
-			// is for the last byte of the previous character.
+			// The index is for the last byte of the previous
+			// character.
 			++idxtv->vval.v_number;
 			if (str == NULL || str[idxtv->vval.v_number] == NUL)
 			{
@@ -2913,6 +2913,7 @@ call_def_function(
 			{
 			    int	clen = mb_ptr2len(str + idxtv->vval.v_number);
 
+			    // Push the next character from the string.
 			    tv = STACK_TV_BOT(0);
 			    tv->v_type = VAR_STRING;
 			    tv->vval.v_string = vim_strnsave(
@@ -2921,9 +2922,41 @@ call_def_function(
 			    idxtv->vval.v_number += clen - 1;
 			}
 		    }
+		    else if (ltv->v_type == VAR_BLOB)
+		    {
+			blob_T	*blob = ltv->vval.v_blob;
+
+			// When we get here the first time make a copy of the
+			// blob, so that the iteration still works when it is
+			// changed.
+			if (idxtv->vval.v_number == -1 && blob != NULL)
+			{
+			    blob_copy(blob, ltv);
+			    blob_unref(blob);
+			    blob = ltv->vval.v_blob;
+			}
+
+			// The index is for the previous byte.
+			++idxtv->vval.v_number;
+			if (blob == NULL
+				     || idxtv->vval.v_number >= blob_len(blob))
+			{
+			    // past the end of the blob, jump to "endfor"
+			    ectx.ec_iidx = iptr->isn_arg.forloop.for_end;
+			    may_restore_cmdmod(&funclocal);
+			}
+			else
+			{
+			    // Push the next byte from the blob.
+			    tv = STACK_TV_BOT(0);
+			    tv->v_type = VAR_NUMBER;
+			    tv->vval.v_number = blob_get(blob,
+							 idxtv->vval.v_number);
+			    ++ectx.ec_stack.ga_len;
+			}
+		    }
 		    else
 		    {
-			// TODO: support Blob
 			semsg(_(e_for_loop_on_str_not_supported),
 						    vartype_name(ltv->v_type));
 			goto failed;