From d551d6c268e435e2fbba22775510fbd0a54477f6 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 18 Apr 2021 13:15:58 +0200 Subject: [PATCH] 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. --- src/testdir/test_blob.vim | 55 +++++++++++++++++++++------------------ src/version.c | 2 ++ src/vim9compile.c | 9 ++++--- src/vim9execute.c | 39 ++++++++++++++++++++++++--- 4 files changed, 72 insertions(+), 33 deletions(-) diff --git a/src/testdir/test_blob.vim b/src/testdir/test_blob.vim index 21f90efb1..fa482b432 100644 --- 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 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 = 0z0001020304 - let i = 0 - for byte in blob - call assert_equal(i, byte) - if i == 1 + 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) - elseif i == 3 - call remove(blob, 3) - endif - let i += 1 - endfor - call assert_equal(5, i) + 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) + END + call CheckLegacyAndVim9Success(lines) endfunc func Test_blob_concatenate() diff --git a/src/version.c b/src/version.c index 12a51c912..c3e2ea852 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2780, /**/ 2779, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 9cb71295c..961050f98 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -7508,13 +7508,12 @@ compile_for(char_u *arg_start, cctx_T *cctx) } 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) + && vartype->tt_type != VAR_BLOB && vartype->tt_type != VAR_ANY) { - // TODO: support Blob 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 *cctx) 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 *cctx) 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; } diff --git a/src/vim9execute.c b/src/vim9execute.c index 8a985214c..4e1af4e58 100644 --- 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; -- 2.40.0