changeset 24434:602e528a8e43 v8.2.2757

patch 8.2.2757: Vim9: blob tests for legacy and Vim9 script are separate Commit: https://github.com/vim/vim/commit/68452177ca4cda4a9d5f93892e437447cf9404c8 Author: Bram Moolenaar <Bram@vim.org> Date: Mon Apr 12 21:21:02 2021 +0200 patch 8.2.2757: Vim9: blob tests for legacy and Vim9 script are separate Problem: Vim9: blob tests for legacy and Vim9 script are separate. Solution: Add CheckLegacyAndVim9Success(). Make blob index assign work.
author Bram Moolenaar <Bram@vim.org>
date Mon, 12 Apr 2021 21:30:04 +0200
parents a1e1e1eaa09b
children 8a5865e08b2a
files src/blob.c src/errors.h src/eval.c src/ex_docmd.c src/proto/blob.pro src/testdir/test_blob.vim src/testdir/vim9.vim src/version.c src/vim9.h src/vim9compile.c src/vim9execute.c
diffstat 11 files changed, 253 insertions(+), 93 deletions(-) [+]
line wrap: on
line diff
--- a/src/blob.c
+++ b/src/blob.c
@@ -337,6 +337,28 @@ blob_slice_or_index(
 }
 
 /*
+ * Set bytes "n1" to "n2" (inclusive) in "dest" to the value of "src".
+ * Caller must make sure "src" is a blob.
+ * Returns FAIL if the number of bytes does not match.
+ */
+    int
+blob_set_range(blob_T *dest, long n1, long n2, typval_T *src)
+{
+    int	il, ir;
+
+    if (n2 - n1 + 1 != blob_len(src->vval.v_blob))
+    {
+	emsg(_("E972: Blob value does not have the right number of bytes"));
+	return FAIL;
+    }
+
+    ir = 0;
+    for (il = n1; il <= n2; il++)
+	blob_set(dest, il, blob_get(src->vval.v_blob, ir++));
+    return OK;
+}
+
+/*
  * "remove({blob})" function
  */
     void
--- a/src/errors.h
+++ b/src/errors.h
@@ -399,3 +399,5 @@ EXTERN char e_variable_arguments_type_mu
 	INIT(= N_("E1180: Variable arguments type must be a list: %s"));
 EXTERN char e_cannot_use_underscore_here[]
 	INIT(= N_("E1181: Cannot use an underscore here"));
+EXTERN char e_blob_required[]
+	INIT(= N_("E1182: Blob required"));
--- a/src/eval.c
+++ b/src/eval.c
@@ -1319,23 +1319,12 @@ set_var_lval(
 
 	    if (lp->ll_range && rettv->v_type == VAR_BLOB)
 	    {
-		int	il, ir;
-
 		if (lp->ll_empty2)
 		    lp->ll_n2 = blob_len(lp->ll_blob) - 1;
 
-		if (lp->ll_n2 - lp->ll_n1 + 1 != blob_len(rettv->vval.v_blob))
-		{
-		    emsg(_("E972: Blob value does not have the right number of bytes"));
+		if (blob_set_range(lp->ll_blob, lp->ll_n1, lp->ll_n2,
+								rettv) == FAIL)
 		    return;
-		}
-		if (lp->ll_empty2)
-		    lp->ll_n2 = blob_len(lp->ll_blob);
-
-		ir = 0;
-		for (il = lp->ll_n1; il <= lp->ll_n2; il++)
-		    blob_set(lp->ll_blob, il,
-			    blob_get(rettv->vval.v_blob, ir++));
 	    }
 	    else
 	    {
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -3429,22 +3429,25 @@ find_ex_command(
 			    // "varname.key" is an expression.
 			 || (*p == '.' && ASCII_ISALPHA(p[1]))))
 	    {
-		char_u	*after = p;
+		char_u	*after = eap->cmd;
 
 		// When followed by "=" or "+=" then it is an assignment.
+		// Skip over the whole thing, it can be:
+		//	name.member = val
+		//	name[a : b] = val
+		//	name[idx] = val
+		//	name[idx].member = val
+		//	etc.
+		eap->cmdidx = CMD_eval;
 		++emsg_silent;
-		if (*after == '.')
-		    after = skipwhite(after + 1);
 		if (skip_expr(&after, NULL) == OK)
+		{
 		    after = skipwhite(after);
-		else
-		    after = (char_u *)"";
-		if (*after == '=' || (*after != NUL && after[1] == '=')
+		    if (*after == '=' || (*after != NUL && after[1] == '=')
 					 || (after[0] == '.' && after[1] == '.'
 							   && after[2] == '='))
-		    eap->cmdidx = CMD_var;
-		else
-		    eap->cmdidx = CMD_eval;
+			eap->cmdidx = CMD_var;
+		}
 		--emsg_silent;
 		return eap->cmd;
 	    }
--- a/src/proto/blob.pro
+++ b/src/proto/blob.pro
@@ -14,5 +14,6 @@ int write_blob(FILE *fd, blob_T *blob);
 char_u *blob2string(blob_T *blob, char_u **tofree, char_u *numbuf);
 blob_T *string2blob(char_u *str);
 int blob_slice_or_index(blob_T *blob, int is_range, varnumber_T n1, varnumber_T n2, int exclusive, typval_T *rettv);
+int blob_set_range(blob_T *dest, long n1, long n2, typval_T *src);
 void blob_remove(typval_T *argvars, typval_T *rettv);
 /* vim: set ft=c : */
--- a/src/testdir/test_blob.vim
+++ b/src/testdir/test_blob.vim
@@ -1,5 +1,7 @@
 " Tests for the Blob types
 
+source vim9.vim
+
 func TearDown()
   " Run garbage collection after every test
   call test_garbagecollect_now()
@@ -9,73 +11,81 @@ endfunc
 
 " Blob creation from constant
 func Test_blob_create()
-  let b = 0zDEADBEEF
-  call assert_equal(v:t_blob, type(b))
-  call assert_equal(4, len(b))
-  call assert_equal(0xDE, b[0])
-  call assert_equal(0xAD, b[1])
-  call assert_equal(0xBE, b[2])
-  call assert_equal(0xEF, b[3])
-  call assert_fails('let x = b[4]')
+  let lines =<< trim END
+      VAR b = 0zDEADBEEF
+      call assert_equal(v:t_blob, type(b))
+      call assert_equal(4, len(b))
+      call assert_equal(0xDE, b[0])
+      call assert_equal(0xAD, b[1])
+      call assert_equal(0xBE, b[2])
+      call assert_equal(0xEF, b[3])
+      call assert_fails('VAR x = b[4]')
 
-  call assert_equal(0xDE, get(b, 0))
-  call assert_equal(0xEF, get(b, 3))
+      call assert_equal(0xDE, get(b, 0))
+      call assert_equal(0xEF, get(b, 3))
 
-  call assert_fails('let b = 0z1', 'E973:')
-  call assert_fails('let b = 0z1x', 'E973:')
-  call assert_fails('let b = 0z12345', 'E973:')
+      call assert_fails('VAR b = 0z1', 'E973:')
+      call assert_fails('VAR b = 0z1x', 'E973:')
+      call assert_fails('VAR b = 0z12345', 'E973:')
 
-  call assert_equal(0z, test_null_blob())
+      call assert_equal(0z, test_null_blob())
 
-  let b = 0z001122.33445566.778899.aabbcc.dd
-  call assert_equal(0z00112233445566778899aabbccdd, b)
-  call assert_fails('let b = 0z1.1')
-  call assert_fails('let b = 0z.')
-  call assert_fails('let b = 0z001122.')
-  call assert_fails('call get("", 1)', 'E896:')
-  call assert_equal(0, len(test_null_blob()))
+      LET b = 0z001122.33445566.778899.aabbcc.dd
+      call assert_equal(0z00112233445566778899aabbccdd, b)
+      call assert_fails('VAR b = 0z1.1')
+      call assert_fails('VAR b = 0z.')
+      call assert_fails('VAR b = 0z001122.')
+      call assert_fails('call get("", 1)', 'E896:')
+      call assert_equal(0, len(test_null_blob()))
+  END
+  call CheckLegacyAndVim9Success(lines)
 endfunc
 
 " assignment to a blob
 func Test_blob_assign()
-  let b = 0zDEADBEEF
-  let b2 = b[1:2]
-  call assert_equal(0zADBE, b2)
+  let lines =<< trim END
+      VAR b = 0zDEADBEEF
+      VAR b2 = b[1 : 2]
+      call assert_equal(0zADBE, b2)
 
-  let bcopy = b[:]
-  call assert_equal(b, bcopy)
-  call assert_false(b is bcopy)
+      VAR bcopy = b[:]
+      call assert_equal(b, bcopy)
+      call assert_false(b is bcopy)
+
+      LET b = 0zDEADBEEF
+      LET b2 = b
+      call assert_true(b is b2)
+      LET b[:] = 0z11223344
+      call assert_equal(0z11223344, b)
+      call assert_equal(0z11223344, b2)
+      call assert_true(b is b2)
 
-  let b = 0zDEADBEEF
-  let b2 = b
-  call assert_true(b is b2)
-  let b[:] = 0z11223344
-  call assert_equal(0z11223344, b)
-  call assert_equal(0z11223344, b2)
-  call assert_true(b is b2)
+      LET b = 0zDEADBEEF
+      LET b[3 :] = 0z66
+      call assert_equal(0zDEADBE66, b)
+      LET b[: 1] = 0z8899
+      call assert_equal(0z8899BE66, b)
 
+      LET b = 0zDEADBEEF
+      LET b += 0z99
+      call assert_equal(0zDEADBEEF99, b)
+
+      VAR l = [0z12]
+      VAR m = deepcopy(l)
+      LET m[0] = 0z34	#" E742 or E741 should not occur.
+  END
+  call CheckLegacyAndVim9Success(lines)
+
+  " TODO: move to above once it works
   let b = 0zDEADBEEF
-  let b[3:] = 0z66
-  call assert_equal(0zDEADBE66, b)
-  let b[:1] = 0z8899
-  call assert_equal(0z8899BE66, b)
-
-  call assert_fails('let b[2:3] = 0z112233', 'E972:')
-  call assert_fails('let b[2:3] = 0z11', 'E972:')
-  call assert_fails('let b[3:2] = 0z', 'E979:')
+  call assert_fails('let b[2 : 3] = 0z112233', 'E972:')
+  call assert_fails('let b[2 : 3] = 0z11', 'E972:')
+  call assert_fails('let b[3 : 2] = 0z', 'E979:')
 
-  let b = 0zDEADBEEF
-  let b += 0z99
-  call assert_equal(0zDEADBEEF99, b)
-
-  call assert_fails('let b .= 0z33', 'E734:')
-  call assert_fails('let b .= "xx"', 'E734:')
+  call assert_fails('let b ..= 0z33', 'E734:')
+  call assert_fails('let b ..= "xx"', 'E734:')
   call assert_fails('let b += "xx"', 'E734:')
-  call assert_fails('let b[1:1] .= 0z55', 'E734:')
-
-  let l = [0z12]
-  let m = deepcopy(l)
-  let m[0] = 0z34	" E742 or E741 should not occur.
+  call assert_fails('let b[1 : 1] ..= 0z55', 'E734:')
 endfunc
 
 func Test_blob_get_range()
--- a/src/testdir/vim9.vim
+++ b/src/testdir/vim9.vim
@@ -133,3 +133,38 @@ def CheckDefExecAndScriptFailure2(
   CheckDefExecFailure(lines, errorDef, lnum)
   CheckScriptFailure(['vim9script'] + lines, errorScript, lnum + 1)
 enddef
+
+
+" Check that "lines" inside a legacy function has no error.
+func CheckLegacySuccess(lines)
+  let cwd = getcwd()
+  let fname = 'XlegacySuccess' .. s:sequence
+  let s:sequence += 1
+  call writefile(['func Func()'] + a:lines + ['endfunc'], fname)
+  try
+    exe 'so ' .. fname
+    call Func()
+    delfunc! Func
+  finally
+    call chdir(cwd)
+    call delete(fname)
+  endtry
+endfunc
+
+" Execute "lines" in a legacy function, :def function and Vim9 script.
+" Use 'VAR' for a declaration.
+" Use 'LET' for an assignment
+" Use ' #"' for a comment
+def CheckLegacyAndVim9Success(lines: list<string>)
+  var legacylines = lines->mapnew((_, v) =>
+  				v->substitute('\<VAR\>', 'let', 'g')
+		           	 ->substitute('\<LET\>', 'let', 'g')
+		           	 ->substitute('#"', ' "', 'g'))
+  CheckLegacySuccess(legacylines)
+
+  var vim9lines = lines->mapnew((_, v) =>
+  				v->substitute('\<VAR\>', 'var', 'g')
+		           	 ->substitute('\<LET ', '', 'g'))
+  CheckDefSuccess(vim9lines)
+  CheckScriptSuccess(['vim9script'] + vim9lines)
+enddef
--- 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 */
 /**/
+    2757,
+/**/
     2756,
 /**/
     2755,
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -57,6 +57,8 @@ typedef enum {
     ISN_STORENR,    // store number into local variable isn_arg.storenr.stnr_idx
     ISN_STOREINDEX,	// store into list or dictionary, type isn_arg.vartype,
 			// value/index/variable on stack
+    ISN_STORERANGE,	// store into blob,
+			// value/index 1/index 2/variable on stack
 
     ISN_UNLET,		// unlet variable isn_arg.unlet.ul_name
     ISN_UNLETENV,	// unlet environment variable isn_arg.unlet.ul_name
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -6064,38 +6064,48 @@ compile_lhs(
 compile_assign_index(
 	char_u	*var_start,
 	lhs_T	*lhs,
-	int	is_assign,
 	int	*range,
 	cctx_T	*cctx)
 {
     size_t	varlen = lhs->lhs_varlen;
     char_u	*p;
     int		r = OK;
+    int		need_white_before = TRUE;
+    int		empty_second;
 
     p = var_start + varlen;
     if (*p == '[')
     {
 	p = skipwhite(p + 1);
-	r = compile_expr0(&p, cctx);
+	if (*p == ':')
+	{
+	    // empty first index, push zero
+	    r = generate_PUSHNR(cctx, 0);
+	    need_white_before = FALSE;
+	}
+	else
+	    r = compile_expr0(&p, cctx);
 
 	if (r == OK && *skipwhite(p) == ':')
 	{
 	    // unlet var[idx : idx]
-	    if (is_assign)
-	    {
-		semsg(_(e_cannot_use_range_with_assignment_str), p);
-		return FAIL;
-	    }
+	    // blob[idx : idx] = value
 	    *range = TRUE;
 	    p = skipwhite(p);
-	    if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1]))
+	    empty_second = *skipwhite(p + 1) == ']';
+	    if ((need_white_before && !IS_WHITE_OR_NUL(p[-1]))
+		    || (!empty_second && !IS_WHITE_OR_NUL(p[1])))
 	    {
 		semsg(_(e_white_space_required_before_and_after_str_at_str),
 								      ":", p);
 		return FAIL;
 	    }
 	    p = skipwhite(p + 1);
-	    r = compile_expr0(&p, cctx);
+	    if (*p == ']')
+		// empty second index, push "none"
+		r = generate_PUSHSPEC(cctx, VVAL_NONE);
+	    else
+		r = compile_expr0(&p, cctx);
 	}
 
 	if (r == OK && *skipwhite(p) != ']')
@@ -6175,8 +6185,14 @@ compile_assign_unlet(
     garray_T    *stack = &cctx->ctx_type_stack;
     int		range = FALSE;
 
-    if (compile_assign_index(var_start, lhs, is_assign, &range, cctx) == FAIL)
-	return FAIL;
+    if (compile_assign_index(var_start, lhs, &range, cctx) == FAIL)
+	return FAIL;
+    if (is_assign && range && lhs->lhs_type != &t_blob
+						    && lhs->lhs_type != &t_any)
+    {
+	semsg(_(e_cannot_use_range_with_assignment_str), var_start);
+	return FAIL;
+    }
 
     if (lhs->lhs_type == &t_any)
     {
@@ -6213,15 +6229,24 @@ compile_assign_unlet(
     if (compile_load_lhs(lhs, var_start, rhs_type, cctx) == FAIL)
 	return FAIL;
 
-    if (dest_type == VAR_LIST || dest_type == VAR_DICT || dest_type == VAR_ANY)
+    if (dest_type == VAR_LIST || dest_type == VAR_DICT
+			      || dest_type == VAR_BLOB || dest_type == VAR_ANY)
     {
 	if (is_assign)
 	{
-	    isn_T	*isn = generate_instr_drop(cctx, ISN_STOREINDEX, 3);
-
-	    if (isn == NULL)
-		return FAIL;
-	    isn->isn_arg.vartype = dest_type;
+	    if (range)
+	    {
+		if (generate_instr_drop(cctx, ISN_STORERANGE, 4) == NULL)
+		    return FAIL;
+	    }
+	    else
+	    {
+		isn_T	*isn = generate_instr_drop(cctx, ISN_STOREINDEX, 3);
+
+		if (isn == NULL)
+		    return FAIL;
+		isn->isn_arg.vartype = dest_type;
+	    }
 	}
 	else if (range)
 	{
@@ -6443,8 +6468,14 @@ compile_assignment(char_u *arg, exarg_T 
 			    // Get member from list or dict.  First compile the
 			    // index value.
 			    if (compile_assign_index(var_start, &lhs,
-						   TRUE, &range, cctx) == FAIL)
+							 &range, cctx) == FAIL)
 				goto theend;
+			    if (range)
+			    {
+				semsg(_(e_cannot_use_range_with_assignment_str),
+								    var_start);
+				return FAIL;
+			    }
 
 			    // Get the member.
 			    if (compile_member(FALSE, cctx) == FAIL)
@@ -9315,6 +9346,7 @@ delete_instr(isn_T *isn)
 	case ISN_SLICE:
 	case ISN_STORE:
 	case ISN_STOREINDEX:
+	case ISN_STORERANGE:
 	case ISN_STORENR:
 	case ISN_STOREOUTER:
 	case ISN_STOREREG:
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2219,6 +2219,10 @@ call_def_function(
 			    clear_tv(tv);
 			}
 		    }
+		    else if (status == OK && dest_type == VAR_BLOB)
+		    {
+			// TODO
+		    }
 		    else
 		    {
 			status = FAIL;
@@ -2236,6 +2240,60 @@ call_def_function(
 		}
 		break;
 
+	    // store value in blob range
+	    case ISN_STORERANGE:
+		{
+		    typval_T	*tv_idx1 = STACK_TV_BOT(-3);
+		    typval_T	*tv_idx2 = STACK_TV_BOT(-2);
+		    typval_T	*tv_dest = STACK_TV_BOT(-1);
+		    int		status = OK;
+
+		    // Stack contains:
+		    // -4 value to be stored
+		    // -3 first index or "none"
+		    // -2 second index or "none"
+		    // -1 destination blob
+		    tv = STACK_TV_BOT(-4);
+		    if (tv_dest->v_type != VAR_BLOB)
+		    {
+			status = FAIL;
+			emsg(_(e_blob_required));
+		    }
+		    else
+		    {
+			varnumber_T n1;
+			varnumber_T n2;
+			int	    error = FALSE;
+
+			n1 = tv_get_number_chk(tv_idx1, &error);
+			if (error)
+			    status = FAIL;
+			else
+			{
+			    if (tv_idx2->v_type == VAR_SPECIAL
+					&& tv_idx2->vval.v_number == VVAL_NONE)
+				n2 = blob_len(tv_dest->vval.v_blob) - 1;
+			    else
+				n2 = tv_get_number_chk(tv_idx2, &error);
+			    if (error)
+				status = FAIL;
+			    else
+				status = blob_set_range(tv_dest->vval.v_blob,
+								   n1, n2, tv);
+			}
+		    }
+
+		    clear_tv(tv_idx1);
+		    clear_tv(tv_idx2);
+		    clear_tv(tv_dest);
+		    ectx.ec_stack.ga_len -= 4;
+		    clear_tv(tv);
+
+		    if (status == FAIL)
+			goto on_error;
+		}
+		break;
+
 	    // load or store variable or argument from outer scope
 	    case ISN_LOADOUTER:
 	    case ISN_STOREOUTER:
@@ -4362,6 +4420,10 @@ ex_disassemble(exarg_T *eap)
 		}
 		break;
 
+	    case ISN_STORERANGE:
+		smsg("%4d STORERANGE", current);
+		break;
+
 	    // constants
 	    case ISN_PUSHNR:
 		smsg("%4d PUSHNR %lld", current,