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<string> = [123]'], 'expected
list<string> but got list<number>')
call CheckDefFailure(['let var: list<number> = ["xx"]'], 'expected
list<number> but got list<string>')
--- 241,246 ----
***************
*** 259,265 ****
call assert_fails('s/^/\=Mess()/n', 'E794:')
call CheckDefFailure(['let var: dict<number'], 'E1009:')
! endfunc
func Test_wrong_type()
call CheckDefFailure(['let var: list<nothing>'], 'E1010:')
--- 256,295 ----
call assert_fails('s/^/\=Mess()/n', 'E794:')
call CheckDefFailure(['let var: dict<number'], 'E1009:')
! enddef
!
! def Test_unlet()
! g:somevar = 'yes'
! assert_true(exists('g:somevar'))
! unlet g:somevar
! assert_false(exists('g:somevar'))
! unlet! g:somevar
!
! call CheckScriptFailure([
! 'vim9script',
! 'let svar = 123',
! 'unlet svar',
! ], 'E1081:')
! call CheckScriptFailure([
! 'vim9script',
! 'let svar = 123',
! 'unlet s:svar',
! ], 'E1081:')
! call CheckScriptFailure([
! 'vim9script',
! 'let svar = 123',
! 'def Func()',
! ' unlet svar',
! 'enddef',
! ], 'E1081:')
! call CheckScriptFailure([
! 'vim9script',
! 'let svar = 123',
! 'def Func()',
! ' unlet s:svar',
! 'enddef',
! ], 'E1081:')
! enddef
func Test_wrong_type()
call CheckDefFailure(['let var: list<nothing>'], '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('<SNR>\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 -- [email protected] -- 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 ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/202004191429.03JETT75030397%40masaka.moolenaar.net.