Patch 8.2.2757

4 views
Skip to first unread message

Bram Moolenaar

unread,
Apr 12, 2021, 3:21:48 PM4/12/21
to vim...@googlegroups.com

Patch 8.2.2757
Problem: Vim9: blob tests for legacy and Vim9 script are separate.
Solution: Add CheckLegacyAndVim9Success(). Make blob index assign work.
Files: src/vim9compile.c, src/vim9.h, src/vim9execute.c, src/errors.h,
src/blob.c, src/proto/blob.pro, src/eval.c, src/ex_docmd.c,
src/testdir/vim9.vim, src/testdir/test_blob.vim


*** ../vim-8.2.2756/src/vim9compile.c 2021-04-11 20:26:30.486312262 +0200
--- src/vim9compile.c 2021-04-12 21:17:06.523249070 +0200
***************
*** 6064,6101 ****
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;

p = var_start + varlen;
if (*p == '[')
{
p = skipwhite(p + 1);
! 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;
! }
*range = TRUE;
p = skipwhite(p);
! if (!IS_WHITE_OR_NUL(p[-1]) || !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 (r == OK && *skipwhite(p) != ']')
--- 6064,6111 ----
compile_assign_index(
char_u *var_start,
lhs_T *lhs,
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);
! 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]
! // blob[idx : idx] = value
*range = TRUE;
p = skipwhite(p);
! 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);
! 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,6182 ****
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 (lhs->lhs_type == &t_any)
{
--- 6185,6198 ----
garray_T *stack = &cctx->ctx_type_stack;
int range = FALSE;

! 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,6227 ****
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 (is_assign)
{
! isn_T *isn = generate_instr_drop(cctx, ISN_STOREINDEX, 3);

! if (isn == NULL)
! return FAIL;
! isn->isn_arg.vartype = dest_type;
}
else if (range)
{
--- 6229,6252 ----
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_BLOB || dest_type == VAR_ANY)
{
if (is_assign)
{
! 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,6450 ****
// Get member from list or dict. First compile the
// index value.
if (compile_assign_index(var_start, &lhs,
! TRUE, &range, cctx) == FAIL)
goto theend;

// Get the member.
if (compile_member(FALSE, cctx) == FAIL)
--- 6468,6481 ----
// Get member from list or dict. First compile the
// index value.
if (compile_assign_index(var_start, &lhs,
! &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,9320 ****
--- 9346,9352 ----
case ISN_SLICE:
case ISN_STORE:
case ISN_STOREINDEX:
+ case ISN_STORERANGE:
case ISN_STORENR:
case ISN_STOREOUTER:
case ISN_STOREREG:
*** ../vim-8.2.2756/src/vim9.h 2021-04-11 20:26:30.486312262 +0200
--- src/vim9.h 2021-04-11 22:08:58.545439336 +0200
***************
*** 57,62 ****
--- 57,64 ----
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
*** ../vim-8.2.2756/src/vim9execute.c 2021-04-11 20:26:30.486312262 +0200
--- src/vim9execute.c 2021-04-12 20:52:21.741867635 +0200
***************
*** 2219,2224 ****
--- 2219,2228 ----
clear_tv(tv);
}
}
+ else if (status == OK && dest_type == VAR_BLOB)
+ {
+ // TODO
+ }
else
{
status = FAIL;
***************
*** 2236,2241 ****
--- 2240,2299 ----
}
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,4367 ****
--- 4420,4429 ----
}
break;

+ case ISN_STORERANGE:
+ smsg("%4d STORERANGE", current);
+ break;
+
// constants
case ISN_PUSHNR:
smsg("%4d PUSHNR %lld", current,
*** ../vim-8.2.2756/src/errors.h 2021-04-10 17:17:33.431942839 +0200
--- src/errors.h 2021-04-11 22:18:38.077671376 +0200
***************
*** 399,401 ****
--- 399,403 ----
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"));
*** ../vim-8.2.2756/src/blob.c 2021-04-11 20:26:30.486312262 +0200
--- src/blob.c 2021-04-12 20:48:33.234630874 +0200
***************
*** 337,342 ****
--- 337,364 ----
}

/*
+ * 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
*** ../vim-8.2.2756/src/proto/blob.pro 2021-04-11 20:26:30.486312262 +0200
--- src/proto/blob.pro 2021-04-12 20:48:02.110737084 +0200
***************
*** 14,18 ****
--- 14,19 ----
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 : */
*** ../vim-8.2.2756/src/eval.c 2021-04-11 20:26:30.486312262 +0200
--- src/eval.c 2021-04-12 20:47:13.250905213 +0200
***************
*** 1319,1341 ****

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"));
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
{
--- 1319,1330 ----

if (lp->ll_range && rettv->v_type == VAR_BLOB)
{
if (lp->ll_empty2)
lp->ll_n2 = blob_len(lp->ll_blob) - 1;

! if (blob_set_range(lp->ll_blob, lp->ll_n1, lp->ll_n2,
! rettv) == FAIL)
return;
}
else
{
*** ../vim-8.2.2756/src/ex_docmd.c 2021-04-11 13:29:13.194824346 +0200
--- src/ex_docmd.c 2021-04-12 21:05:51.644473199 +0200
***************
*** 3429,3450 ****
// "varname.key" is an expression.
|| (*p == '.' && ASCII_ISALPHA(p[1]))))
{
! char_u *after = p;

// When followed by "=" or "+=" then it is an assignment.
++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] == '=')
|| (after[0] == '.' && after[1] == '.'
&& after[2] == '='))
! eap->cmdidx = CMD_var;
! else
! eap->cmdidx = CMD_eval;
--emsg_silent;
return eap->cmd;
}
--- 3429,3453 ----
// "varname.key" is an expression.
|| (*p == '.' && ASCII_ISALPHA(p[1]))))
{
! 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 (skip_expr(&after, NULL) == OK)
+ {
after = skipwhite(after);
! if (*after == '=' || (*after != NUL && after[1] == '=')
|| (after[0] == '.' && after[1] == '.'
&& after[2] == '='))
! eap->cmdidx = CMD_var;
! }
--emsg_silent;
return eap->cmd;
}
*** ../vim-8.2.2756/src/testdir/vim9.vim 2021-04-04 20:49:46.626430253 +0200
--- src/testdir/vim9.vim 2021-04-11 21:26:33.678728448 +0200
***************
*** 133,135 ****
--- 133,170 ----
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
*** ../vim-8.2.2756/src/testdir/test_blob.vim 2020-10-15 22:29:13.566726912 +0200
--- src/testdir/test_blob.vim 2021-04-12 21:07:56.612309041 +0200
***************
*** 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,81 ****

" 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]')
!
! 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_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()))
endfunc

" assignment to a blob
func Test_blob_assign()
! let b = 0zDEADBEEF
! let b2 = b[1:2]
! call assert_equal(0zADBE, b2)
!
! let 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 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:')
!
! 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 += "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.
endfunc

func Test_blob_get_range()
--- 11,91 ----

" Blob creation from constant
func Test_blob_create()
! 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_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())
!
! 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 lines =<< trim END
! VAR b = 0zDEADBEEF
! VAR b2 = b[1 : 2]
! call assert_equal(0zADBE, b2)
!
! 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 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
! 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 ..= 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:')
endfunc

func Test_blob_get_range()
*** ../vim-8.2.2756/src/version.c 2021-04-11 20:26:30.486312262 +0200
--- src/version.c 2021-04-11 21:09:35.779093423 +0200
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 2757,
/**/

--
Momento mori, ergo carpe diem

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
Reply all
Reply to author
Forward
0 new messages