To: vim_dev@googlegroups.com Subject: Patch 8.2.0601 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0601 Problem: Vim9: :unlet is not compiled. Solution: Implement :unlet instruction and check for errors. Files: src/vim9compile.c, src/proto/vim9compile.pro, src/vim9.h, src/vim9execute.c, src/evalvars.c, src/proto/evalvars.pro, src/eval.c, src/testdir/test_vim9_script.vim, src/testdir/test_vim9_disassemble.vim *** ../vim-8.2.0600/src/vim9compile.c 2020-04-19 14:32:13.556206438 +0200 --- src/vim9compile.c 2020-04-19 16:20:36.753337072 +0200 *************** *** 987,992 **** --- 987,1009 ---- } /* + * Generate an ISN_UNLET instruction. + */ + static int + generate_UNLET(cctx_T *cctx, char_u *name, int forceit) + { + isn_T *isn; + + RETURN_OK_IF_SKIP(cctx); + if ((isn = generate_instr(cctx, ISN_UNLET)) == NULL) + return FAIL; + isn->isn_arg.unlet.ul_name = vim_strsave(name); + isn->isn_arg.unlet.ul_forceit = forceit; + + return OK; + } + + /* * Generate an ISN_LOADS instruction. */ static int *************** *** 4543,4548 **** --- 4560,4640 ---- } /* + * Check if "name" can be "unlet". + */ + int + check_vim9_unlet(char_u *name) + { + if (name[1] != ':' || vim_strchr((char_u *)"gwtb", *name) == NULL) + { + semsg(_("E1081: Cannot unlet %s"), name); + return FAIL; + } + return OK; + } + + /* + * Callback passed to ex_unletlock(). + */ + static int + compile_unlet( + lval_T *lvp, + char_u *name_end, + exarg_T *eap, + int deep UNUSED, + void *coookie) + { + cctx_T *cctx = coookie; + + if (lvp->ll_tv == NULL) + { + char_u *p = lvp->ll_name; + int cc = *name_end; + int ret = OK; + + // Normal name. Only supports g:, w:, t: and b: namespaces. + *name_end = NUL; + if (check_vim9_unlet(p) == FAIL) + ret = FAIL; + else + ret = generate_UNLET(cctx, p, eap->forceit); + + *name_end = cc; + return ret; + } + + // TODO: unlet {list}[idx] + // TODO: unlet {dict}[key] + emsg("Sorry, :unlet not fully implemented yet"); + return FAIL; + } + + /* + * compile "unlet var", "lock var" and "unlock var" + * "arg" points to "var". + */ + static char_u * + compile_unletlock(char_u *arg, exarg_T *eap, cctx_T *cctx) + { + char_u *p = arg; + + if (eap->cmdidx != CMD_unlet) + { + emsg("Sorry, :lock and unlock not implemented yet"); + return NULL; + } + + if (*p == '!') + { + p = skipwhite(p + 1); + eap->forceit = TRUE; + } + + ex_unletlock(eap, p, 0, GLV_NO_AUTOLOAD, compile_unlet, cctx); + return eap->nextcmd == NULL ? (char_u *)"" : eap->nextcmd; + } + + /* * Compile an :import command. */ static char_u * *************** *** 6031,6036 **** --- 6123,6134 ---- line = compile_assignment(p, &ea, ea.cmdidx, &cctx); break; + case CMD_unlet: + case CMD_unlockvar: + case CMD_lockvar: + line = compile_unletlock(p, &ea, &cctx); + break; + case CMD_import: line = compile_import(p, &cctx); break; *************** *** 6264,6269 **** --- 6362,6371 ---- vim_free(isn->isn_arg.loadstore.ls_name); break; + case ISN_UNLET: + vim_free(isn->isn_arg.unlet.ul_name); + break; + case ISN_STOREOPT: vim_free(isn->isn_arg.storeopt.so_name); break; *** ../vim-8.2.0600/src/proto/vim9compile.pro 2020-03-20 18:39:42.977273186 +0100 --- src/proto/vim9compile.pro 2020-04-19 16:20:40.765331627 +0200 *************** *** 1,13 **** /* vim9compile.c */ int check_defined(char_u *p, int len, cctx_T *cctx); char_u *skip_type(char_u *start); ! type_T *parse_type(char_u **arg, garray_T *type_list); char *vartype_name(vartype_T type); char *type_name(type_T *type, char **tofree); int get_script_item_idx(int sid, char_u *name, int check_writable); imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx); char_u *to_name_const_end(char_u *arg); int assignment_len(char_u *p, int *heredoc); void compile_def_function(ufunc_T *ufunc, int set_return_type); void delete_instr(isn_T *isn); void delete_def_function(ufunc_T *ufunc); --- 1,14 ---- /* vim9compile.c */ int check_defined(char_u *p, int len, cctx_T *cctx); char_u *skip_type(char_u *start); ! type_T *parse_type(char_u **arg, garray_T *type_gap); char *vartype_name(vartype_T type); char *type_name(type_T *type, char **tofree); int get_script_item_idx(int sid, char_u *name, int check_writable); imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx); char_u *to_name_const_end(char_u *arg); int assignment_len(char_u *p, int *heredoc); + int check_vim9_unlet(char_u *name); void compile_def_function(ufunc_T *ufunc, int set_return_type); void delete_instr(isn_T *isn); void delete_def_function(ufunc_T *ufunc); *** ../vim-8.2.0600/src/vim9.h 2020-04-19 14:32:13.556206438 +0200 --- src/vim9.h 2020-04-19 15:47:44.765833435 +0200 *************** *** 44,49 **** --- 44,51 ---- ISN_STORENR, // store number into local variable isn_arg.storenr.stnr_idx + ISN_UNLET, // unlet variable isn_arg.unlet.ul_name + // constants ISN_PUSHNR, // push number isn_arg.number ISN_PUSHBOOL, // push bool value isn_arg.number *************** *** 205,210 **** --- 207,218 ---- int script_idx; // index in sn_var_vals } script_T; + // arguments to ISN_UNLET + typedef struct { + char_u *ul_name; // variable name with g:, w:, etc. + int ul_forceit; // forceit flag + } unlet_T; + /* * Instruction */ *************** *** 235,240 **** --- 243,249 ---- storeopt_T storeopt; loadstore_T loadstore; script_T script; + unlet_T unlet; } isn_arg; }; *** ../vim-8.2.0600/src/vim9execute.c 2020-04-19 14:32:13.556206438 +0200 --- src/vim9execute.c 2020-04-19 15:56:22.380317530 +0200 *************** *** 1068,1073 **** --- 1068,1079 ---- } break; + case ISN_UNLET: + if (do_unlet(iptr->isn_arg.unlet.ul_name, + iptr->isn_arg.unlet.ul_forceit) == FAIL) + goto failed; + break; + // create a list from items on the stack; uses a single allocation // for the list header and the items case ISN_NEWLIST: *************** *** 2108,2113 **** --- 2114,2124 ---- case ISN_PUSHEXC: smsg("%4d PUSH v:exception", current); break; + case ISN_UNLET: + smsg("%4d UNLET%s %s", current, + iptr->isn_arg.unlet.ul_forceit ? "!" : "", + iptr->isn_arg.unlet.ul_name); + break; case ISN_NEWLIST: smsg("%4d NEWLIST size %lld", current, (long long)(iptr->isn_arg.number)); *** ../vim-8.2.0600/src/evalvars.c 2020-04-14 20:15:45.284566193 +0200 --- src/evalvars.c 2020-04-19 16:21:14.801284268 +0200 *************** *** 172,180 **** static void list_tab_vars(int *first); static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first); static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op); ! static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep); ! static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit); ! static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock); static void item_lock(typval_T *tv, int deep, int lock); static void delete_var(hashtab_T *ht, hashitem_T *hi); static void list_one_var(dictitem_T *v, char *prefix, int *first); --- 172,179 ---- static void list_tab_vars(int *first); static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first); static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op); ! static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie); ! static int do_lock_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie); static void item_lock(typval_T *tv, int deep, int lock); static void delete_var(hashtab_T *ht, hashitem_T *hi); static void list_one_var(dictitem_T *v, char *prefix, int *first); *************** *** 1372,1378 **** void ex_unlet(exarg_T *eap) { ! ex_unletlock(eap, eap->arg, 0); } /* --- 1371,1377 ---- void ex_unlet(exarg_T *eap) { ! ex_unletlock(eap, eap->arg, 0, 0, do_unlet_var, NULL); } /* *************** *** 1392,1408 **** arg = skipwhite(arg); } ! ex_unletlock(eap, arg, deep); } /* * ":unlet", ":lockvar" and ":unlockvar" are quite similar. */ ! static void ex_unletlock( exarg_T *eap, char_u *argstart, ! int deep) { char_u *arg = argstart; char_u *name_end; --- 1391,1412 ---- arg = skipwhite(arg); } ! ex_unletlock(eap, arg, deep, 0, do_lock_var, NULL); } /* * ":unlet", ":lockvar" and ":unlockvar" are quite similar. + * Also used for Vim9 script. "callback" is invoked as: + * callback(&lv, name_end, eap, deep, cookie) */ ! void ex_unletlock( exarg_T *eap, char_u *argstart, ! int deep, ! int glv_flags, ! int (*callback)(lval_T *, char_u *, exarg_T *, int, void *), ! void *cookie) { char_u *arg = argstart; char_u *name_end; *************** *** 1426,1433 **** } // Parse the name and find the end. ! name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error, 0, ! FNE_CHECK_START); if (lv.ll_name == NULL) error = TRUE; // error but continue parsing if (name_end == NULL || (!VIM_ISWHITE(*name_end) --- 1430,1437 ---- } // Parse the name and find the end. ! name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error, ! glv_flags, FNE_CHECK_START); if (lv.ll_name == NULL) error = TRUE; // error but continue parsing if (name_end == NULL || (!VIM_ISWHITE(*name_end) *************** *** 1443,1468 **** break; } ! if (!error && !eap->skip) ! { ! if (eap->cmdidx == CMD_unlet) ! { ! if (do_unlet_var(&lv, name_end, eap->forceit) == FAIL) ! error = TRUE; ! } ! else ! { ! if (do_lock_var(&lv, name_end, deep, ! eap->cmdidx == CMD_lockvar) == FAIL) ! error = TRUE; ! } ! } if (!eap->skip) clear_lval(&lv); arg = skipwhite(name_end); ! } while (!ends_excmd(*arg)); eap->nextcmd = check_nextcmd(arg); } --- 1447,1461 ---- break; } ! if (!error && !eap->skip ! && callback(&lv, name_end, eap, deep, cookie) == FAIL) ! error = TRUE; if (!eap->skip) clear_lval(&lv); arg = skipwhite(name_end); ! } while (!ends_excmd2(name_end, arg)); eap->nextcmd = check_nextcmd(arg); } *************** *** 1471,1478 **** do_unlet_var( lval_T *lp, char_u *name_end, ! int forceit) { int ret = OK; int cc; --- 1464,1474 ---- do_unlet_var( lval_T *lp, char_u *name_end, ! exarg_T *eap, ! int deep UNUSED, ! void *cookie UNUSED) { + int forceit = eap->forceit; int ret = OK; int cc; *************** *** 1541,1546 **** --- 1537,1546 ---- dict_T *d; dictitem_T *di; + if (current_sctx.sc_version == SCRIPT_VERSION_VIM9 + && check_vim9_unlet(name) == FAIL) + return FAIL; + ht = find_var_ht(name, &varname); if (ht != NULL && *varname != NUL) { *************** *** 1592,1600 **** do_lock_var( lval_T *lp, char_u *name_end, int deep, ! int lock) { int ret = OK; int cc; dictitem_T *di; --- 1592,1602 ---- do_lock_var( lval_T *lp, char_u *name_end, + exarg_T *eap, int deep, ! void *cookie UNUSED) { + int lock = eap->cmdidx == CMD_lockvar; int ret = OK; int cc; dictitem_T *di; *** ../vim-8.2.0600/src/proto/evalvars.pro 2020-04-14 20:15:45.284566193 +0200 --- src/proto/evalvars.pro 2020-04-19 15:38:44.039777011 +0200 *************** *** 21,26 **** --- 21,27 ---- void list_hashtable_vars(hashtab_T *ht, char *prefix, int empty, int *first); void ex_unlet(exarg_T *eap); void ex_lockvar(exarg_T *eap); + void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, int glv_flags, int (*callback)(lval_T *, char_u *, exarg_T *, int, void *), void *cookie); int do_unlet(char_u *name, int forceit); void del_menutrans_vars(void); char_u *get_user_var_name(expand_T *xp, int idx); *** ../vim-8.2.0600/src/eval.c 2020-04-16 22:54:28.723107683 +0200 --- src/eval.c 2020-04-19 15:36:14.216123215 +0200 *************** *** 5098,5103 **** --- 5098,5104 ---- int br_nest = 0; char_u *p; int len; + int vim9script = current_sctx.sc_version == SCRIPT_VERSION_VIM9; if (expr_start != NULL) { *************** *** 5106,5117 **** } // Quick check for valid starting character. ! if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg) && *arg != '{') return arg; for (p = arg; *p != NUL && (eval_isnamec(*p) ! || *p == '{' || ((flags & FNE_INCL_BR) && (*p == '[' || *p == '.')) || mb_nest != 0 || br_nest != 0); MB_PTR_ADV(p)) --- 5107,5119 ---- } // Quick check for valid starting character. ! if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg) ! && (*arg != '{' || vim9script)) return arg; for (p = arg; *p != NUL && (eval_isnamec(*p) ! || (*p == '{' && !vim9script) || ((flags & FNE_INCL_BR) && (*p == '[' || *p == '.')) || mb_nest != 0 || br_nest != 0); MB_PTR_ADV(p)) *************** *** 5151,5157 **** --br_nest; } ! if (br_nest == 0) { if (*p == '{') { --- 5153,5159 ---- --br_nest; } ! if (br_nest == 0 && !vim9script) { if (*p == '{') { *** ../vim-8.2.0600/src/testdir/test_vim9_script.vim 2020-04-19 14:32:13.556206438 +0200 --- src/testdir/test_vim9_script.vim 2020-04-19 16:01:04.171549078 +0200 *************** *** 213,219 **** return 'xxx' enddef ! func Test_assignment_failure() call CheckDefFailure(['let var=234'], 'E1004:') call CheckDefFailure(['let var =234'], 'E1004:') call CheckDefFailure(['let var= 234'], 'E1004:') --- 213,219 ---- return 'xxx' enddef ! def Test_assignment_failure() call CheckDefFailure(['let var=234'], 'E1004:') call CheckDefFailure(['let var =234'], 'E1004:') call CheckDefFailure(['let var= 234'], 'E1004:') *************** *** 241,249 **** call CheckDefFailure(['let xnr += 4'], 'E1020:') call CheckScriptFailure(['vim9script', 'def Func()', 'let dummy = s:notfound', 'enddef'], 'E1050:') - " TODO: implement this error - "call CheckScriptFailure(['vim9script', 'let svar = 123', 'unlet svar'], 'E1050:') - "call CheckScriptFailure(['vim9script', 'let svar = 123', 'unlet s:svar'], 'E1050:') call CheckDefFailure(['let var: list = [123]'], 'expected list but got list') call CheckDefFailure(['let var: list = ["xx"]'], 'expected list but got list') --- 241,246 ---- *************** *** 259,265 **** call assert_fails('s/^/\=Mess()/n', 'E794:') call CheckDefFailure(['let var: dict'], 'E1010:') --- 256,295 ---- call assert_fails('s/^/\=Mess()/n', 'E794:') call CheckDefFailure(['let var: dict'], 'E1010:') *************** *** 1155,1160 **** --- 1185,1208 ---- au! TabEnter unlet g:entered + + CheckScriptSuccess([ + 'vim9script', + 'let g:var = 123', + 'let w:var = 777', + 'unlet g:var w:var # something', + ]) + + CheckScriptFailure([ + 'vim9script', + 'let g:var = 123', + 'unlet g:var# comment', + ], 'E108:') + + CheckScriptFailure([ + 'let g:var = 123', + 'unlet g:var # something', + ], 'E488:') enddef " Keep this last, it messes up highlighting. *** ../vim-8.2.0600/src/testdir/test_vim9_disassemble.vim 2020-04-19 14:32:13.556206438 +0200 --- src/testdir/test_vim9_disassemble.vim 2020-04-19 16:16:30.837605121 +0200 *************** *** 126,131 **** --- 126,150 ---- res) enddef + def s:ScriptFuncUnlet() + g:somevar = "value" + unlet g:somevar + unlet! g:somevar + enddef + + def Test_disassemble_unlet() + let res = execute('disass s:ScriptFuncUnlet') + assert_match('\d*_ScriptFuncUnlet.*' .. + 'g:somevar = "value".*' .. + '\d PUSHS "value".*' .. + '\d STOREG g:somevar.*' .. + 'unlet g:somevar.*' .. + '\d UNLET g:somevar.*' .. + 'unlet! g:somevar.*' .. + '\d UNLET! g:somevar.*', + res) + enddef + def s:ScriptFuncTry() try echo 'yes' *** ../vim-8.2.0600/src/version.c 2020-04-19 14:32:13.556206438 +0200 --- src/version.c 2020-04-19 14:50:53.650513615 +0200 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 601, /**/ -- A salesperson says: Translation: "backward compatible" Old technology "Premium" Overpriced "Can't keep it on the shelf" Unavailable "Stands alone" Piece of shit "Proprietary" Incompatible (Scott Adams - The Dilbert principle) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///