To: vim_dev@googlegroups.com Subject: Patch 8.2.2757 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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) + var legacylines = lines->mapnew((_, v) => + v->substitute('\', 'let', 'g') + ->substitute('\', 'let', 'g') + ->substitute('#"', ' "', 'g')) + CheckLegacySuccess(legacylines) + + var vim9lines = lines->mapnew((_, v) => + v->substitute('\', 'var', 'g') + ->substitute('\